Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15c83d26 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull fuse bugfixes from Miklos Szeredi:
 "This contains two more fixes by Maxim for writeback/truncate races and
  fixes for RCU walk in fuse_dentry_revalidate()"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
  fuse: no RCU mode in fuse_access()
  fuse: readdirplus: fix RCU walk
  fuse: don't check_submounts_and_drop() in RCU walk
  fuse: fix fallocate vs. ftruncate race
  fuse: wait for writeback in fuse_file_fallocate()
parents 8e1a2540 698fa1d1
Loading
Loading
Loading
Loading
+13 −7
Original line number Diff line number Diff line
@@ -182,6 +182,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
	struct inode *inode;
	struct dentry *parent;
	struct fuse_conn *fc;
	struct fuse_inode *fi;
	int ret;

	inode = ACCESS_ONCE(entry->d_inode);
@@ -228,7 +229,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
		if (!err && !outarg.nodeid)
			err = -ENOENT;
		if (!err) {
			struct fuse_inode *fi = get_fuse_inode(inode);
			fi = get_fuse_inode(inode);
			if (outarg.nodeid != get_node_id(inode)) {
				fuse_queue_forget(fc, forget, outarg.nodeid, 1);
				goto invalid;
@@ -246,8 +247,11 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
				       attr_version);
		fuse_change_entry_timeout(entry, &outarg);
	} else if (inode) {
		fc = get_fuse_conn(inode);
		if (fc->readdirplus_auto) {
		fi = get_fuse_inode(inode);
		if (flags & LOOKUP_RCU) {
			if (test_bit(FUSE_I_INIT_RDPLUS, &fi->state))
				return -ECHILD;
		} else if (test_and_clear_bit(FUSE_I_INIT_RDPLUS, &fi->state)) {
			parent = dget_parent(entry);
			fuse_advise_use_readdirplus(parent->d_inode);
			dput(parent);
@@ -259,7 +263,8 @@ out:

invalid:
	ret = 0;
	if (check_submounts_and_drop(entry) != 0)

	if (!(flags & LOOKUP_RCU) && check_submounts_and_drop(entry) != 0)
		ret = 1;
	goto out;
}
@@ -1063,6 +1068,8 @@ static int fuse_access(struct inode *inode, int mask)
	struct fuse_access_in inarg;
	int err;

	BUG_ON(mask & MAY_NOT_BLOCK);

	if (fc->no_access)
		return 0;

@@ -1150,9 +1157,6 @@ static int fuse_permission(struct inode *inode, int mask)
		   noticed immediately, only after the attribute
		   timeout has expired */
	} else if (mask & (MAY_ACCESS | MAY_CHDIR)) {
		if (mask & MAY_NOT_BLOCK)
			return -ECHILD;

		err = fuse_access(inode, mask);
	} else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) {
		if (!(inode->i_mode & S_IXUGO)) {
@@ -1291,6 +1295,8 @@ static int fuse_direntplus_link(struct file *file,
	}

found:
	if (fc->readdirplus_auto)
		set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state);
	fuse_change_entry_timeout(dentry, o);

	err = 0;
+17 −6
Original line number Diff line number Diff line
@@ -2467,6 +2467,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
{
	struct fuse_file *ff = file->private_data;
	struct inode *inode = file->f_inode;
	struct fuse_inode *fi = get_fuse_inode(inode);
	struct fuse_conn *fc = ff->fc;
	struct fuse_req *req;
	struct fuse_fallocate_in inarg = {
@@ -2484,9 +2485,19 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,

	if (lock_inode) {
		mutex_lock(&inode->i_mutex);
		if (mode & FALLOC_FL_PUNCH_HOLE)
			fuse_set_nowrite(inode);
		if (mode & FALLOC_FL_PUNCH_HOLE) {
			loff_t endbyte = offset + length - 1;
			err = filemap_write_and_wait_range(inode->i_mapping,
							   offset, endbyte);
			if (err)
				goto out;

			fuse_sync_writes(inode);
		}
	}

	if (!(mode & FALLOC_FL_KEEP_SIZE))
		set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);

	req = fuse_get_req_nopages(fc);
	if (IS_ERR(req)) {
@@ -2520,11 +2531,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset,
	fuse_invalidate_attr(inode);

out:
	if (lock_inode) {
		if (mode & FALLOC_FL_PUNCH_HOLE)
			fuse_release_nowrite(inode);
	if (!(mode & FALLOC_FL_KEEP_SIZE))
		clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state);

	if (lock_inode)
		mutex_unlock(&inode->i_mutex);
	}

	return err;
}
+2 −0
Original line number Diff line number Diff line
@@ -115,6 +115,8 @@ struct fuse_inode {
enum {
	/** Advise readdirplus  */
	FUSE_I_ADVISE_RDPLUS,
	/** Initialized with readdirplus */
	FUSE_I_INIT_RDPLUS,
	/** An operation changing file size is in progress  */
	FUSE_I_SIZE_UNSTABLE,
};