diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2011-05-19 15:36:20 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2011-05-19 15:36:20 +0200 |
commit | 49e85dee59a0d594b409e64dc621915617b888c3 (patch) | |
tree | b610bfca34ff0199a70b003d7fd3251b493e2f3b /lib | |
parent | 7819846300dc036fb94569feff4f86cd33b459d9 (diff) | |
download | android_external_fuse-49e85dee59a0d594b409e64dc621915617b888c3.tar.gz android_external_fuse-49e85dee59a0d594b409e64dc621915617b888c3.tar.bz2 android_external_fuse-49e85dee59a0d594b409e64dc621915617b888c3.zip |
Fix ioctl ABI
Fix the ambiguity of ioctl ABI on the kernel/userspace boundary
for 32bit vs. 64bit userspace
Diffstat (limited to 'lib')
-rw-r--r-- | lib/fuse_i.h | 1 | ||||
-rw-r--r-- | lib/fuse_lowlevel.c | 82 |
2 files changed, 73 insertions, 10 deletions
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, |