Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion fs/fuse/cuse.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#include <linux/user_namespace.h>

#include "fuse_i.h"
#include "fuse_dev_i.h"

#define CUSE_CONNTBL_LEN 64

Expand Down Expand Up @@ -542,7 +543,7 @@ static int cuse_channel_open(struct inode *inode, struct file *file)
*/
static int cuse_channel_release(struct inode *inode, struct file *file)
{
struct fuse_dev *fud = file->private_data;
struct fuse_dev *fud = __fuse_get_dev(file);
struct cuse_conn *cc = fc_to_cc(fud->fc);

/* remove from the conntbl, no more access from this point on */
Expand Down
117 changes: 80 additions & 37 deletions fs/fuse/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1406,14 +1406,34 @@ static int fuse_dev_open(struct inode *inode, struct file *file)
return 0;
}

struct fuse_dev *fuse_get_dev(struct file *file)
{
struct fuse_dev *fud = __fuse_get_dev(file);
int err;

if (likely(fud))
return fud;

err = wait_event_interruptible(fuse_dev_waitq,
READ_ONCE(file->private_data) != FUSE_DEV_SYNC_INIT);
if (err)
return ERR_PTR(err);

fud = __fuse_get_dev(file);
if (!fud)
return ERR_PTR(-EPERM);

return fud;
}

static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to)
{
struct fuse_copy_state cs;
struct file *file = iocb->ki_filp;
struct fuse_dev *fud = fuse_get_dev(file);

if (!fud)
return -EPERM;
if (IS_ERR(fud))
return PTR_ERR(fud);

if (!user_backed_iter(to))
return -EINVAL;
Expand All @@ -1433,8 +1453,8 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
struct fuse_copy_state cs;
struct fuse_dev *fud = fuse_get_dev(in);

if (!fud)
return -EPERM;
if (IS_ERR(fud))
return PTR_ERR(fud);

bufs = kvmalloc_array(pipe->max_usage, sizeof(struct pipe_buffer),
GFP_KERNEL);
Expand Down Expand Up @@ -2015,7 +2035,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
{
struct fuse_copy_state cs;
struct fuse_dev *fud = fuse_get_dev(iocb->ki_filp);
struct fuse_dev *fud = __fuse_get_dev(iocb->ki_filp);

if (!fud)
return -EPERM;
Expand All @@ -2037,11 +2057,10 @@ static ssize_t fuse_dev_splice_write(struct pipe_inode_info *pipe,
unsigned idx;
struct pipe_buffer *bufs;
struct fuse_copy_state cs;
struct fuse_dev *fud;
struct fuse_dev *fud = __fuse_get_dev(out);
size_t rem;
ssize_t ret;

fud = fuse_get_dev(out);
if (!fud)
return -EPERM;

Expand Down Expand Up @@ -2128,7 +2147,7 @@ static __poll_t fuse_dev_poll(struct file *file, poll_table *wait)
struct fuse_iqueue *fiq;
struct fuse_dev *fud = fuse_get_dev(file);

if (!fud)
if (IS_ERR(fud))
return EPOLLERR;

fiq = &fud->fc->iq;
Expand Down Expand Up @@ -2311,7 +2330,7 @@ void fuse_wait_aborted(struct fuse_conn *fc)

int fuse_dev_release(struct inode *inode, struct file *file)
{
struct fuse_dev *fud = fuse_get_dev(file);
struct fuse_dev *fud = __fuse_get_dev(file);

if (fud) {
struct fuse_conn *fc = fud->fc;
Expand Down Expand Up @@ -2342,18 +2361,19 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
{
struct fuse_dev *fud = fuse_get_dev(file);

if (!fud)
return -EPERM;
if (IS_ERR(fud))
return PTR_ERR(fud);

/* No locking - fasync_helper does its own locking */
return fasync_helper(fd, file, on, &fud->fc->iq.fasync);
}


static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
{
struct fuse_dev *fud;

if (new->private_data)
if (__fuse_get_dev(new))
return -EINVAL;

fud = fuse_dev_alloc_install(fc);
Expand All @@ -2366,43 +2386,66 @@ static int fuse_device_clone(struct fuse_conn *fc, struct file *new)
return 0;
}

static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
static long fuse_dev_ioctl_clone(struct file *file, __u32 __user *argp)
{
int res;
int oldfd;
struct fuse_dev *fud = NULL;
struct fd f;

if (get_user(oldfd, argp))
return -EFAULT;

f = fdget(oldfd);
if (!f.file)
return -EINVAL;

/*
* Check against file->f_op because CUSE
* uses the same ioctl handler.
*/
if (f.file->f_op == file->f_op)
fud = __fuse_get_dev(f.file);

res = -EINVAL;
if (fud) {
mutex_lock(&fuse_mutex);
res = fuse_device_clone(fud->fc, file);
mutex_unlock(&fuse_mutex);
}

fdput(f);
return res;
}

static long fuse_dev_ioctl_sync_init(struct file *file)
{
int err = -EINVAL;

mutex_lock(&fuse_mutex);
if (!__fuse_get_dev(file)) {
WRITE_ONCE(file->private_data, FUSE_DEV_SYNC_INIT);
err = 0;
}
mutex_unlock(&fuse_mutex);
return err;
}

static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *argp = (void __user *)arg;

switch (cmd) {
case FUSE_DEV_IOC_CLONE:
if (get_user(oldfd, (__u32 __user *)arg))
return -EFAULT;
return fuse_dev_ioctl_clone(file, argp);

f = fdget(oldfd);
if (!f.file)
return -EINVAL;
case FUSE_DEV_IOC_SYNC_INIT:
return fuse_dev_ioctl_sync_init(file);

/*
* Check against file->f_op because CUSE
* uses the same ioctl handler.
*/
if (f.file->f_op == file->f_op)
fud = fuse_get_dev(f.file);

res = -EINVAL;
if (fud) {
mutex_lock(&fuse_mutex);
res = fuse_device_clone(fud->fc, file);
mutex_unlock(&fuse_mutex);
}
fdput(f);
break;
default:
res = -ENOTTY;
break;
return -ENOTTY;
}
return res;
}

const struct file_operations fuse_dev_operations = {
Expand Down
Loading
Loading