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...
Unified · Split
| @@ -1954,8 +1954,13 @@ OK: · 1953 unmodified lines | |||
| 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 | ||