Commit 8e4f3e15 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull fuse updates from Miklos Szeredi:

 - Fixes for virtiofs submounts

 - Misc fixes and cleanups

* tag 'fuse-update-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  virtiofs: Fix spelling mistakes
  fuse: use DIV_ROUND_UP helper macro for calculations
  fuse: fix illegal access to inode with reused nodeid
  fuse: allow fallocate(FALLOC_FL_ZERO_RANGE)
  fuse: Make fuse_fill_super_submount() static
  fuse: Switch to fc_mount() for submounts
  fuse: Call vfs_get_tree() for submounts
  fuse: add dedicated filesystem context ops for submounts
  virtiofs: propagate sync() to file server
  fuse: reject internal errno
  fuse: check connected before queueing on fpq->io
  fuse: ignore PG_workingset after stealing
  fuse: Fix infinite loop in sget_fc()
  fuse: Fix crash if superblock of submount gets killed early
  fuse: Fix crash in fuse_dentry_automount() error path
parents 729437e3 c4e0cd4e
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -213,7 +213,7 @@ static int fuse_setup_one_mapping(struct inode *inode, unsigned long start_idx,
	dmap->writable = writable;
	if (!upgrade) {
		/*
		 * We don't take a refernce on inode. inode is valid right now
		 * We don't take a reference on inode. inode is valid right now
		 * and when inode is going away, cleanup logic should first
		 * cleanup dmap entries.
		 */
@@ -622,7 +622,7 @@ static int fuse_iomap_begin(struct inode *inode, loff_t pos, loff_t length,
	}

	/*
	 * If read beyond end of file happnes, fs code seems to return
	 * If read beyond end of file happens, fs code seems to return
	 * it as hole
	 */
iomap_hole:
@@ -1207,7 +1207,7 @@ static void fuse_dax_free_mem_worker(struct work_struct *work)
			 ret);
	}

	/* If number of free ranges are still below threhold, requeue */
	/* If number of free ranges are still below threshold, requeue */
	kick_dmap_free_worker(fcd, 1);
}

+12 −2
Original line number Diff line number Diff line
@@ -91,7 +91,7 @@ static void fuse_drop_waiting(struct fuse_conn *fc)
{
	/*
	 * lockess check of fc->connected is okay, because atomic_dec_and_test()
	 * provides a memory barrier mached with the one in fuse_wait_aborted()
	 * provides a memory barrier matched with the one in fuse_wait_aborted()
	 * to ensure no wake-up is missed.
	 */
	if (atomic_dec_and_test(&fc->num_waiting) &&
@@ -783,6 +783,7 @@ static int fuse_check_page(struct page *page)
	       1 << PG_uptodate |
	       1 << PG_lru |
	       1 << PG_active |
	       1 << PG_workingset |
	       1 << PG_reclaim |
	       1 << PG_waiters))) {
		dump_page(page, "fuse: trying to steal weird page");
@@ -1271,6 +1272,15 @@ static ssize_t fuse_dev_do_read(struct fuse_dev *fud, struct file *file,
		goto restart;
	}
	spin_lock(&fpq->lock);
	/*
	 *  Must not put request on fpq->io queue after having been shut down by
	 *  fuse_abort_conn()
	 */
	if (!fpq->connected) {
		req->out.h.error = err = -ECONNABORTED;
		goto out_end;

	}
	list_add(&req->list, &fpq->io);
	spin_unlock(&fpq->lock);
	cs->req = req;
@@ -1857,7 +1867,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
	}

	err = -EINVAL;
	if (oh.error <= -1000 || oh.error > 0)
	if (oh.error <= -512 || oh.error > 0)
		goto copy_finish;

	spin_lock(&fpq->lock);
+9 −54
Original line number Diff line number Diff line
@@ -252,7 +252,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
		if (ret == -ENOMEM)
			goto out;
		if (ret || fuse_invalid_attr(&outarg.attr) ||
		    inode_wrong_type(inode, outarg.attr.mode))
		    fuse_stale_inode(inode, outarg.generation, &outarg.attr))
			goto invalid;

		forget_all_cached_acls(inode);
@@ -309,68 +309,23 @@ static int fuse_dentry_delete(const struct dentry *dentry)
static struct vfsmount *fuse_dentry_automount(struct path *path)
{
	struct fs_context *fsc;
	struct fuse_mount *parent_fm = get_fuse_mount_super(path->mnt->mnt_sb);
	struct fuse_conn *fc = parent_fm->fc;
	struct fuse_mount *fm;
	struct vfsmount *mnt;
	struct fuse_inode *mp_fi = get_fuse_inode(d_inode(path->dentry));
	struct super_block *sb;
	int err;

	fsc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry);
	if (IS_ERR(fsc)) {
		err = PTR_ERR(fsc);
		goto out;
	}

	err = -ENOMEM;
	fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
	if (!fm)
		goto out_put_fsc;

	fsc->s_fs_info = fm;
	sb = sget_fc(fsc, NULL, set_anon_super_fc);
	if (IS_ERR(sb)) {
		err = PTR_ERR(sb);
		kfree(fm);
		goto out_put_fsc;
	}
	fm->fc = fuse_conn_get(fc);

	/* Initialize superblock, making @mp_fi its root */
	err = fuse_fill_super_submount(sb, mp_fi);
	if (err)
		goto out_put_sb;
	if (IS_ERR(fsc))
		return ERR_CAST(fsc);

	sb->s_flags |= SB_ACTIVE;
	fsc->root = dget(sb->s_root);
	/* We are done configuring the superblock, so unlock it */
	up_write(&sb->s_umount);

	down_write(&fc->killsb);
	list_add_tail(&fm->fc_entry, &fc->mounts);
	up_write(&fc->killsb);
	/* Pass the FUSE inode of the mount for fuse_get_tree_submount() */
	fsc->fs_private = mp_fi;

	/* Create the submount */
	mnt = vfs_create_mount(fsc);
	if (IS_ERR(mnt)) {
		err = PTR_ERR(mnt);
		goto out_put_fsc;
	}
	mnt = fc_mount(fsc);
	if (!IS_ERR(mnt))
		mntget(mnt);
	put_fs_context(fsc);
	return mnt;

out_put_sb:
	/*
	 * Only jump here when fsc->root is NULL and sb is still locked
	 * (otherwise put_fs_context() will put the superblock)
	 */
	deactivate_locked_super(sb);
out_put_fsc:
	put_fs_context(fsc);
out:
	return ERR_PTR(err);
	return mnt;
}

const struct dentry_operations fuse_dentry_operations = {
+8 −6
Original line number Diff line number Diff line
@@ -645,7 +645,7 @@ static ssize_t fuse_get_res_by_io(struct fuse_io_priv *io)
 * == bytes_transferred or rw == WRITE, the caller sets 'pos' to -1.
 *
 * An example:
 * User requested DIO read of 64K. It was splitted into two 32K fuse requests,
 * User requested DIO read of 64K. It was split into two 32K fuse requests,
 * both submitted asynchronously. The first of them was ACKed by userspace as
 * fully completed (req->out.args[0].size == 32K) resulting in pos == -1. The
 * second request was ACKed as short, e.g. only 1K was read, resulting in
@@ -1403,7 +1403,7 @@ static int fuse_get_user_pages(struct fuse_args_pages *ap, struct iov_iter *ii,
		nbytes += ret;

		ret += start;
		npages = (ret + PAGE_SIZE - 1) / PAGE_SIZE;
		npages = DIV_ROUND_UP(ret, PAGE_SIZE);

		ap->descs[ap->num_pages].offset = start;
		fuse_page_descs_length_init(ap->descs, ap->num_pages, npages);
@@ -2905,11 +2905,13 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
	};
	int err;
	bool lock_inode = !(mode & FALLOC_FL_KEEP_SIZE) ||
			   (mode & FALLOC_FL_PUNCH_HOLE);
			   (mode & (FALLOC_FL_PUNCH_HOLE |
				    FALLOC_FL_ZERO_RANGE));

	bool block_faults = FUSE_IS_DAX(inode) && lock_inode;

	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE))
	if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE |
		     FALLOC_FL_ZERO_RANGE))
		return -EOPNOTSUPP;

	if (fm->fc->no_fallocate)
@@ -2924,7 +2926,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
				goto out;
		}

		if (mode & FALLOC_FL_PUNCH_HOLE) {
		if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE)) {
			loff_t endbyte = offset + length - 1;

			err = fuse_writeback_range(inode, offset, endbyte);
@@ -2964,7 +2966,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
			file_update_time(file);
	}

	if (mode & FALLOC_FL_PUNCH_HOLE)
	if (mode & (FALLOC_FL_PUNCH_HOLE | FALLOC_FL_ZERO_RANGE))
		truncate_pagecache_range(inode, offset, offset + length - 1);

	fuse_invalidate_attr(inode);
+15 −9
Original line number Diff line number Diff line
@@ -761,6 +761,9 @@ struct fuse_conn {
	/* Auto-mount submounts announced by the server */
	unsigned int auto_submounts:1;

	/* Propagate syncfs() to server */
	unsigned int sync_fs:1;

	/** The number of requests waiting for completion */
	atomic_t num_waiting;

@@ -867,6 +870,13 @@ static inline u64 fuse_get_attr_version(struct fuse_conn *fc)
	return atomic64_read(&fc->attr_version);
}

static inline bool fuse_stale_inode(const struct inode *inode, int generation,
				    struct fuse_attr *attr)
{
	return inode->i_generation != generation ||
		inode_wrong_type(inode, attr->mode);
}

static inline void fuse_make_bad(struct inode *inode)
{
	remove_inode_hash(inode);
@@ -1081,15 +1091,6 @@ void fuse_send_init(struct fuse_mount *fm);
 */
int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx);

/*
 * Fill in superblock for submounts
 * @sb: partially-initialized superblock to fill in
 * @parent_fi: The fuse_inode of the parent filesystem where this submount is
 * 	       mounted
 */
int fuse_fill_super_submount(struct super_block *sb,
			     struct fuse_inode *parent_fi);

/*
 * Remove the mount from the connection
 *
@@ -1097,6 +1098,11 @@ int fuse_fill_super_submount(struct super_block *sb,
 */
bool fuse_mount_remove(struct fuse_mount *fm);

/*
 * Setup context ops for submounts
 */
int fuse_init_fs_context_submount(struct fs_context *fsc);

/*
 * Shut down the connection (possibly sending DESTROY request).
 */
Loading