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: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
Al Viro 2009-04-07 11:49:53 -04:00
parent 9b4a9b14a7
commit 2a73787110
2 changed files with 35 additions and 19 deletions

View File

@ -552,6 +552,17 @@ static __always_inline int link_path_walk(const char *name, struct nameidata *nd
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)
{
int res = 0;
@ -560,14 +571,10 @@ static __always_inline int __vfs_follow_link(struct nameidata *nd, const char *l
goto fail;
if (*link == '/') {
struct fs_struct *fs = current->fs;
set_root(nd);
path_put(&nd->path);
read_lock(&fs->lock);
nd->path = fs->root;
path_get(&fs->root);
read_unlock(&fs->lock);
nd->path = nd->root;
path_get(&nd->root);
}
res = link_path_walk(link, nd);
@ -741,19 +748,16 @@ int follow_down(struct vfsmount **mnt, struct dentry **dentry)
static __always_inline void follow_dotdot(struct nameidata *nd)
{
struct fs_struct *fs = current->fs;
set_root(nd);
while(1) {
struct vfsmount *parent;
struct dentry *old = nd->path.dentry;
read_lock(&fs->lock);
if (nd->path.dentry == fs->root.dentry &&
nd->path.mnt == fs->root.mnt) {
read_unlock(&fs->lock);
if (nd->path.dentry == nd->root.dentry &&
nd->path.mnt == nd->root.mnt) {
break;
}
read_unlock(&fs->lock);
spin_lock(&dcache_lock);
if (nd->path.dentry != nd->path.mnt->mnt_root) {
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
int retval = 0;
int fput_needed;
struct file *file;
struct fs_struct *fs = current->fs;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
nd->depth = 0;
nd->root.mnt = NULL;
if (*name=='/') {
read_lock(&fs->lock);
nd->path = fs->root;
path_get(&fs->root);
read_unlock(&fs->lock);
set_root(nd);
nd->path = nd->root;
path_get(&nd->root);
} else if (dfd == AT_FDCWD) {
struct fs_struct *fs = current->fs;
read_lock(&fs->lock);
nd->path = fs->pwd;
path_get(&fs->pwd);
@ -1079,6 +1083,10 @@ static int do_path_lookup(int dfd, const char *name,
if (unlikely(!retval && !audit_dummy_context() && nd->path.dentry &&
nd->path.dentry->d_inode))
audit_inode(name, nd->path.dentry);
if (nd->root.mnt) {
path_put(&nd->root);
nd->root.mnt = NULL;
}
return retval;
}
@ -1115,6 +1123,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
nd->last_type = LAST_ROOT;
nd->flags = flags;
nd->depth = 0;
nd->root.mnt = NULL;
nd->path.dentry = dentry;
nd->path.mnt = mnt;
@ -1125,8 +1134,12 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt,
nd->path.dentry->d_inode))
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 @@ exit:
if (!IS_ERR(nd.intent.open.file))
release_open_intent(&nd);
exit_parent:
if (nd.root.mnt)
path_put(&nd.root);
path_put(&nd.path);
return ERR_PTR(error);

View File

@ -18,6 +18,7 @@ enum { MAX_NESTED_LINKS = 8 };
struct nameidata {
struct path path;
struct qstr last;
struct path root;
unsigned int flags;
int last_type;
unsigned depth;