diff options
author | Chunwei Chen <[email protected]> | 2016-07-19 12:02:33 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2016-07-20 09:05:43 -0700 |
commit | be88e733a634ad0d7f20350e1a17ede51922d3ff (patch) | |
tree | 22c2f148e55d9f9029720ca04a238de92dfa2b3e | |
parent | 26a08b5ca94ffc96add1d2d9dfe7dc78a0945ef2 (diff) |
Fix NULL pointer in zfs_preumount from 1d9b3bd
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]>
Signed-off-by: Brian Behlendorf <[email protected]>
Closes #4867
Issue #4854
-rw-r--r-- | module/zfs/zfs_vfsops.c | 49 |
1 files changed, 29 insertions, 20 deletions
diff --git a/module/zfs/zfs_vfsops.c b/module/zfs/zfs_vfsops.c index 2f1f44207..848651091 100644 --- a/module/zfs/zfs_vfsops.c +++ b/module/zfs/zfs_vfsops.c @@ -1369,7 +1369,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. */ @@ -1395,6 +1396,11 @@ out: if (error) { dmu_objset_disown(zsb->z_os, zsb); zfs_sb_free(zsb); + /* + * make sure we don't have dangling sb->s_fs_info which + * zfs_preumount will use. + */ + sb->s_fs_info = NULL; } return (error); @@ -1413,26 +1419,29 @@ 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 when lazytime is on, or when zfs_purgedir calls - * zfs_zget, iput would bump i_count from 0 to 1. This would race - * with the i_count check in evict_inodes. This means it could - * destroy the inode while we are still using it. - * - * We wait for two passes. xattr directories in the first pass may - * add xattr entries in zfs_purgedir, so in the second pass we wait - * for them. We don't use taskq_wait here because it is a pool wide - * taskq. Other mounted filesystems can constantly do iput_async - * and there's no guarantee when taskq will be empty. - */ - 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 when lazytime is on, or when zfs_purgedir + * calls zfs_zget, iput would bump i_count from 0 to 1. This + * would race with the i_count check in evict_inodes. This means + * it could destroy the inode while we are still using it. + * + * We wait for two passes. xattr directories in the first pass + * may add xattr entries in zfs_purgedir, so in the second pass + * we wait for them. We don't use taskq_wait here because it is + * a pool wide taskq. Other mounted filesystems can constantly + * do iput_async and there's no guarantee when taskq will be + * empty. + */ + 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); |