Skip to content

Commit

Permalink
Fix NULL pointer in zfs_preumount from 1d9b3bd
Browse files Browse the repository at this point in the history
When zfs_domount fails zsb will be freed, and its caller
mount_nodev/get_sb_nodev will do deactivate_locked_super and calls into
zfs_preumount.

In order to make sure we don't touch any nonexistent stuff, we must make sure
s_fs_info is NULL in the fail path so zfs_preumount can easily check that.

Signed-off-by: Chunwei Chen <[email protected]>
  • Loading branch information
Chunwei Chen committed Jul 19, 2016
1 parent 6fb105e commit 107236d
Showing 1 changed file with 28 additions and 21 deletions.
49 changes: 28 additions & 21 deletions module/zfs/zfs_vfsops.c
Original file line number Diff line number Diff line change
Expand Up @@ -1324,7 +1324,6 @@ zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
goto out;

zsb->z_sb = sb;
sb->s_fs_info = zsb;
sb->s_magic = ZFS_SUPER_MAGIC;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_time_gran = 1;
Expand Down Expand Up @@ -1369,7 +1368,8 @@ zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
dmu_objset_set_user(zsb->z_os, zsb);
mutex_exit(&zsb->z_os->os_user_ptr_lock);
} else {
error = zfs_sb_setup(zsb, B_TRUE);
if ((error = zfs_sb_setup(zsb, B_TRUE)))
goto out;
}

/* Allocate a root inode for the filesystem. */
Expand All @@ -1390,6 +1390,11 @@ zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
if (!zsb->z_issnap)
zfsctl_create(zsb);

/*
* make sure we succeed before assign zsb, so we don't have dangling
* sb->s_fs_info which zfs_preumount will use.
*/
sb->s_fs_info = zsb;
zsb->z_arc_prune = arc_add_prune_callback(zpl_prune_sb, sb);
out:
if (error) {
Expand All @@ -1413,26 +1418,28 @@ zfs_preumount(struct super_block *sb)
{
zfs_sb_t *zsb = sb->s_fs_info;

if (zsb)
/* zsb is NULL when zfs_domount fails during mount */
if (zsb) {
zfsctl_destroy(sb->s_fs_info);
/*
* wait for iput_async before entering evict_inodes in
* generic_shutdown_super. The reason we must finish before
* evict_inodes is iput when lazytime is on or when zfs_purgedir calls
* zfs_zget would bump i_count from 0 to 1. This would race with the
* i_count check in evict_inodes, so it could destroy the inode while
* we are still using it.
*
* We wait for two pass, xattr dirs in the first pass may add xattr
* entries in zfs_purgedir, so we wait for second pass for them. Also,
* we don't use taskq_wait here is because it's a pool wise taskq, so
* other live filesystem can constantly do iput_async, there's no
* guarantee it will finish wait.
*/
taskq_wait_outstanding(dsl_pool_iput_taskq(
dmu_objset_pool(zsb->z_os)), 0);
taskq_wait_outstanding(dsl_pool_iput_taskq(
dmu_objset_pool(zsb->z_os)), 0);
/*
* wait for iput_async before entering evict_inodes in
* generic_shutdown_super. The reason we must finish before
* evict_inodes is iput when lazytime is on or when zfs_purgedir
* calls zfs_zget would bump i_count from 0 to 1. This would
* race with the i_count check in evict_inodes, so it could
* destroy the inode while we are still using it.
*
* We wait for two pass, xattr dirs in the first pass may add
* xattr entries in zfs_purgedir, so we wait for second pass
* for them. Also, we don't use taskq_wait here is because it's
* a pool wise taskq, so other live filesystem can constantly do
* iput_async, there's no guarantee it will finish wait.
*/
taskq_wait_outstanding(dsl_pool_iput_taskq(
dmu_objset_pool(zsb->z_os)), 0);
taskq_wait_outstanding(dsl_pool_iput_taskq(
dmu_objset_pool(zsb->z_os)), 0);
}
}
EXPORT_SYMBOL(zfs_preumount);

Expand Down

0 comments on commit 107236d

Please sign in to comment.