mirror of https://gitee.com/openkylin/linux.git
follow_dotdot{,_rcu}(): massage loops
The logics in both of them is the same: while true if in process' root // uncommon break if *not* in mount root // normal case find the parent return if at absolute root // very uncommon break move to underlying mountpoint report that we are in root Pull the common path out of the loop: if in process' root // uncommon goto in_root if unlikely(in mount root) while true if at absolute root goto in_root move to underlying mountpoint if in process' root goto in_root if in mount root break; find the parent // we are not in mount root return in_root: report that we are in root The reason for that transformation is that we get to keep the common path straight *and* get a separate block for "move through underlying mountpoints", which will allow to sanitize NO_XDEV handling there. What's more, the pared-down loops will be easier to deal with - in particular, non-RCU case has no need to grab mount_lock and rewriting it to the form that wouldn't do that is a non-trivial change. Better do that with less stuff getting in the way... Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
c2df196876
commit
12487f3067
77
fs/namei.c
77
fs/namei.c
|
@ -1691,21 +1691,12 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd,
|
|||
struct inode **inodep,
|
||||
unsigned *seqp)
|
||||
{
|
||||
while (1) {
|
||||
if (path_equal(&nd->path, &nd->root))
|
||||
break;
|
||||
if (nd->path.dentry != nd->path.mnt->mnt_root) {
|
||||
struct dentry *old = nd->path.dentry;
|
||||
struct dentry *parent = old->d_parent;
|
||||
struct dentry *parent, *old;
|
||||
|
||||
*inodep = parent->d_inode;
|
||||
*seqp = read_seqcount_begin(&parent->d_seq);
|
||||
if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq)))
|
||||
return ERR_PTR(-ECHILD);
|
||||
if (unlikely(!path_connected(nd->path.mnt, parent)))
|
||||
return ERR_PTR(-ECHILD);
|
||||
return parent;
|
||||
} else {
|
||||
if (path_equal(&nd->path, &nd->root))
|
||||
goto in_root;
|
||||
if (unlikely(nd->path.dentry == nd->path.mnt->mnt_root)) {
|
||||
while (1) {
|
||||
struct mount *mnt = real_mount(nd->path.mnt);
|
||||
struct mount *mparent = mnt->mnt_parent;
|
||||
struct dentry *mountpoint = mnt->mnt_mountpoint;
|
||||
|
@ -1714,7 +1705,7 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd,
|
|||
if (unlikely(read_seqretry(&mount_lock, nd->m_seq)))
|
||||
return ERR_PTR(-ECHILD);
|
||||
if (&mparent->mnt == nd->path.mnt)
|
||||
break;
|
||||
goto in_root;
|
||||
if (unlikely(nd->flags & LOOKUP_NO_XDEV))
|
||||
return ERR_PTR(-ECHILD);
|
||||
/* we know that mountpoint was pinned */
|
||||
|
@ -1722,8 +1713,22 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd,
|
|||
nd->path.mnt = &mparent->mnt;
|
||||
nd->inode = inode;
|
||||
nd->seq = seq;
|
||||
if (path_equal(&nd->path, &nd->root))
|
||||
goto in_root;
|
||||
if (nd->path.dentry != nd->path.mnt->mnt_root)
|
||||
break;
|
||||
}
|
||||
}
|
||||
old = nd->path.dentry;
|
||||
parent = old->d_parent;
|
||||
*inodep = parent->d_inode;
|
||||
*seqp = read_seqcount_begin(&parent->d_seq);
|
||||
if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq)))
|
||||
return ERR_PTR(-ECHILD);
|
||||
if (unlikely(!path_connected(nd->path.mnt, parent)))
|
||||
return ERR_PTR(-ECHILD);
|
||||
return parent;
|
||||
in_root:
|
||||
if (unlikely(nd->flags & LOOKUP_BENEATH))
|
||||
return ERR_PTR(-ECHILD);
|
||||
return NULL;
|
||||
|
@ -1733,25 +1738,33 @@ static struct dentry *follow_dotdot(struct nameidata *nd,
|
|||
struct inode **inodep,
|
||||
unsigned *seqp)
|
||||
{
|
||||
while (1) {
|
||||
if (path_equal(&nd->path, &nd->root))
|
||||
break;
|
||||
if (nd->path.dentry != nd->path.mnt->mnt_root) {
|
||||
/* rare case of legitimate dget_parent()... */
|
||||
struct dentry *parent = dget_parent(nd->path.dentry);
|
||||
if (unlikely(!path_connected(nd->path.mnt, parent))) {
|
||||
dput(parent);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
*seqp = 0;
|
||||
*inodep = parent->d_inode;
|
||||
return parent;
|
||||
struct dentry *parent;
|
||||
|
||||
if (path_equal(&nd->path, &nd->root))
|
||||
goto in_root;
|
||||
if (unlikely(nd->path.dentry == nd->path.mnt->mnt_root)) {
|
||||
while (1) {
|
||||
if (!follow_up(&nd->path))
|
||||
goto in_root;
|
||||
if (unlikely(nd->flags & LOOKUP_NO_XDEV))
|
||||
return ERR_PTR(-EXDEV);
|
||||
if (path_equal(&nd->path, &nd->root))
|
||||
goto in_root;
|
||||
if (nd->path.dentry != nd->path.mnt->mnt_root)
|
||||
break;
|
||||
}
|
||||
if (!follow_up(&nd->path))
|
||||
break;
|
||||
if (unlikely(nd->flags & LOOKUP_NO_XDEV))
|
||||
return ERR_PTR(-EXDEV);
|
||||
}
|
||||
/* rare case of legitimate dget_parent()... */
|
||||
parent = dget_parent(nd->path.dentry);
|
||||
if (unlikely(!path_connected(nd->path.mnt, parent))) {
|
||||
dput(parent);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
*seqp = 0;
|
||||
*inodep = parent->d_inode;
|
||||
return parent;
|
||||
|
||||
in_root:
|
||||
if (unlikely(nd->flags & LOOKUP_BENEATH))
|
||||
return ERR_PTR(-EXDEV);
|
||||
dget(nd->path.dentry);
|
||||
|
|
Loading…
Reference in New Issue