]> 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 90c6b68e387c3002f7378a8da808a1ae5bb03109..24d9b81f4976cfd7cd357ebec69934f9c95dd2c4 100644 (file)
@@ -22,7 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright 2019 Joyent, Inc.
- * Copyright (c) 2011, 2018 by Delphix. All rights reserved.
+ * 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.
@@ -32,6 +32,7 @@
  * Copyright 2017-2018 RackTop Systems.
  * Copyright (c) 2019 Datto Inc.
  * Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
+ * Copyright (c) 2021 Matt Fiddaman
  */
 
 #include <ctype.h>
@@ -48,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>
@@ -66,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,
@@ -235,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) {
@@ -248,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);
@@ -333,13 +330,10 @@ get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
        (void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
 
        while (zfs_ioctl(hdl, ZFS_IOC_OBJSET_STATS, zc) != 0) {
-               if (errno == ENOMEM) {
-                       if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
-                               return (-1);
-                       }
-               } else {
+               if (errno == ENOMEM)
+                       zcmd_expand_dst_nvlist(hdl, zc);
+               else
                        return (-1);
-               }
        }
        return (0);
 }
@@ -355,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 (zfs_ioctl(hdl, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
-               if (errno == ENOMEM) {
-                       if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
-                               return (-1);
-                       }
-               } else {
+               if (errno == ENOMEM)
+                       zcmd_expand_dst_nvlist(hdl, &zc);
+               else {
                        zcmd_free_nvlists(&zc);
                        return (-1);
                }
@@ -417,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)
@@ -450,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;
@@ -486,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);
@@ -684,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),
@@ -806,16 +800,16 @@ libzfs_mnttab_init(libzfs_handle_t *hdl)
            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;
 
@@ -841,6 +835,7 @@ libzfs_mnttab_update(libzfs_handle_t *hdl)
                avl_add(&hdl->libzfs_mnttab_cache, mtn);
        }
 
+       (void) fclose(mnttab);
        return (0);
 }
 
@@ -872,6 +867,7 @@ 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 ret = ENOENT;
@@ -882,16 +878,14 @@ 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);
        }
 
        pthread_mutex_lock(&hdl->libzfs_mnttab_cache_lock);
@@ -1028,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.
@@ -1067,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];
@@ -1149,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);
@@ -1262,9 +1257,9 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                            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 %s"), propname,
-                                   intval, buf);
+                                   "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;
                        }
@@ -1311,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;
@@ -1364,10 +1358,9 @@ badlabel:
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
+                       zfs_fallthrough;
                }
 
-                       /*FALLTHRU*/
-
                case ZFS_PROP_SHARESMB:
                case ZFS_PROP_SHARENFS:
                        /*
@@ -1425,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 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;
@@ -1599,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,
@@ -1612,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);
 
@@ -1620,7 +1588,7 @@ error:
        return (NULL);
 }
 
-int
+static int
 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 {
        uint64_t old_volsize;
@@ -1722,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)
 {
@@ -1857,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;
 
@@ -1891,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;
@@ -1980,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);
 
@@ -2018,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 {
@@ -2073,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.
@@ -2097,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);
        }
 
@@ -2186,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 ||
@@ -2327,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;
        }
@@ -2399,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);
@@ -2501,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);
@@ -2515,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);
        }
 
@@ -2530,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';
@@ -2551,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;
@@ -2630,13 +2485,11 @@ 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 (value);
+       return (fnvlist_lookup_nvlist(nv, ZPROP_VALUE));
 }
 
 static int
@@ -2911,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);
                }
@@ -3034,13 +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:
                /*
                 * These properties are stored as numbers, but they are
-                * identifiers.
+                * 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);
@@ -3129,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];
@@ -3290,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 {
@@ -3475,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?
  *
@@ -3537,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),
@@ -3690,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);
 
@@ -3714,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,
@@ -3758,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,
@@ -3906,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));
                }
@@ -3979,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);
@@ -3996,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)) {
@@ -4006,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);
 }
 
@@ -4016,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) {
@@ -4028,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"));
 
@@ -4036,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));
@@ -4065,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;
 
@@ -4087,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);
@@ -4152,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);
@@ -4234,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;
@@ -4319,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);
@@ -4337,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 {
@@ -4345,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);
 }
@@ -4462,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'"),
@@ -4512,16 +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;
        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)
@@ -4571,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));
@@ -4612,16 +4479,24 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                return (zfs_error(hdl, EZFS_ZONED, errbuf));
        }
 
-       if (recursive) {
-               zfs_handle_t *zhrp;
+       /*
+        * 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);
-               if (parentname == NULL) {
-                       ret = -1;
-                       goto error;
-               }
                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;
@@ -4630,8 +4505,9 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                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)) {
@@ -4655,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) {
                /*
@@ -4665,7 +4542,7 @@ 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"));
@@ -4759,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;
                }
@@ -4777,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;
@@ -4800,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)
@@ -4814,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);
                        }
@@ -4849,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;
@@ -4897,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;
@@ -4962,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 ||
@@ -4975,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;
@@ -5037,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;
@@ -5061,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;
@@ -5163,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;
@@ -5207,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);
                }
        }
 
@@ -5226,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);
                }
        }
@@ -5243,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);
@@ -5283,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);
                }
        }
 
@@ -5308,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;
 
@@ -5346,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;
                }
        }
@@ -5360,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);
 
@@ -5383,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;
                }
        }
@@ -5463,6 +5329,16 @@ zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
  * 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.
@@ -5492,6 +5368,23 @@ vdev_raidz_asize(uint64_t ndisks, uint64_t nparity, uint64_t 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
@@ -5501,7 +5394,7 @@ static uint64_t
 volsize_from_vdevs(zpool_handle_t *zhp, uint64_t nblocks, uint64_t blksize)
 {
        nvlist_t *config, *tree, **vdevs;
-       uint_t nvdevs, v;
+       uint_t nvdevs;
        uint64_t ret = 0;
 
        config = zpool_get_config(zhp, NULL);
@@ -5511,33 +5404,61 @@ volsize_from_vdevs(zpool_handle_t *zhp, uint64_t nblocks, uint64_t blksize)
                return (nblocks * blksize);
        }
 
-       for (v = 0; v < nvdevs; v++) {
+       for (int v = 0; v < nvdevs; v++) {
                char *type;
                uint64_t nparity, ashift, asize, tsize;
-               nvlist_t **disks;
-               uint_t ndisks;
                uint64_t volsize;
 
                if (nvlist_lookup_string(vdevs[v], ZPOOL_CONFIG_TYPE,
-                   &type) != 0 || strcmp(type, VDEV_TYPE_RAIDZ) != 0 ||
-                   nvlist_lookup_uint64(vdevs[v], ZPOOL_CONFIG_NPARITY,
-                   &nparity) != 0 ||
-                   nvlist_lookup_uint64(vdevs[v], ZPOOL_CONFIG_ASHIFT,
-                   &ashift) != 0 ||
-                   nvlist_lookup_nvlist_array(vdevs[v], ZPOOL_CONFIG_CHILDREN,
-                   &disks, &ndisks) != 0) {
+                   &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;
-               }
 
-               /* 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);
+               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.
+                * Scale this size down as a ratio of 128k / tsize.
+                * See theory statement above.
                 */
                volsize = nblocks * asize * SPA_OLD_MAXBLOCKSIZE / tsize;
                if (volsize > ret) {