]> git.proxmox.com Git - mirror_zfs.git/blobdiff - lib/libzfs/libzfs_dataset.c
Replace ZPROP_INVAL with ZPROP_USERPROP where it means a user property
[mirror_zfs.git] / lib / libzfs / libzfs_dataset.c
index e79a936f94e7a67d4aa791dc89290f64a2bcbd12..24d9b81f4976cfd7cd357ebec69934f9c95dd2c4 100644 (file)
@@ -21,8 +21,8 @@
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2017 by Delphix. All rights reserved.
+ * Copyright 2019 Joyent, Inc.
+ * Copyright (c) 2011, 2020 by Delphix. All rights reserved.
  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright (c) 2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
  * Copyright (c) 2013 Martin Matuska. All rights reserved.
@@ -30,7 +30,9 @@
  * Copyright 2017 Nexenta Systems, Inc.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  * Copyright 2017-2018 RackTop Systems.
- * Copyright (c) 2018 Datto Inc.
+ * Copyright (c) 2019 Datto Inc.
+ * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
+ * Copyright (c) 2021 Matt Fiddaman
  */
 
 #include <ctype.h>
@@ -47,8 +49,6 @@
 #include <sys/mount.h>
 #include <pwd.h>
 #include <grp.h>
-#include <stddef.h>
-#include <ucred.h>
 #ifdef HAVE_IDMAP
 #include <idmap.h>
 #include <aclutils.h>
@@ -65,7 +65,6 @@
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
 #include "libzfs_impl.h"
-#include "libzfs.h"
 #include "zfs_deleg.h"
 
 static int userquota_propname_decode(const char *propname, boolean_t zoned,
@@ -108,6 +107,41 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
        namecheck_err_t why;
        char what;
 
+       if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
+               if (hdl != NULL)
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "snapshot delimiter '@' is not expected here"));
+               return (0);
+       }
+
+       if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
+               if (hdl != NULL)
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "missing '@' delimiter in snapshot name"));
+               return (0);
+       }
+
+       if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) {
+               if (hdl != NULL)
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "bookmark delimiter '#' is not expected here"));
+               return (0);
+       }
+
+       if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) {
+               if (hdl != NULL)
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "missing '#' delimiter in bookmark name"));
+               return (0);
+       }
+
+       if (modifying && strchr(path, '%') != NULL) {
+               if (hdl != NULL)
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "invalid character %c in name"), '%');
+               return (0);
+       }
+
        if (entity_namecheck(path, &why, &what) != 0) {
                if (hdl != NULL) {
                        switch (why) {
@@ -123,7 +157,8 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
 
                        case NAME_ERR_EMPTY_COMPONENT:
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "empty component in name"));
+                                   "empty component or misplaced '@'"
+                                   " or '#' delimiter in name"));
                                break;
 
                        case NAME_ERR_TRAILING_SLASH:
@@ -158,6 +193,16 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
                                    "reserved disk name"));
                                break;
 
+                       case NAME_ERR_SELF_REF:
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "self reference, '.' is found in name"));
+                               break;
+
+                       case NAME_ERR_PARENT_REF:
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "parent reference, '..' is found in name"));
+                               break;
+
                        default:
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "(%d) not defined"), why);
@@ -168,43 +213,6 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
                return (0);
        }
 
-       if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
-               if (hdl != NULL)
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "snapshot delimiter '@' is not expected here"));
-               return (0);
-       }
-
-       if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
-               if (hdl != NULL)
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "missing '@' delimiter in snapshot name, "
-                           "did you mean to use -r?"));
-               return (0);
-       }
-
-       if (!(type & ZFS_TYPE_BOOKMARK) && strchr(path, '#') != NULL) {
-               if (hdl != NULL)
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "bookmark delimiter '#' is not expected here"));
-               return (0);
-       }
-
-       if (type == ZFS_TYPE_BOOKMARK && strchr(path, '#') == NULL) {
-               if (hdl != NULL)
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "missing '#' delimiter in bookmark name, "
-                           "did you mean to use -r?"));
-               return (0);
-       }
-
-       if (modifying && strchr(path, '%') != NULL) {
-               if (hdl != NULL)
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "invalid character %c in name"), '%');
-               return (0);
-       }
-
        return (-1);
 }
 
@@ -225,7 +233,6 @@ process_user_props(zfs_handle_t *zhp, nvlist_t *props)
 {
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        nvpair_t *elem;
-       nvlist_t *propval;
        nvlist_t *nvl;
 
        if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
@@ -238,7 +245,7 @@ process_user_props(zfs_handle_t *zhp, nvlist_t *props)
                if (!zfs_prop_user(nvpair_name(elem)))
                        continue;
 
-               verify(nvpair_value_nvlist(elem, &propval) == 0);
+               nvlist_t *propval = fnvpair_value_nvlist(elem);
                if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
                        nvlist_free(nvl);
                        (void) no_memory(hdl);
@@ -322,14 +329,11 @@ get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
 
        (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
 
-       while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
-               if (errno == ENOMEM) {
-                       if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
-                               return (-1);
-                       }
-               } else {
+       while (zfs_ioctl(hdl, ZFS_IOC_OBJSET_STATS, zc) != 0) {
+               if (errno == ENOMEM)
+                       zcmd_expand_dst_nvlist(hdl, zc);
+               else
                        return (-1);
-               }
        }
        return (0);
 }
@@ -345,17 +349,14 @@ get_recvd_props_ioctl(zfs_handle_t *zhp)
        zfs_cmd_t zc = {"\0"};
        int err;
 
-       if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
-               return (-1);
+       zcmd_alloc_dst_nvlist(hdl, &zc, 0);
 
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-       while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
-               if (errno == ENOMEM) {
-                       if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
-                               return (-1);
-                       }
-               } else {
+       while (zfs_ioctl(hdl, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
+               if (errno == ENOMEM)
+                       zcmd_expand_dst_nvlist(hdl, &zc);
+               else {
                        zcmd_free_nvlists(&zc);
                        return (-1);
                }
@@ -407,8 +408,8 @@ get_stats(zfs_handle_t *zhp)
        int rc = 0;
        zfs_cmd_t zc = {"\0"};
 
-       if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
-               return (-1);
+       zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0);
+
        if (get_stats_ioctl(zhp, &zc) != 0)
                rc = -1;
        else if (put_stats_zhdl(zhp, &zc) != 0)
@@ -440,14 +441,19 @@ make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
         * We've managed to open the dataset and gather statistics.  Determine
         * the high-level type.
         */
-       if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
+       if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) {
                zhp->zfs_head_type = ZFS_TYPE_VOLUME;
-       else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
+       } else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS) {
                zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
-       else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER)
+       } else if (zhp->zfs_dmustats.dds_type == DMU_OST_OTHER) {
+               errno = EINVAL;
                return (-1);
-       else
+       } else if (zhp->zfs_dmustats.dds_inconsistent) {
+               errno = EBUSY;
+               return (-1);
+       } else {
                abort();
+       }
 
        if (zhp->zfs_dmustats.dds_is_snapshot)
                zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
@@ -476,10 +482,8 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
 
        zhp->zfs_hdl = hdl;
        (void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
-       if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
-               free(zhp);
-               return (NULL);
-       }
+       zcmd_alloc_dst_nvlist(hdl, &zc, 0);
+
        if (get_stats_ioctl(zhp, &zc) == -1) {
                zcmd_free_nvlists(&zc);
                free(zhp);
@@ -585,7 +589,6 @@ zfs_bookmark_exists(const char *path)
        int err;
        boolean_t rv;
 
-
        (void) strlcpy(fsname, path, sizeof (fsname));
        pound = strchr(fsname, '#');
        if (pound == NULL)
@@ -675,7 +678,7 @@ zfs_handle_t *
 zfs_open(libzfs_handle_t *hdl, const char *path, int types)
 {
        zfs_handle_t *zhp;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        char *bookp;
 
        (void) snprintf(errbuf, sizeof (errbuf),
@@ -785,27 +788,28 @@ libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
 
        rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
 
-       return (AVL_ISIGN(rv));
+       return (TREE_ISIGN(rv));
 }
 
 void
 libzfs_mnttab_init(libzfs_handle_t *hdl)
 {
+       pthread_mutex_init(&hdl->libzfs_mnttab_cache_lock, NULL);
        assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
        avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
            sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
 }
 
-int
+static int
 libzfs_mnttab_update(libzfs_handle_t *hdl)
 {
+       FILE *mnttab;
        struct mnttab entry;
 
-       /* Reopen MNTTAB to prevent reading stale data from open file */
-       if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
+       if ((mnttab = fopen(MNTTAB, "re")) == NULL)
                return (ENOENT);
 
-       while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
+       while (getmntent(mnttab, &entry) == 0) {
                mnttab_node_t *mtn;
                avl_index_t where;
 
@@ -831,6 +835,7 @@ libzfs_mnttab_update(libzfs_handle_t *hdl)
                avl_add(&hdl->libzfs_mnttab_cache, mtn);
        }
 
+       (void) fclose(mnttab);
        return (0);
 }
 
@@ -849,6 +854,7 @@ libzfs_mnttab_fini(libzfs_handle_t *hdl)
                free(mtn);
        }
        avl_destroy(&hdl->libzfs_mnttab_cache);
+       (void) pthread_mutex_destroy(&hdl->libzfs_mnttab_cache_lock);
 }
 
 void
@@ -861,9 +867,10 @@ int
 libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
     struct mnttab *entry)
 {
+       FILE *mnttab;
        mnttab_node_t find;
        mnttab_node_t *mtn;
-       int error;
+       int ret = ENOENT;
 
        if (!hdl->libzfs_mnttab_enable) {
                struct mnttab srch = { 0 };
@@ -871,29 +878,34 @@ libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
                if (avl_numnodes(&hdl->libzfs_mnttab_cache))
                        libzfs_mnttab_fini(hdl);
 
-               /* Reopen MNTTAB to prevent reading stale data from open file */
-               if (freopen(MNTTAB, "r", hdl->libzfs_mnttab) == NULL)
+               if ((mnttab = fopen(MNTTAB, "re")) == NULL)
                        return (ENOENT);
 
                srch.mnt_special = (char *)fsname;
                srch.mnt_fstype = MNTTYPE_ZFS;
-               if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
-                       return (0);
-               else
-                       return (ENOENT);
+               ret = getmntany(mnttab, entry, &srch) ? ENOENT : 0;
+               (void) fclose(mnttab);
+               return (ret);
        }
 
-       if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
-               if ((error = libzfs_mnttab_update(hdl)) != 0)
+       pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
+       if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0) {
+               int error;
+
+               if ((error = libzfs_mnttab_update(hdl)) != 0) {
+                       pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
                        return (error);
+               }
+       }
 
        find.mtn_mt.mnt_special = (char *)fsname;
        mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
        if (mtn) {
                *entry = mtn->mtn_mt;
-               return (0);
+               ret = 0;
        }
-       return (ENOENT);
+       pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
+       return (ret);
 }
 
 void
@@ -902,14 +914,28 @@ libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
 {
        mnttab_node_t *mtn;
 
-       if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
-               return;
-       mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
-       mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
-       mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
-       mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
-       mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
-       avl_add(&hdl->libzfs_mnttab_cache, mtn);
+       pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
+       if (avl_numnodes(&hdl->libzfs_mnttab_cache) != 0) {
+               mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
+               mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
+               mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
+               mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
+               mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
+               /*
+                * Another thread may have already added this entry
+                * via libzfs_mnttab_update. If so we should skip it.
+                */
+               if (avl_find(&hdl->libzfs_mnttab_cache, mtn, NULL) != NULL) {
+                       free(mtn->mtn_mt.mnt_special);
+                       free(mtn->mtn_mt.mnt_mountp);
+                       free(mtn->mtn_mt.mnt_fstype);
+                       free(mtn->mtn_mt.mnt_mntopts);
+                       free(mtn);
+               } else {
+                       avl_add(&hdl->libzfs_mnttab_cache, mtn);
+               }
+       }
+       pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
 }
 
 void
@@ -918,6 +944,7 @@ libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
        mnttab_node_t find;
        mnttab_node_t *ret;
 
+       pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
        find.mtn_mt.mnt_special = (char *)fsname;
        if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))
            != NULL) {
@@ -928,6 +955,7 @@ libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
                free(ret->mtn_mt.mnt_mntopts);
                free(ret);
        }
+       pthread_mutex_unlock(&hdl->libzfs_mnttab_cache_lock);
 }
 
 int
@@ -994,7 +1022,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                const char *propname = nvpair_name(elem);
 
                prop = zfs_name_to_prop(propname);
-               if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
+               if (prop == ZPROP_USERPROP && zfs_prop_user(propname)) {
                        /*
                         * This is a user property: make sure it's a
                         * string, and that it's less than ZAP_MAXNAMELEN.
@@ -1033,7 +1061,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        goto error;
                }
 
-               if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
+               if (prop == ZPROP_USERPROP && zfs_prop_userquota(propname)) {
                        zfs_userquota_prop_t uqtype;
                        char *newpropname = NULL;
                        char domain[128];
@@ -1115,7 +1143,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        }
                        free(newpropname);
                        continue;
-               } else if (prop == ZPROP_INVAL && zfs_prop_written(propname)) {
+               } else if (prop == ZPROP_USERPROP &&
+                   zfs_prop_written(propname)) {
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "'%s' is readonly"),
                            propname);
@@ -1200,12 +1229,19 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                }
 
                case ZFS_PROP_SPECIAL_SMALL_BLOCKS:
+               {
+                       int maxbs = SPA_OLD_MAXBLOCKSIZE;
+                       char buf[64];
+
                        if (zpool_hdl != NULL) {
                                char state[64] = "";
 
+                               maxbs = zpool_get_prop_int(zpool_hdl,
+                                   ZPOOL_PROP_MAXBLOCKSIZE, NULL);
+
                                /*
                                 * Issue a warning but do not fail so that
-                                * tests for setable properties succeed.
+                                * tests for settable properties succeed.
                                 */
                                if (zpool_prop_get_feature(zpool_hdl,
                                    "feature@allocation_classes", state,
@@ -1218,15 +1254,17 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        }
                        if (intval != 0 &&
                            (intval < SPA_MINBLOCKSIZE ||
-                           intval > SPA_OLD_MAXBLOCKSIZE || !ISP2(intval))) {
+                           intval > maxbs || !ISP2(intval))) {
+                               zfs_nicebytes(maxbs, buf, sizeof (buf));
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "invalid '%s=%d' property: must be zero or "
-                                   "a power of 2 from 512B to 128K"), propname,
-                                   intval);
+                                   "invalid '%s=%llu' property: must be zero "
+                                   "or a power of 2 from 512B to %s"),
+                                   propname, (unsigned long long)intval, buf);
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
                        break;
+               }
 
                case ZFS_PROP_MLSLABEL:
                {
@@ -1268,8 +1306,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        /* Replace the label string with the internal form. */
                        (void) nvlist_remove(ret, zfs_prop_to_name(prop),
                            DATA_TYPE_STRING);
-                       verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
-                           hex) == 0);
+                       fnvlist_add_string(ret, zfs_prop_to_name(prop), hex);
                        free(hex);
 
                        break;
@@ -1321,10 +1358,9 @@ badlabel:
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
+                       zfs_fallthrough;
                }
 
-                       /*FALLTHRU*/
-
                case ZFS_PROP_SHARESMB:
                case ZFS_PROP_SHARENFS:
                        /*
@@ -1382,56 +1418,22 @@ badlabel:
                            prop == ZFS_PROP_SHARESMB) &&
                            strcmp(strval, "on") != 0 &&
                            strcmp(strval, "off") != 0) {
-                               zfs_share_proto_t proto;
+                               enum sa_protocol proto;
 
                                if (prop == ZFS_PROP_SHARESMB)
-                                       proto = PROTO_SMB;
+                                       proto = SA_PROTOCOL_SMB;
                                else
-                                       proto = PROTO_NFS;
+                                       proto = SA_PROTOCOL_NFS;
 
-                               /*
-                                * Must be an valid sharing protocol
-                                * option string so init the libshare
-                                * in order to enable the parser and
-                                * then parse the options. We use the
-                                * control API since we don't care about
-                                * the current configuration and don't
-                                * want the overhead of loading it
-                                * until we actually do something.
-                                */
-
-                               if (zfs_init_libshare(hdl,
-                                   SA_INIT_CONTROL_API) != SA_OK) {
-                                       /*
-                                        * An error occurred so we can't do
-                                        * anything
-                                        */
-                                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                           "'%s' cannot be set: problem "
-                                           "in share initialization"),
-                                           propname);
-                                       (void) zfs_error(hdl, EZFS_BADPROP,
-                                           errbuf);
-                                       goto error;
-                               }
-
-                               if (zfs_parse_options(strval, proto) != SA_OK) {
-                                       /*
-                                        * There was an error in parsing so
-                                        * deal with it by issuing an error
-                                        * message and leaving after
-                                        * uninitializing the the libshare
-                                        * interface.
-                                        */
+                               if (sa_validate_shareopts(strval, proto) !=
+                                   SA_OK) {
                                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                            "'%s' cannot be set to invalid "
                                            "options"), propname);
                                        (void) zfs_error(hdl, EZFS_BADPROP,
                                            errbuf);
-                                       zfs_uninit_libshare(hdl);
                                        goto error;
                                }
-                               zfs_uninit_libshare(hdl);
                        }
 
                        break;
@@ -1556,6 +1558,9 @@ badlabel:
         *
         * If normalization was chosen, but rejecting non-UTF8 names
         * was explicitly not chosen, it is an error.
+        *
+        * If utf8only was turned off, but the parent has normalization,
+        * turn off normalization.
         */
        if (chosen_normal > 0 && chosen_utf < 0) {
                if (nvlist_add_uint64(ret,
@@ -1569,6 +1574,12 @@ badlabel:
                    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                goto error;
+       } else if (chosen_normal < 0 && chosen_utf == 0) {
+               if (nvlist_add_uint64(ret,
+                   zfs_prop_to_name(ZFS_PROP_NORMALIZE), 0) != 0) {
+                       (void) no_memory(hdl);
+                       goto error;
+               }
        }
        return (ret);
 
@@ -1577,7 +1588,7 @@ error:
        return (NULL);
 }
 
-int
+static int
 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 {
        uint64_t old_volsize;
@@ -1586,6 +1597,7 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
        uint64_t new_reservation;
        zfs_prop_t resv_prop;
        nvlist_t *props;
+       zpool_handle_t *zph = zpool_handle(zhp);
 
        /*
         * If this is an existing volume, and someone is setting the volsize,
@@ -1600,7 +1612,7 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
        fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
            zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
 
-       if ((zvol_volsize_to_reservation(old_volsize, props) !=
+       if ((zvol_volsize_to_reservation(zph, old_volsize, props) !=
            old_reservation) || nvlist_exists(nvl,
            zfs_prop_to_name(resv_prop))) {
                fnvlist_free(props);
@@ -1611,7 +1623,7 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
                fnvlist_free(props);
                return (-1);
        }
-       new_reservation = zvol_volsize_to_reservation(new_volsize, props);
+       new_reservation = zvol_volsize_to_reservation(zph, new_volsize, props);
        fnvlist_free(props);
 
        if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
@@ -1624,7 +1636,7 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 
 /*
  * Helper for 'zfs {set|clone} refreservation=auto'.  Must be called after
- * zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinal value.
+ * zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinel value.
  * Return codes must match zfs_add_synthetic_resv().
  */
 static int
@@ -1666,7 +1678,8 @@ zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl)
                volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
        }
 
-       resvsize = zvol_volsize_to_reservation(volsize, props);
+       resvsize = zvol_volsize_to_reservation(zpool_handle(zhp), volsize,
+           props);
        fnvlist_free(props);
 
        (void) nvlist_remove_all(nvl, zfs_prop_to_name(prop));
@@ -1677,114 +1690,6 @@ zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl)
        return (1);
 }
 
-void
-zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
-    char *errbuf)
-{
-       switch (err) {
-
-       case ENOSPC:
-               /*
-                * For quotas and reservations, ENOSPC indicates
-                * something different; setting a quota or reservation
-                * doesn't use any disk space.
-                */
-               switch (prop) {
-               case ZFS_PROP_QUOTA:
-               case ZFS_PROP_REFQUOTA:
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "size is less than current used or "
-                           "reserved space"));
-                       (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
-                       break;
-
-               case ZFS_PROP_RESERVATION:
-               case ZFS_PROP_REFRESERVATION:
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "size is greater than available space"));
-                       (void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
-                       break;
-
-               default:
-                       (void) zfs_standard_error(hdl, err, errbuf);
-                       break;
-               }
-               break;
-
-       case EBUSY:
-               (void) zfs_standard_error(hdl, EBUSY, errbuf);
-               break;
-
-       case EROFS:
-               (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
-               break;
-
-       case E2BIG:
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "property value too long"));
-               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-               break;
-
-       case ENOTSUP:
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "pool and or dataset must be upgraded to set this "
-                   "property or value"));
-               (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
-               break;
-
-       case ERANGE:
-               if (prop == ZFS_PROP_COMPRESSION ||
-                   prop == ZFS_PROP_DNODESIZE ||
-                   prop == ZFS_PROP_RECORDSIZE) {
-                       (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "property setting is not allowed on "
-                           "bootable datasets"));
-                       (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
-               } else if (prop == ZFS_PROP_CHECKSUM ||
-                   prop == ZFS_PROP_DEDUP) {
-                       (void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "property setting is not allowed on "
-                           "root pools"));
-                       (void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
-               } else {
-                       (void) zfs_standard_error(hdl, err, errbuf);
-               }
-               break;
-
-       case EINVAL:
-               if (prop == ZPROP_INVAL) {
-                       (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-               } else {
-                       (void) zfs_standard_error(hdl, err, errbuf);
-               }
-               break;
-
-       case EACCES:
-               if (prop == ZFS_PROP_KEYLOCATION) {
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "keylocation may only be set on encryption roots"));
-                       (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-               } else {
-                       (void) zfs_standard_error(hdl, err, errbuf);
-               }
-               break;
-
-       case EOVERFLOW:
-               /*
-                * This platform can't address a volume this big.
-                */
-#ifdef _ILP32
-               if (prop == ZFS_PROP_VOLSIZE) {
-                       (void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
-                       break;
-               }
-#endif
-               /* FALLTHROUGH */
-       default:
-               (void) zfs_standard_error(hdl, err, errbuf);
-       }
-}
-
 static boolean_t
 zfs_is_namespace_prop(zfs_prop_t prop)
 {
@@ -1812,7 +1717,7 @@ int
 zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
 {
        int ret = -1;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        nvlist_t *nvl = NULL;
 
@@ -1846,7 +1751,7 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
        int ret = -1;
        prop_changelist_t **cls = NULL;
        int cl_idx;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        nvlist_t *nvl;
        int nvl_len = 0;
@@ -1935,9 +1840,8 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
         */
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-       if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 ||
-           (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0)
-               goto error;
+       zcmd_write_src_nvlist(hdl, &zc, nvl);
+       zcmd_alloc_dst_nvlist(hdl, &zc, 0);
 
        ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 
@@ -1973,8 +1877,7 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
                            zfs_prop_to_name(ZFS_PROP_VOLSIZE),
                            old_volsize) != 0)
                                goto error;
-                       if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
-                               goto error;
+                       zcmd_write_src_nvlist(hdl, &zc, nvl);
                        (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
                }
        } else {
@@ -2028,14 +1931,14 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
        int ret;
        prop_changelist_t *cl;
        libzfs_handle_t *hdl = zhp->zfs_hdl;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        zfs_prop_t prop;
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
 
        zc.zc_cookie = received;
-       if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
+       if ((prop = zfs_name_to_prop(propname)) == ZPROP_USERPROP) {
                /*
                 * For user properties, the amount of work we have to do is very
                 * small, so just do it here.
@@ -2052,6 +1955,7 @@ zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
                if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
                        return (zfs_standard_error(hdl, errno, errbuf));
 
+               (void) get_stats(zhp);
                return (0);
        }
 
@@ -2141,7 +2045,7 @@ getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
        *source = NULL;
        if (nvlist_lookup_nvlist(zhp->zfs_props,
            zfs_prop_to_name(prop), &nv) == 0) {
-               verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
+               value = fnvlist_lookup_uint64(nv, ZPROP_VALUE);
                (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
        } else {
                verify(!zhp->zfs_props_table ||
@@ -2282,12 +2186,9 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                libzfs_handle_t *hdl = zhp->zfs_hdl;
                struct mnttab entry;
 
-               if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
+               if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0)
                        zhp->zfs_mntopts = zfs_strdup(hdl,
                            entry.mnt_mntopts);
-                       if (zhp->zfs_mntopts == NULL)
-                               return (-1);
-               }
 
                zhp->zfs_mntcheck = B_TRUE;
        }
@@ -2304,7 +2205,9 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
        case ZFS_PROP_EXEC:
        case ZFS_PROP_READONLY:
        case ZFS_PROP_SETUID:
+#ifndef __FreeBSD__
        case ZFS_PROP_XATTR:
+#endif
        case ZFS_PROP_NBMAND:
                *val = getprop_uint64(zhp, prop, source);
 
@@ -2352,8 +2255,8 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
        case ZFS_PROP_NORMALIZE:
        case ZFS_PROP_UTF8ONLY:
        case ZFS_PROP_CASE:
-               if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
-                       return (-1);
+               zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0);
+
                (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
                if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
                        zcmd_free_nvlists(&zc);
@@ -2376,6 +2279,10 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                *val = zhp->zfs_dmustats.dds_inconsistent;
                break;
 
+       case ZFS_PROP_REDACTED:
+               *val = zhp->zfs_dmustats.dds_redacted;
+               break;
+
        default:
                switch (zfs_prop_get_type(prop)) {
                case PROP_TYPE_NUMBER:
@@ -2450,7 +2357,7 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
 
        prop = zfs_name_to_prop(propname);
 
-       if (prop != ZPROP_INVAL) {
+       if (prop != ZPROP_USERPROP) {
                uint64_t cookie;
                if (!nvlist_exists(zhp->zfs_recvd_props, propname))
                        return (-1);
@@ -2464,8 +2371,7 @@ zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
                if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
                    propname, &propval) != 0)
                        return (-1);
-               verify(nvlist_lookup_string(propval, ZPROP_VALUE,
-                   &recvdval) == 0);
+               recvdval = fnvlist_lookup_string(propval, ZPROP_VALUE);
                (void) strlcpy(propbuf, recvdval, proplen);
        }
 
@@ -2479,7 +2385,7 @@ get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
        nvpair_t *pair;
 
        value = zfs_get_clones_nvl(zhp);
-       if (value == NULL)
+       if (value == NULL || nvlist_empty(value))
                return (-1);
 
        propbuf[0] = '\0';
@@ -2500,7 +2406,7 @@ struct get_clones_arg {
        char buf[ZFS_MAX_DATASET_NAME_LEN];
 };
 
-int
+static int
 get_clones_cb(zfs_handle_t *zhp, void *arg)
 {
        struct get_clones_arg *gca = arg;
@@ -2579,13 +2485,42 @@ zfs_get_clones_nvl(zfs_handle_t *zhp)
                }
                nvlist_free(nv);
                nvlist_free(value);
-               verify(0 == nvlist_lookup_nvlist(zhp->zfs_props,
-                   zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
+               nv = fnvlist_lookup_nvlist(zhp->zfs_props,
+                   zfs_prop_to_name(ZFS_PROP_CLONES));
        }
 
-       verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
+       return (fnvlist_lookup_nvlist(nv, ZPROP_VALUE));
+}
 
-       return (value);
+static int
+get_rsnaps_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
+{
+       nvlist_t *value;
+       uint64_t *snaps;
+       uint_t nsnaps;
+
+       if (nvlist_lookup_nvlist(zhp->zfs_props,
+           zfs_prop_to_name(ZFS_PROP_REDACT_SNAPS), &value) != 0)
+               return (-1);
+       if (nvlist_lookup_uint64_array(value, ZPROP_VALUE, &snaps,
+           &nsnaps) != 0)
+               return (-1);
+       if (nsnaps == 0) {
+               /* There's no redaction snapshots; pass a special value back */
+               (void) snprintf(propbuf, proplen, "none");
+               return (0);
+       }
+       propbuf[0] = '\0';
+       for (int i = 0; i < nsnaps; i++) {
+               char buf[128];
+               if (propbuf[0] != '\0')
+                       (void) strlcat(propbuf, ",", proplen);
+               (void) snprintf(buf, sizeof (buf), "%llu",
+                   (u_longlong_t)snaps[i]);
+               (void) strlcat(propbuf, buf, proplen);
+       }
+
+       return (0);
 }
 
 /*
@@ -2782,6 +2717,11 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                zcp_check(zhp, prop, 0, str);
                break;
 
+       case ZFS_PROP_REDACT_SNAPS:
+               if (get_rsnaps_string(zhp, propbuf, proplen) != 0)
+                       return (-1);
+               break;
+
        case ZFS_PROP_CLONES:
                if (get_clones_string(zhp, propbuf, proplen) != 0)
                        return (-1);
@@ -2824,16 +2764,15 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                        return (-1);
 
                /*
-                * If limit is UINT64_MAX, we translate this into 'none' (unless
-                * literal is set), and indicate that it's the default value.
-                * Otherwise, we print the number nicely and indicate that it's
-                * set locally.
+                * If limit is UINT64_MAX, we translate this into 'none', and
+                * indicate that it's the default value. Otherwise, we print
+                * the number nicely and indicate that it's set locally.
                 */
-               if (literal) {
+               if (val == UINT64_MAX) {
+                       (void) strlcpy(propbuf, "none", proplen);
+               } else if (literal) {
                        (void) snprintf(propbuf, proplen, "%llu",
                            (u_longlong_t)val);
-               } else if (val == UINT64_MAX) {
-                       (void) strlcpy(propbuf, "none", proplen);
                } else {
                        zfs_nicenum(val, propbuf, proplen);
                }
@@ -2947,11 +2886,16 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                break;
 
        case ZFS_PROP_GUID:
+       case ZFS_PROP_KEY_GUID:
+       case ZFS_PROP_IVSET_GUID:
        case ZFS_PROP_CREATETXG:
+       case ZFS_PROP_OBJSETID:
+       case ZFS_PROP_PBKDF2_ITERS:
                /*
-                * GUIDs are stored as numbers, but they are identifiers.
+                * These properties are stored as numbers, but they are
+                * identifiers or counters.
                 * We don't want them to be pretty printed, because pretty
-                * printing mangles the ID into a truncated and useless value.
+                * printing truncates their values making them useless.
                 */
                if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
                        return (-1);
@@ -3040,7 +2984,7 @@ zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
        return (val);
 }
 
-int
+static int
 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
 {
        char buf[64];
@@ -3201,6 +3145,7 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
                if (errno != 0 || *end != '\0')
                        return (EINVAL);
 #else
+               (void) domainlen;
                return (ENOSYS);
 #endif /* HAVE_IDMAP */
        } else {
@@ -3248,7 +3193,7 @@ zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
        if (err)
                return (err);
 
-       err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
+       err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_USERSPACE_ONE, &zc);
        if (err)
                return (err);
 
@@ -3299,6 +3244,9 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
        return (0);
 }
 
+/*
+ * propname must start with "written@" or "written#".
+ */
 int
 zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue)
@@ -3309,8 +3257,10 @@ zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
 
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-       snapname = strchr(propname, '@') + 1;
-       if (strchr(snapname, '@')) {
+       assert(zfs_prop_written(propname));
+       snapname = propname + strlen("written@");
+       if (strchr(snapname, '@') != NULL || strchr(snapname, '#') != NULL) {
+               /* full snapshot or bookmark name specified */
                (void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
        } else {
                /* snapname is the short name, append it to zhp's fsname */
@@ -3321,11 +3271,10 @@ zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
                cp = strchr(zc.zc_value, '@');
                if (cp != NULL)
                        *cp = '\0';
-               (void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
-               (void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
+               (void) strlcat(zc.zc_value, snapname - 1, sizeof (zc.zc_value));
        }
 
-       err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
+       err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SPACE_WRITTEN, &zc);
        if (err)
                return (err);
 
@@ -3382,6 +3331,16 @@ zfs_get_type(const zfs_handle_t *zhp)
        return (zhp->zfs_type);
 }
 
+/*
+ * Returns the type of the given zfs handle,
+ * or, if a snapshot, the type of the snapshotted dataset.
+ */
+zfs_type_t
+zfs_get_underlying_type(const zfs_handle_t *zhp)
+{
+       return (zhp->zfs_head_type);
+}
+
 /*
  * Is one dataset name a child dataset of another?
  *
@@ -3444,7 +3403,7 @@ check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
        char parent[ZFS_MAX_DATASET_NAME_LEN];
        char *slash;
        zfs_handle_t *zhp;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        uint64_t is_zoned;
 
        (void) snprintf(errbuf, sizeof (errbuf),
@@ -3462,7 +3421,7 @@ check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
                slash = parent + strlen(parent);
        (void) strncpy(zc.zc_name, parent, slash - parent);
        zc.zc_name[slash - parent] = '\0';
-       if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
+       if (zfs_ioctl(hdl, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
            errno == ENOENT) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "no such pool '%s'"), zc.zc_name);
@@ -3597,13 +3556,14 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
                        goto ancestorerr;
                }
 
-               if (zfs_share(h) != 0) {
+               if (zfs_share(h, NULL) != 0) {
                        opname = dgettext(TEXT_DOMAIN, "share");
                        goto ancestorerr;
                }
 
                zfs_close(h);
        }
+       zfs_commit_shares(NULL);
 
        return (0);
 
@@ -3621,7 +3581,7 @@ zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
 {
        int prefix;
        char *path_copy;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        int rc = 0;
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
@@ -3665,7 +3625,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
        zpool_handle_t *zpool_handle;
        uint8_t *wkeydata = NULL;
        uint_t wkeylen = 0;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        char parent[ZFS_MAX_DATASET_NAME_LEN];
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
@@ -3789,11 +3749,6 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
                            "no such parent '%s'"), parent);
                        return (zfs_error(hdl, EZFS_NOENT, errbuf));
 
-               case EINVAL:
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "parent '%s' is not a filesystem"), parent);
-                       return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
-
                case ENOTSUP:
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "pool must be upgraded to set this "
@@ -3818,8 +3773,8 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
                        if (type == ZFS_TYPE_VOLUME)
                                return (zfs_error(hdl, EZFS_VOLTOOBIG,
                                    errbuf));
+                       zfs_fallthrough;
 #endif
-                       /* FALLTHROUGH */
                default:
                        return (zfs_standard_error(hdl, errno, errbuf));
                }
@@ -3836,32 +3791,34 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
 int
 zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
 {
-       zfs_cmd_t zc = {"\0"};
+       int error;
+
+       if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT && defer)
+               return (EINVAL);
 
        if (zhp->zfs_type == ZFS_TYPE_BOOKMARK) {
                nvlist_t *nv = fnvlist_alloc();
                fnvlist_add_boolean(nv, zhp->zfs_name);
-               int error = lzc_destroy_bookmarks(nv, NULL);
+               error = lzc_destroy_bookmarks(nv, NULL);
                fnvlist_free(nv);
                if (error != 0) {
-                       return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
+                       return (zfs_standard_error_fmt(zhp->zfs_hdl, error,
                            dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
                            zhp->zfs_name));
                }
                return (0);
        }
 
-       (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-
-       if (ZFS_IS_VOLUME(zhp)) {
-               zc.zc_objset_type = DMU_OST_ZVOL;
+       if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
+               nvlist_t *nv = fnvlist_alloc();
+               fnvlist_add_boolean(nv, zhp->zfs_name);
+               error = lzc_destroy_snaps(nv, defer, NULL);
+               fnvlist_free(nv);
        } else {
-               zc.zc_objset_type = DMU_OST_ZFS;
+               error = lzc_destroy(zhp->zfs_name);
        }
 
-       zc.zc_defer_destroy = defer;
-       if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0 &&
-           errno != ENOENT) {
+       if (error != 0 && error != ENOENT) {
                return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
                    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
                    zhp->zfs_name));
@@ -3889,7 +3846,7 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
                return (EINVAL);
 
        if (lzc_exists(name))
-               verify(nvlist_add_boolean(dd->nvl, name) == 0);
+               fnvlist_add_boolean(dd->nvl, name);
 
        rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
        zfs_close(zhp);
@@ -3906,7 +3863,7 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
        struct destroydata dd = { 0 };
 
        dd.snapname = snapname;
-       verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) == 0);
+       dd.nvl = fnvlist_alloc();
        (void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
 
        if (nvlist_empty(dd.nvl)) {
@@ -3916,7 +3873,7 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
        } else {
                ret = zfs_destroy_snaps_nvl(zhp->zfs_hdl, dd.nvl, defer);
        }
-       nvlist_free(dd.nvl);
+       fnvlist_free(dd.nvl);
        return (ret);
 }
 
@@ -3926,10 +3883,13 @@ zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
 int
 zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
 {
-       int ret;
        nvlist_t *errlist = NULL;
        nvpair_t *pair;
 
+       int ret = zfs_destroy_snaps_nvl_os(hdl, snaps);
+       if (ret != 0)
+               return (ret);
+
        ret = lzc_destroy_snaps(snaps, defer, &errlist);
 
        if (ret == 0) {
@@ -3938,7 +3898,7 @@ zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
        }
 
        if (nvlist_empty(errlist)) {
-               char errbuf[1024];
+               char errbuf[ERRBUFLEN];
                (void) snprintf(errbuf, sizeof (errbuf),
                    dgettext(TEXT_DOMAIN, "cannot destroy snapshots"));
 
@@ -3946,7 +3906,7 @@ zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
        }
        for (pair = nvlist_next_nvpair(errlist, NULL);
            pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
-               char errbuf[1024];
+               char errbuf[ERRBUFLEN];
                (void) snprintf(errbuf, sizeof (errbuf),
                    dgettext(TEXT_DOMAIN, "cannot destroy snapshot %s"),
                    nvpair_name(pair));
@@ -3975,7 +3935,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
 {
        char parent[ZFS_MAX_DATASET_NAME_LEN];
        int ret;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        uint64_t zoned;
 
@@ -3997,13 +3957,10 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
        /* do the clone */
 
        if (props) {
-               zfs_type_t type;
+               zfs_type_t type = ZFS_TYPE_FILESYSTEM;
 
-               if (ZFS_IS_VOLUME(zhp)) {
+               if (ZFS_IS_VOLUME(zhp))
                        type = ZFS_TYPE_VOLUME;
-               } else {
-                       type = ZFS_TYPE_FILESYSTEM;
-               }
                if ((props = zfs_valid_proplist(hdl, type, props, zoned,
                    zhp, zhp->zpool_hdl, B_TRUE, errbuf)) == NULL)
                        return (-1);
@@ -4062,7 +4019,7 @@ zfs_promote(zfs_handle_t *zhp)
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        char snapname[ZFS_MAX_DATASET_NAME_LEN];
        int ret;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot promote '%s'"), zhp->zfs_name);
@@ -4086,6 +4043,16 @@ zfs_promote(zfs_handle_t *zhp)
 
        if (ret != 0) {
                switch (ret) {
+               case EACCES:
+                       /*
+                        * Promoting encrypted dataset outside its
+                        * encryption root.
+                        */
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "cannot promote dataset outside its "
+                           "encryption root"));
+                       return (zfs_error(hdl, EZFS_EXISTS, errbuf));
+
                case EEXIST:
                        /* There is a conflicting snapshot name. */
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
@@ -4126,36 +4093,6 @@ zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
        return (rv);
 }
 
-int
-zfs_remap_indirects(libzfs_handle_t *hdl, const char *fs)
-{
-       int err;
-       char errbuf[1024];
-
-       (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
-           "cannot remap dataset '%s'"), fs);
-
-       err = lzc_remap(fs);
-
-       if (err != 0) {
-               switch (err) {
-               case ENOTSUP:
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "pool must be upgraded"));
-                       (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
-                       break;
-               case EINVAL:
-                       (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
-                       break;
-               default:
-                       (void) zfs_standard_error(hdl, err, errbuf);
-                       break;
-               }
-       }
-
-       return (err);
-}
-
 /*
  * Creates snapshots.  The keys in the snaps nvlist are the snapshots to be
  * created.
@@ -4164,7 +4101,7 @@ int
 zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
 {
        int ret;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        nvpair_t *elem;
        nvlist_t *errors;
        zpool_handle_t *zpool_hdl;
@@ -4249,7 +4186,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
        char fsname[ZFS_MAX_DATASET_NAME_LEN];
        char *cp;
        zfs_handle_t *zhp;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot snapshot %s"), path);
@@ -4267,7 +4204,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
                return (-1);
        }
 
-       verify(nvlist_alloc(&sd.sd_nvl, NV_UNIQUE_NAME, 0) == 0);
+       sd.sd_nvl = fnvlist_alloc();
        if (recursive) {
                (void) zfs_snapshot_cb(zfs_handle_dup(zhp), &sd);
        } else {
@@ -4275,7 +4212,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
        }
 
        ret = zfs_snapshot_nvl(hdl, sd.sd_nvl, props);
-       nvlist_free(sd.sd_nvl);
+       fnvlist_free(sd.sd_nvl);
        zfs_close(zhp);
        return (ret);
 }
@@ -4349,6 +4286,7 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
        boolean_t restore_resv = 0;
        uint64_t old_volsize = 0, new_volsize;
        zfs_prop_t resv_prop = { 0 };
+       uint64_t min_txg = 0;
 
        assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
            zhp->zfs_type == ZFS_TYPE_VOLUME);
@@ -4359,7 +4297,13 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
        cb.cb_force = force;
        cb.cb_target = snap->zfs_name;
        cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
-       (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb);
+
+       if (cb.cb_create > 0)
+               min_txg = cb.cb_create;
+
+       (void) zfs_iter_snapshots(zhp, B_FALSE, rollback_destroy, &cb,
+           min_txg, 0);
+
        (void) zfs_iter_bookmarks(zhp, rollback_destroy, &cb);
 
        if (cb.cb_error)
@@ -4385,7 +4329,7 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
         */
        err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);
        if (err != 0) {
-               char errbuf[1024];
+               char errbuf[ERRBUFLEN];
 
                (void) snprintf(errbuf, sizeof (errbuf),
                    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
@@ -4435,18 +4379,16 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
  * Renames the given dataset.
  */
 int
-zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
-    boolean_t force_unmount)
+zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
 {
        int ret = 0;
        zfs_cmd_t zc = {"\0"};
        char *delim;
        prop_changelist_t *cl = NULL;
-       zfs_handle_t *zhrp = NULL;
-       char *parentname = NULL;
        char parent[ZFS_MAX_DATASET_NAME_LEN];
+       char property[ZFS_MAXPROPLEN];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
 
        /* if we have the same exact name, just return success */
        if (strcmp(zhp->zfs_name, target) == 0)
@@ -4496,7 +4438,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
                        return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
        } else {
-               if (recursive) {
+               if (flags.recursive) {
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "recursive rename must be a snapshot"));
                        return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
@@ -4537,23 +4479,35 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                return (zfs_error(hdl, EZFS_ZONED, errbuf));
        }
 
-       if (recursive) {
-               parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
-               if (parentname == NULL) {
-                       ret = -1;
-                       goto error;
-               }
+       /*
+        * Avoid unmounting file systems with mountpoint property set to
+        * 'legacy' or 'none' even if -u option is not given.
+        */
+       if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
+           !flags.recursive && !flags.nounmount &&
+           zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
+           sizeof (property), NULL, NULL, 0, B_FALSE) == 0 &&
+           (strcmp(property, "legacy") == 0 ||
+           strcmp(property, "none") == 0)) {
+               flags.nounmount = B_TRUE;
+       }
+       if (flags.recursive) {
+               char *parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
                delim = strchr(parentname, '@');
                *delim = '\0';
-               zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
+               zfs_handle_t *zhrp = zfs_open(zhp->zfs_hdl, parentname,
+                   ZFS_TYPE_DATASET);
+               free(parentname);
                if (zhrp == NULL) {
                        ret = -1;
                        goto error;
                }
+               zfs_close(zhrp);
        } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
                if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
+                   flags.nounmount ? CL_GATHER_DONT_UNMOUNT :
                    CL_GATHER_ITER_MOUNTED,
-                   force_unmount ? MS_FORCE : 0)) == NULL)
+                   flags.forceunmount ? MS_FORCE : 0)) == NULL)
                        return (-1);
 
                if (changelist_haszonedchild(cl)) {
@@ -4577,7 +4531,8 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
        (void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
 
-       zc.zc_cookie = recursive;
+       zc.zc_cookie = !!flags.recursive;
+       zc.zc_cookie |= (!!flags.nounmount) << 1;
 
        if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
                /*
@@ -4587,22 +4542,15 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
                    "cannot rename '%s'"), zc.zc_name);
 
-               if (recursive && errno == EEXIST) {
+               if (flags.recursive && errno == EEXIST) {
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "a child dataset already has a snapshot "
                            "with the new name"));
                        (void) zfs_error(hdl, EZFS_EXISTS, errbuf);
                } else if (errno == EACCES) {
-                       if (zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION) ==
-                           ZIO_CRYPT_OFF) {
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "cannot rename an unencrypted dataset to "
-                                   "be a decendent of an encrypted one"));
-                       } else {
-                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "cannot move encryption child outside of "
-                                   "its encryption root"));
-                       }
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "cannot move encrypted child outside of "
+                           "its encryption root"));
                        (void) zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf);
                } else {
                        (void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
@@ -4622,12 +4570,6 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
        }
 
 error:
-       if (parentname != NULL) {
-               free(parentname);
-       }
-       if (zhrp != NULL) {
-               zfs_close(zhrp);
-       }
        if (cl != NULL) {
                changelist_free(cl);
        }
@@ -4694,7 +4636,7 @@ zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
                 */
                start = plp;
                while (*start != NULL) {
-                       if ((*start)->pl_prop == ZPROP_INVAL)
+                       if ((*start)->pl_prop == ZPROP_USERPROP)
                                break;
                        start = &(*start)->pl_next;
                }
@@ -4712,15 +4654,10 @@ zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
                        }
 
                        if (*last == NULL) {
-                               if ((entry = zfs_alloc(hdl,
-                                   sizeof (zprop_list_t))) == NULL ||
-                                   ((entry->pl_user_prop = zfs_strdup(hdl,
-                                   nvpair_name(elem)))) == NULL) {
-                                       free(entry);
-                                       return (-1);
-                               }
-
-                               entry->pl_prop = ZPROP_INVAL;
+                               entry = zfs_alloc(hdl, sizeof (zprop_list_t));
+                               entry->pl_user_prop =
+                                   zfs_strdup(hdl, nvpair_name(elem));
+                               entry->pl_prop = ZPROP_USERPROP;
                                entry->pl_width = strlen(nvpair_name(elem));
                                entry->pl_all = B_TRUE;
                                *last = entry;
@@ -4735,7 +4672,7 @@ zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
                if (entry->pl_fixed && !literal)
                        continue;
 
-               if (entry->pl_prop != ZPROP_INVAL) {
+               if (entry->pl_prop != ZPROP_USERPROP) {
                        if (zfs_prop_get(zhp, entry->pl_prop,
                            buf, sizeof (buf), NULL, NULL, 0, literal) == 0) {
                                if (strlen(buf) > entry->pl_width)
@@ -4749,8 +4686,8 @@ zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received,
                } else {
                        if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
                            &propval) == 0) {
-                               verify(nvlist_lookup_string(propval,
-                                   ZPROP_VALUE, &strval) == 0);
+                               strval = fnvlist_lookup_string(propval,
+                                   ZPROP_VALUE);
                                if (strlen(strval) > entry->pl_width)
                                        entry->pl_width = strlen(strval);
                        }
@@ -4784,13 +4721,14 @@ zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
                next = nvlist_next_nvpair(zhp->zfs_props, curr);
 
                /*
-                * User properties will result in ZPROP_INVAL, and since we
+                * User properties will result in ZPROP_USERPROP (an alias
+                * for ZPROP_INVAL), and since we
                 * only know how to prune standard ZFS properties, we always
                 * leave these in the list.  This can also happen if we
                 * encounter an unknown DSL property (when running older
                 * software, for example).
                 */
-               if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
+               if (zfs_prop != ZPROP_USERPROP && props[zfs_prop] == B_FALSE)
                        (void) nvlist_remove(zhp->zfs_props,
                            nvpair_name(curr), nvpair_type(curr));
                curr = next;
@@ -4832,10 +4770,7 @@ zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
                                (void) no_memory(hdl);
                                return (-1);
                }
-               if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
-                       nvlist_free(nvlist);
-                       return (-1);
-               }
+               zcmd_write_src_nvlist(hdl, &zc, nvlist);
                break;
        case ZFS_SMB_ACL_PURGE:
                break;
@@ -4897,8 +4832,6 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
 
                zc.zc_nvlist_dst_size = sizeof (buf);
                if (zfs_ioctl(hdl, ZFS_IOC_USERSPACE_MANY, &zc) != 0) {
-                       char errbuf[1024];
-
                        if ((errno == ENOTSUP &&
                            (type == ZFS_PROP_USEROBJUSED ||
                            type == ZFS_PROP_GROUPOBJUSED ||
@@ -4910,10 +4843,9 @@ zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
                            type == ZFS_PROP_PROJECTQUOTA)))
                                break;
 
-                       (void) snprintf(errbuf, sizeof (errbuf),
+                       return (zfs_standard_error_fmt(hdl, errno,
                            dgettext(TEXT_DOMAIN,
-                           "cannot get used/quota for %s"), zc.zc_name);
-                       return (zfs_standard_error_fmt(hdl, errno, errbuf));
+                           "cannot get used/quota for %s"), zc.zc_name));
                }
                if (zc.zc_nvlist_dst_size == 0)
                        break;
@@ -4972,7 +4904,7 @@ zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
        (void) zfs_hold_one(zfs_handle_dup(zhp), &ha);
 
        if (nvlist_empty(ha.nvl)) {
-               char errbuf[1024];
+               char errbuf[ERRBUFLEN];
 
                fnvlist_free(ha.nvl);
                ret = ENOENT;
@@ -4996,7 +4928,7 @@ zfs_hold_nvl(zfs_handle_t *zhp, int cleanup_fd, nvlist_t *holds)
        int ret;
        nvlist_t *errors;
        libzfs_handle_t *hdl = zhp->zfs_hdl;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        nvpair_t *elem;
 
        errors = NULL;
@@ -5098,7 +5030,7 @@ zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
        nvlist_t *errors = NULL;
        nvpair_t *elem;
        libzfs_handle_t *hdl = zhp->zfs_hdl;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
 
        ha.nvl = fnvlist_alloc();
        ha.snapname = snapname;
@@ -5142,7 +5074,7 @@ zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
                        (void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
                        break;
                default:
-                       (void) zfs_standard_error_fmt(hdl, errno, errbuf);
+                       (void) zfs_standard_error(hdl, errno, errbuf);
                }
        }
 
@@ -5161,7 +5093,7 @@ zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
                        (void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
                        break;
                default:
-                       (void) zfs_standard_error_fmt(hdl,
+                       (void) zfs_standard_error(hdl,
                            fnvpair_value_int32(elem), errbuf);
                }
        }
@@ -5178,7 +5110,7 @@ zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
        int nvsz = 2048;
        void *nvbuf;
        int err = 0;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
 
        assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
            zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
@@ -5196,7 +5128,7 @@ tryagain:
 
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-       if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
+       if (zfs_ioctl(hdl, ZFS_IOC_GET_FSACL, &zc) != 0) {
                (void) snprintf(errbuf, sizeof (errbuf),
                    dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
                    zc.zc_name);
@@ -5218,17 +5150,16 @@ tryagain:
                        err = zfs_error(hdl, EZFS_NOENT, errbuf);
                        break;
                default:
-                       err = zfs_standard_error_fmt(hdl, errno, errbuf);
+                       err = zfs_standard_error(hdl, errno, errbuf);
                        break;
                }
        } else {
                /* success */
                int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
                if (rc) {
-                       (void) snprintf(errbuf, sizeof (errbuf), dgettext(
+                       err = zfs_standard_error_fmt(hdl, rc, dgettext(
                            TEXT_DOMAIN, "cannot get permissions on '%s'"),
                            zc.zc_name);
-                       err = zfs_standard_error_fmt(hdl, rc, errbuf);
                }
        }
 
@@ -5243,7 +5174,7 @@ zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
        zfs_cmd_t zc = {"\0"};
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        char *nvbuf;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
        size_t nvsz;
        int err;
 
@@ -5281,7 +5212,7 @@ zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
                        err = zfs_error(hdl, EZFS_NOENT, errbuf);
                        break;
                default:
-                       err = zfs_standard_error_fmt(hdl, errno, errbuf);
+                       err = zfs_standard_error(hdl, errno, errbuf);
                        break;
                }
        }
@@ -5295,7 +5226,7 @@ int
 zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
 {
        int err;
-       char errbuf[1024];
+       char errbuf[ERRBUFLEN];
 
        err = lzc_get_holds(zhp->zfs_name, nvl);
 
@@ -5318,7 +5249,7 @@ zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
                        err = zfs_error(hdl, EZFS_NOENT, errbuf);
                        break;
                default:
-                       err = zfs_standard_error_fmt(hdl, errno, errbuf);
+                       err = zfs_standard_error(hdl, errno, errbuf);
                        break;
                }
        }
@@ -5327,12 +5258,231 @@ zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
 }
 
 /*
- * Convert the zvol's volume size to an appropriate reservation.
+ * The theory of raidz space accounting
+ *
+ * The "referenced" property of RAIDZ vdevs is scaled such that a 128KB block
+ * will "reference" 128KB, even though it allocates more than that, to store the
+ * parity information (and perhaps skip sectors). This concept of the
+ * "referenced" (and other DMU space accounting) being lower than the allocated
+ * space by a constant factor is called "raidz deflation."
+ *
+ * As mentioned above, the constant factor for raidz deflation assumes a 128KB
+ * block size. However, zvols typically have a much smaller block size (default
+ * 8KB). These smaller blocks may require proportionally much more parity
+ * information (and perhaps skip sectors). In this case, the change to the
+ * "referenced" property may be much more than the logical block size.
+ *
+ * Suppose a raidz vdev has 5 disks with ashift=12.  A 128k block may be written
+ * as follows.
+ *
+ * +-------+-------+-------+-------+-------+
+ * | disk1 | disk2 | disk3 | disk4 | disk5 |
+ * +-------+-------+-------+-------+-------+
+ * |  P0   |  D0   |  D8   |  D16  |  D24  |
+ * |  P1   |  D1   |  D9   |  D17  |  D25  |
+ * |  P2   |  D2   |  D10  |  D18  |  D26  |
+ * |  P3   |  D3   |  D11  |  D19  |  D27  |
+ * |  P4   |  D4   |  D12  |  D20  |  D28  |
+ * |  P5   |  D5   |  D13  |  D21  |  D29  |
+ * |  P6   |  D6   |  D14  |  D22  |  D30  |
+ * |  P7   |  D7   |  D15  |  D23  |  D31  |
+ * +-------+-------+-------+-------+-------+
+ *
+ * Above, notice that 160k was allocated: 8 x 4k parity sectors + 32 x 4k data
+ * sectors.  The dataset's referenced will increase by 128k and the pool's
+ * allocated and free properties will be adjusted by 160k.
+ *
+ * A 4k block written to the same raidz vdev will require two 4k sectors.  The
+ * blank cells represent unallocated space.
+ *
+ * +-------+-------+-------+-------+-------+
+ * | disk1 | disk2 | disk3 | disk4 | disk5 |
+ * +-------+-------+-------+-------+-------+
+ * |  P0   |  D0   |       |       |       |
+ * +-------+-------+-------+-------+-------+
+ *
+ * Above, notice that the 4k block required one sector for parity and another
+ * for data.  vdev_raidz_asize() will return 8k and as such the pool's allocated
+ * and free properties will be adjusted by 8k.  The dataset will not be charged
+ * 8k.  Rather, it will be charged a value that is scaled according to the
+ * overhead of the 128k block on the same vdev.  This 8k allocation will be
+ * charged 8k * 128k / 160k.  128k is from SPA_OLD_MAXBLOCKSIZE and 160k is as
+ * calculated in the 128k block example above.
+ *
+ * Every raidz allocation is sized to be a multiple of nparity+1 sectors.  That
+ * is, every raidz1 allocation will be a multiple of 2 sectors, raidz2
+ * allocations are a multiple of 3 sectors, and raidz3 allocations are a
+ * multiple of of 4 sectors.  When a block does not fill the required number of
+ * sectors, skip blocks (sectors) are used.
+ *
+ * An 8k block being written to a raidz vdev may be written as follows:
+ *
+ * +-------+-------+-------+-------+-------+
+ * | disk1 | disk2 | disk3 | disk4 | disk5 |
+ * +-------+-------+-------+-------+-------+
+ * |  P0   |  D0   |  D1   |  S0   |       |
+ * +-------+-------+-------+-------+-------+
+ *
+ * In order to maintain the nparity+1 allocation size, a skip block (S0) was
+ * added.  For this 8k block, the pool's allocated and free properties are
+ * adjusted by 16k and the dataset's referenced is increased by 16k * 128k /
+ * 160k.  Again, 128k is from SPA_OLD_MAXBLOCKSIZE and 160k is as calculated in
+ * the 128k block example above.
+ *
+ * The situation is slightly different for dRAID since the minimum allocation
+ * size is the full group width.  The same 8K block above would be written as
+ * follows in a dRAID group:
+ *
+ * +-------+-------+-------+-------+-------+
+ * | disk1 | disk2 | disk3 | disk4 | disk5 |
+ * +-------+-------+-------+-------+-------+
+ * |  P0   |  D0   |  D1   |  S0   |  S1   |
+ * +-------+-------+-------+-------+-------+
+ *
+ * Compression may lead to a variety of block sizes being written for the same
+ * volume or file.  There is no clear way to reserve just the amount of space
+ * that will be required, so the worst case (no compression) is assumed.
+ * Note that metadata blocks will typically be compressed, so the reservation
+ * size returned by zvol_volsize_to_reservation() will generally be slightly
+ * larger than the maximum that the volume can reference.
+ */
+
+/*
+ * Derived from function of same name in module/zfs/vdev_raidz.c.  Returns the
+ * amount of space (in bytes) that will be allocated for the specified block
+ * size. Note that the "referenced" space accounted will be less than this, but
+ * not necessarily equal to "blksize", due to RAIDZ deflation.
+ */
+static uint64_t
+vdev_raidz_asize(uint64_t ndisks, uint64_t nparity, uint64_t ashift,
+    uint64_t blksize)
+{
+       uint64_t asize, ndata;
+
+       ASSERT3U(ndisks, >, nparity);
+       ndata = ndisks - nparity;
+       asize = ((blksize - 1) >> ashift) + 1;
+       asize += nparity * ((asize + ndata - 1) / ndata);
+       asize = roundup(asize, nparity + 1) << ashift;
+
+       return (asize);
+}
+
+/*
+ * Derived from function of same name in module/zfs/vdev_draid.c.  Returns the
+ * amount of space (in bytes) that will be allocated for the specified block
+ * size.
+ */
+static uint64_t
+vdev_draid_asize(uint64_t ndisks, uint64_t nparity, uint64_t ashift,
+    uint64_t blksize)
+{
+       ASSERT3U(ndisks, >, nparity);
+       uint64_t ndata = ndisks - nparity;
+       uint64_t rows = ((blksize - 1) / (ndata << ashift)) + 1;
+       uint64_t asize = (rows * ndisks) << ashift;
+
+       return (asize);
+}
+
+/*
+ * Determine how much space will be allocated if it lands on the most space-
+ * inefficient top-level vdev.  Returns the size in bytes required to store one
+ * copy of the volume data.  See theory comment above.
+ */
+static uint64_t
+volsize_from_vdevs(zpool_handle_t *zhp, uint64_t nblocks, uint64_t blksize)
+{
+       nvlist_t *config, *tree, **vdevs;
+       uint_t nvdevs;
+       uint64_t ret = 0;
+
+       config = zpool_get_config(zhp, NULL);
+       if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &tree) != 0 ||
+           nvlist_lookup_nvlist_array(tree, ZPOOL_CONFIG_CHILDREN,
+           &vdevs, &nvdevs) != 0) {
+               return (nblocks * blksize);
+       }
+
+       for (int v = 0; v < nvdevs; v++) {
+               char *type;
+               uint64_t nparity, ashift, asize, tsize;
+               uint64_t volsize;
+
+               if (nvlist_lookup_string(vdevs[v], ZPOOL_CONFIG_TYPE,
+                   &type) != 0)
+                       continue;
+
+               if (strcmp(type, VDEV_TYPE_RAIDZ) != 0 &&
+                   strcmp(type, VDEV_TYPE_DRAID) != 0)
+                       continue;
+
+               if (nvlist_lookup_uint64(vdevs[v],
+                   ZPOOL_CONFIG_NPARITY, &nparity) != 0)
+                       continue;
+
+               if (nvlist_lookup_uint64(vdevs[v],
+                   ZPOOL_CONFIG_ASHIFT, &ashift) != 0)
+                       continue;
+
+               if (strcmp(type, VDEV_TYPE_RAIDZ) == 0) {
+                       nvlist_t **disks;
+                       uint_t ndisks;
+
+                       if (nvlist_lookup_nvlist_array(vdevs[v],
+                           ZPOOL_CONFIG_CHILDREN, &disks, &ndisks) != 0)
+                               continue;
+
+                       /* allocation size for the "typical" 128k block */
+                       tsize = vdev_raidz_asize(ndisks, nparity, ashift,
+                           SPA_OLD_MAXBLOCKSIZE);
+
+                       /* allocation size for the blksize block */
+                       asize = vdev_raidz_asize(ndisks, nparity, ashift,
+                           blksize);
+               } else {
+                       uint64_t ndata;
+
+                       if (nvlist_lookup_uint64(vdevs[v],
+                           ZPOOL_CONFIG_DRAID_NDATA, &ndata) != 0)
+                               continue;
+
+                       /* allocation size for the "typical" 128k block */
+                       tsize = vdev_draid_asize(ndata + nparity, nparity,
+                           ashift, SPA_OLD_MAXBLOCKSIZE);
+
+                       /* allocation size for the blksize block */
+                       asize = vdev_draid_asize(ndata + nparity, nparity,
+                           ashift, blksize);
+               }
+
+               /*
+                * Scale this size down as a ratio of 128k / tsize.
+                * See theory statement above.
+                */
+               volsize = nblocks * asize * SPA_OLD_MAXBLOCKSIZE / tsize;
+               if (volsize > ret) {
+                       ret = volsize;
+               }
+       }
+
+       if (ret == 0) {
+               ret = nblocks * blksize;
+       }
+
+       return (ret);
+}
+
+/*
+ * Convert the zvol's volume size to an appropriate reservation.  See theory
+ * comment above.
+ *
  * Note: If this routine is updated, it is necessary to update the ZFS test
- * suite's shell version in reservation.kshlib.
+ * suite's shell version in reservation.shlib.
  */
 uint64_t
-zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
+zvol_volsize_to_reservation(zpool_handle_t *zph, uint64_t volsize,
+    nvlist_t *props)
 {
        uint64_t numdb;
        uint64_t nblocks, volblocksize;
@@ -5348,7 +5498,14 @@ zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
            zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
            &volblocksize) != 0)
                volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
-       nblocks = volsize/volblocksize;
+
+       nblocks = volsize / volblocksize;
+       /*
+        * Metadata defaults to using 128k blocks, not volblocksize blocks.  For
+        * this reason, only the data blocks are scaled based on vdev config.
+        */
+       volsize = volsize_from_vdevs(zph, nblocks, volblocksize);
+
        /* start with metadnode L0-L6 */
        numdb = 7;
        /* calculate number of indirects */
@@ -5368,3 +5525,31 @@ zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
        volsize += numdb;
        return (volsize);
 }
+
+/*
+ * Wait for the given activity and return the status of the wait (whether or not
+ * any waiting was done) in the 'waited' parameter. Non-existent fses are
+ * reported via the 'missing' parameter, rather than by printing an error
+ * message. This is convenient when this function is called in a loop over a
+ * long period of time (as it is, for example, by zfs's wait cmd). In that
+ * scenario, a fs being exported or destroyed should be considered a normal
+ * event, so we don't want to print an error when we find that the fs doesn't
+ * exist.
+ */
+int
+zfs_wait_status(zfs_handle_t *zhp, zfs_wait_activity_t activity,
+    boolean_t *missing, boolean_t *waited)
+{
+       int error = lzc_wait_fs(zhp->zfs_name, activity, waited);
+       *missing = (error == ENOENT);
+       if (*missing)
+               return (0);
+
+       if (error != 0) {
+               (void) zfs_standard_error_fmt(zhp->zfs_hdl, error,
+                   dgettext(TEXT_DOMAIN, "error waiting in fs '%s'"),
+                   zhp->zfs_name);
+       }
+
+       return (error);
+}