aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/usb/core/inode.c1
-rw-r--r--fs/configfs/dir.c4
-rw-r--r--fs/namei.c21
-rw-r--r--fs/namespace.c6
-rw-r--r--include/linux/dcache.h14
5 files changed, 35 insertions, 11 deletions
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 4a6366a42129..111a01a747fc 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -380,6 +380,7 @@ static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
380 mutex_lock(&inode->i_mutex); 380 mutex_lock(&inode->i_mutex);
381 dentry_unhash(dentry); 381 dentry_unhash(dentry);
382 if (usbfs_empty(dentry)) { 382 if (usbfs_empty(dentry)) {
383 dont_mount(dentry);
383 drop_nlink(dentry->d_inode); 384 drop_nlink(dentry->d_inode);
384 drop_nlink(dentry->d_inode); 385 drop_nlink(dentry->d_inode);
385 dput(dentry); 386 dput(dentry);
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c
index 8e48b52205aa..0b502f80c691 100644
--- a/fs/configfs/dir.c
+++ b/fs/configfs/dir.c
@@ -645,6 +645,7 @@ static void detach_groups(struct config_group *group)
645 645
646 configfs_detach_group(sd->s_element); 646 configfs_detach_group(sd->s_element);
647 child->d_inode->i_flags |= S_DEAD; 647 child->d_inode->i_flags |= S_DEAD;
648 dont_mount(child);
648 649
649 mutex_unlock(&child->d_inode->i_mutex); 650 mutex_unlock(&child->d_inode->i_mutex);
650 651
@@ -840,6 +841,7 @@ static int configfs_attach_item(struct config_item *parent_item,
840 mutex_lock(&dentry->d_inode->i_mutex); 841 mutex_lock(&dentry->d_inode->i_mutex);
841 configfs_remove_dir(item); 842 configfs_remove_dir(item);
842 dentry->d_inode->i_flags |= S_DEAD; 843 dentry->d_inode->i_flags |= S_DEAD;
844 dont_mount(dentry);
843 mutex_unlock(&dentry->d_inode->i_mutex); 845 mutex_unlock(&dentry->d_inode->i_mutex);
844 d_delete(dentry); 846 d_delete(dentry);
845 } 847 }
@@ -882,6 +884,7 @@ static int configfs_attach_group(struct config_item *parent_item,
882 if (ret) { 884 if (ret) {
883 configfs_detach_item(item); 885 configfs_detach_item(item);
884 dentry->d_inode->i_flags |= S_DEAD; 886 dentry->d_inode->i_flags |= S_DEAD;
887 dont_mount(dentry);
885 } 888 }
886 configfs_adjust_dir_dirent_depth_after_populate(sd); 889 configfs_adjust_dir_dirent_depth_after_populate(sd);
887 mutex_unlock(&dentry->d_inode->i_mutex); 890 mutex_unlock(&dentry->d_inode->i_mutex);
@@ -1725,6 +1728,7 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
1725 mutex_unlock(&configfs_symlink_mutex); 1728 mutex_unlock(&configfs_symlink_mutex);
1726 configfs_detach_group(&group->cg_item); 1729 configfs_detach_group(&group->cg_item);
1727 dentry->d_inode->i_flags |= S_DEAD; 1730 dentry->d_inode->i_flags |= S_DEAD;
1731 dont_mount(dentry);
1728 mutex_unlock(&dentry->d_inode->i_mutex); 1732 mutex_unlock(&dentry->d_inode->i_mutex);
1729 1733
1730 d_delete(dentry); 1734 d_delete(dentry);
diff --git a/fs/namei.c b/fs/namei.c
index 16df7277a92e..b86b96fe1dc3 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 }
diff --git a/fs/namespace.c b/fs/namespace.c
index 8174c8ab5c70..f20cb57d1067 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1432,7 +1432,7 @@ static int graft_tree(struct vfsmount *mnt, struct path *path)
1432 1432
1433 err = -ENOENT; 1433 err = -ENOENT;
1434 mutex_lock(&path->dentry->d_inode->i_mutex); 1434 mutex_lock(&path->dentry->d_inode->i_mutex);
1435 if (IS_DEADDIR(path->dentry->d_inode)) 1435 if (cant_mount(path->dentry))
1436 goto out_unlock; 1436 goto out_unlock;
1437 1437
1438 err = security_sb_check_sb(mnt, path); 1438 err = security_sb_check_sb(mnt, path);
@@ -1623,7 +1623,7 @@ static int do_move_mount(struct path *path, char *old_name)
1623 1623
1624 err = -ENOENT; 1624 err = -ENOENT;
1625 mutex_lock(&path->dentry->d_inode->i_mutex); 1625 mutex_lock(&path->dentry->d_inode->i_mutex);
1626 if (IS_DEADDIR(path->dentry->d_inode)) 1626 if (cant_mount(path->dentry))
1627 goto out1; 1627 goto out1;
1628 1628
1629 if (d_unlinked(path->dentry)) 1629 if (d_unlinked(path->dentry))
@@ -2234,7 +2234,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
2234 if (!check_mnt(root.mnt)) 2234 if (!check_mnt(root.mnt))
2235 goto out2; 2235 goto out2;
2236 error = -ENOENT; 2236 error = -ENOENT;
2237 if (IS_DEADDIR(new.dentry->d_inode)) 2237 if (cant_mount(old.dentry))
2238 goto out2; 2238 goto out2;
2239 if (d_unlinked(new.dentry)) 2239 if (d_unlinked(new.dentry))
2240 goto out2; 2240 goto out2;
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 30b93b2a01a4..eebb617c17d8 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -186,6 +186,8 @@ d_iput: no no no yes
186 186
187#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */ 187#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */
188 188
189#define DCACHE_CANT_MOUNT 0x0100
190
189extern spinlock_t dcache_lock; 191extern spinlock_t dcache_lock;
190extern seqlock_t rename_lock; 192extern seqlock_t rename_lock;
191 193
@@ -358,6 +360,18 @@ static inline int d_unlinked(struct dentry *dentry)
358 return d_unhashed(dentry) && !IS_ROOT(dentry); 360 return d_unhashed(dentry) && !IS_ROOT(dentry);
359} 361}
360 362
363static inline int cant_mount(struct dentry *dentry)
364{
365 return (dentry->d_flags & DCACHE_CANT_MOUNT);
366}
367
368static inline void dont_mount(struct dentry *dentry)
369{
370 spin_lock(&dentry->d_lock);
371 dentry->d_flags |= DCACHE_CANT_MOUNT;
372 spin_unlock(&dentry->d_lock);
373}
374
361static inline struct dentry *dget_parent(struct dentry *dentry) 375static inline struct dentry *dget_parent(struct dentry *dentry)
362{ 376{
363 struct dentry *ret; 377 struct dentry *ret;