diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2020-02-26 14:59:56 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2020-04-02 01:09:19 -0400 |
commit | 12487f3067d8d68a90dd2fc0dbc2a287ae699236 (patch) | |
tree | 0e3445827c7ed76ca3c476f4302f7ae3d2c2d369 /fs/namei.c | |
parent | c2df196876609e00d90909f51b7a3392dc8e377c (diff) |
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>
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 77 |
1 files changed, 45 insertions, 32 deletions
diff --git a/fs/namei.c b/fs/namei.c index fda239b9baff..8cfbd5fd0c78 100644 --- a/fs/namei.c +++ b/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); |