Commit 74dbbdd7 authored by Al Viro's avatar Al Viro

New helper: deactivate_locked_super()

Does equivalent of up_write(&s->s_umount); deactivate_super(s);
However, it does not does not unlock it until it's all over.
As the result, it's safe to use to dispose of new superblock on ->get_sb()
failure exits - nobody will see the sucker until it's all over.
Equivalent using up_write/deactivate_super is safe for that purpose
if superblock is either	safe to use or has NULL ->s_root when we unlock.
Normally filesystems take the required precautions, but
	a) we do have bugs in that area in some of them.
	b) up_write/deactivate_super sequence is extremely common,
so the helper makes sense anyway.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 677c9b2e
...@@ -207,6 +207,34 @@ void deactivate_super(struct super_block *s) ...@@ -207,6 +207,34 @@ void deactivate_super(struct super_block *s)
EXPORT_SYMBOL(deactivate_super); EXPORT_SYMBOL(deactivate_super);
/**
* deactivate_locked_super - drop an active reference to superblock
* @s: superblock to deactivate
*
* Equivalent of up_write(&s->s_umount); deactivate_super(s);, except that
* it does not unlock it until it's all over. As the result, it's safe to
* use to dispose of new superblock on ->get_sb() failure exits - nobody
* will see the sucker until it's all over. Equivalent using up_write +
* deactivate_super is safe for that purpose only if superblock is either
* safe to use or has NULL ->s_root when we unlock.
*/
void deactivate_locked_super(struct super_block *s)
{
struct file_system_type *fs = s->s_type;
if (atomic_dec_and_lock(&s->s_active, &sb_lock)) {
s->s_count -= S_BIAS-1;
spin_unlock(&sb_lock);
vfs_dq_off(s, 0);
fs->kill_sb(s);
put_filesystem(fs);
put_super(s);
} else {
up_write(&s->s_umount);
}
}
EXPORT_SYMBOL(deactivate_locked_super);
/** /**
* grab_super - acquire an active reference * grab_super - acquire an active reference
* @s: reference we are trying to make active * @s: reference we are trying to make active
...@@ -797,8 +825,7 @@ int get_sb_ns(struct file_system_type *fs_type, int flags, void *data, ...@@ -797,8 +825,7 @@ int get_sb_ns(struct file_system_type *fs_type, int flags, void *data,
sb->s_flags = flags; sb->s_flags = flags;
err = fill_super(sb, data, flags & MS_SILENT ? 1 : 0); err = fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
if (err) { if (err) {
up_write(&sb->s_umount); deactivate_locked_super(sb);
deactivate_super(sb);
return err; return err;
} }
...@@ -854,8 +881,7 @@ int get_sb_bdev(struct file_system_type *fs_type, ...@@ -854,8 +881,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
if (s->s_root) { if (s->s_root) {
if ((flags ^ s->s_flags) & MS_RDONLY) { if ((flags ^ s->s_flags) & MS_RDONLY) {
up_write(&s->s_umount); deactivate_locked_super(s);
deactivate_super(s);
error = -EBUSY; error = -EBUSY;
goto error_bdev; goto error_bdev;
} }
...@@ -870,8 +896,7 @@ int get_sb_bdev(struct file_system_type *fs_type, ...@@ -870,8 +896,7 @@ int get_sb_bdev(struct file_system_type *fs_type,
sb_set_blocksize(s, block_size(bdev)); sb_set_blocksize(s, block_size(bdev));
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) { if (error) {
up_write(&s->s_umount); deactivate_locked_super(s);
deactivate_super(s);
goto error; goto error;
} }
...@@ -921,8 +946,7 @@ int get_sb_nodev(struct file_system_type *fs_type, ...@@ -921,8 +946,7 @@ int get_sb_nodev(struct file_system_type *fs_type,
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) { if (error) {
up_write(&s->s_umount); deactivate_locked_super(s);
deactivate_super(s);
return error; return error;
} }
s->s_flags |= MS_ACTIVE; s->s_flags |= MS_ACTIVE;
...@@ -952,8 +976,7 @@ int get_sb_single(struct file_system_type *fs_type, ...@@ -952,8 +976,7 @@ int get_sb_single(struct file_system_type *fs_type,
s->s_flags = flags; s->s_flags = flags;
error = fill_super(s, data, flags & MS_SILENT ? 1 : 0); error = fill_super(s, data, flags & MS_SILENT ? 1 : 0);
if (error) { if (error) {
up_write(&s->s_umount); deactivate_locked_super(s);
deactivate_super(s);
return error; return error;
} }
s->s_flags |= MS_ACTIVE; s->s_flags |= MS_ACTIVE;
...@@ -1006,8 +1029,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void ...@@ -1006,8 +1029,7 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
return mnt; return mnt;
out_sb: out_sb:
dput(mnt->mnt_root); dput(mnt->mnt_root);
up_write(&mnt->mnt_sb->s_umount); deactivate_locked_super(mnt->mnt_sb);
deactivate_super(mnt->mnt_sb);
out_free_secdata: out_free_secdata:
free_secdata(secdata); free_secdata(secdata);
out_mnt: out_mnt:
......
...@@ -1775,6 +1775,7 @@ void kill_block_super(struct super_block *sb); ...@@ -1775,6 +1775,7 @@ void kill_block_super(struct super_block *sb);
void kill_anon_super(struct super_block *sb); void kill_anon_super(struct super_block *sb);
void kill_litter_super(struct super_block *sb); void kill_litter_super(struct super_block *sb);
void deactivate_super(struct super_block *sb); void deactivate_super(struct super_block *sb);
void deactivate_locked_super(struct super_block *sb);
int set_anon_super(struct super_block *s, void *data); int set_anon_super(struct super_block *s, void *data);
struct super_block *sget(struct file_system_type *type, struct super_block *sget(struct file_system_type *type,
int (*test)(struct super_block *,void *), int (*test)(struct super_block *,void *),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment