Commit 5b6ca027 authored by Al Viro's avatar Al Viro

reduce vfs_path_lookup() to do_path_lookup()

New lookup flag: LOOKUP_ROOT.  nd->root is set (and held) by caller,
path_init() starts walking from that place and all pathname resolution
machinery never drops nd->root if that flag is set.  That turns
vfs_path_lookup() into a special case of do_path_lookup() *and*
gets us down to 3 callers of link_path_walk(), making it finally
feasible to rip the handling of trailing symlink out of link_path_walk().
That will not only simply the living hell out of it, but make life
much simpler for unionfs merge.  Trailing symlink handling will
become iterative, which is a good thing for stack footprint in
a lot of situations as well.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 5a18fff2
...@@ -401,9 +401,11 @@ static int nameidata_drop_rcu(struct nameidata *nd) ...@@ -401,9 +401,11 @@ static int nameidata_drop_rcu(struct nameidata *nd)
{ {
struct fs_struct *fs = current->fs; struct fs_struct *fs = current->fs;
struct dentry *dentry = nd->path.dentry; struct dentry *dentry = nd->path.dentry;
int want_root = 0;
BUG_ON(!(nd->flags & LOOKUP_RCU)); BUG_ON(!(nd->flags & LOOKUP_RCU));
if (nd->root.mnt) { if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
want_root = 1;
spin_lock(&fs->lock); spin_lock(&fs->lock);
if (nd->root.mnt != fs->root.mnt || if (nd->root.mnt != fs->root.mnt ||
nd->root.dentry != fs->root.dentry) nd->root.dentry != fs->root.dentry)
...@@ -414,7 +416,7 @@ static int nameidata_drop_rcu(struct nameidata *nd) ...@@ -414,7 +416,7 @@ static int nameidata_drop_rcu(struct nameidata *nd)
goto err; goto err;
BUG_ON(nd->inode != dentry->d_inode); BUG_ON(nd->inode != dentry->d_inode);
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
if (nd->root.mnt) { if (want_root) {
path_get(&nd->root); path_get(&nd->root);
spin_unlock(&fs->lock); spin_unlock(&fs->lock);
} }
...@@ -427,7 +429,7 @@ static int nameidata_drop_rcu(struct nameidata *nd) ...@@ -427,7 +429,7 @@ static int nameidata_drop_rcu(struct nameidata *nd)
err: err:
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
err_root: err_root:
if (nd->root.mnt) if (want_root)
spin_unlock(&fs->lock); spin_unlock(&fs->lock);
return -ECHILD; return -ECHILD;
} }
...@@ -454,9 +456,11 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry ...@@ -454,9 +456,11 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry
{ {
struct fs_struct *fs = current->fs; struct fs_struct *fs = current->fs;
struct dentry *parent = nd->path.dentry; struct dentry *parent = nd->path.dentry;
int want_root = 0;
BUG_ON(!(nd->flags & LOOKUP_RCU)); BUG_ON(!(nd->flags & LOOKUP_RCU));
if (nd->root.mnt) { if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
want_root = 1;
spin_lock(&fs->lock); spin_lock(&fs->lock);
if (nd->root.mnt != fs->root.mnt || if (nd->root.mnt != fs->root.mnt ||
nd->root.dentry != fs->root.dentry) nd->root.dentry != fs->root.dentry)
...@@ -476,7 +480,7 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry ...@@ -476,7 +480,7 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry
parent->d_count++; parent->d_count++;
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&parent->d_lock); spin_unlock(&parent->d_lock);
if (nd->root.mnt) { if (want_root) {
path_get(&nd->root); path_get(&nd->root);
spin_unlock(&fs->lock); spin_unlock(&fs->lock);
} }
...@@ -490,7 +494,7 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry ...@@ -490,7 +494,7 @@ static int nameidata_dentry_drop_rcu(struct nameidata *nd, struct dentry *dentry
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
spin_unlock(&parent->d_lock); spin_unlock(&parent->d_lock);
err_root: err_root:
if (nd->root.mnt) if (want_root)
spin_unlock(&fs->lock); spin_unlock(&fs->lock);
return -ECHILD; return -ECHILD;
} }
...@@ -501,6 +505,7 @@ static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct d ...@@ -501,6 +505,7 @@ static inline int nameidata_dentry_drop_rcu_maybe(struct nameidata *nd, struct d
if (nd->flags & LOOKUP_RCU) { if (nd->flags & LOOKUP_RCU) {
if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) { if (unlikely(nameidata_dentry_drop_rcu(nd, dentry))) {
nd->flags &= ~LOOKUP_RCU; nd->flags &= ~LOOKUP_RCU;
if (!(nd->flags & LOOKUP_ROOT))
nd->root.mnt = NULL; nd->root.mnt = NULL;
rcu_read_unlock(); rcu_read_unlock();
br_read_unlock(vfsmount_lock); br_read_unlock(vfsmount_lock);
...@@ -525,6 +530,7 @@ static int nameidata_drop_rcu_last(struct nameidata *nd) ...@@ -525,6 +530,7 @@ static int nameidata_drop_rcu_last(struct nameidata *nd)
BUG_ON(!(nd->flags & LOOKUP_RCU)); BUG_ON(!(nd->flags & LOOKUP_RCU));
nd->flags &= ~LOOKUP_RCU; nd->flags &= ~LOOKUP_RCU;
if (!(nd->flags & LOOKUP_ROOT))
nd->root.mnt = NULL; nd->root.mnt = NULL;
spin_lock(&dentry->d_lock); spin_lock(&dentry->d_lock);
if (!__d_rcu_to_refcount(dentry, nd->seq)) if (!__d_rcu_to_refcount(dentry, nd->seq))
...@@ -1053,6 +1059,7 @@ static int follow_dotdot_rcu(struct nameidata *nd) ...@@ -1053,6 +1059,7 @@ static int follow_dotdot_rcu(struct nameidata *nd)
failed: failed:
nd->flags &= ~LOOKUP_RCU; nd->flags &= ~LOOKUP_RCU;
if (!(nd->flags & LOOKUP_ROOT))
nd->root.mnt = NULL; nd->root.mnt = NULL;
rcu_read_unlock(); rcu_read_unlock();
br_read_unlock(vfsmount_lock); br_read_unlock(vfsmount_lock);
...@@ -1310,6 +1317,7 @@ static void terminate_walk(struct nameidata *nd) ...@@ -1310,6 +1317,7 @@ static void terminate_walk(struct nameidata *nd)
path_put(&nd->path); path_put(&nd->path);
} else { } else {
nd->flags &= ~LOOKUP_RCU; nd->flags &= ~LOOKUP_RCU;
if (!(nd->flags & LOOKUP_ROOT))
nd->root.mnt = NULL; nd->root.mnt = NULL;
rcu_read_unlock(); rcu_read_unlock();
br_read_unlock(vfsmount_lock); br_read_unlock(vfsmount_lock);
...@@ -1477,6 +1485,25 @@ static int path_init(int dfd, const char *name, unsigned int flags, ...@@ -1477,6 +1485,25 @@ static int path_init(int dfd, const char *name, unsigned int flags,
nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags | LOOKUP_JUMPED; nd->flags = flags | LOOKUP_JUMPED;
nd->depth = 0; nd->depth = 0;
if (flags & LOOKUP_ROOT) {
struct inode *inode = nd->root.dentry->d_inode;
if (!inode->i_op->lookup)
return -ENOTDIR;
retval = inode_permission(inode, MAY_EXEC);
if (retval)
return retval;
nd->path = nd->root;
nd->inode = inode;
if (flags & LOOKUP_RCU) {
br_read_lock(vfsmount_lock);
rcu_read_lock();
nd->seq = __read_seqcount_begin(&nd->path.dentry->d_seq);
} else {
path_get(&nd->path);
}
return 0;
}
nd->root.mnt = NULL; nd->root.mnt = NULL;
if (*name=='/') { if (*name=='/') {
...@@ -1587,7 +1614,7 @@ static int path_lookupat(int dfd, const char *name, ...@@ -1587,7 +1614,7 @@ static int path_lookupat(int dfd, const char *name,
if (base) if (base)
fput(base); fput(base);
if (nd->root.mnt) { if (nd->root.mnt && !(nd->flags & LOOKUP_ROOT)) {
path_put(&nd->root); path_put(&nd->root);
nd->root.mnt = NULL; nd->root.mnt = NULL;
} }
...@@ -1638,46 +1665,10 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1638,46 +1665,10 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
const char *name, unsigned int flags, const char *name, unsigned int flags,
struct nameidata *nd) struct nameidata *nd)
{ {
int result; nd->root.dentry = dentry;
nd->root.mnt = mnt;
/* same as do_path_lookup */ /* the first argument of do_path_lookup() is ignored with LOOKUP_ROOT */
nd->last_type = LAST_ROOT; return do_path_lookup(AT_FDCWD, name, flags | LOOKUP_ROOT, nd);
nd->flags = flags | LOOKUP_JUMPED;
nd->depth = 0;
nd->path.dentry = dentry;
nd->path.mnt = mnt;
path_get(&nd->path);
nd->root = nd->path;
path_get(&nd->root);
nd->inode = nd->path.dentry->d_inode;
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;
nd->path.dentry = dentry;
nd->path.mnt = mnt;
nd->inode = dentry->d_inode;
path_get(&nd->path);
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))
audit_inode(name, nd->path.dentry);
path_put(&nd->root);
nd->root.mnt = NULL;
return result;
} }
static struct dentry *__lookup_hash(struct qstr *name, static struct dentry *__lookup_hash(struct qstr *name,
...@@ -2320,7 +2311,7 @@ static struct file *path_openat(int dfd, const char *pathname, ...@@ -2320,7 +2311,7 @@ static struct file *path_openat(int dfd, const char *pathname,
path_put(&link); path_put(&link);
} }
out: out:
if (nd.root.mnt) if (nd.root.mnt && !(nd.flags & LOOKUP_ROOT))
path_put(&nd.root); path_put(&nd.root);
if (base) if (base)
fput(base); fput(base);
......
...@@ -63,6 +63,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND}; ...@@ -63,6 +63,7 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
#define LOOKUP_RENAME_TARGET 0x0800 #define LOOKUP_RENAME_TARGET 0x0800
#define LOOKUP_JUMPED 0x1000 #define LOOKUP_JUMPED 0x1000
#define LOOKUP_ROOT 0x2000
extern int user_path_at(int, const char __user *, unsigned, struct path *); extern int user_path_at(int, const char __user *, unsigned, struct path *);
......
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