From 49e85dee59a0d594b409e64dc621915617b888c3 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Thu, 19 May 2011 15:36:20 +0200 Subject: [PATCH] Fix ioctl ABI Fix the ambiguity of ioctl ABI on the kernel/userspace boundary for 32bit vs. 64bit userspace --- ChangeLog | 3 ++ include/fuse_kernel.h | 10 ++++++ lib/fuse_i.h | 1 + lib/fuse_lowlevel.c | 82 +++++++++++++++++++++++++++++++++++++------ 4 files changed, 86 insertions(+), 10 deletions(-) diff --git a/ChangeLog b/ChangeLog index caacc4a..82d6133 100644 --- 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 * Add new write_buf() method to the highlevel API. Similarly to diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index 7e7ca17..ab8b94e 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -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; diff --git a/lib/fuse_i.h b/lib/fuse_i.h index d35d5f3..b715da7 100644 --- a/lib/fuse_i.h +++ b/lib/fuse_i.h @@ -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; diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index faa415a..ee8fe2d 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -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, -- 2.30.2