aboutsummaryrefslogtreecommitdiffstats
path: root/fs/namei.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2010-04-30 17:17:09 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2010-05-15 07:16:33 -0400
commitd83c49f3e36cecd2e8823b6c48ffba083b8a5704 (patch)
treea304de4eb43652d2a9528d4b43f798ab821c8b93 /fs/namei.c
parent6a251b0ab67989f468f4cb65179e0cf40cf8c295 (diff)
Fix the regression created by "set S_DEAD on unlink()..." commit
1) i_flags simply doesn't work for mount/unlink race prevention; we may have many links to file and rm on one of those obviously shouldn't prevent bind on top of another later on. To fix it right way we need to mark _dentry_ as unsuitable for mounting upon; new flag (DCACHE_CANT_MOUNT) is protected by d_flags and i_mutex on the inode in question. Set it (with dont_mount(dentry)) in unlink/rmdir/etc., check (with cant_mount(dentry)) in places in namespace.c that used to check for S_DEAD. Setting S_DEAD is still needed in places where we used to set it (for directories getting killed), since we rely on it for readdir/rmdir race prevention. 2) rename()/mount() protection has another bogosity - we unhash the target before we'd checked that it's not a mountpoint. Fixed. 3) ancient bogosity in pivot_root() - we locked i_mutex on the right directory, but checked S_DEAD on the different (and wrong) one. Noticed and fixed. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namei.c')
-rw-r--r--fs/namei.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/fs/namei.c b/fs/namei.c
index 16df7277a92..b86b96fe1dc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -2176,8 +2176,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
2176 error = security_inode_rmdir(dir, dentry); 2176 error = security_inode_rmdir(dir, dentry);
2177 if (!error) { 2177 if (!error) {
2178 error = dir->i_op->rmdir(dir, dentry); 2178 error = dir->i_op->rmdir(dir, dentry);
2179 if (!error) 2179 if (!error) {
2180 dentry->d_inode->i_flags |= S_DEAD; 2180 dentry->d_inode->i_flags |= S_DEAD;
2181 dont_mount(dentry);
2182 }
2181 } 2183 }
2182 } 2184 }
2183 mutex_unlock(&dentry->d_inode->i_mutex); 2185 mutex_unlock(&dentry->d_inode->i_mutex);
@@ -2261,7 +2263,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
2261 if (!error) { 2263 if (!error) {
2262 error = dir->i_op->unlink(dir, dentry); 2264 error = dir->i_op->unlink(dir, dentry);
2263 if (!error) 2265 if (!error)
2264 dentry->d_inode->i_flags |= S_DEAD; 2266 dont_mount(dentry);
2265 } 2267 }
2266 } 2268 }
2267 mutex_unlock(&dentry->d_inode->i_mutex); 2269 mutex_unlock(&dentry->d_inode->i_mutex);
@@ -2572,17 +2574,20 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
2572 return error; 2574 return error;
2573 2575
2574 target = new_dentry->d_inode; 2576 target = new_dentry->d_inode;
2575 if (target) { 2577 if (target)
2576 mutex_lock(&target->i_mutex); 2578 mutex_lock(&target->i_mutex);
2577 dentry_unhash(new_dentry);
2578 }
2579 if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) 2579 if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
2580 error = -EBUSY; 2580 error = -EBUSY;
2581 else 2581 else {
2582 if (target)
2583 dentry_unhash(new_dentry);
2582 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); 2584 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
2585 }
2583 if (target) { 2586 if (target) {
2584 if (!error) 2587 if (!error) {
2585 target->i_flags |= S_DEAD; 2588 target->i_flags |= S_DEAD;
2589 dont_mount(new_dentry);
2590 }
2586 mutex_unlock(&target->i_mutex); 2591 mutex_unlock(&target->i_mutex);
2587 if (d_unhashed(new_dentry)) 2592 if (d_unhashed(new_dentry))
2588 d_rehash(new_dentry); 2593 d_rehash(new_dentry);
@@ -2614,7 +2619,7 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
2614 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); 2619 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
2615 if (!error) { 2620 if (!error) {
2616 if (target) 2621 if (target)
2617 target->i_flags |= S_DEAD; 2622 dont_mount(new_dentry);
2618 if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) 2623 if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
2619 d_move(old_dentry, new_dentry); 2624 d_move(old_dentry, new_dentry);
2620 } 2625 }