]> granicus.if.org Git - zfs/commitdiff
Add temporary mount options
authorBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 31 Aug 2015 23:46:01 +0000 (16:46 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 3 Sep 2015 21:14:55 +0000 (14:14 -0700)
Add the required kernel side infrastructure to parse arbitrary
mount options.  This enables us to support temporary mount
options in largely the same way it is handled on other platforms.

See the 'Temporary Mount Point Properties' section of zfs(8)
for complete details.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #985
Closes #3351

13 files changed:
cmd/mount_zfs/mount_zfs.c
include/sys/Makefile.am
include/sys/mntent.h [moved from lib/libspl/include/sys/mntent.h with 98% similarity]
include/sys/zfs_vfsops.h
include/sys/zpl.h
lib/libspl/include/sys/Makefile.am
lib/libspl/include/sys/mnttab.h
lib/libspl/include/sys/mount.h
lib/libzfs/libzfs_dataset.c
man/man8/zfs.8
module/zfs/zfs_ioctl.c
module/zfs/zfs_vfsops.c
module/zfs/zpl_super.c

index 0f4995943cc4836625b70cfe68dd92ee43b19dd4..e3e8cfc2201557bca7135675cc2ec3d4bda8457a 100644 (file)
 #include <unistd.h>
 #include <sys/file.h>
 #include <sys/mount.h>
+#include <sys/mntent.h>
 #include <sys/stat.h>
 #include <libzfs.h>
 #include <locale.h>
 
+#define        ZS_COMMENT      0x00000000      /* comment */
+#define        ZS_ZFSUTIL      0x00000001      /* caller is zfs(8) */
+
 libzfs_handle_t *g_zfs;
 
 typedef struct option_map {
@@ -335,14 +339,18 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
 }
 
 static void
-__zfs_selinux_setcontext(const char *name, const char *context, char *mntopts,
-    char *mtabopt)
+append_mntopt(const char *name, const char *val, char *mntopts,
+    char *mtabopt, boolean_t quote)
 {
        char tmp[MNT_LINE_MAX];
 
-       snprintf(tmp, MNT_LINE_MAX, ",%s=\"%s\"", name, context);
-       strlcat(mntopts, tmp, MNT_LINE_MAX);
-       strlcat(mtabopt, tmp, MNT_LINE_MAX);
+       snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val);
+
+       if (mntopts)
+               strlcat(mntopts, tmp, MNT_LINE_MAX);
+
+       if (mtabopt)
+               strlcat(mtabopt, tmp, MNT_LINE_MAX);
 }
 
 static void
@@ -354,7 +362,7 @@ zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name,
        if (zfs_prop_get(zhp, zpt, context, sizeof (context),
            NULL, NULL, 0, B_FALSE) == 0) {
                if (strcmp(context, "none") != 0)
-                   __zfs_selinux_setcontext(name, context, mntopts, mtabopt);
+                   append_mntopt(name, context, mntopts, mtabopt, B_TRUE);
        }
 }
 
@@ -506,11 +514,14 @@ main(int argc, char **argv)
                            ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT,
                            mntopts, mtabopt);
                } else {
-                       __zfs_selinux_setcontext(MNTOPT_CONTEXT,
-                           prop, mntopts, mtabopt);
+                       append_mntopt(MNTOPT_CONTEXT, prop,
+                           mntopts, mtabopt, B_TRUE);
                }
        }
 
+       /* A hint used to determine an auto-mounted snapshot mount point */
+       append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE);
+
        /* treat all snapshots as legacy mount points */
        if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
                (void) strlcpy(prop, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN);
index fd80b34ee44d57fb28394b77a08b0627c43edcf7..77ecfb2dcf8e39eb4aafa0d38bf4cc9d9a121a7a 100644 (file)
@@ -33,6 +33,7 @@ COMMON_H = \
        $(top_srcdir)/include/sys/efi_partition.h \
        $(top_srcdir)/include/sys/metaslab.h \
        $(top_srcdir)/include/sys/metaslab_impl.h \
+       $(top_srcdir)/include/sys/mntent.h \
        $(top_srcdir)/include/sys/multilist.h \
        $(top_srcdir)/include/sys/nvpair.h \
        $(top_srcdir)/include/sys/nvpair_impl.h \
similarity index 98%
rename from lib/libspl/include/sys/mntent.h
rename to include/sys/mntent.h
index b57ffee83d4f2ca2596320ee0bf4a0ca8a8c058b..e6ce117769a406294597d1de915fe2f092c94327 100644 (file)
@@ -95,8 +95,6 @@
 #define        MNTOPT_ACL      "acl"           /* passed by util-linux-2.24 mount */
 #define        MNTOPT_NOACL    "noacl"         /* likewise */
 #define        MNTOPT_POSIXACL "posixacl"      /* likewise */
-
-#define        ZS_COMMENT      0x00000000      /* comment */
-#define        ZS_ZFSUTIL      0x00000001      /* caller is zfs(8) */
+#define        MNTOPT_MNTPOINT "mntpoint"      /* mount point hint */
 
 #endif /* _SYS_MNTENT_H */
index af99686a61181752b37f385858bb12412f37130e..3d2b249c9f3dbefa6f985fe1482ccc74e9357703 100644 (file)
@@ -41,11 +41,33 @@ extern "C" {
 struct zfs_sb;
 struct znode;
 
+typedef struct zfs_mntopts {
+       char            *z_osname;      /* Objset name */
+       char            *z_mntpoint;    /* Primary mount point */
+       boolean_t       z_readonly;
+       boolean_t       z_do_readonly;
+       boolean_t       z_setuid;
+       boolean_t       z_do_setuid;
+       boolean_t       z_exec;
+       boolean_t       z_do_exec;
+       boolean_t       z_devices;
+       boolean_t       z_do_devices;
+       boolean_t       z_xattr;
+       boolean_t       z_do_xattr;
+       boolean_t       z_atime;
+       boolean_t       z_do_atime;
+       boolean_t       z_relatime;
+       boolean_t       z_do_relatime;
+       boolean_t       z_nbmand;
+       boolean_t       z_do_nbmand;
+} zfs_mntopts_t;
+
 typedef struct zfs_sb {
        struct super_block *z_sb;       /* generic super_block */
        struct backing_dev_info z_bdi;  /* generic backing dev info */
        struct zfs_sb   *z_parent;      /* parent fs */
        objset_t        *z_os;          /* objset reference */
+       zfs_mntopts_t   *z_mntopts;     /* passed mount options */
        uint64_t        z_flags;        /* super_block flags */
        uint64_t        z_root;         /* id of root znode */
        uint64_t        z_unlinkedobj;  /* id of unlinked zapobj */
@@ -170,7 +192,10 @@ extern boolean_t zfs_fuid_overquota(zfs_sb_t *zsb, boolean_t isgroup,
 extern int zfs_set_version(zfs_sb_t *zsb, uint64_t newvers);
 extern int zfs_get_zplprop(objset_t *os, zfs_prop_t prop,
     uint64_t *value);
-extern int zfs_sb_create(const char *name, zfs_sb_t **zsbp);
+extern zfs_mntopts_t *zfs_mntopts_alloc(void);
+extern void zfs_mntopts_free(zfs_mntopts_t *zmo);
+extern int zfs_sb_create(const char *name, zfs_mntopts_t *zmo,
+    zfs_sb_t **zsbp);
 extern int zfs_sb_setup(zfs_sb_t *zsb, boolean_t mounting);
 extern void zfs_sb_free(zfs_sb_t *zsb);
 extern int zfs_sb_prune(struct super_block *sb, unsigned long nr_to_scan,
@@ -181,10 +206,10 @@ extern boolean_t zfs_is_readonly(zfs_sb_t *zsb);
 
 extern int zfs_register_callbacks(zfs_sb_t *zsb);
 extern void zfs_unregister_callbacks(zfs_sb_t *zsb);
-extern int zfs_domount(struct super_block *sb, void *data, int silent);
+extern int zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent);
 extern void zfs_preumount(struct super_block *sb);
 extern int zfs_umount(struct super_block *sb);
-extern int zfs_remount(struct super_block *sb, int *flags, char *data);
+extern int zfs_remount(struct super_block *sb, int *flags, zfs_mntopts_t *zmo);
 extern int zfs_root(zfs_sb_t *zsb, struct inode **ipp);
 extern int zfs_statvfs(struct dentry *dentry, struct kstatfs *statp);
 extern int zfs_vget(struct super_block *sb, struct inode **ipp, fid_t *fidp);
index c7701aae57d09f0516ea88c8bea5c8ef7864d32e..54b35e02de74f194e50aabbc0f0e874b9158e71d 100644 (file)
 #ifndef        _SYS_ZPL_H
 #define        _SYS_ZPL_H
 
+#include <sys/mntent.h>
 #include <sys/vfs.h>
 #include <linux/aio.h>
 #include <linux/dcache_compat.h>
 #include <linux/exportfs.h>
 #include <linux/falloc.h>
 #include <linux/file_compat.h>
+#include <linux/parser.h>
 #include <linux/task_io_accounting_ops.h>
 #include <linux/vfs_compat.h>
 #include <linux/writeback.h>
@@ -65,11 +67,6 @@ extern const struct file_operations zpl_dir_file_operations;
 /* zpl_super.c */
 extern void zpl_prune_sb(int64_t nr_to_scan, void *arg);
 
-typedef struct zpl_mount_data {
-       const char *z_osname;   /* Dataset name */
-       void *z_data;           /* Mount options string */
-} zpl_mount_data_t;
-
 extern const struct super_operations zpl_super_operations;
 extern const struct export_operations zpl_export_operations;
 extern struct file_system_type zpl_fs_type;
index cd8a29fb5cb656118721f7def8a951c25941c038..6aad8450ffdcba5a73de08ba220df7dbe9fdfb4d 100644 (file)
@@ -26,7 +26,6 @@ libspl_HEADERS = \
        $(top_srcdir)/lib/libspl/include/sys/list_impl.h \
        $(top_srcdir)/lib/libspl/include/sys/mhd.h \
        $(top_srcdir)/lib/libspl/include/sys/mkdev.h \
-       $(top_srcdir)/lib/libspl/include/sys/mntent.h \
        $(top_srcdir)/lib/libspl/include/sys/mnttab.h \
        $(top_srcdir)/lib/libspl/include/sys/mount.h \
        $(top_srcdir)/lib/libspl/include/sys/note.h \
index 6bfbdd69c4a2ba65485a265e9abd603985e1d7a3..21d89658c6bf2db24c3aca7ed1b0eb8c291aa06c 100644 (file)
@@ -39,7 +39,7 @@
 #endif /* MNTTAB */
 
 #define        MNTTAB          "/etc/mtab"
-#define        MNT_LINE_MAX    1024
+#define        MNT_LINE_MAX    4096
 
 #define        MNT_TOOLONG     1       /* entry exceeds MNT_LINE_MAX */
 #define        MNT_TOOMANY     2       /* too many fields in line */
index 41cd839b7f1c8fab8778225337113086f21da43f..ad1fa383e63c69140fd4dad6728c5aa6fe8dcc8c 100644 (file)
@@ -29,7 +29,6 @@
 #ifndef _LIBSPL_SYS_MOUNT_H
 #define        _LIBSPL_SYS_MOUNT_H
 
-#include <sys/mntent.h>
 #include <assert.h>
 #include <string.h>
 #include <stdlib.h>
index 6b67a73efd8b51aee3eab9f0c37c47930336eb74..088ee35c4f6b2a14ce7fd83e7313a3c85d994444 100644 (file)
@@ -1922,6 +1922,8 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                mnt.mnt_mntopts = zhp->zfs_mntopts;
 
        switch (prop) {
+       case ZFS_PROP_ATIME:
+       case ZFS_PROP_RELATIME:
        case ZFS_PROP_DEVICES:
        case ZFS_PROP_EXEC:
        case ZFS_PROP_READONLY:
@@ -1944,8 +1946,6 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                }
                break;
 
-       case ZFS_PROP_ATIME:
-       case ZFS_PROP_RELATIME:
        case ZFS_PROP_CANMOUNT:
        case ZFS_PROP_VOLSIZE:
        case ZFS_PROP_QUOTA:
index 9f42da38b452ceb29af574f8d5d26d8d110d0f71..208abd7167089810b6fd7bde5ee0854bdfe04f18 100644 (file)
@@ -1535,6 +1535,9 @@ When a file system is mounted, either through \fBmount\fR(8) for legacy mounts o
      readonly                ro/rw
      setuid                  setuid/nosetuid
      xattr                   xattr/noxattr
+     atime                   atime/noatime
+     relatime                relatime/norelatime
+     nbmand                  nbmand/nonbmand
 .fi
 .in -2
 .sp
index 61bc4b82b165b36cb681457e18d41978a5d8463e..06aae82a10e38dfe5c975f9eba576735115eb66a 100644 (file)
@@ -1449,7 +1449,7 @@ zfs_sb_hold(const char *name, void *tag, zfs_sb_t **zsbp, boolean_t writer)
        int error = 0;
 
        if (get_zfs_sb(name, zsbp) != 0)
-               error = zfs_sb_create(name, zsbp);
+               error = zfs_sb_create(name, NULL, zsbp);
        if (error == 0) {
                rrm_enter(&(*zsbp)->z_teardown_lock, (writer) ? RW_WRITER :
                    RW_READER, tag);
index f94073cbbc57fefa06c0cb7d60851cc052fd635c..9ee7b2e8ad9beebd991cde679a9547051598345e 100644 (file)
@@ -262,11 +262,22 @@ zfs_register_callbacks(zfs_sb_t *zsb)
 {
        struct dsl_dataset *ds = NULL;
        objset_t *os = zsb->z_os;
-       boolean_t do_readonly = B_FALSE;
+       zfs_mntopts_t *zmo = zsb->z_mntopts;
        int error = 0;
 
-       if (zfs_is_readonly(zsb) || !spa_writeable(dmu_objset_spa(os)))
-               do_readonly = B_TRUE;
+       ASSERT(zsb);
+       ASSERT(zmo);
+
+       /*
+        * The act of registering our callbacks will destroy any mount
+        * options we may have.  In order to enable temporary overrides
+        * of mount options, we stash away the current values and
+        * restore them after we register the callbacks.
+        */
+       if (zfs_is_readonly(zsb) || !spa_writeable(dmu_objset_spa(os))) {
+               zmo->z_do_readonly = B_TRUE;
+               zmo->z_readonly = B_TRUE;
+       }
 
        /*
         * Register property callbacks.
@@ -307,8 +318,25 @@ zfs_register_callbacks(zfs_sb_t *zsb)
        if (error)
                goto unregister;
 
-       if (do_readonly)
-               readonly_changed_cb(zsb, B_TRUE);
+       /*
+        * Invoke our callbacks to restore temporary mount options.
+        */
+       if (zmo->z_do_readonly)
+               readonly_changed_cb(zsb, zmo->z_readonly);
+       if (zmo->z_do_setuid)
+               setuid_changed_cb(zsb, zmo->z_setuid);
+       if (zmo->z_do_exec)
+               exec_changed_cb(zsb, zmo->z_exec);
+       if (zmo->z_do_devices)
+               devices_changed_cb(zsb, zmo->z_devices);
+       if (zmo->z_do_xattr)
+               xattr_changed_cb(zsb, zmo->z_xattr);
+       if (zmo->z_do_atime)
+               atime_changed_cb(zsb, zmo->z_atime);
+       if (zmo->z_do_relatime)
+               relatime_changed_cb(zsb, zmo->z_relatime);
+       if (zmo->z_do_nbmand)
+               nbmand_changed_cb(zsb, zmo->z_nbmand);
 
        return (0);
 
@@ -642,8 +670,26 @@ zfs_owner_overquota(zfs_sb_t *zsb, znode_t *zp, boolean_t isgroup)
 }
 EXPORT_SYMBOL(zfs_owner_overquota);
 
+zfs_mntopts_t *
+zfs_mntopts_alloc(void)
+{
+       return (kmem_zalloc(sizeof (zfs_mntopts_t), KM_SLEEP));
+}
+
+void
+zfs_mntopts_free(zfs_mntopts_t *zmo)
+{
+       if (zmo->z_osname)
+               strfree(zmo->z_osname);
+
+       if (zmo->z_mntpoint)
+               strfree(zmo->z_mntpoint);
+
+       kmem_free(zmo, sizeof (zfs_mntopts_t));
+}
+
 int
-zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
+zfs_sb_create(const char *osname, zfs_mntopts_t *zmo, zfs_sb_t **zsbp)
 {
        objset_t *os;
        zfs_sb_t *zsb;
@@ -663,6 +709,11 @@ zfs_sb_create(const char *osname, zfs_sb_t **zsbp)
                return (error);
        }
 
+       /*
+        * Optional temporary mount options, free'd in zfs_sb_free().
+        */
+       zsb->z_mntopts = (zmo ? zmo : zfs_mntopts_alloc());
+
        /*
         * Initialize the zfs-specific filesystem structure.
         * Should probably make this a kmem cache, shuffle fields,
@@ -892,6 +943,7 @@ zfs_sb_free(zfs_sb_t *zsb)
        for (i = 0; i != ZFS_OBJ_MTX_SZ; i++)
                mutex_destroy(&zsb->z_hold_mtx[i]);
        vmem_free(zsb->z_hold_mtx, sizeof (kmutex_t) * ZFS_OBJ_MTX_SZ);
+       zfs_mntopts_free(zsb->z_mntopts);
        kmem_free(zsb, sizeof (zfs_sb_t));
 }
 EXPORT_SYMBOL(zfs_sb_free);
@@ -1310,16 +1362,15 @@ atomic_long_t zfs_bdi_seq = ATOMIC_LONG_INIT(0);
 #endif
 
 int
-zfs_domount(struct super_block *sb, void *data, int silent)
+zfs_domount(struct super_block *sb, zfs_mntopts_t *zmo, int silent)
 {
-       zpl_mount_data_t *zmd = data;
-       const char *osname = zmd->z_osname;
+       const char *osname = zmo->z_osname;
        zfs_sb_t *zsb;
        struct inode *root_inode;
        uint64_t recordsize;
        int error;
 
-       error = zfs_sb_create(osname, &zsb);
+       error = zfs_sb_create(osname, zmo, &zsb);
        if (error)
                return (error);
 
@@ -1462,13 +1513,15 @@ zfs_umount(struct super_block *sb)
 EXPORT_SYMBOL(zfs_umount);
 
 int
-zfs_remount(struct super_block *sb, int *flags, char *data)
+zfs_remount(struct super_block *sb, int *flags, zfs_mntopts_t *zmo)
 {
-       /*
-        * All namespace flags (MNT_*) and super block flags (MS_*) will
-        * be handled by the Linux VFS.  Only handle custom options here.
-        */
-       return (0);
+       zfs_sb_t *zsb = sb->s_fs_info;
+       int error;
+
+       zfs_unregister_callbacks(zsb);
+       error = zfs_register_callbacks(zsb);
+
+       return (error);
 }
 EXPORT_SYMBOL(zfs_remount);
 
index ecdbc36d81f833cf52ad779a3a91161e00f3ccac..3edacba462eeb88d687e893c85abca8308e1b619 100644 (file)
@@ -184,35 +184,204 @@ zpl_statfs(struct dentry *dentry, struct kstatfs *statp)
        return (error);
 }
 
+enum {
+       TOKEN_RO,
+       TOKEN_RW,
+       TOKEN_SETUID,
+       TOKEN_NOSETUID,
+       TOKEN_EXEC,
+       TOKEN_NOEXEC,
+       TOKEN_DEVICES,
+       TOKEN_NODEVICES,
+       TOKEN_XATTR,
+       TOKEN_NOXATTR,
+       TOKEN_ATIME,
+       TOKEN_NOATIME,
+       TOKEN_RELATIME,
+       TOKEN_NORELATIME,
+       TOKEN_NBMAND,
+       TOKEN_NONBMAND,
+       TOKEN_MNTPOINT,
+       TOKEN_LAST,
+};
+
+static const match_table_t zpl_tokens = {
+       { TOKEN_RO,             MNTOPT_RO },
+       { TOKEN_RW,             MNTOPT_RW },
+       { TOKEN_SETUID,         MNTOPT_SETUID },
+       { TOKEN_NOSETUID,       MNTOPT_NOSETUID },
+       { TOKEN_EXEC,           MNTOPT_EXEC },
+       { TOKEN_NOEXEC,         MNTOPT_NOEXEC },
+       { TOKEN_DEVICES,        MNTOPT_DEVICES },
+       { TOKEN_NODEVICES,      MNTOPT_NODEVICES },
+       { TOKEN_XATTR,          MNTOPT_XATTR },
+       { TOKEN_NOXATTR,        MNTOPT_NOXATTR },
+       { TOKEN_ATIME,          MNTOPT_ATIME },
+       { TOKEN_NOATIME,        MNTOPT_NOATIME },
+       { TOKEN_RELATIME,       MNTOPT_RELATIME },
+       { TOKEN_NORELATIME,     MNTOPT_NORELATIME },
+       { TOKEN_NBMAND,         MNTOPT_NBMAND },
+       { TOKEN_NONBMAND,       MNTOPT_NONBMAND },
+       { TOKEN_MNTPOINT,       MNTOPT_MNTPOINT "=%s" },
+       { TOKEN_LAST,           NULL },
+};
+
+static int
+zpl_parse_option(char *option, int token, substring_t *args,
+    zfs_mntopts_t *zmo, boolean_t isremount)
+{
+       switch (token) {
+       case TOKEN_RO:
+               zmo->z_readonly = B_TRUE;
+               zmo->z_do_readonly = B_TRUE;
+               break;
+       case TOKEN_RW:
+               zmo->z_readonly = B_FALSE;
+               zmo->z_do_readonly = B_TRUE;
+               break;
+       case TOKEN_SETUID:
+               zmo->z_setuid = B_TRUE;
+               zmo->z_do_setuid = B_TRUE;
+               break;
+       case TOKEN_NOSETUID:
+               zmo->z_setuid = B_FALSE;
+               zmo->z_do_setuid = B_TRUE;
+               break;
+       case TOKEN_EXEC:
+               zmo->z_exec = B_TRUE;
+               zmo->z_do_exec = B_TRUE;
+               break;
+       case TOKEN_NOEXEC:
+               zmo->z_exec = B_FALSE;
+               zmo->z_do_exec = B_TRUE;
+               break;
+       case TOKEN_DEVICES:
+               zmo->z_devices = B_TRUE;
+               zmo->z_do_devices = B_TRUE;
+               break;
+       case TOKEN_NODEVICES:
+               zmo->z_devices = B_FALSE;
+               zmo->z_do_devices = B_TRUE;
+               break;
+       case TOKEN_XATTR:
+               zmo->z_xattr = B_TRUE;
+               zmo->z_do_xattr = B_TRUE;
+               break;
+       case TOKEN_NOXATTR:
+               zmo->z_xattr = B_FALSE;
+               zmo->z_do_xattr = B_TRUE;
+               break;
+       case TOKEN_ATIME:
+               zmo->z_atime = B_TRUE;
+               zmo->z_do_atime = B_TRUE;
+               break;
+       case TOKEN_NOATIME:
+               zmo->z_atime = B_FALSE;
+               zmo->z_do_atime = B_TRUE;
+               break;
+       case TOKEN_RELATIME:
+               zmo->z_relatime = B_TRUE;
+               zmo->z_do_relatime = B_TRUE;
+               break;
+       case TOKEN_NORELATIME:
+               zmo->z_relatime = B_FALSE;
+               zmo->z_do_relatime = B_TRUE;
+               break;
+       case TOKEN_NBMAND:
+               zmo->z_nbmand = B_TRUE;
+               zmo->z_do_nbmand = B_TRUE;
+               break;
+       case TOKEN_NONBMAND:
+               zmo->z_nbmand = B_FALSE;
+               zmo->z_do_nbmand = B_TRUE;
+               break;
+       case TOKEN_MNTPOINT:
+               zmo->z_mntpoint = match_strdup(&args[0]);
+               if (zmo->z_mntpoint == NULL)
+                       return (-ENOMEM);
+
+               break;
+       default:
+               break;
+       }
+
+       return (0);
+}
+
+/*
+ * Parse the mntopts string storing the results in provided zmo argument.
+ * If an error occurs the zmo argument will not be modified.  The caller
+ * needs to set isremount when recycling an existing zfs_mntopts_t.
+ */
+static int
+zpl_parse_options(char *osname, char *mntopts, zfs_mntopts_t *zmo,
+    boolean_t isremount)
+{
+       zfs_mntopts_t *tmp_zmo;
+       substring_t args[MAX_OPT_ARGS];
+       char *tmp_mntopts, *p;
+       int error, token;
+
+       if (mntopts == NULL)
+               return (-EINVAL);
+
+       tmp_zmo = zfs_mntopts_alloc();
+       tmp_zmo->z_osname = strdup(osname);
+       tmp_mntopts = strdup(mntopts);
+
+       while ((p = strsep(&tmp_mntopts, ",")) != NULL) {
+               if (!*p)
+                       continue;
+
+               args[0].to = args[0].from = NULL;
+               token = match_token(p, zpl_tokens, args);
+               error = zpl_parse_option(p, token, args, tmp_zmo, isremount);
+               if (error) {
+                       zfs_mntopts_free(tmp_zmo);
+                       strfree(tmp_mntopts);
+                       return (error);
+               }
+       }
+
+       strfree(tmp_mntopts);
+
+       if (isremount == B_TRUE) {
+               if (zmo->z_osname)
+                       strfree(zmo->z_osname);
+
+               if (zmo->z_mntpoint)
+                       strfree(zmo->z_mntpoint);
+       } else {
+               ASSERT3P(zmo->z_osname, ==, NULL);
+               ASSERT3P(zmo->z_mntpoint, ==, NULL);
+       }
+
+       memcpy(zmo, tmp_zmo, sizeof (zfs_mntopts_t));
+       kmem_free(tmp_zmo, sizeof (zfs_mntopts_t));
+
+       return (0);
+}
+
 static int
 zpl_remount_fs(struct super_block *sb, int *flags, char *data)
 {
+       zfs_sb_t *zsb = sb->s_fs_info;
        fstrans_cookie_t cookie;
        int error;
 
+       error = zpl_parse_options(zsb->z_mntopts->z_osname, data,
+           zsb->z_mntopts, B_TRUE);
+       if (error)
+               return (error);
+
        cookie = spl_fstrans_mark();
-       error = -zfs_remount(sb, flags, data);
+       error = -zfs_remount(sb, flags, zsb->z_mntopts);
        spl_fstrans_unmark(cookie);
        ASSERT3S(error, <=, 0);
 
        return (error);
 }
 
-/*
- * ZFS specific features must be explicitly handled here, the VFS will
- * automatically handled the following generic functionality.
- *
- *   MNT_NOSUID,
- *   MNT_NODEV,
- *   MNT_NOEXEC,
- *   MNT_NOATIME,
- *   MNT_NODIRATIME,
- *   MNT_READONLY,
- *   MNT_STRICTATIME,
- *   MS_SYNCHRONOUS,
- *   MS_DIRSYNC,
- *   MS_MANDLOCK.
- */
 static int
 __zpl_show_options(struct seq_file *seq, zfs_sb_t *zsb)
 {
@@ -249,11 +418,12 @@ zpl_show_options(struct seq_file *seq, struct vfsmount *vfsp)
 static int
 zpl_fill_super(struct super_block *sb, void *data, int silent)
 {
+       zfs_mntopts_t *zmo = (zfs_mntopts_t *)data;
        fstrans_cookie_t cookie;
        int error;
 
        cookie = spl_fstrans_mark();
-       error = -zfs_domount(sb, data, silent);
+       error = -zfs_domount(sb, zmo, silent);
        spl_fstrans_unmark(cookie);
        ASSERT3S(error, <=, 0);
 
@@ -265,18 +435,32 @@ static struct dentry *
 zpl_mount(struct file_system_type *fs_type, int flags,
     const char *osname, void *data)
 {
-       zpl_mount_data_t zmd = { osname, data };
+       zfs_mntopts_t *zmo = zfs_mntopts_alloc();
+       int error;
+
+       error = zpl_parse_options((char *)osname, (char *)data, zmo, B_FALSE);
+       if (error) {
+               zfs_mntopts_free(zmo);
+               return (ERR_PTR(error));
+       }
 
-       return (mount_nodev(fs_type, flags, &zmd, zpl_fill_super));
+       return (mount_nodev(fs_type, flags, zmo, zpl_fill_super));
 }
 #else
 static int
 zpl_get_sb(struct file_system_type *fs_type, int flags,
     const char *osname, void *data, struct vfsmount *mnt)
 {
-       zpl_mount_data_t zmd = { osname, data };
+       zfs_mntopts_t *zmo = zfs_mntopts_alloc();
+       int error;
+
+       error = zpl_parse_options((char *)osname, (char *)data, zmo, B_FALSE);
+       if (error) {
+               zfs_mntopts_free(zmo);
+               return (error);
+       }
 
-       return (get_sb_nodev(fs_type, flags, &zmd, zpl_fill_super, mnt));
+       return (get_sb_nodev(fs_type, flags, zmo, zpl_fill_super, mnt));
 }
 #endif /* HAVE_MOUNT_NODEV */