diff options
Diffstat (limited to 'fs/namei.c')
-rw-r--r-- | fs/namei.c | 35 |
1 files changed, 20 insertions, 15 deletions
diff --git a/fs/namei.c b/fs/namei.c index 8cfbd5fd0c78..af18ede95e2d 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1696,28 +1696,31 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd, if (path_equal(&nd->path, &nd->root)) goto in_root; if (unlikely(nd->path.dentry == nd->path.mnt->mnt_root)) { + struct path path = nd->path; + unsigned seq; + while (1) { - struct mount *mnt = real_mount(nd->path.mnt); + struct mount *mnt = real_mount(path.mnt); struct mount *mparent = mnt->mnt_parent; struct dentry *mountpoint = mnt->mnt_mountpoint; - struct inode *inode = mountpoint->d_inode; - unsigned seq = read_seqcount_begin(&mountpoint->d_seq); - if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) - return ERR_PTR(-ECHILD); - if (&mparent->mnt == nd->path.mnt) + seq = read_seqcount_begin(&mountpoint->d_seq); + if (&mparent->mnt == path.mnt) goto in_root; - if (unlikely(nd->flags & LOOKUP_NO_XDEV)) - return ERR_PTR(-ECHILD); - /* we know that mountpoint was pinned */ - nd->path.dentry = mountpoint; - nd->path.mnt = &mparent->mnt; - nd->inode = inode; - nd->seq = seq; - if (path_equal(&nd->path, &nd->root)) + path.dentry = mountpoint; + path.mnt = &mparent->mnt; + if (path_equal(&path, &nd->root)) goto in_root; - if (nd->path.dentry != nd->path.mnt->mnt_root) + if (path.dentry != path.mnt->mnt_root) break; } + if (unlikely(nd->flags & LOOKUP_NO_XDEV)) + return ERR_PTR(-ECHILD); + nd->path = path; + nd->inode = path.dentry->d_inode; + nd->seq = seq; + if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) + return ERR_PTR(-ECHILD); + /* we know that mountpoint was pinned */ } old = nd->path.dentry; parent = old->d_parent; @@ -1729,6 +1732,8 @@ static struct dentry *follow_dotdot_rcu(struct nameidata *nd, return ERR_PTR(-ECHILD); return parent; in_root: + if (unlikely(read_seqretry(&mount_lock, nd->m_seq))) + return ERR_PTR(-ECHILD); if (unlikely(nd->flags & LOOKUP_BENEATH)) return ERR_PTR(-ECHILD); return NULL; |