aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2011-07-12 21:42:24 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2011-07-14 21:33:41 -0400
commit1836750115f20b774e55c032a3893e8c5bdf41ed (patch)
tree3c0cb24361ccfb460b93b0fd6385650df80a26e6 /fs/dcache.c
parent94c0d4ecbe7f9fe56e052b26b2ab484e246c07b4 (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/dcache.c')
-rw-r--r--fs/dcache.c51
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 */
2223void d_move(struct dentry * dentry, struct dentry * target) 2224static 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 */
2287void 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}
2280EXPORT_SYMBOL(d_move); 2293EXPORT_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;
2333out_unalias: 2341out_unalias:
2334 d_move(alias, dentry); 2342 __d_move(alias, dentry);
2335 ret = alias; 2343 ret = alias;
2336out_err: 2344out_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;