]> git.proxmox.com Git - mirror_zfs.git/blobdiff - lib/libzfs/libzfs_dataset.c
Make createtxg and guid properties public
[mirror_zfs.git] / lib / libzfs / libzfs_dataset.c
index 6b67a73efd8b51aee3eab9f0c37c47930336eb74..f1346b69c6f43316a46ac07328d733982643c900 100644 (file)
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
- * Copyright (c) 2013 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2016 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 2013 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2016 Nexenta Systems, Inc.
+ * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  */
 
 #include <ctype.h>
@@ -43,7 +44,6 @@
 #include <fcntl.h>
 #include <sys/mntent.h>
 #include <sys/mount.h>
-#include <priv.h>
 #include <pwd.h>
 #include <grp.h>
 #include <stddef.h>
@@ -81,8 +81,12 @@ zfs_type_to_name(zfs_type_t type)
                return (dgettext(TEXT_DOMAIN, "snapshot"));
        case ZFS_TYPE_VOLUME:
                return (dgettext(TEXT_DOMAIN, "volume"));
+       case ZFS_TYPE_POOL:
+               return (dgettext(TEXT_DOMAIN, "pool"));
+       case ZFS_TYPE_BOOKMARK:
+               return (dgettext(TEXT_DOMAIN, "bookmark"));
        default:
-               break;
+               assert(!"unhandled zfs_type_t");
        }
 
        return (NULL);
@@ -101,7 +105,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
        char what;
 
        (void) zfs_prop_get_table();
-       if (dataset_namecheck(path, &why, &what) != 0) {
+       if (entity_namecheck(path, &why, &what) != 0) {
                if (hdl != NULL) {
                        switch (why) {
                        case NAME_ERR_TOOLONG:
@@ -130,9 +134,10 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
                                    "'%c' in name"), what);
                                break;
 
-                       case NAME_ERR_MULTIPLE_AT:
+                       case NAME_ERR_MULTIPLE_DELIMITERS:
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                                   "multiple '@' delimiters in name"));
+                                   "multiple '@' and/or '#' delimiters in "
+                                   "name"));
                                break;
 
                        case NAME_ERR_NOLETTER:
@@ -149,7 +154,10 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "reserved disk name"));
                                break;
+
                        default:
+                               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                                   "(%d) not defined"), why);
                                break;
                        }
                }
@@ -160,7 +168,7 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
        if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
                if (hdl != NULL)
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "snapshot delimiter '@' in filesystem name"));
+                           "snapshot delimiter '@' is not expected here"));
                return (0);
        }
 
@@ -171,6 +179,20 @@ zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
                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,
@@ -552,7 +574,7 @@ zfs_bookmark_exists(const char *path)
 {
        nvlist_t *bmarks;
        nvlist_t *props;
-       char fsname[ZFS_MAXNAMELEN];
+       char fsname[ZFS_MAX_DATASET_NAME_LEN];
        char *bmark_name;
        char *pound;
        int err;
@@ -611,8 +633,36 @@ make_bookmark_handle(zfs_handle_t *parent, const char *path,
        return (zhp);
 }
 
+struct zfs_open_bookmarks_cb_data {
+       const char *path;
+       zfs_handle_t *zhp;
+};
+
+static int
+zfs_open_bookmarks_cb(zfs_handle_t *zhp, void *data)
+{
+       struct zfs_open_bookmarks_cb_data *dp = data;
+
+       /*
+        * Is it the one we are looking for?
+        */
+       if (strcmp(dp->path, zfs_get_name(zhp)) == 0) {
+               /*
+                * We found it.  Save it and let the caller know we are done.
+                */
+               dp->zhp = zhp;
+               return (EEXIST);
+       }
+
+       /*
+        * Not found.  Close the handle and ask for another one.
+        */
+       zfs_close(zhp);
+       return (0);
+}
+
 /*
- * Opens the given snapshot, filesystem, or volume.   The 'types'
+ * Opens the given snapshot, bookmark, filesystem, or volume.   The 'types'
  * argument is a mask of acceptable types.  The function will print an
  * appropriate error message and return NULL if it can't be opened.
  */
@@ -621,6 +671,7 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
 {
        zfs_handle_t *zhp;
        char errbuf[1024];
+       char *bookp;
 
        (void) snprintf(errbuf, sizeof (errbuf),
            dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
@@ -628,20 +679,68 @@ zfs_open(libzfs_handle_t *hdl, const char *path, int types)
        /*
         * Validate the name before we even try to open it.
         */
-       if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "invalid dataset name"));
+       if (!zfs_validate_name(hdl, path, types, B_FALSE)) {
                (void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
                return (NULL);
        }
 
        /*
-        * Try to get stats for the dataset, which will tell us if it exists.
+        * Bookmarks needs to be handled separately.
         */
-       errno = 0;
-       if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
-               (void) zfs_standard_error(hdl, errno, errbuf);
-               return (NULL);
+       bookp = strchr(path, '#');
+       if (bookp == NULL) {
+               /*
+                * Try to get stats for the dataset, which will tell us if it
+                * exists.
+                */
+               errno = 0;
+               if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
+                       (void) zfs_standard_error(hdl, errno, errbuf);
+                       return (NULL);
+               }
+       } else {
+               char dsname[ZFS_MAX_DATASET_NAME_LEN];
+               zfs_handle_t *pzhp;
+               struct zfs_open_bookmarks_cb_data cb_data = {path, NULL};
+
+               /*
+                * We need to cut out '#' and everything after '#'
+                * to get the parent dataset name only.
+                */
+               assert(bookp - path < sizeof (dsname));
+               (void) strncpy(dsname, path, bookp - path);
+               dsname[bookp - path] = '\0';
+
+               /*
+                * Create handle for the parent dataset.
+                */
+               errno = 0;
+               if ((pzhp = make_dataset_handle(hdl, dsname)) == NULL) {
+                       (void) zfs_standard_error(hdl, errno, errbuf);
+                       return (NULL);
+               }
+
+               /*
+                * Iterate bookmarks to find the right one.
+                */
+               errno = 0;
+               if ((zfs_iter_bookmarks(pzhp, zfs_open_bookmarks_cb,
+                   &cb_data) == 0) && (cb_data.zhp == NULL)) {
+                       (void) zfs_error(hdl, EZFS_NOENT, errbuf);
+                       zfs_close(pzhp);
+                       return (NULL);
+               }
+               if (cb_data.zhp == NULL) {
+                       (void) zfs_standard_error(hdl, errno, errbuf);
+                       zfs_close(pzhp);
+                       return (NULL);
+               }
+               zhp = cb_data.zhp;
+
+               /*
+                * Cleanup.
+                */
+               zfs_close(pzhp);
        }
 
        if (!(types & zhp->zfs_type)) {
@@ -675,15 +774,13 @@ typedef struct mnttab_node {
 static int
 libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
 {
-       const mnttab_node_t *mtn1 = arg1;
-       const mnttab_node_t *mtn2 = arg2;
+       const mnttab_node_t *mtn1 = (const mnttab_node_t *)arg1;
+       const mnttab_node_t *mtn2 = (const mnttab_node_t *)arg2;
        int rv;
 
        rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
 
-       if (rv == 0)
-               return (0);
-       return (rv > 0 ? 1 : -1);
+       return (AVL_ISIGN(rv));
 }
 
 void
@@ -738,7 +835,8 @@ libzfs_mnttab_fini(libzfs_handle_t *hdl)
        void *cookie = NULL;
        mnttab_node_t *mtn;
 
-       while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))) {
+       while ((mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie))
+           != NULL) {
                free(mtn->mtn_mt.mnt_special);
                free(mtn->mtn_mt.mnt_mountp);
                free(mtn->mtn_mt.mnt_fstype);
@@ -816,7 +914,8 @@ libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
        mnttab_node_t *ret;
 
        find.mtn_mt.mnt_special = (char *)fsname;
-       if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))) {
+       if ((ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL))
+           != NULL) {
                avl_remove(&hdl->libzfs_mnttab_cache, ret);
                free(ret->mtn_mt.mnt_special);
                free(ret->mtn_mt.mnt_mountp);
@@ -865,7 +964,8 @@ 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, const char *errbuf)
+    uint64_t zoned, zfs_handle_t *zhp, zpool_handle_t *zpool_hdl,
+    const char *errbuf)
 {
        nvpair_t *elem;
        uint64_t intval;
@@ -946,7 +1046,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_GROUPQUOTA &&
+                           uqtype != ZFS_PROP_USEROBJQUOTA &&
+                           uqtype != ZFS_PROP_GROUPOBJQUOTA) {
                                zfs_error_aux(hdl,
                                    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
                                    propname);
@@ -1061,8 +1163,8 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                        int maxbs = SPA_MAXBLOCKSIZE;
                        char buf[64];
 
-                       if (zhp != NULL) {
-                               maxbs = zpool_get_prop_int(zhp->zpool_hdl,
+                       if (zpool_hdl != NULL) {
+                               maxbs = zpool_get_prop_int(zpool_hdl,
                                    ZPOOL_PROP_MAXBLOCKSIZE, NULL);
                        }
                        /*
@@ -1071,7 +1173,7 @@ zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
                         */
                        if (intval < SPA_MINBLOCKSIZE ||
                            intval > maxbs || !ISP2(intval)) {
-                               zfs_nicenum(maxbs, buf, sizeof (buf));
+                               zfs_nicebytes(maxbs, buf, sizeof (buf));
                                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                    "'%s' must be power of 2 from 512B "
                                    "to %s"), propname, buf);
@@ -1162,7 +1264,12 @@ badlabel:
                                            "component of '%s' is too long"),
                                            propname);
                                        break;
+
                                default:
+                                       zfs_error_aux(hdl,
+                                           dgettext(TEXT_DOMAIN,
+                                           "(%d) not defined"),
+                                           why);
                                        break;
                                }
                                (void) zfs_error(hdl, EZFS_BADPROP, errbuf);
@@ -1282,12 +1389,15 @@ badlabel:
                        }
 
                        break;
+
                case ZFS_PROP_UTF8ONLY:
                        chosen_utf = (int)intval;
                        break;
+
                case ZFS_PROP_NORMALIZE:
                        chosen_normal = (int)intval;
                        break;
+
                default:
                        break;
                }
@@ -1318,7 +1428,7 @@ badlabel:
 
                        case ZFS_PROP_VOLSIZE:
                                if (intval % blocksize != 0) {
-                                       zfs_nicenum(blocksize, buf,
+                                       zfs_nicebytes(blocksize, buf,
                                            sizeof (buf));
                                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                                            "'%s' must be a multiple of "
@@ -1338,6 +1448,7 @@ badlabel:
                                        goto error;
                                }
                                break;
+
                        default:
                                break;
                        }
@@ -1379,6 +1490,7 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
        uint64_t old_reservation;
        uint64_t new_reservation;
        zfs_prop_t resv_prop;
+       nvlist_t *props;
 
        /*
         * If this is an existing volume, and someone is setting the volsize,
@@ -1388,16 +1500,25 @@ zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
        if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
                return (-1);
        old_reservation = zfs_prop_get_int(zhp, resv_prop);
-       if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) !=
-           old_reservation) || nvlist_lookup_uint64(nvl,
-           zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) {
+
+       props = fnvlist_alloc();
+       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) !=
+           old_reservation) || nvlist_exists(nvl,
+           zfs_prop_to_name(resv_prop))) {
+               fnvlist_free(props);
                return (0);
        }
        if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
-           &new_volsize) != 0)
+           &new_volsize) != 0) {
+               fnvlist_free(props);
                return (-1);
-       new_reservation = zvol_volsize_to_reservation(new_volsize,
-           zhp->zfs_props);
+       }
+       new_reservation = zvol_volsize_to_reservation(new_volsize, props);
+       fnvlist_free(props);
+
        if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
            new_reservation) != 0) {
                (void) no_memory(zhp->zfs_hdl);
@@ -1463,11 +1584,18 @@ zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
 
        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);
                }
@@ -1523,15 +1651,10 @@ zfs_is_namespace_prop(zfs_prop_t prop)
 int
 zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
 {
-       zfs_cmd_t zc = {"\0"};
        int ret = -1;
-       prop_changelist_t *cl = NULL;
        char errbuf[1024];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
-       nvlist_t *nvl = NULL, *realprops;
-       zfs_prop_t prop;
-       boolean_t do_prefix = B_TRUE;
-       int added_resv = 0;
+       nvlist_t *nvl = NULL;
 
        (void) snprintf(errbuf, sizeof (errbuf),
            dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
@@ -1543,65 +1666,136 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
                goto error;
        }
 
-       if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
-           zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
-               goto error;
+       ret = zfs_prop_set_list(zhp, nvl);
 
+error:
        nvlist_free(nvl);
-       nvl = realprops;
+       return (ret);
+}
 
-       prop = zfs_name_to_prop(propname);
 
-       if (prop == ZFS_PROP_VOLSIZE) {
-               if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1)
-                       goto error;
-       }
 
-       if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
-               goto error;
+/*
+ * Given an nvlist of property names and values, set the properties for the
+ * given dataset.
+ */
+int
+zfs_prop_set_list(zfs_handle_t *zhp, nvlist_t *props)
+{
+       zfs_cmd_t zc = {"\0"};
+       int ret = -1;
+       prop_changelist_t **cls = NULL;
+       int cl_idx;
+       char errbuf[1024];
+       libzfs_handle_t *hdl = zhp->zfs_hdl;
+       nvlist_t *nvl;
+       int nvl_len = 0;
+       int added_resv = 0;
+       zfs_prop_t prop = 0;
+       nvpair_t *elem;
 
-       if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "child dataset with inherited mountpoint is used "
-                   "in a non-global zone"));
-               ret = zfs_error(hdl, EZFS_ZONED, errbuf);
+       (void) snprintf(errbuf, sizeof (errbuf),
+           dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
+           zhp->zfs_name);
+
+       if ((nvl = zfs_valid_proplist(hdl, zhp->zfs_type, props,
+           zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, zhp->zpool_hdl,
+           errbuf)) == NULL)
                goto error;
-       }
 
        /*
-        * We don't want to unmount & remount the dataset when changing
-        * its canmount property to 'on' or 'noauto'.  We only use
-        * the changelist logic to unmount when setting canmount=off.
+        * We have to check for any extra properties which need to be added
+        * before computing the length of the nvlist.
         */
-       if (prop == ZFS_PROP_CANMOUNT) {
-               uint64_t idx;
-               int err = zprop_string_to_index(prop, propval, &idx,
-                   ZFS_TYPE_DATASET);
-               if (err == 0 && idx != ZFS_CANMOUNT_OFF)
-                       do_prefix = B_FALSE;
+       for (elem = nvlist_next_nvpair(nvl, NULL);
+           elem != NULL;
+           elem = nvlist_next_nvpair(nvl, elem)) {
+               if (zfs_name_to_prop(nvpair_name(elem)) == ZFS_PROP_VOLSIZE &&
+                   (added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1) {
+                       goto error;
+               }
        }
-
-       if (do_prefix && (ret = changelist_prefix(cl)) != 0)
+       /*
+        * Check how many properties we're setting and allocate an array to
+        * store changelist pointers for postfix().
+        */
+       for (elem = nvlist_next_nvpair(nvl, NULL);
+           elem != NULL;
+           elem = nvlist_next_nvpair(nvl, elem))
+               nvl_len++;
+       if ((cls = calloc(nvl_len, sizeof (prop_changelist_t *))) == NULL)
                goto error;
 
+       cl_idx = 0;
+       for (elem = nvlist_next_nvpair(nvl, NULL);
+           elem != NULL;
+           elem = nvlist_next_nvpair(nvl, elem)) {
+
+               prop = zfs_name_to_prop(nvpair_name(elem));
+
+               assert(cl_idx < nvl_len);
+               /*
+                * We don't want to unmount & remount the dataset when changing
+                * its canmount property to 'on' or 'noauto'.  We only use
+                * the changelist logic to unmount when setting canmount=off.
+                */
+               if (prop != ZFS_PROP_CANMOUNT ||
+                   (fnvpair_value_uint64(elem) == ZFS_CANMOUNT_OFF &&
+                   zfs_is_mounted(zhp, NULL))) {
+                       cls[cl_idx] = changelist_gather(zhp, prop, 0, 0);
+                       if (cls[cl_idx] == NULL)
+                               goto error;
+               }
+
+               if (prop == ZFS_PROP_MOUNTPOINT &&
+                   changelist_haszonedchild(cls[cl_idx])) {
+                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                           "child dataset with inherited mountpoint is used "
+                           "in a non-global zone"));
+                       ret = zfs_error(hdl, EZFS_ZONED, errbuf);
+                       goto error;
+               }
+
+               if (cls[cl_idx] != NULL &&
+                   (ret = changelist_prefix(cls[cl_idx])) != 0)
+                       goto error;
+
+               cl_idx++;
+       }
+       assert(cl_idx == nvl_len);
+
        /*
-        * Execute the corresponding ioctl() to set this property.
+        * Execute the corresponding ioctl() to set this list of properties.
         */
        (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
-       if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
+       if ((ret = zcmd_write_src_nvlist(hdl, &zc, nvl)) != 0 ||
+           (ret = zcmd_alloc_dst_nvlist(hdl, &zc, 0)) != 0)
                goto error;
 
        ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 
        if (ret != 0) {
-               zfs_setprop_error(hdl, prop, errno, errbuf);
+               /* 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);
+                   elem != NULL;
+                   elem = nvlist_next_nvpair(nvl, elem)) {
+                       prop = zfs_name_to_prop(nvpair_name(elem));
+                       zfs_setprop_error(hdl, prop, errno, errbuf);
+               }
+               nvlist_free(errorprops);
+
                if (added_resv && errno == ENOSPC) {
                        /* clean up the volsize property we tried to set */
                        uint64_t old_volsize = zfs_prop_get_int(zhp,
                            ZFS_PROP_VOLSIZE);
                        nvlist_free(nvl);
+                       nvl = NULL;
                        zcmd_free_nvlists(&zc);
+
                        if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
                                goto error;
                        if (nvlist_add_uint64(nvl,
@@ -1613,8 +1807,13 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
                        (void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
                }
        } else {
-               if (do_prefix)
-                       ret = changelist_postfix(cl);
+               for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {
+                       if (cls[cl_idx] != NULL) {
+                               int clp_err = changelist_postfix(cls[cl_idx]);
+                               if (clp_err != 0)
+                                       ret = clp_err;
+                       }
+               }
 
                if (ret == 0) {
                        /*
@@ -1637,8 +1836,13 @@ zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
 error:
        nvlist_free(nvl);
        zcmd_free_nvlists(&zc);
-       if (cl)
-               changelist_free(cl);
+       if (cls != NULL) {
+               for (cl_idx = 0; cl_idx < nvl_len; cl_idx++) {
+                       if (cls[cl_idx] != NULL)
+                               changelist_free(cls[cl_idx]);
+               }
+               free(cls);
+       }
        return (ret);
 }
 
@@ -1778,22 +1982,21 @@ getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
        return (value);
 }
 
-static char *
+static const char *
 getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
 {
        nvlist_t *nv;
-       char *value;
+       const char *value;
 
        *source = NULL;
        if (nvlist_lookup_nvlist(zhp->zfs_props,
            zfs_prop_to_name(prop), &nv) == 0) {
-               verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
+               value = fnvlist_lookup_string(nv, ZPROP_VALUE);
                (void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
        } else {
                verify(!zhp->zfs_props_table ||
                    zhp->zfs_props_table[prop] == B_TRUE);
-               if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
-                       value = "";
+               value = zfs_prop_default_string(prop);
                *source = "";
        }
 
@@ -1825,9 +2028,9 @@ zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
  * zfs_prop_get_int() are built using this interface.
  *
  * Certain properties can be overridden using 'mount -o'.  In this case, scan
- * the contents of the /etc/mtab entry, searching for the appropriate options.
- * If they differ from the on-disk values, report the current values and mark
- * the source "temporary".
+ * the contents of the /proc/self/mounts entry, searching for the
+ * appropriate options. If they differ from the on-disk values, report the
+ * current values and mark the source "temporary".
  */
 static int
 get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
@@ -1847,9 +2050,9 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
         * the property is valid for the snapshot's head dataset type.
         */
        if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT &&
-               !zfs_prop_valid_for_type(prop, zhp->zfs_head_type, B_TRUE)) {
-                       *val = zfs_prop_default_numeric(prop);
-                       return (-1);
+           !zfs_prop_valid_for_type(prop, zhp->zfs_head_type, B_TRUE)) {
+               *val = zfs_prop_default_numeric(prop);
+               return (-1);
        }
 
        switch (prop) {
@@ -1892,14 +2095,16 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                mntopt_on = MNTOPT_NBMAND;
                mntopt_off = MNTOPT_NONBMAND;
                break;
+
        default:
                break;
        }
 
        /*
         * Because looking up the mount options is potentially expensive
-        * (iterating over all of /etc/mtab), we defer its calculation until
-        * we're looking up a property which requires its presence.
+        * (iterating over all of /proc/self/mounts), we defer its
+        * calculation until we're looking up a property which requires
+        * its presence.
         */
        if (!zhp->zfs_mntcheck &&
            (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
@@ -1922,6 +2127,8 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                mnt.mnt_mntopts = zhp->zfs_mntopts;
 
        switch (prop) {
+       case ZFS_PROP_ATIME:
+       case ZFS_PROP_RELATIME:
        case ZFS_PROP_DEVICES:
        case ZFS_PROP_EXEC:
        case ZFS_PROP_READONLY:
@@ -1944,8 +2151,6 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                }
                break;
 
-       case ZFS_PROP_ATIME:
-       case ZFS_PROP_RELATIME:
        case ZFS_PROP_CANMOUNT:
        case ZFS_PROP_VOLSIZE:
        case ZFS_PROP_QUOTA:
@@ -1992,8 +2197,7 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                        zcmd_free_nvlists(&zc);
                        return (-1);
                }
-               if (zplprops)
-                       nvlist_free(zplprops);
+               nvlist_free(zplprops);
                zcmd_free_nvlists(&zc);
                break;
 
@@ -2009,11 +2213,15 @@ get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
                        /*
                         * If we tried to use a default value for a
                         * readonly property, it means that it was not
-                        * present.
+                        * present.  Note this only applies to "truly"
+                        * readonly properties, not set-once properties
+                        * like volblocksize.
                         */
                        if (zfs_prop_readonly(prop) &&
+                           !zfs_prop_setonce(prop) &&
                            *source != NULL && (*source)[0] == '\0') {
                                *source = NULL;
+                               return (-1);
                        }
                        break;
 
@@ -2116,7 +2324,7 @@ struct get_clones_arg {
        uint64_t numclones;
        nvlist_t *value;
        const char *origin;
-       char buf[ZFS_MAXNAMELEN];
+       char buf[ZFS_MAX_DATASET_NAME_LEN];
 };
 
 int
@@ -2171,7 +2379,7 @@ zfs_get_clones_nvl(zfs_handle_t *zhp)
 
                if (gca.numclones != 0) {
                        zfs_handle_t *root;
-                       char pool[ZFS_MAXNAMELEN];
+                       char pool[ZFS_MAX_DATASET_NAME_LEN];
                        char *cp = pool;
 
                        /* get the pool name */
@@ -2179,6 +2387,11 @@ zfs_get_clones_nvl(zfs_handle_t *zhp)
                        (void) strsep(&cp, "/@");
                        root = zfs_open(zhp->zfs_hdl, pool,
                            ZFS_TYPE_FILESYSTEM);
+                       if (root == NULL) {
+                               nvlist_free(nv);
+                               nvlist_free(value);
+                               return (NULL);
+                       }
 
                        (void) get_clones_cb(root, &gca);
                }
@@ -2215,7 +2428,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
 {
        char *source = NULL;
        uint64_t val;
-       char *str;
+       const char *str;
        const char *strval;
        boolean_t received = zfs_is_recvd_props_mode(zhp);
 
@@ -2247,7 +2460,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                            strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
                            &t) == 0)
                                (void) snprintf(propbuf, proplen, "%llu",
-                                   (u_longlong_t) val);
+                                   (u_longlong_t)val);
                }
                break;
 
@@ -2286,8 +2499,8 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                        }
 
                        if ((zpool_get_prop(zhp->zpool_hdl,
-                           ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
-                           (strcmp(root, "-") == 0))
+                           ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL,
+                           B_FALSE)) || (strcmp(root, "-") == 0))
                                root[0] = '\0';
                        /*
                         * Special case an alternate root of '/'. This will
@@ -2321,14 +2534,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                break;
 
        case ZFS_PROP_ORIGIN:
-               (void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
-                   proplen);
-               /*
-                * If there is no parent at all, return failure to indicate that
-                * it doesn't apply to this dataset.
-                */
-               if (propbuf[0] == '\0')
+               str = getprop_string(zhp, prop, &source);
+               if (str == NULL)
                        return (-1);
+               (void) strlcpy(propbuf, str, proplen);
                break;
 
        case ZFS_PROP_CLONES:
@@ -2360,7 +2569,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);
                        else
-                               zfs_nicenum(val, propbuf, proplen);
+                               zfs_nicebytes(val, propbuf, proplen);
                }
                break;
 
@@ -2486,6 +2695,7 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                break;
 
        case ZFS_PROP_GUID:
+       case ZFS_PROP_CREATETXG:
                /*
                 * GUIDs are stored as numbers, but they are identifiers.
                 * We don't want them to be pretty printed, because pretty
@@ -2496,6 +2706,22 @@ 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);
                break;
 
+       case ZFS_PROP_REFERENCED:
+       case ZFS_PROP_AVAILABLE:
+       case ZFS_PROP_USED:
+       case ZFS_PROP_USEDSNAP:
+       case ZFS_PROP_USEDDS:
+       case ZFS_PROP_USEDREFRESERV:
+       case ZFS_PROP_USEDCHILD:
+               if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
+                       return (-1);
+               if (literal)
+                       (void) snprintf(propbuf, proplen, "%llu",
+                           (u_longlong_t)val);
+               else
+                       zfs_nicebytes(val, propbuf, proplen);
+               break;
+
        default:
                switch (zfs_prop_get_type(prop)) {
                case PROP_TYPE_NUMBER:
@@ -2510,8 +2736,10 @@ zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
                        break;
 
                case PROP_TYPE_STRING:
-                       (void) strlcpy(propbuf,
-                           getprop_string(zhp, prop, &source), proplen);
+                       str = getprop_string(zhp, prop, &source);
+                       if (str == NULL)
+                               return (-1);
+                       (void) strlcpy(propbuf, str, proplen);
                        break;
 
                case PROP_TYPE_INDEX:
@@ -2542,7 +2770,7 @@ uint64_t
 zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
 {
        char *source;
-       uint64_t val;
+       uint64_t val = 0;
 
        (void) get_numeric_property(zhp, prop, NULL, &source, &val);
 
@@ -2649,8 +2877,12 @@ userquota_propname_decode(const char *propname, boolean_t zoned,
                return (EINVAL);
        *typep = type;
 
-       isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED);
-       isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED);
+       isuser = (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_USERUSED ||
+           type == ZFS_PROP_USEROBJQUOTA ||
+           type == ZFS_PROP_USEROBJUSED);
+       isgroup = (type == ZFS_PROP_GROUPQUOTA || type == ZFS_PROP_GROUPUSED ||
+           type == ZFS_PROP_GROUPOBJQUOTA ||
+           type == ZFS_PROP_GROUPOBJUSED);
 
        cp = strchr(propname, '@') + 1;
 
@@ -2783,8 +3015,12 @@ zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
                (void) snprintf(propbuf, proplen, "%llu",
                    (u_longlong_t)propvalue);
        } else if (propvalue == 0 &&
-           (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
+           (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
+           type == ZFS_PROP_USEROBJQUOTA || type == ZFS_PROP_GROUPOBJQUOTA)) {
                (void) strlcpy(propbuf, "none", proplen);
+       } else if (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA ||
+           type == ZFS_PROP_USERUSED || type == ZFS_PROP_GROUPUSED) {
+               zfs_nicebytes(propvalue, propbuf, proplen);
        } else {
                zfs_nicenum(propvalue, propbuf, proplen);
        }
@@ -2841,7 +3077,7 @@ zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
                (void) snprintf(propbuf, proplen, "%llu",
                    (u_longlong_t)propvalue);
        } else {
-               zfs_nicenum(propvalue, propbuf, proplen);
+               zfs_nicebytes(propvalue, propbuf, proplen);
        }
 
        return (0);
@@ -2856,6 +3092,15 @@ zfs_get_name(const zfs_handle_t *zhp)
        return (zhp->zfs_name);
 }
 
+/*
+ * Returns the name of the parent pool for the given zfs handle.
+ */
+const char *
+zfs_get_pool_name(const zfs_handle_t *zhp)
+{
+       return (zhp->zpool_hdl->zpool_name);
+}
+
 /*
  * Returns the type of the given zfs handle.
  */
@@ -2918,7 +3163,7 @@ check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
     boolean_t accept_ancestor, int *prefixlen)
 {
        zfs_cmd_t zc = {"\0"};
-       char parent[ZFS_MAXNAMELEN];
+       char parent[ZFS_MAX_DATASET_NAME_LEN];
        char *slash;
        zfs_handle_t *zhp;
        char errbuf[1024];
@@ -3046,7 +3291,7 @@ create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
         * up to the prefixlen-long one.
         */
        for (cp = target + prefixlen + 1;
-           (cp = strchr(cp, '/')); *cp = '/', cp++) {
+           (cp = strchr(cp, '/')) != NULL; *cp = '/', cp++) {
 
                *cp = '\0';
 
@@ -3125,7 +3370,8 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
        uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
        char errbuf[1024];
        uint64_t zoned;
-       dmu_objset_type_t ost;
+       enum lzc_dataset_type ost;
+       zpool_handle_t *zpool_handle;
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot create '%s'"), path);
@@ -3152,13 +3398,28 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
        }
 
        if (type == ZFS_TYPE_VOLUME)
-               ost = DMU_OST_ZVOL;
+               ost = LZC_DATSET_TYPE_ZVOL;
        else
-               ost = DMU_OST_ZFS;
+               ost = LZC_DATSET_TYPE_ZFS;
+
+       /* open zpool handle for prop validation */
+       char pool_path[ZFS_MAX_DATASET_NAME_LEN];
+       (void) strlcpy(pool_path, path, sizeof (pool_path));
+
+       /* truncate pool_path at first slash */
+       char *p = strchr(pool_path, '/');
+       if (p != NULL)
+               *p = '\0';
+
+       if ((zpool_handle = zpool_open(hdl, pool_path)) == NULL)
+               return (-1);
 
        if (props && (props = zfs_valid_proplist(hdl, type, props,
-           zoned, NULL, errbuf)) == 0)
+           zoned, NULL, zpool_handle, errbuf)) == 0) {
+               zpool_close(zpool_handle);
                return (-1);
+       }
+       zpool_close(zpool_handle);
 
        if (type == ZFS_TYPE_VOLUME) {
                /*
@@ -3212,9 +3473,7 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
 
        /* check for failure */
        if (ret != 0) {
-               char parent[ZFS_MAXNAMELEN];
-               char buf[64];
-
+               char parent[ZFS_MAX_DATASET_NAME_LEN];
                (void) parent_name(path, parent, sizeof (parent));
 
                switch (errno) {
@@ -3228,14 +3487,6 @@ zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
                            "parent '%s' is not a filesystem"), parent);
                        return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 
-               case EDOM:
-                       zfs_nicenum(SPA_MAXBLOCKSIZE, buf, sizeof (buf));
-                       zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                           "volume block size must be power of 2 from "
-                           "512B to %s"), buf);
-
-                       return (zfs_error(hdl, EZFS_BADPROP, errbuf));
-
                case ENOTSUP:
                        zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                            "pool must be upgraded to set this "
@@ -3312,7 +3563,7 @@ static int
 zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
 {
        struct destroydata *dd = arg;
-       char name[ZFS_MAXNAMELEN];
+       char name[ZFS_MAX_DATASET_NAME_LEN];
        int rv = 0;
 
        (void) snprintf(name, sizeof (name),
@@ -3357,13 +3608,15 @@ int
 zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
 {
        int ret;
-       nvlist_t *errlist;
+       nvlist_t *errlist = NULL;
        nvpair_t *pair;
 
        ret = lzc_destroy_snaps(snaps, defer, &errlist);
 
-       if (ret == 0)
+       if (ret == 0) {
+               nvlist_free(errlist);
                return (0);
+       }
 
        if (nvlist_empty(errlist)) {
                char errbuf[1024];
@@ -3391,6 +3644,7 @@ zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
                }
        }
 
+       nvlist_free(errlist);
        return (ret);
 }
 
@@ -3400,7 +3654,7 @@ zfs_destroy_snaps_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, boolean_t defer)
 int
 zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
 {
-       char parent[ZFS_MAXNAMELEN];
+       char parent[ZFS_MAX_DATASET_NAME_LEN];
        int ret;
        char errbuf[1024];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
@@ -3431,7 +3685,7 @@ zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
                        type = ZFS_TYPE_FILESYSTEM;
                }
                if ((props = zfs_valid_proplist(hdl, type, props, zoned,
-                   zhp, errbuf)) == NULL)
+                   zhp, zhp->zpool_hdl, errbuf)) == NULL)
                        return (-1);
        }
 
@@ -3530,7 +3784,7 @@ static int
 zfs_snapshot_cb(zfs_handle_t *zhp, void *arg)
 {
        snapdata_t *sd = arg;
-       char name[ZFS_MAXNAMELEN];
+       char name[ZFS_MAX_DATASET_NAME_LEN];
        int rv = 0;
 
        if (zfs_prop_get_int(zhp, ZFS_PROP_INCONSISTENT) == 0) {
@@ -3557,6 +3811,8 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
        char errbuf[1024];
        nvpair_t *elem;
        nvlist_t *errors;
+       zpool_handle_t *zpool_hdl;
+       char pool[ZFS_MAX_DATASET_NAME_LEN];
 
        (void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
            "cannot create snapshots "));
@@ -3575,11 +3831,24 @@ zfs_snapshot_nvl(libzfs_handle_t *hdl, nvlist_t *snaps, nvlist_t *props)
                }
        }
 
+       /*
+        * get pool handle for prop validation. assumes all snaps are in the
+        * same pool, as does lzc_snapshot (below).
+        */
+       elem = nvlist_next_nvpair(snaps, NULL);
+       (void) strlcpy(pool, nvpair_name(elem), sizeof (pool));
+       pool[strcspn(pool, "/@")] = '\0';
+       zpool_hdl = zpool_open(hdl, pool);
+       if (zpool_hdl == NULL)
+               return (-1);
+
        if (props != NULL &&
            (props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
-           props, B_FALSE, NULL, errbuf)) == NULL) {
+           props, B_FALSE, NULL, zpool_hdl, errbuf)) == NULL) {
+               zpool_close(zpool_hdl);
                return (-1);
        }
+       zpool_close(zpool_hdl);
 
        ret = lzc_snapshot(snaps, props, &errors);
 
@@ -3621,7 +3890,7 @@ zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
 {
        int ret;
        snapdata_t sd = { 0 };
-       char fsname[ZFS_MAXNAMELEN];
+       char fsname[ZFS_MAX_DATASET_NAME_LEN];
        char *cp;
        zfs_handle_t *zhp;
        char errbuf[1024];
@@ -3794,13 +4063,13 @@ int
 zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
     boolean_t force_unmount)
 {
-       int ret;
+       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_MAXNAMELEN];
+       char parent[ZFS_MAX_DATASET_NAME_LEN];
        libzfs_handle_t *hdl = zhp->zfs_hdl;
        char errbuf[1024];
 
@@ -3889,7 +4158,6 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
        }
 
        if (recursive) {
-
                parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
                if (parentname == NULL) {
                        ret = -1;
@@ -3902,8 +4170,7 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                        ret = -1;
                        goto error;
                }
-
-       } else {
+       } else if (zhp->zfs_type != ZFS_TYPE_SNAPSHOT) {
                if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0,
                    force_unmount ? MS_FORCE : 0)) == NULL)
                        return (-1);
@@ -3952,23 +4219,23 @@ zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive,
                 * On failure, we still want to remount any filesystems that
                 * were previously mounted, so we don't alter the system state.
                 */
-               if (!recursive)
+               if (cl != NULL)
                        (void) changelist_postfix(cl);
        } else {
-               if (!recursive) {
+               if (cl != NULL) {
                        changelist_rename(cl, zfs_get_name(zhp), target);
                        ret = changelist_postfix(cl);
                }
        }
 
 error:
-       if (parentname) {
+       if (parentname != NULL) {
                free(parentname);
        }
-       if (zhrp) {
+       if (zhrp != NULL) {
                zfs_close(zhrp);
        }
-       if (cl) {
+       if (cl != NULL) {
                changelist_free(cl);
        }
        return (ret);
@@ -4094,6 +4361,7 @@ void
 zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
 {
        nvpair_t *curr;
+       nvpair_t *next;
 
        /*
         * Keep a reference to the props-table against which we prune the
@@ -4105,7 +4373,7 @@ zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
 
        while (curr) {
                zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
-               nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
+               next = nvlist_next_nvpair(zhp->zfs_props, curr);
 
                /*
                 * User properties will result in ZPROP_INVAL, and since we
@@ -4136,7 +4404,7 @@ zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
        if (cmd == ZFS_SMB_ACL_RENAME) {
                if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
                        (void) no_memory(hdl);
-                       return (-1);
+                       return (0);
                }
        }
 
@@ -4167,8 +4435,7 @@ zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
                return (-1);
        }
        error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
-       if (nvlist)
-               nvlist_free(nvlist);
+       nvlist_free(nvlist);
        return (error);
 }
 
@@ -4222,7 +4489,14 @@ 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[ZFS_MAXNAMELEN + 32];
+                       char errbuf[1024];
+
+                       if ((errno == ENOTSUP &&
+                           (type == ZFS_PROP_USEROBJUSED ||
+                           type == ZFS_PROP_GROUPOBJUSED ||
+                           type == ZFS_PROP_USEROBJQUOTA ||
+                           type == ZFS_PROP_GROUPOBJQUOTA)))
+                               break;
 
                        (void) snprintf(errbuf, sizeof (errbuf),
                            dgettext(TEXT_DOMAIN,
@@ -4256,7 +4530,7 @@ static int
 zfs_hold_one(zfs_handle_t *zhp, void *arg)
 {
        struct holdarg *ha = arg;
-       char name[ZFS_MAXNAMELEN];
+       char name[ZFS_MAX_DATASET_NAME_LEN];
        int rv = 0;
 
        (void) snprintf(name, sizeof (name),
@@ -4375,7 +4649,7 @@ static int
 zfs_release_one(zfs_handle_t *zhp, void *arg)
 {
        struct holdarg *ha = arg;
-       char name[ZFS_MAXNAMELEN];
+       char name[ZFS_MAX_DATASET_NAME_LEN];
        int rv = 0;
        nvlist_t *existing_holds;
 
@@ -4504,7 +4778,7 @@ tryagain:
        zc.zc_nvlist_dst_size = nvsz;
        zc.zc_nvlist_dst = (uintptr_t)nvbuf;
 
-       (void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
+       (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
        if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) != 0) {
                (void) snprintf(errbuf, sizeof (errbuf),