vfs: fix the rest of sget() races
unfortunately, just checking MS_BORN after having grabbed ->s_umount in sget() is not enough; places that pick superblock from a list and grab s_umount shared need the same check in addition to checking for ->s_root; otherwise three-way race between failing mount, sget() and such list-walker can leave us with list-walker coming *second*, when temporary active ref grabbed by sget() (to be dropped when sget() notices that original mount has failed by checking MS_BORN) has lead to deactivate_locked_super() from failing ->mount() *not* doing ->kill_sb() and just releasing ->s_umount. Once sget() gets through and notices that MS_BORN had never been set it will drop the active ref and fs will be shut down and kicked out of all lists, but it's too late for something like sync_supers(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
cf31e70d6c
commit
dabe0dc194
20
fs/super.c
20
fs/super.c
|
@ -337,7 +337,7 @@ bool grab_super_passive(struct super_block *sb)
|
|||
spin_unlock(&sb_lock);
|
||||
|
||||
if (down_read_trylock(&sb->s_umount)) {
|
||||
if (sb->s_root)
|
||||
if (sb->s_root && (sb->s_flags & MS_BORN))
|
||||
return true;
|
||||
up_read(&sb->s_umount);
|
||||
}
|
||||
|
@ -505,7 +505,7 @@ void sync_supers(void)
|
|||
spin_unlock(&sb_lock);
|
||||
|
||||
down_read(&sb->s_umount);
|
||||
if (sb->s_root && sb->s_dirt)
|
||||
if (sb->s_root && sb->s_dirt && (sb->s_flags & MS_BORN))
|
||||
sb->s_op->write_super(sb);
|
||||
up_read(&sb->s_umount);
|
||||
|
||||
|
@ -540,7 +540,7 @@ void iterate_supers(void (*f)(struct super_block *, void *), void *arg)
|
|||
spin_unlock(&sb_lock);
|
||||
|
||||
down_read(&sb->s_umount);
|
||||
if (sb->s_root)
|
||||
if (sb->s_root && (sb->s_flags & MS_BORN))
|
||||
f(sb, arg);
|
||||
up_read(&sb->s_umount);
|
||||
|
||||
|
@ -575,7 +575,7 @@ void iterate_supers_type(struct file_system_type *type,
|
|||
spin_unlock(&sb_lock);
|
||||
|
||||
down_read(&sb->s_umount);
|
||||
if (sb->s_root)
|
||||
if (sb->s_root && (sb->s_flags & MS_BORN))
|
||||
f(sb, arg);
|
||||
up_read(&sb->s_umount);
|
||||
|
||||
|
@ -616,7 +616,7 @@ rescan:
|
|||
spin_unlock(&sb_lock);
|
||||
down_read(&sb->s_umount);
|
||||
/* still alive? */
|
||||
if (sb->s_root)
|
||||
if (sb->s_root && (sb->s_flags & MS_BORN))
|
||||
return sb;
|
||||
up_read(&sb->s_umount);
|
||||
/* nope, got unmounted */
|
||||
|
@ -676,7 +676,7 @@ rescan:
|
|||
spin_unlock(&sb_lock);
|
||||
down_read(&sb->s_umount);
|
||||
/* still alive? */
|
||||
if (sb->s_root)
|
||||
if (sb->s_root && (sb->s_flags & MS_BORN))
|
||||
return sb;
|
||||
up_read(&sb->s_umount);
|
||||
/* nope, got unmounted */
|
||||
|
@ -763,7 +763,8 @@ static void do_emergency_remount(struct work_struct *work)
|
|||
sb->s_count++;
|
||||
spin_unlock(&sb_lock);
|
||||
down_write(&sb->s_umount);
|
||||
if (sb->s_root && sb->s_bdev && !(sb->s_flags & MS_RDONLY)) {
|
||||
if (sb->s_root && sb->s_bdev && (sb->s_flags & MS_BORN) &&
|
||||
!(sb->s_flags & MS_RDONLY)) {
|
||||
/*
|
||||
* What lock protects sb->s_flags??
|
||||
*/
|
||||
|
@ -1146,6 +1147,11 @@ int freeze_super(struct super_block *sb)
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!(sb->s_flags & MS_BORN)) {
|
||||
up_write(&sb->s_umount);
|
||||
return 0; /* sic - it's "nothing to do" */
|
||||
}
|
||||
|
||||
if (sb->s_flags & MS_RDONLY) {
|
||||
sb->s_frozen = SB_FREEZE_TRANS;
|
||||
smp_wmb();
|
||||
|
|
Loading…
Reference in New Issue