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

Commit b8faf035 authored by NeilBrown's avatar NeilBrown Committed by Al Viro
Browse files

VFS: allow ->d_manage() to declare -EISDIR in rcu_walk mode.



In REF-walk mode, ->d_manage can return -EISDIR to indicate
that the dentry is not really a mount trap (or even a mount point)
and that any mounts or any DCACHE_NEED_AUTOMOUNT flag should be
ignored.

RCU-walk mode doesn't currently support this, so if there is a dentry
with DCACHE_NEED_AUTOMOUNT set but which shouldn't be a mount-trap,
lookup_fast() will always drop in REF-walk mode.

With this patch, an -EISDIR from ->d_manage will always cause mounts
and automounts to be ignored, both in REF-walk and RCU-walk.

Bug-fixed-by: default avatarDan Carpenter <dan.carpenter@oracle.com>
Cc: Ian Kent <raven@themaw.net>
Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 7c33d597
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1053,7 +1053,8 @@ struct dentry_operations {
	If the 'rcu_walk' parameter is true, then the caller is doing a
	pathwalk in RCU-walk mode.  Sleeping is not permitted in this mode,
	and the caller can be asked to leave it and call again by returning
	-ECHILD.
	-ECHILD.  -EISDIR may also be returned to tell pathwalk to
	ignore d_automount or any mounts.

	This function is only used if DCACHE_MANAGE_TRANSIT is set on the
	dentry being transited from.
+16 −11
Original line number Diff line number Diff line
@@ -1091,10 +1091,10 @@ int follow_down_one(struct path *path)
}
EXPORT_SYMBOL(follow_down_one);

static inline bool managed_dentry_might_block(struct dentry *dentry)
static inline int managed_dentry_rcu(struct dentry *dentry)
{
	return (dentry->d_flags & DCACHE_MANAGE_TRANSIT &&
		dentry->d_op->d_manage(dentry, true) < 0);
	return (dentry->d_flags & DCACHE_MANAGE_TRANSIT) ?
		dentry->d_op->d_manage(dentry, true) : 0;
}

/*
@@ -1110,11 +1110,18 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
		 * Don't forget we might have a non-mountpoint managed dentry
		 * that wants to block transit.
		 */
		if (unlikely(managed_dentry_might_block(path->dentry)))
		switch (managed_dentry_rcu(path->dentry)) {
		case -ECHILD:
		default:
			return false;
		case -EISDIR:
			return true;
		case 0:
			break;
		}

		if (!d_mountpoint(path->dentry))
			return true;
			return !(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);

		mounted = __lookup_mnt(path->mnt, path->dentry);
		if (!mounted)
@@ -1130,7 +1137,8 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
		 */
		*inode = path->dentry->d_inode;
	}
	return read_seqretry(&mount_lock, nd->m_seq);
	return read_seqretry(&mount_lock, nd->m_seq) &&
		!(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT);
}

static int follow_dotdot_rcu(struct nameidata *nd)
@@ -1402,10 +1410,7 @@ static int lookup_fast(struct nameidata *nd,
		}
		path->mnt = mnt;
		path->dentry = dentry;
		if (unlikely(!__follow_mount_rcu(nd, path, inode)))
			goto unlazy;
		if (unlikely(path->dentry->d_flags & DCACHE_NEED_AUTOMOUNT))
			goto unlazy;
		if (likely(__follow_mount_rcu(nd, path, inode)))
			return 0;
unlazy:
		if (unlazy_walk(nd, dentry))