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

Commit 16c2cd71 authored by Al Viro's avatar Al Viro
Browse files

untangle the "need_reval_dot" mess



instead of ad-hackery around need_reval_dot(), do the following:
set a flag (LOOKUP_JUMPED) in the beginning of path, on absolute
symlink traversal, on ".." and on procfs-style symlinks.  Clear on
normal components, leave unchanged on ".".  Non-nested callers of
link_path_walk() call handle_reval_path(), which checks that flag
is set and that fs does want the final revalidate thing, then does
->d_revalidate().  In link_path_walk() all the return_reval stuff
is gone.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent fe479a58
Loading
Loading
Loading
Loading
+44 −63
Original line number Diff line number Diff line
@@ -613,19 +613,8 @@ do_revalidate_rcu(struct dentry *dentry, struct nameidata *nd)
	return dentry;
}

static inline int need_reval_dot(struct dentry *dentry)
{
	if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE)))
		return 0;

	if (likely(!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)))
		return 0;

	return 1;
}

/*
 * force_reval_path - force revalidation of a dentry
 * handle_reval_path - force revalidation of a dentry
 *
 * In some situations the path walking code will trust dentries without
 * revalidating them. This causes problems for filesystems that depend on
@@ -639,27 +628,28 @@ static inline int need_reval_dot(struct dentry *dentry)
 * invalidate the dentry. It's up to the caller to handle putting references
 * to the path if necessary.
 */
static int
force_reval_path(struct path *path, struct nameidata *nd)
static inline int handle_reval_path(struct nameidata *nd)
{
	struct dentry *dentry = nd->path.dentry;
	int status;
	struct dentry *dentry = path->dentry;

	/*
	 * only check on filesystems where it's possible for the dentry to
	 * become stale.
	 */
	if (!need_reval_dot(dentry))
	if (likely(!(nd->flags & LOOKUP_JUMPED)))
		return 0;

	if (likely(!(dentry->d_flags & DCACHE_OP_REVALIDATE)))
		return 0;

	if (likely(!(dentry->d_sb->s_type->fs_flags & FS_REVAL_DOT)))
		return 0;

	/* Note: we do not d_invalidate() */
	status = d_revalidate(dentry, nd);
	if (status > 0)
		return 0;

	if (!status) {
		d_invalidate(dentry);
	if (!status)
		status = -ESTALE;
	}

	return status;
}

@@ -728,6 +718,7 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
		path_put(&nd->path);
		nd->path = nd->root;
		path_get(&nd->root);
		nd->flags |= LOOKUP_JUMPED;
	}
	nd->inode = nd->path.dentry->d_inode;

@@ -779,11 +770,8 @@ __do_follow_link(const struct path *link, struct nameidata *nd, void **p)
		error = 0;
		if (s)
			error = __vfs_follow_link(nd, s);
		else if (nd->last_type == LAST_BIND) {
			error = force_reval_path(&nd->path, nd);
			if (error)
				path_put(&nd->path);
		}
		else if (nd->last_type == LAST_BIND)
			nd->flags |= LOOKUP_JUMPED;
	}
	return error;
}
@@ -1351,7 +1339,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
	while (*name=='/')
		name++;
	if (!*name)
		goto return_reval;
		goto return_base;

	if (nd->depth)
		lookup_flags = LOOKUP_FOLLOW | (nd->flags & LOOKUP_CONTINUE);
@@ -1385,12 +1373,16 @@ static int link_path_walk(const char *name, struct nameidata *nd)
		type = LAST_NORM;
		if (this.name[0] == '.') switch (this.len) {
			case 2:
				if (this.name[1] == '.')
				if (this.name[1] == '.') {
					type = LAST_DOTDOT;
					nd->flags |= LOOKUP_JUMPED;
				}
				break;
			case 1:
				type = LAST_DOT;
		}
		if (likely(type == LAST_NORM))
			nd->flags &= ~LOOKUP_JUMPED;

		/* remove trailing slashes? */
		if (!c)
@@ -1456,7 +1448,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
				} else
					follow_dotdot(nd);
			}
			goto return_reval;
			goto return_base;
		}
		err = do_lookup(nd, &this, &next, &inode);
		if (err)
@@ -1483,24 +1475,6 @@ static int link_path_walk(const char *name, struct nameidata *nd)
lookup_parent:
		nd->last = this;
		nd->last_type = type;
		if (type == LAST_NORM)
			goto return_base;
return_reval:
		/*
		 * We bypassed the ordinary revalidation routines.
		 * We may need to check the cached dentry for staleness.
		 */
		if (need_reval_dot(nd->path.dentry)) {
			if (nameidata_drop_rcu_last_maybe(nd))
				return -ECHILD;
			/* Note: we do not d_invalidate() */
			err = d_revalidate(nd->path.dentry, nd);
			if (!err)
				err = -ESTALE;
			if (err < 0)
				break;
			return 0;
		}
return_base:
		if (nameidata_drop_rcu_last_maybe(nd))
			return -ECHILD;
@@ -1523,7 +1497,7 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei
	struct file *file;

	nd->last_type = LAST_ROOT; /* if there are only slashes... */
	nd->flags = flags;
	nd->flags = flags | LOOKUP_JUMPED;
	nd->depth = 0;
	nd->root.mnt = NULL;
	nd->file = NULL;
@@ -1630,6 +1604,9 @@ static int path_lookupat(int dfd, const char *name,
		br_read_unlock(vfsmount_lock);
	}

	if (!retval)
		retval = handle_reval_path(nd);

	if (nd->file) {
		fput(nd->file);
		nd->file = NULL;
@@ -1690,7 +1667,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,

	/* same as do_path_lookup */
	nd->last_type = LAST_ROOT;
	nd->flags = flags;
	nd->flags = flags | LOOKUP_JUMPED;
	nd->depth = 0;

	nd->path.dentry = dentry;
@@ -1703,6 +1680,8 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
	current->total_link_count = 0;

	result = link_path_walk(name, nd);
	if (!result)
		result = handle_reval_path(nd);
	if (result == -ESTALE) {
		/* nd->path had been dropped */
		current->total_link_count = 0;
@@ -1710,8 +1689,11 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
		nd->path.mnt = mnt;
		nd->inode = dentry->d_inode;
		path_get(&nd->path);
		nd->flags |= LOOKUP_REVAL;
		nd->flags = flags | LOOKUP_JUMPED | LOOKUP_REVAL;

		result = link_path_walk(name, nd);
		if (!result)
			result = handle_reval_path(nd);
	}
	if (unlikely(!result && !audit_dummy_context() && nd->path.dentry &&
				nd->inode))
@@ -2198,30 +2180,29 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
{
	struct dentry *dir = nd->path.dentry;
	struct file *filp;
	int error = -EISDIR;
	int error;

	switch (nd->last_type) {
	case LAST_DOTDOT:
		follow_dotdot(nd);
		dir = nd->path.dentry;
	case LAST_DOT:
		if (need_reval_dot(dir)) {
			int status = d_revalidate(nd->path.dentry, nd);
			if (!status)
				status = -ESTALE;
			if (status < 0) {
				error = status;
				goto exit;
			}
		}
		/* fallthrough */
	case LAST_ROOT:
		error = handle_reval_path(nd);
		if (error)
			goto exit;
		error = -EISDIR;
		goto exit;
	case LAST_BIND:
		error = handle_reval_path(nd);
		if (error)
			goto exit;
		audit_inode(pathname, dir);
		goto ok;
	}

	error = -EISDIR;
	/* trailing slashes? */
	if (nd->last.name[nd->last.len])
		goto exit;
@@ -2422,7 +2403,7 @@ struct file *do_filp_open(int dfd, const char *pathname,
	/*
	 * We have the parent and last component.
	 */
	nd.flags = flags;
	nd.flags = (nd.flags & ~LOOKUP_PARENT) | flags;
	filp = do_last(&nd, &path, open_flag, acc_mode, mode, pathname);
	while (unlikely(!filp)) { /* trailing symlink */
		struct path link = path;
+2 −0
Original line number Diff line number Diff line
@@ -63,6 +63,8 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
#define LOOKUP_EXCL		0x0400
#define LOOKUP_RENAME_TARGET	0x0800

#define LOOKUP_JUMPED		0x1000

extern int user_path_at(int, const char __user *, unsigned, struct path *);

#define user_path(name, path) user_path_at(AT_FDCWD, name, LOOKUP_FOLLOW, path)