Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

Al Viro d83c49f3 6a251b0a

+35 -11
+1
drivers/usb/core/inode.c
··· 380 380 mutex_lock(&inode->i_mutex); 381 381 dentry_unhash(dentry); 382 382 if (usbfs_empty(dentry)) { 383 + dont_mount(dentry); 383 384 drop_nlink(dentry->d_inode); 384 385 drop_nlink(dentry->d_inode); 385 386 dput(dentry);
+4
fs/configfs/dir.c
··· 645 645 646 646 configfs_detach_group(sd->s_element); 647 647 child->d_inode->i_flags |= S_DEAD; 648 + dont_mount(child); 648 649 649 650 mutex_unlock(&child->d_inode->i_mutex); 650 651 ··· 841 840 mutex_lock(&dentry->d_inode->i_mutex); 842 841 configfs_remove_dir(item); 843 842 dentry->d_inode->i_flags |= S_DEAD; 843 + dont_mount(dentry); 844 844 mutex_unlock(&dentry->d_inode->i_mutex); 845 845 d_delete(dentry); 846 846 } ··· 884 882 if (ret) { 885 883 configfs_detach_item(item); 886 884 dentry->d_inode->i_flags |= S_DEAD; 885 + dont_mount(dentry); 887 886 } 888 887 configfs_adjust_dir_dirent_depth_after_populate(sd); 889 888 mutex_unlock(&dentry->d_inode->i_mutex); ··· 1728 1725 mutex_unlock(&configfs_symlink_mutex); 1729 1726 configfs_detach_group(&group->cg_item); 1730 1727 dentry->d_inode->i_flags |= S_DEAD; 1728 + dont_mount(dentry); 1731 1729 mutex_unlock(&dentry->d_inode->i_mutex); 1732 1730 1733 1731 d_delete(dentry);
+13 -8
fs/namei.c
··· 2176 2176 error = security_inode_rmdir(dir, dentry); 2177 2177 if (!error) { 2178 2178 error = dir->i_op->rmdir(dir, dentry); 2179 - if (!error) 2179 + if (!error) { 2180 2180 dentry->d_inode->i_flags |= S_DEAD; 2181 + dont_mount(dentry); 2182 + } 2181 2183 } 2182 2184 } 2183 2185 mutex_unlock(&dentry->d_inode->i_mutex); ··· 2263 2261 if (!error) { 2264 2262 error = dir->i_op->unlink(dir, dentry); 2265 2263 if (!error) 2266 - dentry->d_inode->i_flags |= S_DEAD; 2264 + dont_mount(dentry); 2267 2265 } 2268 2266 } 2269 2267 mutex_unlock(&dentry->d_inode->i_mutex); ··· 2574 2572 return error; 2575 2573 2576 2574 target = new_dentry->d_inode; 2577 - if (target) { 2575 + if (target) 2578 2576 mutex_lock(&target->i_mutex); 2579 - dentry_unhash(new_dentry); 2580 - } 2581 2577 if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry)) 2582 2578 error = -EBUSY; 2583 - else 2579 + else { 2580 + if (target) 2581 + dentry_unhash(new_dentry); 2584 2582 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); 2583 + } 2585 2584 if (target) { 2586 - if (!error) 2585 + if (!error) { 2587 2586 target->i_flags |= S_DEAD; 2587 + dont_mount(new_dentry); 2588 + } 2588 2589 mutex_unlock(&target->i_mutex); 2589 2590 if (d_unhashed(new_dentry)) 2590 2591 d_rehash(new_dentry); ··· 2619 2614 error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry); 2620 2615 if (!error) { 2621 2616 if (target) 2622 - target->i_flags |= S_DEAD; 2617 + dont_mount(new_dentry); 2623 2618 if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) 2624 2619 d_move(old_dentry, new_dentry); 2625 2620 }
+3 -3
fs/namespace.c
··· 1432 1432 1433 1433 err = -ENOENT; 1434 1434 mutex_lock(&path->dentry->d_inode->i_mutex); 1435 - if (IS_DEADDIR(path->dentry->d_inode)) 1435 + if (cant_mount(path->dentry)) 1436 1436 goto out_unlock; 1437 1437 1438 1438 err = security_sb_check_sb(mnt, path); ··· 1623 1623 1624 1624 err = -ENOENT; 1625 1625 mutex_lock(&path->dentry->d_inode->i_mutex); 1626 - if (IS_DEADDIR(path->dentry->d_inode)) 1626 + if (cant_mount(path->dentry)) 1627 1627 goto out1; 1628 1628 1629 1629 if (d_unlinked(path->dentry)) ··· 2234 2234 if (!check_mnt(root.mnt)) 2235 2235 goto out2; 2236 2236 error = -ENOENT; 2237 - if (IS_DEADDIR(new.dentry->d_inode)) 2237 + if (cant_mount(old.dentry)) 2238 2238 goto out2; 2239 2239 if (d_unlinked(new.dentry)) 2240 2240 goto out2;
+14
include/linux/dcache.h
··· 186 186 187 187 #define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */ 188 188 189 + #define DCACHE_CANT_MOUNT 0x0100 190 + 189 191 extern spinlock_t dcache_lock; 190 192 extern seqlock_t rename_lock; 191 193 ··· 358 356 static inline int d_unlinked(struct dentry *dentry) 359 357 { 360 358 return d_unhashed(dentry) && !IS_ROOT(dentry); 359 + } 360 + 361 + static inline int cant_mount(struct dentry *dentry) 362 + { 363 + return (dentry->d_flags & DCACHE_CANT_MOUNT); 364 + } 365 + 366 + static inline void dont_mount(struct dentry *dentry) 367 + { 368 + spin_lock(&dentry->d_lock); 369 + dentry->d_flags |= DCACHE_CANT_MOUNT; 370 + spin_unlock(&dentry->d_lock); 361 371 } 362 372 363 373 static inline struct dentry *dget_parent(struct dentry *dentry)