]> 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 0f18fd6900d57c37c2ea04cb9432d63963bcd91c..24d9b81f4976cfd7cd357ebec69934f9c95dd2c4 100644 (file)
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- * Copyright (c) 2011, 2016 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.
  * Copyright (c) 2013 Steven Hartland. All rights reserved.
- * Copyright 2016 Nexenta Systems, Inc.
+ * Copyright 2017 Nexenta Systems, Inc.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
+ * 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>
 #include <errno.h>
 #include <libintl.h>
-#include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
@@ -46,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>
@@ -57,7 +58,9 @@
 #include <sys/dnode.h>
 #include <sys/spa.h>
 #include <sys/zap.h>
+#include <sys/dsl_crypt.h>
 #include <libzfs.h>
+#include <libzutil.h>
 
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
@@ -104,7 +107,41 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
        namecheck_err_t why;
        char what;
 
-       (void) zfs_prop_get_table();
+       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) {
@@ -120,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:
@@ -155,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);
@@ -165,41 +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"));
-               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);
-       }
-
        return (-1);
 }
 
@@ -220,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) {
@@ -233,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);
@@ -317,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);
 }
@@ -340,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);
                }
@@ -402,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)
@@ -435,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;
@@ -464,17 +475,15 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
 {
        zfs_cmd_t zc = {"\0"};
 
-       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+       zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));
 
        if (zhp == NULL)
                return (NULL);
 
        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);
@@ -491,7 +500,7 @@ make_dataset_handle(libzfs_handle_t *hdl, const char *path)
 zfs_handle_t *
 make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
 {
-       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+       zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));
 
        if (zhp == NULL)
                return (NULL);
@@ -508,7 +517,7 @@ make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
 zfs_handle_t *
 make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
 {
-       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+       zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));
 
        if (zhp == NULL)
                return (NULL);
@@ -525,7 +534,7 @@ make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
 zfs_handle_t *
 zfs_handle_dup(zfs_handle_t *zhp_orig)
 {
-       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+       zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));
 
        if (zhp == NULL)
                return (NULL);
@@ -580,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)
@@ -605,7 +613,7 @@ zfs_handle_t *
 make_bookmark_handle(zfs_handle_t *parent, const char *path,
     nvlist_t *bmark_props)
 {
-       zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
+       zfs_handle_t *zhp = calloc(1, sizeof (zfs_handle_t));
 
        if (zhp == NULL)
                return (NULL);
@@ -670,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),
@@ -780,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;
 
@@ -826,6 +835,7 @@ libzfs_mnttab_update(libzfs_handle_t *hdl)
                avl_add(&hdl->libzfs_mnttab_cache, mtn);
        }
 
+       (void) fclose(mnttab);
        return (0);
 }
 
@@ -844,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
@@ -856,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 };
@@ -866,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
@@ -897,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
@@ -913,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) {
@@ -923,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
@@ -965,7 +998,7 @@ zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
 nvlist_t *
 zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
     uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,
-    const char *errbuf)
+    boolean_t key_params_ok, const char *errbuf)
 {
        nvpair_t *elem;
        uint64_t intval;
@@ -989,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.
@@ -1028,12 +1061,13 @@ 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[128];
+                       char *newpropname = NULL;
                        char domain[128];
                        uint64_t rid;
                        uint64_t valary[3];
+                       int rc;
 
                        if (userquota_propname_decode(propname, zoned,
                            &uqtype, domain, sizeof (domain), &rid) != 0) {
@@ -1048,7 +1082,9 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        if (uqtype != ZFS_PROP_USERQUOTA &&
                            uqtype != ZFS_PROP_GROUPQUOTA &&
                            uqtype != ZFS_PROP_USEROBJQUOTA &&
-                           uqtype != ZFS_PROP_GROUPOBJQUOTA) {
+                           uqtype != ZFS_PROP_GROUPOBJQUOTA &&
+                           uqtype != ZFS_PROP_PROJECTQUOTA &&
+                           uqtype != ZFS_PROP_PROJECTOBJQUOTA) {
                                zfs_error_aux(hdl,
                                    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
                                    propname);
@@ -1073,7 +1109,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                                if (intval == 0) {
                                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                            "use 'none' to disable "
-                                           "userquota/groupquota"));
+                                           "{user|group|project}quota"));
                                        goto error;
                                }
                        } else {
@@ -1088,19 +1124,27 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                         * userquota@<hex-rid>-domain, to make it easy
                         * for the kernel to decode.
                         */
-                       (void) snprintf(newpropname, sizeof (newpropname),
-                           "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype],
+                       rc = asprintf(&newpropname, "%s%llx-%s",
+                           zfs_userquota_prop_prefixes[uqtype],
                            (longlong_t)rid, domain);
+                       if (rc == -1 || newpropname == NULL) {
+                               (void) no_memory(hdl);
+                               goto error;
+                       }
+
                        valary[0] = uqtype;
                        valary[1] = rid;
                        valary[2] = intval;
                        if (nvlist_add_uint64_array(ret, newpropname,
                            valary, 3) != 0) {
+                               free(newpropname);
                                (void) no_memory(hdl);
                                goto error;
                        }
+                       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);
@@ -1124,7 +1168,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                }
 
                if (zfs_prop_readonly(prop) &&
-                   (!zfs_prop_setonce(prop) || zhp != NULL)) {
+                   !(zfs_prop_setonce(prop) && zhp == NULL) &&
+                   !(zfs_prop_encryption_key_param(prop) && key_params_ok)) {
                        zfs_error_aux(hdl,
                            dgettext(TEXT_DOMAIN, "'%s' is readonly"),
                            propname);
@@ -1182,6 +1227,45 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        }
                        break;
                }
+
+               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 settable properties succeed.
+                                */
+                               if (zpool_prop_get_feature(zpool_hdl,
+                                   "feature@allocation_classes", state,
+                                   sizeof (state)) != 0 ||
+                                   strcmp(state, ZFS_FEATURE_ACTIVE) != 0) {
+                                       (void) fprintf(stderr, gettext(
+                                           "%s: property requires a special "
+                                           "device in the pool\n"), propname);
+                               }
+                       }
+                       if (intval != 0 &&
+                           (intval < SPA_MINBLOCKSIZE ||
+                           intval > maxbs || !ISP2(intval))) {
+                               zfs_nicebytes(maxbs, buf, sizeof (buf));
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "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:
                {
 #ifdef HAVE_MLSLABEL
@@ -1222,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;
@@ -1275,10 +1358,9 @@ badlabel:
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
                                goto error;
                        }
+                       zfs_fallthrough;
                }
 
-                       /*FALLTHRU*/
-
                case ZFS_PROP_SHARESMB:
                case ZFS_PROP_SHARENFS:
                        /*
@@ -1336,58 +1418,66 @@ 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
-                                        */
+                               if (sa_validate_shareopts(strval, proto) !=
+                                   SA_OK) {
                                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                           "'%s' cannot be set: problem "
-                                           "in share initialization"),
-                                           propname);
+                                           "'%s' cannot be set to invalid "
+                                           "options"), 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.
-                                        */
+                       break;
+
+               case ZFS_PROP_KEYLOCATION:
+                       if (!zfs_prop_valid_keylocation(strval, B_FALSE)) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "invalid keylocation"));
+                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                               goto error;
+                       }
+
+                       if (zhp != NULL) {
+                               uint64_t crypt =
+                                   zfs_prop_get_int(zhp, ZFS_PROP_ENCRYPTION);
+
+                               if (crypt == ZIO_CRYPT_OFF &&
+                                   strcmp(strval, "none") != 0) {
                                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                           "'%s' cannot be set to invalid "
-                                           "options"), propname);
+                                           "keylocation must be 'none' "
+                                           "for unencrypted datasets"));
+                                       (void) zfs_error(hdl, EZFS_BADPROP,
+                                           errbuf);
+                                       goto error;
+                               } else if (crypt != ZIO_CRYPT_OFF &&
+                                   strcmp(strval, "none") == 0) {
+                                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                           "keylocation must not be 'none' "
+                                           "for encrypted datasets"));
                                        (void) zfs_error(hdl, EZFS_BADPROP,
                                            errbuf);
-                                       zfs_uninit_libshare(hdl);
                                        goto error;
                                }
-                               zfs_uninit_libshare(hdl);
                        }
+                       break;
 
+               case ZFS_PROP_PBKDF2_ITERS:
+                       if (intval < MIN_PBKDF2_ITERATIONS) {
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "minimum pbkdf2 iterations is %u"),
+                                   MIN_PBKDF2_ITERATIONS);
+                               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
+                               goto error;
+                       }
                        break;
 
                case ZFS_PROP_UTF8ONLY:
@@ -1407,25 +1497,11 @@ badlabel:
                 * checks to enforce.
                 */
                if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
-                       uint64_t volsize = zfs_prop_get_int(zhp,
-                           ZFS_PROP_VOLSIZE);
                        uint64_t blocksize = zfs_prop_get_int(zhp,
                            ZFS_PROP_VOLBLOCKSIZE);
                        char buf[64];
 
                        switch (prop) {
-                       case ZFS_PROP_RESERVATION:
-                       case ZFS_PROP_REFRESERVATION:
-                               if (intval > volsize) {
-                                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                           "'%s' is greater than current "
-                                           "volume size"), propname);
-                                       (void) zfs_error(hdl, EZFS_BADPROP,
-                                           errbuf);
-                                       goto error;
-                               }
-                               break;
-
                        case ZFS_PROP_VOLSIZE:
                                if (intval % blocksize != 0) {
                                        zfs_nicebytes(blocksize, buf,
@@ -1453,6 +1529,27 @@ badlabel:
                                break;
                        }
                }
+
+               /* check encryption properties */
+               if (zhp != NULL) {
+                       int64_t crypt = zfs_prop_get_int(zhp,
+                           ZFS_PROP_ENCRYPTION);
+
+                       switch (prop) {
+                       case ZFS_PROP_COPIES:
+                               if (crypt != ZIO_CRYPT_OFF && intval > 2) {
+                                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                           "encrypted datasets cannot have "
+                                           "3 copies"));
+                                       (void) zfs_error(hdl, EZFS_BADPROP,
+                                           errbuf);
+                                       goto error;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               }
        }
 
        /*
@@ -1461,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,
@@ -1474,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);
 
@@ -1482,7 +1588,7 @@ error:
        return (NULL);
 }
 
-int
+static int
 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 {
        uint64_t old_volsize;
@@ -1491,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,
@@ -1505,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);
@@ -1516,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),
@@ -1527,102 +1634,60 @@ zfs_add_synthetic_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)
+/*
+ * Helper for 'zfs {set|clone} refreservation=auto'.  Must be called after
+ * zfs_valid_proplist(), as it is what sets the UINT64_MAX sentinel value.
+ * Return codes must match zfs_add_synthetic_resv().
+ */
+static int
+zfs_fix_auto_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 {
-       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;
+       uint64_t volsize;
+       uint64_t resvsize;
+       zfs_prop_t prop;
+       nvlist_t *props;
 
-               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;
+       if (!ZFS_IS_VOLUME(zhp)) {
+               return (0);
+       }
 
-               default:
-                       (void) zfs_standard_error(hdl, err, errbuf);
-                       break;
-               }
-               break;
+       if (zfs_which_resv_prop(zhp, &prop) != 0) {
+               return (-1);
+       }
 
-       case EBUSY:
-               (void) zfs_standard_error(hdl, EBUSY, errbuf);
-               break;
+       if (prop != ZFS_PROP_REFRESERVATION) {
+               return (0);
+       }
 
-       case EROFS:
-               (void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
-               break;
+       if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(prop), &resvsize) != 0) {
+               /* No value being set, so it can't be "auto" */
+               return (0);
+       }
+       if (resvsize != UINT64_MAX) {
+               /* Being set to a value other than "auto" */
+               return (0);
+       }
 
-       case E2BIG:
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "property value too long"));
-               (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-               break;
+       props = fnvlist_alloc();
 
-       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;
+       fnvlist_add_uint64(props, zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
+           zfs_prop_get_int(zhp, ZFS_PROP_VOLBLOCKSIZE));
 
-       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;
+       if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
+           &volsize) != 0) {
+               volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
+       }
 
-       case EINVAL:
-               if (prop == ZPROP_INVAL) {
-                       (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
-               } else {
-                       (void) zfs_standard_error(hdl, err, errbuf);
-               }
-               break;
+       resvsize = zvol_volsize_to_reservation(zpool_handle(zhp), volsize,
+           props);
+       fnvlist_free(props);
 
-       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);
+       (void) nvlist_remove_all(nvl, zfs_prop_to_name(prop));
+       if (nvlist_add_uint64(nvl, zfs_prop_to_name(prop), resvsize) != 0) {
+               (void) no_memory(zhp->zfs_hdl);
+               return (-1);
        }
+       return (1);
 }
 
 static boolean_t
@@ -1652,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;
 
@@ -1686,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;
@@ -1700,7 +1765,7 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
 
        if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,
            zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
-           errbuf)) == NULL)
+           B_FALSE, errbuf)) == NULL)
                goto error;
 
        /*
@@ -1715,6 +1780,12 @@ zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
                        goto error;
                }
        }
+
+       if (added_resv != 1 &&
+           (added_resv = zfs_fix_auto_resv(zhp, nvl)) == -1) {
+               goto error;
+       }
+
        /*
         * Check how many properties we're setting and allocate an array to
         * store changelist pointers for postfix().
@@ -1769,20 +1840,24 @@ 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);
 
        if (ret != 0) {
+               if (zc.zc_nvlist_dst_filled == B_FALSE) {
+                       (void) zfs_standard_error(hdl, errno, errbuf);
+                       goto error;
+               }
+
                /* Get the list of unset properties back and report them. */
                nvlist_t *errorprops = NULL;
                if (zcmd_read_dst_nvlist(hdl, &zc, &errorprops) != 0)
                        goto error;
-               for (elem = nvlist_next_nvpair(nvl, NULL);
+               for (nvpair_t *elem = nvlist_next_nvpair(errorprops, NULL);
                    elem != NULL;
-                   elem = nvlist_next_nvpair(nvl, elem)) {
+                   elem = nvlist_next_nvpair(errorprops, elem)) {
                        prop = zfs_name_to_prop(nvpair_name(elem));
                        zfs_setprop_error(hdl, prop, errno, errbuf);
                }
@@ -1802,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 {
@@ -1857,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.
@@ -1881,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);
        }
 
@@ -1970,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 ||
@@ -2111,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;
        }
@@ -2133,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);
 
@@ -2181,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);
@@ -2205,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:
@@ -2244,8 +2322,10 @@ static void
 get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
     char *statbuf, size_t statlen)
 {
-       if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
+       if (statbuf == NULL ||
+           srctype == NULL || *srctype == ZPROP_SRC_TEMPORARY) {
                return;
+       }
 
        if (source == NULL) {
                *srctype = ZPROP_SRC_NONE;
@@ -2277,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);
@@ -2291,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);
        }
 
@@ -2306,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';
@@ -2327,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;
@@ -2406,13 +2485,109 @@ 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));
+       }
+
+       return (fnvlist_lookup_nvlist(nv, ZPROP_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);
        }
 
-       verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) == 0);
+       return (0);
+}
 
-       return (value);
+/*
+ * Accepts a property and value and checks that the value
+ * matches the one found by the channel program. If they are
+ * not equal, print both of them.
+ */
+static void
+zcp_check(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t intval,
+    const char *strval)
+{
+       if (!zhp->zfs_hdl->libzfs_prop_debug)
+               return;
+       int error;
+       char *poolname = zhp->zpool_hdl->zpool_name;
+       const char *prop_name = zfs_prop_to_name(prop);
+       const char *program =
+           "args = ...\n"
+           "ds = args['dataset']\n"
+           "prop = args['property']\n"
+           "value, setpoint = zfs.get_prop(ds, prop)\n"
+           "return {value=value, setpoint=setpoint}\n";
+       nvlist_t *outnvl;
+       nvlist_t *retnvl;
+       nvlist_t *argnvl = fnvlist_alloc();
+
+       fnvlist_add_string(argnvl, "dataset", zhp->zfs_name);
+       fnvlist_add_string(argnvl, "property", zfs_prop_to_name(prop));
+
+       error = lzc_channel_program_nosync(poolname, program,
+           10 * 1000 * 1000, 10 * 1024 * 1024, argnvl, &outnvl);
+
+       if (error == 0) {
+               retnvl = fnvlist_lookup_nvlist(outnvl, "return");
+               if (zfs_prop_get_type(prop) == PROP_TYPE_NUMBER) {
+                       int64_t ans;
+                       error = nvlist_lookup_int64(retnvl, "value", &ans);
+                       if (error != 0) {
+                               (void) fprintf(stderr, "%s: zcp check error: "
+                                   "%u\n", prop_name, error);
+                               return;
+                       }
+                       if (ans != intval) {
+                               (void) fprintf(stderr, "%s: zfs found %llu, "
+                                   "but zcp found %llu\n", prop_name,
+                                   (u_longlong_t)intval, (u_longlong_t)ans);
+                       }
+               } else {
+                       char *str_ans;
+                       error = nvlist_lookup_string(retnvl, "value", &str_ans);
+                       if (error != 0) {
+                               (void) fprintf(stderr, "%s: zcp check error: "
+                                   "%u\n", prop_name, error);
+                               return;
+                       }
+                       if (strcmp(strval, str_ans) != 0) {
+                               (void) fprintf(stderr,
+                                   "%s: zfs found '%s', but zcp found '%s'\n",
+                                   prop_name, strval, str_ans);
+                       }
+               }
+       } else {
+               (void) fprintf(stderr, "%s: zcp check failed, channel program "
+                   "error: %u\n", prop_name, error);
+       }
+       nvlist_free(argnvl);
+       nvlist_free(outnvl);
 }
 
 /*
@@ -2462,6 +2637,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                                (void) snprintf(propbuf, proplen, "%llu",
                                    (u_longlong_t)val);
                }
+               zcp_check(zhp, prop, val, NULL);
                break;
 
        case ZFS_PROP_MOUNTPOINT:
@@ -2530,7 +2706,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                        /* 'legacy' or 'none' */
                        (void) strlcpy(propbuf, str, proplen);
                }
-
+               zcp_check(zhp, prop, 0, propbuf);
                break;
 
        case ZFS_PROP_ORIGIN:
@@ -2538,6 +2714,12 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                if (str == NULL)
                        return (-1);
                (void) strlcpy(propbuf, str, 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:
@@ -2552,7 +2734,6 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
 
                if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
                        return (-1);
-
                /*
                 * If quota or reservation is 0, we translate this into 'none'
                 * (unless literal is set), and indicate that it's the default
@@ -2571,6 +2752,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                        else
                                zfs_nicebytes(val, propbuf, proplen);
                }
+               zcp_check(zhp, prop, val, NULL);
                break;
 
        case ZFS_PROP_FILESYSTEM_LIMIT:
@@ -2582,28 +2764,35 @@ 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);
                }
+
+               zcp_check(zhp, prop, val, NULL);
                break;
 
        case ZFS_PROP_REFRATIO:
        case ZFS_PROP_COMPRESSRATIO:
                if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
                        return (-1);
-               (void) snprintf(propbuf, proplen, "%llu.%02llux",
-                   (u_longlong_t)(val / 100),
-                   (u_longlong_t)(val % 100));
+               if (literal)
+                       (void) snprintf(propbuf, proplen, "%llu.%02llu",
+                           (u_longlong_t)(val / 100),
+                           (u_longlong_t)(val % 100));
+               else
+                       (void) snprintf(propbuf, proplen, "%llu.%02llux",
+                           (u_longlong_t)(val / 100),
+                           (u_longlong_t)(val % 100));
+               zcp_check(zhp, prop, val, NULL);
                break;
 
        case ZFS_PROP_TYPE:
@@ -2624,6 +2813,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                        abort();
                }
                (void) snprintf(propbuf, proplen, "%s", str);
+               zcp_check(zhp, prop, 0, propbuf);
                break;
 
        case ZFS_PROP_MOUNTED:
@@ -2649,6 +2839,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                 * consumers.
                 */
                (void) strlcpy(propbuf, zhp->zfs_name, proplen);
+               zcp_check(zhp, prop, 0, propbuf);
                break;
 
        case ZFS_PROP_MLSLABEL:
@@ -2695,14 +2886,21 @@ 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);
                (void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
+               zcp_check(zhp, prop, val, NULL);
                break;
 
        case ZFS_PROP_REFERENCED:
@@ -2714,31 +2912,39 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
        case ZFS_PROP_USEDCHILD:
                if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
                        return (-1);
-               if (literal)
+               if (literal) {
                        (void) snprintf(propbuf, proplen, "%llu",
                            (u_longlong_t)val);
-               else
+               } else {
                        zfs_nicebytes(val, propbuf, proplen);
+               }
+               zcp_check(zhp, prop, val, NULL);
                break;
 
        default:
                switch (zfs_prop_get_type(prop)) {
                case PROP_TYPE_NUMBER:
                        if (get_numeric_property(zhp, prop, src,
-                           &source, &val) != 0)
+                           &source, &val) != 0) {
                                return (-1);
-                       if (literal)
+                       }
+
+                       if (literal) {
                                (void) snprintf(propbuf, proplen, "%llu",
                                    (u_longlong_t)val);
-                       else
+                       } else {
                                zfs_nicenum(val, propbuf, proplen);
+                       }
+                       zcp_check(zhp, prop, val, NULL);
                        break;
 
                case PROP_TYPE_STRING:
                        str = getprop_string(zhp, prop, &source);
                        if (str == NULL)
                                return (-1);
+
                        (void) strlcpy(propbuf, str, proplen);
+                       zcp_check(zhp, prop, 0, str);
                        break;
 
                case PROP_TYPE_INDEX:
@@ -2747,7 +2953,9 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                                return (-1);
                        if (zfs_prop_index_to_string(prop, val, &strval) != 0)
                                return (-1);
+
                        (void) strlcpy(propbuf, strval, proplen);
+                       zcp_check(zhp, prop, 0, strval);
                        break;
 
                default:
@@ -2776,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];
@@ -2852,6 +3060,8 @@ out:
  * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
  * Eg: groupquota@staff -> ZFS_PROP_GROUPQUOTA, "", 1234
  * Eg: groupused@staff -> ZFS_PROP_GROUPUSED, "", 1234
+ * Eg: projectquota@123 -> ZFS_PROP_PROJECTQUOTA, "", 123
+ * Eg: projectused@789 -> ZFS_PROP_PROJECTUSED, "", 789
  */
 static int
 userquota_propname_decode(const char *propname, boolean_t zoned,
@@ -2861,12 +3071,13 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
        char *cp;
        boolean_t isuser;
        boolean_t isgroup;
+       boolean_t isproject;
        struct passwd *pw;
        struct group *gr;
 
        domain[0] = '\0';
 
-       /* Figure out the property type ({user|group}{quota|space}) */
+       /* Figure out the property type ({user|group|project}{quota|space}) */
        for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
                if (strncmp(propname, zfs_userquota_prop_prefixes[type],
                    strlen(zfs_userquota_prop_prefixes[type])) == 0)
@@ -2882,6 +3093,9 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
        isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED ||
            type == ZFS_PROP_GROUPOBJQUOTA ||
            type == ZFS_PROP_GROUPOBJUSED);
+       isproject = (type == ZFS_PROP_PROJECTQUOTA ||
+           type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTOBJQUOTA ||
+           type == ZFS_PROP_PROJECTOBJUSED);
 
        cp = strchr(propname, '@') + 1;
 
@@ -2893,7 +3107,7 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
                if (zoned && getzoneid() == GLOBAL_ZONEID)
                        return (ENOENT);
                *ridp = gr->gr_gid;
-       } else if (strchr(cp, '@')) {
+       } else if (!isproject && strchr(cp, '@')) {
 #ifdef HAVE_IDMAP
                /*
                 * It's a SID name (eg "user@domain") that needs to be
@@ -2931,16 +3145,17 @@ 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 {
-               /* It's a user/group ID (eg "12345"). */
+               /* It's a user/group/project ID (eg "12345"). */
                uid_t id;
                char *end;
                id = strtoul(cp, &end, 10);
                if (*end != '\0')
                        return (EINVAL);
-               if (id > MAXUID) {
+               if (id > MAXUID && !isproject) {
 #ifdef HAVE_IDMAP
                        /* It's an ephemeral ID. */
                        idmap_rid_t rid;
@@ -2978,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);
 
@@ -3015,10 +3230,13 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
                    (u_longlong_t)propvalue);
        } else if (propvalue == 0 &&
            (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
-           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA)) {
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA ||
+           type == ZFS_PROP_PROJECTQUOTA ||
+           type == ZFS_PROP_PROJECTOBJQUOTA)) {
                (void) strlcpy(propbuf, "none", proplen);
        } else if (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
-           type == ZFS_PROP_USERUSED || type == ZFS_PROP_GROUPUSED) {
+           type == ZFS_PROP_USERUSED || type == ZFS_PROP_GROUPUSED ||
+           type == ZFS_PROP_PROJECTUSED || type == ZFS_PROP_PROJECTQUOTA) {
                zfs_nicebytes(propvalue, propbuf, proplen);
        } else {
                zfs_nicenum(propvalue, propbuf, proplen);
@@ -3026,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)
@@ -3036,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 */
@@ -3048,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);
 
@@ -3109,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?
  *
@@ -3149,6 +3381,12 @@ parent_name(const char *path, char *buf, size_t buflen)
        return (0);
 }
 
+int
+zfs_parent_name(zfs_handle_t *zhp, char *buf, size_t buflen)
+{
+       return (parent_name(zfs_get_name(zhp), buf, buflen));
+}
+
 /*
  * If accept_ancestor is false, then check to make sure that the given path has
  * a parent, and that it exists.  If accept_ancestor is true, then find the
@@ -3165,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),
@@ -3183,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);
@@ -3318,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);
 
@@ -3342,8 +3581,22 @@ zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
 {
        int prefix;
        char *path_copy;
+       char errbuf[ERRBUFLEN];
        int rc = 0;
 
+       (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
+           "cannot create '%s'"), path);
+
+       /*
+        * Check that we are not passing the nesting limit
+        * before we start creating any ancestors.
+        */
+       if (dataset_nestcheck(path) != 0) {
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "maximum name nesting depth exceeded"));
+               return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+       }
+
        if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
                return (-1);
 
@@ -3367,10 +3620,13 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
        int ret;
        uint64_t size = 0;
        uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
-       char errbuf[1024];
        uint64_t zoned;
        enum lzc_dataset_type ost;
        zpool_handle_t *zpool_handle;
+       uint8_t *wkeydata = NULL;
+       uint_t wkeylen = 0;
+       char errbuf[ERRBUFLEN];
+       char parent[ZFS_MAX_DATASET_NAME_LEN];
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot create '%s'"), path);
@@ -3379,6 +3635,12 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
        if (!zfs_validate_name(hdl, path, type, B_TRUE))
                return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
+       if (dataset_nestcheck(path) != 0) {
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "maximum name nesting depth exceeded"));
+               return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+       }
+
        /* validate parents exist */
        if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
                return (-1);
@@ -3414,7 +3676,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
                return (-1);
 
        if (props && (props = zfs_valid_proplist(hdl, type, props,
-           zoned, NULL, zpool_handle, errbuf)) == 0) {
+           zoned, NULL, zpool_handle, B_TRUE, errbuf)) == 0) {
                zpool_close(zpool_handle);
                return (-1);
        }
@@ -3466,31 +3728,43 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
                }
        }
 
+       (void) parent_name(path, parent, sizeof (parent));
+       if (zfs_crypto_create(hdl, parent, props, NULL, B_TRUE,
+           &wkeydata, &wkeylen) != 0) {
+               nvlist_free(props);
+               return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
+       }
+
        /* create the dataset */
-       ret = lzc_create(path, ost, props);
+       ret = lzc_create(path, ost, props, wkeydata, wkeylen);
        nvlist_free(props);
+       if (wkeydata != NULL)
+               free(wkeydata);
 
        /* check for failure */
        if (ret != 0) {
-               char parent[ZFS_MAX_DATASET_NAME_LEN];
-               (void) parent_name(path, parent, sizeof (parent));
-
                switch (errno) {
                case ENOENT:
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "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 "
                            "property or value"));
                        return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
+
+               case EACCES:
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "encryption root's key is not loaded "
+                           "or provided"));
+                       return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
+
+               case ERANGE:
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "invalid property value(s) specified"));
+                       return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 #ifdef _ILP32
                case EOVERFLOW:
                        /*
@@ -3499,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));
                }
@@ -3517,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));
@@ -3565,11 +3841,12 @@ zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
        char name[ZFS_MAX_DATASET_NAME_LEN];
        int rv = 0;
 
-       (void) snprintf(name, sizeof (name),
-           "%s@%s", zhp->zfs_name, dd->snapname);
+       if (snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name,
+           dd->snapname) >= sizeof (name))
+               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);
@@ -3586,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)) {
@@ -3596,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);
 }
 
@@ -3606,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) {
@@ -3618,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"));
 
@@ -3626,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));
@@ -3655,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;
 
@@ -3677,15 +3957,22 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
        /* do the clone */
 
        if (props) {
-               zfs_type_t type;
-               if (ZFS_IS_VOLUME(zhp)) {
+               zfs_type_t type = ZFS_TYPE_FILESYSTEM;
+
+               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, errbuf)) == NULL)
+                   zhp, zhp->zpool_hdl, B_TRUE, errbuf)) == NULL)
                        return (-1);
+               if (zfs_fix_auto_resv(zhp, props) == -1) {
+                       nvlist_free(props);
+                       return (-1);
+               }
+       }
+
+       if (zfs_crypto_clone_check(hdl, zhp, parent, props) != 0) {
+               nvlist_free(props);
+               return (zfs_error(hdl, EZFS_CRYPTOFAILED, errbuf));
        }
 
        ret = lzc_clone(target, zhp->zfs_name, props);
@@ -3730,10 +4017,9 @@ int
 zfs_promote(zfs_handle_t *zhp)
 {
        libzfs_handle_t *hdl = zhp->zfs_hdl;
-       zfs_cmd_t zc = {"\0"};
-       char parent[MAXPATHLEN];
+       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);
@@ -3744,31 +4030,38 @@ zfs_promote(zfs_handle_t *zhp)
                return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
        }
 
-       (void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
-       if (parent[0] == '\0') {
+       if (zhp->zfs_dmustats.dds_origin[0] == '\0') {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "not a cloned filesystem"));
                return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
        }
 
-       (void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
-           sizeof (zc.zc_value));
-       (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
-       ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
+       if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))
+               return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+
+       ret = lzc_promote(zhp->zfs_name, snapname, sizeof (snapname));
 
        if (ret != 0) {
-               int save_errno = errno;
+               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));
 
-               switch (save_errno) {
                case EEXIST:
                        /* There is a conflicting snapshot name. */
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "conflicting snapshot '%s' from parent '%s'"),
-                           zc.zc_string, parent);
+                           snapname, zhp->zfs_dmustats.dds_origin);
                        return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 
                default:
-                       return (zfs_standard_error(hdl, save_errno, errbuf));
+                       return (zfs_standard_error(hdl, ret, errbuf));
                }
        }
        return (ret);
@@ -3787,8 +4080,9 @@ zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
        int rv = 0;
 
        if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) {
-               (void) snprintf(name, sizeof (name),
-                   "%s@%s", zfs_get_name(zhp), sd->sd_snapname);
+               if (snprintf(name, sizeof (name), "%s@%s", zfs_get_name(zhp),
+                   sd->sd_snapname) >= sizeof (name))
+                       return (EINVAL);
 
                fnvlist_add_boolean(sd->sd_nvl, name);
 
@@ -3807,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;
@@ -3843,7 +4137,7 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
 
        if (props != NULL &&
            (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
-           props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) {
+           props, B_FALSE, NULL, zpool_hdl, B_FALSE, errbuf)) == NULL) {
                zpool_close(zpool_hdl);
                return (-1);
        }
@@ -3892,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);
@@ -3910,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 {
@@ -3918,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);
 }
@@ -3992,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);
@@ -4002,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)
@@ -4022,17 +4323,36 @@ zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
        }
 
        /*
-        * We rely on zfs_iter_children() to verify that there are no
-        * newer snapshots for the given dataset.  Therefore, we can
-        * simply pass the name on to the ioctl() call.  There is still
-        * an unlikely race condition where the user has taken a
-        * snapshot since we verified that this was the most recent.
+        * Pass both the filesystem and the wanted snapshot names,
+        * we would get an error back if the snapshot is destroyed or
+        * a new snapshot is created before this request is processed.
         */
-       err = lzc_rollback(zhp->zfs_name, NULL, 0);
+       err = lzc_rollback_to(zhp->zfs_name, snap->zfs_name);
        if (err != 0) {
-               (void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
+               char errbuf[ERRBUFLEN];
+
+               (void) snprintf(errbuf, sizeof (errbuf),
                    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
                    zhp->zfs_name);
+               switch (err) {
+               case EEXIST:
+                       zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                           "there is a snapshot or bookmark more recent "
+                           "than '%s'"), snap->zfs_name);
+                       (void) zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf);
+                       break;
+               case ESRCH:
+                       zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
+                           "'%s' is not found among snapshots of '%s'"),
+                           snap->zfs_name, zhp->zfs_name);
+                       (void) zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf);
+                       break;
+               case EINVAL:
+                       (void) zfs_error(zhp->zfs_hdl, EZFS_BADTYPE, errbuf);
+                       break;
+               default:
+                       (void) zfs_standard_error(zhp->zfs_hdl, err, errbuf);
+               }
                return (err);
        }
 
@@ -4059,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)
@@ -4079,6 +4397,10 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot rename to '%s'"), target);
 
+       /* make sure source name is valid */
+       if (!zfs_validate_name(hdl, zhp->zfs_name, zhp->zfs_type, B_TRUE))
+               return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
+
        /*
         * Make sure the target name is valid
         */
@@ -4112,10 +4434,11 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                                    errbuf));
                        }
                }
+
                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));
@@ -4156,22 +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, 0,
-                   force_unmount ? MS_FORCE : 0)) == NULL)
+               if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
+                   flags.nounmount ? CL_GATHER_DONT_UNMOUNT :
+                   CL_GATHER_ITER_MOUNTED,
+                   flags.forceunmount ? MS_FORCE : 0)) == NULL)
                        return (-1);
 
                if (changelist_haszonedchild(cl)) {
@@ -4195,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) {
                /*
@@ -4205,11 +4542,16 @@ 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) {
+                       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);
                }
@@ -4228,18 +4570,27 @@ 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);
        }
        return (ret);
 }
 
+nvlist_t *
+zfs_get_all_props(zfs_handle_t *zhp)
+{
+       return (zhp->zfs_props);
+}
+
+nvlist_t *
+zfs_get_recvd_props(zfs_handle_t *zhp)
+{
+       if (zhp->zfs_recvd_props == NULL)
+               if (get_recvd_props_ioctl(zhp) != 0)
+                       return (NULL);
+       return (zhp->zfs_recvd_props);
+}
+
 nvlist_t *
 zfs_get_user_props(zfs_handle_t *zhp)
 {
@@ -4285,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;
                }
@@ -4303,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;
@@ -4326,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)
@@ -4340,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);
                        }
@@ -4375,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;
@@ -4423,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;
@@ -4488,19 +4832,20 @@ 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 ||
                            type == ZFS_PROP_USEROBJQUOTA ||
-                           type == ZFS_PROP_GROUPOBJQUOTA)))
+                           type == ZFS_PROP_GROUPOBJQUOTA ||
+                           type == ZFS_PROP_PROJECTOBJUSED ||
+                           type == ZFS_PROP_PROJECTOBJQUOTA ||
+                           type == ZFS_PROP_PROJECTUSED ||
+                           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;
@@ -4532,8 +4877,9 @@ zfs_hold_one(zfs_handle_t *zhp, void *arg)
        char name[ZFS_MAX_DATASET_NAME_LEN];
        int rv = 0;
 
-       (void) snprintf(name, sizeof (name),
-           "%s@%s", zhp->zfs_name, ha->snapname);
+       if (snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name,
+           ha->snapname) >= sizeof (name))
+               return (EINVAL);
 
        if (lzc_exists(name))
                fnvlist_add_string(ha->nvl, name, ha->tag);
@@ -4558,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;
@@ -4582,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;
@@ -4652,8 +4998,11 @@ zfs_release_one(zfs_handle_t *zhp, void *arg)
        int rv = 0;
        nvlist_t *existing_holds;
 
-       (void) snprintf(name, sizeof (name),
-           "%s@%s", zhp->zfs_name, ha->snapname);
+       if (snprintf(name, sizeof (name), "%s@%s", zhp->zfs_name,
+           ha->snapname) >= sizeof (name)) {
+               ha->error = EINVAL;
+               rv = EINVAL;
+       }
 
        if (lzc_get_holds(name, &existing_holds) != 0) {
                ha->error = ENOENT;
@@ -4681,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;
@@ -4725,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);
                }
        }
 
@@ -4744,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);
                }
        }
@@ -4761,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);
@@ -4779,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);
@@ -4801,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);
                }
        }
 
@@ -4826,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;
 
@@ -4864,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;
                }
        }
@@ -4878,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);
 
@@ -4901,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;
                }
        }
@@ -4910,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;
@@ -4931,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 */
@@ -4951,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);
+}