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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2021-04-01 22:03:41 -04:00
parent ffb37ca3bd
commit bcba1e7d0d
4 changed files with 34 additions and 33 deletions

View File

@ -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

View File

@ -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,8 +854,9 @@ 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))
nd->root.mnt = NULL; if (!(nd->flags & LOOKUP_IS_SCOPED))
nd->root.mnt = NULL;
nd->flags &= ~LOOKUP_CACHED; nd->flags &= ~LOOKUP_CACHED;
if (!try_to_unlazy(nd)) if (!try_to_unlazy(nd))
return -ECHILD; return -ECHILD;
@ -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);

View File

@ -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" })

View File

@ -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. */