diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2011-07-12 21:42:24 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-07-14 21:33:41 -0400 |
commit | 1836750115f20b774e55c032a3893e8c5bdf41ed (patch) | |
tree | 3c0cb24361ccfb460b93b0fd6385650df80a26e6 /fs | |
parent | 94c0d4ecbe7f9fe56e052b26b2ab484e246c07b4 (diff) |
fix loop checks in d_materialise_unique()
Both __d_unalias() and __d_materialise_dentry() need loop prevention.
Grab rename_lock in caller, check for loops there...
As a side benefit, we have dentry_lock_for_move() called only under
rename_lock, which seriously reduces deadlock potential of the
execrable "locking order" used for ->d_lock.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 51 |
1 files changed, 34 insertions, 17 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 37f72ee5bf7c..6e4ea6d87774 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -2213,14 +2213,15 @@ static void dentry_unlock_parents_for_move(struct dentry *dentry, | |||
2213 | * The hash value has to match the hash queue that the dentry is on.. | 2213 | * The hash value has to match the hash queue that the dentry is on.. |
2214 | */ | 2214 | */ |
2215 | /* | 2215 | /* |
2216 | * d_move - move a dentry | 2216 | * __d_move - move a dentry |
2217 | * @dentry: entry to move | 2217 | * @dentry: entry to move |
2218 | * @target: new dentry | 2218 | * @target: new dentry |
2219 | * | 2219 | * |
2220 | * Update the dcache to reflect the move of a file name. Negative | 2220 | * Update the dcache to reflect the move of a file name. Negative |
2221 | * dcache entries should not be moved in this way. | 2221 | * dcache entries should not be moved in this way. Caller hold |
2222 | * rename_lock. | ||
2222 | */ | 2223 | */ |
2223 | void d_move(struct dentry * dentry, struct dentry * target) | 2224 | static void __d_move(struct dentry * dentry, struct dentry * target) |
2224 | { | 2225 | { |
2225 | if (!dentry->d_inode) | 2226 | if (!dentry->d_inode) |
2226 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); | 2227 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); |
@@ -2228,8 +2229,6 @@ void d_move(struct dentry * dentry, struct dentry * target) | |||
2228 | BUG_ON(d_ancestor(dentry, target)); | 2229 | BUG_ON(d_ancestor(dentry, target)); |
2229 | BUG_ON(d_ancestor(target, dentry)); | 2230 | BUG_ON(d_ancestor(target, dentry)); |
2230 | 2231 | ||
2231 | write_seqlock(&rename_lock); | ||
2232 | |||
2233 | dentry_lock_for_move(dentry, target); | 2232 | dentry_lock_for_move(dentry, target); |
2234 | 2233 | ||
2235 | write_seqcount_begin(&dentry->d_seq); | 2234 | write_seqcount_begin(&dentry->d_seq); |
@@ -2275,6 +2274,20 @@ void d_move(struct dentry * dentry, struct dentry * target) | |||
2275 | spin_unlock(&target->d_lock); | 2274 | spin_unlock(&target->d_lock); |
2276 | fsnotify_d_move(dentry); | 2275 | fsnotify_d_move(dentry); |
2277 | spin_unlock(&dentry->d_lock); | 2276 | spin_unlock(&dentry->d_lock); |
2277 | } | ||
2278 | |||
2279 | /* | ||
2280 | * d_move - move a dentry | ||
2281 | * @dentry: entry to move | ||
2282 | * @target: new dentry | ||
2283 | * | ||
2284 | * Update the dcache to reflect the move of a file name. Negative | ||
2285 | * dcache entries should not be moved in this way. | ||
2286 | */ | ||
2287 | void d_move(struct dentry *dentry, struct dentry *target) | ||
2288 | { | ||
2289 | write_seqlock(&rename_lock); | ||
2290 | __d_move(dentry, target); | ||
2278 | write_sequnlock(&rename_lock); | 2291 | write_sequnlock(&rename_lock); |
2279 | } | 2292 | } |
2280 | EXPORT_SYMBOL(d_move); | 2293 | EXPORT_SYMBOL(d_move); |
@@ -2302,7 +2315,7 @@ struct dentry *d_ancestor(struct dentry *p1, struct dentry *p2) | |||
2302 | * This helper attempts to cope with remotely renamed directories | 2315 | * This helper attempts to cope with remotely renamed directories |
2303 | * | 2316 | * |
2304 | * It assumes that the caller is already holding | 2317 | * It assumes that the caller is already holding |
2305 | * dentry->d_parent->d_inode->i_mutex and the inode->i_lock | 2318 | * dentry->d_parent->d_inode->i_mutex, inode->i_lock and rename_lock |
2306 | * | 2319 | * |
2307 | * Note: If ever the locking in lock_rename() changes, then please | 2320 | * Note: If ever the locking in lock_rename() changes, then please |
2308 | * remember to update this too... | 2321 | * remember to update this too... |
@@ -2317,11 +2330,6 @@ static struct dentry *__d_unalias(struct inode *inode, | |||
2317 | if (alias->d_parent == dentry->d_parent) | 2330 | if (alias->d_parent == dentry->d_parent) |
2318 | goto out_unalias; | 2331 | goto out_unalias; |
2319 | 2332 | ||
2320 | /* Check for loops */ | ||
2321 | ret = ERR_PTR(-ELOOP); | ||
2322 | if (d_ancestor(alias, dentry)) | ||
2323 | goto out_err; | ||
2324 | |||
2325 | /* See lock_rename() */ | 2333 | /* See lock_rename() */ |
2326 | ret = ERR_PTR(-EBUSY); | 2334 | ret = ERR_PTR(-EBUSY); |
2327 | if (!mutex_trylock(&dentry->d_sb->s_vfs_rename_mutex)) | 2335 | if (!mutex_trylock(&dentry->d_sb->s_vfs_rename_mutex)) |
@@ -2331,7 +2339,7 @@ static struct dentry *__d_unalias(struct inode *inode, | |||
2331 | goto out_err; | 2339 | goto out_err; |
2332 | m2 = &alias->d_parent->d_inode->i_mutex; | 2340 | m2 = &alias->d_parent->d_inode->i_mutex; |
2333 | out_unalias: | 2341 | out_unalias: |
2334 | d_move(alias, dentry); | 2342 | __d_move(alias, dentry); |
2335 | ret = alias; | 2343 | ret = alias; |
2336 | out_err: | 2344 | out_err: |
2337 | spin_unlock(&inode->i_lock); | 2345 | spin_unlock(&inode->i_lock); |
@@ -2416,15 +2424,24 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) | |||
2416 | alias = __d_find_alias(inode, 0); | 2424 | alias = __d_find_alias(inode, 0); |
2417 | if (alias) { | 2425 | if (alias) { |
2418 | actual = alias; | 2426 | actual = alias; |
2419 | /* Is this an anonymous mountpoint that we could splice | 2427 | write_seqlock(&rename_lock); |
2420 | * into our tree? */ | 2428 | |
2421 | if (IS_ROOT(alias)) { | 2429 | if (d_ancestor(alias, dentry)) { |
2430 | /* Check for loops */ | ||
2431 | actual = ERR_PTR(-ELOOP); | ||
2432 | } else if (IS_ROOT(alias)) { | ||
2433 | /* Is this an anonymous mountpoint that we | ||
2434 | * could splice into our tree? */ | ||
2422 | __d_materialise_dentry(dentry, alias); | 2435 | __d_materialise_dentry(dentry, alias); |
2436 | write_sequnlock(&rename_lock); | ||
2423 | __d_drop(alias); | 2437 | __d_drop(alias); |
2424 | goto found; | 2438 | goto found; |
2439 | } else { | ||
2440 | /* Nope, but we must(!) avoid directory | ||
2441 | * aliasing */ | ||
2442 | actual = __d_unalias(inode, dentry, alias); | ||
2425 | } | 2443 | } |
2426 | /* Nope, but we must(!) avoid directory aliasing */ | 2444 | write_sequnlock(&rename_lock); |
2427 | actual = __d_unalias(inode, dentry, alias); | ||
2428 | if (IS_ERR(actual)) | 2445 | if (IS_ERR(actual)) |
2429 | dput(alias); | 2446 | dput(alias); |
2430 | goto out_nolock; | 2447 | goto out_nolock; |