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:
121
fs/namei.c
121
fs/namei.c
@@ -468,43 +468,6 @@ err_root:
|
|||||||
return -ECHILD;
|
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
|
* release_open_intent - free up open intent resources
|
||||||
* @nd: pointer to nameidata
|
* @nd: pointer to nameidata
|
||||||
@@ -548,26 +511,39 @@ do_revalidate(struct dentry *dentry, struct nameidata *nd)
|
|||||||
return dentry;
|
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
|
* If we had been in RCU mode, drop out of it and legitimize nd->path.
|
||||||
* revalidating them. This causes problems for filesystems that depend on
|
* Revalidate the final result, unless we'd already done that during
|
||||||
* d_revalidate to handle file opens (e.g. NFSv4). When FS_REVAL_DOT is set
|
* the path walk or the filesystem doesn't ask for it. Return 0 on
|
||||||
* (which indicates that it's possible for the dentry to go stale), force
|
* success, -error on failure. In case of failure caller does not
|
||||||
* a d_revalidate call before proceeding.
|
* need to drop nd->path.
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
static inline int handle_reval_path(struct nameidata *nd)
|
static int complete_walk(struct nameidata *nd)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = nd->path.dentry;
|
struct dentry *dentry = nd->path.dentry;
|
||||||
int status;
|
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)))
|
if (likely(!(nd->flags & LOOKUP_JUMPED)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@@ -585,6 +561,7 @@ static inline int handle_reval_path(struct nameidata *nd)
|
|||||||
if (!status)
|
if (!status)
|
||||||
status = -ESTALE;
|
status = -ESTALE;
|
||||||
|
|
||||||
|
path_put(&nd->path);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1598,18 +1575,8 @@ static int path_lookupat(int dfd, const char *name,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
if (!err)
|
||||||
/* went all way through without dropping RCU */
|
err = complete_walk(nd);
|
||||||
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 && nd->flags & LOOKUP_DIRECTORY) {
|
if (!err && nd->flags & LOOKUP_DIRECTORY) {
|
||||||
if (!nd->inode->i_op->lookup) {
|
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);
|
return ERR_PTR(error);
|
||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case LAST_ROOT:
|
case LAST_ROOT:
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
error = complete_walk(nd);
|
||||||
if (nameidata_drop_rcu_last(nd))
|
|
||||||
return ERR_PTR(-ECHILD);
|
|
||||||
}
|
|
||||||
error = handle_reval_path(nd);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto exit;
|
return ERR_PTR(error);
|
||||||
audit_inode(pathname, nd->path.dentry);
|
audit_inode(pathname, nd->path.dentry);
|
||||||
if (open_flag & O_CREAT) {
|
if (open_flag & O_CREAT) {
|
||||||
error = -EISDIR;
|
error = -EISDIR;
|
||||||
@@ -2089,10 +2052,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||||||
}
|
}
|
||||||
goto ok;
|
goto ok;
|
||||||
case LAST_BIND:
|
case LAST_BIND:
|
||||||
/* can't be RCU mode here */
|
error = complete_walk(nd);
|
||||||
error = handle_reval_path(nd);
|
|
||||||
if (error)
|
if (error)
|
||||||
goto exit;
|
return ERR_PTR(error);
|
||||||
audit_inode(pathname, dir);
|
audit_inode(pathname, dir);
|
||||||
goto ok;
|
goto ok;
|
||||||
}
|
}
|
||||||
@@ -2111,10 +2073,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||||||
if (error) /* symlink */
|
if (error) /* symlink */
|
||||||
return NULL;
|
return NULL;
|
||||||
/* sayonara */
|
/* sayonara */
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
error = complete_walk(nd);
|
||||||
if (nameidata_drop_rcu_last(nd))
|
if (error)
|
||||||
return ERR_PTR(-ECHILD);
|
return ERR_PTR(-ECHILD);
|
||||||
}
|
|
||||||
|
|
||||||
error = -ENOTDIR;
|
error = -ENOTDIR;
|
||||||
if (nd->flags & LOOKUP_DIRECTORY) {
|
if (nd->flags & LOOKUP_DIRECTORY) {
|
||||||
@@ -2126,11 +2087,9 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create side of things */
|
/* create side of things */
|
||||||
|
error = complete_walk(nd);
|
||||||
if (nd->flags & LOOKUP_RCU) {
|
if (error)
|
||||||
if (nameidata_drop_rcu_last(nd))
|
return ERR_PTR(error);
|
||||||
return ERR_PTR(-ECHILD);
|
|
||||||
}
|
|
||||||
|
|
||||||
audit_inode(pathname, dir);
|
audit_inode(pathname, dir);
|
||||||
error = -EISDIR;
|
error = -EISDIR;
|
||||||
|
Reference in New Issue
Block a user