summaryrefslogtreecommitdiff
path: root/fs/namei.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2020-02-26 14:59:56 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2020-04-02 01:09:19 -0400
commit12487f3067d8d68a90dd2fc0dbc2a287ae699236 (patch)
tree0e3445827c7ed76ca3c476f4302f7ae3d2c2d369 /fs/namei.c
parentc2df196876609e00d90909f51b7a3392dc8e377c (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.c77
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);