Fix ioctl ABI
authorMiklos Szeredi <mszeredi@suse.cz>
Thu, 19 May 2011 13:36:20 +0000 (15:36 +0200)
committerMiklos Szeredi <mszeredi@suse.cz>
Thu, 19 May 2011 13:36:20 +0000 (15:36 +0200)
Fix the ambiguity of ioctl ABI on the kernel/userspace boundary
for 32bit vs. 64bit userspace

ChangeLog
include/fuse_kernel.h
lib/fuse_i.h
lib/fuse_lowlevel.c

index caacc4aa86520cc9a7272f271ff49f2fedb089f2..82d61335409cf64429d730b12c25f88504b597d7 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -56,6 +56,9 @@
        filesystem may implement this to process multiple forget requests
        in one call
 
+       * Fix the ambiguity of ioctl ABI on the kernel/userspace boundary
+       for 32bit vs. 64bit userspace
+
 2010-11-10  Miklos Szeredi <miklos@szeredi.hu>
 
        * Add new write_buf() method to the highlevel API.  Similarly to
index 7e7ca1745eeb0561909f4351162558a73bb5f85c..ab8b94e4bfaba7340f88466ad159740847dff61d 100644 (file)
@@ -70,6 +70,9 @@
  *
  * 7.16
  *  - add BATCH_FORGET request
+ *  - FUSE_IOCTL_UNRESTRICTED shall now return with array of 'struct
+ *    fuse_ioctl_iovec' instead of ambiguous 'struct iovec'
+ *  - add FUSE_IOCTL_32BIT flag
  */
 
 #ifndef _LINUX_FUSE_H
@@ -234,12 +237,14 @@ struct fuse_file_lock {
  * 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_32BIT: 32bit ioctl
  *
  * 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_32BIT       (1 << 3)
 
 #define FUSE_IOCTL_MAX_IOV     256
 
@@ -555,6 +560,11 @@ struct fuse_ioctl_in {
        __u32   out_size;
 };
 
+struct fuse_ioctl_iovec {
+       __u64   base;
+       __u64   len;
+};
+
 struct fuse_ioctl_out {
        __s32   result;
        __u32   flags;
index d35d5f3a9bf8533991734de68bfee4b38aaed7d8..b715da7bd40435642bb04dccf653421c728675f6 100644 (file)
@@ -36,6 +36,7 @@ struct fuse_req {
        struct fuse_ctx ctx;
        struct fuse_chan *ch;
        int interrupted;
+       unsigned int ioctl_64bit : 1;
        union {
                struct {
                        uint64_t unique;
index faa415aa6cd193027d58182485f5d2a2e21d0e67..ee8fe2de439503a810eeb1d4b0e2c6b4db63dd12 100644 (file)
@@ -791,13 +791,34 @@ int fuse_reply_bmap(fuse_req_t req, uint64_t idx)
        return send_reply_ok(req, &arg, sizeof(arg));
 }
 
+static struct fuse_ioctl_iovec *fuse_ioctl_iovec_copy(const struct iovec *iov,
+                                                     size_t count)
+{
+       struct fuse_ioctl_iovec *fiov;
+       size_t i;
+
+       fiov = malloc(sizeof(fiov[0]) * count);
+       if (!fiov)
+               return NULL;
+
+       for (i = 0; i < count; i++) {
+               fiov[i].base = (uintptr_t) iov[i].iov_base;
+               fiov[i].len = iov[i].iov_len;
+       }
+
+       return fiov;
+}
+
 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 fuse_ioctl_iovec *in_fiov = NULL;
+       struct fuse_ioctl_iovec *out_fiov = NULL;
        struct iovec iov[4];
        size_t count = 1;
+       int res;
 
        memset(&arg, 0, sizeof(arg));
        arg.flags |= FUSE_IOCTL_RETRY;
@@ -807,19 +828,55 @@ int fuse_reply_ioctl_retry(fuse_req_t req,
        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 (req->f->conn.proto_minor < 16) {
+               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++;
+               if (out_count) {
+                       iov[count].iov_base = (void *)out_iov;
+                       iov[count].iov_len = sizeof(out_iov[0]) * out_count;
+                       count++;
+               }
+       } else {
+               /* Can't handle non-compat 64bit ioctls on 32bit */
+               if (sizeof(void *) == 4 && req->ioctl_64bit) {
+                       res = fuse_reply_err(req, EINVAL);
+                       goto out;
+               }
+
+               if (in_count) {
+                       in_fiov = fuse_ioctl_iovec_copy(in_iov, in_count);
+                       if (!in_fiov)
+                               goto enomem;
+
+                       iov[count].iov_base = (void *)in_fiov;
+                       iov[count].iov_len = sizeof(in_fiov[0]) * in_count;
+                       count++;
+               }
+               if (out_count) {
+                       out_fiov = fuse_ioctl_iovec_copy(out_iov, out_count);
+                       if (!out_fiov)
+                               goto enomem;
+
+                       iov[count].iov_base = (void *)out_fiov;
+                       iov[count].iov_len = sizeof(out_fiov[0]) * out_count;
+                       count++;
+               }
        }
 
-       return send_reply_iov(req, 0, iov, count);
+       res = send_reply_iov(req, 0, iov, count);
+out:
+       free(in_fiov);
+       free(out_fiov);
+
+       return res;
+
+enomem:
+       res = fuse_reply_err(req, ENOMEM);
+       goto out;
 }
 
 int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size)
@@ -1545,6 +1602,11 @@ static void do_ioctl(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
        fi.fh = arg->fh;
        fi.fh_old = fi.fh;
 
+       if (sizeof(void *) == 4 && req->f->conn.proto_minor >= 16 &&
+           !(flags & FUSE_IOCTL_32BIT)) {
+               req->ioctl_64bit = 1;
+       }
+
        if (req->f->op.ioctl)
                req->f->op.ioctl(req, nodeid, arg->cmd,
                                 (void *)(uintptr_t)arg->arg, &fi, flags,