aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2008-12-05 10:55:36 +0000
committerMiklos Szeredi <miklos@szeredi.hu>2008-12-05 10:55:36 +0000
commitecfa5263ab5b19a58d53a7116fb079f3b956b918 (patch)
treea9f9dd2ec17e9185e4d515328b78e3b5e84938f2 /lib
parentcafdcb253e4c7ad6238198982425c004b487d2e6 (diff)
downloadlibfuse-ecfa5263ab5b19a58d53a7116fb079f3b956b918.tar.gz
* 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
Diffstat (limited to 'lib')
-rw-r--r--lib/fuse.c67
-rw-r--r--lib/fuse_lowlevel.c72
-rw-r--r--lib/fuse_versionscript3
3 files changed, 142 insertions, 0 deletions
diff --git a/lib/fuse.c b/lib/fuse.c
index 9c5dd0f..453fca5 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -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)
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 34ff76c..6b5fdce 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -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" },
};
diff --git a/lib/fuse_versionscript b/lib/fuse_versionscript
index 2f6aff3..4aa1c0b 100644
--- a/lib/fuse_versionscript
+++ b/lib/fuse_versionscript
@@ -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:
*;