diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2015-08-01 19:59:28 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-08-01 20:18:38 -0400 |
commit | 97242f99a013950af63effa0732f8ef7db4e31ec (patch) | |
tree | 5cb822a4df9340f2b51a12a66d1653b0a6a1826e | |
parent | cbfe8fa6cd672011c755c3cd85c9ffd4e2d10a6f (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.c | 7 |
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: | |||
1954 | continue; | 1954 | continue; |
1955 | } | 1955 | } |
1956 | } | 1956 | } |
1957 | if (unlikely(!d_can_lookup(nd->path.dentry))) | 1957 | if (unlikely(!d_can_lookup(nd->path.dentry))) { |
1958 | if (nd->flags & LOOKUP_RCU) { | ||
1959 | if (unlazy_walk(nd, NULL, 0)) | ||
1960 | return -ECHILD; | ||
1961 | } | ||
1958 | return -ENOTDIR; | 1962 | return -ENOTDIR; |
1963 | } | ||
1959 | } | 1964 | } |
1960 | } | 1965 | } |
1961 | 1966 | ||