Commit bcba1e7d authored by Al Viro's avatar Al Viro
Browse files

take LOOKUP_{ROOT,ROOT_GRABBED,JUMPED} out of LOOKUP_... space



Separate field in nameidata (nd->state) holding the flags that
should be internal-only - that way we both get some spare bits
in LOOKUP_... and get simpler rules for nd->root lifetime rules,
since we can set the replacement of LOOKUP_ROOT (ND_ROOT_PRESET)
at the same time we set nd->root.

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent ffb37ca3
Loading
Loading
Loading
Loading
+3 −3
Original line number Original line Diff line number Diff line
@@ -1321,18 +1321,18 @@ to lookup: RCU-walk, REF-walk, and REF-walk with forced revalidation.
yet.  This is primarily used to tell the audit subsystem the full
yet.  This is primarily used to tell the audit subsystem the full
context of a particular access being audited.
context of a particular access being audited.


``LOOKUP_ROOT`` indicates that the ``root`` field in the ``nameidata`` was
``ND_ROOT_PRESET`` indicates that the ``root`` field in the ``nameidata`` was
provided by the caller, so it shouldn't be released when it is no
provided by the caller, so it shouldn't be released when it is no
longer needed.
longer needed.


``LOOKUP_JUMPED`` means that the current dentry was chosen not because
``ND_JUMPED`` means that the current dentry was chosen not because
it had the right name but for some other reason.  This happens when
it had the right name but for some other reason.  This happens when
following "``..``", following a symlink to ``/``, crossing a mount point
following "``..``", following a symlink to ``/``, crossing a mount point
or accessing a "``/proc/$PID/fd/$FD``" symlink (also known as a "magic
or accessing a "``/proc/$PID/fd/$FD``" symlink (also known as a "magic
link"). In this case the filesystem has not been asked to revalidate the
link"). In this case the filesystem has not been asked to revalidate the
name (with ``d_revalidate()``).  In such cases the inode may still need
name (with ``d_revalidate()``).  In such cases the inode may still need
to be revalidated, so ``d_op->d_weak_revalidate()`` is called if
to be revalidated, so ``d_op->d_weak_revalidate()`` is called if
``LOOKUP_JUMPED`` is set when the look completes - which may be at the
``ND_JUMPED`` is set when the look completes - which may be at the
final component or, when creating, unlinking, or renaming, at the penultimate component.
final component or, when creating, unlinking, or renaming, at the penultimate component.


Resolution-restriction flags
Resolution-restriction flags
+31 −23
Original line number Original line Diff line number Diff line
@@ -554,7 +554,7 @@ struct nameidata {
	struct qstr	last;
	struct qstr	last;
	struct path	root;
	struct path	root;
	struct inode	*inode; /* path.dentry.d_inode */
	struct inode	*inode; /* path.dentry.d_inode */
	unsigned int	flags;
	unsigned int	flags, state;
	unsigned	seq, m_seq, r_seq;
	unsigned	seq, m_seq, r_seq;
	int		last_type;
	int		last_type;
	unsigned	depth;
	unsigned	depth;
@@ -573,6 +573,10 @@ struct nameidata {
	umode_t		dir_mode;
	umode_t		dir_mode;
} __randomize_layout;
} __randomize_layout;


#define ND_ROOT_PRESET 1
#define ND_ROOT_GRABBED 2
#define ND_JUMPED 4

static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
{
{
	struct nameidata *old = current->nameidata;
	struct nameidata *old = current->nameidata;
@@ -583,6 +587,7 @@ static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
	p->path.dentry = NULL;
	p->path.dentry = NULL;
	p->total_link_count = old ? old->total_link_count : 0;
	p->total_link_count = old ? old->total_link_count : 0;
	p->saved = old;
	p->saved = old;
	p->state = 0;
	current->nameidata = p;
	current->nameidata = p;
}
}


@@ -645,9 +650,9 @@ static void terminate_walk(struct nameidata *nd)
		path_put(&nd->path);
		path_put(&nd->path);
		for (i = 0; i < nd->depth; i++)
		for (i = 0; i < nd->depth; i++)
			path_put(&nd->stack[i].link);
			path_put(&nd->stack[i].link);
		if (nd->flags & LOOKUP_ROOT_GRABBED) {
		if (nd->state & ND_ROOT_GRABBED) {
			path_put(&nd->root);
			path_put(&nd->root);
			nd->flags &= ~LOOKUP_ROOT_GRABBED;
			nd->state &= ~ND_ROOT_GRABBED;
		}
		}
	} else {
	} else {
		nd->flags &= ~LOOKUP_RCU;
		nd->flags &= ~LOOKUP_RCU;
@@ -710,9 +715,9 @@ static bool legitimize_root(struct nameidata *nd)
	if (!nd->root.mnt && (nd->flags & LOOKUP_IS_SCOPED))
	if (!nd->root.mnt && (nd->flags & LOOKUP_IS_SCOPED))
		return false;
		return false;
	/* Nothing to do if nd->root is zero or is managed by the VFS user. */
	/* Nothing to do if nd->root is zero or is managed by the VFS user. */
	if (!nd->root.mnt || (nd->flags & LOOKUP_ROOT))
	if (!nd->root.mnt || (nd->state & ND_ROOT_PRESET))
		return true;
		return true;
	nd->flags |= LOOKUP_ROOT_GRABBED;
	nd->state |= ND_ROOT_GRABBED;
	return legitimize_path(nd, &nd->root, nd->root_seq);
	return legitimize_path(nd, &nd->root, nd->root_seq);
}
}


@@ -849,7 +854,8 @@ static int complete_walk(struct nameidata *nd)
		 * We don't want to zero nd->root for scoped-lookups or
		 * We don't want to zero nd->root for scoped-lookups or
		 * externally-managed nd->root.
		 * externally-managed nd->root.
		 */
		 */
		if (!(nd->flags & (LOOKUP_ROOT | LOOKUP_IS_SCOPED)))
		if (!(nd->state & ND_ROOT_PRESET))
			if (!(nd->flags & LOOKUP_IS_SCOPED))
				nd->root.mnt = NULL;
				nd->root.mnt = NULL;
		nd->flags &= ~LOOKUP_CACHED;
		nd->flags &= ~LOOKUP_CACHED;
		if (!try_to_unlazy(nd))
		if (!try_to_unlazy(nd))
@@ -877,7 +883,7 @@ static int complete_walk(struct nameidata *nd)
			return -EXDEV;
			return -EXDEV;
	}
	}


	if (likely(!(nd->flags & LOOKUP_JUMPED)))
	if (likely(!(nd->state & ND_JUMPED)))
		return 0;
		return 0;


	if (likely(!(dentry->d_flags & DCACHE_OP_WEAK_REVALIDATE)))
	if (likely(!(dentry->d_flags & DCACHE_OP_WEAK_REVALIDATE)))
@@ -915,7 +921,7 @@ static int set_root(struct nameidata *nd)
		} while (read_seqcount_retry(&fs->seq, seq));
		} while (read_seqcount_retry(&fs->seq, seq));
	} else {
	} else {
		get_fs_root(fs, &nd->root);
		get_fs_root(fs, &nd->root);
		nd->flags |= LOOKUP_ROOT_GRABBED;
		nd->state |= ND_ROOT_GRABBED;
	}
	}
	return 0;
	return 0;
}
}
@@ -948,7 +954,7 @@ static int nd_jump_root(struct nameidata *nd)
		path_get(&nd->path);
		path_get(&nd->path);
		nd->inode = nd->path.dentry->d_inode;
		nd->inode = nd->path.dentry->d_inode;
	}
	}
	nd->flags |= LOOKUP_JUMPED;
	nd->state |= ND_JUMPED;
	return 0;
	return 0;
}
}


@@ -976,7 +982,7 @@ int nd_jump_link(struct path *path)
	path_put(&nd->path);
	path_put(&nd->path);
	nd->path = *path;
	nd->path = *path;
	nd->inode = nd->path.dentry->d_inode;
	nd->inode = nd->path.dentry->d_inode;
	nd->flags |= LOOKUP_JUMPED;
	nd->state |= ND_JUMPED;
	return 0;
	return 0;


err:
err:
@@ -1424,7 +1430,7 @@ static bool __follow_mount_rcu(struct nameidata *nd, struct path *path,
			if (mounted) {
			if (mounted) {
				path->mnt = &mounted->mnt;
				path->mnt = &mounted->mnt;
				dentry = path->dentry = mounted->mnt.mnt_root;
				dentry = path->dentry = mounted->mnt.mnt_root;
				nd->flags |= LOOKUP_JUMPED;
				nd->state |= ND_JUMPED;
				*seqp = read_seqcount_begin(&dentry->d_seq);
				*seqp = read_seqcount_begin(&dentry->d_seq);
				*inode = dentry->d_inode;
				*inode = dentry->d_inode;
				/*
				/*
@@ -1469,7 +1475,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
		if (unlikely(nd->flags & LOOKUP_NO_XDEV))
		if (unlikely(nd->flags & LOOKUP_NO_XDEV))
			ret = -EXDEV;
			ret = -EXDEV;
		else
		else
			nd->flags |= LOOKUP_JUMPED;
			nd->state |= ND_JUMPED;
	}
	}
	if (unlikely(ret)) {
	if (unlikely(ret)) {
		dput(path->dentry);
		dput(path->dentry);
@@ -2220,7 +2226,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
			case 2:
			case 2:
				if (name[1] == '.') {
				if (name[1] == '.') {
					type = LAST_DOTDOT;
					type = LAST_DOTDOT;
					nd->flags |= LOOKUP_JUMPED;
					nd->state |= ND_JUMPED;
				}
				}
				break;
				break;
			case 1:
			case 1:
@@ -2228,7 +2234,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
		}
		}
		if (likely(type == LAST_NORM)) {
		if (likely(type == LAST_NORM)) {
			struct dentry *parent = nd->path.dentry;
			struct dentry *parent = nd->path.dentry;
			nd->flags &= ~LOOKUP_JUMPED;
			nd->state &= ~ND_JUMPED;
			if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
			if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
				struct qstr this = { { .hash_len = hash_len }, .name = name };
				struct qstr this = { { .hash_len = hash_len }, .name = name };
				err = parent->d_op->d_hash(parent, &this);
				err = parent->d_op->d_hash(parent, &this);
@@ -2302,14 +2308,15 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
	if (flags & LOOKUP_RCU)
	if (flags & LOOKUP_RCU)
		rcu_read_lock();
		rcu_read_lock();


	nd->flags = flags | LOOKUP_JUMPED;
	nd->flags = flags;
	nd->state |= ND_JUMPED;
	nd->depth = 0;
	nd->depth = 0;


	nd->m_seq = __read_seqcount_begin(&mount_lock.seqcount);
	nd->m_seq = __read_seqcount_begin(&mount_lock.seqcount);
	nd->r_seq = __read_seqcount_begin(&rename_lock.seqcount);
	nd->r_seq = __read_seqcount_begin(&rename_lock.seqcount);
	smp_rmb();
	smp_rmb();


	if (flags & LOOKUP_ROOT) {
	if (nd->state & ND_ROOT_PRESET) {
		struct dentry *root = nd->root.dentry;
		struct dentry *root = nd->root.dentry;
		struct inode *inode = root->d_inode;
		struct inode *inode = root->d_inode;
		if (*s && unlikely(!d_can_lookup(root)))
		if (*s && unlikely(!d_can_lookup(root)))
@@ -2384,7 +2391,7 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
			nd->root_seq = nd->seq;
			nd->root_seq = nd->seq;
		} else {
		} else {
			path_get(&nd->root);
			path_get(&nd->root);
			nd->flags |= LOOKUP_ROOT_GRABBED;
			nd->state |= ND_ROOT_GRABBED;
		}
		}
	}
	}
	return s;
	return s;
@@ -2423,7 +2430,7 @@ static int path_lookupat(struct nameidata *nd, unsigned flags, struct path *path
		;
		;
	if (!err && unlikely(nd->flags & LOOKUP_MOUNTPOINT)) {
	if (!err && unlikely(nd->flags & LOOKUP_MOUNTPOINT)) {
		err = handle_lookup_down(nd);
		err = handle_lookup_down(nd);
		nd->flags &= ~LOOKUP_JUMPED; // no d_weak_revalidate(), please...
		nd->state &= ~ND_JUMPED; // no d_weak_revalidate(), please...
	}
	}
	if (!err)
	if (!err)
		err = complete_walk(nd);
		err = complete_walk(nd);
@@ -2447,11 +2454,11 @@ int filename_lookup(int dfd, struct filename *name, unsigned flags,
	struct nameidata nd;
	struct nameidata nd;
	if (IS_ERR(name))
	if (IS_ERR(name))
		return PTR_ERR(name);
		return PTR_ERR(name);
	set_nameidata(&nd, dfd, name);
	if (unlikely(root)) {
	if (unlikely(root)) {
		nd.root = *root;
		nd.root = *root;
		flags |= LOOKUP_ROOT;
		nd.state = ND_ROOT_PRESET;
	}
	}
	set_nameidata(&nd, dfd, name);
	retval = path_lookupat(&nd, flags | LOOKUP_RCU, path);
	retval = path_lookupat(&nd, flags | LOOKUP_RCU, path);
	if (unlikely(retval == -ECHILD))
	if (unlikely(retval == -ECHILD))
		retval = path_lookupat(&nd, flags, path);
		retval = path_lookupat(&nd, flags, path);
@@ -3539,7 +3546,7 @@ struct file *do_file_open_root(const struct path *root,
	struct nameidata nd;
	struct nameidata nd;
	struct file *file;
	struct file *file;
	struct filename *filename;
	struct filename *filename;
	int flags = op->lookup_flags | LOOKUP_ROOT;
	int flags = op->lookup_flags;


	if (d_is_symlink(root->dentry) && op->intent & LOOKUP_OPEN)
	if (d_is_symlink(root->dentry) && op->intent & LOOKUP_OPEN)
		return ERR_PTR(-ELOOP);
		return ERR_PTR(-ELOOP);
@@ -3548,8 +3555,9 @@ struct file *do_file_open_root(const struct path *root,
	if (IS_ERR(filename))
	if (IS_ERR(filename))
		return ERR_CAST(filename);
		return ERR_CAST(filename);


	nd.root = *root;
	set_nameidata(&nd, -1, filename);
	set_nameidata(&nd, -1, filename);
	nd.root = *root;
	nd.state = ND_ROOT_PRESET;
	file = path_openat(&nd, op, flags | LOOKUP_RCU);
	file = path_openat(&nd, op, flags | LOOKUP_RCU);
	if (unlikely(file == ERR_PTR(-ECHILD)))
	if (unlikely(file == ERR_PTR(-ECHILD)))
		file = path_openat(&nd, op, flags);
		file = path_openat(&nd, op, flags);
+0 −4
Original line number Original line Diff line number Diff line
@@ -271,8 +271,6 @@ TRACE_DEFINE_ENUM(LOOKUP_OPEN);
TRACE_DEFINE_ENUM(LOOKUP_CREATE);
TRACE_DEFINE_ENUM(LOOKUP_CREATE);
TRACE_DEFINE_ENUM(LOOKUP_EXCL);
TRACE_DEFINE_ENUM(LOOKUP_EXCL);
TRACE_DEFINE_ENUM(LOOKUP_RENAME_TARGET);
TRACE_DEFINE_ENUM(LOOKUP_RENAME_TARGET);
TRACE_DEFINE_ENUM(LOOKUP_JUMPED);
TRACE_DEFINE_ENUM(LOOKUP_ROOT);
TRACE_DEFINE_ENUM(LOOKUP_EMPTY);
TRACE_DEFINE_ENUM(LOOKUP_EMPTY);
TRACE_DEFINE_ENUM(LOOKUP_DOWN);
TRACE_DEFINE_ENUM(LOOKUP_DOWN);


@@ -288,8 +286,6 @@ TRACE_DEFINE_ENUM(LOOKUP_DOWN);
			{ LOOKUP_CREATE, "CREATE" }, \
			{ LOOKUP_CREATE, "CREATE" }, \
			{ LOOKUP_EXCL, "EXCL" }, \
			{ LOOKUP_EXCL, "EXCL" }, \
			{ LOOKUP_RENAME_TARGET, "RENAME_TARGET" }, \
			{ LOOKUP_RENAME_TARGET, "RENAME_TARGET" }, \
			{ LOOKUP_JUMPED, "JUMPED" }, \
			{ LOOKUP_ROOT, "ROOT" }, \
			{ LOOKUP_EMPTY, "EMPTY" }, \
			{ LOOKUP_EMPTY, "EMPTY" }, \
			{ LOOKUP_DOWN, "DOWN" })
			{ LOOKUP_DOWN, "DOWN" })


+0 −3
Original line number Original line Diff line number Diff line
@@ -36,9 +36,6 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};


/* internal use only */
/* internal use only */
#define LOOKUP_PARENT		0x0010
#define LOOKUP_PARENT		0x0010
#define LOOKUP_JUMPED		0x1000
#define LOOKUP_ROOT		0x2000
#define LOOKUP_ROOT_GRABBED	0x0008


/* Scoping flags for lookup. */
/* Scoping flags for lookup. */
#define LOOKUP_NO_SYMLINKS	0x010000 /* No symlink crossing. */
#define LOOKUP_NO_SYMLINKS	0x010000 /* No symlink crossing. */