summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-08-01 19:59:28 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2015-08-01 20:18:38 -0400
commit97242f99a013950af63effa0732f8ef7db4e31ec (patch)
tree5cb822a4df9340f2b51a12a66d1653b0a6a1826e
parentcbfe8fa6cd672011c755c3cd85c9ffd4e2d10a6f (diff)
link_path_walk(): be careful when failing with ENOTDIR
In RCU mode we might end up with dentry evicted just we check that it's a directory. In such case we should return ECHILD rather than ENOTDIR, so that pathwalk would be retries in non-RCU mode. Breakage had been introduced in commit b18825a - prior to that we were looking at nd->inode, which had been fetched before verifying that ->d_seq was still valid. That form of check would only be satisfied if at some point the pathname prefix would indeed have resolved to a non-directory. The fix consists of checking ->d_seq after we'd run into a non-directory dentry, and failing with ECHILD in case of mismatch. Note that all branches since 3.12 have that problem... Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/namei.c7
1 files changed, 6 insertions, 1 deletions
diff --git a/fs/namei.c b/fs/namei.c
index ae4e4c18b2ac..fbbcf0993312 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1954,8 +1954,13 @@ OK:
continue;
}
}
- if (unlikely(!d_can_lookup(nd->path.dentry)))
+ if (unlikely(!d_can_lookup(nd->path.dentry))) {
+ if (nd->flags & LOOKUP_RCU) {
+ if (unlazy_walk(nd, NULL, 0))
+ return -ECHILD;
+ }
return -ENOTDIR;
+ }
}
}