aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2011-05-19 15:36:20 +0200
committerMiklos Szeredi <mszeredi@suse.cz>2011-05-19 15:36:20 +0200
commit49e85dee59a0d594b409e64dc621915617b888c3 (patch)
treeb610bfca34ff0199a70b003d7fd3251b493e2f3b /lib
parent7819846300dc036fb94569feff4f86cd33b459d9 (diff)
downloadandroid_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.h1
-rw-r--r--lib/fuse_lowlevel.c82
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,