Commit 9f1fafee authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 19660af7
...@@ -468,43 +468,6 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry) ...@@ -468,43 +468,6 @@ static int unlazy_walk(struct nameidata *nd, struct dentry *dentry)
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) ...@@ -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
* 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, * If we had been in RCU mode, drop out of it and legitimize nd->path.
* either return the error returned by d_revalidate or -ESTALE if the * Revalidate the final result, unless we'd already done that during
* revalidation it just returned 0. If d_revalidate returns 0, we attempt to * the path walk or the filesystem doesn't ask for it. Return 0 on
* invalidate the dentry. It's up to the caller to handle putting references * success, -error on failure. In case of failure caller does not
* to the path if necessary. * 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; 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) ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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;
......
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