Skip to content

Commit d83c49f

Browse files
author
Al Viro
committed
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>
1 parent 6a251b0 commit d83c49f

File tree

5 files changed

+35
-11
lines changed

5 files changed

+35
-11
lines changed

drivers/usb/core/inode.c

+1
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@ static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
380380
mutex_lock(&inode->i_mutex);
381381
dentry_unhash(dentry);
382382
if (usbfs_empty(dentry)) {
383+
dont_mount(dentry);
383384
drop_nlink(dentry->d_inode);
384385
drop_nlink(dentry->d_inode);
385386
dput(dentry);

fs/configfs/dir.c

+4
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ static void detach_groups(struct config_group *group)
645645

646646
configfs_detach_group(sd->s_element);
647647
child->d_inode->i_flags |= S_DEAD;
648+
dont_mount(child);
648649

649650
mutex_unlock(&child->d_inode->i_mutex);
650651

@@ -840,6 +841,7 @@ static int configfs_attach_item(struct config_item *parent_item,
840841
mutex_lock(&dentry->d_inode->i_mutex);
841842
configfs_remove_dir(item);
842843
dentry->d_inode->i_flags |= S_DEAD;
844+
dont_mount(dentry);
843845
mutex_unlock(&dentry->d_inode->i_mutex);
844846
d_delete(dentry);
845847
}
@@ -882,6 +884,7 @@ static int configfs_attach_group(struct config_item *parent_item,
882884
if (ret) {
883885
configfs_detach_item(item);
884886
dentry->d_inode->i_flags |= S_DEAD;
887+
dont_mount(dentry);
885888
}
886889
configfs_adjust_dir_dirent_depth_after_populate(sd);
887890
mutex_unlock(&dentry->d_inode->i_mutex);
@@ -1725,6 +1728,7 @@ void configfs_unregister_subsystem(struct configfs_subsystem *subsys)
17251728
mutex_unlock(&configfs_symlink_mutex);
17261729
configfs_detach_group(&group->cg_item);
17271730
dentry->d_inode->i_flags |= S_DEAD;
1731+
dont_mount(dentry);
17281732
mutex_unlock(&dentry->d_inode->i_mutex);
17291733

17301734
d_delete(dentry);

fs/namei.c

+13-8
Original file line numberDiff line numberDiff line change
@@ -2176,8 +2176,10 @@ int vfs_rmdir(struct inode *dir, struct dentry *dentry)
21762176
error = security_inode_rmdir(dir, dentry);
21772177
if (!error) {
21782178
error = dir->i_op->rmdir(dir, dentry);
2179-
if (!error)
2179+
if (!error) {
21802180
dentry->d_inode->i_flags |= S_DEAD;
2181+
dont_mount(dentry);
2182+
}
21812183
}
21822184
}
21832185
mutex_unlock(&dentry->d_inode->i_mutex);
@@ -2261,7 +2263,7 @@ int vfs_unlink(struct inode *dir, struct dentry *dentry)
22612263
if (!error) {
22622264
error = dir->i_op->unlink(dir, dentry);
22632265
if (!error)
2264-
dentry->d_inode->i_flags |= S_DEAD;
2266+
dont_mount(dentry);
22652267
}
22662268
}
22672269
mutex_unlock(&dentry->d_inode->i_mutex);
@@ -2572,17 +2574,20 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
25722574
return error;
25732575

25742576
target = new_dentry->d_inode;
2575-
if (target) {
2577+
if (target)
25762578
mutex_lock(&target->i_mutex);
2577-
dentry_unhash(new_dentry);
2578-
}
25792579
if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
25802580
error = -EBUSY;
2581-
else
2581+
else {
2582+
if (target)
2583+
dentry_unhash(new_dentry);
25822584
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
2585+
}
25832586
if (target) {
2584-
if (!error)
2587+
if (!error) {
25852588
target->i_flags |= S_DEAD;
2589+
dont_mount(new_dentry);
2590+
}
25862591
mutex_unlock(&target->i_mutex);
25872592
if (d_unhashed(new_dentry))
25882593
d_rehash(new_dentry);
@@ -2614,7 +2619,7 @@ static int vfs_rename_other(struct inode *old_dir, struct dentry *old_dentry,
26142619
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
26152620
if (!error) {
26162621
if (target)
2617-
target->i_flags |= S_DEAD;
2622+
dont_mount(new_dentry);
26182623
if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
26192624
d_move(old_dentry, new_dentry);
26202625
}

fs/namespace.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -1432,7 +1432,7 @@ static int graft_tree(struct vfsmount *mnt, struct path *path)
14321432

14331433
err = -ENOENT;
14341434
mutex_lock(&path->dentry->d_inode->i_mutex);
1435-
if (IS_DEADDIR(path->dentry->d_inode))
1435+
if (cant_mount(path->dentry))
14361436
goto out_unlock;
14371437

14381438
err = security_sb_check_sb(mnt, path);
@@ -1623,7 +1623,7 @@ static int do_move_mount(struct path *path, char *old_name)
16231623

16241624
err = -ENOENT;
16251625
mutex_lock(&path->dentry->d_inode->i_mutex);
1626-
if (IS_DEADDIR(path->dentry->d_inode))
1626+
if (cant_mount(path->dentry))
16271627
goto out1;
16281628

16291629
if (d_unlinked(path->dentry))
@@ -2234,7 +2234,7 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
22342234
if (!check_mnt(root.mnt))
22352235
goto out2;
22362236
error = -ENOENT;
2237-
if (IS_DEADDIR(new.dentry->d_inode))
2237+
if (cant_mount(old.dentry))
22382238
goto out2;
22392239
if (d_unlinked(new.dentry))
22402240
goto out2;

include/linux/dcache.h

+14
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ d_iput: no no no yes
186186

187187
#define DCACHE_FSNOTIFY_PARENT_WATCHED 0x0080 /* Parent inode is watched by some fsnotify listener */
188188

189+
#define DCACHE_CANT_MOUNT 0x0100
190+
189191
extern spinlock_t dcache_lock;
190192
extern seqlock_t rename_lock;
191193

@@ -358,6 +360,18 @@ static inline int d_unlinked(struct dentry *dentry)
358360
return d_unhashed(dentry) && !IS_ROOT(dentry);
359361
}
360362

363+
static inline int cant_mount(struct dentry *dentry)
364+
{
365+
return (dentry->d_flags & DCACHE_CANT_MOUNT);
366+
}
367+
368+
static 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+
361375
static inline struct dentry *dget_parent(struct dentry *dentry)
362376
{
363377
struct dentry *ret;

0 commit comments

Comments
 (0)