Commit b2d86c7c authored by Jens Axboe's avatar Jens Axboe
Browse files

Merge branch 'work.namei' of...

Merge branch 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs into for-5.12/io_uring

Merge RESOLVE_CACHED bits from Al, as the io_uring changes will build on
top of that.

* 'work.namei' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  fs: expose LOOKUP_CACHED through openat2() RESOLVE_CACHED
  fs: add support for LOOKUP_CACHED
  saner calling conventions for unlazy_child()
  fs: make unlazy_walk() error handling consistent
  fs/namei.c: Remove unlikely of status being -ECHILD in lookup_fast()
  do_tmpfile(): don't mess with finish_open()
parents 1048ba83 99668f61
Loading
Loading
Loading
Loading
+43 −45
Original line number Diff line number Diff line
@@ -669,23 +669,25 @@ static bool legitimize_root(struct nameidata *nd)
 */

/**
 * unlazy_walk - try to switch to ref-walk mode.
 * try_to_unlazy - try to switch to ref-walk mode.
 * @nd: nameidata pathwalk data
 * Returns: 0 on success, -ECHILD on failure
 * Returns: true on success, false on failure
 *
 * unlazy_walk attempts to legitimize the current nd->path and nd->root
 * try_to_unlazy attempts to legitimize the current nd->path and nd->root
 * for ref-walk mode.
 * Must be called from rcu-walk context.
 * Nothing should touch nameidata between unlazy_walk() failure and
 * Nothing should touch nameidata between try_to_unlazy() failure and
 * terminate_walk().
 */
static int unlazy_walk(struct nameidata *nd)
static bool try_to_unlazy(struct nameidata *nd)
{
	struct dentry *parent = nd->path.dentry;

	BUG_ON(!(nd->flags & LOOKUP_RCU));

	nd->flags &= ~LOOKUP_RCU;
	if (nd->flags & LOOKUP_CACHED)
		goto out1;
	if (unlikely(!legitimize_links(nd)))
		goto out1;
	if (unlikely(!legitimize_path(nd, &nd->path, nd->seq)))
@@ -694,34 +696,36 @@ static int unlazy_walk(struct nameidata *nd)
		goto out;
	rcu_read_unlock();
	BUG_ON(nd->inode != parent->d_inode);
	return 0;
	return true;

out1:
	nd->path.mnt = NULL;
	nd->path.dentry = NULL;
out:
	rcu_read_unlock();
	return -ECHILD;
	return false;
}

/**
 * unlazy_child - try to switch to ref-walk mode.
 * try_to_unlazy_next - try to switch to ref-walk mode.
 * @nd: nameidata pathwalk data
 * @dentry: child of nd->path.dentry
 * @seq: seq number to check dentry against
 * Returns: 0 on success, -ECHILD on failure
 * @dentry: next dentry to step into
 * @seq: seq number to check @dentry against
 * Returns: true on success, false on failure
 *
 * unlazy_child attempts to legitimize the current nd->path, nd->root and dentry
 * for ref-walk mode.  @dentry must be a path found by a do_lookup call on
 * @nd.  Must be called from rcu-walk context.
 * Nothing should touch nameidata between unlazy_child() failure and
 * Similar to to try_to_unlazy(), but here we have the next dentry already
 * picked by rcu-walk and want to legitimize that in addition to the current
 * nd->path and nd->root for ref-walk mode.  Must be called from rcu-walk context.
 * Nothing should touch nameidata between try_to_unlazy_next() failure and
 * terminate_walk().
 */
static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned seq)
static bool try_to_unlazy_next(struct nameidata *nd, struct dentry *dentry, unsigned seq)
{
	BUG_ON(!(nd->flags & LOOKUP_RCU));

	nd->flags &= ~LOOKUP_RCU;
	if (nd->flags & LOOKUP_CACHED)
		goto out2;
	if (unlikely(!legitimize_links(nd)))
		goto out2;
	if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq)))
@@ -747,7 +751,7 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se
	if (unlikely(!legitimize_root(nd)))
		goto out_dput;
	rcu_read_unlock();
	return 0;
	return true;

out2:
	nd->path.mnt = NULL;
@@ -755,11 +759,11 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se
	nd->path.dentry = NULL;
out:
	rcu_read_unlock();
	return -ECHILD;
	return false;
out_dput:
	rcu_read_unlock();
	dput(dentry);
	return -ECHILD;
	return false;
}

static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
@@ -792,7 +796,8 @@ static int complete_walk(struct nameidata *nd)
		 */
		if (!(nd->flags & (LOOKUP_ROOT | LOOKUP_IS_SCOPED)))
			nd->root.mnt = NULL;
		if (unlikely(unlazy_walk(nd)))
		nd->flags &= ~LOOKUP_CACHED;
		if (!try_to_unlazy(nd))
			return -ECHILD;
	}

@@ -1372,7 +1377,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
			return -ENOENT;
		if (likely(__follow_mount_rcu(nd, path, inode, seqp)))
			return 0;
		if (unlazy_child(nd, dentry, seq))
		if (!try_to_unlazy_next(nd, dentry, seq))
			return -ECHILD;
		// *path might've been clobbered by __follow_mount_rcu()
		path->mnt = nd->path.mnt;
@@ -1466,7 +1471,7 @@ static struct dentry *lookup_fast(struct nameidata *nd,
		unsigned seq;
		dentry = __d_lookup_rcu(parent, &nd->last, &seq);
		if (unlikely(!dentry)) {
			if (unlazy_walk(nd))
			if (!try_to_unlazy(nd))
				return ERR_PTR(-ECHILD);
			return NULL;
		}
@@ -1493,9 +1498,9 @@ static struct dentry *lookup_fast(struct nameidata *nd,
		status = d_revalidate(dentry, nd->flags);
		if (likely(status > 0))
			return dentry;
		if (unlazy_child(nd, dentry, seq))
		if (!try_to_unlazy_next(nd, dentry, seq))
			return ERR_PTR(-ECHILD);
		if (unlikely(status == -ECHILD))
		if (status == -ECHILD)
			/* we'd been told to redo it in non-rcu mode */
			status = d_revalidate(dentry, nd->flags);
	} else {
@@ -1567,10 +1572,8 @@ static inline int may_lookup(struct nameidata *nd)
{
	if (nd->flags & LOOKUP_RCU) {
		int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
		if (err != -ECHILD)
		if (err != -ECHILD || !try_to_unlazy(nd))
			return err;
		if (unlazy_walk(nd))
			return -ECHILD;
	}
	return inode_permission(nd->inode, MAY_EXEC);
}
@@ -1592,7 +1595,7 @@ static int reserve_stack(struct nameidata *nd, struct path *link, unsigned seq)
		// unlazy even if we fail to grab the link - cleanup needs it
		bool grabbed_link = legitimize_path(nd, link, seq);

		if (unlazy_walk(nd) != 0 || !grabbed_link)
		if (!try_to_unlazy(nd) != 0 || !grabbed_link)
			return -ECHILD;

		if (nd_alloc_stack(nd))
@@ -1634,7 +1637,7 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
		touch_atime(&last->link);
		cond_resched();
	} else if (atime_needs_update(&last->link, inode)) {
		if (unlikely(unlazy_walk(nd)))
		if (!try_to_unlazy(nd))
			return ERR_PTR(-ECHILD);
		touch_atime(&last->link);
	}
@@ -1651,11 +1654,8 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
		get = inode->i_op->get_link;
		if (nd->flags & LOOKUP_RCU) {
			res = get(NULL, inode, &last->done);
			if (res == ERR_PTR(-ECHILD)) {
				if (unlikely(unlazy_walk(nd)))
					return ERR_PTR(-ECHILD);
			if (res == ERR_PTR(-ECHILD) && try_to_unlazy(nd))
				res = get(link->dentry, inode, &last->done);
			}
		} else {
			res = get(link->dentry, inode, &last->done);
		}
@@ -2195,7 +2195,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
		}
		if (unlikely(!d_can_lookup(nd->path.dentry))) {
			if (nd->flags & LOOKUP_RCU) {
				if (unlazy_walk(nd))
				if (!try_to_unlazy(nd))
					return -ECHILD;
			}
			return -ENOTDIR;
@@ -2209,6 +2209,10 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
	int error;
	const char *s = nd->name->name;

	/* LOOKUP_CACHED requires RCU, ask caller to retry */
	if ((flags & (LOOKUP_RCU | LOOKUP_CACHED)) == LOOKUP_CACHED)
		return ERR_PTR(-EAGAIN);

	if (!*s)
		flags &= ~LOOKUP_RCU;
	if (flags & LOOKUP_RCU)
@@ -3129,7 +3133,6 @@ static const char *open_last_lookups(struct nameidata *nd,
	struct inode *inode;
	struct dentry *dentry;
	const char *res;
	int error;

	nd->flags |= op->intent;

@@ -3153,9 +3156,8 @@ static const char *open_last_lookups(struct nameidata *nd,
	} else {
		/* create side of things */
		if (nd->flags & LOOKUP_RCU) {
			error = unlazy_walk(nd);
			if (unlikely(error))
				return ERR_PTR(error);
			if (!try_to_unlazy(nd))
				return ERR_PTR(-ECHILD);
		}
		audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
		/* trailing slashes? */
@@ -3164,9 +3166,7 @@ static const char *open_last_lookups(struct nameidata *nd,
	}

	if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
		error = mnt_want_write(nd->path.mnt);
		if (!error)
			got_write = true;
		got_write = !mnt_want_write(nd->path.mnt);
		/*
		 * do _not_ fail yet - we might not need that or fail with
		 * a different error; let lookup_open() decide; we'll be
@@ -3325,10 +3325,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
	audit_inode(nd->name, child, 0);
	/* Don't check for other permissions, the inode was just created */
	error = may_open(&path, 0, op->open_flag);
	if (error)
		goto out2;
	file->f_path.mnt = path.mnt;
	error = finish_open(file, child, NULL);
	if (!error)
		error = vfs_open(&path, file);
out2:
	mnt_drop_write(path.mnt);
out:
+6 −0
Original line number Diff line number Diff line
@@ -1091,6 +1091,12 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
		lookup_flags |= LOOKUP_BENEATH;
	if (how->resolve & RESOLVE_IN_ROOT)
		lookup_flags |= LOOKUP_IN_ROOT;
	if (how->resolve & RESOLVE_CACHED) {
		/* Don't bother even trying for create/truncate/tmpfile open */
		if (flags & (O_TRUNC | O_CREAT | O_TMPFILE))
			return -EAGAIN;
		lookup_flags |= LOOKUP_CACHED;
	}

	op->lookup_flags = lookup_flags;
	return 0;
+1 −1
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@
/* List of all valid flags for the how->resolve argument: */
#define VALID_RESOLVE_FLAGS \
	(RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \
	 RESOLVE_BENEATH | RESOLVE_IN_ROOT)
	 RESOLVE_BENEATH | RESOLVE_IN_ROOT | RESOLVE_CACHED)

/* List of all open_how "versions". */
#define OPEN_HOW_SIZE_VER0	24 /* sizeof first published struct */
+1 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
#define LOOKUP_NO_XDEV		0x040000 /* No mountpoint crossing. */
#define LOOKUP_BENEATH		0x080000 /* No escaping from starting point. */
#define LOOKUP_IN_ROOT		0x100000 /* Treat dirfd as fs root. */
#define LOOKUP_CACHED		0x200000 /* Only do cached lookup */
/* LOOKUP_* flags which do scope-related checks based on the dirfd. */
#define LOOKUP_IS_SCOPED (LOOKUP_BENEATH | LOOKUP_IN_ROOT)

+4 −0
Original line number Diff line number Diff line
@@ -35,5 +35,9 @@ struct open_how {
#define RESOLVE_IN_ROOT		0x10 /* Make all jumps to "/" and ".."
					be scoped inside the dirfd
					(similar to chroot(2)). */
#define RESOLVE_CACHED		0x20 /* Only complete if resolution can be
					completed through cached lookup. May
					return -EAGAIN if that's not
					possible. */

#endif /* _UAPI_LINUX_OPENAT2_H */