Commit 12487f30 authored by Al Viro's avatar Al Viro

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: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent c2df1968
...@@ -1691,21 +1691,12 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd, ...@@ -1691,21 +1691,12 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd,
struct inode **inodep, struct inode **inodep,
unsigned *seqp) unsigned *seqp)
{ {
while (1) { struct dentry *parent, *old;
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;
*inodep = parent->d_inode; if (path_equal(&nd->path, &nd->root))
*seqp = read_seqcount_begin(&parent->d_seq); goto in_root;
if (unlikely(read_seqcount_retry(&old->d_seq, nd->seq))) if (unlikely(nd->path.dentry == nd->path.mnt->mnt_root)) {
return ERR_PTR(-ECHILD); while (1) {
if (unlikely(!path_connected(nd->path.mnt, parent)))
return ERR_PTR(-ECHILD);
return parent;
} else {
struct mount *mnt = real_mount(nd->path.mnt); struct mount *mnt = real_mount(nd->path.mnt);
struct mount *mparent = mnt->mnt_parent; struct mount *mparent = mnt->mnt_parent;
struct dentry *mountpoint = mnt->mnt_mountpoint; struct dentry *mountpoint = mnt->mnt_mountpoint;
...@@ -1714,7 +1705,7 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd, ...@@ -1714,7 +1705,7 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd,
if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) if (unlikely(read_seqretry(&mount_lock, nd->m_seq)))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
if (&mparent->mnt == nd->path.mnt) if (&mparent->mnt == nd->path.mnt)
break; goto in_root;
if (unlikely(nd->flags & LOOKUP_NO_XDEV)) if (unlikely(nd->flags & LOOKUP_NO_XDEV))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
/* we know that mountpoint was pinned */ /* we know that mountpoint was pinned */
...@@ -1722,8 +1713,22 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd, ...@@ -1722,8 +1713,22 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd,
nd->path.mnt = &mparent->mnt; nd->path.mnt = &mparent->mnt;
nd->inode = inode; nd->inode = inode;
nd->seq = seq; 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)) if (unlikely(nd->flags & LOOKUP_BENEATH))
return ERR_PTR(-ECHILD); return ERR_PTR(-ECHILD);
return NULL; return NULL;
...@@ -1733,12 +1738,24 @@ static struct dentry *follow_dotdot(struct nameidata *nd, ...@@ -1733,12 +1738,24 @@ static struct dentry *follow_dotdot(struct nameidata *nd,
struct inode **inodep, struct inode **inodep,
unsigned *seqp) unsigned *seqp)
{ {
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) { 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)) if (path_equal(&nd->path, &nd->root))
goto in_root;
if (nd->path.dentry != nd->path.mnt->mnt_root)
break; break;
if (nd->path.dentry != nd->path.mnt->mnt_root) { }
}
/* rare case of legitimate dget_parent()... */ /* rare case of legitimate dget_parent()... */
struct dentry *parent = dget_parent(nd->path.dentry); parent = dget_parent(nd->path.dentry);
if (unlikely(!path_connected(nd->path.mnt, parent))) { if (unlikely(!path_connected(nd->path.mnt, parent))) {
dput(parent); dput(parent);
return ERR_PTR(-ENOENT); return ERR_PTR(-ENOENT);
...@@ -1746,12 +1763,8 @@ static struct dentry *follow_dotdot(struct nameidata *nd, ...@@ -1746,12 +1763,8 @@ static struct dentry *follow_dotdot(struct nameidata *nd,
*seqp = 0; *seqp = 0;
*inodep = parent->d_inode; *inodep = parent->d_inode;
return parent; return parent;
}
if (!follow_up(&nd->path)) in_root:
break;
if (unlikely(nd->flags & LOOKUP_NO_XDEV))
return ERR_PTR(-EXDEV);
}
if (unlikely(nd->flags & LOOKUP_BENEATH)) if (unlikely(nd->flags & LOOKUP_BENEATH))
return ERR_PTR(-EXDEV); return ERR_PTR(-EXDEV);
dget(nd->path.dentry); dget(nd->path.dentry);
......
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