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 | 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 | 1962 | return -ENOTDIR; |
| 1963 | + } | |
| 1959 | 1964 | } |
| 1960 | 1965 | } |
| 1961 | 1966 | |