aboutsummaryrefslogtreecommitdiff
path: root/fs
diff options
context:
space:
mode:
authorMiklos Szeredi2015-07-01 16:26:08 +0200
committerMiklos Szeredi2015-07-01 16:26:08 +0200
commit00c570f4ba43ae73b41fa0a2269c3b0ac20386ef (patch)
tree5dad39b5bf083a172c7ac544ae0121d7fab1eeb0 /fs
parentee314a870e402f4e63b8a651bc96c740ed69cb31 (diff)
fuse: device fd clone
Allow an open fuse device to be "cloned". Userspace can create a clone by: newfd = open("/dev/fuse", O_RDWR) ioctl(newfd, FUSE_DEV_IOC_CLONE, &oldfd); At this point newfd will refer to the same fuse connection as oldfd. Signed-off-by: Miklos Szeredi <mszeredi@suse.cz> Reviewed-by: Ashish Samant <ashish.samant@oracle.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/fuse/dev.c40
1 files changed, 40 insertions, 0 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index dcfc87172a47..d405c1fa4618 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -2197,6 +2197,44 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
return fasync_helper(fd, file, on, &fc->iq.fasync);
}
+static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
+{
+ if (new->private_data)
+ return -EINVAL;
+
+ new->private_data = fuse_conn_get(fc);
+
+ return 0;
+}
+
+static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int err = -ENOTTY;
+
+ if (cmd == FUSE_DEV_IOC_CLONE) {
+ int oldfd;
+
+ err = -EFAULT;
+ if (!get_user(oldfd, (__u32 __user *) arg)) {
+ struct file *old = fget(oldfd);
+
+ err = -EINVAL;
+ if (old) {
+ struct fuse_conn *fc = fuse_get_conn(old);
+
+ if (fc) {
+ mutex_lock(&fuse_mutex);
+ err = fuse_device_clone(fc, file);
+ mutex_unlock(&fuse_mutex);
+ }
+ fput(old);
+ }
+ }
+ }
+ return err;
+}
+
const struct file_operations fuse_dev_operations = {
.owner = THIS_MODULE,
.open = fuse_dev_open,
@@ -2208,6 +2246,8 @@ const struct file_operations fuse_dev_operations = {
.poll = fuse_dev_poll,
.release = fuse_dev_release,
.fasync = fuse_dev_fasync,
+ .unlocked_ioctl = fuse_dev_ioctl,
+ .compat_ioctl = fuse_dev_ioctl,
};
EXPORT_SYMBOL_GPL(fuse_dev_operations);