Commit dfb84c33 authored by Miklos Szeredi's avatar Miklos Szeredi
Browse files

fuse: allow synchronous FUSE_INIT



FUSE_INIT has always been asynchronous with mount.  That means that the
server processed this request after the mount syscall returned.

This means that FUSE_INIT can't supply the root inode's ID, hence it
currently has a hardcoded value.  There are other limitations such as not
being able to perform getxattr during mount, which is needed by selinux.

To remove these limitations allow server to process FUSE_INIT while
initializing the in-core super block for the fuse filesystem.  This can
only be done if the server is prepared to handle this, so add
FUSE_DEV_IOC_SYNC_INIT ioctl, which

 a) lets the server know whether this feature is supported, returning
 ENOTTY othewrwise.

 b) lets the kernel know to perform a synchronous initialization

The implementation is slightly tricky, since fuse_dev/fuse_conn are set up
only during super block creation.  This is solved by setting the private
data of the fuse device file to a special value ((struct fuse_dev *) 1) and
waiting for this to be turned into a proper fuse_dev before commecing with
operations on the device file.

Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 3ca1b311
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@
#include <linux/user_namespace.h>

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

#define CUSE_CONNTBL_LEN	64

@@ -547,7 +548,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 */
+53 −18
Original line number Diff line number Diff line
@@ -1530,14 +1530,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;
@@ -1557,8 +1577,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);
@@ -2231,7 +2251,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;
@@ -2253,11 +2273,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;

@@ -2343,7 +2362,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;
@@ -2490,7 +2509,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;
@@ -2521,8 +2540,8 @@ 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);
@@ -2532,7 +2551,7 @@ 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);
@@ -2563,7 +2582,7 @@ static long fuse_dev_ioctl_clone(struct file *file, __u32 __user *argp)
	 * uses the same ioctl handler.
	 */
	if (fd_file(f)->f_op == file->f_op)
		fud = fuse_get_dev(fd_file(f));
		fud = __fuse_get_dev(fd_file(f));

	res = -EINVAL;
	if (fud) {
@@ -2581,8 +2600,8 @@ static long fuse_dev_ioctl_backing_open(struct file *file,
	struct fuse_dev *fud = fuse_get_dev(file);
	struct fuse_backing_map map;

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

	if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
		return -EOPNOTSUPP;
@@ -2598,8 +2617,8 @@ static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user *argp)
	struct fuse_dev *fud = fuse_get_dev(file);
	int backing_id;

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

	if (!IS_ENABLED(CONFIG_FUSE_PASSTHROUGH))
		return -EOPNOTSUPP;
@@ -2610,6 +2629,19 @@ static long fuse_dev_ioctl_backing_close(struct file *file, __u32 __user *argp)
	return fuse_backing_close(fud->fc, backing_id);
}

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)
{
@@ -2625,6 +2657,9 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
	case FUSE_DEV_IOC_BACKING_CLOSE:
		return fuse_dev_ioctl_backing_close(file, argp);

	case FUSE_DEV_IOC_SYNC_INIT:
		return fuse_dev_ioctl_sync_init(file);

	default:
		return -ENOTTY;
	}
@@ -2633,7 +2668,7 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd,
#ifdef CONFIG_PROC_FS
static void fuse_dev_show_fdinfo(struct seq_file *seq, struct file *file)
{
	struct fuse_dev *fud = fuse_get_dev(file);
	struct fuse_dev *fud = __fuse_get_dev(file);
	if (!fud)
		return;

+2 −2
Original line number Diff line number Diff line
@@ -1139,9 +1139,9 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags)
		return -EINVAL;

	fud = fuse_get_dev(cmd->file);
	if (!fud) {
	if (IS_ERR(fud)) {
		pr_info_ratelimited("No fuse device found\n");
		return -ENOTCONN;
		return PTR_ERR(fud);
	}
	fc = fud->fc;

+11 −2
Original line number Diff line number Diff line
@@ -12,6 +12,8 @@
#define FUSE_INT_REQ_BIT (1ULL << 0)
#define FUSE_REQ_ID_STEP (1ULL << 1)

extern struct wait_queue_head fuse_dev_waitq;

struct fuse_arg;
struct fuse_args;
struct fuse_pqueue;
@@ -37,15 +39,22 @@ struct fuse_copy_state {
	} ring;
};

static inline struct fuse_dev *fuse_get_dev(struct file *file)
#define FUSE_DEV_SYNC_INIT ((struct fuse_dev *) 1)
#define FUSE_DEV_PTR_MASK (~1UL)

static inline struct fuse_dev *__fuse_get_dev(struct file *file)
{
	/*
	 * Lockless access is OK, because file->private data is set
	 * once during mount and is valid until the file is released.
	 */
	return READ_ONCE(file->private_data);
	struct fuse_dev *fud = READ_ONCE(file->private_data);

	return (typeof(fud)) ((unsigned long) fud & FUSE_DEV_PTR_MASK);
}

struct fuse_dev *fuse_get_dev(struct file *file);

unsigned int fuse_req_hash(u64 unique);
struct fuse_req *fuse_request_find(struct fuse_pqueue *fpq, u64 unique);

+4 −1
Original line number Diff line number Diff line
@@ -904,6 +904,9 @@ struct fuse_conn {
	/* Is link not implemented by fs? */
	unsigned int no_link:1;

	/* Is synchronous FUSE_INIT allowed? */
	unsigned int sync_init:1;

	/* Use io_uring for communication */
	unsigned int io_uring;

@@ -1318,7 +1321,7 @@ struct fuse_dev *fuse_dev_alloc_install(struct fuse_conn *fc);
struct fuse_dev *fuse_dev_alloc(void);
void fuse_dev_install(struct fuse_dev *fud, struct fuse_conn *fc);
void fuse_dev_free(struct fuse_dev *fud);
void fuse_send_init(struct fuse_mount *fm);
int fuse_send_init(struct fuse_mount *fm);

/**
 * Fill in superblock and initialize fuse connection
Loading