diff --git a/include/os/linux/zfs/sys/zfs_vfsops_os.h b/include/os/linux/zfs/sys/zfs_vfsops_os.h index 90fd0fb3c..d045fbf04 100644 --- a/include/os/linux/zfs/sys/zfs_vfsops_os.h +++ b/include/os/linux/zfs/sys/zfs_vfsops_os.h @@ -76,7 +76,7 @@ typedef struct vfs { typedef struct zfs_mnt { const char *mnt_osname; /* Objset name */ - char *mnt_data; /* Raw mount options */ + vfs_t *mnt_opts; /* Parsed options */ } zfs_mnt_t; struct zfsvfs { diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c index 196c2c25e..a5563eee1 100644 --- a/module/os/linux/zfs/zfs_vfsops.c +++ b/module/os/linux/zfs/zfs_vfsops.c @@ -1323,7 +1323,7 @@ zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent) uint64_t recordsize; int error = 0; zfsvfs_t *zfsvfs = NULL; - vfs_t *vfs = NULL; + vfs_t *vfs = zm->mnt_opts; int canwrite; int dataset_visible_zone; @@ -1341,9 +1341,6 @@ zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent) return (SET_ERROR(EPERM)); } - /* XXX temp keep things working during params upgrade */ - vfs = zfsvfs_vfs_alloc(); - /* * If a non-writable filesystem is being mounted without the * read-only flag, pretend it was set, as done for snapshots. @@ -1352,16 +1349,12 @@ zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent) vfs->vfs_readonly = B_TRUE; error = zfsvfs_create(osname, vfs->vfs_readonly, &zfsvfs); - if (error) { - zfsvfs_vfs_free(vfs); + if (error) goto out; - } if ((error = dsl_prop_get_integer(osname, "recordsize", - &recordsize, NULL))) { - zfsvfs_vfs_free(vfs); + &recordsize, NULL))) goto out; - } vfs->vfs_data = zfsvfs; zfsvfs->z_vfs = vfs; @@ -1443,6 +1436,13 @@ zfs_domount(struct super_block *sb, zfs_mnt_t *zm, int silent) out: if (error) { if (zfsvfs != NULL) { + /* + * We're returning error, so the caller still owns + * the mount options vfs_t. Remove them from zfsvfs + * so we don't try to free them. + */ + zfsvfs->z_vfs = NULL; + dmu_objset_disown(zfsvfs->z_os, B_TRUE, zfsvfs); zfsvfs_free(zfsvfs); } @@ -1536,7 +1536,7 @@ int zfs_remount(struct super_block *sb, int *flags, zfs_mnt_t *zm) { zfsvfs_t *zfsvfs = sb->s_fs_info; - vfs_t *vfsp; + vfs_t *vfsp = zm->mnt_opts; boolean_t issnap = dmu_objset_is_snapshot(zfsvfs->z_os); if ((issnap || !spa_writeable(dmu_objset_spa(zfsvfs->z_os))) && @@ -1545,9 +1545,6 @@ zfs_remount(struct super_block *sb, int *flags, zfs_mnt_t *zm) return (EROFS); } - /* XXX temp keep things working during params upgrade */ - vfsp = zfsvfs_vfs_alloc(); - if (!zfs_is_readonly(zfsvfs) && (*flags & SB_RDONLY)) txg_wait_synced(dmu_objset_pool(zfsvfs->z_os), 0); diff --git a/module/os/linux/zfs/zpl_super.c b/module/os/linux/zfs/zpl_super.c index 75f4ad390..8adf18343 100644 --- a/module/os/linux/zfs/zpl_super.c +++ b/module/os/linux/zfs/zpl_super.c @@ -448,7 +448,7 @@ zpl_get_tree(struct fs_context *fc) if (sb->s_root == NULL) { zfs_mnt_t zm = { .mnt_osname = fc->source, - .mnt_data = NULL, + .mnt_opts = fc->fs_private, }; fstrans_cookie_t cookie = spl_fstrans_mark(); @@ -460,6 +460,12 @@ zpl_get_tree(struct fs_context *fc) return (-err); } + /* + * zfsvfs has taken ownership of the mount options, so we + * need to ensure we don't free them. + */ + fc->fs_private = NULL; + sb->s_flags |= SB_ACTIVE; } else if (!issnap && ((fc->sb_flags ^ sb->s_flags) & SB_RDONLY)) { /* @@ -481,7 +487,7 @@ zpl_get_tree(struct fs_context *fc) static int zpl_reconfigure(struct fs_context *fc) { - zfs_mnt_t zm = { .mnt_osname = NULL, .mnt_data = NULL }; + zfs_mnt_t zm = { .mnt_osname = NULL, .mnt_opts = fc->fs_private }; fstrans_cookie_t cookie; int error; @@ -490,18 +496,80 @@ zpl_reconfigure(struct fs_context *fc) spl_fstrans_unmark(cookie); ASSERT3S(error, <=, 0); + if (error == 0) { + /* + * zfsvfs has taken ownership of the mount options, so we + * need to ensure we don't free them. + */ + fc->fs_private = NULL; + } + return (error); } +static int +zpl_dup_fc(struct fs_context *fc, struct fs_context *src_fc) +{ + vfs_t *src_vfs = src_fc->fs_private; + if (src_vfs == NULL) + return (0); + + vfs_t *vfs = zfsvfs_vfs_alloc(); + if (vfs == NULL) + return (-SET_ERROR(ENOMEM)); + + /* + * This is annoying, but a straight memcpy() would require us to + * reinitialise the lock. + */ + vfs->vfs_xattr = src_vfs->vfs_xattr; + vfs->vfs_readonly = src_vfs->vfs_readonly; + vfs->vfs_do_readonly = src_vfs->vfs_do_readonly; + vfs->vfs_setuid = src_vfs->vfs_setuid; + vfs->vfs_do_setuid = src_vfs->vfs_do_setuid; + vfs->vfs_exec = src_vfs->vfs_exec; + vfs->vfs_do_exec = src_vfs->vfs_do_exec; + vfs->vfs_devices = src_vfs->vfs_devices; + vfs->vfs_do_devices = src_vfs->vfs_do_devices; + vfs->vfs_do_xattr = src_vfs->vfs_do_xattr; + vfs->vfs_atime = src_vfs->vfs_atime; + vfs->vfs_do_atime = src_vfs->vfs_do_atime; + vfs->vfs_relatime = src_vfs->vfs_relatime; + vfs->vfs_do_relatime = src_vfs->vfs_do_relatime; + vfs->vfs_nbmand = src_vfs->vfs_nbmand; + vfs->vfs_do_nbmand = src_vfs->vfs_do_nbmand; + + mutex_enter(&src_vfs->vfs_mntpt_lock); + if (src_vfs->vfs_mntpoint != NULL) + vfs->vfs_mntpoint = kmem_strdup(src_vfs->vfs_mntpoint); + mutex_exit(&src_vfs->vfs_mntpt_lock); + + fc->fs_private = vfs; + return (0); +} + +static void +zpl_free_fc(struct fs_context *fc) +{ + zfsvfs_vfs_free(fc->fs_private); +} + const struct fs_context_operations zpl_fs_context_operations = { .get_tree = zpl_get_tree, .reconfigure = zpl_reconfigure, + .dup = zpl_dup_fc, + .free = zpl_free_fc, }; static int zpl_init_fs_context(struct fs_context *fc) { + fc->fs_private = zfsvfs_vfs_alloc(); + if (fc->fs_private == NULL) + return (-SET_ERROR(ENOMEM)); + fc->ops = &zpl_fs_context_operations; + return (0); }