* Implement ioctl support. On high level interface only
authorMiklos Szeredi <miklos@szeredi.hu>
Fri, 5 Dec 2008 10:55:36 +0000 (10:55 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Fri, 5 Dec 2008 10:55:36 +0000 (10:55 +0000)
"restricted" ioctls are supported (which are defined with the
_IO(), _IOR(), _IOW() or _IOWR() macros).  Unrestricted ioctls
will only be allwed to CUSE (Character Device in Userspace)
servers.  Patch by Tejun Heo

13 files changed:
ChangeLog
example/.cvsignore
example/Makefile.am
example/fioc.c [new file with mode: 0644]
example/fioc.h [new file with mode: 0644]
example/fioclient.c [new file with mode: 0644]
include/fuse.h
include/fuse_common.h
include/fuse_kernel.h
include/fuse_lowlevel.h
lib/fuse.c
lib/fuse_lowlevel.c
lib/fuse_versionscript

index d6de6ff0f0bad8ffadcfc5956d6dc9251547e9c9..5220ff5775050bab02680455dca2cab99f47478d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-12-05  Miklos Szeredi <miklos@szeredi.hu>
+
+       * Implement ioctl support.  On high level interface only
+       "restricted" ioctls are supported (which are defined with the
+       _IO(), _IOR(), _IOW() or _IOWR() macros).  Unrestricted ioctls
+       will only be allwed to CUSE (Character Device in Userspace)
+       servers.  Patch by Tejun Heo
+
 2008-11-28  Miklos Szeredi <miklos@szeredi.hu>
 
        * If open sets fi->nonseekable, libfuse will tell the kernel that
index 56dabc1922a07c351e0218a28a0ba88abf03eb16..7332bc0584fa4ed814acc5cc8b862a7123711ef9 100644 (file)
@@ -6,4 +6,6 @@ fusexmp_fh
 null
 hello
 hello_ll
+fioc
+fioclient
 .libs
index 91f33cc537de0a81e5c048f197ee5674eb8a351a..43b195d108668b108662650ae37b44cd4972b5a3 100644 (file)
@@ -1,7 +1,13 @@
 ## Process this file with automake to produce Makefile.in
 
 AM_CPPFLAGS = -I$(top_srcdir)/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT
-noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll
+noinst_HEADERS = fioc.h
+noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll fioc fioclient
 
 LDADD = ../lib/libfuse.la @libfuse_libs@
 fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la @libfuse_libs@
+
+fioclient_CPPFLAGS =
+fioclient_LDFLAGS =
+fioclient_LDADD =
+
diff --git a/example/fioc.c b/example/fioc.c
new file mode 100644 (file)
index 0000000..d0dce15
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+  FUSE fioc: FUSE ioctl example
+  Copyright (C) 2008       SUSE Linux Products GmbH
+  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
+
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
+
+  gcc -Wall `pkg-config fuse --cflags --libs` fioc.c -o fioc
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include "fioc.h"
+
+#define FIOC_NAME      "fioc"
+
+enum {
+       FIOC_NONE,
+       FIOC_ROOT,
+       FIOC_FILE,
+};
+
+static void *fioc_buf;
+static size_t fioc_size;
+
+static int fioc_resize(size_t new_size)
+{
+       void *new_buf;
+
+       if (new_size == fioc_size)
+               return 0;
+
+       new_buf = realloc(fioc_buf, new_size);
+       if (!new_buf && new_size)
+               return -ENOMEM;
+
+       if (new_size > fioc_size)
+               memset(new_buf + fioc_size, 0, new_size - fioc_size);
+
+       fioc_buf = new_buf;
+       fioc_size = new_size;
+
+       return 0;
+}
+
+static int fioc_expand(size_t new_size)
+{
+       if (new_size > fioc_size)
+               return fioc_resize(new_size);
+       return 0;
+}
+
+static int fioc_file_type(const char *path)
+{
+       if (strcmp(path, "/") == 0)
+               return FIOC_ROOT;
+       if (strcmp(path, "/" FIOC_NAME) == 0)
+               return FIOC_FILE;
+       return FIOC_NONE;
+}
+
+static int fioc_getattr(const char *path, struct stat *stbuf)
+{
+       stbuf->st_uid = getuid();
+       stbuf->st_gid = getgid();
+       stbuf->st_atime = stbuf->st_mtime = time(NULL);
+
+       switch (fioc_file_type(path)) {
+       case FIOC_ROOT:
+               stbuf->st_mode = S_IFDIR | 0755;
+               stbuf->st_nlink = 2;
+               break;
+       case FIOC_FILE:
+               stbuf->st_mode = S_IFREG | 0644;
+               stbuf->st_nlink = 1;
+               stbuf->st_size = fioc_size;
+               break;
+       case FIOC_NONE:
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+static int fioc_open(const char *path, struct fuse_file_info *fi)
+{
+       (void) fi;
+
+       if (fioc_file_type(path) != FIOC_NONE)
+               return 0;
+       return -ENOENT;
+}
+
+static int fioc_do_read(char *buf, size_t size, off_t offset)
+{
+       if (offset >= fioc_size)
+               return 0;
+
+       if (size > fioc_size - offset)
+               size = fioc_size - offset;
+
+       memcpy(buf, fioc_buf + offset, size);
+
+       return size;
+}
+
+static int fioc_read(const char *path, char *buf, size_t size,
+                    off_t offset, struct fuse_file_info *fi)
+{
+       (void) fi;
+
+       if (fioc_file_type(path) != FIOC_FILE)
+               return -EINVAL;
+
+       return fioc_do_read(buf, size, offset);
+}
+
+static int fioc_do_write(const char *buf, size_t size, off_t offset)
+{
+       if (fioc_expand(offset + size))
+               return -ENOMEM;
+
+       memcpy(fioc_buf + offset, buf, size);
+
+       return size;
+}
+
+static int fioc_write(const char *path, const char *buf, size_t size,
+                     off_t offset, struct fuse_file_info *fi)
+{
+       (void) fi;
+
+       if (fioc_file_type(path) != FIOC_FILE)
+               return -EINVAL;
+
+       return fioc_do_write(buf, size, offset);
+}
+
+static int fioc_truncate(const char *path, off_t size)
+{
+       if (fioc_file_type(path) != FIOC_FILE)
+               return -EINVAL;
+
+       return fioc_resize(size);
+}
+
+static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+                       off_t offset, struct fuse_file_info *fi)
+{
+       (void) fi;
+       (void) offset;
+
+       if (fioc_file_type(path) != FIOC_ROOT)
+               return -ENOENT;
+
+       filler(buf, ".", NULL, 0);
+       filler(buf, "..", NULL, 0);
+       filler(buf, FIOC_NAME, NULL, 0);
+
+       return 0;
+}
+
+static int fioc_ioctl(const char *path, int cmd, void *arg,
+                     struct fuse_file_info *fi, unsigned int flags, void *data)
+{
+       (void) arg;
+       (void) fi;
+       (void) flags;
+
+       if (fioc_file_type(path) != FIOC_FILE)
+               return -EINVAL;
+
+       if (flags & FUSE_IOCTL_COMPAT)
+               return -ENOSYS;
+
+       switch (cmd) {
+       case FIOC_GET_SIZE:
+               *(size_t *)data = fioc_size;
+               return 0;
+
+       case FIOC_SET_SIZE:
+               fioc_resize(*(size_t *)data);
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+static struct fuse_operations fioc_oper = {
+       .getattr        = fioc_getattr,
+       .readdir        = fioc_readdir,
+       .truncate       = fioc_truncate,
+       .open           = fioc_open,
+       .read           = fioc_read,
+       .write          = fioc_write,
+       .ioctl          = fioc_ioctl,
+};
+
+int main(int argc, char *argv[])
+{
+       return fuse_main(argc, argv, &fioc_oper, NULL);
+}
diff --git a/example/fioc.h b/example/fioc.h
new file mode 100644 (file)
index 0000000..c1d9cdf
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+  FUSE-ioctl: ioctl support for FUSE
+  Copyright (C) 2008       SUSE Linux Products GmbH
+  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
+
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
+*/
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+
+enum {
+       FIOC_GET_SIZE = _IOR('E', 0, size_t),
+       FIOC_SET_SIZE = _IOW('E', 1, size_t),
+};
diff --git a/example/fioclient.c b/example/fioclient.c
new file mode 100644 (file)
index 0000000..3ab63b2
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+  FUSE fioclient: FUSE ioctl example client
+  Copyright (C) 2008       SUSE Linux Products GmbH
+  Copyright (C) 2008       Tejun Heo <teheo@suse.de>
+
+  This program can be distributed under the terms of the GNU GPL.
+  See the file COPYING.
+
+  gcc -Wall fioclient.c -o fioclient
+*/
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "fioc.h"
+
+const char *usage =
+"Usage: fioclient FIOC_FILE [SIZE]\n"
+"\n"
+"       get size if SIZE is omitted, set size otherwise\n"
+"\n";
+
+int main(int argc, char **argv)
+{
+       size_t size;
+       int fd;
+
+       if (argc < 2) {
+               goto usage;
+       }
+
+       fd = open(argv[1], O_RDWR);
+       if (fd < 0) {
+               perror("open");
+               return 1;
+       }
+
+       if (argc == 2) {
+               if (ioctl(fd, FIOC_GET_SIZE, &size)) {
+                       perror("ioctl");
+                       return 1;
+               }
+               printf("%zu\n", size);
+       } else {
+               char *endp;
+
+               size = strtoul(argv[2], &endp, 0);
+               if (endp == argv[2] || *endp != '\0')
+                       goto usage;
+
+               if (ioctl(fd, FIOC_SET_SIZE, &size)) {
+                       perror("ioctl");
+                       return 1;
+               }
+       }
+       return 0;
+
+usage:
+       fprintf(stderr, usage);
+       return 1;
+}
index 8d47bc5c199343e10633ebcf64eb1315b282fdf6..2016ccc66bbe1790235c57e09d3ed81de8649b35 100644 (file)
@@ -31,6 +31,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/statvfs.h>
+#include <sys/uio.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -457,6 +458,22 @@ struct fuse_operations {
         * Reserved flags, don't set
         */
        unsigned int flag_reserved : 31;
+
+       /**
+        * Ioctl
+        *
+        * @flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
+        * 64bit environment.  The size and direction of @data is
+        * determined by _IOC_*() decoding of @cmd.  For _IOC_NONE,
+        * @data will be NULL, for _IOC_WRITE @data is out area, for
+        * _IOC_READ in area and if both are set in/out area.  In all
+        * non-NULL cases, the area is of _IOC_SIZE(@cmd) bytes.
+        *
+        * Introduced in version 2.9
+        */
+       int (*ioctl) (const char *, int cmd, void *arg,
+                     struct fuse_file_info *, unsigned int flags, void *data);
+
 };
 
 /** Extra context that may be needed by some filesystems
@@ -689,6 +706,8 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
                        const char *name);
 int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
                 uint64_t *idx);
+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
+                 struct fuse_file_info *fi, unsigned int flags, void *data);
 void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
 void fuse_fs_destroy(struct fuse_fs *fs);
 
index 9bbc3868fd22a05ff5fa6f8b3c981f81f15fd997..fb18b61307b71c22a7519f8a8a57bcdc8e05d4d9 100644 (file)
@@ -93,6 +93,21 @@ struct fuse_file_info {
 #define FUSE_CAP_ATOMIC_O_TRUNC        (1 << 3)
 #define FUSE_CAP_BIG_WRITES    (1 << 5)
 
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT      (1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED        (1 << 1)
+#define FUSE_IOCTL_RETRY       (1 << 2)
+
+#define FUSE_IOCTL_MAX_IOV     256
+
 /**
  * Connection information, passed to the ->init() method
  *
index b37d9696ae261e57d9607ae8f608ba5318d239f5..eb28a35616be3aad6ce93456fc78342dee4d4b98 100644 (file)
@@ -1,6 +1,6 @@
 /*
     This file defines the kernel interface of FUSE
-    Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
+    Copyright (C) 2001-2008  Miklos Szeredi <miklos@szeredi.hu>
 
     This program can be distributed under the terms of the GNU GPL.
     See the file COPYING.
  *
  * 7.10
  *  - add nonseekable open flag
+ *
+ * 7.11
+ *  - add IOCTL message
  */
 
 #ifndef _LINUX_FUSE_H
 #define _LINUX_FUSE_H
 
-#ifndef linux
 #include <sys/types.h>
 #define __u64 uint64_t
 #define __u32 uint32_t
 #define __s32 int32_t
-#else
-#include <asm/types.h>
-#include <linux/major.h>
-#endif
 
 /** Version number of this interface */
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 10
+#define FUSE_KERNEL_MINOR_VERSION 11
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
 
-/** The major number of the fuse character device */
-#define FUSE_MAJOR MISC_MAJOR
-
-/** The minor number of the fuse character device */
-#define FUSE_MINOR 229
-
 /* Make sure all structures are padded to 64bit boundary, so 32bit
    userspace works under 64bit kernels */
 
@@ -184,6 +176,21 @@ struct fuse_file_lock {
  */
 #define FUSE_READ_LOCKOWNER    (1 << 1)
 
+/**
+ * Ioctl flags
+ *
+ * FUSE_IOCTL_COMPAT: 32bit compat ioctl on 64bit machine
+ * FUSE_IOCTL_UNRESTRICTED: not restricted to well-formed ioctls, retry allowed
+ * FUSE_IOCTL_RETRY: retry with new iovecs
+ *
+ * FUSE_IOCTL_MAX_IOV: maximum of in_iovecs + out_iovecs
+ */
+#define FUSE_IOCTL_COMPAT      (1 << 0)
+#define FUSE_IOCTL_UNRESTRICTED        (1 << 1)
+#define FUSE_IOCTL_RETRY       (1 << 2)
+
+#define FUSE_IOCTL_MAX_IOV     256
+
 enum fuse_opcode {
        FUSE_LOOKUP        = 1,
        FUSE_FORGET        = 2,  /* no reply */
@@ -221,6 +228,7 @@ enum fuse_opcode {
        FUSE_INTERRUPT     = 36,
        FUSE_BMAP          = 37,
        FUSE_DESTROY       = 38,
+       FUSE_IOCTL         = 39,
 };
 
 /* The read buffer is required to be at least 8k, but may be much larger */
@@ -421,6 +429,22 @@ struct fuse_bmap_out {
        __u64   block;
 };
 
+struct fuse_ioctl_in {
+       __u64   fh;
+       __u32   flags;
+       __u32   cmd;
+       __u64   arg;
+       __u32   in_size;
+       __u32   out_size;
+};
+
+struct fuse_ioctl_out {
+       __s32   result;
+       __u32   flags;
+       __u32   in_iovs;
+       __u32   out_iovs;
+};
+
 struct fuse_in_header {
        __u32   len;
        __u32   opcode;
index 9330548c80bc9dd16bd514335d50db3dd95f9fe1..e17274f48bb14f380c2a8769c296bcbcdfe745cf 100644 (file)
@@ -807,6 +807,36 @@ struct fuse_lowlevel_ops {
         */
        void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
                      uint64_t idx);
+
+       /**
+        * Ioctl
+        *
+        * Note: For unrestricted ioctls (not allowed for FUSE
+        * servers), data in and out areas can be discovered by giving
+        * iovs and setting FUSE_IOCTL_RETRY in *@flagsp.  For
+        * restricted ioctls, kernel prepares in/out data area
+        * according to the information encoded in @cmd.
+        *
+        * Introduced in version 2.9
+        *
+        * Valid replies:
+        *   fuse_reply_ioctl_retry
+        *   fuse_reply_ioctl
+        *   fuse_reply_err
+        *
+        * @param req request handle
+        * @param ino the inode number
+        * @param cmd ioctl command
+        * @param arg ioctl argument
+        * @param fi file information
+        * @param flagsp io/out parameter for FUSE_IOCTL_* flags
+        * @param in_buf data fetched from the caller
+        * @param in_size number of fetched bytes
+        * @param out_size maximum size of output data
+        */
+       void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+                      struct fuse_file_info *fi, unsigned *flagsp,
+                      const void *in_buf, size_t in_bufsz, size_t out_bufszp);
 };
 
 /**
@@ -1022,6 +1052,38 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
                         const char *name, const struct stat *stbuf,
                         off_t off);
 
+/**
+ * Reply to ask for data fetch and output buffer preparation.  ioctl
+ * will be retried with the specified input data fetched and output
+ * buffer prepared.
+ *
+ * Possible requests:
+ *   ioctl
+ *
+ * @param req request handle
+ * @param in_iov iovec specifying data to fetch from the caller
+ * @param in_count number of entries in @in_iov
+ * @param out_iov iovec specifying addresses to write output to
+ * @param out_count number of entries in @out_iov
+ * @return zero for success, -errno for failure to send reply
+ */
+int fuse_reply_ioctl_retry(fuse_req_t req,
+                          const struct iovec *in_iov, size_t in_count,
+                          const struct iovec *out_iov, size_t out_count);
+
+/**
+ * Reply to finish ioctl
+ *
+ * Possible requests:
+ *   ioctl
+ *
+ * @param req request handle
+ * @param result result to be passed to the caller
+ * @param buf buffer containing output data
+ * @param size length of output data
+ */
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
+
 /* ----------------------------------------------------------- *
  * Utility functions                                          *
  * ----------------------------------------------------------- */
index 9c5dd0fd765f2cd22f736dceefb432f73e49d7fd..453fca51c08c4eb61ccdeb76588bb4e9d55c6ce5 100644 (file)
@@ -16,6 +16,7 @@
 #include "fuse_misc.h"
 #include "fuse_common_compat.h"
 #include "fuse_compat.h"
+#include "fuse_kernel.h"
 
 #include <stdio.h>
 #include <string.h>
@@ -1652,6 +1653,20 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path, const char *name)
        }
 }
 
+int fuse_fs_ioctl(struct fuse_fs *fs, const char *path, int cmd, void *arg,
+                 struct fuse_file_info *fi, unsigned int flags, void *data)
+{
+       fuse_get_context()->private_data = fs->user_data;
+       if (fs->op.ioctl) {
+               if (fs->debug)
+                       fprintf(stderr, "ioctl[%llu] 0x%x flags: 0x%x\n",
+                               (unsigned long long) fi->fh, cmd, flags);
+
+               return fs->op.ioctl(path, cmd, arg, fi, flags, data);
+       } else
+               return -ENOSYS;
+}
+
 static int is_open(struct fuse *f, fuse_ino_t dir, const char *name)
 {
        struct node *node;
@@ -3169,6 +3184,57 @@ static void fuse_lib_bmap(fuse_req_t req, fuse_ino_t ino, size_t blocksize,
                reply_err(req, err);
 }
 
+static void fuse_lib_ioctl(fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+                          struct fuse_file_info *fi, unsigned int *flagsp,
+                          const void *in_buf, size_t in_bufsz,
+                          size_t out_bufsz)
+{
+       struct fuse *f = req_fuse_prepare(req);
+       struct fuse_intr_data d;
+       char *path, *out_buf = NULL;
+       struct iovec *in_iov = NULL, *out_iov = NULL;
+       int err;
+
+       if (*flagsp & FUSE_IOCTL_UNRESTRICTED) {
+               reply_err(req, -EPERM);
+               return;
+       }
+
+       if (out_bufsz) {
+               out_buf = malloc(out_bufsz);
+               if (!out_buf)
+                       goto enomem;
+       }
+
+       assert(!in_bufsz || !out_bufsz || in_bufsz == out_bufsz);
+       if (out_buf)
+               memcpy(out_buf, in_buf, in_bufsz);
+
+       err = get_path(f, ino, &path);
+       if (err)
+               goto out;
+
+       fuse_prepare_interrupt(f, req, &d);
+
+       err = fuse_fs_ioctl(f->fs, path, cmd, arg, fi, *flagsp,
+                           out_buf ?: (void *)in_buf);
+
+       fuse_finish_interrupt(f, req, &d);
+       free_path(f, ino, path);
+
+       fuse_reply_ioctl(req, err, out_buf, out_bufsz);
+
+out:
+       free(out_buf);
+       free(in_iov);
+       free(out_iov);
+       return;
+
+enomem:
+       reply_err(req, -ENOMEM);
+       goto out;
+}
+
 static struct fuse_lowlevel_ops fuse_path_ops = {
        .init = fuse_lib_init,
        .destroy = fuse_lib_destroy,
@@ -3204,6 +3270,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
        .getlk = fuse_lib_getlk,
        .setlk = fuse_lib_setlk,
        .bmap = fuse_lib_bmap,
+       .ioctl = fuse_lib_ioctl,
 };
 
 static void free_cmd(struct fuse_cmd *cmd)
index 34ff76cb7ea0f97ad17bb5d1e2a31aa1256123a7..6b5fdced68dcdd5eed70a013d8da1b54a202bbe5 100644 (file)
@@ -451,6 +451,58 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
        return send_reply_ok(req, &arg, sizeof(arg));
 }
 
+int fuse_reply_ioctl_retry(fuse_req_t req,
+                          const struct iovec *in_iov, size_t in_count,
+                          const struct iovec *out_iov, size_t out_count)
+{
+       struct fuse_ioctl_out arg;
+       struct iovec iov[4];
+       size_t count = 1;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.flags |= FUSE_IOCTL_RETRY;
+       arg.in_iovs = in_count;
+       arg.out_iovs = out_count;
+       iov[count].iov_base = &arg;
+       iov[count].iov_len = sizeof(arg);
+       count++;
+
+       if (in_count) {
+               iov[count].iov_base = (void *)in_iov;
+               iov[count].iov_len = sizeof(in_iov[0]) * in_count;
+               count++;
+       }
+
+       if (out_count) {
+               iov[count].iov_base = (void *)out_iov;
+               iov[count].iov_len = sizeof(out_iov[0]) * out_count;
+               count++;
+       }
+
+       return send_reply_iov(req, 0, iov, count);
+}
+
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
+{
+       struct fuse_ioctl_out arg;
+       struct iovec iov[3];
+       size_t count = 1;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.result = result;
+       iov[count].iov_base = &arg;
+       iov[count].iov_len = sizeof(arg);
+       count++;
+
+       if (size) {
+               iov[count].iov_base = (char *) buf;
+               iov[count].iov_len = size;
+               count++;
+       }
+
+       return send_reply_iov(req, 0, iov, count);
+}
+
 static void do_lookup(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
        char *name = (char *) inarg;
@@ -1004,6 +1056,25 @@ static void do_bmap(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
                fuse_reply_err(req, ENOSYS);
 }
 
+static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+       struct fuse_ioctl_in *arg = (struct fuse_ioctl_in *) inarg;
+       unsigned int flags = arg->flags;
+       void *in_buf = arg->in_size ? PARAM(arg) : NULL;
+       struct fuse_file_info fi;
+
+       memset(&fi, 0, sizeof(fi));
+       fi.fh = arg->fh;
+       fi.fh_old = fi.fh;
+
+       if (req->f->op.ioctl)
+               req->f->op.ioctl(req, nodeid, arg->cmd,
+                                (void *)(uintptr_t)arg->arg, &fi, &flags,
+                                in_buf, arg->in_size, arg->out_size);
+       else
+               fuse_reply_err(req, ENOSYS);
+}
+
 static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
 {
        struct fuse_init_in *arg = (struct fuse_init_in *) inarg;
@@ -1183,6 +1254,7 @@ static struct {
        [FUSE_CREATE]      = { do_create,      "CREATE"      },
        [FUSE_INTERRUPT]   = { do_interrupt,   "INTERRUPT"   },
        [FUSE_BMAP]        = { do_bmap,        "BMAP"        },
+       [FUSE_IOCTL]       = { do_ioctl,       "IOCTL"       },
        [FUSE_DESTROY]     = { do_destroy,     "DESTROY"     },
 };
 
index 2f6aff3603880b6ab55e4e787f81f6536dae8cb5..4aa1c0bb20c094090bd19e406bced80f36a29171 100644 (file)
@@ -155,8 +155,11 @@ FUSE_2.7 {
 
 FUSE_2.8 {
        global:
+               fuse_fs_ioctl;
                fuse_opt_add_opt_escaped;
                fuse_reply_bmap;
+               fuse_reply_ioctl;
+               fuse_reply_ioctl_retry;
 
        local:
                *;