Commit 2a737871 authored by Al Viro's avatar Al Viro

Cache root in nameidata

New field: nd->root.  When pathname resolution wants to know the root,
check if nd->root.mnt is non-NULL; use nd->root if it is, otherwise
copy current->fs->root there.  After path_walk() is finished, we check
if we'd got a cached value in nd->root and drop it.  Before calling
path_walk() we should either set nd->root.mnt to NULL *or* copy (and
pin down) some path to nd->root.  In the latter case we won't be
looking at current->fs->root at all.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 9b4a9b14
...@@ -552,6 +552,17 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd ...@@ -552,6 +552,17 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd
return result; return result;
} }
static __always_inline void set_root(struct nameidata *nd)
{
if (!nd->root.mnt) {
struct fs_struct *fs = current->fs;
read_lock(&fs->lock);
nd->root = fs->root;
path_get(&nd->root);
read_unlock(&fs->lock);
}
}
static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link) static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *link)
{ {
int res = 0; int res = 0;
...@@ -560,14 +571,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l ...@@ -560,14 +571,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
goto fail; goto fail;
if (*link == '/') { if (*link == '/') {
struct fs_struct *fs = current->fs; set_root(nd);
path_put(&nd->path); path_put(&nd->path);
nd->path = nd->root;
read_lock(&fs->lock); path_get(&nd->root);
nd->path = fs->root;
path_get(&fs->root);
read_unlock(&fs->lock);
} }
res = link_path_walk(link, nd); res = link_path_walk(link, nd);
...@@ -741,19 +748,16 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry) ...@@ -741,19 +748,16 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry)
static __always_inline void follow_dotdot(struct nameidata *nd) static __always_inline void follow_dotdot(struct nameidata *nd)
{ {
struct fs_struct *fs = current->fs; set_root(nd);
while(1) { while(1) {
struct vfsmount *parent; struct vfsmount *parent;
struct dentry *old = nd->path.dentry; struct dentry *old = nd->path.dentry;
read_lock(&fs->lock); if (nd->path.dentry == nd->root.dentry &&
if (nd->path.dentry == fs->root.dentry && nd->path.mnt == nd->root.mnt) {
nd->path.mnt == fs->root.mnt) {
read_unlock(&fs->lock);
break; break;
} }
read_unlock(&fs->lock);
spin_lock(&dcache_lock); spin_lock(&dcache_lock);
if (nd->path.dentry != nd->path.mnt->mnt_root) { if (nd->path.dentry != nd->path.mnt->mnt_root) {
nd->path.dentry = dget(nd->path.dentry->d_parent); nd->path.dentry = dget(nd->path.dentry->d_parent);
...@@ -1022,18 +1026,18 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei ...@@ -1022,18 +1026,18 @@ static int path_init(int dfd, const char *name, unsigned int flags, struct namei
int retval = 0; int retval = 0;
int fput_needed; int fput_needed;
struct file *file; struct file *file;
struct fs_struct *fs = current->fs;
nd->last_type = LAST_ROOT; /* if there are only slashes... */ nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags; nd->flags = flags;
nd->depth = 0; nd->depth = 0;
nd->root.mnt = NULL;
if (*name=='/') { if (*name=='/') {
read_lock(&fs->lock); set_root(nd);
nd->path = fs->root; nd->path = nd->root;
path_get(&fs->root); path_get(&nd->root);
read_unlock(&fs->lock);
} else if (dfd == AT_FDCWD) { } else if (dfd == AT_FDCWD) {
struct fs_struct *fs = current->fs;
read_lock(&fs->lock); read_lock(&fs->lock);
nd->path = fs->pwd; nd->path = fs->pwd;
path_get(&fs->pwd); path_get(&fs->pwd);
...@@ -1079,6 +1083,10 @@ static int do_path_lookup(int dfd, const char *name, ...@@ -1079,6 +1083,10 @@ static int do_path_lookup(int dfd, const char *name,
if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry && if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
nd->path.dentry->d_inode)) nd->path.dentry->d_inode))
audit_inode(name, nd->path.dentry); audit_inode(name, nd->path.dentry);
if (nd->root.mnt) {
path_put(&nd->root);
nd->root.mnt = NULL;
}
return retval; return retval;
} }
...@@ -1115,6 +1123,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1115,6 +1123,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
nd->last_type = LAST_ROOT; nd->last_type = LAST_ROOT;
nd->flags = flags; nd->flags = flags;
nd->depth = 0; nd->depth = 0;
nd->root.mnt = NULL;
nd->path.dentry = dentry; nd->path.dentry = dentry;
nd->path.mnt = mnt; nd->path.mnt = mnt;
...@@ -1125,8 +1134,12 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, ...@@ -1125,8 +1134,12 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
nd->path.dentry->d_inode)) nd->path.dentry->d_inode))
audit_inode(name, nd->path.dentry); audit_inode(name, nd->path.dentry);
return retval; if (nd->root.mnt) {
path_put(&nd->root);
nd->root.mnt = NULL;
}
return retval;
} }
/** /**
...@@ -1817,6 +1830,8 @@ struct file *do_filp_open(int dfd, const char *pathname, ...@@ -1817,6 +1830,8 @@ struct file *do_filp_open(int dfd, const char *pathname,
if (!IS_ERR(nd.intent.open.file)) if (!IS_ERR(nd.intent.open.file))
release_open_intent(&nd); release_open_intent(&nd);
exit_parent: exit_parent:
if (nd.root.mnt)
path_put(&nd.root);
path_put(&nd.path); path_put(&nd.path);
return ERR_PTR(error); return ERR_PTR(error);
......
...@@ -18,6 +18,7 @@ enum { MAX_NESTED_LINKS = 8 }; ...@@ -18,6 +18,7 @@ enum { MAX_NESTED_LINKS = 8 };
struct nameidata { struct nameidata {
struct path path; struct path path;
struct qstr last; struct qstr last;
struct path root;
unsigned int flags; unsigned int flags;
int last_type; int last_type;
unsigned depth; unsigned depth;
......
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