summaryrefslogtreecommitdiff
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
parentcafdcb253e4c7ad6238198982425c004b487d2e6 (diff)
downloadfuse-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
-rw-r--r--ChangeLog8
-rw-r--r--example/.cvsignore2
-rw-r--r--example/Makefile.am8
-rw-r--r--example/fioc.c211
-rw-r--r--example/fioc.h17
-rw-r--r--example/fioclient.c66
-rw-r--r--include/fuse.h19
-rw-r--r--include/fuse_common.h15
-rw-r--r--include/fuse_kernel.h50
-rw-r--r--include/fuse_lowlevel.h62
-rw-r--r--lib/fuse.c67
-rw-r--r--lib/fuse_lowlevel.c72
-rw-r--r--lib/fuse_versionscript3
13 files changed, 586 insertions, 14 deletions
diff --git a/ChangeLog b/ChangeLog
index d6de6ff..5220ff5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2008-12-05 Miklos Szeredi <miklos@szeredi.hu>
+
+ * 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
+
2008-11-28 Miklos Szeredi <miklos@szeredi.hu>
* If open sets fi->nonseekable, libfuse will tell the kernel that
diff --git a/example/.cvsignore b/example/.cvsignore
index 56dabc1..7332bc0 100644
--- a/example/.cvsignore
+++ b/example/.cvsignore
@@ -6,4 +6,6 @@ fusexmp_fh
null
hello
hello_ll
+fioc
+fioclient
.libs
diff --git a/example/Makefile.am b/example/Makefile.am
index 91f33cc..43b195d 100644
--- a/example/Makefile.am
+++ b/example/Makefile.am
@@ -1,7 +1,13 @@
## Process this file with automake to produce Makefile.in
AM_CPPFLAGS = -I$(top_srcdir)/include -D_FILE_OFFSET_BITS=64 -D_REENTRANT
-noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll
+noinst_HEADERS = fioc.h
+noinst_PROGRAMS = fusexmp fusexmp_fh null hello hello_ll fioc fioclient
LDADD = ../lib/libfuse.la @libfuse_libs@
fusexmp_fh_LDADD = ../lib/libfuse.la ../lib/libulockmgr.la @libfuse_libs@
+
+fioclient_CPPFLAGS =
+fioclient_LDFLAGS =
+fioclient_LDADD =
+
diff --git a/example/fioc.c b/example/fioc.c
new file mode 100644
index 0000000..d0dce15
--- /dev/null
+++ b/example/fioc.c
@@ -0,0 +1,211 @@
+/*
+ FUSE fioc: FUSE ioctl example
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall `pkg-config fuse --cflags --libs` fioc.c -o fioc
+*/
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+#include "fioc.h"
+
+#define FIOC_NAME "fioc"
+
+enum {
+ FIOC_NONE,
+ FIOC_ROOT,
+ FIOC_FILE,
+};
+
+static void *fioc_buf;
+static size_t fioc_size;
+
+static int fioc_resize(size_t new_size)
+{
+ void *new_buf;
+
+ if (new_size == fioc_size)
+ return 0;
+
+ new_buf = realloc(fioc_buf, new_size);
+ if (!new_buf && new_size)
+ return -ENOMEM;
+
+ if (new_size > fioc_size)
+ memset(new_buf + fioc_size, 0, new_size - fioc_size);
+
+ fioc_buf = new_buf;
+ fioc_size = new_size;
+
+ return 0;
+}
+
+static int fioc_expand(size_t new_size)
+{
+ if (new_size > fioc_size)
+ return fioc_resize(new_size);
+ return 0;
+}
+
+static int fioc_file_type(const char *path)
+{
+ if (strcmp(path, "/") == 0)
+ return FIOC_ROOT;
+ if (strcmp(path, "/" FIOC_NAME) == 0)
+ return FIOC_FILE;
+ return FIOC_NONE;
+}
+
+static int fioc_getattr(const char *path, struct stat *stbuf)
+{
+ stbuf->st_uid = getuid();
+ stbuf->st_gid = getgid();
+ stbuf->st_atime = stbuf->st_mtime = time(NULL);
+
+ switch (fioc_file_type(path)) {
+ case FIOC_ROOT:
+ stbuf->st_mode = S_IFDIR | 0755;
+ stbuf->st_nlink = 2;
+ break;
+ case FIOC_FILE:
+ stbuf->st_mode = S_IFREG | 0644;
+ stbuf->st_nlink = 1;
+ stbuf->st_size = fioc_size;
+ break;
+ case FIOC_NONE:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int fioc_open(const char *path, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (fioc_file_type(path) != FIOC_NONE)
+ return 0;
+ return -ENOENT;
+}
+
+static int fioc_do_read(char *buf, size_t size, off_t offset)
+{
+ if (offset >= fioc_size)
+ return 0;
+
+ if (size > fioc_size - offset)
+ size = fioc_size - offset;
+
+ memcpy(buf, fioc_buf + offset, size);
+
+ return size;
+}
+
+static int fioc_read(const char *path, char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ return fioc_do_read(buf, size, offset);
+}
+
+static int fioc_do_write(const char *buf, size_t size, off_t offset)
+{
+ if (fioc_expand(offset + size))
+ return -ENOMEM;
+
+ memcpy(fioc_buf + offset, buf, size);
+
+ return size;
+}
+
+static int fioc_write(const char *path, const char *buf, size_t size,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) fi;
+
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ return fioc_do_write(buf, size, offset);
+}
+
+static int fioc_truncate(const char *path, off_t size)
+{
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ return fioc_resize(size);
+}
+
+static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info *fi)
+{
+ (void) fi;
+ (void) offset;
+
+ if (fioc_file_type(path) != FIOC_ROOT)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ filler(buf, FIOC_NAME, NULL, 0);
+
+ return 0;
+}
+
+static int fioc_ioctl(const char *path, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned int flags, void *data)
+{
+ (void) arg;
+ (void) fi;
+ (void) flags;
+
+ if (fioc_file_type(path) != FIOC_FILE)
+ return -EINVAL;
+
+ if (flags & FUSE_IOCTL_COMPAT)
+ return -ENOSYS;
+
+ switch (cmd) {
+ case FIOC_GET_SIZE:
+ *(size_t *)data = fioc_size;
+ return 0;
+
+ case FIOC_SET_SIZE:
+ fioc_resize(*(size_t *)data);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static struct fuse_operations fioc_oper = {
+ .getattr = fioc_getattr,
+ .readdir = fioc_readdir,
+ .truncate = fioc_truncate,
+ .open = fioc_open,
+ .read = fioc_read,
+ .write = fioc_write,
+ .ioctl = fioc_ioctl,
+};
+
+int main(int argc, char *argv[])
+{
+ return fuse_main(argc, argv, &fioc_oper, NULL);
+}
diff --git a/example/fioc.h b/example/fioc.h
new file mode 100644
index 0000000..c1d9cdf
--- /dev/null
+++ b/example/fioc.h
@@ -0,0 +1,17 @@
+/*
+ FUSE-ioctl: ioctl support for FUSE
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+*/
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+
+enum {
+ FIOC_GET_SIZE = _IOR('E', 0, size_t),
+ FIOC_SET_SIZE = _IOW('E', 1, size_t),
+};
diff --git a/example/fioclient.c b/example/fioclient.c
new file mode 100644
index 0000000..3ab63b2
--- /dev/null
+++ b/example/fioclient.c
@@ -0,0 +1,66 @@
+/*
+ FUSE fioclient: FUSE ioctl example client
+ Copyright (C) 2008 SUSE Linux Products GmbH
+ Copyright (C) 2008 Tejun Heo <teheo@suse.de>
+
+ This program can be distributed under the terms of the GNU GPL.
+ See the file COPYING.
+
+ gcc -Wall fioclient.c -o fioclient
+*/
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include "fioc.h"
+
+const char *usage =
+"Usage: fioclient FIOC_FILE [SIZE]\n"
+"\n"
+" get size if SIZE is omitted, set size otherwise\n"
+"\n";
+
+int main(int argc, char **argv)
+{
+ size_t size;
+ int fd;
+
+ if (argc < 2) {
+ goto usage;
+ }
+
+ fd = open(argv[1], O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ if (argc == 2) {
+ if (ioctl(fd, FIOC_GET_SIZE, &size)) {
+ perror("ioctl");
+ return 1;
+ }
+ printf("%zu\n", size);
+ } else {
+ char *endp;
+
+ size = strtoul(argv[2], &endp, 0);
+ if (endp == argv[2] || *endp != '\0')
+ goto usage;
+
+ if (ioctl(fd, FIOC_SET_SIZE, &size)) {
+ perror("ioctl");
+ return 1;
+ }
+ }
+ return 0;
+
+usage:
+ fprintf(stderr, usage);
+ return 1;
+}
diff --git a/include/fuse.h b/include/fuse.h
index 8d47bc5..2016ccc 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -31,6 +31,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
+#include <sys/uio.h>
#ifdef __cplusplus
extern "C" {
@@ -457,6 +458,22 @@ struct fuse_operations {
* Reserved flags, don't set
*/
unsigned int flag_reserved : 31;
+
+ /**
+ * Ioctl
+ *
+ * @flags will have FUSE_IOCTL_COMPAT set for 32bit ioctls in
+ * 64bit environment. The size and direction of @data is
+ * determined by _IOC_*() decoding of @cmd. For _IOC_NONE,
+ * @data will be NULL, for _IOC_WRITE @data is out area, for
+ * _IOC_READ in area and if both are set in/out area. In all
+ * non-NULL cases, the area is of _IOC_SIZE(@cmd) bytes.
+ *
+ * Introduced in version 2.9
+ */
+ int (*ioctl) (const char *, int cmd, void *arg,
+ struct fuse_file_info *, unsigned int flags, void *data);
+
};
/** Extra context that may be needed by some filesystems
@@ -689,6 +706,8 @@ int fuse_fs_removexattr(struct fuse_fs *fs, const char *path,
const char *name);
int fuse_fs_bmap(struct fuse_fs *fs, const char *path, size_t blocksize,
uint64_t *idx);
+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);
void fuse_fs_init(struct fuse_fs *fs, struct fuse_conn_info *conn);
void fuse_fs_destroy(struct fuse_fs *fs);
diff --git a/include/fuse_common.h b/include/fuse_common.h
index 9bbc386..fb18b61 100644
--- a/include/fuse_common.h
+++ b/include/fuse_common.h
@@ -94,6 +94,21 @@ struct fuse_file_info {
#define FUSE_CAP_BIG_WRITES (1 << 5)
/**
+ * Ioctl flags
+ *
+ * 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_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_MAX_IOV 256
+
+/**
* Connection information, passed to the ->init() method
*
* Some of the elements are read-write, these can be changed to
diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h
index b37d969..eb28a35 100644
--- a/include/fuse_kernel.h
+++ b/include/fuse_kernel.h
@@ -1,6 +1,6 @@
/*
This file defines the kernel interface of FUSE
- Copyright (C) 2001-2007 Miklos Szeredi <miklos@szeredi.hu>
+ Copyright (C) 2001-2008 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
@@ -46,36 +46,28 @@
*
* 7.10
* - add nonseekable open flag
+ *
+ * 7.11
+ * - add IOCTL message
*/
#ifndef _LINUX_FUSE_H
#define _LINUX_FUSE_H
-#ifndef linux
#include <sys/types.h>
#define __u64 uint64_t
#define __u32 uint32_t
#define __s32 int32_t
-#else
-#include <asm/types.h>
-#include <linux/major.h>
-#endif
/** Version number of this interface */
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 10
+#define FUSE_KERNEL_MINOR_VERSION 11
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
-/** The major number of the fuse character device */
-#define FUSE_MAJOR MISC_MAJOR
-
-/** The minor number of the fuse character device */
-#define FUSE_MINOR 229
-
/* Make sure all structures are padded to 64bit boundary, so 32bit
userspace works under 64bit kernels */
@@ -184,6 +176,21 @@ struct fuse_file_lock {
*/
#define FUSE_READ_LOCKOWNER (1 << 1)
+/**
+ * Ioctl flags
+ *
+ * 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_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_MAX_IOV 256
+
enum fuse_opcode {
FUSE_LOOKUP = 1,
FUSE_FORGET = 2, /* no reply */
@@ -221,6 +228,7 @@ enum fuse_opcode {
FUSE_INTERRUPT = 36,
FUSE_BMAP = 37,
FUSE_DESTROY = 38,
+ FUSE_IOCTL = 39,
};
/* The read buffer is required to be at least 8k, but may be much larger */
@@ -421,6 +429,22 @@ struct fuse_bmap_out {
__u64 block;
};
+struct fuse_ioctl_in {
+ __u64 fh;
+ __u32 flags;
+ __u32 cmd;
+ __u64 arg;
+ __u32 in_size;
+ __u32 out_size;
+};
+
+struct fuse_ioctl_out {
+ __s32 result;
+ __u32 flags;
+ __u32 in_iovs;
+ __u32 out_iovs;
+};
+
struct fuse_in_header {
__u32 len;
__u32 opcode;
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index 9330548..e17274f 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -807,6 +807,36 @@ struct fuse_lowlevel_ops {
*/
void (*bmap) (fuse_req_t req, fuse_ino_t ino, size_t blocksize,
uint64_t idx);
+
+ /**
+ * Ioctl
+ *
+ * Note: For unrestricted ioctls (not allowed for FUSE
+ * servers), data in and out areas can be discovered by giving
+ * iovs and setting FUSE_IOCTL_RETRY in *@flagsp. For
+ * restricted ioctls, kernel prepares in/out data area
+ * according to the information encoded in @cmd.
+ *
+ * Introduced in version 2.9
+ *
+ * Valid replies:
+ * fuse_reply_ioctl_retry
+ * fuse_reply_ioctl
+ * fuse_reply_err
+ *
+ * @param req request handle
+ * @param ino the inode number
+ * @param cmd ioctl command
+ * @param arg ioctl argument
+ * @param fi file information
+ * @param flagsp io/out parameter for FUSE_IOCTL_* flags
+ * @param in_buf data fetched from the caller
+ * @param in_size number of fetched bytes
+ * @param out_size maximum size of output data
+ */
+ void (*ioctl) (fuse_req_t req, fuse_ino_t ino, int cmd, void *arg,
+ struct fuse_file_info *fi, unsigned *flagsp,
+ const void *in_buf, size_t in_bufsz, size_t out_bufszp);
};
/**
@@ -1022,6 +1052,38 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
const char *name, const struct stat *stbuf,
off_t off);
+/**
+ * Reply to ask for data fetch and output buffer preparation. ioctl
+ * will be retried with the specified input data fetched and output
+ * buffer prepared.
+ *
+ * Possible requests:
+ * ioctl
+ *
+ * @param req request handle
+ * @param in_iov iovec specifying data to fetch from the caller
+ * @param in_count number of entries in @in_iov
+ * @param out_iov iovec specifying addresses to write output to
+ * @param out_count number of entries in @out_iov
+ * @return zero for success, -errno for failure to send reply
+ */
+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);
+
+/**
+ * Reply to finish ioctl
+ *
+ * Possible requests:
+ * ioctl
+ *
+ * @param req request handle
+ * @param result result to be passed to the caller
+ * @param buf buffer containing output data
+ * @param size length of output data
+ */
+int fuse_reply_ioctl(fuse_req_t req, int result, const void *buf, size_t size);
+
/* ----------------------------------------------------------- *
* Utility functions *
* ----------------------------------------------------------- */
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:
*;