Commit b2d86c7c authored by Jens Axboe's avatar Jens Axboe

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
...@@ -669,23 +669,25 @@ static bool legitimize_root(struct nameidata *nd) ...@@ -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 * @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. * for ref-walk mode.
* Must be called from rcu-walk context. * 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(). * terminate_walk().
*/ */
static int unlazy_walk(struct nameidata *nd) static bool try_to_unlazy(struct nameidata *nd)
{ {
struct dentry *parent = nd->path.dentry; struct dentry *parent = nd->path.dentry;
BUG_ON(!(nd->flags & LOOKUP_RCU)); BUG_ON(!(nd->flags & LOOKUP_RCU));
nd->flags &= ~LOOKUP_RCU; nd->flags &= ~LOOKUP_RCU;
if (nd->flags & LOOKUP_CACHED)
goto out1;
if (unlikely(!legitimize_links(nd))) if (unlikely(!legitimize_links(nd)))
goto out1; goto out1;
if (unlikely(!legitimize_path(nd, &nd->path, nd->seq))) if (unlikely(!legitimize_path(nd, &nd->path, nd->seq)))
...@@ -694,34 +696,36 @@ static int unlazy_walk(struct nameidata *nd) ...@@ -694,34 +696,36 @@ static int unlazy_walk(struct nameidata *nd)
goto out; goto out;
rcu_read_unlock(); rcu_read_unlock();
BUG_ON(nd->inode != parent->d_inode); BUG_ON(nd->inode != parent->d_inode);
return 0; return true;
out1: out1:
nd->path.mnt = NULL; nd->path.mnt = NULL;
nd->path.dentry = NULL; nd->path.dentry = NULL;
out: out:
rcu_read_unlock(); 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 * @nd: nameidata pathwalk data
* @dentry: child of nd->path.dentry * @dentry: next dentry to step into
* @seq: seq number to check dentry against * @seq: seq number to check @dentry against
* Returns: 0 on success, -ECHILD on failure * Returns: true on success, false on failure
* *
* unlazy_child attempts to legitimize the current nd->path, nd->root and dentry * Similar to to try_to_unlazy(), but here we have the next dentry already
* for ref-walk mode. @dentry must be a path found by a do_lookup call on * picked by rcu-walk and want to legitimize that in addition to the current
* @nd. Must be called from rcu-walk context. * nd->path and nd->root for ref-walk mode. Must be called from rcu-walk context.
* Nothing should touch nameidata between unlazy_child() failure and * Nothing should touch nameidata between try_to_unlazy_next() failure and
* terminate_walk(). * 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)); BUG_ON(!(nd->flags & LOOKUP_RCU));
nd->flags &= ~LOOKUP_RCU; nd->flags &= ~LOOKUP_RCU;
if (nd->flags & LOOKUP_CACHED)
goto out2;
if (unlikely(!legitimize_links(nd))) if (unlikely(!legitimize_links(nd)))
goto out2; goto out2;
if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq))) 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 ...@@ -747,7 +751,7 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se
if (unlikely(!legitimize_root(nd))) if (unlikely(!legitimize_root(nd)))
goto out_dput; goto out_dput;
rcu_read_unlock(); rcu_read_unlock();
return 0; return true;
out2: out2:
nd->path.mnt = NULL; nd->path.mnt = NULL;
...@@ -755,11 +759,11 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se ...@@ -755,11 +759,11 @@ static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned se
nd->path.dentry = NULL; nd->path.dentry = NULL;
out: out:
rcu_read_unlock(); rcu_read_unlock();
return -ECHILD; return false;
out_dput: out_dput:
rcu_read_unlock(); rcu_read_unlock();
dput(dentry); dput(dentry);
return -ECHILD; return false;
} }
static inline int d_revalidate(struct dentry *dentry, unsigned int flags) static inline int d_revalidate(struct dentry *dentry, unsigned int flags)
...@@ -792,7 +796,8 @@ static int complete_walk(struct nameidata *nd) ...@@ -792,7 +796,8 @@ static int complete_walk(struct nameidata *nd)
*/ */
if (!(nd->flags & (LOOKUP_ROOT | LOOKUP_IS_SCOPED))) if (!(nd->flags & (LOOKUP_ROOT | LOOKUP_IS_SCOPED)))
nd->root.mnt = NULL; nd->root.mnt = NULL;
if (unlikely(unlazy_walk(nd))) nd->flags &= ~LOOKUP_CACHED;
if (!try_to_unlazy(nd))
return -ECHILD; return -ECHILD;
} }
...@@ -1372,7 +1377,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry, ...@@ -1372,7 +1377,7 @@ static inline int handle_mounts(struct nameidata *nd, struct dentry *dentry,
return -ENOENT; return -ENOENT;
if (likely(__follow_mount_rcu(nd, path, inode, seqp))) if (likely(__follow_mount_rcu(nd, path, inode, seqp)))
return 0; return 0;
if (unlazy_child(nd, dentry, seq)) if (!try_to_unlazy_next(nd, dentry, seq))
return -ECHILD; return -ECHILD;
// *path might've been clobbered by __follow_mount_rcu() // *path might've been clobbered by __follow_mount_rcu()
path->mnt = nd->path.mnt; path->mnt = nd->path.mnt;
...@@ -1466,7 +1471,7 @@ static struct dentry *lookup_fast(struct nameidata *nd, ...@@ -1466,7 +1471,7 @@ static struct dentry *lookup_fast(struct nameidata *nd,
unsigned seq; unsigned seq;
dentry = __d_lookup_rcu(parent, &nd->last, &seq); dentry = __d_lookup_rcu(parent, &nd->last, &seq);
if (unlikely(!dentry)) { if (unlikely(!dentry)) {
if (unlazy_walk(nd)) if (!try_to_unlazy(nd))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
return NULL; return NULL;
} }
...@@ -1493,9 +1498,9 @@ static struct dentry *lookup_fast(struct nameidata *nd, ...@@ -1493,9 +1498,9 @@ static struct dentry *lookup_fast(struct nameidata *nd,
status = d_revalidate(dentry, nd->flags); status = d_revalidate(dentry, nd->flags);
if (likely(status > 0)) if (likely(status > 0))
return dentry; return dentry;
if (unlazy_child(nd, dentry, seq)) if (!try_to_unlazy_next(nd, dentry, seq))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
if (unlikely(status == -ECHILD)) if (status == -ECHILD)
/* we'd been told to redo it in non-rcu mode */ /* we'd been told to redo it in non-rcu mode */
status = d_revalidate(dentry, nd->flags); status = d_revalidate(dentry, nd->flags);
} else { } else {
...@@ -1567,10 +1572,8 @@ static inline int may_lookup(struct nameidata *nd) ...@@ -1567,10 +1572,8 @@ static inline int may_lookup(struct nameidata *nd)
{ {
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK); int err = inode_permission(nd->inode, MAY_EXEC|MAY_NOT_BLOCK);
if (err != -ECHILD) if (err != -ECHILD || !try_to_unlazy(nd))
return err; return err;
if (unlazy_walk(nd))
return -ECHILD;
} }
return inode_permission(nd->inode, MAY_EXEC); return inode_permission(nd->inode, MAY_EXEC);
} }
...@@ -1592,7 +1595,7 @@ static int reserve_stack(struct nameidata *nd, struct path *link, unsigned seq) ...@@ -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 // unlazy even if we fail to grab the link - cleanup needs it
bool grabbed_link = legitimize_path(nd, link, seq); 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; return -ECHILD;
if (nd_alloc_stack(nd)) if (nd_alloc_stack(nd))
...@@ -1634,7 +1637,7 @@ static const char *pick_link(struct nameidata *nd, struct path *link, ...@@ -1634,7 +1637,7 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
touch_atime(&last->link); touch_atime(&last->link);
cond_resched(); cond_resched();
} else if (atime_needs_update(&last->link, inode)) { } else if (atime_needs_update(&last->link, inode)) {
if (unlikely(unlazy_walk(nd))) if (!try_to_unlazy(nd))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
touch_atime(&last->link); touch_atime(&last->link);
} }
...@@ -1651,11 +1654,8 @@ static const char *pick_link(struct nameidata *nd, struct path *link, ...@@ -1651,11 +1654,8 @@ static const char *pick_link(struct nameidata *nd, struct path *link,
get = inode->i_op->get_link; get = inode->i_op->get_link;
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
res = get(NULL, inode, &last->done); res = get(NULL, inode, &last->done);
if (res == ERR_PTR(-ECHILD)) { if (res == ERR_PTR(-ECHILD) && try_to_unlazy(nd))
if (unlikely(unlazy_walk(nd)))
return ERR_PTR(-ECHILD);
res = get(link->dentry, inode, &last->done); res = get(link->dentry, inode, &last->done);
}
} else { } else {
res = get(link->dentry, inode, &last->done); res = get(link->dentry, inode, &last->done);
} }
...@@ -2195,7 +2195,7 @@ static int link_path_walk(const char *name, struct nameidata *nd) ...@@ -2195,7 +2195,7 @@ static int link_path_walk(const char *name, struct nameidata *nd)
} }
if (unlikely(!d_can_lookup(nd->path.dentry))) { if (unlikely(!d_can_lookup(nd->path.dentry))) {
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
if (unlazy_walk(nd)) if (!try_to_unlazy(nd))
return -ECHILD; return -ECHILD;
} }
return -ENOTDIR; return -ENOTDIR;
...@@ -2209,6 +2209,10 @@ static const char *path_init(struct nameidata *nd, unsigned flags) ...@@ -2209,6 +2209,10 @@ static const char *path_init(struct nameidata *nd, unsigned flags)
int error; int error;
const char *s = nd->name->name; 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) if (!*s)
flags &= ~LOOKUP_RCU; flags &= ~LOOKUP_RCU;
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
...@@ -3129,7 +3133,6 @@ static const char *open_last_lookups(struct nameidata *nd, ...@@ -3129,7 +3133,6 @@ static const char *open_last_lookups(struct nameidata *nd,
struct inode *inode; struct inode *inode;
struct dentry *dentry; struct dentry *dentry;
const char *res; const char *res;
int error;
nd->flags |= op->intent; nd->flags |= op->intent;
...@@ -3153,9 +3156,8 @@ static const char *open_last_lookups(struct nameidata *nd, ...@@ -3153,9 +3156,8 @@ static const char *open_last_lookups(struct nameidata *nd,
} else { } else {
/* create side of things */ /* create side of things */
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
error = unlazy_walk(nd); if (!try_to_unlazy(nd))
if (unlikely(error)) return ERR_PTR(-ECHILD);
return ERR_PTR(error);
} }
audit_inode(nd->name, dir, AUDIT_INODE_PARENT); audit_inode(nd->name, dir, AUDIT_INODE_PARENT);
/* trailing slashes? */ /* trailing slashes? */
...@@ -3164,9 +3166,7 @@ static const char *open_last_lookups(struct nameidata *nd, ...@@ -3164,9 +3166,7 @@ static const char *open_last_lookups(struct nameidata *nd,
} }
if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) { if (open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
error = mnt_want_write(nd->path.mnt); got_write = !mnt_want_write(nd->path.mnt);
if (!error)
got_write = true;
/* /*
* do _not_ fail yet - we might not need that or fail with * do _not_ fail yet - we might not need that or fail with
* a different error; let lookup_open() decide; we'll be * a different error; let lookup_open() decide; we'll be
...@@ -3325,10 +3325,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags, ...@@ -3325,10 +3325,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
audit_inode(nd->name, child, 0); audit_inode(nd->name, child, 0);
/* Don't check for other permissions, the inode was just created */ /* Don't check for other permissions, the inode was just created */
error = may_open(&path, 0, op->open_flag); error = may_open(&path, 0, op->open_flag);
if (error) if (!error)
goto out2; error = vfs_open(&path, file);
file->f_path.mnt = path.mnt;
error = finish_open(file, child, NULL);
out2: out2:
mnt_drop_write(path.mnt); mnt_drop_write(path.mnt);
out: out:
......
...@@ -1091,6 +1091,12 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op) ...@@ -1091,6 +1091,12 @@ inline int build_open_flags(const struct open_how *how, struct open_flags *op)
lookup_flags |= LOOKUP_BENEATH; lookup_flags |= LOOKUP_BENEATH;
if (how->resolve & RESOLVE_IN_ROOT) if (how->resolve & RESOLVE_IN_ROOT)
lookup_flags |= LOOKUP_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; op->lookup_flags = lookup_flags;
return 0; return 0;
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
/* List of all valid flags for the how->resolve argument: */ /* List of all valid flags for the how->resolve argument: */
#define VALID_RESOLVE_FLAGS \ #define VALID_RESOLVE_FLAGS \
(RESOLVE_NO_XDEV | RESOLVE_NO_MAGICLINKS | RESOLVE_NO_SYMLINKS | \ (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". */ /* List of all open_how "versions". */
#define OPEN_HOW_SIZE_VER0 24 /* sizeof first published struct */ #define OPEN_HOW_SIZE_VER0 24 /* sizeof first published struct */
......
...@@ -46,6 +46,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT}; ...@@ -46,6 +46,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT};
#define LOOKUP_NO_XDEV 0x040000 /* No mountpoint crossing. */ #define LOOKUP_NO_XDEV 0x040000 /* No mountpoint crossing. */
#define LOOKUP_BENEATH 0x080000 /* No escaping from starting point. */ #define LOOKUP_BENEATH 0x080000 /* No escaping from starting point. */
#define LOOKUP_IN_ROOT 0x100000 /* Treat dirfd as fs root. */ #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. */ /* LOOKUP_* flags which do scope-related checks based on the dirfd. */
#define LOOKUP_IS_SCOPED (LOOKUP_BENEATH | LOOKUP_IN_ROOT) #define LOOKUP_IS_SCOPED (LOOKUP_BENEATH | LOOKUP_IN_ROOT)
......
...@@ -35,5 +35,9 @@ struct open_how { ...@@ -35,5 +35,9 @@ struct open_how {
#define RESOLVE_IN_ROOT 0x10 /* Make all jumps to "/" and ".." #define RESOLVE_IN_ROOT 0x10 /* Make all jumps to "/" and ".."
be scoped inside the dirfd be scoped inside the dirfd
(similar to chroot(2)). */ (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 */ #endif /* _UAPI_LINUX_OPENAT2_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment