merge handle_reval_dot and nameidata_drop_rcu_last
new helper: complete_walk(). Done on successful completion of walk, drops out of RCU mode, does d_revalidate of final result if that hadn't been done already. handle_reval_dot() and nameidata_drop_rcu_last() subsumed into that one; callers converted to use of complete_walk(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
19660af736
commit
9f1fafee9e
121
fs/namei.c
121
fs/namei.c
|
@ -468,43 +468,6 @@ err_root:
|
|||
return -ECHILD;
|
||||
}
|
||||
|
||||
/**
|
||||
* nameidata_drop_rcu_last - drop nameidata ending path walk out of rcu-walk
|
||||
* @nd: nameidata pathwalk data to drop
|
||||
* Returns: 0 on success, -ECHILD on failure
|
||||
*
|
||||
* nameidata_drop_rcu_last attempts to drop the current nd->path into ref-walk.
|
||||
* nd->path should be the final element of the lookup, so nd->root is discarded.
|
||||
* Must be called from rcu-walk context.
|
||||
*/
|
||||
static int nameidata_drop_rcu_last(struct nameidata *nd)
|
||||
{
|
||||
struct dentry *dentry = nd->path.dentry;
|
||||
|
||||
BUG_ON(!(nd->flags & LOOKUP_RCU));
|
||||
nd->flags &= ~LOOKUP_RCU;
|
||||
if (!(nd->flags & LOOKUP_ROOT))
|
||||
nd->root.mnt = NULL;
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (!__d_rcu_to_refcount(dentry, nd->seq))
|
||||
goto err_unlock;
|
||||
BUG_ON(nd->inode != dentry->d_inode);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
|
||||
mntget(nd->path.mnt);
|
||||
|
||||
rcu_read_unlock();
|
||||
br_read_unlock(vfsmount_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
err_unlock:
|
||||
spin_unlock(&dentry->d_lock);
|
||||
rcu_read_unlock();
|
||||
br_read_unlock(vfsmount_lock);
|
||||
return -ECHILD;
|
||||
}
|
||||
|
||||
/**
|
||||
* release_open_intent - free up open intent resources
|
||||
* @nd: pointer to nameidata
|
||||
|
@ -548,26 +511,39 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
|
|||
return dentry;
|
||||
}
|
||||
|
||||
/*
|
||||
* handle_reval_path - force revalidation of a dentry
|
||||
/**
|
||||
* complete_walk - successful completion of path walk
|
||||
* @nd: pointer nameidata
|
||||
*
|
||||
* In some situations the path walking code will trust dentries without
|
||||
* revalidating them. This causes problems for filesystems that depend on
|
||||
* d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set
|
||||
* (which indicates that it's possible for the dentry to go stale), force
|
||||
* a d_revalidate call before proceeding.
|
||||
*
|
||||
* Returns 0 if the revalidation was successful. If the revalidation fails,
|
||||
* either return the error returned by d_revalidate or -ESTALE if the
|
||||
* revalidation it just returned 0. If d_revalidate returns 0, we attempt to
|
||||
* invalidate the dentry. It's up to the caller to handle putting references
|
||||
* to the path if necessary.
|
||||
* If we had been in RCU mode, drop out of it and legitimize nd->path.
|
||||
* Revalidate the final result, unless we'd already done that during
|
||||
* the path walk or the filesystem doesn't ask for it. Return 0 on
|
||||
* success, -error on failure. In case of failure caller does not
|
||||
* need to drop nd->path.
|
||||
*/
|
||||
static inline int handle_reval_path(struct nameidata *nd)
|
||||
static int complete_walk(struct nameidata *nd)
|
||||
{
|
||||
struct dentry *dentry = nd->path.dentry;
|
||||
int status;
|
||||
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
nd->flags &= ~LOOKUP_RCU;
|
||||
if (!(nd->flags & LOOKUP_ROOT))
|
||||
nd->root.mnt = NULL;
|
||||
spin_lock(&dentry->d_lock);
|
||||
if (unlikely(!__d_rcu_to_refcount(dentry, nd->seq))) {
|
||||
spin_unlock(&dentry->d_lock);
|
||||
rcu_read_unlock();
|
||||
br_read_unlock(vfsmount_lock);
|
||||
return -ECHILD;
|
||||
}
|
||||
BUG_ON(nd->inode != dentry->d_inode);
|
||||
spin_unlock(&dentry->d_lock);
|
||||
mntget(nd->path.mnt);
|
||||
rcu_read_unlock();
|
||||
br_read_unlock(vfsmount_lock);
|
||||
}
|
||||
|
||||
if (likely(!(nd->flags & LOOKUP_JUMPED)))
|
||||
return 0;
|
||||
|
||||
|
@ -585,6 +561,7 @@ static inline int handle_reval_path(struct nameidata *nd)
|
|||
if (!status)
|
||||
status = -ESTALE;
|
||||
|
||||
path_put(&nd->path);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -1598,18 +1575,8 @@ static int path_lookupat(int dfd, const char *name,
|
|||
}
|
||||
}
|
||||
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
/* went all way through without dropping RCU */
|
||||
BUG_ON(err);
|
||||
if (nameidata_drop_rcu_last(nd))
|
||||
err = -ECHILD;
|
||||
}
|
||||
|
||||
if (!err) {
|
||||
err = handle_reval_path(nd);
|
||||
if (err)
|
||||
path_put(&nd->path);
|
||||
}
|
||||
if (!err)
|
||||
err = complete_walk(nd);
|
||||
|
||||
if (!err && nd->flags & LOOKUP_DIRECTORY) {
|
||||
if (!nd->inode->i_op->lookup) {
|
||||
|
@ -2075,13 +2042,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||
return ERR_PTR(error);
|
||||
/* fallthrough */
|
||||
case LAST_ROOT:
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
if (nameidata_drop_rcu_last(nd))
|
||||
return ERR_PTR(-ECHILD);
|
||||
}
|
||||
error = handle_reval_path(nd);
|
||||
error = complete_walk(nd);
|
||||
if (error)
|
||||
goto exit;
|
||||
return ERR_PTR(error);
|
||||
audit_inode(pathname, nd->path.dentry);
|
||||
if (open_flag & O_CREAT) {
|
||||
error = -EISDIR;
|
||||
|
@ -2089,10 +2052,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||
}
|
||||
goto ok;
|
||||
case LAST_BIND:
|
||||
/* can't be RCU mode here */
|
||||
error = handle_reval_path(nd);
|
||||
error = complete_walk(nd);
|
||||
if (error)
|
||||
goto exit;
|
||||
return ERR_PTR(error);
|
||||
audit_inode(pathname, dir);
|
||||
goto ok;
|
||||
}
|
||||
|
@ -2111,10 +2073,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||
if (error) /* symlink */
|
||||
return NULL;
|
||||
/* sayonara */
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
if (nameidata_drop_rcu_last(nd))
|
||||
return ERR_PTR(-ECHILD);
|
||||
}
|
||||
error = complete_walk(nd);
|
||||
if (error)
|
||||
return ERR_PTR(-ECHILD);
|
||||
|
||||
error = -ENOTDIR;
|
||||
if (nd->flags & LOOKUP_DIRECTORY) {
|
||||
|
@ -2126,11 +2087,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||
}
|
||||
|
||||
/* create side of things */
|
||||
|
||||
if (nd->flags & LOOKUP_RCU) {
|
||||
if (nameidata_drop_rcu_last(nd))
|
||||
return ERR_PTR(-ECHILD);
|
||||
}
|
||||
error = complete_walk(nd);
|
||||
if (error)
|
||||
return ERR_PTR(error);
|
||||
|
||||
audit_inode(pathname, dir);
|
||||
error = -EISDIR;
|
||||
|
|
Loading…
Reference in New Issue