Documentation updates
[zfs.git] / cmd / zfs / zfs_main.c
index 353fd4f..8d75d2a 100644 (file)
@@ -40,6 +40,7 @@
 #include <zone.h>
 #include <grp.h>
 #include <pwd.h>
+#include <signal.h>
 #include <sys/mkdev.h>
 #include <sys/mntent.h>
 #include <sys/mnttab.h>
@@ -84,6 +85,7 @@ static int zfs_do_userspace(int argc, char **argv);
 static int zfs_do_python(int argc, char **argv);
 static int zfs_do_hold(int argc, char **argv);
 static int zfs_do_release(int argc, char **argv);
+static int zfs_do_diff(int argc, char **argv);
 
 /*
  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
@@ -128,7 +130,8 @@ typedef enum {
        HELP_GROUPSPACE,
        HELP_HOLD,
        HELP_HOLDS,
-       HELP_RELEASE
+       HELP_RELEASE,
+       HELP_DIFF
 } zfs_help_t;
 
 typedef struct zfs_command {
@@ -180,6 +183,7 @@ static zfs_command_t command_table[] = {
        { "hold",       zfs_do_hold,            HELP_HOLD               },
        { "holds",      zfs_do_python,          HELP_HOLDS              },
        { "release",    zfs_do_release,         HELP_RELEASE            },
+       { "diff",       zfs_do_diff,            HELP_DIFF               },
 };
 
 #define        NCOMMAND        (sizeof (command_table) / sizeof (command_table[0]))
@@ -283,6 +287,9 @@ get_usage(zfs_help_t idx)
                return (gettext("\tholds [-r] <snapshot> ...\n"));
        case HELP_RELEASE:
                return (gettext("\trelease [-r] <tag> <snapshot> ...\n"));
+       case HELP_DIFF:
+               return (gettext("\tdiff [-FHt] <snapshot> "
+                   "[snapshot|filesystem]\n"));
        }
 
        abort();
@@ -311,6 +318,7 @@ safe_malloc(size_t size)
        return (data);
 }
 
+#ifdef HAVE_ZPL
 static char *
 safe_strdup(char *str)
 {
@@ -321,6 +329,7 @@ safe_strdup(char *str)
 
        return (dupstr);
 }
+#endif /* HAVE_ZPL */
 
 /*
  * Callback routine that will print out information for each of
@@ -488,6 +497,7 @@ parse_depth(char *opt, int *flags)
 
 #define        PROGRESS_DELAY 2                /* seconds */
 
+#ifdef HAVE_ZPL
 static char *pt_reverse = "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
 static time_t pt_begin;
 static char *pt_header = NULL;
@@ -539,6 +549,8 @@ finish_progress(char *done)
        free(pt_header);
        pt_header = NULL;
 }
+#endif /* HAVE_ZPL */
+
 /*
  * zfs clone [-p] [-o prop=value] ... <snap> <fs | vol>
  *
@@ -619,16 +631,19 @@ zfs_do_clone(int argc, char **argv)
        ret = zfs_clone(zhp, argv[1], props);
 
        /* create the mountpoint if necessary */
+#ifdef HAVE_ZPL
        if (ret == 0) {
                zfs_handle_t *clone;
 
                clone = zfs_open(g_zfs, argv[1], ZFS_TYPE_DATASET);
                if (clone != NULL) {
-                       if ((ret = zfs_mount(clone, NULL, 0)) == 0)
-                               ret = zfs_share(clone);
+                       if (zfs_get_type(clone) != ZFS_TYPE_VOLUME)
+                               if ((ret = zfs_mount(clone, NULL, 0)) == 0)
+                                       ret = zfs_share(clone);
                        zfs_close(clone);
                }
        }
+#endif /* HAVE_ZPL */
 
        zfs_close(zhp);
        nvlist_free(props);
@@ -663,7 +678,7 @@ zfs_do_create(int argc, char **argv)
 {
        zfs_type_t type = ZFS_TYPE_FILESYSTEM;
        zfs_handle_t *zhp = NULL;
-       uint64_t volsize;
+       uint64_t volsize = 0;
        int c;
        boolean_t noreserve = B_FALSE;
        boolean_t bflag = B_FALSE;
@@ -671,7 +686,7 @@ zfs_do_create(int argc, char **argv)
        int ret = 1;
        nvlist_t *props;
        uint64_t intval;
-       int canmount;
+       int canmount = ZFS_CANMOUNT_OFF;
 
        if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
                nomem();
@@ -756,7 +771,7 @@ zfs_do_create(int argc, char **argv)
                zfs_prop_t resv_prop;
                char *strval;
 
-               if (p = strchr(argv[0], '/'))
+               if ((p = strchr(argv[0], '/')))
                        *p = '\0';
                zpool_handle = zpool_open(g_zfs, argv[0]);
                if (p != NULL)
@@ -802,19 +817,21 @@ zfs_do_create(int argc, char **argv)
 
        if ((zhp = zfs_open(g_zfs, argv[0], ZFS_TYPE_DATASET)) == NULL)
                goto error;
+
+       ret = 0;
        /*
         * if the user doesn't want the dataset automatically mounted,
         * then skip the mount/share step
         */
-
-       canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
+       if (zfs_prop_valid_for_type(ZFS_PROP_CANMOUNT, type))
+               canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
 
        /*
         * Mount and/or share the new filesystem as appropriate.  We provide a
         * verbose error message to let the user know that their filesystem was
         * in fact created, even if we failed to mount or share it.
         */
-       ret = 0;
+#ifdef HAVE_ZPL
        if (canmount == ZFS_CANMOUNT_ON) {
                if (zfs_mount(zhp, NULL, 0) != 0) {
                        (void) fprintf(stderr, gettext("filesystem "
@@ -826,6 +843,7 @@ zfs_do_create(int argc, char **argv)
                        ret = 1;
                }
        }
+#endif /* HAVE_ZPL */
 
 error:
        if (zhp)
@@ -1673,7 +1691,7 @@ upgrade_set_callback(zfs_handle_t *zhp, void *data)
        if (version < cb->cb_version) {
                char verstr[16];
                (void) snprintf(verstr, sizeof (verstr),
-                   "%llu", cb->cb_version);
+                   "%llu", (u_longlong_t)cb->cb_version);
                if (cb->cb_lastfs[0] && !same_pool(zhp, cb->cb_lastfs)) {
                        /*
                         * If they did "zfs upgrade -a", then we could
@@ -1711,7 +1729,7 @@ zfs_do_upgrade(int argc, char **argv)
        boolean_t showversions = B_FALSE;
        int ret;
        upgrade_cbdata_t cb = { 0 };
-       char c;
+       signed char c;
        int flags = ZFS_ITER_ARGS_CAN_BE_PATHS;
 
        /* check options */
@@ -1780,11 +1798,11 @@ zfs_do_upgrade(int argc, char **argv)
                ret = zfs_for_each(argc, argv, flags, ZFS_TYPE_FILESYSTEM,
                    NULL, NULL, 0, upgrade_set_callback, &cb);
                (void) printf(gettext("%llu filesystems upgraded\n"),
-                   cb.cb_numupgraded);
+                   (u_longlong_t)cb.cb_numupgraded);
                if (cb.cb_numsamegraded) {
                        (void) printf(gettext("%llu filesystems already at "
                            "this version\n"),
-                           cb.cb_numsamegraded);
+                           (u_longlong_t)cb.cb_numsamegraded);
                }
                if (cb.cb_numfailed != 0)
                        ret = 1;
@@ -1948,9 +1966,9 @@ print_header(zprop_list_t *pl)
                if (pl->pl_next == NULL && !right_justify)
                        (void) printf("%s", header);
                else if (right_justify)
-                       (void) printf("%*s", pl->pl_width, header);
+                       (void) printf("%*s", (int)pl->pl_width, header);
                else
-                       (void) printf("%-*s", pl->pl_width, header);
+                       (void) printf("%-*s", (int)pl->pl_width, header);
        }
 
        (void) printf("\n");
@@ -2322,6 +2340,7 @@ typedef struct rollback_cbdata {
  * 'cb_dependent' is set, then this is a dependent and we should report it
  * without checking the transaction group.
  */
+#ifdef HAVE_ZPL
 static int
 rollback_check(zfs_handle_t *zhp, void *data)
 {
@@ -2381,10 +2400,12 @@ rollback_check(zfs_handle_t *zhp, void *data)
        zfs_close(zhp);
        return (0);
 }
+#endif /* HAVE_ZPL */
 
 static int
 zfs_do_rollback(int argc, char **argv)
 {
+#ifdef HAVE_ZPL
        int ret;
        int c;
        boolean_t force = B_FALSE;
@@ -2466,6 +2487,9 @@ out:
                return (0);
        else
                return (1);
+#else
+       return ENOSYS;
+#endif /*HAVE_ZPL*/
 }
 
 /*
@@ -2541,7 +2565,7 @@ zfs_do_set(int argc, char **argv)
                usage(B_FALSE);
        }
 
-       ret = zfs_for_each(argc - 2, argv + 2, NULL,
+       ret = zfs_for_each(argc - 2, argv + 2, 0,
            ZFS_TYPE_DATASET, NULL, NULL, 0, set_callback, &cb);
 
        return (ret);
@@ -2558,7 +2582,7 @@ zfs_do_snapshot(int argc, char **argv)
 {
        boolean_t recursive = B_FALSE;
        int ret;
-       char c;
+       signed char c;
        nvlist_t *props;
 
        if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0)
@@ -2888,7 +2912,7 @@ zfs_do_hold_rele_impl(int argc, char **argv, boolean_t holding)
                }
                if (holding) {
                        if (zfs_hold(zhp, delim+1, tag, recursive,
-                           temphold, B_FALSE) != 0)
+                           temphold, B_FALSE, -1, 0, 0) != 0)
                                ++errors;
                } else {
                        if (zfs_release(zhp, delim+1, tag, recursive) != 0)
@@ -2927,18 +2951,11 @@ zfs_do_release(int argc, char **argv)
        return (zfs_do_hold_rele_impl(argc, argv, B_FALSE));
 }
 
-typedef struct get_all_cbdata {
-       zfs_handle_t    **cb_handles;
-       size_t          cb_alloc;
-       size_t          cb_used;
-       uint_t          cb_types;
-       boolean_t       cb_verbose;
-} get_all_cbdata_t;
-
 #define        CHECK_SPINNER 30
 #define        SPINNER_TIME 3          /* seconds */
 #define        MOUNT_TIME 5            /* seconds */
 
+#ifdef HAVE_ZPL
 static int
 get_one_dataset(zfs_handle_t *zhp, void *data)
 {
@@ -2946,7 +2963,7 @@ get_one_dataset(zfs_handle_t *zhp, void *data)
        static int spinval = 0;
        static int spincheck = 0;
        static time_t last_spin_time = (time_t)0;
-       get_all_cbdata_t *cbp = data;
+       get_all_cb_t *cbp = data;
        zfs_type_t type = zfs_get_type(zhp);
 
        if (cbp->cb_verbose) {
@@ -2963,8 +2980,7 @@ get_one_dataset(zfs_handle_t *zhp, void *data)
        /*
         * Interate over any nested datasets.
         */
-       if (type == ZFS_TYPE_FILESYSTEM &&
-           zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
+       if (zfs_iter_filesystems(zhp, get_one_dataset, data) != 0) {
                zfs_close(zhp);
                return (1);
        }
@@ -2972,46 +2988,25 @@ get_one_dataset(zfs_handle_t *zhp, void *data)
        /*
         * Skip any datasets whose type does not match.
         */
-       if ((type & cbp->cb_types) == 0) {
+       if ((type & ZFS_TYPE_FILESYSTEM) == 0) {
                zfs_close(zhp);
                return (0);
        }
-
-       if (cbp->cb_alloc == cbp->cb_used) {
-               zfs_handle_t **handles;
-
-               if (cbp->cb_alloc == 0)
-                       cbp->cb_alloc = 64;
-               else
-                       cbp->cb_alloc *= 2;
-
-               handles = safe_malloc(cbp->cb_alloc * sizeof (void *));
-
-               if (cbp->cb_handles) {
-                       bcopy(cbp->cb_handles, handles,
-                           cbp->cb_used * sizeof (void *));
-                       free(cbp->cb_handles);
-               }
-
-               cbp->cb_handles = handles;
-       }
-
-       cbp->cb_handles[cbp->cb_used++] = zhp;
+       libzfs_add_handle(cbp, zhp);
+       assert(cbp->cb_used <= cbp->cb_alloc);
 
        return (0);
 }
 
 static void
-get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count,
-    boolean_t verbose)
+get_all_datasets(zfs_handle_t ***dslist, size_t *count, boolean_t verbose)
 {
-       get_all_cbdata_t cb = { 0 };
-       cb.cb_types = types;
+       get_all_cb_t cb = { 0 };
        cb.cb_verbose = verbose;
+       cb.cb_getone = get_one_dataset;
 
        if (verbose)
                set_progress_header(gettext("Reading ZFS config"));
-
        (void) zfs_iter_root(g_zfs, get_one_dataset, &cb);
 
        *dslist = cb.cb_handles;
@@ -3021,33 +3016,6 @@ get_all_datasets(uint_t types, zfs_handle_t ***dslist, size_t *count,
                finish_progress(gettext("done."));
 }
 
-static int
-dataset_cmp(const void *a, const void *b)
-{
-       zfs_handle_t **za = (zfs_handle_t **)a;
-       zfs_handle_t **zb = (zfs_handle_t **)b;
-       char mounta[MAXPATHLEN];
-       char mountb[MAXPATHLEN];
-       boolean_t gota, gotb;
-
-       if ((gota = (zfs_get_type(*za) == ZFS_TYPE_FILESYSTEM)) != 0)
-               verify(zfs_prop_get(*za, ZFS_PROP_MOUNTPOINT, mounta,
-                   sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0);
-       if ((gotb = (zfs_get_type(*zb) == ZFS_TYPE_FILESYSTEM)) != 0)
-               verify(zfs_prop_get(*zb, ZFS_PROP_MOUNTPOINT, mountb,
-                   sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0);
-
-       if (gota && gotb)
-               return (strcmp(mounta, mountb));
-
-       if (gota)
-               return (-1);
-       if (gotb)
-               return (1);
-
-       return (strcmp(zfs_get_name(a), zfs_get_name(b)));
-}
-
 /*
  * Generic callback for sharing or mounting filesystems.  Because the code is so
  * similar, we have a common function with an extra parameter to determine which
@@ -3069,184 +3037,180 @@ share_mount_one(zfs_handle_t *zhp, int op, int flags, char *protocol,
        const char *cmdname = op == OP_SHARE ? "share" : "mount";
        struct mnttab mnt;
        uint64_t zoned, canmount;
-       zfs_type_t type = zfs_get_type(zhp);
        boolean_t shared_nfs, shared_smb;
 
-       assert(type & (ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME));
+       assert(zfs_get_type(zhp) & ZFS_TYPE_FILESYSTEM);
 
-       if (type == ZFS_TYPE_FILESYSTEM) {
-               /*
-                * Check to make sure we can mount/share this dataset.  If we
-                * are in the global zone and the filesystem is exported to a
-                * local zone, or if we are in a local zone and the
-                * filesystem is not exported, then it is an error.
-                */
-               zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
+       /*
+        * Check to make sure we can mount/share this dataset.  If we
+        * are in the global zone and the filesystem is exported to a
+        * local zone, or if we are in a local zone and the
+        * filesystem is not exported, then it is an error.
+        */
+       zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
 
-               if (zoned && getzoneid() == GLOBAL_ZONEID) {
-                       if (!explicit)
-                               return (0);
+       if (zoned && getzoneid() == GLOBAL_ZONEID) {
+               if (!explicit)
+                       return (0);
 
-                       (void) fprintf(stderr, gettext("cannot %s '%s': "
-                           "dataset is exported to a local zone\n"), cmdname,
-                           zfs_get_name(zhp));
-                       return (1);
+               (void) fprintf(stderr, gettext("cannot %s '%s': "
+                   "dataset is exported to a local zone\n"), cmdname,
+                   zfs_get_name(zhp));
+               return (1);
 
-               } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
-                       if (!explicit)
-                               return (0);
+       } else if (!zoned && getzoneid() != GLOBAL_ZONEID) {
+               if (!explicit)
+                       return (0);
 
-                       (void) fprintf(stderr, gettext("cannot %s '%s': "
-                           "permission denied\n"), cmdname,
-                           zfs_get_name(zhp));
-                       return (1);
-               }
+               (void) fprintf(stderr, gettext("cannot %s '%s': "
+                   "permission denied\n"), cmdname,
+                   zfs_get_name(zhp));
+               return (1);
+       }
 
-               /*
-                * Ignore any filesystems which don't apply to us. This
-                * includes those with a legacy mountpoint, or those with
-                * legacy share options.
-                */
-               verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
-                   sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
-               verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
-                   sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
-               verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
-                   sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
-
-               if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
-                   strcmp(smbshareopts, "off") == 0) {
-                       if (!explicit)
-                               return (0);
+       /*
+        * Ignore any filesystems which don't apply to us. This
+        * includes those with a legacy mountpoint, or those with
+        * legacy share options.
+        */
+       verify(zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
+           sizeof (mountpoint), NULL, NULL, 0, B_FALSE) == 0);
+       verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS, shareopts,
+           sizeof (shareopts), NULL, NULL, 0, B_FALSE) == 0);
+       verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB, smbshareopts,
+           sizeof (smbshareopts), NULL, NULL, 0, B_FALSE) == 0);
+
+       if (op == OP_SHARE && strcmp(shareopts, "off") == 0 &&
+           strcmp(smbshareopts, "off") == 0) {
+               if (!explicit)
+                       return (0);
 
-                       (void) fprintf(stderr, gettext("cannot share '%s': "
-                           "legacy share\n"), zfs_get_name(zhp));
-                       (void) fprintf(stderr, gettext("use share(1M) to "
-                           "share this filesystem, or set "
-                           "sharenfs property on\n"));
-                       return (1);
-               }
+               (void) fprintf(stderr, gettext("cannot share '%s': "
+                   "legacy share\n"), zfs_get_name(zhp));
+               (void) fprintf(stderr, gettext("use share(1M) to "
+                   "share this filesystem, or set "
+                   "sharenfs property on\n"));
+               return (1);
+       }
 
-               /*
-                * We cannot share or mount legacy filesystems. If the
-                * shareopts is non-legacy but the mountpoint is legacy, we
-                * treat it as a legacy share.
-                */
-               if (strcmp(mountpoint, "legacy") == 0) {
-                       if (!explicit)
-                               return (0);
+       /*
+        * We cannot share or mount legacy filesystems. If the
+        * shareopts is non-legacy but the mountpoint is legacy, we
+        * treat it as a legacy share.
+        */
+       if (strcmp(mountpoint, "legacy") == 0) {
+               if (!explicit)
+                       return (0);
 
-                       (void) fprintf(stderr, gettext("cannot %s '%s': "
-                           "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
-                       (void) fprintf(stderr, gettext("use %s(1M) to "
-                           "%s this filesystem\n"), cmdname, cmdname);
-                       return (1);
-               }
+               (void) fprintf(stderr, gettext("cannot %s '%s': "
+                   "legacy mountpoint\n"), cmdname, zfs_get_name(zhp));
+               (void) fprintf(stderr, gettext("use %s(1M) to "
+                   "%s this filesystem\n"), cmdname, cmdname);
+               return (1);
+       }
 
-               if (strcmp(mountpoint, "none") == 0) {
-                       if (!explicit)
-                               return (0);
+       if (strcmp(mountpoint, "none") == 0) {
+               if (!explicit)
+                       return (0);
 
-                       (void) fprintf(stderr, gettext("cannot %s '%s': no "
-                           "mountpoint set\n"), cmdname, zfs_get_name(zhp));
-                       return (1);
-               }
+               (void) fprintf(stderr, gettext("cannot %s '%s': no "
+                   "mountpoint set\n"), cmdname, zfs_get_name(zhp));
+               return (1);
+       }
 
-               /*
-                * canmount     explicit        outcome
-                * on           no              pass through
-                * on           yes             pass through
-                * off          no              return 0
-                * off          yes             display error, return 1
-                * noauto       no              return 0
-                * noauto       yes             pass through
-                */
-               canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
-               if (canmount == ZFS_CANMOUNT_OFF) {
+       /*
+        * canmount     explicit        outcome
+        * on           no              pass through
+        * on           yes             pass through
+        * off          no              return 0
+        * off          yes             display error, return 1
+        * noauto       no              return 0
+        * noauto       yes             pass through
+        */
+       canmount = zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT);
+       if (canmount == ZFS_CANMOUNT_OFF) {
+               if (!explicit)
+                       return (0);
+
+               (void) fprintf(stderr, gettext("cannot %s '%s': "
+                   "'canmount' property is set to 'off'\n"), cmdname,
+                   zfs_get_name(zhp));
+               return (1);
+       } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
+               return (0);
+       }
+
+       /*
+        * At this point, we have verified that the mountpoint and/or
+        * shareopts are appropriate for auto management. If the
+        * filesystem is already mounted or shared, return (failing
+        * for explicit requests); otherwise mount or share the
+        * filesystem.
+        */
+       switch (op) {
+       case OP_SHARE:
+
+               shared_nfs = zfs_is_shared_nfs(zhp, NULL);
+               shared_smb = zfs_is_shared_smb(zhp, NULL);
+
+               if ((shared_nfs && shared_smb) ||
+                   ((shared_nfs && strcmp(shareopts, "on") == 0) &&
+                   (strcmp(smbshareopts, "off") == 0)) ||
+                   ((shared_smb && strcmp(smbshareopts, "on") == 0) &&
+                   (strcmp(shareopts, "off") == 0))) {
                        if (!explicit)
                                return (0);
 
-                       (void) fprintf(stderr, gettext("cannot %s '%s': "
-                           "'canmount' property is set to 'off'\n"), cmdname,
+                       (void) fprintf(stderr, gettext("cannot share "
+                           "'%s': filesystem already shared\n"),
                            zfs_get_name(zhp));
                        return (1);
-               } else if (canmount == ZFS_CANMOUNT_NOAUTO && !explicit) {
-                       return (0);
                }
 
-               /*
-                * At this point, we have verified that the mountpoint and/or
-                * shareopts are appropriate for auto management. If the
-                * filesystem is already mounted or shared, return (failing
-                * for explicit requests); otherwise mount or share the
-                * filesystem.
-                */
-               switch (op) {
-               case OP_SHARE:
-
-                       shared_nfs = zfs_is_shared_nfs(zhp, NULL);
-                       shared_smb = zfs_is_shared_smb(zhp, NULL);
-
-                       if (shared_nfs && shared_smb ||
-                           (shared_nfs && strcmp(shareopts, "on") == 0 &&
-                           strcmp(smbshareopts, "off") == 0) ||
-                           (shared_smb && strcmp(smbshareopts, "on") == 0 &&
-                           strcmp(shareopts, "off") == 0)) {
-                               if (!explicit)
-                                       return (0);
+               if (!zfs_is_mounted(zhp, NULL) &&
+                   zfs_mount(zhp, NULL, 0) != 0)
+                       return (1);
 
-                               (void) fprintf(stderr, gettext("cannot share "
-                                   "'%s': filesystem already shared\n"),
-                                   zfs_get_name(zhp));
+               if (protocol == NULL) {
+                       if (zfs_shareall(zhp) != 0)
                                return (1);
-                       }
-
-                       if (!zfs_is_mounted(zhp, NULL) &&
-                           zfs_mount(zhp, NULL, 0) != 0)
+               } else if (strcmp(protocol, "nfs") == 0) {
+                       if (zfs_share_nfs(zhp))
                                return (1);
-
-                       if (protocol == NULL) {
-                               if (zfs_shareall(zhp) != 0)
-                                       return (1);
-                       } else if (strcmp(protocol, "nfs") == 0) {
-                               if (zfs_share_nfs(zhp))
-                                       return (1);
-                       } else if (strcmp(protocol, "smb") == 0) {
-                               if (zfs_share_smb(zhp))
-                                       return (1);
-                       } else {
-                               (void) fprintf(stderr, gettext("cannot share "
-                                   "'%s': invalid share type '%s' "
-                                   "specified\n"),
-                                   zfs_get_name(zhp), protocol);
+               } else if (strcmp(protocol, "smb") == 0) {
+                       if (zfs_share_smb(zhp))
                                return (1);
-                       }
-
-                       break;
+               } else {
+                       (void) fprintf(stderr, gettext("cannot share "
+                           "'%s': invalid share type '%s' "
+                           "specified\n"),
+                           zfs_get_name(zhp), protocol);
+                       return (1);
+               }
 
-               case OP_MOUNT:
-                       if (options == NULL)
-                               mnt.mnt_mntopts = "";
-                       else
-                               mnt.mnt_mntopts = (char *)options;
+               break;
 
-                       if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
-                           zfs_is_mounted(zhp, NULL)) {
-                               if (!explicit)
-                                       return (0);
+       case OP_MOUNT:
+               if (options == NULL)
+                       mnt.mnt_mntopts = "";
+               else
+                       mnt.mnt_mntopts = (char *)options;
 
-                               (void) fprintf(stderr, gettext("cannot mount "
-                                   "'%s': filesystem already mounted\n"),
-                                   zfs_get_name(zhp));
-                               return (1);
-                       }
+               if (!hasmntopt(&mnt, MNTOPT_REMOUNT) &&
+                   zfs_is_mounted(zhp, NULL)) {
+                       if (!explicit)
+                               return (0);
 
-                       if (zfs_mount(zhp, options, flags) != 0)
-                               return (1);
-                       break;
+                       (void) fprintf(stderr, gettext("cannot mount "
+                           "'%s': filesystem already mounted\n"),
+                           zfs_get_name(zhp));
+                       return (1);
                }
-       } else
-               assert(op == OP_SHARE);
+
+               if (zfs_mount(zhp, options, flags) != 0)
+                       return (1);
+               break;
+       }
 
        return (0);
 }
@@ -3290,7 +3254,7 @@ append_options(char *mntopts, char *newopts)
        /* original length plus new string to append plus 1 for the comma */
        if (len + 1 + strlen(newopts) >= MNT_LINE_MAX) {
                (void) fprintf(stderr, gettext("the opts argument for "
-                   "'%c' option is too long (more than %d chars)\n"),
+                   "'%s' option is too long (more than %d chars)\n"),
                    "-o", MNT_LINE_MAX);
                usage(B_FALSE);
        }
@@ -3308,10 +3272,10 @@ share_mount(int op, int argc, char **argv)
        boolean_t verbose = B_FALSE;
        int c, ret = 0;
        char *options = NULL;
-       int types, flags = 0;
+       int flags = 0;
 
        /* check options */
-       while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:O" : "a"))
+       while ((c = getopt(argc, argv, op == OP_MOUNT ? ":avo:" : "a"))
            != -1) {
                switch (c) {
                case 'a':
@@ -3334,9 +3298,6 @@ share_mount(int op, int argc, char **argv)
                        append_options(options, optarg);
                        break;
 
-               case 'O':
-                       flags |= MS_OVERLAY;
-                       break;
                case ':':
                        (void) fprintf(stderr, gettext("missing argument for "
                            "'%c' option\n"), optopt);
@@ -3358,13 +3319,9 @@ share_mount(int op, int argc, char **argv)
                size_t i, count = 0;
                char *protocol = NULL;
 
-               if (op == OP_MOUNT) {
-                       types = ZFS_TYPE_FILESYSTEM;
-               } else if (argc > 0) {
-                       if (strcmp(argv[0], "nfs") == 0 ||
-                           strcmp(argv[0], "smb") == 0) {
-                               types = ZFS_TYPE_FILESYSTEM;
-                       } else {
+               if (op == OP_SHARE && argc > 0) {
+                       if (strcmp(argv[0], "nfs") != 0 &&
+                           strcmp(argv[0], "smb") != 0) {
                                (void) fprintf(stderr, gettext("share type "
                                    "must be 'nfs' or 'smb'\n"));
                                usage(B_FALSE);
@@ -3372,8 +3329,6 @@ share_mount(int op, int argc, char **argv)
                        protocol = argv[0];
                        argc--;
                        argv++;
-               } else {
-                       types = ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME;
                }
 
                if (argc != 0) {
@@ -3382,12 +3337,12 @@ share_mount(int op, int argc, char **argv)
                }
 
                start_progress_timer();
-               get_all_datasets(types, &dslist, &count, verbose);
+               get_all_datasets(&dslist, &count, verbose);
 
                if (count == 0)
                        return (0);
 
-               qsort(dslist, count, sizeof (void *), dataset_cmp);
+               qsort(dslist, count, sizeof (void *), libzfs_dataset_cmp);
 
                for (i = 0; i < count; i++) {
                        if (verbose)
@@ -3410,7 +3365,7 @@ share_mount(int op, int argc, char **argv)
                }
 
                /*
-                * When mount is given no arguments, go through /etc/mnttab and
+                * When mount is given no arguments, go through /etc/mtab and
                 * display any active ZFS mounts.  We hide any snapshots, since
                 * they are controlled automatically.
                 */
@@ -3427,17 +3382,14 @@ share_mount(int op, int argc, char **argv)
        } else {
                zfs_handle_t *zhp;
 
-               types = ZFS_TYPE_FILESYSTEM;
-               if (op == OP_SHARE)
-                       types |= ZFS_TYPE_VOLUME;
-
                if (argc > 1) {
                        (void) fprintf(stderr,
                            gettext("too many arguments\n"));
                        usage(B_FALSE);
                }
 
-               if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL) {
+               if ((zhp = zfs_open(g_zfs, argv[0],
+                   ZFS_TYPE_FILESYSTEM)) == NULL) {
                        ret = 1;
                } else {
                        ret = share_mount_one(zhp, op, flags, NULL, B_TRUE,
@@ -3448,6 +3400,7 @@ share_mount(int op, int argc, char **argv)
 
        return (ret);
 }
+#endif  /* HAVE_ZPL */
 
 /*
  * zfs mount -a [nfs]
@@ -3458,7 +3411,11 @@ share_mount(int op, int argc, char **argv)
 static int
 zfs_do_mount(int argc, char **argv)
 {
+#ifdef HAVE_ZPL
        return (share_mount(OP_MOUNT, argc, argv));
+#else
+       return ENOSYS;
+#endif  /* HAVE_ZPL */
 }
 
 /*
@@ -3470,9 +3427,14 @@ zfs_do_mount(int argc, char **argv)
 static int
 zfs_do_share(int argc, char **argv)
 {
+#ifdef HAVE_ZPL
        return (share_mount(OP_SHARE, argc, argv));
+#else
+       return ENOSYS;
+#endif  /* HAVE_ZPL */
 }
 
+#ifdef HAVE_ZPL
 typedef struct unshare_unmount_node {
        zfs_handle_t    *un_zhp;
        char            *un_mountp;
@@ -3491,7 +3453,7 @@ unshare_unmount_compare(const void *larg, const void *rarg, void *unused)
 
 /*
  * Convenience routine used by zfs_do_umount() and manual_unmount().  Given an
- * absolute path, find the entry /etc/mnttab, verify that its a ZFS filesystem,
+ * absolute path, find the entry /etc/mtab, verify that its a ZFS filesystem,
  * and unmount it appropriately.
  */
 static int
@@ -3505,7 +3467,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
        ino_t path_inode;
 
        /*
-        * Search for the path in /etc/mnttab.  Rather than looking for the
+        * Search for the path in /etc/mtab.  Rather than looking for the
         * specific path, which can be fooled by non-standard paths (i.e. ".."
         * or "//"), we stat() the path and search for the corresponding
         * (major,minor) device pair.
@@ -3532,7 +3494,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
                            "currently mounted\n"), cmdname, path);
                        return (1);
                }
-               (void) fprintf(stderr, gettext("warning: %s not in mnttab\n"),
+               (void) fprintf(stderr, gettext("warning: %s not in mtab\n"),
                    path);
                if ((ret = umount2(path, flags)) != 0)
                        (void) fprintf(stderr, gettext("%s: %s\n"), path,
@@ -3574,8 +3536,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
                    strcmp(smbshare_prop, "off") == 0) {
                        (void) fprintf(stderr, gettext("cannot unshare "
                            "'%s': legacy share\n"), path);
-                       (void) fprintf(stderr, gettext("use "
-                           "unshare(1M) to unshare this filesystem\n"));
+                       (void) fprintf(stderr, gettext("use exportfs(8) "
+                           "or smbcontrol(1) to unshare this filesystem\n"));
                } else if (!zfs_is_shared(zhp)) {
                        (void) fprintf(stderr, gettext("cannot unshare '%s': "
                            "not currently shared\n"), path);
@@ -3594,7 +3556,7 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
                        (void) fprintf(stderr, gettext("cannot unmount "
                            "'%s': legacy mountpoint\n"),
                            zfs_get_name(zhp));
-                       (void) fprintf(stderr, gettext("use umount(1M) "
+                       (void) fprintf(stderr, gettext("use umount(8) "
                            "to unmount this filesystem\n"));
                } else {
                        ret = zfs_unmountall(zhp, flags);
@@ -3616,7 +3578,7 @@ unshare_unmount(int op, int argc, char **argv)
        int do_all = 0;
        int flags = 0;
        int ret = 0;
-       int types, c;
+       int c;
        zfs_handle_t *zhp;
        char nfs_mnt_prop[ZFS_MAXPROPLEN];
        char sharesmb[ZFS_MAXPROPLEN];
@@ -3644,8 +3606,8 @@ unshare_unmount(int op, int argc, char **argv)
                /*
                 * We could make use of zfs_for_each() to walk all datasets in
                 * the system, but this would be very inefficient, especially
-                * since we would have to linearly search /etc/mnttab for each
-                * one.  Instead, do one pass through /etc/mnttab looking for
+                * since we would have to linearly search /etc/mtab for each
+                * one.  Instead, do one pass through /etc/mtab looking for
                 * zfs entries and call zfs_unmount() for each one.
                 *
                 * Things get a little tricky if the administrator has created
@@ -3657,7 +3619,7 @@ unshare_unmount(int op, int argc, char **argv)
                 */
                struct mnttab entry;
                uu_avl_pool_t *pool;
-               uu_avl_t *tree;
+               uu_avl_t *tree = NULL;
                unshare_unmount_node_t *node;
                uu_avl_index_t idx;
                uu_avl_walk_t *walk;
@@ -3792,68 +3754,63 @@ unshare_unmount(int op, int argc, char **argv)
                        return (unshare_unmount_path(op, argv[0],
                            flags, B_FALSE));
 
-               types = ZFS_TYPE_FILESYSTEM;
-               if (op == OP_SHARE)
-                       types |= ZFS_TYPE_VOLUME;
-
-               if ((zhp = zfs_open(g_zfs, argv[0], types)) == NULL)
+               if ((zhp = zfs_open(g_zfs, argv[0],
+                   ZFS_TYPE_FILESYSTEM)) == NULL)
                        return (1);
 
-               if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
-                       verify(zfs_prop_get(zhp, op == OP_SHARE ?
-                           ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
-                           nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
-                           NULL, 0, B_FALSE) == 0);
+               verify(zfs_prop_get(zhp, op == OP_SHARE ?
+                   ZFS_PROP_SHARENFS : ZFS_PROP_MOUNTPOINT,
+                   nfs_mnt_prop, sizeof (nfs_mnt_prop), NULL,
+                   NULL, 0, B_FALSE) == 0);
 
-                       switch (op) {
-                       case OP_SHARE:
-                               verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
-                                   nfs_mnt_prop,
-                                   sizeof (nfs_mnt_prop),
-                                   NULL, NULL, 0, B_FALSE) == 0);
-                               verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
-                                   sharesmb, sizeof (sharesmb), NULL, NULL,
-                                   0, B_FALSE) == 0);
-
-                               if (strcmp(nfs_mnt_prop, "off") == 0 &&
-                                   strcmp(sharesmb, "off") == 0) {
-                                       (void) fprintf(stderr, gettext("cannot "
-                                           "unshare '%s': legacy share\n"),
-                                           zfs_get_name(zhp));
-                                       (void) fprintf(stderr, gettext("use "
-                                           "unshare(1M) to unshare this "
-                                           "filesystem\n"));
-                                       ret = 1;
-                               } else if (!zfs_is_shared(zhp)) {
-                                       (void) fprintf(stderr, gettext("cannot "
-                                           "unshare '%s': not currently "
-                                           "shared\n"), zfs_get_name(zhp));
-                                       ret = 1;
-                               } else if (zfs_unshareall(zhp) != 0) {
-                                       ret = 1;
-                               }
-                               break;
+               switch (op) {
+               case OP_SHARE:
+                       verify(zfs_prop_get(zhp, ZFS_PROP_SHARENFS,
+                           nfs_mnt_prop,
+                           sizeof (nfs_mnt_prop),
+                           NULL, NULL, 0, B_FALSE) == 0);
+                       verify(zfs_prop_get(zhp, ZFS_PROP_SHARESMB,
+                           sharesmb, sizeof (sharesmb), NULL, NULL,
+                           0, B_FALSE) == 0);
+
+                       if (strcmp(nfs_mnt_prop, "off") == 0 &&
+                           strcmp(sharesmb, "off") == 0) {
+                               (void) fprintf(stderr, gettext("cannot "
+                                   "unshare '%s': legacy share\n"),
+                                   zfs_get_name(zhp));
+                               (void) fprintf(stderr, gettext("use "
+                                   "unshare(1M) to unshare this "
+                                   "filesystem\n"));
+                               ret = 1;
+                       } else if (!zfs_is_shared(zhp)) {
+                               (void) fprintf(stderr, gettext("cannot "
+                                   "unshare '%s': not currently "
+                                   "shared\n"), zfs_get_name(zhp));
+                               ret = 1;
+                       } else if (zfs_unshareall(zhp) != 0) {
+                               ret = 1;
+                       }
+                       break;
 
-                       case OP_MOUNT:
-                               if (strcmp(nfs_mnt_prop, "legacy") == 0) {
-                                       (void) fprintf(stderr, gettext("cannot "
-                                           "unmount '%s': legacy "
-                                           "mountpoint\n"), zfs_get_name(zhp));
-                                       (void) fprintf(stderr, gettext("use "
-                                           "umount(1M) to unmount this "
-                                           "filesystem\n"));
-                                       ret = 1;
-                               } else if (!zfs_is_mounted(zhp, NULL)) {
-                                       (void) fprintf(stderr, gettext("cannot "
-                                           "unmount '%s': not currently "
-                                           "mounted\n"),
-                                           zfs_get_name(zhp));
-                                       ret = 1;
-                               } else if (zfs_unmountall(zhp, flags) != 0) {
-                                       ret = 1;
-                               }
-                               break;
+               case OP_MOUNT:
+                       if (strcmp(nfs_mnt_prop, "legacy") == 0) {
+                               (void) fprintf(stderr, gettext("cannot "
+                                   "unmount '%s': legacy "
+                                   "mountpoint\n"), zfs_get_name(zhp));
+                               (void) fprintf(stderr, gettext("use "
+                                   "umount(1M) to unmount this "
+                                   "filesystem\n"));
+                               ret = 1;
+                       } else if (!zfs_is_mounted(zhp, NULL)) {
+                               (void) fprintf(stderr, gettext("cannot "
+                                   "unmount '%s': not currently "
+                                   "mounted\n"),
+                                   zfs_get_name(zhp));
+                               ret = 1;
+                       } else if (zfs_unmountall(zhp, flags) != 0) {
+                               ret = 1;
                        }
+                       break;
                }
 
                zfs_close(zhp);
@@ -3861,6 +3818,7 @@ unshare_unmount(int op, int argc, char **argv)
 
        return (ret);
 }
+#endif  /* HAVE_ZPL */
 
 /*
  * zfs unmount -a
@@ -3871,7 +3829,11 @@ unshare_unmount(int op, int argc, char **argv)
 static int
 zfs_do_unmount(int argc, char **argv)
 {
+#ifdef HAVE_ZPL
        return (unshare_unmount(OP_MOUNT, argc, argv));
+#else
+       return ENOSYS;
+#endif  /* HAVE_ZPL */
 }
 
 /*
@@ -3883,7 +3845,11 @@ zfs_do_unmount(int argc, char **argv)
 static int
 zfs_do_unshare(int argc, char **argv)
 {
+#ifdef HAVE_ZPL
        return (unshare_unmount(OP_SHARE, argc, argv));
+#else
+       return ENOSYS;
+#endif  /* HAVE_ZPL */
 }
 
 /* ARGSUSED */
@@ -3895,44 +3861,208 @@ zfs_do_python(int argc, char **argv)
        return (-1);
 }
 
+typedef struct option_map {
+       const char *name;
+       int mask;
+} option_map_t;
+
+static const option_map_t option_map[] = {
+       /* Canonicalized filesystem independent options from mount(8) */
+       { MNTOPT_NOAUTO,        MS_COMMENT      },
+       { MNTOPT_DEFAULTS,      MS_COMMENT      },
+       { MNTOPT_NODEVICES,     MS_NODEV        },
+       { MNTOPT_DIRSYNC,       MS_DIRSYNC      },
+       { MNTOPT_NOEXEC,        MS_NOEXEC       },
+       { MNTOPT_GROUP,         MS_GROUP        },
+       { MNTOPT_NETDEV,        MS_COMMENT      },
+       { MNTOPT_NOFAIL,        MS_COMMENT      },
+       { MNTOPT_NOSUID,        MS_NOSUID       },
+       { MNTOPT_OWNER,         MS_OWNER        },
+       { MNTOPT_REMOUNT,       MS_REMOUNT      },
+       { MNTOPT_RO,            MS_RDONLY       },
+       { MNTOPT_SYNC,          MS_SYNCHRONOUS  },
+       { MNTOPT_USER,          MS_USERS        },
+       { MNTOPT_USERS,         MS_USERS        },
+#ifdef MS_NOATIME
+       { MNTOPT_NOATIME,       MS_NOATIME      },
+#endif
+#ifdef MS_NODIRATIME
+       { MNTOPT_NODIRATIME,    MS_NODIRATIME   },
+#endif
+#ifdef MS_RELATIME
+       { MNTOPT_RELATIME,      MS_RELATIME     },
+#endif
+#ifdef MS_STRICTATIME
+       { MNTOPT_DFRATIME,      MS_STRICTATIME  },
+#endif
+#ifdef HAVE_SELINUX
+       { MNTOPT_CONTEXT,       MS_COMMENT      },
+       { MNTOPT_FSCONTEXT,     MS_COMMENT      },
+       { MNTOPT_DEFCONTEXT,    MS_COMMENT      },
+       { MNTOPT_ROOTCONTEXT,   MS_COMMENT      },
+#endif
+#ifdef MS_I_VERSION
+       { MNTOPT_IVERSION,      MS_I_VERSION    },
+#endif
+#ifdef MS_MANDLOCK
+       { MNTOPT_NBMAND,        MS_MANDLOCK     },
+#endif
+       /* Valid options not found in mount(8) */
+       { MNTOPT_BIND,          MS_BIND         },
+       { MNTOPT_RBIND,         MS_BIND|MS_REC  },
+       { MNTOPT_COMMENT,       MS_COMMENT      },
+       { MNTOPT_BOOTWAIT,      MS_COMMENT      },
+       { MNTOPT_NOBOOTWAIT,    MS_COMMENT      },
+       { MNTOPT_OPTIONAL,      MS_COMMENT      },
+       { MNTOPT_SHOWTHROUGH,   MS_COMMENT      },
+#ifdef MS_NOSUB
+       { MNTOPT_NOSUB,         MS_NOSUB        },
+#endif
+#ifdef MS_SILENT
+       { MNTOPT_QUIET,         MS_SILENT       },
+#endif
+       /* Custom zfs options */
+       { MNTOPT_NOXATTR,       MS_COMMENT      },
+       { NULL,                 0               } };
+
+/*
+ * Break the mount option in to a name/value pair.  The name is
+ * validated against the option map and mount flags set accordingly.
+ */
+static int
+parse_option(char *mntopt, unsigned long *mntflags, int sloppy)
+{
+       const option_map_t *opt;
+       char *ptr, *name, *value = NULL;
+       int rc;
+
+       name = strdup(mntopt);
+       if (name == NULL)
+               return (ENOMEM);
+
+       for (ptr = name; ptr && *ptr; ptr++) {
+               if (*ptr == '=') {
+                       *ptr = '\0';
+                       value = ptr+1;
+                       break;
+               }
+       }
+
+       for (opt = option_map; opt->name != NULL; opt++) {
+               if (strncmp(name, opt->name, strlen(name)) == 0) {
+                       *mntflags |= opt->mask;
+
+                       /* MS_USERS implies default user options */
+                       if (opt->mask & (MS_USERS))
+                               *mntflags |= (MS_NOEXEC|MS_NOSUID|MS_NODEV);
+
+                       /* MS_OWNER|MS_GROUP imply default owner options */
+                       if (opt->mask & (MS_OWNER | MS_GROUP))
+                               *mntflags |= (MS_NOSUID|MS_NODEV);
+
+                       rc = 0;
+                       goto out;
+               }
+       }
+
+       if (!sloppy)
+               rc = ENOENT;
+out:
+       /* If required further process on the value may be done here */
+       free(name);
+       return (rc);
+}
+
+/*
+ * Translate the mount option string in to MS_* mount flags for the
+ * kernel vfs.  When sloppy is non-zero unknown options will be ignored
+ * otherwise they are considered fatal are copied in to badopt.
+ */
+static int
+parse_options(char *mntopts, unsigned long *mntflags, int sloppy, char *badopt)
+{
+       int rc = 0, quote = 0;
+       char *ptr, *opt, *opts;
+
+       opts = strdup(mntopts);
+       if (opts == NULL)
+               return (ENOMEM);
+
+       *mntflags = 0;
+       opt = NULL;
+
+       /*
+        * Scan through all mount options which must be comma delimited.
+        * We must be careful to notice regions which are double quoted
+        * and skip commas in these regions.  Each option is then checked
+        * to determine if it is a known option.
+        */
+       for (ptr = opts; ptr && *ptr; ptr++) {
+               if (opt == NULL)
+                       opt = ptr;
+
+               if (*ptr == '"')
+                       quote = !quote;
+
+               if (quote)
+                       continue;
+
+               if ((*ptr == ',') || (*ptr == '\0')) {
+                       *ptr = '\0';
+                       rc = parse_option(opt, mntflags, sloppy);
+                       if (rc) {
+                               strcpy(badopt, opt);
+                               goto out;
+                       }
+
+                       opt = NULL;
+               }
+       }
+out:
+       free(opts);
+       return (rc);
+}
+
 /*
- * Called when invoked as /etc/fs/zfs/mount.  Do the mount if the mountpoint is
- * 'legacy'.  Otherwise, complain that use should be using 'zfs mount'.
+ * Called when invoked as /sbin/mount.zfs, mount helper for mount(8).
  */
 static int
 manual_mount(int argc, char **argv)
 {
        zfs_handle_t *zhp;
-       char mountpoint[ZFS_MAXPROPLEN];
+       char legacy[ZFS_MAXPROPLEN];
        char mntopts[MNT_LINE_MAX] = { '\0' };
-       int ret;
-       int c;
-       int flags = 0;
-       char *dataset, *path;
+       char badopt[MNT_LINE_MAX] = { '\0' };
+       char *dataset, *mntpoint;
+       unsigned long mntflags;
+       int sloppy = 0, fake = 0, verbose = 0;
+       int rc, c;
 
        /* check options */
-       while ((c = getopt(argc, argv, ":mo:O")) != -1) {
+       while ((c = getopt(argc, argv, "sfnvo:h?")) != -1) {
                switch (c) {
-               case 'o':
-                       (void) strlcpy(mntopts, optarg, sizeof (mntopts));
+               case 's':
+                       sloppy = 1;
                        break;
-               case 'O':
-                       flags |= MS_OVERLAY;
+               case 'f':
+                       fake = 1;
                        break;
-               case 'm':
-                       flags |= MS_NOMNTTAB;
+               case 'n':
+                       /* Ignored, handled by mount(8) */
                        break;
-               case ':':
-                       (void) fprintf(stderr, gettext("missing argument for "
-                           "'%c' option\n"), optopt);
-                       usage(B_FALSE);
+               case 'v':
+                       verbose++;
                        break;
+               case 'o':
+                       (void) strlcpy(mntopts, optarg, sizeof (mntopts));
+                       break;
+               case 'h':
                case '?':
-                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                       (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
                            optopt);
-                       (void) fprintf(stderr, gettext("usage: mount [-o opts] "
-                           "<path>\n"));
-                       return (2);
+                       (void) fprintf(stderr, gettext("Usage: mount.zfs "
+                           "[-sfnv] [-o options] <dataset> <mountpoint>\n"));
+                       return (MOUNT_USAGE);
                }
        }
 
@@ -3950,85 +4080,149 @@ manual_mount(int argc, char **argv)
                else
                        (void) fprintf(stderr, gettext("too many arguments\n"));
                (void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
-               return (2);
+               return (MOUNT_USAGE);
        }
 
        dataset = argv[0];
-       path = argv[1];
+       mntpoint = argv[1];
 
-       /* try to open the dataset */
-       if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL)
-               return (1);
+       /* try to open the dataset to access the mount point */
+       if ((zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_FILESYSTEM)) == NULL) {
+               (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
+                   "mounted, unable to open the dataset\n"), dataset);
+               return (MOUNT_USAGE);
+       }
 
-       (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint,
-           sizeof (mountpoint), NULL, NULL, 0, B_FALSE);
+       (void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, legacy,
+           sizeof (legacy), NULL, NULL, 0, B_FALSE);
 
-       /* check for legacy mountpoint and complain appropriately */
-       ret = 0;
-       if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) {
-               if (mount(dataset, path, MS_OPTIONSTR | flags, MNTTYPE_ZFS,
-                   NULL, 0, mntopts, sizeof (mntopts)) != 0) {
-                       (void) fprintf(stderr, gettext("mount failed: %s\n"),
-                           strerror(errno));
-                       ret = 1;
-               }
-       } else {
+       zfs_close(zhp);
+
+       /* check for legacy mountpoint or util mount option */
+       if ((!strcmp(legacy, ZFS_MOUNTPOINT_LEGACY) == 0) &&
+           (strstr(mntopts, MNTOPT_ZFSUTIL) == NULL)) {
                (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
-                   "mounted using 'mount -F zfs'\n"), dataset);
+                   "mounted using 'mount -a -t zfs'\n"), dataset);
                (void) fprintf(stderr, gettext("Use 'zfs set mountpoint=%s' "
-                   "instead.\n"), path);
-               (void) fprintf(stderr, gettext("If you must use 'mount -F zfs' "
-                   "or /etc/vfstab, use 'zfs set mountpoint=legacy'.\n"));
-               (void) fprintf(stderr, gettext("See zfs(1M) for more "
+                   "instead.\n"), mntpoint);
+               (void) fprintf(stderr, gettext("If you must use 'mount -a -t "
+                   "zfs' or /etc/fstab, use 'zfs set mountpoint=legacy'.\n"));
+               (void) fprintf(stderr, gettext("See zfs(8) for more "
                    "information.\n"));
-               ret = 1;
+               return (MOUNT_USAGE);
        }
 
-       return (ret);
+       /* validate mount options and set mntflags */
+       rc = parse_options(mntopts, &mntflags, sloppy, badopt);
+       if (rc) {
+               switch (rc) {
+               case ENOMEM:
+                       (void) fprintf(stderr, gettext("filesystem '%s' "
+                           "cannot be mounted due to a memory allocation "
+                           "failure\n"), dataset);
+                       return (MOUNT_SYSERR);
+               case EINVAL:
+                       (void) fprintf(stderr, gettext("filesystem '%s' "
+                           "cannot be mounted of due to the invalid option "
+                           "'%s'\n"), dataset, badopt);
+                       (void) fprintf(stderr, gettext("Use the '-s' option "
+                           "to ignore the bad mount option.\n"));
+                       return (MOUNT_USAGE);
+               default:
+                       (void) fprintf(stderr, gettext("filesystem '%s' "
+                           "cannot be mounted due to internal error %d\n"),
+                           dataset, rc);
+                       return (MOUNT_SOFTWARE);
+               }
+       }
+
+       if (verbose > 2)
+               printf("mount.zfs: dataset: \"%s\", mountpoint: \"%s\" "
+                   "mountflags: 0x%lx, mountopts: \"%s\"\n", dataset,
+                   mntpoint, mntflags, mntopts);
+
+       /* load the zfs posix layer module (zpl) */
+       if (libzfs_load_module("zpl")) {
+               (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
+                   "mounted without the zpl kernel module\n"), dataset);
+               (void) fprintf(stderr, gettext("Use 'dmesg' to determine why "
+                   "the module could not be loaded.\n"));
+               return (MOUNT_SYSERR);
+       }
+
+       if (!fake) {
+               rc = mount(dataset, mntpoint, MNTTYPE_ZFS, mntflags, mntopts);
+               if (rc) {
+                       (void) fprintf(stderr, gettext("filesystem '%s' can"
+                           "not be mounted due to error %d\n"), dataset, rc);
+                       return (MOUNT_USAGE);
+               }
+       }
+
+       return (MOUNT_SUCCESS);
 }
 
+#ifdef HAVE_UNMOUNT_HELPER
 /*
- * Called when invoked as /etc/fs/zfs/umount.  Unlike a manual mount, we allow
- * unmounts of non-legacy filesystems, as this is the dominant administrative
- * interface.
+ * Called when invoked as /sbin/umount.zfs, mount helper for mount(8).
+ * Unlike a manual mount, we allow unmounts of non-legacy filesystems,
+ * as this is the dominant administrative interface.
  */
 static int
 manual_unmount(int argc, char **argv)
 {
-       int flags = 0;
+       int verbose = 0, flags = 0;
        int c;
 
        /* check options */
-       while ((c = getopt(argc, argv, "f")) != -1) {
+       while ((c = getopt(argc, argv, "nlfvrh?")) != -1) {
                switch (c) {
+               case 'n':
+                       /* Ignored, handled by mount(8) */
+                       break;
+               case 'l':
+                       flags = MS_DETACH;
+                       break;
                case 'f':
                        flags = MS_FORCE;
                        break;
+               case 'v':
+                       verbose++;
+                       break;
+               case 'r':
+                       /* Remount read-only on umount failure, unsupported */
+                       (void) fprintf(stderr, gettext("Unsupported option "
+                           "'%c'\n"), optopt);
+                       return (MOUNT_USAGE);
+               case 'h':
                case '?':
-                       (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+                       (void) fprintf(stderr, gettext("Invalid option '%c'\n"),
                            optopt);
-                       (void) fprintf(stderr, gettext("usage: unmount [-f] "
-                           "<path>\n"));
-                       return (2);
+                       (void) fprintf(stderr, gettext("Usage: umount.zfs "
+                           "[-nlfvr] <mountpoint>\n"));
+                       return (MOUNT_USAGE);
                }
        }
 
        argc -= optind;
        argv += optind;
 
-       /* check arguments */
+       /* check that we only have one argument */
        if (argc != 1) {
                if (argc == 0)
-                       (void) fprintf(stderr, gettext("missing path "
+                       (void) fprintf(stderr, gettext("missing mountpoint "
                            "argument\n"));
                else
                        (void) fprintf(stderr, gettext("too many arguments\n"));
-               (void) fprintf(stderr, gettext("usage: unmount [-f] <path>\n"));
-               return (2);
+
+               (void) fprintf(stderr, gettext("Usage: umount.zfs [-nlfvr] "
+                   "<mountpoint>\n"));
+               return (MOUNT_USAGE);
        }
 
        return (unshare_unmount_path(OP_MOUNT, argv[0], flags, B_TRUE));
 }
+#endif /* HAVE_UNMOUNT_HELPER */
 
 static int
 find_command_idx(char *command, int *idx)
@@ -4047,11 +4241,86 @@ find_command_idx(char *command, int *idx)
        return (1);
 }
 
+static int
+zfs_do_diff(int argc, char **argv)
+{
+       zfs_handle_t *zhp;
+       int flags = 0;
+       char *tosnap = NULL;
+       char *fromsnap = NULL;
+       char *atp, *copy;
+       int err;
+       int c;
+
+       while ((c = getopt(argc, argv, "FHt")) != -1) {
+               switch (c) {
+               case 'F':
+                       flags |= ZFS_DIFF_CLASSIFY;
+                       break;
+               case 'H':
+                       flags |= ZFS_DIFF_PARSEABLE;
+                       break;
+               case 't':
+                       flags |= ZFS_DIFF_TIMESTAMP;
+                       break;
+               default:
+                       (void) fprintf(stderr,
+                           gettext("invalid option '%c'\n"), optopt);
+                       usage(B_FALSE);
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+       if (argc < 1) {
+               (void) fprintf(stderr,
+               gettext("must provide at least one snapshot name\n"));
+               usage(B_FALSE);
+       }
+
+       if (argc > 2) {
+               (void) fprintf(stderr, gettext("too many arguments\n"));
+               usage(B_FALSE);
+       }
+
+       fromsnap = argv[0];
+       tosnap = (argc == 2) ? argv[1] : NULL;
+
+       copy = NULL;
+       if (*fromsnap != '@')
+               copy = strdup(fromsnap);
+       else if (tosnap)
+               copy = strdup(tosnap);
+       if (copy == NULL)
+               usage(B_FALSE);
+
+       if ((atp = strchr(copy, '@')))
+               *atp = '\0';
+
+       if ((zhp = zfs_open(g_zfs, copy, ZFS_TYPE_FILESYSTEM)) == NULL)
+               return (1);
+
+       free(copy);
+
+       /*
+        * Ignore SIGPIPE so that the library can give us
+        * information on any failure
+        */
+       (void) sigignore(SIGPIPE);
+
+       err = zfs_show_diffs(zhp, STDOUT_FILENO, fromsnap, tosnap, flags);
+
+       zfs_close(zhp);
+
+       return (err != 0);
+}
+
 int
 main(int argc, char **argv)
 {
        int ret;
-       int i;
+       int i = 0;
        char *progname;
        char *cmdname;
 
@@ -4060,17 +4329,6 @@ main(int argc, char **argv)
 
        opterr = 0;
 
-       if ((g_zfs = libzfs_init()) == NULL) {
-               (void) fprintf(stderr, gettext("internal error: failed to "
-                   "initialize ZFS library\n"));
-               return (1);
-       }
-
-       zpool_set_history_str("zfs", argc, argv, history_str);
-       verify(zpool_stage_history(g_zfs, history_str) == 0);
-
-       libzfs_print_on_error(g_zfs, B_TRUE);
-
        if ((mnttab_file = fopen(MNTTAB, "r")) == NULL) {
                (void) fprintf(stderr, gettext("internal error: unable to "
                    "open %s\n"), MNTTAB);
@@ -4082,10 +4340,12 @@ main(int argc, char **argv)
         * Determine if we should take this behavior based on argv[0].
         */
        progname = basename(argv[0]);
-       if (strcmp(progname, "mount") == 0) {
+       if (strcmp(progname, "mount.zfs") == 0) {
                ret = manual_mount(argc, argv);
-       } else if (strcmp(progname, "umount") == 0) {
+#ifdef HAVE_UNMOUNT_HELPER
+       } else if (strcmp(progname, "umount.zfs") == 0) {
                ret = manual_unmount(argc, argv);
+#endif /* HAVE_UNMOUNT_HELPER */
        } else {
                /*
                 * Make sure the user has specified some command.
@@ -4112,9 +4372,18 @@ main(int argc, char **argv)
                /*
                 * Special case '-?'
                 */
-               if (strcmp(cmdname, "-?") == 0)
+               if ((strcmp(cmdname, "-?") == 0) ||
+                   (strcmp(cmdname, "--help") == 0))
                        usage(B_TRUE);
 
+               if ((g_zfs = libzfs_init()) == NULL)
+                       return (1);
+
+               zpool_set_history_str("zfs", argc, argv, history_str);
+               verify(zpool_stage_history(g_zfs, history_str) == 0);
+
+               libzfs_print_on_error(g_zfs, B_TRUE);
+
                /*
                 * Run the appropriate command.
                 */
@@ -4130,6 +4399,7 @@ main(int argc, char **argv)
                        (void) fprintf(stderr, gettext("unrecognized "
                            "command '%s'\n"), cmdname);
                        usage(B_FALSE);
+                       ret = 1;
                }
                libzfs_mnttab_cache(g_zfs, B_FALSE);
        }