From nobody@FreeBSD.org  Tue May 15 06:10:11 2012
Return-Path: <nobody@FreeBSD.org>
Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34])
	by hub.freebsd.org (Postfix) with ESMTP id 7A34B106564A
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 15 May 2012 06:10:11 +0000 (UTC)
	(envelope-from nobody@FreeBSD.org)
Received: from red.freebsd.org (red.freebsd.org [IPv6:2001:4f8:fff6::22])
	by mx1.freebsd.org (Postfix) with ESMTP id 65A9B8FC12
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 15 May 2012 06:10:11 +0000 (UTC)
Received: from red.freebsd.org (localhost [127.0.0.1])
	by red.freebsd.org (8.14.4/8.14.4) with ESMTP id q4F6AAEw090230
	for <freebsd-gnats-submit@FreeBSD.org>; Tue, 15 May 2012 06:10:10 GMT
	(envelope-from nobody@red.freebsd.org)
Received: (from nobody@localhost)
	by red.freebsd.org (8.14.4/8.14.4/Submit) id q4F6AAj0090229;
	Tue, 15 May 2012 06:10:10 GMT
	(envelope-from nobody)
Message-Id: <201205150610.q4F6AAj0090229@red.freebsd.org>
Date: Tue, 15 May 2012 06:10:10 GMT
From: vermaden <vermaden@interia.pl>
To: freebsd-gnats-submit@FreeBSD.org
Subject: zfs set canmount=on UNMOUNTS dataset
X-Send-Pr-Version: www-3.1
X-GNATS-Notify: bryan@shatow.net

>Number:         167905
>Category:       kern
>Synopsis:       [zfs] zfs set canmount=on UNMOUNTS dataset
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    mm
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue May 15 06:20:02 UTC 2012
>Closed-Date:    Fri Jun 22 20:41:27 UTC 2012
>Last-Modified:  Thu Jul 12 06:40:02 UTC 2012
>Originator:     vermaden
>Release:        9.0-RELEASE
>Organization:
>Environment:
9.0-RELEASE
>Description:
Its possible to change CANMOUNT from ON to NOAUTO on a mounted filesystem, but its not possible to change CANMOUNT from NOAUTO to ON as it REMOUNTS that dataset even if its ALREADY MOUNTED.
>How-To-Repeat:
# mount | head -1
zroot/ROOT/default on / (zfs, local, noatime, nfsv4acls)

# zfs get canmount zroot/ROOT/default
NAME                PROPERTY  VALUE     SOURCE
zroot/ROOT/default  canmount  noauto    local

# zfs set canmount=on zroot/ROOT/default
cannot unmount '/': Invalid argument

>Fix:
Do not remount dataset after setting CANMOUNT to ON from NOAUTO when its already mounted.

>Release-Note:
>Audit-Trail:
Responsible-Changed-From-To: freebsd-bugs->freebsd-fs 
Responsible-Changed-By: linimon 
Responsible-Changed-When: Wed May 16 02:33:39 UTC 2012 
Responsible-Changed-Why:  
Over to maintainer(s). 

http://www.freebsd.org/cgi/query-pr.cgi?pr=167905 

From: Bryan Drewery <bryan@shatow.net>
To: bug-followup@FreeBSD.org, vermaden@interia.pl
Cc: Pawel Jakub Dawidek <pjd@FreeBSD.org>, mm@freebsd.org
Subject: Re: kern/167905: [zfs] zfs set canmount=on UNMOUNTS dataset
Date: Tue, 12 Jun 2012 00:35:58 -0500

 This is a multi-part message in MIME format.
 --------------040108060500020307070404
 Content-Type: text/plain; charset=ISO-8859-1
 Content-Transfer-Encoding: 7bit
 
 Attached is a patch to fix setting 'zfs set canmount=on' to not cause a
 remount if the dataset is *already mounted*. This fixes the issue
 reported here, as well as here
 http://lists.freebsd.org/pipermail/freebsd-fs/2012-May/014241.html
 
 $ cd /usr/src/cddl
 $ patch -p1 < patch-zfs-dataset-canmount-on.txt
 $ make obj depend all install
 
 The change adds to the complex condition as I did not want to refactor
 it too much given the unclear "contrib" status of the code.
 
 Also attached is a test script to see the functionality before and after.
 
 I did some research and neither OpenIndiana/Illumos nor ZfsOnLinux have
 addressed this issue. Not sure the proper way to share or report this
 "upstream" currently.
 
 Regards,
 Bryan Drewery
 
 --------------040108060500020307070404
 Content-Type: text/plain; charset=windows-1252;
  name="patch-zfs-dataset-canmount-on.txt"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="patch-zfs-dataset-canmount-on.txt"
 
 --- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c.orig	2012-06-12 00:10:11.000000000 -0500
 +++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	2012-06-12 00:17:34.000000000 -0500
 @@ -1467,11 +1467,12 @@
  
  	/*
  	 * If the dataset's canmount property is being set to noauto,
 +	 * or to on and already mounted,
  	 * then we want to prevent unmounting & remounting it.
  	 */
  	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
  	    (zprop_string_to_index(prop, propval, &idx,
 -	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
 +	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO || (idx == ZFS_CANMOUNT_ON && zfs_is_mounted(zhp, NULL))));
  
  	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
  		goto error;
 
 --------------040108060500020307070404
 Content-Type: text/plain; charset=windows-1252;
  name="test-zsh.sh"
 Content-Transfer-Encoding: 7bit
 Content-Disposition: attachment;
  filename="test-zsh.sh"
 
 #! /bin/sh
 set -e
 
 cleanup() {
 	test -z "${PASS}" && echo "FAILED"
 	cd /
 	zpool destroy zpool
 	mdconfig -d -u 0
 	mdconfig -d -u 1
 }
 
 run() {
 	echo "$@"
 	"$@"
 	return $?
 }
 
 trap cleanup SIGINT SIGTERM SIGKILL EXIT
 
 
 rm -rf /tmp/test
 mdconfig -a -t swap -s 128M -u 0
 mdconfig -a -t swap -s 128M -u 1
 zpool create zpool mirror md0 md1
 zfs create -o mountpoint=/tmp/test -o canmount=on zpool/test 
 cd /tmp/test
 
 # This fails even though the property is not changing and dataset is already mounted
 run zfs set canmount=on zpool/test
 # Currently works
 run zfs set canmount=noauto zpool/test
 # Expect this to try to unmount and fail, may warrant more discussion
 #run zfs set canmount=off zpool/test
 # This fails even though the property is not changing and dataset is already mounted
 run zfs set canmount=on zpool/test
 
 echo "PASS"
 PASS=1
 
 --------------040108060500020307070404--

From: vermaden <vermaden@interia.pl>
To: Bryan Drewery <bryan@shatow.net>
Cc: bug-followup@FreeBSD.org, Pawel Jakub Dawidek <pjd@FreeBSD.org>,
	mm@freebsd.org
Subject: Re: kern/167905: [zfs] zfs set canmount=on UNMOUNTS dataset
Date: Wed, 13 Jun 2012 08:37:24 +0200

 --=-eOAg1opG/d7eXMURa76A
 Content-Type: text/plain; charset="UTF-8"
 Content-Transfer-Encoding: quoted-printable
 
 Hi,
 
 at last I had some time to check Your work, but the patch did not applied t=
 o 9-STABLE @ r236934:
 
 / # cd /usr/src/cddl=20
  /usr/src/cddl # patch -p1 < /root/patch-zfs-dataset-canmount-on
 Hmm...  Looks like a unified diff to me...
 The text leading up to this was:
 --------------------------
 |--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c.orig   201=
 2-06-12 00:10:11.000000000 -0500
 |+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c        201=
 2-06-12 00:17:34.000000000 -0500
 --------------------------
 Patching file contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c using =
 Plan A...
 Hunk #1 failed at 1467.
 1 out of 1 hunks failed--saving rejects to contrib/opensolaris/lib/libzfs/c=
 ommon/libzfs_dataset.c.rej
 done
 
 Regards,
 vermaden
 
 
 "Bryan Drewery" <bryan@shatow.net> pisze:
 > Attached is a patch to fix setting 'zfs set canmount=3Don' to not cause a
 > remount if the dataset is *already mounted*. This fixes the issue
 > reported here, as well as here
 > http://lists.freebsd.org/pipermail/freebsd-fs/2012-May/014241.html
 >=20
 > $ cd /usr/src/cddl
 > $ patch -p1 < patch-zfs-dataset-canmount-on.txt
 > $ make obj depend all install
 >=20
 > The change adds to the complex condition as I did not want to refactor
 > it too much given the unclear "contrib" status of the code.
 >=20
 > Also attached is a test script to see the functionality before and after.
 >=20
 > I did some research and neither OpenIndiana/Illumos nor ZfsOnLinux have
 > addressed this issue. Not sure the proper way to share or report this
 > "upstream" currently.
 >=20
 > Regards,
 > Bryan Drewery
 >=20
 
 
 
 --=20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 ...
 
 --=-eOAg1opG/d7eXMURa76A
 Content-Type: text/plain; name="libzfs_dataset.c.rej"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="libzfs_dataset.c.rej"
 
 ***************
 *** 1467,1477 ****
  =20
   	/*
   	 * If the dataset's canmount property is being set to noauto,
   	 * then we want to prevent unmounting & remounting it.
   	 */
   	do_prefix =3D !((prop =3D=3D ZFS_PROP_CANMOUNT) &&
   	    (zprop_string_to_index(prop, propval, &idx,
 - 	    ZFS_TYPE_DATASET) =3D=3D 0) && (idx =3D=3D ZFS_CANMOUNT_NOAUTO));
  =20
   	if (do_prefix && (ret =3D changelist_prefix(cl)) !=3D 0)
   		goto error;
 --- 1467,1478 ----
  =20
   	/*
   	 * If the dataset's canmount property is being set to noauto,
 + 	 * or to on and already mounted,
   	 * then we want to prevent unmounting & remounting it.
   	 */
   	do_prefix =3D !((prop =3D=3D ZFS_PROP_CANMOUNT) &&
   	    (zprop_string_to_index(prop, propval, &idx,
 + 	    ZFS_TYPE_DATASET) =3D=3D 0) && (idx =3D=3D ZFS_CANMOUNT_NOAUTO || (i=
 dx =3D=3D ZFS_CANMOUNT_ON && zfs_is_mounted(zhp, NULL))));
  =20
   	if (do_prefix && (ret =3D changelist_prefix(cl)) !=3D 0)
   		goto error;
 
 
 --=-eOAg1opG/d7eXMURa76A
 Content-Type: text/plain; name="libzfs_dataset.c.orig"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="libzfs_dataset.c.orig"
 
 /*
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
  * Common Development and Distribution License (the "License").
  * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
  * See the License for the specific language governing permissions
  * and limitations under the License.
  *
  * When distributing Covered Code, include this CDDL HEADER in each
  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  * If applicable, add the following below this CDDL HEADER, with the
  * fields enclosed by brackets "[]" replaced with your own identifying
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reser=
 ved.
  * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
  * All rights reserved.
  * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
  */
 
 #include <ctype.h>
 #include <errno.h>
 #include <libintl.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
 #include <unistd.h>
 #include <stddef.h>
 #include <zone.h>
 #include <fcntl.h>
 #include <sys/mntent.h>
 #include <sys/mount.h>
 #include <priv.h>
 #include <pwd.h>
 #include <grp.h>
 #include <stddef.h>
 #include <idmap.h>
 
 #include <sys/dnode.h>
 #include <sys/spa.h>
 #include <sys/zap.h>
 #include <sys/misc.h>
 #include <libzfs.h>
 
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
 #include "libzfs_impl.h"
 #include "zfs_deleg.h"
 
 static int userquota_propname_decode(const char *propname, boolean_t zoned,
     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *rid=
 p);
 
 /*
  * Given a single type (not a mask of types), return the type in a human
  * readable form.
  */
 const char *
 zfs_type_to_name(zfs_type_t type)
 {
 	switch (type) {
 	case ZFS_TYPE_FILESYSTEM:
 		return (dgettext(TEXT_DOMAIN, "filesystem"));
 	case ZFS_TYPE_SNAPSHOT:
 		return (dgettext(TEXT_DOMAIN, "snapshot"));
 	case ZFS_TYPE_VOLUME:
 		return (dgettext(TEXT_DOMAIN, "volume"));
 	}
 
 	return (NULL);
 }
 
 /*
  * Given a path and mask of ZFS types, return a string describing this data=
 set.
  * This is used when we fail to open a dataset and we cannot get an exact t=
 ype.
  * We guess what the type would have been based on the path and the mask of
  * acceptable types.
  */
 static const char *
 path_to_str(const char *path, int types)
 {
 	/*
 	 * When given a single type, always report the exact type.
 	 */
 	if (types =3D=3D ZFS_TYPE_SNAPSHOT)
 		return (dgettext(TEXT_DOMAIN, "snapshot"));
 	if (types =3D=3D ZFS_TYPE_FILESYSTEM)
 		return (dgettext(TEXT_DOMAIN, "filesystem"));
 	if (types =3D=3D ZFS_TYPE_VOLUME)
 		return (dgettext(TEXT_DOMAIN, "volume"));
 
 	/*
 	 * The user is requesting more than one type of dataset.  If this is the
 	 * case, consult the path itself.  If we're looking for a snapshot, and
 	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
 	 * snapshot attribute and try again.
 	 */
 	if (types & ZFS_TYPE_SNAPSHOT) {
 		if (strchr(path, '@') !=3D NULL)
 			return (dgettext(TEXT_DOMAIN, "snapshot"));
 		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
 	}
 
 	/*
 	 * The user has requested either filesystems or volumes.
 	 * We have no way of knowing a priori what type this would be, so always
 	 * report it as "filesystem" or "volume", our two primitive types.
 	 */
 	if (types & ZFS_TYPE_FILESYSTEM)
 		return (dgettext(TEXT_DOMAIN, "filesystem"));
 
 	assert(types & ZFS_TYPE_VOLUME);
 	return (dgettext(TEXT_DOMAIN, "volume"));
 }
 
 /*
  * Validate a ZFS path.  This is used even before trying to open the datase=
 t, to
  * provide a more meaningful error message.  We call zfs_error_aux() to
  * explain exactly why the name was not valid.
  */
 int
 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
     boolean_t modifying)
 {
 	namecheck_err_t why;
 	char what;
 
 	(void) zfs_prop_get_table();
 	if (dataset_namecheck(path, &why, &what) !=3D 0) {
 		if (hdl !=3D NULL) {
 			switch (why) {
 			case NAME_ERR_TOOLONG:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "name is too long"));
 				break;
 
 			case NAME_ERR_LEADING_SLASH:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "leading slash in name"));
 				break;
 
 			case NAME_ERR_EMPTY_COMPONENT:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "empty component in name"));
 				break;
 
 			case NAME_ERR_TRAILING_SLASH:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "trailing slash in name"));
 				break;
 
 			case NAME_ERR_INVALCHAR:
 				zfs_error_aux(hdl,
 				    dgettext(TEXT_DOMAIN, "invalid character "
 				    "'%c' in name"), what);
 				break;
 
 			case NAME_ERR_MULTIPLE_AT:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "multiple '@' delimiters in name"));
 				break;
 
 			case NAME_ERR_NOLETTER:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "pool doesn't begin with a letter"));
 				break;
 
 			case NAME_ERR_RESERVED:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "name is reserved"));
 				break;
 
 			case NAME_ERR_DISKLIKE:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "reserved disk name"));
 				break;
 			}
 		}
 
 		return (0);
 	}
 
 	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') !=3D NULL) {
 		if (hdl !=3D NULL)
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "snapshot delimiter '@' in filesystem name"));
 		return (0);
 	}
 
 	if (type =3D=3D ZFS_TYPE_SNAPSHOT && strchr(path, '@') =3D=3D NULL) {
 		if (hdl !=3D NULL)
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "missing '@' delimiter in snapshot name"));
 		return (0);
 	}
 
 	if (modifying && strchr(path, '%') !=3D NULL) {
 		if (hdl !=3D NULL)
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid character %c in name"), '%');
 		return (0);
 	}
 
 	return (-1);
 }
 
 int
 zfs_name_valid(const char *name, zfs_type_t type)
 {
 	if (type =3D=3D ZFS_TYPE_POOL)
 		return (zpool_name_valid(NULL, B_FALSE, name));
 	return (zfs_validate_name(NULL, name, type, B_FALSE));
 }
 
 /*
  * This function takes the raw DSL properties, and filters out the user-def=
 ined
  * properties into a separate nvlist.
  */
 static nvlist_t *
 process_user_props(zfs_handle_t *zhp, nvlist_t *props)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	nvpair_t *elem;
 	nvlist_t *propval;
 	nvlist_t *nvl;
 
 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) !=3D 0) {
 		(void) no_memory(hdl);
 		return (NULL);
 	}
 
 	elem =3D NULL;
 	while ((elem =3D nvlist_next_nvpair(props, elem)) !=3D NULL) {
 		if (!zfs_prop_user(nvpair_name(elem)))
 			continue;
 
 		verify(nvpair_value_nvlist(elem, &propval) =3D=3D 0);
 		if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) !=3D 0) {
 			nvlist_free(nvl);
 			(void) no_memory(hdl);
 			return (NULL);
 		}
 	}
 
 	return (nvl);
 }
 
 static zpool_handle_t *
 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zpool_handle_t *zph;
 
 	if ((zph =3D zpool_open_canfail(hdl, pool_name)) !=3D NULL) {
 		if (hdl->libzfs_pool_handles !=3D NULL)
 			zph->zpool_next =3D hdl->libzfs_pool_handles;
 		hdl->libzfs_pool_handles =3D zph;
 	}
 	return (zph);
 }
 
 static zpool_handle_t *
 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zpool_handle_t *zph =3D hdl->libzfs_pool_handles;
 
 	while ((zph !=3D NULL) &&
 	    (strncmp(pool_name, zpool_get_name(zph), len) !=3D 0))
 		zph =3D zph->zpool_next;
 	return (zph);
 }
 
 /*
  * Returns a handle to the pool that contains the provided dataset.
  * If a handle to that pool already exists then that handle is returned.
  * Otherwise, a new handle is created and added to the list of handles.
  */
 static zpool_handle_t *
 zpool_handle(zfs_handle_t *zhp)
 {
 	char *pool_name;
 	int len;
 	zpool_handle_t *zph;
 
 	len =3D strcspn(zhp->zfs_name, "/@") + 1;
 	pool_name =3D zfs_alloc(zhp->zfs_hdl, len);
 	(void) strlcpy(pool_name, zhp->zfs_name, len);
 
 	zph =3D zpool_find_handle(zhp, pool_name, len);
 	if (zph =3D=3D NULL)
 		zph =3D zpool_add_handle(zhp, pool_name);
 
 	free(pool_name);
 	return (zph);
 }
 
 void
 zpool_free_handles(libzfs_handle_t *hdl)
 {
 	zpool_handle_t *next, *zph =3D hdl->libzfs_pool_handles;
 
 	while (zph !=3D NULL) {
 		next =3D zph->zpool_next;
 		zpool_close(zph);
 		zph =3D next;
 	}
 	hdl->libzfs_pool_handles =3D NULL;
 }
 
 /*
  * Utility function to gather stats (objset and zpl) for the given object.
  */
 static int
 get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 
 	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
 
 	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) !=3D 0) {
 		if (errno =3D=3D ENOMEM) {
 			if (zcmd_expand_dst_nvlist(hdl, zc) !=3D 0) {
 				return (-1);
 			}
 		} else {
 			return (-1);
 		}
 	}
 	return (0);
 }
 
 /*
  * Utility function to get the received properties of the given object.
  */
 static int
 get_recvd_props_ioctl(zfs_handle_t *zhp)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	nvlist_t *recvdprops;
 	zfs_cmd_t zc =3D { 0 };
 	int err;
 
 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) !=3D 0)
 		return (-1);
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) !=3D 0) {
 		if (errno =3D=3D ENOMEM) {
 			if (zcmd_expand_dst_nvlist(hdl, &zc) !=3D 0) {
 				return (-1);
 			}
 		} else {
 			zcmd_free_nvlists(&zc);
 			return (-1);
 		}
 	}
 
 	err =3D zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
 	zcmd_free_nvlists(&zc);
 	if (err !=3D 0)
 		return (-1);
 
 	nvlist_free(zhp->zfs_recvd_props);
 	zhp->zfs_recvd_props =3D recvdprops;
 
 	return (0);
 }
 
 static int
 put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
 {
 	nvlist_t *allprops, *userprops;
 
 	zhp->zfs_dmustats =3D zc->zc_objset_stats; /* structure assignment */
 
 	if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) !=3D 0) {
 		return (-1);
 	}
 
 	/*
 	 * XXX Why do we store the user props separately, in addition to
 	 * storing them in zfs_props?
 	 */
 	if ((userprops =3D process_user_props(zhp, allprops)) =3D=3D NULL) {
 		nvlist_free(allprops);
 		return (-1);
 	}
 
 	nvlist_free(zhp->zfs_props);
 	nvlist_free(zhp->zfs_user_props);
 
 	zhp->zfs_props =3D allprops;
 	zhp->zfs_user_props =3D userprops;
 
 	return (0);
 }
 
 static int
 get_stats(zfs_handle_t *zhp)
 {
 	int rc =3D 0;
 	zfs_cmd_t zc =3D { 0 };
 
 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) !=3D 0)
 		return (-1);
 	if (get_stats_ioctl(zhp, &zc) !=3D 0)
 		rc =3D -1;
 	else if (put_stats_zhdl(zhp, &zc) !=3D 0)
 		rc =3D -1;
 	zcmd_free_nvlists(&zc);
 	return (rc);
 }
 
 /*
  * Refresh the properties currently stored in the handle.
  */
 void
 zfs_refresh_properties(zfs_handle_t *zhp)
 {
 	(void) get_stats(zhp);
 }
 
 /*
  * Makes a handle from the given dataset name.  Used by zfs_open() and
  * zfs_iter_* to create child handles on the fly.
  */
 static int
 make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
 {
 	if (put_stats_zhdl(zhp, zc) !=3D 0)
 		return (-1);
 
 	/*
 	 * We've managed to open the dataset and gather statistics.  Determine
 	 * the high-level type.
 	 */
 	if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZVOL)
 		zhp->zfs_head_type =3D ZFS_TYPE_VOLUME;
 	else if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZFS)
 		zhp->zfs_head_type =3D ZFS_TYPE_FILESYSTEM;
 	else
 		abort();
 
 	if (zhp->zfs_dmustats.dds_is_snapshot)
 		zhp->zfs_type =3D ZFS_TYPE_SNAPSHOT;
 	else if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZVOL)
 		zhp->zfs_type =3D ZFS_TYPE_VOLUME;
 	else if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZFS)
 		zhp->zfs_type =3D ZFS_TYPE_FILESYSTEM;
 	else
 		abort();	/* we should never see any other types */
 
 	if ((zhp->zpool_hdl =3D zpool_handle(zhp)) =3D=3D NULL)
 		return (-1);
 
 	return (0);
 }
 
 zfs_handle_t *
 make_dataset_handle(libzfs_handle_t *hdl, const char *path)
 {
 	zfs_cmd_t zc =3D { 0 };
 
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D hdl;
 	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) !=3D 0) {
 		free(zhp);
 		return (NULL);
 	}
 	if (get_stats_ioctl(zhp, &zc) =3D=3D -1) {
 		zcmd_free_nvlists(&zc);
 		free(zhp);
 		return (NULL);
 	}
 	if (make_dataset_handle_common(zhp, &zc) =3D=3D -1) {
 		free(zhp);
 		zhp =3D NULL;
 	}
 	zcmd_free_nvlists(&zc);
 	return (zhp);
 }
 
 zfs_handle_t *
 make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
 {
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D hdl;
 	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
 	if (make_dataset_handle_common(zhp, zc) =3D=3D -1) {
 		free(zhp);
 		return (NULL);
 	}
 	return (zhp);
 }
 
 zfs_handle_t *
 make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
 {
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D pzhp->zfs_hdl;
 	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
 	zhp->zfs_head_type =3D pzhp->zfs_type;
 	zhp->zfs_type =3D ZFS_TYPE_SNAPSHOT;
 	zhp->zpool_hdl =3D zpool_handle(zhp);
 	return (zhp);
 }
 
 zfs_handle_t *
 zfs_handle_dup(zfs_handle_t *zhp_orig)
 {
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D zhp_orig->zfs_hdl;
 	zhp->zpool_hdl =3D zhp_orig->zpool_hdl;
 	(void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
 	    sizeof (zhp->zfs_name));
 	zhp->zfs_type =3D zhp_orig->zfs_type;
 	zhp->zfs_head_type =3D zhp_orig->zfs_head_type;
 	zhp->zfs_dmustats =3D zhp_orig->zfs_dmustats;
 	if (zhp_orig->zfs_props !=3D NULL) {
 		if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) !=3D 0) {
 			(void) no_memory(zhp->zfs_hdl);
 			zfs_close(zhp);
 			return (NULL);
 		}
 	}
 	if (zhp_orig->zfs_user_props !=3D NULL) {
 		if (nvlist_dup(zhp_orig->zfs_user_props,
 		    &zhp->zfs_user_props, 0) !=3D 0) {
 			(void) no_memory(zhp->zfs_hdl);
 			zfs_close(zhp);
 			return (NULL);
 		}
 	}
 	if (zhp_orig->zfs_recvd_props !=3D NULL) {
 		if (nvlist_dup(zhp_orig->zfs_recvd_props,
 		    &zhp->zfs_recvd_props, 0)) {
 			(void) no_memory(zhp->zfs_hdl);
 			zfs_close(zhp);
 			return (NULL);
 		}
 	}
 	zhp->zfs_mntcheck =3D zhp_orig->zfs_mntcheck;
 	if (zhp_orig->zfs_mntopts !=3D NULL) {
 		zhp->zfs_mntopts =3D zfs_strdup(zhp_orig->zfs_hdl,
 		    zhp_orig->zfs_mntopts);
 	}
 	zhp->zfs_props_table =3D zhp_orig->zfs_props_table;
 	return (zhp);
 }
 
 /*
  * Opens the given snapshot, 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.
  */
 zfs_handle_t *
 zfs_open(libzfs_handle_t *hdl, const char *path, int types)
 {
 	zfs_handle_t *zhp;
 	char errbuf[1024];
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
 
 	/*
 	 * 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"));
 		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 		return (NULL);
 	}
 
 	/*
 	 * Try to get stats for the dataset, which will tell us if it exists.
 	 */
 	errno =3D 0;
 	if ((zhp =3D make_dataset_handle(hdl, path)) =3D=3D NULL) {
 		(void) zfs_standard_error(hdl, errno, errbuf);
 		return (NULL);
 	}
 
 	if (!(types & zhp->zfs_type)) {
 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
 		zfs_close(zhp);
 		return (NULL);
 	}
 
 	return (zhp);
 }
 
 /*
  * Release a ZFS handle.  Nothing to do but free the associated memory.
  */
 void
 zfs_close(zfs_handle_t *zhp)
 {
 	if (zhp->zfs_mntopts)
 		free(zhp->zfs_mntopts);
 	nvlist_free(zhp->zfs_props);
 	nvlist_free(zhp->zfs_user_props);
 	nvlist_free(zhp->zfs_recvd_props);
 	free(zhp);
 }
 
 typedef struct mnttab_node {
 	struct mnttab mtn_mt;
 	avl_node_t mtn_node;
 } mnttab_node_t;
 
 static int
 libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
 {
 	const mnttab_node_t *mtn1 =3D arg1;
 	const mnttab_node_t *mtn2 =3D arg2;
 	int rv;
 
 	rv =3D strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
 
 	if (rv =3D=3D 0)
 		return (0);
 	return (rv > 0 ? 1 : -1);
 }
 
 void
 libzfs_mnttab_init(libzfs_handle_t *hdl)
 {
 	assert(avl_numnodes(&hdl->libzfs_mnttab_cache) =3D=3D 0);
 	avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
 	    sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
 }
 
 void
 libzfs_mnttab_update(libzfs_handle_t *hdl)
 {
 	struct mnttab entry;
 
 	rewind(hdl->libzfs_mnttab);
 	while (getmntent(hdl->libzfs_mnttab, &entry) =3D=3D 0) {
 		mnttab_node_t *mtn;
 
 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) !=3D 0)
 			continue;
 		mtn =3D zfs_alloc(hdl, sizeof (mnttab_node_t));
 		mtn->mtn_mt.mnt_special =3D zfs_strdup(hdl, entry.mnt_special);
 		mtn->mtn_mt.mnt_mountp =3D zfs_strdup(hdl, entry.mnt_mountp);
 		mtn->mtn_mt.mnt_fstype =3D zfs_strdup(hdl, entry.mnt_fstype);
 		mtn->mtn_mt.mnt_mntopts =3D zfs_strdup(hdl, entry.mnt_mntopts);
 		avl_add(&hdl->libzfs_mnttab_cache, mtn);
 	}
 }
 
 void
 libzfs_mnttab_fini(libzfs_handle_t *hdl)
 {
 	void *cookie =3D NULL;
 	mnttab_node_t *mtn;
 
 	while (mtn =3D avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
 		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);
 	}
 	avl_destroy(&hdl->libzfs_mnttab_cache);
 }
 
 void
 libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
 {
 	hdl->libzfs_mnttab_enable =3D enable;
 }
 
 int
 libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
     struct mnttab *entry)
 {
 	mnttab_node_t find;
 	mnttab_node_t *mtn;
 
 	if (!hdl->libzfs_mnttab_enable) {
 		struct mnttab srch =3D { 0 };
 
 		if (avl_numnodes(&hdl->libzfs_mnttab_cache))
 			libzfs_mnttab_fini(hdl);
 		rewind(hdl->libzfs_mnttab);
 		srch.mnt_special =3D (char *)fsname;
 		srch.mnt_fstype =3D MNTTYPE_ZFS;
 		if (getmntany(hdl->libzfs_mnttab, entry, &srch) =3D=3D 0)
 			return (0);
 		else
 			return (ENOENT);
 	}
 
 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) =3D=3D 0)
 		libzfs_mnttab_update(hdl);
 
 	find.mtn_mt.mnt_special =3D (char *)fsname;
 	mtn =3D avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
 	if (mtn) {
 		*entry =3D mtn->mtn_mt;
 		return (0);
 	}
 	return (ENOENT);
 }
 
 void
 libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
     const char *mountp, const char *mntopts)
 {
 	mnttab_node_t *mtn;
 
 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) =3D=3D 0)
 		return;
 	mtn =3D zfs_alloc(hdl, sizeof (mnttab_node_t));
 	mtn->mtn_mt.mnt_special =3D zfs_strdup(hdl, special);
 	mtn->mtn_mt.mnt_mountp =3D zfs_strdup(hdl, mountp);
 	mtn->mtn_mt.mnt_fstype =3D zfs_strdup(hdl, MNTTYPE_ZFS);
 	mtn->mtn_mt.mnt_mntopts =3D zfs_strdup(hdl, mntopts);
 	avl_add(&hdl->libzfs_mnttab_cache, mtn);
 }
 
 void
 libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
 {
 	mnttab_node_t find;
 	mnttab_node_t *ret;
 
 	find.mtn_mt.mnt_special =3D (char *)fsname;
 	if (ret =3D avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
 		avl_remove(&hdl->libzfs_mnttab_cache, ret);
 		free(ret->mtn_mt.mnt_special);
 		free(ret->mtn_mt.mnt_mountp);
 		free(ret->mtn_mt.mnt_fstype);
 		free(ret->mtn_mt.mnt_mntopts);
 		free(ret);
 	}
 }
 
 int
 zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
 {
 	zpool_handle_t *zpool_handle =3D zhp->zpool_hdl;
 
 	if (zpool_handle =3D=3D NULL)
 		return (-1);
 
 	*spa_version =3D zpool_get_prop_int(zpool_handle,
 	    ZPOOL_PROP_VERSION, NULL);
 	return (0);
 }
 
 /*
  * The choice of reservation property depends on the SPA version.
  */
 static int
 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
 {
 	int spa_version;
 
 	if (zfs_spa_version(zhp, &spa_version) < 0)
 		return (-1);
 
 	if (spa_version >=3D SPA_VERSION_REFRESERVATION)
 		*resv_prop =3D ZFS_PROP_REFRESERVATION;
 	else
 		*resv_prop =3D ZFS_PROP_RESERVATION;
 
 	return (0);
 }
 
 /*
  * Given an nvlist of properties to set, validates that they are correct, a=
 nd
  * parses any numeric properties (index, boolean, etc) if they are specifie=
 d as
  * strings.
  */
 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)
 {
 	nvpair_t *elem;
 	uint64_t intval;
 	char *strval;
 	zfs_prop_t prop;
 	nvlist_t *ret;
 	int chosen_normal =3D -1;
 	int chosen_utf =3D -1;
 
 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) !=3D 0) {
 		(void) no_memory(hdl);
 		return (NULL);
 	}
 
 	/*
 	 * Make sure this property is valid and applies to this type.
 	 */
 
 	elem =3D NULL;
 	while ((elem =3D nvlist_next_nvpair(nvl, elem)) !=3D NULL) {
 		const char *propname =3D nvpair_name(elem);
 
 		prop =3D zfs_name_to_prop(propname);
 		if (prop =3D=3D ZPROP_INVAL && zfs_prop_user(propname)) {
 			/*
 			 * This is a user property: make sure it's a
 			 * string, and that it's less than ZAP_MAXNAMELEN.
 			 */
 			if (nvpair_type(elem) !=3D DATA_TYPE_STRING) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' must be a string"), propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			if (strlen(nvpair_name(elem)) >=3D ZAP_MAXNAMELEN) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "property name '%s' is too long"),
 				    propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			(void) nvpair_value_string(elem, &strval);
 			if (nvlist_add_string(ret, propname, strval) !=3D 0) {
 				(void) no_memory(hdl);
 				goto error;
 			}
 			continue;
 		}
 
 		/*
 		 * Currently, only user properties can be modified on
 		 * snapshots.
 		 */
 		if (type =3D=3D ZFS_TYPE_SNAPSHOT) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "this property can not be modified for snapshots"));
 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
 			goto error;
 		}
 
 		if (prop =3D=3D ZPROP_INVAL && zfs_prop_userquota(propname)) {
 			zfs_userquota_prop_t uqtype;
 			char newpropname[128];
 			char domain[128];
 			uint64_t rid;
 			uint64_t valary[3];
 
 			if (userquota_propname_decode(propname, zoned,
 			    &uqtype, domain, sizeof (domain), &rid) !=3D 0) {
 				zfs_error_aux(hdl,
 				    dgettext(TEXT_DOMAIN,
 				    "'%s' has an invalid user/group name"),
 				    propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			if (uqtype !=3D ZFS_PROP_USERQUOTA &&
 			    uqtype !=3D ZFS_PROP_GROUPQUOTA) {
 				zfs_error_aux(hdl,
 				    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
 				    propname);
 				(void) zfs_error(hdl, EZFS_PROPREADONLY,
 				    errbuf);
 				goto error;
 			}
 
 			if (nvpair_type(elem) =3D=3D DATA_TYPE_STRING) {
 				(void) nvpair_value_string(elem, &strval);
 				if (strcmp(strval, "none") =3D=3D 0) {
 					intval =3D 0;
 				} else if (zfs_nicestrtonum(hdl,
 				    strval, &intval) !=3D 0) {
 					(void) zfs_error(hdl,
 					    EZFS_BADPROP, errbuf);
 					goto error;
 				}
 			} else if (nvpair_type(elem) =3D=3D
 			    DATA_TYPE_UINT64) {
 				(void) nvpair_value_uint64(elem, &intval);
 				if (intval =3D=3D 0) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "use 'none' to disable "
 					    "userquota/groupquota"));
 					goto error;
 				}
 			} else {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' must be a number"), propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			/*
 			 * Encode the prop name as
 			 * 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],
 			    (longlong_t)rid, domain);
 			valary[0] =3D uqtype;
 			valary[1] =3D rid;
 			valary[2] =3D intval;
 			if (nvlist_add_uint64_array(ret, newpropname,
 			    valary, 3) !=3D 0) {
 				(void) no_memory(hdl);
 				goto error;
 			}
 			continue;
 		} else if (prop =3D=3D ZPROP_INVAL && zfs_prop_written(propname)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "'%s' is readonly"),
 			    propname);
 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
 			goto error;
 		}
 
 		if (prop =3D=3D ZPROP_INVAL) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid property '%s'"), propname);
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 			goto error;
 		}
 
 		if (!zfs_prop_valid_for_type(prop, type)) {
 			zfs_error_aux(hdl,
 			    dgettext(TEXT_DOMAIN, "'%s' does not "
 			    "apply to datasets of this type"), propname);
 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
 			goto error;
 		}
 
 		if (zfs_prop_readonly(prop) &&
 		    (!zfs_prop_setonce(prop) || zhp !=3D NULL)) {
 			zfs_error_aux(hdl,
 			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
 			    propname);
 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
 			goto error;
 		}
 
 		if (zprop_parse_value(hdl, elem, prop, type, ret,
 		    &strval, &intval, errbuf) !=3D 0)
 			goto error;
 
 		/*
 		 * Perform some additional checks for specific properties.
 		 */
 		switch (prop) {
 		case ZFS_PROP_VERSION:
 		{
 			int version;
 
 			if (zhp =3D=3D NULL)
 				break;
 			version =3D zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
 			if (intval < version) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "Can not downgrade; already at version %u"),
 				    version);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 			break;
 		}
 
 		case ZFS_PROP_RECORDSIZE:
 		case ZFS_PROP_VOLBLOCKSIZE:
 			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
 			if (intval < SPA_MINBLOCKSIZE ||
 			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' must be power of 2 from %u "
 				    "to %uk"), propname,
 				    (uint_t)SPA_MINBLOCKSIZE,
 				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 			break;
 
 		case ZFS_PROP_MLSLABEL:
 		{
 #ifdef sun
 			/*
 			 * Verify the mlslabel string and convert to
 			 * internal hex label string.
 			 */
 
 			m_label_t *new_sl;
 			char *hex =3D NULL;	/* internal label string */
 
 			/* Default value is already OK. */
 			if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) =3D=3D 0)
 				break;
 
 			/* Verify the label can be converted to binary form */
 			if (((new_sl =3D m_label_alloc(MAC_LABEL)) =3D=3D NULL) ||
 			    (str_to_label(strval, &new_sl, MAC_LABEL,
 			    L_NO_CORRECTION, NULL) =3D=3D -1)) {
 				goto badlabel;
 			}
 
 			/* Now translate to hex internal label string */
 			if (label_to_str(new_sl, &hex, M_INTERNAL,
 			    DEF_NAMES) !=3D 0) {
 				if (hex)
 					free(hex);
 				goto badlabel;
 			}
 			m_label_free(new_sl);
 
 			/* If string is already in internal form, we're done. */
 			if (strcmp(strval, hex) =3D=3D 0) {
 				free(hex);
 				break;
 			}
 
 			/* 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) =3D=3D 0);
 			free(hex);
 
 			break;
 
 badlabel:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid mlslabel '%s'"), strval);
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 			m_label_free(new_sl);	/* OK if null */
 #else	/* !sun */
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "mlslabel is not supported on FreeBSD"));
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 #endif	/* !sun */
 			goto error;
 
 		}
 
 		case ZFS_PROP_MOUNTPOINT:
 		{
 			namecheck_err_t why;
 
 			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) =3D=3D 0 ||
 			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) =3D=3D 0)
 				break;
 
 			if (mountpoint_namecheck(strval, &why)) {
 				switch (why) {
 				case NAME_ERR_LEADING_SLASH:
 					zfs_error_aux(hdl,
 					    dgettext(TEXT_DOMAIN,
 					    "'%s' must be an absolute path, "
 					    "'none', or 'legacy'"), propname);
 					break;
 				case NAME_ERR_TOOLONG:
 					zfs_error_aux(hdl,
 					    dgettext(TEXT_DOMAIN,
 					    "component of '%s' is too long"),
 					    propname);
 					break;
 				}
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 		}
 
 			/*FALLTHRU*/
 
 		case ZFS_PROP_SHARESMB:
 		case ZFS_PROP_SHARENFS:
 			/*
 			 * For the mountpoint and sharenfs or sharesmb
 			 * properties, check if it can be set in a
 			 * global/non-global zone based on
 			 * the zoned property value:
 			 *
 			 *		global zone	    non-global zone
 			 * --------------------------------------------------
 			 * zoned=3Don	mountpoint (no)	    mountpoint (yes)
 			 *		sharenfs (no)	    sharenfs (no)
 			 *		sharesmb (no)	    sharesmb (no)
 			 *
 			 * zoned=3Doff	mountpoint (yes)	N/A
 			 *		sharenfs (yes)
 			 *		sharesmb (yes)
 			 */
 			if (zoned) {
 				if (getzoneid() =3D=3D GLOBAL_ZONEID) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set on "
 					    "dataset in a non-global zone"),
 					    propname);
 					(void) zfs_error(hdl, EZFS_ZONED,
 					    errbuf);
 					goto error;
 				} else if (prop =3D=3D ZFS_PROP_SHARENFS ||
 				    prop =3D=3D ZFS_PROP_SHARESMB) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set in "
 					    "a non-global zone"), propname);
 					(void) zfs_error(hdl, EZFS_ZONED,
 					    errbuf);
 					goto error;
 				}
 			} else if (getzoneid() !=3D GLOBAL_ZONEID) {
 				/*
 				 * If zoned property is 'off', this must be in
 				 * a global zone. If not, something is wrong.
 				 */
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' cannot be set while dataset "
 				    "'zoned' property is set"), propname);
 				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
 				goto error;
 			}
 
 			/*
 			 * At this point, it is legitimate to set the
 			 * property. Now we want to make sure that the
 			 * property value is valid if it is sharenfs.
 			 */
 			if ((prop =3D=3D ZFS_PROP_SHARENFS ||
 			    prop =3D=3D ZFS_PROP_SHARESMB) &&
 			    strcmp(strval, "on") !=3D 0 &&
 			    strcmp(strval, "off") !=3D 0) {
 				zfs_share_proto_t proto;
 
 				if (prop =3D=3D ZFS_PROP_SHARESMB)
 					proto =3D PROTO_SMB;
 				else
 					proto =3D PROTO_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) !=3D SA_OK) {
 					/*
 					 * An error occurred so we can't do
 					 * anything
 					 */
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set: problem "
 					    "in share initialization"),
 					    propname);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					goto error;
 				}
 
 				if (zfs_parse_options(strval, proto) !=3D 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.
 					 */
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set to invalid "
 					    "options"), propname);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					zfs_uninit_libshare(hdl);
 					goto error;
 				}
 				zfs_uninit_libshare(hdl);
 			}
 
 			break;
 		case ZFS_PROP_UTF8ONLY:
 			chosen_utf =3D (int)intval;
 			break;
 		case ZFS_PROP_NORMALIZE:
 			chosen_normal =3D (int)intval;
 			break;
 		}
 
 		/*
 		 * For changes to existing volumes, we have some additional
 		 * checks to enforce.
 		 */
 		if (type =3D=3D ZFS_TYPE_VOLUME && zhp !=3D NULL) {
 			uint64_t volsize =3D zfs_prop_get_int(zhp,
 			    ZFS_PROP_VOLSIZE);
 			uint64_t blocksize =3D 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 !=3D 0) {
 					zfs_nicenum(blocksize, buf,
 					    sizeof (buf));
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' must be a multiple of "
 					    "volume block size (%s)"),
 					    propname, buf);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					goto error;
 				}
 
 				if (intval =3D=3D 0) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be zero"),
 					    propname);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					goto error;
 				}
 				break;
 			}
 		}
 	}
 
 	/*
 	 * If normalization was chosen, but no UTF8 choice was made,
 	 * enforce rejection of non-UTF8 names.
 	 *
 	 * If normalization was chosen, but rejecting non-UTF8 names
 	 * was explicitly not chosen, it is an error.
 	 */
 	if (chosen_normal > 0 && chosen_utf < 0) {
 		if (nvlist_add_uint64(ret,
 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) !=3D 0) {
 			(void) no_memory(hdl);
 			goto error;
 		}
 	} else if (chosen_normal > 0 && chosen_utf =3D=3D 0) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "'%s' must be set 'on' if normalization chosen"),
 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
 		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 		goto error;
 	}
 	return (ret);
 
 error:
 	nvlist_free(ret);
 	return (NULL);
 }
 
 int
 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 {
 	uint64_t old_volsize;
 	uint64_t new_volsize;
 	uint64_t old_reservation;
 	uint64_t new_reservation;
 	zfs_prop_t resv_prop;
 
 	/*
 	 * If this is an existing volume, and someone is setting the volsize,
 	 * make sure that it matches the reservation, or add it if necessary.
 	 */
 	old_volsize =3D zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
 	if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
 		return (-1);
 	old_reservation =3D zfs_prop_get_int(zhp, resv_prop);
 	if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) !=3D
 	    old_reservation) || nvlist_lookup_uint64(nvl,
 	    zfs_prop_to_name(resv_prop), &new_reservation) !=3D ENOENT) {
 		return (0);
 	}
 	if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
 	    &new_volsize) !=3D 0)
 		return (-1);
 	new_reservation =3D zvol_volsize_to_reservation(new_volsize,
 	    zhp->zfs_props);
 	if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
 	    new_reservation) !=3D 0) {
 		(void) no_memory(zhp->zfs_hdl);
 		return (-1);
 	}
 	return (1);
 }
 
 void
 zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
     char *errbuf)
 {
 	switch (err) {
 
 	case ENOSPC:
 		/*
 		 * For quotas and reservations, ENOSPC indicates
 		 * something different; setting a quota or reservation
 		 * doesn't use any disk space.
 		 */
 		switch (prop) {
 		case ZFS_PROP_QUOTA:
 		case ZFS_PROP_REFQUOTA:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "size is less than current used or "
 			    "reserved space"));
 			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
 			break;
 
 		case ZFS_PROP_RESERVATION:
 		case ZFS_PROP_REFRESERVATION:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "size is greater than available space"));
 			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
 			break;
 
 		default:
 			(void) zfs_standard_error(hdl, err, errbuf);
 			break;
 		}
 		break;
 
 	case EBUSY:
 		(void) zfs_standard_error(hdl, EBUSY, errbuf);
 		break;
 
 	case EROFS:
 		(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
 		break;
 
 	case ENOTSUP:
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "pool and or dataset must be upgraded to set this "
 		    "property or value"));
 		(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
 		break;
 
 	case ERANGE:
 		if (prop =3D=3D ZFS_PROP_COMPRESSION) {
 			(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "property setting is not allowed on "
 			    "bootable datasets"));
 			(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
 		} else {
 			(void) zfs_standard_error(hdl, err, errbuf);
 		}
 		break;
 
 	case EINVAL:
 		if (prop =3D=3D ZPROP_INVAL) {
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 		} else {
 			(void) zfs_standard_error(hdl, err, errbuf);
 		}
 		break;
 
 	case EOVERFLOW:
 		/*
 		 * This platform can't address a volume this big.
 		 */
 #ifdef _ILP32
 		if (prop =3D=3D ZFS_PROP_VOLSIZE) {
 			(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
 			break;
 		}
 #endif
 		/* FALLTHROUGH */
 	default:
 		(void) zfs_standard_error(hdl, err, errbuf);
 	}
 }
 
 /*
  * Given a property name and value, set the property for the given dataset.
  */
 int
 zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int ret =3D -1;
 	prop_changelist_t *cl =3D NULL;
 	char errbuf[1024];
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	nvlist_t *nvl =3D NULL, *realprops;
 	zfs_prop_t prop;
 	boolean_t do_prefix;
 	uint64_t idx;
 	int added_resv;
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
 	    zhp->zfs_name);
 
 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) !=3D 0 ||
 	    nvlist_add_string(nvl, propname, propval) !=3D 0) {
 		(void) no_memory(hdl);
 		goto error;
 	}
 
 	if ((realprops =3D zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) =3D=3D NULL)
 		goto error;
 
 	nvlist_free(nvl);
 	nvl =3D realprops;
 
 	prop =3D zfs_name_to_prop(propname);
 
 	/* We don't support those properties on FreeBSD. */
 	switch (prop) {
 	case ZFS_PROP_DEVICES:
 	case ZFS_PROP_ISCSIOPTIONS:
 	case ZFS_PROP_XATTR:
 	case ZFS_PROP_VSCAN:
 	case ZFS_PROP_NBMAND:
 	case ZFS_PROP_MLSLABEL:
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    "property '%s' not supported on FreeBSD", propname);
 		ret =3D zfs_error(hdl, EZFS_PERM, errbuf);
 		goto error;
 	}
 
 	if (prop =3D=3D ZFS_PROP_VOLSIZE) {
 		if ((added_resv =3D zfs_add_synthetic_resv(zhp, nvl)) =3D=3D -1)
 			goto error;
 	}
 
 	if ((cl =3D changelist_gather(zhp, prop, 0, 0)) =3D=3D NULL)
 		goto error;
 
 	if (prop =3D=3D 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 =3D zfs_error(hdl, EZFS_ZONED, errbuf);
 		goto error;
 	}
 
 	/*
 	 * If the dataset's canmount property is being set to noauto,
 	 * then we want to prevent unmounting & remounting it.
 	 */
 	do_prefix =3D !((prop =3D=3D ZFS_PROP_CANMOUNT) &&
 	    (zprop_string_to_index(prop, propval, &idx,
 	    ZFS_TYPE_DATASET) =3D=3D 0) && (idx =3D=3D ZFS_CANMOUNT_NOAUTO));
 
 	if (do_prefix && (ret =3D changelist_prefix(cl)) !=3D 0)
 		goto error;
 
 	/*
 	 * Execute the corresponding ioctl() to set this property.
 	 */
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (zcmd_write_src_nvlist(hdl, &zc, nvl) !=3D 0)
 		goto error;
 
 	ret =3D zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 
 	if (ret !=3D 0) {
 		zfs_setprop_error(hdl, prop, errno, errbuf);
 		if (added_resv && errno =3D=3D ENOSPC) {
 			/* clean up the volsize property we tried to set */
 			uint64_t old_volsize =3D zfs_prop_get_int(zhp,
 			    ZFS_PROP_VOLSIZE);
 			nvlist_free(nvl);
 			zcmd_free_nvlists(&zc);
 			if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) !=3D 0)
 				goto error;
 			if (nvlist_add_uint64(nvl,
 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
 			    old_volsize) !=3D 0)
 				goto error;
 			if (zcmd_write_src_nvlist(hdl, &zc, nvl) !=3D 0)
 				goto error;
 			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 		}
 	} else {
 		if (do_prefix)
 			ret =3D changelist_postfix(cl);
 
 		/*
 		 * Refresh the statistics so the new property value
 		 * is reflected.
 		 */
 		if (ret =3D=3D 0)
 			(void) get_stats(zhp);
 	}
 
 error:
 	nvlist_free(nvl);
 	zcmd_free_nvlists(&zc);
 	if (cl)
 		changelist_free(cl);
 	return (ret);
 }
 
 /*
  * Given a property, inherit the value from the parent dataset, or if recei=
 ved
  * is TRUE, revert to the received value, if any.
  */
 int
 zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t receive=
 d)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int ret;
 	prop_changelist_t *cl;
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	char errbuf[1024];
 	zfs_prop_t prop;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
 
 	zc.zc_cookie =3D received;
 	if ((prop =3D zfs_name_to_prop(propname)) =3D=3D ZPROP_INVAL) {
 		/*
 		 * For user properties, the amount of work we have to do is very
 		 * small, so just do it here.
 		 */
 		if (!zfs_prop_user(propname)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid property"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
 
 		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) !=3D 0)
 			return (zfs_standard_error(hdl, errno, errbuf));
 
 		return (0);
 	}
 
 	/*
 	 * Verify that this property is inheritable.
 	 */
 	if (zfs_prop_readonly(prop))
 		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
 
 	if (!zfs_prop_inheritable(prop) && !received)
 		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
 
 	/*
 	 * Check to see if the value applies to this type
 	 */
 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
 
 	/*
 	 * Normalize the name, to get rid of shorthand abbreviations.
 	 */
 	propname =3D zfs_prop_to_name(prop);
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
 
 	if (prop =3D=3D ZFS_PROP_MOUNTPOINT && getzoneid() =3D=3D GLOBAL_ZONEID &&
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "dataset is used in a non-global zone"));
 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
 	}
 
 	/*
 	 * Determine datasets which will be affected by this change, if any.
 	 */
 	if ((cl =3D changelist_gather(zhp, prop, 0, 0)) =3D=3D NULL)
 		return (-1);
 
 	if (prop =3D=3D 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 =3D zfs_error(hdl, EZFS_ZONED, errbuf);
 		goto error;
 	}
 
 	if ((ret =3D changelist_prefix(cl)) !=3D 0)
 		goto error;
 
 	if ((ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) !=3D 0) {
 		return (zfs_standard_error(hdl, errno, errbuf));
 	} else {
 
 		if ((ret =3D changelist_postfix(cl)) !=3D 0)
 			goto error;
 
 		/*
 		 * Refresh the statistics so the new property is reflected.
 		 */
 		(void) get_stats(zhp);
 	}
 
 error:
 	changelist_free(cl);
 	return (ret);
 }
 
 /*
  * True DSL properties are stored in an nvlist.  The following two function=
 s
  * extract them appropriately.
  */
 static uint64_t
 getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
 {
 	nvlist_t *nv;
 	uint64_t value;
 
 	*source =3D NULL;
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(prop), &nv) =3D=3D 0) {
 		verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) =3D=3D 0);
 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
 	} else {
 		verify(!zhp->zfs_props_table ||
 		    zhp->zfs_props_table[prop] =3D=3D B_TRUE);
 		value =3D zfs_prop_default_numeric(prop);
 		*source =3D "";
 	}
 
 	return (value);
 }
 
 static char *
 getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
 {
 	nvlist_t *nv;
 	char *value;
 
 	*source =3D NULL;
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(prop), &nv) =3D=3D 0) {
 		verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) =3D=3D 0);
 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
 	} else {
 		verify(!zhp->zfs_props_table ||
 		    zhp->zfs_props_table[prop] =3D=3D B_TRUE);
 		if ((value =3D (char *)zfs_prop_default_string(prop)) =3D=3D NULL)
 			value =3D "";
 		*source =3D "";
 	}
 
 	return (value);
 }
 
 static boolean_t
 zfs_is_recvd_props_mode(zfs_handle_t *zhp)
 {
 	return (zhp->zfs_props =3D=3D zhp->zfs_recvd_props);
 }
 
 static void
 zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
 {
 	*cookie =3D (uint64_t)(uintptr_t)zhp->zfs_props;
 	zhp->zfs_props =3D zhp->zfs_recvd_props;
 }
 
 static void
 zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
 {
 	zhp->zfs_props =3D (nvlist_t *)(uintptr_t)*cookie;
 	*cookie =3D 0;
 }
 
 /*
  * Internal function for getting a numeric property.  Both zfs_prop_get() a=
 nd
  * zfs_prop_get_int() are built using this interface.
  *
  * Certain properties can be overridden using 'mount -o'.  In this case, sc=
 an
  * the contents of the /etc/mnttab entry, searching for the appropriate opt=
 ions.
  * If they differ from the on-disk values, report the current values and ma=
 rk
  * the source "temporary".
  */
 static int
 get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *sr=
 c,
     char **source, uint64_t *val)
 {
 	zfs_cmd_t zc =3D { 0 };
 	nvlist_t *zplprops =3D NULL;
 	struct mnttab mnt;
 	char *mntopt_on =3D NULL;
 	char *mntopt_off =3D NULL;
 	boolean_t received =3D zfs_is_recvd_props_mode(zhp);
 
 	*source =3D NULL;
 
 	switch (prop) {
 	case ZFS_PROP_ATIME:
 		mntopt_on =3D MNTOPT_ATIME;
 		mntopt_off =3D MNTOPT_NOATIME;
 		break;
 
 	case ZFS_PROP_DEVICES:
 		mntopt_on =3D MNTOPT_DEVICES;
 		mntopt_off =3D MNTOPT_NODEVICES;
 		break;
 
 	case ZFS_PROP_EXEC:
 		mntopt_on =3D MNTOPT_EXEC;
 		mntopt_off =3D MNTOPT_NOEXEC;
 		break;
 
 	case ZFS_PROP_READONLY:
 		mntopt_on =3D MNTOPT_RO;
 		mntopt_off =3D MNTOPT_RW;
 		break;
 
 	case ZFS_PROP_SETUID:
 		mntopt_on =3D MNTOPT_SETUID;
 		mntopt_off =3D MNTOPT_NOSETUID;
 		break;
 
 	case ZFS_PROP_XATTR:
 		mntopt_on =3D MNTOPT_XATTR;
 		mntopt_off =3D MNTOPT_NOXATTR;
 		break;
 
 	case ZFS_PROP_NBMAND:
 		mntopt_on =3D MNTOPT_NBMAND;
 		mntopt_off =3D MNTOPT_NONBMAND;
 		break;
 	}
 
 	/*
 	 * Because looking up the mount options is potentially expensive
 	 * (iterating over all of /etc/mnttab), we defer its calculation until
 	 * we're looking up a property which requires its presence.
 	 */
 	if (!zhp->zfs_mntcheck &&
 	    (mntopt_on !=3D NULL || prop =3D=3D ZFS_PROP_MOUNTED)) {
 		libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 		struct mnttab entry;
 
 		if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) =3D=3D 0) {
 			zhp->zfs_mntopts =3D zfs_strdup(hdl,
 			    entry.mnt_mntopts);
 			if (zhp->zfs_mntopts =3D=3D NULL)
 				return (-1);
 		}
 
 		zhp->zfs_mntcheck =3D B_TRUE;
 	}
 
 	if (zhp->zfs_mntopts =3D=3D NULL)
 		mnt.mnt_mntopts =3D "";
 	else
 		mnt.mnt_mntopts =3D zhp->zfs_mntopts;
 
 	switch (prop) {
 	case ZFS_PROP_ATIME:
 	case ZFS_PROP_DEVICES:
 	case ZFS_PROP_EXEC:
 	case ZFS_PROP_READONLY:
 	case ZFS_PROP_SETUID:
 	case ZFS_PROP_XATTR:
 	case ZFS_PROP_NBMAND:
 		*val =3D getprop_uint64(zhp, prop, source);
 
 		if (received)
 			break;
 
 		if (hasmntopt(&mnt, mntopt_on) && !*val) {
 			*val =3D B_TRUE;
 			if (src)
 				*src =3D ZPROP_SRC_TEMPORARY;
 		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
 			*val =3D B_FALSE;
 			if (src)
 				*src =3D ZPROP_SRC_TEMPORARY;
 		}
 		break;
 
 	case ZFS_PROP_CANMOUNT:
 	case ZFS_PROP_VOLSIZE:
 	case ZFS_PROP_QUOTA:
 	case ZFS_PROP_REFQUOTA:
 	case ZFS_PROP_RESERVATION:
 	case ZFS_PROP_REFRESERVATION:
 		*val =3D getprop_uint64(zhp, prop, source);
 
 		if (*source =3D=3D NULL) {
 			/* not default, must be local */
 			*source =3D zhp->zfs_name;
 		}
 		break;
 
 	case ZFS_PROP_MOUNTED:
 		*val =3D (zhp->zfs_mntopts !=3D NULL);
 		break;
 
 	case ZFS_PROP_NUMCLONES:
 		*val =3D zhp->zfs_dmustats.dds_num_clones;
 		break;
 
 	case ZFS_PROP_VERSION:
 	case ZFS_PROP_NORMALIZE:
 	case ZFS_PROP_UTF8ONLY:
 	case ZFS_PROP_CASE:
 		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
 		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) !=3D 0)
 			return (-1);
 		(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);
 			return (-1);
 		}
 		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) !=3D 0 ||
 		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
 		    val) !=3D 0) {
 			zcmd_free_nvlists(&zc);
 			return (-1);
 		}
 		if (zplprops)
 			nvlist_free(zplprops);
 		zcmd_free_nvlists(&zc);
 		break;
 
 	default:
 		switch (zfs_prop_get_type(prop)) {
 		case PROP_TYPE_NUMBER:
 		case PROP_TYPE_INDEX:
 			*val =3D getprop_uint64(zhp, prop, source);
 			/*
 			 * If we tried to use a default value for a
 			 * readonly property, it means that it was not
 			 * present.
 			 */
 			if (zfs_prop_readonly(prop) &&
 			    *source !=3D NULL && (*source)[0] =3D=3D '\0') {
 				*source =3D NULL;
 			}
 			break;
 
 		case PROP_TYPE_STRING:
 		default:
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "cannot get non-numeric property"));
 			return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
 			    dgettext(TEXT_DOMAIN, "internal error")));
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Calculate the source type, given the raw source string.
  */
 static void
 get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
     char *statbuf, size_t statlen)
 {
 	if (statbuf =3D=3D NULL || *srctype =3D=3D ZPROP_SRC_TEMPORARY)
 		return;
 
 	if (source =3D=3D NULL) {
 		*srctype =3D ZPROP_SRC_NONE;
 	} else if (source[0] =3D=3D '\0') {
 		*srctype =3D ZPROP_SRC_DEFAULT;
 	} else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) !=3D NULL) {
 		*srctype =3D ZPROP_SRC_RECEIVED;
 	} else {
 		if (strcmp(source, zhp->zfs_name) =3D=3D 0) {
 			*srctype =3D ZPROP_SRC_LOCAL;
 		} else {
 			(void) strlcpy(statbuf, source, statlen);
 			*srctype =3D ZPROP_SRC_INHERITED;
 		}
 	}
 
 }
 
 int
 zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
     size_t proplen, boolean_t literal)
 {
 	zfs_prop_t prop;
 	int err =3D 0;
 
 	if (zhp->zfs_recvd_props =3D=3D NULL)
 		if (get_recvd_props_ioctl(zhp) !=3D 0)
 			return (-1);
 
 	prop =3D zfs_name_to_prop(propname);
 
 	if (prop !=3D ZPROP_INVAL) {
 		uint64_t cookie;
 		if (!nvlist_exists(zhp->zfs_recvd_props, propname))
 			return (-1);
 		zfs_set_recvd_props_mode(zhp, &cookie);
 		err =3D zfs_prop_get(zhp, prop, propbuf, proplen,
 		    NULL, NULL, 0, literal);
 		zfs_unset_recvd_props_mode(zhp, &cookie);
 	} else {
 		nvlist_t *propval;
 		char *recvdval;
 		if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
 		    propname, &propval) !=3D 0)
 			return (-1);
 		verify(nvlist_lookup_string(propval, ZPROP_VALUE,
 		    &recvdval) =3D=3D 0);
 		(void) strlcpy(propbuf, recvdval, proplen);
 	}
 
 	return (err =3D=3D 0 ? 0 : -1);
 }
 
 static int
 get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
 {
 	nvlist_t *value;
 	nvpair_t *pair;
 
 	value =3D zfs_get_clones_nvl(zhp);
 	if (value =3D=3D NULL)
 		return (-1);
 
 	propbuf[0] =3D '\0';
 	for (pair =3D nvlist_next_nvpair(value, NULL); pair !=3D NULL;
 	    pair =3D nvlist_next_nvpair(value, pair)) {
 		if (propbuf[0] !=3D '\0')
 			(void) strlcat(propbuf, ",", proplen);
 		(void) strlcat(propbuf, nvpair_name(pair), proplen);
 	}
 
 	return (0);
 }
 
 struct get_clones_arg {
 	uint64_t numclones;
 	nvlist_t *value;
 	const char *origin;
 	char buf[ZFS_MAXNAMELEN];
 };
 
 int
 get_clones_cb(zfs_handle_t *zhp, void *arg)
 {
 	struct get_clones_arg *gca =3D arg;
 
 	if (gca->numclones =3D=3D 0) {
 		zfs_close(zhp);
 		return (0);
 	}
 
 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
 	    NULL, NULL, 0, B_TRUE) !=3D 0)
 		goto out;
 	if (strcmp(gca->buf, gca->origin) =3D=3D 0) {
 		if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) !=3D 0) {
 			zfs_close(zhp);
 			return (no_memory(zhp->zfs_hdl));
 		}
 		gca->numclones--;
 	}
 
 out:
 	(void) zfs_iter_children(zhp, get_clones_cb, gca);
 	zfs_close(zhp);
 	return (0);
 }
 
 nvlist_t *
 zfs_get_clones_nvl(zfs_handle_t *zhp)
 {
 	nvlist_t *nv, *value;
 
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(ZFS_PROP_CLONES), &nv) !=3D 0) {
 		struct get_clones_arg gca;
 
 		/*
 		 * if this is a snapshot, then the kernel wasn't able
 		 * to get the clones.  Do it by slowly iterating.
 		 */
 		if (zhp->zfs_type !=3D ZFS_TYPE_SNAPSHOT)
 			return (NULL);
 		if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) !=3D 0)
 			return (NULL);
 		if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) !=3D 0) {
 			nvlist_free(nv);
 			return (NULL);
 		}
 
 		gca.numclones =3D zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
 		gca.value =3D value;
 		gca.origin =3D zhp->zfs_name;
 
 		if (gca.numclones !=3D 0) {
 			zfs_handle_t *root;
 			char pool[ZFS_MAXNAMELEN];
 			char *cp =3D pool;
 
 			/* get the pool name */
 			(void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
 			(void) strsep(&cp, "/@");
 			root =3D zfs_open(zhp->zfs_hdl, pool,
 			    ZFS_TYPE_FILESYSTEM);
 
 			(void) get_clones_cb(root, &gca);
 		}
 
 		if (gca.numclones !=3D 0 ||
 		    nvlist_add_nvlist(nv, ZPROP_VALUE, value) !=3D 0 ||
 		    nvlist_add_nvlist(zhp->zfs_props,
 		    zfs_prop_to_name(ZFS_PROP_CLONES), nv) !=3D 0) {
 			nvlist_free(nv);
 			nvlist_free(value);
 			return (NULL);
 		}
 		nvlist_free(nv);
 		nvlist_free(value);
 		verify(0 =3D=3D nvlist_lookup_nvlist(zhp->zfs_props,
 		    zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
 	}
 
 	verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) =3D=3D 0);
 
 	return (value);
 }
 
 /*
  * Retrieve a property from the given object.  If 'literal' is specified, t=
 hen
  * numbers are left as exact values.  Otherwise, numbers are converted to a
  * human-readable form.
  *
  * Returns 0 on success, or -1 on error.
  */
 int
 zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t prop=
 len,
     zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
 {
 	char *source =3D NULL;
 	uint64_t val;
 	char *str;
 	const char *strval;
 	boolean_t received =3D zfs_is_recvd_props_mode(zhp);
 
 	/*
 	 * Check to see if this property applies to our object
 	 */
 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
 		return (-1);
 
 	if (received && zfs_prop_readonly(prop))
 		return (-1);
 
 	if (src)
 		*src =3D ZPROP_SRC_NONE;
 
 	switch (prop) {
 	case ZFS_PROP_CREATION:
 		/*
 		 * 'creation' is a time_t stored in the statistics.  We convert
 		 * this into a string unless 'literal' is specified.
 		 */
 		{
 			val =3D getprop_uint64(zhp, prop, &source);
 			time_t time =3D (time_t)val;
 			struct tm t;
 
 			if (literal ||
 			    localtime_r(&time, &t) =3D=3D NULL ||
 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
 			    &t) =3D=3D 0)
 				(void) snprintf(propbuf, proplen, "%llu", val);
 		}
 		break;
 
 	case ZFS_PROP_MOUNTPOINT:
 		/*
 		 * Getting the precise mountpoint can be tricky.
 		 *
 		 *  - for 'none' or 'legacy', return those values.
 		 *  - for inherited mountpoints, we want to take everything
 		 *    after our ancestor and append it to the inherited value.
 		 *
 		 * If the pool has an alternate root, we want to prepend that
 		 * root to any values we return.
 		 */
 
 		str =3D getprop_string(zhp, prop, &source);
 
 		if (str[0] =3D=3D '/') {
 			char buf[MAXPATHLEN];
 			char *root =3D buf;
 			const char *relpath;
 
 			/*
 			 * If we inherit the mountpoint, even from a dataset
 			 * with a received value, the source will be the path of
 			 * the dataset we inherit from. If source is
 			 * ZPROP_SOURCE_VAL_RECVD, the received value is not
 			 * inherited.
 			 */
 			if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) =3D=3D 0) {
 				relpath =3D "";
 			} else {
 				relpath =3D zhp->zfs_name + strlen(source);
 				if (relpath[0] =3D=3D '/')
 					relpath++;
 			}
 
 			if ((zpool_get_prop(zhp->zpool_hdl,
 			    ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
 			    (strcmp(root, "-") =3D=3D 0))
 				root[0] =3D '\0';
 			/*
 			 * Special case an alternate root of '/'. This will
 			 * avoid having multiple leading slashes in the
 			 * mountpoint path.
 			 */
 			if (strcmp(root, "/") =3D=3D 0)
 				root++;
 
 			/*
 			 * If the mountpoint is '/' then skip over this
 			 * if we are obtaining either an alternate root or
 			 * an inherited mountpoint.
 			 */
 			if (str[1] =3D=3D '\0' && (root[0] !=3D '\0' ||
 			    relpath[0] !=3D '\0'))
 				str++;
 
 			if (relpath[0] =3D=3D '\0')
 				(void) snprintf(propbuf, proplen, "%s%s",
 				    root, str);
 			else
 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
 				    root, str, relpath[0] =3D=3D '@' ? "" : "/",
 				    relpath);
 		} else {
 			/* 'legacy' or 'none' */
 			(void) strlcpy(propbuf, str, 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] =3D=3D '\0')
 			return (-1);
 		break;
 
 	case ZFS_PROP_CLONES:
 		if (get_clones_string(zhp, propbuf, proplen) !=3D 0)
 			return (-1);
 		break;
 
 	case ZFS_PROP_QUOTA:
 	case ZFS_PROP_REFQUOTA:
 	case ZFS_PROP_RESERVATION:
 	case ZFS_PROP_REFRESERVATION:
 
 		if (get_numeric_property(zhp, prop, src, &source, &val) !=3D 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
 		 * value.  Otherwise, we print the number nicely and indicate
 		 * that its set locally.
 		 */
 		if (val =3D=3D 0) {
 			if (literal)
 				(void) strlcpy(propbuf, "0", proplen);
 			else
 				(void) strlcpy(propbuf, "none", proplen);
 		} else {
 			if (literal)
 				(void) snprintf(propbuf, proplen, "%llu",
 				    (u_longlong_t)val);
 			else
 				zfs_nicenum(val, propbuf, proplen);
 		}
 		break;
 
 	case ZFS_PROP_REFRATIO:
 	case ZFS_PROP_COMPRESSRATIO:
 		if (get_numeric_property(zhp, prop, src, &source, &val) !=3D 0)
 			return (-1);
 		(void) snprintf(propbuf, proplen, "%llu.%02llux",
 		    (u_longlong_t)(val / 100),
 		    (u_longlong_t)(val % 100));
 		break;
 
 	case ZFS_PROP_TYPE:
 		switch (zhp->zfs_type) {
 		case ZFS_TYPE_FILESYSTEM:
 			str =3D "filesystem";
 			break;
 		case ZFS_TYPE_VOLUME:
 			str =3D "volume";
 			break;
 		case ZFS_TYPE_SNAPSHOT:
 			str =3D "snapshot";
 			break;
 		default:
 			abort();
 		}
 		(void) snprintf(propbuf, proplen, "%s", str);
 		break;
 
 	case ZFS_PROP_MOUNTED:
 		/*
 		 * The 'mounted' property is a pseudo-property that described
 		 * whether the filesystem is currently mounted.  Even though
 		 * it's a boolean value, the typical values of "on" and "off"
 		 * don't make sense, so we translate to "yes" and "no".
 		 */
 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
 		    src, &source, &val) !=3D 0)
 			return (-1);
 		if (val)
 			(void) strlcpy(propbuf, "yes", proplen);
 		else
 			(void) strlcpy(propbuf, "no", proplen);
 		break;
 
 	case ZFS_PROP_NAME:
 		/*
 		 * The 'name' property is a pseudo-property derived from the
 		 * dataset name.  It is presented as a real property to simplify
 		 * consumers.
 		 */
 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
 		break;
 
 	case ZFS_PROP_MLSLABEL:
 		{
 #ifdef sun
 			m_label_t *new_sl =3D NULL;
 			char *ascii =3D NULL;	/* human readable label */
 
 			(void) strlcpy(propbuf,
 			    getprop_string(zhp, prop, &source), proplen);
 
 			if (literal || (strcasecmp(propbuf,
 			    ZFS_MLSLABEL_DEFAULT) =3D=3D 0))
 				break;
 
 			/*
 			 * Try to translate the internal hex string to
 			 * human-readable output.  If there are any
 			 * problems just use the hex string.
 			 */
 
 			if (str_to_label(propbuf, &new_sl, MAC_LABEL,
 			    L_NO_CORRECTION, NULL) =3D=3D -1) {
 				m_label_free(new_sl);
 				break;
 			}
 
 			if (label_to_str(new_sl, &ascii, M_LABEL,
 			    DEF_NAMES) !=3D 0) {
 				if (ascii)
 					free(ascii);
 				m_label_free(new_sl);
 				break;
 			}
 			m_label_free(new_sl);
 
 			(void) strlcpy(propbuf, ascii, proplen);
 			free(ascii);
 #else	/* !sun */
 			propbuf[0] =3D '\0';
 #endif	/* !sun */
 		}
 		break;
 
 	case ZFS_PROP_GUID:
 		/*
 		 * GUIDs are stored as numbers, but they are identifiers.
 		 * We don't want them to be pretty printed, because pretty
 		 * printing mangles the ID into a truncated and useless value.
 		 */
 		if (get_numeric_property(zhp, prop, src, &source, &val) !=3D 0)
 			return (-1);
 		(void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
 		break;
 
 	default:
 		switch (zfs_prop_get_type(prop)) {
 		case PROP_TYPE_NUMBER:
 			if (get_numeric_property(zhp, prop, src,
 			    &source, &val) !=3D 0)
 				return (-1);
 			if (literal)
 				(void) snprintf(propbuf, proplen, "%llu",
 				    (u_longlong_t)val);
 			else
 				zfs_nicenum(val, propbuf, proplen);
 			break;
 
 		case PROP_TYPE_STRING:
 			(void) strlcpy(propbuf,
 			    getprop_string(zhp, prop, &source), proplen);
 			break;
 
 		case PROP_TYPE_INDEX:
 			if (get_numeric_property(zhp, prop, src,
 			    &source, &val) !=3D 0)
 				return (-1);
 			if (zfs_prop_index_to_string(prop, val, &strval) !=3D 0)
 				return (-1);
 			(void) strlcpy(propbuf, strval, proplen);
 			break;
 
 		default:
 			abort();
 		}
 	}
 
 	get_source(zhp, src, source, statbuf, statlen);
 
 	return (0);
 }
 
 /*
  * Utility function to get the given numeric property.  Does no validation =
 that
  * the given property is the appropriate type; should only be used with
  * hard-coded property types.
  */
 uint64_t
 zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
 {
 	char *source;
 	uint64_t val;
 
 	(void) get_numeric_property(zhp, prop, NULL, &source, &val);
 
 	return (val);
 }
 
 int
 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
 {
 	char buf[64];
 
 	(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
 	return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
 }
 
 /*
  * Similar to zfs_prop_get(), but returns the value as an integer.
  */
 int
 zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
     zprop_source_t *src, char *statbuf, size_t statlen)
 {
 	char *source;
 
 	/*
 	 * Check to see if this property applies to our object
 	 */
 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
 		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
 		    zfs_prop_to_name(prop)));
 	}
 
 	if (src)
 		*src =3D ZPROP_SRC_NONE;
 
 	if (get_numeric_property(zhp, prop, src, &source, value) !=3D 0)
 		return (-1);
 
 	get_source(zhp, src, source, statbuf, statlen);
 
 	return (0);
 }
 
 static int
 idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
     char **domainp, idmap_rid_t *ridp)
 {
 #ifdef sun
 	idmap_get_handle_t *get_hdl =3D NULL;
 	idmap_stat status;
 	int err =3D EINVAL;
 
 	if (idmap_get_create(&get_hdl) !=3D IDMAP_SUCCESS)
 		goto out;
 
 	if (isuser) {
 		err =3D idmap_get_sidbyuid(get_hdl, id,
 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
 	} else {
 		err =3D idmap_get_sidbygid(get_hdl, id,
 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
 	}
 	if (err =3D=3D IDMAP_SUCCESS &&
 	    idmap_get_mappings(get_hdl) =3D=3D IDMAP_SUCCESS &&
 	    status =3D=3D IDMAP_SUCCESS)
 		err =3D 0;
 	else
 		err =3D EINVAL;
 out:
 	if (get_hdl)
 		idmap_get_destroy(get_hdl);
 	return (err);
 #else	/* !sun */
 	assert(!"invalid code path");
 #endif	/* !sun */
 }
 
 /*
  * convert the propname into parameters needed by kernel
  * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
  * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
  */
 static int
 userquota_propname_decode(const char *propname, boolean_t zoned,
     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *rid=
 p)
 {
 	zfs_userquota_prop_t type;
 	char *cp, *end;
 	char *numericsid =3D NULL;
 	boolean_t isuser;
 
 	domain[0] =3D '\0';
 
 	/* Figure out the property type ({user|group}{quota|space}) */
 	for (type =3D 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
 		if (strncmp(propname, zfs_userquota_prop_prefixes[type],
 		    strlen(zfs_userquota_prop_prefixes[type])) =3D=3D 0)
 			break;
 	}
 	if (type =3D=3D ZFS_NUM_USERQUOTA_PROPS)
 		return (EINVAL);
 	*typep =3D type;
 
 	isuser =3D (type =3D=3D ZFS_PROP_USERQUOTA ||
 	    type =3D=3D ZFS_PROP_USERUSED);
 
 	cp =3D strchr(propname, '@') + 1;
 
 	if (strchr(cp, '@')) {
 #ifdef sun
 		/*
 		 * It's a SID name (eg "user@domain") that needs to be
 		 * turned into S-1-domainID-RID.
 		 */
 		directory_error_t e;
 		if (zoned && getzoneid() =3D=3D GLOBAL_ZONEID)
 			return (ENOENT);
 		if (isuser) {
 			e =3D directory_sid_from_user_name(NULL,
 			    cp, &numericsid);
 		} else {
 			e =3D directory_sid_from_group_name(NULL,
 			    cp, &numericsid);
 		}
 		if (e !=3D NULL) {
 			directory_error_free(e);
 			return (ENOENT);
 		}
 		if (numericsid =3D=3D NULL)
 			return (ENOENT);
 		cp =3D numericsid;
 		/* will be further decoded below */
 #else	/* !sun */
 		return (ENOENT);
 #endif	/* !sun */
 	}
 
 	if (strncmp(cp, "S-1-", 4) =3D=3D 0) {
 		/* It's a numeric SID (eg "S-1-234-567-89") */
 		(void) strlcpy(domain, cp, domainlen);
 		cp =3D strrchr(domain, '-');
 		*cp =3D '\0';
 		cp++;
 
 		errno =3D 0;
 		*ridp =3D strtoull(cp, &end, 10);
 		if (numericsid) {
 			free(numericsid);
 			numericsid =3D NULL;
 		}
 		if (errno !=3D 0 || *end !=3D '\0')
 			return (EINVAL);
 	} else if (!isdigit(*cp)) {
 		/*
 		 * It's a user/group name (eg "user") that needs to be
 		 * turned into a uid/gid
 		 */
 		if (zoned && getzoneid() =3D=3D GLOBAL_ZONEID)
 			return (ENOENT);
 		if (isuser) {
 			struct passwd *pw;
 			pw =3D getpwnam(cp);
 			if (pw =3D=3D NULL)
 				return (ENOENT);
 			*ridp =3D pw->pw_uid;
 		} else {
 			struct group *gr;
 			gr =3D getgrnam(cp);
 			if (gr =3D=3D NULL)
 				return (ENOENT);
 			*ridp =3D gr->gr_gid;
 		}
 	} else {
 		/* It's a user/group ID (eg "12345"). */
 		uid_t id =3D strtoul(cp, &end, 10);
 		idmap_rid_t rid;
 		char *mapdomain;
 
 		if (*end !=3D '\0')
 			return (EINVAL);
 		if (id > MAXUID) {
 			/* It's an ephemeral ID. */
 			if (idmap_id_to_numeric_domain_rid(id, isuser,
 			    &mapdomain, &rid) !=3D 0)
 				return (ENOENT);
 			(void) strlcpy(domain, mapdomain, domainlen);
 			*ridp =3D rid;
 		} else {
 			*ridp =3D id;
 		}
 	}
 
 	ASSERT3P(numericsid, =3D=3D, NULL);
 	return (0);
 }
 
 static int
 zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue, zfs_userquota_prop_t *typep)
 {
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	err =3D userquota_propname_decode(propname,
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
 	    typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
 	zc.zc_objset_type =3D *typep;
 	if (err)
 		return (err);
 
 	err =3D ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
 	if (err)
 		return (err);
 
 	*propvalue =3D zc.zc_cookie;
 	return (0);
 }
 
 int
 zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue)
 {
 	zfs_userquota_prop_t type;
 
 	return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
 	    &type));
 }
 
 int
 zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
     char *propbuf, int proplen, boolean_t literal)
 {
 	int err;
 	uint64_t propvalue;
 	zfs_userquota_prop_t type;
 
 	err =3D zfs_prop_get_userquota_common(zhp, propname, &propvalue,
 	    &type);
 
 	if (err)
 		return (err);
 
 	if (literal) {
 		(void) snprintf(propbuf, proplen, "%llu", propvalue);
 	} else if (propvalue =3D=3D 0 &&
 	    (type =3D=3D ZFS_PROP_USERQUOTA || type =3D=3D ZFS_PROP_GROUPQUOTA)) {
 		(void) strlcpy(propbuf, "none", proplen);
 	} else {
 		zfs_nicenum(propvalue, propbuf, proplen);
 	}
 	return (0);
 }
 
 int
 zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue)
 {
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 	const char *snapname;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	snapname =3D strchr(propname, '@') + 1;
 	if (strchr(snapname, '@')) {
 		(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 	} else {
 		/* snapname is the short name, append it to zhp's fsname */
 		char *cp;
 
 		(void) strlcpy(zc.zc_value, zhp->zfs_name,
 		    sizeof (zc.zc_value));
 		cp =3D strchr(zc.zc_value, '@');
 		if (cp !=3D NULL)
 			*cp =3D '\0';
 		(void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
 		(void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
 	}
 
 	err =3D ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
 	if (err)
 		return (err);
 
 	*propvalue =3D zc.zc_cookie;
 	return (0);
 }
 
 int
 zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
     char *propbuf, int proplen, boolean_t literal)
 {
 	int err;
 	uint64_t propvalue;
 
 	err =3D zfs_prop_get_written_int(zhp, propname, &propvalue);
 
 	if (err)
 		return (err);
 
 	if (literal) {
 		(void) snprintf(propbuf, proplen, "%llu", propvalue);
 	} else {
 		zfs_nicenum(propvalue, propbuf, proplen);
 	}
 	return (0);
 }
 
 int
 zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
     uint64_t *usedp)
 {
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
 
 	err =3D ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
 	if (err)
 		return (err);
 
 	*usedp =3D zc.zc_cookie;
 
 	return (0);
 }
 
 /*
  * Returns the name of the given zfs handle.
  */
 const char *
 zfs_get_name(const zfs_handle_t *zhp)
 {
 	return (zhp->zfs_name);
 }
 
 /*
  * Returns the type of the given zfs handle.
  */
 zfs_type_t
 zfs_get_type(const zfs_handle_t *zhp)
 {
 	return (zhp->zfs_type);
 }
 
 /*
  * Is one dataset name a child dataset of another?
  *
  * Needs to handle these cases:
  * Dataset 1	"a/foo"		"a/foo"		"a/foo"		"a/foo"
  * Dataset 2	"a/fo"		"a/foobar"	"a/bar/baz"	"a/foo/bar"
  * Descendant?	No.		No.		No.		Yes.
  */
 static boolean_t
 is_descendant(const char *ds1, const char *ds2)
 {
 	size_t d1len =3D strlen(ds1);
 
 	/* ds2 can't be a descendant if it's smaller */
 	if (strlen(ds2) < d1len)
 		return (B_FALSE);
 
 	/* otherwise, compare strings and verify that there's a '/' char */
 	return (ds2[d1len] =3D=3D '/' && (strncmp(ds1, ds2, d1len) =3D=3D 0));
 }
 
 /*
  * Given a complete name, return just the portion that refers to the parent=
 .
  * Will return -1 if there is no parent (path is just the name of the
  * pool).
  */
 static int
 parent_name(const char *path, char *buf, size_t buflen)
 {
 	char *slashp;
 
 	(void) strlcpy(buf, path, buflen);
 
 	if ((slashp =3D strrchr(buf, '/')) =3D=3D NULL)
 		return (-1);
 	*slashp =3D '\0';
 
 	return (0);
 }
 
 /*
  * 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
  * closest existing ancestor for the given path.  In prefixlen return the
  * length of already existing prefix of the given path.  We also fetch the
  * 'zoned' property, which is used to validate property settings when creat=
 ing
  * new datasets.
  */
 static int
 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
     boolean_t accept_ancestor, int *prefixlen)
 {
 	zfs_cmd_t zc =3D { 0 };
 	char parent[ZFS_MAXNAMELEN];
 	char *slash;
 	zfs_handle_t *zhp;
 	char errbuf[1024];
 	uint64_t is_zoned;
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
 
 	/* get parent, and check to see if this is just a pool */
 	if (parent_name(path, parent, sizeof (parent)) !=3D 0) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "missing dataset name"));
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 	}
 
 	/* check to see if the pool exists */
 	if ((slash =3D strchr(parent, '/')) =3D=3D NULL)
 		slash =3D parent + strlen(parent);
 	(void) strncpy(zc.zc_name, parent, slash - parent);
 	zc.zc_name[slash - parent] =3D '\0';
 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) !=3D 0 &&
 	    errno =3D=3D ENOENT) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "no such pool '%s'"), zc.zc_name);
 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
 	}
 
 	/* check to see if the parent dataset exists */
 	while ((zhp =3D make_dataset_handle(hdl, parent)) =3D=3D NULL) {
 		if (errno =3D=3D ENOENT && accept_ancestor) {
 			/*
 			 * Go deeper to find an ancestor, give up on top level.
 			 */
 			if (parent_name(parent, parent, sizeof (parent)) !=3D 0) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "no such pool '%s'"), zc.zc_name);
 				return (zfs_error(hdl, EZFS_NOENT, errbuf));
 			}
 		} else if (errno =3D=3D ENOENT) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "parent does not exist"));
 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
 		} else
 			return (zfs_standard_error(hdl, errno, errbuf));
 	}
 
 	is_zoned =3D zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
 	if (zoned !=3D NULL)
 		*zoned =3D is_zoned;
 
 	/* we are in a non-global zone, but parent is in the global zone */
 	if (getzoneid() !=3D GLOBAL_ZONEID && !is_zoned) {
 		(void) zfs_standard_error(hdl, EPERM, errbuf);
 		zfs_close(zhp);
 		return (-1);
 	}
 
 	/* make sure parent is a filesystem */
 	if (zfs_get_type(zhp) !=3D ZFS_TYPE_FILESYSTEM) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "parent is not a filesystem"));
 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
 		zfs_close(zhp);
 		return (-1);
 	}
 
 	zfs_close(zhp);
 	if (prefixlen !=3D NULL)
 		*prefixlen =3D strlen(parent);
 	return (0);
 }
 
 /*
  * Finds whether the dataset of the given type(s) exists.
  */
 boolean_t
 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types=
 )
 {
 	zfs_handle_t *zhp;
 
 	if (!zfs_validate_name(hdl, path, types, B_FALSE))
 		return (B_FALSE);
 
 	/*
 	 * Try to get stats for the dataset, which will tell us if it exists.
 	 */
 	if ((zhp =3D make_dataset_handle(hdl, path)) !=3D NULL) {
 		int ds_type =3D zhp->zfs_type;
 
 		zfs_close(zhp);
 		if (types & ds_type)
 			return (B_TRUE);
 	}
 	return (B_FALSE);
 }
 
 /*
  * Given a path to 'target', create all the ancestors between
  * the prefixlen portion of the path, and the target itself.
  * Fail if the initial prefixlen-ancestor does not already exist.
  */
 int
 create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
 {
 	zfs_handle_t *h;
 	char *cp;
 	const char *opname;
 
 	/* make sure prefix exists */
 	cp =3D target + prefixlen;
 	if (*cp !=3D '/') {
 		assert(strchr(cp, '/') =3D=3D NULL);
 		h =3D zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
 	} else {
 		*cp =3D '\0';
 		h =3D zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
 		*cp =3D '/';
 	}
 	if (h =3D=3D NULL)
 		return (-1);
 	zfs_close(h);
 
 	/*
 	 * Attempt to create, mount, and share any ancestor filesystems,
 	 * up to the prefixlen-long one.
 	 */
 	for (cp =3D target + prefixlen + 1;
 	    cp =3D strchr(cp, '/'); *cp =3D '/', cp++) {
 		char *logstr;
 
 		*cp =3D '\0';
 
 		h =3D make_dataset_handle(hdl, target);
 		if (h) {
 			/* it already exists, nothing to do here */
 			zfs_close(h);
 			continue;
 		}
 
 		logstr =3D hdl->libzfs_log_str;
 		hdl->libzfs_log_str =3D NULL;
 		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
 		    NULL) !=3D 0) {
 			hdl->libzfs_log_str =3D logstr;
 			opname =3D dgettext(TEXT_DOMAIN, "create");
 			goto ancestorerr;
 		}
 
 		hdl->libzfs_log_str =3D logstr;
 		h =3D zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
 		if (h =3D=3D NULL) {
 			opname =3D dgettext(TEXT_DOMAIN, "open");
 			goto ancestorerr;
 		}
 
 		if (zfs_mount(h, NULL, 0) !=3D 0) {
 			opname =3D dgettext(TEXT_DOMAIN, "mount");
 			goto ancestorerr;
 		}
 
 		if (zfs_share(h) !=3D 0) {
 			opname =3D dgettext(TEXT_DOMAIN, "share");
 			goto ancestorerr;
 		}
 
 		zfs_close(h);
 	}
 
 	return (0);
 
 ancestorerr:
 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 	    "failed to %s ancestor '%s'"), opname, target);
 	return (-1);
 }
 
 /*
  * Creates non-existing ancestors of the given path.
  */
 int
 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
 {
 	int prefix;
 	char *path_copy;
 	int rc;
 
 	if (check_parents(hdl, path, NULL, B_TRUE, &prefix) !=3D 0)
 		return (-1);
 
 	if ((path_copy =3D strdup(path)) !=3D NULL) {
 		rc =3D create_parents(hdl, path_copy, prefix);
 		free(path_copy);
 	}
 	if (path_copy =3D=3D NULL || rc !=3D 0)
 		return (-1);
 
 	return (0);
 }
 
 /*
  * Create a new filesystem or volume.
  */
 int
 zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
     nvlist_t *props)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int ret;
 	uint64_t size =3D 0;
 	uint64_t blocksize =3D zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
 	char errbuf[1024];
 	uint64_t zoned;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot create '%s'"), path);
 
 	/* validate the path, taking care to note the extended error message */
 	if (!zfs_validate_name(hdl, path, type, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	/* validate parents exist */
 	if (check_parents(hdl, path, &zoned, B_FALSE, NULL) !=3D 0)
 		return (-1);
 
 	/*
 	 * The failure modes when creating a dataset of a different type over
 	 * one that already exists is a little strange.  In particular, if you
 	 * try to create a dataset on top of an existing dataset, the ioctl()
 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
 	 * first try to see if the dataset exists.
 	 */
 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
 	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "dataset already exists"));
 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 	}
 
 	if (type =3D=3D ZFS_TYPE_VOLUME)
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 
 	if (props && (props =3D zfs_valid_proplist(hdl, type, props,
 	    zoned, NULL, errbuf)) =3D=3D 0)
 		return (-1);
 
 	if (type =3D=3D ZFS_TYPE_VOLUME) {
 		/*
 		 * If we are creating a volume, the size and block size must
 		 * satisfy a few restraints.  First, the blocksize must be a
 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
 		 * volsize must be a multiple of the block size, and cannot be
 		 * zero.
 		 */
 		if (props =3D=3D NULL || nvlist_lookup_uint64(props,
 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) !=3D 0) {
 			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "missing volume size"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		if ((ret =3D nvlist_lookup_uint64(props,
 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
 		    &blocksize)) !=3D 0) {
 			if (ret =3D=3D ENOENT) {
 				blocksize =3D zfs_prop_default_numeric(
 				    ZFS_PROP_VOLBLOCKSIZE);
 			} else {
 				nvlist_free(props);
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "missing volume block size"));
 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 			}
 		}
 
 		if (size =3D=3D 0) {
 			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "volume size cannot be zero"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		if (size % blocksize !=3D 0) {
 			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "volume size must be a multiple of volume block "
 			    "size"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 	}
 
 	if (props && zcmd_write_src_nvlist(hdl, &zc, props) !=3D 0)
 		return (-1);
 	nvlist_free(props);
 
 	/* create the dataset */
 	ret =3D zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
 
 	zcmd_free_nvlists(&zc);
 
 	/* check for failure */
 	if (ret !=3D 0) {
 		char parent[ZFS_MAXNAMELEN];
 		(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 EDOM:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "volume block size must be power of 2 from "
 			    "%u to %uk"),
 			    (uint_t)SPA_MINBLOCKSIZE,
 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
 
 			return (zfs_error(hdl, EZFS_BADPROP, 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));
 #ifdef _ILP32
 		case EOVERFLOW:
 			/*
 			 * This platform can't address a volume this big.
 			 */
 			if (type =3D=3D ZFS_TYPE_VOLUME)
 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
 				    errbuf));
 #endif
 			/* FALLTHROUGH */
 		default:
 			return (zfs_standard_error(hdl, errno, errbuf));
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Destroys the given dataset.  The caller must make sure that the filesyst=
 em
  * isn't mounted, and that there are no active dependents.
  */
 int
 zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
 {
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (ZFS_IS_VOLUME(zhp)) {
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	} else {
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 	}
 
 	zc.zc_defer_destroy =3D defer;
 	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) !=3D 0) {
 		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
 		    zhp->zfs_name));
 	}
 
 	remove_mountpoint(zhp);
 
 	return (0);
 }
 
 struct destroydata {
 	nvlist_t *nvl;
 	const char *snapname;
 };
 
 static int
 zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
 {
 	struct destroydata *dd =3D arg;
 	zfs_handle_t *szhp;
 	char name[ZFS_MAXNAMELEN];
 	int rv =3D 0;
 
 	(void) snprintf(name, sizeof (name),
 	    "%s@%s", zhp->zfs_name, dd->snapname);
 
 	szhp =3D make_dataset_handle(zhp->zfs_hdl, name);
 	if (szhp) {
 		verify(nvlist_add_boolean(dd->nvl, name) =3D=3D 0);
 		zfs_close(szhp);
 	}
 
 	rv =3D zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
 	zfs_close(zhp);
 	return (rv);
 }
 
 /*
  * Destroys all snapshots with the given name in zhp & descendants.
  */
 int
 zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
 {
 	int ret;
 	struct destroydata dd =3D { 0 };
 
 	dd.snapname =3D snapname;
 	verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) =3D=3D 0);
 	(void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
 
 	if (nvlist_next_nvpair(dd.nvl, NULL) =3D=3D NULL) {
 		ret =3D zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
 		    zhp->zfs_name, snapname);
 	} else {
 		ret =3D zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
 	}
 	nvlist_free(dd.nvl);
 	return (ret);
 }
 
 /*
  * Destroys all the snapshots named in the nvlist.  They must be underneath
  * the zhp (either snapshots of it, or snapshots of its descendants).
  */
 int
 zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
 {
 	int ret;
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) !=3D 0)
 		return (-1);
 	zc.zc_defer_destroy =3D defer;
 
 	ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
 	if (ret !=3D 0) {
 		char errbuf[1024];
 
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot destroy snapshots in %s"), zc.zc_name);
 
 		switch (errno) {
 		case EEXIST:
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "snapshot is cloned"));
 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
 
 		default:
 			return (zfs_standard_error(zhp->zfs_hdl, errno,
 			    errbuf));
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Clones the given dataset.  The target must be of the same type as the so=
 urce.
  */
 int
 zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
 {
 	zfs_cmd_t zc =3D { 0 };
 	char parent[ZFS_MAXNAMELEN];
 	int ret;
 	char errbuf[1024];
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zfs_type_t type;
 	uint64_t zoned;
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT);
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot create '%s'"), target);
 
 	/* validate the target/clone name */
 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	/* validate parents exist */
 	if (check_parents(hdl, target, &zoned, B_FALSE, NULL) !=3D 0)
 		return (-1);
 
 	(void) parent_name(target, parent, sizeof (parent));
 
 	/* do the clone */
 	if (ZFS_IS_VOLUME(zhp)) {
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 		type =3D ZFS_TYPE_VOLUME;
 	} else {
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 		type =3D ZFS_TYPE_FILESYSTEM;
 	}
 
 	if (props) {
 		if ((props =3D zfs_valid_proplist(hdl, type, props, zoned,
 		    zhp, errbuf)) =3D=3D NULL)
 			return (-1);
 
 		if (zcmd_write_src_nvlist(hdl, &zc, props) !=3D 0) {
 			nvlist_free(props);
 			return (-1);
 		}
 
 		nvlist_free(props);
 	}
 
 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
 	ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
 
 	zcmd_free_nvlists(&zc);
 
 	if (ret !=3D 0) {
 		switch (errno) {
 
 		case ENOENT:
 			/*
 			 * The parent doesn't exist.  We should have caught this
 			 * above, but there may a race condition that has since
 			 * destroyed the parent.
 			 *
 			 * At this point, we don't know whether it's the source
 			 * that doesn't exist anymore, or whether the target
 			 * dataset doesn't exist.
 			 */
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "no such parent '%s'"), parent);
 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
 
 		case EXDEV:
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "source and target pools differ"));
 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
 			    errbuf));
 
 		default:
 			return (zfs_standard_error(zhp->zfs_hdl, errno,
 			    errbuf));
 		}
 	}
 
 	return (ret);
 }
 
 /*
  * Promotes the given clone fs to be the clone parent.
  */
 int
 zfs_promote(zfs_handle_t *zhp)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zfs_cmd_t zc =3D { 0 };
 	char parent[MAXPATHLEN];
 	int ret;
 	char errbuf[1024];
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot promote '%s'"), zhp->zfs_name);
 
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "snapshots can not be promoted"));
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	}
 
 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
 	if (parent[0] =3D=3D '\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 =3D zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
 
 	if (ret !=3D 0) {
 		int save_errno =3D errno;
 
 		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);
 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 
 		default:
 			return (zfs_standard_error(hdl, save_errno, errbuf));
 		}
 	}
 	return (ret);
 }
 
 /*
  * Takes a snapshot of the given dataset.
  */
 int
 zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
     nvlist_t *props)
 {
 	const char *delim;
 	char parent[ZFS_MAXNAMELEN];
 	zfs_handle_t *zhp;
 	zfs_cmd_t zc =3D { 0 };
 	int ret;
 	char errbuf[1024];
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot snapshot '%s'"), path);
 
 	/* validate the target name */
 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	if (props) {
 		if ((props =3D zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
 		    props, B_FALSE, NULL, errbuf)) =3D=3D NULL)
 			return (-1);
 
 		if (zcmd_write_src_nvlist(hdl, &zc, props) !=3D 0) {
 			nvlist_free(props);
 			return (-1);
 		}
 
 		nvlist_free(props);
 	}
 
 	/* make sure the parent exists and is of the appropriate type */
 	delim =3D strchr(path, '@');
 	(void) strncpy(parent, path, delim - path);
 	parent[delim - path] =3D '\0';
 
 	if ((zhp =3D zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
 	    ZFS_TYPE_VOLUME)) =3D=3D NULL) {
 		zcmd_free_nvlists(&zc);
 		return (-1);
 	}
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
 	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 	zc.zc_cookie =3D recursive;
 	ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
 
 	zcmd_free_nvlists(&zc);
 
 	/*
 	 * if it was recursive, the one that actually failed will be in
 	 * zc.zc_name.
 	 */
 	if (ret !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
 		(void) zfs_standard_error(hdl, errno, errbuf);
 	}
 
 	zfs_close(zhp);
 
 	return (ret);
 }
 
 /*
  * Destroy any more recent snapshots.  We invoke this callback on any depen=
 dents
  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then t=
 his
  * is a dependent and we should just destroy it without checking the transa=
 ction
  * group.
  */
 typedef struct rollback_data {
 	const char	*cb_target;		/* the snapshot */
 	uint64_t	cb_create;		/* creation time reference */
 	boolean_t	cb_error;
 	boolean_t	cb_dependent;
 	boolean_t	cb_force;
 } rollback_data_t;
 
 static int
 rollback_destroy(zfs_handle_t *zhp, void *data)
 {
 	rollback_data_t *cbp =3D data;
 
 	if (!cbp->cb_dependent) {
 		if (strcmp(zhp->zfs_name, cbp->cb_target) !=3D 0 &&
 		    zfs_get_type(zhp) =3D=3D ZFS_TYPE_SNAPSHOT &&
 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
 		    cbp->cb_create) {
 			char *logstr;
 
 			cbp->cb_dependent =3D B_TRUE;
 			cbp->cb_error |=3D zfs_iter_dependents(zhp, B_FALSE,
 			    rollback_destroy, cbp);
 			cbp->cb_dependent =3D B_FALSE;
 
 			logstr =3D zhp->zfs_hdl->libzfs_log_str;
 			zhp->zfs_hdl->libzfs_log_str =3D NULL;
 			cbp->cb_error |=3D zfs_destroy(zhp, B_FALSE);
 			zhp->zfs_hdl->libzfs_log_str =3D logstr;
 		}
 	} else {
 		/* We must destroy this clone; first unmount it */
 		prop_changelist_t *clp;
 
 		clp =3D changelist_gather(zhp, ZFS_PROP_NAME, 0,
 		    cbp->cb_force ? MS_FORCE: 0);
 		if (clp =3D=3D NULL || changelist_prefix(clp) !=3D 0) {
 			cbp->cb_error =3D B_TRUE;
 			zfs_close(zhp);
 			return (0);
 		}
 		if (zfs_destroy(zhp, B_FALSE) !=3D 0)
 			cbp->cb_error =3D B_TRUE;
 		else
 			changelist_remove(clp, zhp->zfs_name);
 		(void) changelist_postfix(clp);
 		changelist_free(clp);
 	}
 
 	zfs_close(zhp);
 	return (0);
 }
 
 /*
  * Given a dataset, rollback to a specific snapshot, discarding any
  * data changes since then and making it the active dataset.
  *
  * Any snapshots more recent than the target are destroyed, along with
  * their dependents.
  */
 int
 zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
 {
 	rollback_data_t cb =3D { 0 };
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 	boolean_t restore_resv =3D 0;
 	uint64_t old_volsize, new_volsize;
 	zfs_prop_t resv_prop;
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM ||
 	    zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME);
 
 	/*
 	 * Destroy all recent snapshots and its dependends.
 	 */
 	cb.cb_force =3D force;
 	cb.cb_target =3D snap->zfs_name;
 	cb.cb_create =3D zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
 
 	if (cb.cb_error)
 		return (-1);
 
 	/*
 	 * Now that we have verified that the snapshot is the latest,
 	 * rollback to the given snapshot.
 	 */
 
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME) {
 		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
 			return (-1);
 		old_volsize =3D zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
 		restore_resv =3D
 		    (old_volsize =3D=3D zfs_prop_get_int(zhp, resv_prop));
 	}
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 
 	/*
 	 * 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.
 	 *
 	 */
 	if ((err =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) !=3D 0) {
 		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
 		    zhp->zfs_name);
 		return (err);
 	}
 
 	/*
 	 * For volumes, if the pre-rollback volsize matched the pre-
 	 * rollback reservation and the volsize has changed then set
 	 * the reservation property to the post-rollback volsize.
 	 * Make a new handle since the rollback closed the dataset.
 	 */
 	if ((zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME) &&
 	    (zhp =3D make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
 		if (restore_resv) {
 			new_volsize =3D zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
 			if (old_volsize !=3D new_volsize)
 				err =3D zfs_prop_set_int(zhp, resv_prop,
 				    new_volsize);
 		}
 		zfs_close(zhp);
 	}
 	return (err);
 }
 
 /*
  * Renames the given dataset.
  */
 int
 zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
 {
 	int ret;
 	zfs_cmd_t zc =3D { 0 };
 	char *delim;
 	prop_changelist_t *cl =3D NULL;
 	zfs_handle_t *zhrp =3D NULL;
 	char *parentname =3D NULL;
 	char parent[ZFS_MAXNAMELEN];
 	char property[ZFS_MAXPROPLEN];
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	char errbuf[1024];
 
 	/* if we have the same exact name, just return success */
 	if (strcmp(zhp->zfs_name, target) =3D=3D 0)
 		return (0);
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot rename to '%s'"), target);
 
 	/*
 	 * Make sure the target name is valid
 	 */
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT) {
 		if ((strchr(target, '@') =3D=3D NULL) ||
 		    *target =3D=3D '@') {
 			/*
 			 * Snapshot target name is abbreviated,
 			 * reconstruct full dataset name
 			 */
 			(void) strlcpy(parent, zhp->zfs_name,
 			    sizeof (parent));
 			delim =3D strchr(parent, '@');
 			if (strchr(target, '@') =3D=3D NULL)
 				*(++delim) =3D '\0';
 			else
 				*delim =3D '\0';
 			(void) strlcat(parent, target, sizeof (parent));
 			target =3D parent;
 		} else {
 			/*
 			 * Make sure we're renaming within the same dataset.
 			 */
 			delim =3D strchr(target, '@');
 			if (strncmp(zhp->zfs_name, target, delim - target)
 			    !=3D 0 || zhp->zfs_name[delim - target] !=3D '@') {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "snapshots must be part of same "
 				    "dataset"));
 				return (zfs_error(hdl, EZFS_CROSSTARGET,
 				    errbuf));
 			}
 		}
 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 	} else {
 		if (flags.recurse) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "recursive rename must be a snapshot"));
 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 		}
 
 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 		/* validate parents */
 		if (check_parents(hdl, target, NULL, B_FALSE, NULL) !=3D 0)
 			return (-1);
 
 		/* make sure we're in the same pool */
 		verify((delim =3D strchr(target, '/')) !=3D NULL);
 		if (strncmp(zhp->zfs_name, target, delim - target) !=3D 0 ||
 		    zhp->zfs_name[delim - target] !=3D '/') {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "datasets must be within same pool"));
 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
 		}
 
 		/* new name cannot be a child of the current dataset name */
 		if (is_descendant(zhp->zfs_name, target)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "New dataset name cannot be a descendant of "
 			    "current dataset name"));
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 		}
 	}
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
 
 	if (getzoneid() =3D=3D GLOBAL_ZONEID &&
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "dataset is used in a non-global zone"));
 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
 	}
 
 	/*
 	 * Avoid unmounting file systems with mountpoint property set to
 	 * 'legacy' or 'none' even if -u option is not given.
 	 */
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM &&
 	    !flags.recurse && !flags.nounmount &&
 	    zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
 	    sizeof (property), NULL, NULL, 0, B_FALSE) =3D=3D 0 &&
 	    (strcmp(property, "legacy") =3D=3D 0 ||
 	     strcmp(property, "none") =3D=3D 0)) {
 		flags.nounmount =3D B_TRUE;
 	}
 
 	if (flags.recurse) {
 
 		parentname =3D zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
 		if (parentname =3D=3D NULL) {
 			ret =3D -1;
 			goto error;
 		}
 		delim =3D strchr(parentname, '@');
 		*delim =3D '\0';
 		zhrp =3D zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
 		if (zhrp =3D=3D NULL) {
 			ret =3D -1;
 			goto error;
 		}
 
 	} else {
 		if ((cl =3D changelist_gather(zhp, ZFS_PROP_NAME,
 		    flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0,
 		    flags.forceunmount ? MS_FORCE : 0)) =3D=3D NULL) {
 			return (-1);
 		}
 
 		if (changelist_haszonedchild(cl)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "child dataset with inherited mountpoint is used "
 			    "in a non-global zone"));
 			(void) zfs_error(hdl, EZFS_ZONED, errbuf);
 			goto error;
 		}
 
 		if ((ret =3D changelist_prefix(cl)) !=3D 0)
 			goto error;
 	}
 
 	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 
 	(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 =3D flags.recurse ? 1 : 0;
 	if (flags.nounmount)
 		zc.zc_cookie |=3D 2;
 
 	if ((ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) !=3D 0) {
 		/*
 		 * if it was recursive, the one that actually failed will
 		 * be in zc.zc_name
 		 */
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot rename '%s'"), zc.zc_name);
 
 		if (flags.recurse && errno =3D=3D 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 {
 			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
 		}
 
 		/*
 		 * On failure, we still want to remount any filesystems that
 		 * were previously mounted, so we don't alter the system state.
 		 */
 		if (!flags.recurse)
 			(void) changelist_postfix(cl);
 	} else {
 		if (!flags.recurse) {
 			changelist_rename(cl, zfs_get_name(zhp), target);
 			ret =3D changelist_postfix(cl);
 		}
 	}
 
 error:
 	if (parentname) {
 		free(parentname);
 	}
 	if (zhrp) {
 		zfs_close(zhrp);
 	}
 	if (cl) {
 		changelist_free(cl);
 	}
 	return (ret);
 }
 
 nvlist_t *
 zfs_get_user_props(zfs_handle_t *zhp)
 {
 	return (zhp->zfs_user_props);
 }
 
 nvlist_t *
 zfs_get_recvd_props(zfs_handle_t *zhp)
 {
 	if (zhp->zfs_recvd_props =3D=3D NULL)
 		if (get_recvd_props_ioctl(zhp) !=3D 0)
 			return (NULL);
 	return (zhp->zfs_recvd_props);
 }
 
 /*
  * This function is used by 'zfs list' to determine the exact set of column=
 s to
  * display, and their maximum widths.  This does two main things:
  *
  *      - If this is a list of all properties, then expand the list to incl=
 ude
  *        all native properties, and set a flag so that for each dataset we=
  look
  *        for new unique user properties and add them to the list.
  *
  *      - For non fixed-width properties, keep track of the maximum width s=
 een
  *        so that we can size the column appropriately. If the user has
  *        requested received property values, we also need to compute the w=
 idth
  *        of the RECEIVED column.
  */
 int
 zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t receiv=
 ed)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zprop_list_t *entry;
 	zprop_list_t **last, **start;
 	nvlist_t *userprops, *propval;
 	nvpair_t *elem;
 	char *strval;
 	char buf[ZFS_MAXPROPLEN];
 
 	if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) !=3D 0)
 		return (-1);
 
 	userprops =3D zfs_get_user_props(zhp);
 
 	entry =3D *plp;
 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) !=3D NULL) {
 		/*
 		 * Go through and add any user properties as necessary.  We
 		 * start by incrementing our list pointer to the first
 		 * non-native property.
 		 */
 		start =3D plp;
 		while (*start !=3D NULL) {
 			if ((*start)->pl_prop =3D=3D ZPROP_INVAL)
 				break;
 			start =3D &(*start)->pl_next;
 		}
 
 		elem =3D NULL;
 		while ((elem =3D nvlist_next_nvpair(userprops, elem)) !=3D NULL) {
 			/*
 			 * See if we've already found this property in our list.
 			 */
 			for (last =3D start; *last !=3D NULL;
 			    last =3D &(*last)->pl_next) {
 				if (strcmp((*last)->pl_user_prop,
 				    nvpair_name(elem)) =3D=3D 0)
 					break;
 			}
 
 			if (*last =3D=3D NULL) {
 				if ((entry =3D zfs_alloc(hdl,
 				    sizeof (zprop_list_t))) =3D=3D NULL ||
 				    ((entry->pl_user_prop =3D zfs_strdup(hdl,
 				    nvpair_name(elem)))) =3D=3D NULL) {
 					free(entry);
 					return (-1);
 				}
 
 				entry->pl_prop =3D ZPROP_INVAL;
 				entry->pl_width =3D strlen(nvpair_name(elem));
 				entry->pl_all =3D B_TRUE;
 				*last =3D entry;
 			}
 		}
 	}
 
 	/*
 	 * Now go through and check the width of any non-fixed columns
 	 */
 	for (entry =3D *plp; entry !=3D NULL; entry =3D entry->pl_next) {
 		if (entry->pl_fixed)
 			continue;
 
 		if (entry->pl_prop !=3D ZPROP_INVAL) {
 			if (zfs_prop_get(zhp, entry->pl_prop,
 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) =3D=3D 0) {
 				if (strlen(buf) > entry->pl_width)
 					entry->pl_width =3D strlen(buf);
 			}
 			if (received && zfs_prop_get_recvd(zhp,
 			    zfs_prop_to_name(entry->pl_prop),
 			    buf, sizeof (buf), B_FALSE) =3D=3D 0)
 				if (strlen(buf) > entry->pl_recvd_width)
 					entry->pl_recvd_width =3D strlen(buf);
 		} else {
 			if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
 			    &propval) =3D=3D 0) {
 				verify(nvlist_lookup_string(propval,
 				    ZPROP_VALUE, &strval) =3D=3D 0);
 				if (strlen(strval) > entry->pl_width)
 					entry->pl_width =3D strlen(strval);
 			}
 			if (received && zfs_prop_get_recvd(zhp,
 			    entry->pl_user_prop,
 			    buf, sizeof (buf), B_FALSE) =3D=3D 0)
 				if (strlen(buf) > entry->pl_recvd_width)
 					entry->pl_recvd_width =3D strlen(buf);
 		}
 	}
 
 	return (0);
 }
 
 int
 zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
     char *resource, void *export, void *sharetab,
     int sharemax, zfs_share_op_t operation)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int error;
 
 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
 	if (resource)
 		(void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
 	zc.zc_share.z_sharedata =3D (uint64_t)(uintptr_t)sharetab;
 	zc.zc_share.z_exportdata =3D (uint64_t)(uintptr_t)export;
 	zc.zc_share.z_sharetype =3D operation;
 	zc.zc_share.z_sharemax =3D sharemax;
 	error =3D ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
 	return (error);
 }
 
 void
 zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
 {
 	nvpair_t *curr;
 
 	/*
 	 * Keep a reference to the props-table against which we prune the
 	 * properties.
 	 */
 	zhp->zfs_props_table =3D props;
 
 	curr =3D nvlist_next_nvpair(zhp->zfs_props, NULL);
 
 	while (curr) {
 		zfs_prop_t zfs_prop =3D zfs_name_to_prop(nvpair_name(curr));
 		nvpair_t *next =3D nvlist_next_nvpair(zhp->zfs_props, curr);
 
 		/*
 		 * User properties will result in 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 !=3D ZPROP_INVAL && props[zfs_prop] =3D=3D B_FALSE)
 			(void) nvlist_remove(zhp->zfs_props,
 			    nvpair_name(curr), nvpair_type(curr));
 		curr =3D next;
 	}
 }
 
 #ifdef sun
 static int
 zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
     zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
 {
 	zfs_cmd_t zc =3D { 0 };
 	nvlist_t *nvlist =3D NULL;
 	int error;
 
 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
 	zc.zc_cookie =3D (uint64_t)cmd;
 
 	if (cmd =3D=3D ZFS_SMB_ACL_RENAME) {
 		if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) !=3D 0) {
 			(void) no_memory(hdl);
 			return (NULL);
 		}
 	}
 
 	switch (cmd) {
 	case ZFS_SMB_ACL_ADD:
 	case ZFS_SMB_ACL_REMOVE:
 		(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
 		break;
 	case ZFS_SMB_ACL_RENAME:
 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
 		    resource1) !=3D 0) {
 				(void) no_memory(hdl);
 				return (-1);
 		}
 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
 		    resource2) !=3D 0) {
 				(void) no_memory(hdl);
 				return (-1);
 		}
 		if (zcmd_write_src_nvlist(hdl, &zc, nvlist) !=3D 0) {
 			nvlist_free(nvlist);
 			return (-1);
 		}
 		break;
 	case ZFS_SMB_ACL_PURGE:
 		break;
 	default:
 		return (-1);
 	}
 	error =3D ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
 	if (nvlist)
 		nvlist_free(nvlist);
 	return (error);
 }
 
 int
 zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
     char *path, char *resource)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
 	    resource, NULL));
 }
 
 int
 zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
     char *path, char *resource)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
 	    resource, NULL));
 }
 
 int
 zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
 	    NULL, NULL));
 }
 
 int
 zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
     char *oldname, char *newname)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
 	    oldname, newname));
 }
 #endif	/* sun */
 
 int
 zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
     zfs_userspace_cb_t func, void *arg)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int error;
 	zfs_useracct_t buf[100];
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	zc.zc_objset_type =3D type;
 	zc.zc_nvlist_dst =3D (uintptr_t)buf;
 
 	/* CONSTCOND */
 	while (1) {
 		zfs_useracct_t *zua =3D buf;
 
 		zc.zc_nvlist_dst_size =3D sizeof (buf);
 		error =3D ioctl(zhp->zfs_hdl->libzfs_fd,
 		    ZFS_IOC_USERSPACE_MANY, &zc);
 		if (error || zc.zc_nvlist_dst_size =3D=3D 0)
 			break;
 
 		while (zc.zc_nvlist_dst_size > 0) {
 			error =3D func(arg, zua->zu_domain, zua->zu_rid,
 			    zua->zu_space);
 			if (error !=3D 0)
 				return (error);
 			zua++;
 			zc.zc_nvlist_dst_size -=3D sizeof (zfs_useracct_t);
 		}
 	}
 
 	return (error);
 }
 
 int
 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
     boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
     int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 
 	ASSERT(!recursive || dsobj =3D=3D 0);
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
 	    >=3D sizeof (zc.zc_string))
 		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
 	zc.zc_cookie =3D recursive;
 	zc.zc_temphold =3D temphold;
 	zc.zc_cleanup_fd =3D cleanup_fd;
 	zc.zc_sendobj =3D dsobj;
 	zc.zc_createtxg =3D createtxg;
 
 	if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) !=3D 0) {
 		char errbuf[ZFS_MAXNAMELEN+32];
 
 		/*
 		 * if it was recursive, the one that actually failed will be in
 		 * zc.zc_name.
 		 */
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot hold '%s@%s'"), zc.zc_name, snapname);
 		switch (errno) {
 		case E2BIG:
 			/*
 			 * Temporary tags wind up having the ds object id
 			 * prepended. So even if we passed the length check
 			 * above, it's still possible for the tag to wind
 			 * up being slightly too long.
 			 */
 			return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf));
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
 		case EINVAL:
 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 		case EEXIST:
 			return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
 		case ENOENT:
 			if (enoent_ok)
 				return (ENOENT);
 			/* FALLTHROUGH */
 		default:
 			return (zfs_standard_error_fmt(hdl, errno, errbuf));
 		}
 	}
 
 	return (0);
 }
 
 int
 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
     boolean_t recursive)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
 	    >=3D sizeof (zc.zc_string))
 		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
 	zc.zc_cookie =3D recursive;
 
 	if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) !=3D 0) {
 		char errbuf[ZFS_MAXNAMELEN+32];
 
 		/*
 		 * if it was recursive, the one that actually failed will be in
 		 * zc.zc_name.
 		 */
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot release '%s' from '%s@%s'"), tag, zc.zc_name,
 		    snapname);
 		switch (errno) {
 		case ESRCH:
 			return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
 		case EINVAL:
 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 		default:
 			return (zfs_standard_error_fmt(hdl, errno, errbuf));
 		}
 	}
 
 	return (0);
 }
 
 int
 zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	int nvsz =3D 2048;
 	void *nvbuf;
 	int err =3D 0;
 	char errbuf[ZFS_MAXNAMELEN+32];
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME ||
 	    zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM);
 
 tryagain:
 
 	nvbuf =3D malloc(nvsz);
 	if (nvbuf =3D=3D NULL) {
 		err =3D (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
 		goto out;
 	}
 
 	zc.zc_nvlist_dst_size =3D nvsz;
 	zc.zc_nvlist_dst =3D (uintptr_t)nvbuf;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
 
 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
 		    zc.zc_name);
 		switch (errno) {
 		case ENOMEM:
 			free(nvbuf);
 			nvsz =3D zc.zc_nvlist_dst_size;
 			goto tryagain;
 
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			err =3D zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 		case EINVAL:
 			err =3D zfs_error(hdl, EZFS_BADTYPE, errbuf);
 			break;
 		case ENOENT:
 			err =3D zfs_error(hdl, EZFS_NOENT, errbuf);
 			break;
 		default:
 			err =3D zfs_standard_error_fmt(hdl, errno, errbuf);
 			break;
 		}
 	} else {
 		/* success */
 		int rc =3D nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
 		if (rc) {
 			(void) snprintf(errbuf, sizeof (errbuf), dgettext(
 			    TEXT_DOMAIN, "cannot get permissions on '%s'"),
 			    zc.zc_name);
 			err =3D zfs_standard_error_fmt(hdl, rc, errbuf);
 		}
 	}
 
 	free(nvbuf);
 out:
 	return (err);
 }
 
 int
 zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	char *nvbuf;
 	char errbuf[ZFS_MAXNAMELEN+32];
 	size_t nvsz;
 	int err;
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME ||
 	    zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM);
 
 	err =3D nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
 	assert(err =3D=3D 0);
 
 	nvbuf =3D malloc(nvsz);
 
 	err =3D nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
 	assert(err =3D=3D 0);
 
 	zc.zc_nvlist_src_size =3D nvsz;
 	zc.zc_nvlist_src =3D (uintptr_t)nvbuf;
 	zc.zc_perm_action =3D un;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
 		    zc.zc_name);
 		switch (errno) {
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			err =3D zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 		case EINVAL:
 			err =3D zfs_error(hdl, EZFS_BADTYPE, errbuf);
 			break;
 		case ENOENT:
 			err =3D zfs_error(hdl, EZFS_NOENT, errbuf);
 			break;
 		default:
 			err =3D zfs_standard_error_fmt(hdl, errno, errbuf);
 			break;
 		}
 	}
 
 	free(nvbuf);
 
 	return (err);
 }
 
 int
 zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	int nvsz =3D 2048;
 	void *nvbuf;
 	int err =3D 0;
 	char errbuf[ZFS_MAXNAMELEN+32];
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT);
 
 tryagain:
 
 	nvbuf =3D malloc(nvsz);
 	if (nvbuf =3D=3D NULL) {
 		err =3D (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
 		goto out;
 	}
 
 	zc.zc_nvlist_dst_size =3D nvsz;
 	zc.zc_nvlist_dst =3D (uintptr_t)nvbuf;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
 
 	if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
 		    zc.zc_name);
 		switch (errno) {
 		case ENOMEM:
 			free(nvbuf);
 			nvsz =3D zc.zc_nvlist_dst_size;
 			goto tryagain;
 
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			err =3D zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 		case EINVAL:
 			err =3D zfs_error(hdl, EZFS_BADTYPE, errbuf);
 			break;
 		case ENOENT:
 			err =3D zfs_error(hdl, EZFS_NOENT, errbuf);
 			break;
 		default:
 			err =3D zfs_standard_error_fmt(hdl, errno, errbuf);
 			break;
 		}
 	} else {
 		/* success */
 		int rc =3D nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
 		if (rc) {
 			(void) snprintf(errbuf, sizeof (errbuf),
 			    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
 			    zc.zc_name);
 			err =3D zfs_standard_error_fmt(hdl, rc, errbuf);
 		}
 	}
 
 	free(nvbuf);
 out:
 	return (err);
 }
 
 uint64_t
 zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
 {
 	uint64_t numdb;
 	uint64_t nblocks, volblocksize;
 	int ncopies;
 	char *strval;
 
 	if (nvlist_lookup_string(props,
 	    zfs_prop_to_name(ZFS_PROP_COPIES), &strval) =3D=3D 0)
 		ncopies =3D atoi(strval);
 	else
 		ncopies =3D 1;
 	if (nvlist_lookup_uint64(props,
 	    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
 	    &volblocksize) !=3D 0)
 		volblocksize =3D ZVOL_DEFAULT_BLOCKSIZE;
 	nblocks =3D volsize/volblocksize;
 	/* start with metadnode L0-L6 */
 	numdb =3D 7;
 	/* calculate number of indirects */
 	while (nblocks > 1) {
 		nblocks +=3D DNODES_PER_LEVEL - 1;
 		nblocks /=3D DNODES_PER_LEVEL;
 		numdb +=3D nblocks;
 	}
 	numdb *=3D MIN(SPA_DVAS_PER_BP, ncopies + 1);
 	volsize *=3D ncopies;
 	/*
 	 * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
 	 * compressed, but in practice they compress down to about
 	 * 1100 bytes
 	 */
 	numdb *=3D 1ULL << DN_MAX_INDBLKSHIFT;
 	volsize +=3D numdb;
 	return (volsize);
 }
 
 /*
  * Attach/detach the given filesystem to/from the given jail.
  */
 int
 zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zfs_cmd_t zc =3D { 0 };
 	char errbuf[1024];
 	unsigned long cmd;
 	int ret;
 
 	if (attach) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
 	} else {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
 	}
 
 	switch (zhp->zfs_type) {
 	case ZFS_TYPE_VOLUME:
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "volumes can not be jailed"));
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	case ZFS_TYPE_SNAPSHOT:
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "snapshots can not be jailed"));
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	}
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM);
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	zc.zc_objset_type =3D DMU_OST_ZFS;
 	zc.zc_jailid =3D jailid;
 
 	cmd =3D attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
 	if ((ret =3D ioctl(hdl->libzfs_fd, cmd, &zc)) !=3D 0)
 		zfs_standard_error(hdl, errno, errbuf);
 
 	return (ret);
 }
 
 
 --=-eOAg1opG/d7eXMURa76A
 Content-Type: text/plain; name="libzfs_dataset.c"
 Content-Transfer-Encoding: quoted-printable
 Content-Disposition: attachment; filename="libzfs_dataset.c"
 
 /*
  * CDDL HEADER START
  *
  * The contents of this file are subject to the terms of the
  * Common Development and Distribution License (the "License").
  * You may not use this file except in compliance with the License.
  *
  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  * or http://www.opensolaris.org/os/licensing.
  * See the License for the specific language governing permissions
  * and limitations under the License.
  *
  * When distributing Covered Code, include this CDDL HEADER in each
  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  * If applicable, add the following below this CDDL HEADER, with the
  * fields enclosed by brackets "[]" replaced with your own identifying
  * information: Portions Copyright [yyyy] [name of copyright owner]
  *
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reser=
 ved.
  * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2011 by Delphix. All rights reserved.
  * Copyright (c) 2012 DEY Storage Systems, Inc.  All rights reserved.
  * Copyright (c) 2011-2012 Pawel Jakub Dawidek <pawel@dawidek.net>.
  * All rights reserved.
  * Copyright (c) 2012 Martin Matuska <mm@FreeBSD.org>. All rights reserved.
  */
 
 #include <ctype.h>
 #include <errno.h>
 #include <libintl.h>
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <strings.h>
 #include <unistd.h>
 #include <stddef.h>
 #include <zone.h>
 #include <fcntl.h>
 #include <sys/mntent.h>
 #include <sys/mount.h>
 #include <priv.h>
 #include <pwd.h>
 #include <grp.h>
 #include <stddef.h>
 #include <idmap.h>
 
 #include <sys/dnode.h>
 #include <sys/spa.h>
 #include <sys/zap.h>
 #include <sys/misc.h>
 #include <libzfs.h>
 
 #include "zfs_namecheck.h"
 #include "zfs_prop.h"
 #include "libzfs_impl.h"
 #include "zfs_deleg.h"
 
 static int userquota_propname_decode(const char *propname, boolean_t zoned,
     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *rid=
 p);
 
 /*
  * Given a single type (not a mask of types), return the type in a human
  * readable form.
  */
 const char *
 zfs_type_to_name(zfs_type_t type)
 {
 	switch (type) {
 	case ZFS_TYPE_FILESYSTEM:
 		return (dgettext(TEXT_DOMAIN, "filesystem"));
 	case ZFS_TYPE_SNAPSHOT:
 		return (dgettext(TEXT_DOMAIN, "snapshot"));
 	case ZFS_TYPE_VOLUME:
 		return (dgettext(TEXT_DOMAIN, "volume"));
 	}
 
 	return (NULL);
 }
 
 /*
  * Given a path and mask of ZFS types, return a string describing this data=
 set.
  * This is used when we fail to open a dataset and we cannot get an exact t=
 ype.
  * We guess what the type would have been based on the path and the mask of
  * acceptable types.
  */
 static const char *
 path_to_str(const char *path, int types)
 {
 	/*
 	 * When given a single type, always report the exact type.
 	 */
 	if (types =3D=3D ZFS_TYPE_SNAPSHOT)
 		return (dgettext(TEXT_DOMAIN, "snapshot"));
 	if (types =3D=3D ZFS_TYPE_FILESYSTEM)
 		return (dgettext(TEXT_DOMAIN, "filesystem"));
 	if (types =3D=3D ZFS_TYPE_VOLUME)
 		return (dgettext(TEXT_DOMAIN, "volume"));
 
 	/*
 	 * The user is requesting more than one type of dataset.  If this is the
 	 * case, consult the path itself.  If we're looking for a snapshot, and
 	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
 	 * snapshot attribute and try again.
 	 */
 	if (types & ZFS_TYPE_SNAPSHOT) {
 		if (strchr(path, '@') !=3D NULL)
 			return (dgettext(TEXT_DOMAIN, "snapshot"));
 		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
 	}
 
 	/*
 	 * The user has requested either filesystems or volumes.
 	 * We have no way of knowing a priori what type this would be, so always
 	 * report it as "filesystem" or "volume", our two primitive types.
 	 */
 	if (types & ZFS_TYPE_FILESYSTEM)
 		return (dgettext(TEXT_DOMAIN, "filesystem"));
 
 	assert(types & ZFS_TYPE_VOLUME);
 	return (dgettext(TEXT_DOMAIN, "volume"));
 }
 
 /*
  * Validate a ZFS path.  This is used even before trying to open the datase=
 t, to
  * provide a more meaningful error message.  We call zfs_error_aux() to
  * explain exactly why the name was not valid.
  */
 int
 zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
     boolean_t modifying)
 {
 	namecheck_err_t why;
 	char what;
 
 	(void) zfs_prop_get_table();
 	if (dataset_namecheck(path, &why, &what) !=3D 0) {
 		if (hdl !=3D NULL) {
 			switch (why) {
 			case NAME_ERR_TOOLONG:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "name is too long"));
 				break;
 
 			case NAME_ERR_LEADING_SLASH:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "leading slash in name"));
 				break;
 
 			case NAME_ERR_EMPTY_COMPONENT:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "empty component in name"));
 				break;
 
 			case NAME_ERR_TRAILING_SLASH:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "trailing slash in name"));
 				break;
 
 			case NAME_ERR_INVALCHAR:
 				zfs_error_aux(hdl,
 				    dgettext(TEXT_DOMAIN, "invalid character "
 				    "'%c' in name"), what);
 				break;
 
 			case NAME_ERR_MULTIPLE_AT:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "multiple '@' delimiters in name"));
 				break;
 
 			case NAME_ERR_NOLETTER:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "pool doesn't begin with a letter"));
 				break;
 
 			case NAME_ERR_RESERVED:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "name is reserved"));
 				break;
 
 			case NAME_ERR_DISKLIKE:
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "reserved disk name"));
 				break;
 			}
 		}
 
 		return (0);
 	}
 
 	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') !=3D NULL) {
 		if (hdl !=3D NULL)
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "snapshot delimiter '@' in filesystem name"));
 		return (0);
 	}
 
 	if (type =3D=3D ZFS_TYPE_SNAPSHOT && strchr(path, '@') =3D=3D NULL) {
 		if (hdl !=3D NULL)
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "missing '@' delimiter in snapshot name"));
 		return (0);
 	}
 
 	if (modifying && strchr(path, '%') !=3D NULL) {
 		if (hdl !=3D NULL)
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid character %c in name"), '%');
 		return (0);
 	}
 
 	return (-1);
 }
 
 int
 zfs_name_valid(const char *name, zfs_type_t type)
 {
 	if (type =3D=3D ZFS_TYPE_POOL)
 		return (zpool_name_valid(NULL, B_FALSE, name));
 	return (zfs_validate_name(NULL, name, type, B_FALSE));
 }
 
 /*
  * This function takes the raw DSL properties, and filters out the user-def=
 ined
  * properties into a separate nvlist.
  */
 static nvlist_t *
 process_user_props(zfs_handle_t *zhp, nvlist_t *props)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	nvpair_t *elem;
 	nvlist_t *propval;
 	nvlist_t *nvl;
 
 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) !=3D 0) {
 		(void) no_memory(hdl);
 		return (NULL);
 	}
 
 	elem =3D NULL;
 	while ((elem =3D nvlist_next_nvpair(props, elem)) !=3D NULL) {
 		if (!zfs_prop_user(nvpair_name(elem)))
 			continue;
 
 		verify(nvpair_value_nvlist(elem, &propval) =3D=3D 0);
 		if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) !=3D 0) {
 			nvlist_free(nvl);
 			(void) no_memory(hdl);
 			return (NULL);
 		}
 	}
 
 	return (nvl);
 }
 
 static zpool_handle_t *
 zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zpool_handle_t *zph;
 
 	if ((zph =3D zpool_open_canfail(hdl, pool_name)) !=3D NULL) {
 		if (hdl->libzfs_pool_handles !=3D NULL)
 			zph->zpool_next =3D hdl->libzfs_pool_handles;
 		hdl->libzfs_pool_handles =3D zph;
 	}
 	return (zph);
 }
 
 static zpool_handle_t *
 zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zpool_handle_t *zph =3D hdl->libzfs_pool_handles;
 
 	while ((zph !=3D NULL) &&
 	    (strncmp(pool_name, zpool_get_name(zph), len) !=3D 0))
 		zph =3D zph->zpool_next;
 	return (zph);
 }
 
 /*
  * Returns a handle to the pool that contains the provided dataset.
  * If a handle to that pool already exists then that handle is returned.
  * Otherwise, a new handle is created and added to the list of handles.
  */
 static zpool_handle_t *
 zpool_handle(zfs_handle_t *zhp)
 {
 	char *pool_name;
 	int len;
 	zpool_handle_t *zph;
 
 	len =3D strcspn(zhp->zfs_name, "/@") + 1;
 	pool_name =3D zfs_alloc(zhp->zfs_hdl, len);
 	(void) strlcpy(pool_name, zhp->zfs_name, len);
 
 	zph =3D zpool_find_handle(zhp, pool_name, len);
 	if (zph =3D=3D NULL)
 		zph =3D zpool_add_handle(zhp, pool_name);
 
 	free(pool_name);
 	return (zph);
 }
 
 void
 zpool_free_handles(libzfs_handle_t *hdl)
 {
 	zpool_handle_t *next, *zph =3D hdl->libzfs_pool_handles;
 
 	while (zph !=3D NULL) {
 		next =3D zph->zpool_next;
 		zpool_close(zph);
 		zph =3D next;
 	}
 	hdl->libzfs_pool_handles =3D NULL;
 }
 
 /*
  * Utility function to gather stats (objset and zpl) for the given object.
  */
 static int
 get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 
 	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
 
 	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) !=3D 0) {
 		if (errno =3D=3D ENOMEM) {
 			if (zcmd_expand_dst_nvlist(hdl, zc) !=3D 0) {
 				return (-1);
 			}
 		} else {
 			return (-1);
 		}
 	}
 	return (0);
 }
 
 /*
  * Utility function to get the received properties of the given object.
  */
 static int
 get_recvd_props_ioctl(zfs_handle_t *zhp)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	nvlist_t *recvdprops;
 	zfs_cmd_t zc =3D { 0 };
 	int err;
 
 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) !=3D 0)
 		return (-1);
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) !=3D 0) {
 		if (errno =3D=3D ENOMEM) {
 			if (zcmd_expand_dst_nvlist(hdl, &zc) !=3D 0) {
 				return (-1);
 			}
 		} else {
 			zcmd_free_nvlists(&zc);
 			return (-1);
 		}
 	}
 
 	err =3D zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
 	zcmd_free_nvlists(&zc);
 	if (err !=3D 0)
 		return (-1);
 
 	nvlist_free(zhp->zfs_recvd_props);
 	zhp->zfs_recvd_props =3D recvdprops;
 
 	return (0);
 }
 
 static int
 put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
 {
 	nvlist_t *allprops, *userprops;
 
 	zhp->zfs_dmustats =3D zc->zc_objset_stats; /* structure assignment */
 
 	if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) !=3D 0) {
 		return (-1);
 	}
 
 	/*
 	 * XXX Why do we store the user props separately, in addition to
 	 * storing them in zfs_props?
 	 */
 	if ((userprops =3D process_user_props(zhp, allprops)) =3D=3D NULL) {
 		nvlist_free(allprops);
 		return (-1);
 	}
 
 	nvlist_free(zhp->zfs_props);
 	nvlist_free(zhp->zfs_user_props);
 
 	zhp->zfs_props =3D allprops;
 	zhp->zfs_user_props =3D userprops;
 
 	return (0);
 }
 
 static int
 get_stats(zfs_handle_t *zhp)
 {
 	int rc =3D 0;
 	zfs_cmd_t zc =3D { 0 };
 
 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) !=3D 0)
 		return (-1);
 	if (get_stats_ioctl(zhp, &zc) !=3D 0)
 		rc =3D -1;
 	else if (put_stats_zhdl(zhp, &zc) !=3D 0)
 		rc =3D -1;
 	zcmd_free_nvlists(&zc);
 	return (rc);
 }
 
 /*
  * Refresh the properties currently stored in the handle.
  */
 void
 zfs_refresh_properties(zfs_handle_t *zhp)
 {
 	(void) get_stats(zhp);
 }
 
 /*
  * Makes a handle from the given dataset name.  Used by zfs_open() and
  * zfs_iter_* to create child handles on the fly.
  */
 static int
 make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
 {
 	if (put_stats_zhdl(zhp, zc) !=3D 0)
 		return (-1);
 
 	/*
 	 * We've managed to open the dataset and gather statistics.  Determine
 	 * the high-level type.
 	 */
 	if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZVOL)
 		zhp->zfs_head_type =3D ZFS_TYPE_VOLUME;
 	else if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZFS)
 		zhp->zfs_head_type =3D ZFS_TYPE_FILESYSTEM;
 	else
 		abort();
 
 	if (zhp->zfs_dmustats.dds_is_snapshot)
 		zhp->zfs_type =3D ZFS_TYPE_SNAPSHOT;
 	else if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZVOL)
 		zhp->zfs_type =3D ZFS_TYPE_VOLUME;
 	else if (zhp->zfs_dmustats.dds_type =3D=3D DMU_OST_ZFS)
 		zhp->zfs_type =3D ZFS_TYPE_FILESYSTEM;
 	else
 		abort();	/* we should never see any other types */
 
 	if ((zhp->zpool_hdl =3D zpool_handle(zhp)) =3D=3D NULL)
 		return (-1);
 
 	return (0);
 }
 
 zfs_handle_t *
 make_dataset_handle(libzfs_handle_t *hdl, const char *path)
 {
 	zfs_cmd_t zc =3D { 0 };
 
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D hdl;
 	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) !=3D 0) {
 		free(zhp);
 		return (NULL);
 	}
 	if (get_stats_ioctl(zhp, &zc) =3D=3D -1) {
 		zcmd_free_nvlists(&zc);
 		free(zhp);
 		return (NULL);
 	}
 	if (make_dataset_handle_common(zhp, &zc) =3D=3D -1) {
 		free(zhp);
 		zhp =3D NULL;
 	}
 	zcmd_free_nvlists(&zc);
 	return (zhp);
 }
 
 zfs_handle_t *
 make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
 {
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D hdl;
 	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
 	if (make_dataset_handle_common(zhp, zc) =3D=3D -1) {
 		free(zhp);
 		return (NULL);
 	}
 	return (zhp);
 }
 
 zfs_handle_t *
 make_dataset_simple_handle_zc(zfs_handle_t *pzhp, zfs_cmd_t *zc)
 {
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D pzhp->zfs_hdl;
 	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
 	zhp->zfs_head_type =3D pzhp->zfs_type;
 	zhp->zfs_type =3D ZFS_TYPE_SNAPSHOT;
 	zhp->zpool_hdl =3D zpool_handle(zhp);
 	return (zhp);
 }
 
 zfs_handle_t *
 zfs_handle_dup(zfs_handle_t *zhp_orig)
 {
 	zfs_handle_t *zhp =3D calloc(sizeof (zfs_handle_t), 1);
 
 	if (zhp =3D=3D NULL)
 		return (NULL);
 
 	zhp->zfs_hdl =3D zhp_orig->zfs_hdl;
 	zhp->zpool_hdl =3D zhp_orig->zpool_hdl;
 	(void) strlcpy(zhp->zfs_name, zhp_orig->zfs_name,
 	    sizeof (zhp->zfs_name));
 	zhp->zfs_type =3D zhp_orig->zfs_type;
 	zhp->zfs_head_type =3D zhp_orig->zfs_head_type;
 	zhp->zfs_dmustats =3D zhp_orig->zfs_dmustats;
 	if (zhp_orig->zfs_props !=3D NULL) {
 		if (nvlist_dup(zhp_orig->zfs_props, &zhp->zfs_props, 0) !=3D 0) {
 			(void) no_memory(zhp->zfs_hdl);
 			zfs_close(zhp);
 			return (NULL);
 		}
 	}
 	if (zhp_orig->zfs_user_props !=3D NULL) {
 		if (nvlist_dup(zhp_orig->zfs_user_props,
 		    &zhp->zfs_user_props, 0) !=3D 0) {
 			(void) no_memory(zhp->zfs_hdl);
 			zfs_close(zhp);
 			return (NULL);
 		}
 	}
 	if (zhp_orig->zfs_recvd_props !=3D NULL) {
 		if (nvlist_dup(zhp_orig->zfs_recvd_props,
 		    &zhp->zfs_recvd_props, 0)) {
 			(void) no_memory(zhp->zfs_hdl);
 			zfs_close(zhp);
 			return (NULL);
 		}
 	}
 	zhp->zfs_mntcheck =3D zhp_orig->zfs_mntcheck;
 	if (zhp_orig->zfs_mntopts !=3D NULL) {
 		zhp->zfs_mntopts =3D zfs_strdup(zhp_orig->zfs_hdl,
 		    zhp_orig->zfs_mntopts);
 	}
 	zhp->zfs_props_table =3D zhp_orig->zfs_props_table;
 	return (zhp);
 }
 
 /*
  * Opens the given snapshot, 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.
  */
 zfs_handle_t *
 zfs_open(libzfs_handle_t *hdl, const char *path, int types)
 {
 	zfs_handle_t *zhp;
 	char errbuf[1024];
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
 
 	/*
 	 * 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"));
 		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
 		return (NULL);
 	}
 
 	/*
 	 * Try to get stats for the dataset, which will tell us if it exists.
 	 */
 	errno =3D 0;
 	if ((zhp =3D make_dataset_handle(hdl, path)) =3D=3D NULL) {
 		(void) zfs_standard_error(hdl, errno, errbuf);
 		return (NULL);
 	}
 
 	if (!(types & zhp->zfs_type)) {
 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
 		zfs_close(zhp);
 		return (NULL);
 	}
 
 	return (zhp);
 }
 
 /*
  * Release a ZFS handle.  Nothing to do but free the associated memory.
  */
 void
 zfs_close(zfs_handle_t *zhp)
 {
 	if (zhp->zfs_mntopts)
 		free(zhp->zfs_mntopts);
 	nvlist_free(zhp->zfs_props);
 	nvlist_free(zhp->zfs_user_props);
 	nvlist_free(zhp->zfs_recvd_props);
 	free(zhp);
 }
 
 typedef struct mnttab_node {
 	struct mnttab mtn_mt;
 	avl_node_t mtn_node;
 } mnttab_node_t;
 
 static int
 libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
 {
 	const mnttab_node_t *mtn1 =3D arg1;
 	const mnttab_node_t *mtn2 =3D arg2;
 	int rv;
 
 	rv =3D strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
 
 	if (rv =3D=3D 0)
 		return (0);
 	return (rv > 0 ? 1 : -1);
 }
 
 void
 libzfs_mnttab_init(libzfs_handle_t *hdl)
 {
 	assert(avl_numnodes(&hdl->libzfs_mnttab_cache) =3D=3D 0);
 	avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
 	    sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
 }
 
 void
 libzfs_mnttab_update(libzfs_handle_t *hdl)
 {
 	struct mnttab entry;
 
 	rewind(hdl->libzfs_mnttab);
 	while (getmntent(hdl->libzfs_mnttab, &entry) =3D=3D 0) {
 		mnttab_node_t *mtn;
 
 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) !=3D 0)
 			continue;
 		mtn =3D zfs_alloc(hdl, sizeof (mnttab_node_t));
 		mtn->mtn_mt.mnt_special =3D zfs_strdup(hdl, entry.mnt_special);
 		mtn->mtn_mt.mnt_mountp =3D zfs_strdup(hdl, entry.mnt_mountp);
 		mtn->mtn_mt.mnt_fstype =3D zfs_strdup(hdl, entry.mnt_fstype);
 		mtn->mtn_mt.mnt_mntopts =3D zfs_strdup(hdl, entry.mnt_mntopts);
 		avl_add(&hdl->libzfs_mnttab_cache, mtn);
 	}
 }
 
 void
 libzfs_mnttab_fini(libzfs_handle_t *hdl)
 {
 	void *cookie =3D NULL;
 	mnttab_node_t *mtn;
 
 	while (mtn =3D avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
 		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);
 	}
 	avl_destroy(&hdl->libzfs_mnttab_cache);
 }
 
 void
 libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
 {
 	hdl->libzfs_mnttab_enable =3D enable;
 }
 
 int
 libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
     struct mnttab *entry)
 {
 	mnttab_node_t find;
 	mnttab_node_t *mtn;
 
 	if (!hdl->libzfs_mnttab_enable) {
 		struct mnttab srch =3D { 0 };
 
 		if (avl_numnodes(&hdl->libzfs_mnttab_cache))
 			libzfs_mnttab_fini(hdl);
 		rewind(hdl->libzfs_mnttab);
 		srch.mnt_special =3D (char *)fsname;
 		srch.mnt_fstype =3D MNTTYPE_ZFS;
 		if (getmntany(hdl->libzfs_mnttab, entry, &srch) =3D=3D 0)
 			return (0);
 		else
 			return (ENOENT);
 	}
 
 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) =3D=3D 0)
 		libzfs_mnttab_update(hdl);
 
 	find.mtn_mt.mnt_special =3D (char *)fsname;
 	mtn =3D avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
 	if (mtn) {
 		*entry =3D mtn->mtn_mt;
 		return (0);
 	}
 	return (ENOENT);
 }
 
 void
 libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
     const char *mountp, const char *mntopts)
 {
 	mnttab_node_t *mtn;
 
 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) =3D=3D 0)
 		return;
 	mtn =3D zfs_alloc(hdl, sizeof (mnttab_node_t));
 	mtn->mtn_mt.mnt_special =3D zfs_strdup(hdl, special);
 	mtn->mtn_mt.mnt_mountp =3D zfs_strdup(hdl, mountp);
 	mtn->mtn_mt.mnt_fstype =3D zfs_strdup(hdl, MNTTYPE_ZFS);
 	mtn->mtn_mt.mnt_mntopts =3D zfs_strdup(hdl, mntopts);
 	avl_add(&hdl->libzfs_mnttab_cache, mtn);
 }
 
 void
 libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
 {
 	mnttab_node_t find;
 	mnttab_node_t *ret;
 
 	find.mtn_mt.mnt_special =3D (char *)fsname;
 	if (ret =3D avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
 		avl_remove(&hdl->libzfs_mnttab_cache, ret);
 		free(ret->mtn_mt.mnt_special);
 		free(ret->mtn_mt.mnt_mountp);
 		free(ret->mtn_mt.mnt_fstype);
 		free(ret->mtn_mt.mnt_mntopts);
 		free(ret);
 	}
 }
 
 int
 zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
 {
 	zpool_handle_t *zpool_handle =3D zhp->zpool_hdl;
 
 	if (zpool_handle =3D=3D NULL)
 		return (-1);
 
 	*spa_version =3D zpool_get_prop_int(zpool_handle,
 	    ZPOOL_PROP_VERSION, NULL);
 	return (0);
 }
 
 /*
  * The choice of reservation property depends on the SPA version.
  */
 static int
 zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
 {
 	int spa_version;
 
 	if (zfs_spa_version(zhp, &spa_version) < 0)
 		return (-1);
 
 	if (spa_version >=3D SPA_VERSION_REFRESERVATION)
 		*resv_prop =3D ZFS_PROP_REFRESERVATION;
 	else
 		*resv_prop =3D ZFS_PROP_RESERVATION;
 
 	return (0);
 }
 
 /*
  * Given an nvlist of properties to set, validates that they are correct, a=
 nd
  * parses any numeric properties (index, boolean, etc) if they are specifie=
 d as
  * strings.
  */
 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)
 {
 	nvpair_t *elem;
 	uint64_t intval;
 	char *strval;
 	zfs_prop_t prop;
 	nvlist_t *ret;
 	int chosen_normal =3D -1;
 	int chosen_utf =3D -1;
 
 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) !=3D 0) {
 		(void) no_memory(hdl);
 		return (NULL);
 	}
 
 	/*
 	 * Make sure this property is valid and applies to this type.
 	 */
 
 	elem =3D NULL;
 	while ((elem =3D nvlist_next_nvpair(nvl, elem)) !=3D NULL) {
 		const char *propname =3D nvpair_name(elem);
 
 		prop =3D zfs_name_to_prop(propname);
 		if (prop =3D=3D ZPROP_INVAL && zfs_prop_user(propname)) {
 			/*
 			 * This is a user property: make sure it's a
 			 * string, and that it's less than ZAP_MAXNAMELEN.
 			 */
 			if (nvpair_type(elem) !=3D DATA_TYPE_STRING) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' must be a string"), propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			if (strlen(nvpair_name(elem)) >=3D ZAP_MAXNAMELEN) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "property name '%s' is too long"),
 				    propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			(void) nvpair_value_string(elem, &strval);
 			if (nvlist_add_string(ret, propname, strval) !=3D 0) {
 				(void) no_memory(hdl);
 				goto error;
 			}
 			continue;
 		}
 
 		/*
 		 * Currently, only user properties can be modified on
 		 * snapshots.
 		 */
 		if (type =3D=3D ZFS_TYPE_SNAPSHOT) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "this property can not be modified for snapshots"));
 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
 			goto error;
 		}
 
 		if (prop =3D=3D ZPROP_INVAL && zfs_prop_userquota(propname)) {
 			zfs_userquota_prop_t uqtype;
 			char newpropname[128];
 			char domain[128];
 			uint64_t rid;
 			uint64_t valary[3];
 
 			if (userquota_propname_decode(propname, zoned,
 			    &uqtype, domain, sizeof (domain), &rid) !=3D 0) {
 				zfs_error_aux(hdl,
 				    dgettext(TEXT_DOMAIN,
 				    "'%s' has an invalid user/group name"),
 				    propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			if (uqtype !=3D ZFS_PROP_USERQUOTA &&
 			    uqtype !=3D ZFS_PROP_GROUPQUOTA) {
 				zfs_error_aux(hdl,
 				    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
 				    propname);
 				(void) zfs_error(hdl, EZFS_PROPREADONLY,
 				    errbuf);
 				goto error;
 			}
 
 			if (nvpair_type(elem) =3D=3D DATA_TYPE_STRING) {
 				(void) nvpair_value_string(elem, &strval);
 				if (strcmp(strval, "none") =3D=3D 0) {
 					intval =3D 0;
 				} else if (zfs_nicestrtonum(hdl,
 				    strval, &intval) !=3D 0) {
 					(void) zfs_error(hdl,
 					    EZFS_BADPROP, errbuf);
 					goto error;
 				}
 			} else if (nvpair_type(elem) =3D=3D
 			    DATA_TYPE_UINT64) {
 				(void) nvpair_value_uint64(elem, &intval);
 				if (intval =3D=3D 0) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "use 'none' to disable "
 					    "userquota/groupquota"));
 					goto error;
 				}
 			} else {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' must be a number"), propname);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 
 			/*
 			 * Encode the prop name as
 			 * 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],
 			    (longlong_t)rid, domain);
 			valary[0] =3D uqtype;
 			valary[1] =3D rid;
 			valary[2] =3D intval;
 			if (nvlist_add_uint64_array(ret, newpropname,
 			    valary, 3) !=3D 0) {
 				(void) no_memory(hdl);
 				goto error;
 			}
 			continue;
 		} else if (prop =3D=3D ZPROP_INVAL && zfs_prop_written(propname)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "'%s' is readonly"),
 			    propname);
 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
 			goto error;
 		}
 
 		if (prop =3D=3D ZPROP_INVAL) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid property '%s'"), propname);
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 			goto error;
 		}
 
 		if (!zfs_prop_valid_for_type(prop, type)) {
 			zfs_error_aux(hdl,
 			    dgettext(TEXT_DOMAIN, "'%s' does not "
 			    "apply to datasets of this type"), propname);
 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
 			goto error;
 		}
 
 		if (zfs_prop_readonly(prop) &&
 		    (!zfs_prop_setonce(prop) || zhp !=3D NULL)) {
 			zfs_error_aux(hdl,
 			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
 			    propname);
 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
 			goto error;
 		}
 
 		if (zprop_parse_value(hdl, elem, prop, type, ret,
 		    &strval, &intval, errbuf) !=3D 0)
 			goto error;
 
 		/*
 		 * Perform some additional checks for specific properties.
 		 */
 		switch (prop) {
 		case ZFS_PROP_VERSION:
 		{
 			int version;
 
 			if (zhp =3D=3D NULL)
 				break;
 			version =3D zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
 			if (intval < version) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "Can not downgrade; already at version %u"),
 				    version);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 			break;
 		}
 
 		case ZFS_PROP_RECORDSIZE:
 		case ZFS_PROP_VOLBLOCKSIZE:
 			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
 			if (intval < SPA_MINBLOCKSIZE ||
 			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' must be power of 2 from %u "
 				    "to %uk"), propname,
 				    (uint_t)SPA_MINBLOCKSIZE,
 				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 			break;
 
 		case ZFS_PROP_MLSLABEL:
 		{
 #ifdef sun
 			/*
 			 * Verify the mlslabel string and convert to
 			 * internal hex label string.
 			 */
 
 			m_label_t *new_sl;
 			char *hex =3D NULL;	/* internal label string */
 
 			/* Default value is already OK. */
 			if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) =3D=3D 0)
 				break;
 
 			/* Verify the label can be converted to binary form */
 			if (((new_sl =3D m_label_alloc(MAC_LABEL)) =3D=3D NULL) ||
 			    (str_to_label(strval, &new_sl, MAC_LABEL,
 			    L_NO_CORRECTION, NULL) =3D=3D -1)) {
 				goto badlabel;
 			}
 
 			/* Now translate to hex internal label string */
 			if (label_to_str(new_sl, &hex, M_INTERNAL,
 			    DEF_NAMES) !=3D 0) {
 				if (hex)
 					free(hex);
 				goto badlabel;
 			}
 			m_label_free(new_sl);
 
 			/* If string is already in internal form, we're done. */
 			if (strcmp(strval, hex) =3D=3D 0) {
 				free(hex);
 				break;
 			}
 
 			/* 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) =3D=3D 0);
 			free(hex);
 
 			break;
 
 badlabel:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid mlslabel '%s'"), strval);
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 			m_label_free(new_sl);	/* OK if null */
 #else	/* !sun */
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "mlslabel is not supported on FreeBSD"));
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 #endif	/* !sun */
 			goto error;
 
 		}
 
 		case ZFS_PROP_MOUNTPOINT:
 		{
 			namecheck_err_t why;
 
 			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) =3D=3D 0 ||
 			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) =3D=3D 0)
 				break;
 
 			if (mountpoint_namecheck(strval, &why)) {
 				switch (why) {
 				case NAME_ERR_LEADING_SLASH:
 					zfs_error_aux(hdl,
 					    dgettext(TEXT_DOMAIN,
 					    "'%s' must be an absolute path, "
 					    "'none', or 'legacy'"), propname);
 					break;
 				case NAME_ERR_TOOLONG:
 					zfs_error_aux(hdl,
 					    dgettext(TEXT_DOMAIN,
 					    "component of '%s' is too long"),
 					    propname);
 					break;
 				}
 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 				goto error;
 			}
 		}
 
 			/*FALLTHRU*/
 
 		case ZFS_PROP_SHARESMB:
 		case ZFS_PROP_SHARENFS:
 			/*
 			 * For the mountpoint and sharenfs or sharesmb
 			 * properties, check if it can be set in a
 			 * global/non-global zone based on
 			 * the zoned property value:
 			 *
 			 *		global zone	    non-global zone
 			 * --------------------------------------------------
 			 * zoned=3Don	mountpoint (no)	    mountpoint (yes)
 			 *		sharenfs (no)	    sharenfs (no)
 			 *		sharesmb (no)	    sharesmb (no)
 			 *
 			 * zoned=3Doff	mountpoint (yes)	N/A
 			 *		sharenfs (yes)
 			 *		sharesmb (yes)
 			 */
 			if (zoned) {
 				if (getzoneid() =3D=3D GLOBAL_ZONEID) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set on "
 					    "dataset in a non-global zone"),
 					    propname);
 					(void) zfs_error(hdl, EZFS_ZONED,
 					    errbuf);
 					goto error;
 				} else if (prop =3D=3D ZFS_PROP_SHARENFS ||
 				    prop =3D=3D ZFS_PROP_SHARESMB) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set in "
 					    "a non-global zone"), propname);
 					(void) zfs_error(hdl, EZFS_ZONED,
 					    errbuf);
 					goto error;
 				}
 			} else if (getzoneid() !=3D GLOBAL_ZONEID) {
 				/*
 				 * If zoned property is 'off', this must be in
 				 * a global zone. If not, something is wrong.
 				 */
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "'%s' cannot be set while dataset "
 				    "'zoned' property is set"), propname);
 				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
 				goto error;
 			}
 
 			/*
 			 * At this point, it is legitimate to set the
 			 * property. Now we want to make sure that the
 			 * property value is valid if it is sharenfs.
 			 */
 			if ((prop =3D=3D ZFS_PROP_SHARENFS ||
 			    prop =3D=3D ZFS_PROP_SHARESMB) &&
 			    strcmp(strval, "on") !=3D 0 &&
 			    strcmp(strval, "off") !=3D 0) {
 				zfs_share_proto_t proto;
 
 				if (prop =3D=3D ZFS_PROP_SHARESMB)
 					proto =3D PROTO_SMB;
 				else
 					proto =3D PROTO_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) !=3D SA_OK) {
 					/*
 					 * An error occurred so we can't do
 					 * anything
 					 */
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set: problem "
 					    "in share initialization"),
 					    propname);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					goto error;
 				}
 
 				if (zfs_parse_options(strval, proto) !=3D 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.
 					 */
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be set to invalid "
 					    "options"), propname);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					zfs_uninit_libshare(hdl);
 					goto error;
 				}
 				zfs_uninit_libshare(hdl);
 			}
 
 			break;
 		case ZFS_PROP_UTF8ONLY:
 			chosen_utf =3D (int)intval;
 			break;
 		case ZFS_PROP_NORMALIZE:
 			chosen_normal =3D (int)intval;
 			break;
 		}
 
 		/*
 		 * For changes to existing volumes, we have some additional
 		 * checks to enforce.
 		 */
 		if (type =3D=3D ZFS_TYPE_VOLUME && zhp !=3D NULL) {
 			uint64_t volsize =3D zfs_prop_get_int(zhp,
 			    ZFS_PROP_VOLSIZE);
 			uint64_t blocksize =3D 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 !=3D 0) {
 					zfs_nicenum(blocksize, buf,
 					    sizeof (buf));
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' must be a multiple of "
 					    "volume block size (%s)"),
 					    propname, buf);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					goto error;
 				}
 
 				if (intval =3D=3D 0) {
 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 					    "'%s' cannot be zero"),
 					    propname);
 					(void) zfs_error(hdl, EZFS_BADPROP,
 					    errbuf);
 					goto error;
 				}
 				break;
 			}
 		}
 	}
 
 	/*
 	 * If normalization was chosen, but no UTF8 choice was made,
 	 * enforce rejection of non-UTF8 names.
 	 *
 	 * If normalization was chosen, but rejecting non-UTF8 names
 	 * was explicitly not chosen, it is an error.
 	 */
 	if (chosen_normal > 0 && chosen_utf < 0) {
 		if (nvlist_add_uint64(ret,
 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) !=3D 0) {
 			(void) no_memory(hdl);
 			goto error;
 		}
 	} else if (chosen_normal > 0 && chosen_utf =3D=3D 0) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "'%s' must be set 'on' if normalization chosen"),
 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
 		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 		goto error;
 	}
 	return (ret);
 
 error:
 	nvlist_free(ret);
 	return (NULL);
 }
 
 int
 zfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
 {
 	uint64_t old_volsize;
 	uint64_t new_volsize;
 	uint64_t old_reservation;
 	uint64_t new_reservation;
 	zfs_prop_t resv_prop;
 
 	/*
 	 * If this is an existing volume, and someone is setting the volsize,
 	 * make sure that it matches the reservation, or add it if necessary.
 	 */
 	old_volsize =3D zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
 	if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
 		return (-1);
 	old_reservation =3D zfs_prop_get_int(zhp, resv_prop);
 	if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) !=3D
 	    old_reservation) || nvlist_lookup_uint64(nvl,
 	    zfs_prop_to_name(resv_prop), &new_reservation) !=3D ENOENT) {
 		return (0);
 	}
 	if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
 	    &new_volsize) !=3D 0)
 		return (-1);
 	new_reservation =3D zvol_volsize_to_reservation(new_volsize,
 	    zhp->zfs_props);
 	if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
 	    new_reservation) !=3D 0) {
 		(void) no_memory(zhp->zfs_hdl);
 		return (-1);
 	}
 	return (1);
 }
 
 void
 zfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
     char *errbuf)
 {
 	switch (err) {
 
 	case ENOSPC:
 		/*
 		 * For quotas and reservations, ENOSPC indicates
 		 * something different; setting a quota or reservation
 		 * doesn't use any disk space.
 		 */
 		switch (prop) {
 		case ZFS_PROP_QUOTA:
 		case ZFS_PROP_REFQUOTA:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "size is less than current used or "
 			    "reserved space"));
 			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
 			break;
 
 		case ZFS_PROP_RESERVATION:
 		case ZFS_PROP_REFRESERVATION:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "size is greater than available space"));
 			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
 			break;
 
 		default:
 			(void) zfs_standard_error(hdl, err, errbuf);
 			break;
 		}
 		break;
 
 	case EBUSY:
 		(void) zfs_standard_error(hdl, EBUSY, errbuf);
 		break;
 
 	case EROFS:
 		(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
 		break;
 
 	case ENOTSUP:
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "pool and or dataset must be upgraded to set this "
 		    "property or value"));
 		(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
 		break;
 
 	case ERANGE:
 		if (prop =3D=3D ZFS_PROP_COMPRESSION) {
 			(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "property setting is not allowed on "
 			    "bootable datasets"));
 			(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
 		} else {
 			(void) zfs_standard_error(hdl, err, errbuf);
 		}
 		break;
 
 	case EINVAL:
 		if (prop =3D=3D ZPROP_INVAL) {
 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
 		} else {
 			(void) zfs_standard_error(hdl, err, errbuf);
 		}
 		break;
 
 	case EOVERFLOW:
 		/*
 		 * This platform can't address a volume this big.
 		 */
 #ifdef _ILP32
 		if (prop =3D=3D ZFS_PROP_VOLSIZE) {
 			(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
 			break;
 		}
 #endif
 		/* FALLTHROUGH */
 	default:
 		(void) zfs_standard_error(hdl, err, errbuf);
 	}
 }
 
 /*
  * Given a property name and value, set the property for the given dataset.
  */
 int
 zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int ret =3D -1;
 	prop_changelist_t *cl =3D NULL;
 	char errbuf[1024];
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	nvlist_t *nvl =3D NULL, *realprops;
 	zfs_prop_t prop;
 	boolean_t do_prefix;
 	uint64_t idx;
 	int added_resv;
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
 	    zhp->zfs_name);
 
 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) !=3D 0 ||
 	    nvlist_add_string(nvl, propname, propval) !=3D 0) {
 		(void) no_memory(hdl);
 		goto error;
 	}
 
 	if ((realprops =3D zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) =3D=3D NULL)
 		goto error;
 
 	nvlist_free(nvl);
 	nvl =3D realprops;
 
 	prop =3D zfs_name_to_prop(propname);
 
 	/* We don't support those properties on FreeBSD. */
 	switch (prop) {
 	case ZFS_PROP_DEVICES:
 	case ZFS_PROP_ISCSIOPTIONS:
 	case ZFS_PROP_XATTR:
 	case ZFS_PROP_VSCAN:
 	case ZFS_PROP_NBMAND:
 	case ZFS_PROP_MLSLABEL:
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    "property '%s' not supported on FreeBSD", propname);
 		ret =3D zfs_error(hdl, EZFS_PERM, errbuf);
 		goto error;
 	}
 
 	if (prop =3D=3D ZFS_PROP_VOLSIZE) {
 		if ((added_resv =3D zfs_add_synthetic_resv(zhp, nvl)) =3D=3D -1)
 			goto error;
 	}
 
 	if ((cl =3D changelist_gather(zhp, prop, 0, 0)) =3D=3D NULL)
 		goto error;
 
 	if (prop =3D=3D 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 =3D zfs_error(hdl, EZFS_ZONED, errbuf);
 		goto error;
 	}
 
 	/*
 	 * If the dataset's canmount property is being set to noauto,
 	 * then we want to prevent unmounting & remounting it.
 	 */
 	do_prefix =3D !((prop =3D=3D ZFS_PROP_CANMOUNT) &&
 	    (zprop_string_to_index(prop, propval, &idx,
 	    ZFS_TYPE_DATASET) =3D=3D 0) && (idx =3D=3D ZFS_CANMOUNT_NOAUTO));
 
 	if (do_prefix && (ret =3D changelist_prefix(cl)) !=3D 0)
 		goto error;
 
 	/*
 	 * Execute the corresponding ioctl() to set this property.
 	 */
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (zcmd_write_src_nvlist(hdl, &zc, nvl) !=3D 0)
 		goto error;
 
 	ret =3D zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 
 	if (ret !=3D 0) {
 		zfs_setprop_error(hdl, prop, errno, errbuf);
 		if (added_resv && errno =3D=3D ENOSPC) {
 			/* clean up the volsize property we tried to set */
 			uint64_t old_volsize =3D zfs_prop_get_int(zhp,
 			    ZFS_PROP_VOLSIZE);
 			nvlist_free(nvl);
 			zcmd_free_nvlists(&zc);
 			if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) !=3D 0)
 				goto error;
 			if (nvlist_add_uint64(nvl,
 			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
 			    old_volsize) !=3D 0)
 				goto error;
 			if (zcmd_write_src_nvlist(hdl, &zc, nvl) !=3D 0)
 				goto error;
 			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
 		}
 	} else {
 		if (do_prefix)
 			ret =3D changelist_postfix(cl);
 
 		/*
 		 * Refresh the statistics so the new property value
 		 * is reflected.
 		 */
 		if (ret =3D=3D 0)
 			(void) get_stats(zhp);
 	}
 
 error:
 	nvlist_free(nvl);
 	zcmd_free_nvlists(&zc);
 	if (cl)
 		changelist_free(cl);
 	return (ret);
 }
 
 /*
  * Given a property, inherit the value from the parent dataset, or if recei=
 ved
  * is TRUE, revert to the received value, if any.
  */
 int
 zfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t receive=
 d)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int ret;
 	prop_changelist_t *cl;
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	char errbuf[1024];
 	zfs_prop_t prop;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
 
 	zc.zc_cookie =3D received;
 	if ((prop =3D zfs_name_to_prop(propname)) =3D=3D ZPROP_INVAL) {
 		/*
 		 * For user properties, the amount of work we have to do is very
 		 * small, so just do it here.
 		 */
 		if (!zfs_prop_user(propname)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "invalid property"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
 
 		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) !=3D 0)
 			return (zfs_standard_error(hdl, errno, errbuf));
 
 		return (0);
 	}
 
 	/*
 	 * Verify that this property is inheritable.
 	 */
 	if (zfs_prop_readonly(prop))
 		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
 
 	if (!zfs_prop_inheritable(prop) && !received)
 		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
 
 	/*
 	 * Check to see if the value applies to this type
 	 */
 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
 
 	/*
 	 * Normalize the name, to get rid of shorthand abbreviations.
 	 */
 	propname =3D zfs_prop_to_name(prop);
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
 
 	if (prop =3D=3D ZFS_PROP_MOUNTPOINT && getzoneid() =3D=3D GLOBAL_ZONEID &&
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "dataset is used in a non-global zone"));
 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
 	}
 
 	/*
 	 * Determine datasets which will be affected by this change, if any.
 	 */
 	if ((cl =3D changelist_gather(zhp, prop, 0, 0)) =3D=3D NULL)
 		return (-1);
 
 	if (prop =3D=3D 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 =3D zfs_error(hdl, EZFS_ZONED, errbuf);
 		goto error;
 	}
 
 	if ((ret =3D changelist_prefix(cl)) !=3D 0)
 		goto error;
 
 	if ((ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) !=3D 0) {
 		return (zfs_standard_error(hdl, errno, errbuf));
 	} else {
 
 		if ((ret =3D changelist_postfix(cl)) !=3D 0)
 			goto error;
 
 		/*
 		 * Refresh the statistics so the new property is reflected.
 		 */
 		(void) get_stats(zhp);
 	}
 
 error:
 	changelist_free(cl);
 	return (ret);
 }
 
 /*
  * True DSL properties are stored in an nvlist.  The following two function=
 s
  * extract them appropriately.
  */
 static uint64_t
 getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
 {
 	nvlist_t *nv;
 	uint64_t value;
 
 	*source =3D NULL;
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(prop), &nv) =3D=3D 0) {
 		verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) =3D=3D 0);
 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
 	} else {
 		verify(!zhp->zfs_props_table ||
 		    zhp->zfs_props_table[prop] =3D=3D B_TRUE);
 		value =3D zfs_prop_default_numeric(prop);
 		*source =3D "";
 	}
 
 	return (value);
 }
 
 static char *
 getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
 {
 	nvlist_t *nv;
 	char *value;
 
 	*source =3D NULL;
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(prop), &nv) =3D=3D 0) {
 		verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) =3D=3D 0);
 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
 	} else {
 		verify(!zhp->zfs_props_table ||
 		    zhp->zfs_props_table[prop] =3D=3D B_TRUE);
 		if ((value =3D (char *)zfs_prop_default_string(prop)) =3D=3D NULL)
 			value =3D "";
 		*source =3D "";
 	}
 
 	return (value);
 }
 
 static boolean_t
 zfs_is_recvd_props_mode(zfs_handle_t *zhp)
 {
 	return (zhp->zfs_props =3D=3D zhp->zfs_recvd_props);
 }
 
 static void
 zfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
 {
 	*cookie =3D (uint64_t)(uintptr_t)zhp->zfs_props;
 	zhp->zfs_props =3D zhp->zfs_recvd_props;
 }
 
 static void
 zfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
 {
 	zhp->zfs_props =3D (nvlist_t *)(uintptr_t)*cookie;
 	*cookie =3D 0;
 }
 
 /*
  * Internal function for getting a numeric property.  Both zfs_prop_get() a=
 nd
  * zfs_prop_get_int() are built using this interface.
  *
  * Certain properties can be overridden using 'mount -o'.  In this case, sc=
 an
  * the contents of the /etc/mnttab entry, searching for the appropriate opt=
 ions.
  * If they differ from the on-disk values, report the current values and ma=
 rk
  * the source "temporary".
  */
 static int
 get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *sr=
 c,
     char **source, uint64_t *val)
 {
 	zfs_cmd_t zc =3D { 0 };
 	nvlist_t *zplprops =3D NULL;
 	struct mnttab mnt;
 	char *mntopt_on =3D NULL;
 	char *mntopt_off =3D NULL;
 	boolean_t received =3D zfs_is_recvd_props_mode(zhp);
 
 	*source =3D NULL;
 
 	switch (prop) {
 	case ZFS_PROP_ATIME:
 		mntopt_on =3D MNTOPT_ATIME;
 		mntopt_off =3D MNTOPT_NOATIME;
 		break;
 
 	case ZFS_PROP_DEVICES:
 		mntopt_on =3D MNTOPT_DEVICES;
 		mntopt_off =3D MNTOPT_NODEVICES;
 		break;
 
 	case ZFS_PROP_EXEC:
 		mntopt_on =3D MNTOPT_EXEC;
 		mntopt_off =3D MNTOPT_NOEXEC;
 		break;
 
 	case ZFS_PROP_READONLY:
 		mntopt_on =3D MNTOPT_RO;
 		mntopt_off =3D MNTOPT_RW;
 		break;
 
 	case ZFS_PROP_SETUID:
 		mntopt_on =3D MNTOPT_SETUID;
 		mntopt_off =3D MNTOPT_NOSETUID;
 		break;
 
 	case ZFS_PROP_XATTR:
 		mntopt_on =3D MNTOPT_XATTR;
 		mntopt_off =3D MNTOPT_NOXATTR;
 		break;
 
 	case ZFS_PROP_NBMAND:
 		mntopt_on =3D MNTOPT_NBMAND;
 		mntopt_off =3D MNTOPT_NONBMAND;
 		break;
 	}
 
 	/*
 	 * Because looking up the mount options is potentially expensive
 	 * (iterating over all of /etc/mnttab), we defer its calculation until
 	 * we're looking up a property which requires its presence.
 	 */
 	if (!zhp->zfs_mntcheck &&
 	    (mntopt_on !=3D NULL || prop =3D=3D ZFS_PROP_MOUNTED)) {
 		libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 		struct mnttab entry;
 
 		if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) =3D=3D 0) {
 			zhp->zfs_mntopts =3D zfs_strdup(hdl,
 			    entry.mnt_mntopts);
 			if (zhp->zfs_mntopts =3D=3D NULL)
 				return (-1);
 		}
 
 		zhp->zfs_mntcheck =3D B_TRUE;
 	}
 
 	if (zhp->zfs_mntopts =3D=3D NULL)
 		mnt.mnt_mntopts =3D "";
 	else
 		mnt.mnt_mntopts =3D zhp->zfs_mntopts;
 
 	switch (prop) {
 	case ZFS_PROP_ATIME:
 	case ZFS_PROP_DEVICES:
 	case ZFS_PROP_EXEC:
 	case ZFS_PROP_READONLY:
 	case ZFS_PROP_SETUID:
 	case ZFS_PROP_XATTR:
 	case ZFS_PROP_NBMAND:
 		*val =3D getprop_uint64(zhp, prop, source);
 
 		if (received)
 			break;
 
 		if (hasmntopt(&mnt, mntopt_on) && !*val) {
 			*val =3D B_TRUE;
 			if (src)
 				*src =3D ZPROP_SRC_TEMPORARY;
 		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
 			*val =3D B_FALSE;
 			if (src)
 				*src =3D ZPROP_SRC_TEMPORARY;
 		}
 		break;
 
 	case ZFS_PROP_CANMOUNT:
 	case ZFS_PROP_VOLSIZE:
 	case ZFS_PROP_QUOTA:
 	case ZFS_PROP_REFQUOTA:
 	case ZFS_PROP_RESERVATION:
 	case ZFS_PROP_REFRESERVATION:
 		*val =3D getprop_uint64(zhp, prop, source);
 
 		if (*source =3D=3D NULL) {
 			/* not default, must be local */
 			*source =3D zhp->zfs_name;
 		}
 		break;
 
 	case ZFS_PROP_MOUNTED:
 		*val =3D (zhp->zfs_mntopts !=3D NULL);
 		break;
 
 	case ZFS_PROP_NUMCLONES:
 		*val =3D zhp->zfs_dmustats.dds_num_clones;
 		break;
 
 	case ZFS_PROP_VERSION:
 	case ZFS_PROP_NORMALIZE:
 	case ZFS_PROP_UTF8ONLY:
 	case ZFS_PROP_CASE:
 		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
 		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) !=3D 0)
 			return (-1);
 		(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);
 			return (-1);
 		}
 		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) !=3D 0 ||
 		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
 		    val) !=3D 0) {
 			zcmd_free_nvlists(&zc);
 			return (-1);
 		}
 		if (zplprops)
 			nvlist_free(zplprops);
 		zcmd_free_nvlists(&zc);
 		break;
 
 	default:
 		switch (zfs_prop_get_type(prop)) {
 		case PROP_TYPE_NUMBER:
 		case PROP_TYPE_INDEX:
 			*val =3D getprop_uint64(zhp, prop, source);
 			/*
 			 * If we tried to use a default value for a
 			 * readonly property, it means that it was not
 			 * present.
 			 */
 			if (zfs_prop_readonly(prop) &&
 			    *source !=3D NULL && (*source)[0] =3D=3D '\0') {
 				*source =3D NULL;
 			}
 			break;
 
 		case PROP_TYPE_STRING:
 		default:
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "cannot get non-numeric property"));
 			return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
 			    dgettext(TEXT_DOMAIN, "internal error")));
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Calculate the source type, given the raw source string.
  */
 static void
 get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
     char *statbuf, size_t statlen)
 {
 	if (statbuf =3D=3D NULL || *srctype =3D=3D ZPROP_SRC_TEMPORARY)
 		return;
 
 	if (source =3D=3D NULL) {
 		*srctype =3D ZPROP_SRC_NONE;
 	} else if (source[0] =3D=3D '\0') {
 		*srctype =3D ZPROP_SRC_DEFAULT;
 	} else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) !=3D NULL) {
 		*srctype =3D ZPROP_SRC_RECEIVED;
 	} else {
 		if (strcmp(source, zhp->zfs_name) =3D=3D 0) {
 			*srctype =3D ZPROP_SRC_LOCAL;
 		} else {
 			(void) strlcpy(statbuf, source, statlen);
 			*srctype =3D ZPROP_SRC_INHERITED;
 		}
 	}
 
 }
 
 int
 zfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
     size_t proplen, boolean_t literal)
 {
 	zfs_prop_t prop;
 	int err =3D 0;
 
 	if (zhp->zfs_recvd_props =3D=3D NULL)
 		if (get_recvd_props_ioctl(zhp) !=3D 0)
 			return (-1);
 
 	prop =3D zfs_name_to_prop(propname);
 
 	if (prop !=3D ZPROP_INVAL) {
 		uint64_t cookie;
 		if (!nvlist_exists(zhp->zfs_recvd_props, propname))
 			return (-1);
 		zfs_set_recvd_props_mode(zhp, &cookie);
 		err =3D zfs_prop_get(zhp, prop, propbuf, proplen,
 		    NULL, NULL, 0, literal);
 		zfs_unset_recvd_props_mode(zhp, &cookie);
 	} else {
 		nvlist_t *propval;
 		char *recvdval;
 		if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
 		    propname, &propval) !=3D 0)
 			return (-1);
 		verify(nvlist_lookup_string(propval, ZPROP_VALUE,
 		    &recvdval) =3D=3D 0);
 		(void) strlcpy(propbuf, recvdval, proplen);
 	}
 
 	return (err =3D=3D 0 ? 0 : -1);
 }
 
 static int
 get_clones_string(zfs_handle_t *zhp, char *propbuf, size_t proplen)
 {
 	nvlist_t *value;
 	nvpair_t *pair;
 
 	value =3D zfs_get_clones_nvl(zhp);
 	if (value =3D=3D NULL)
 		return (-1);
 
 	propbuf[0] =3D '\0';
 	for (pair =3D nvlist_next_nvpair(value, NULL); pair !=3D NULL;
 	    pair =3D nvlist_next_nvpair(value, pair)) {
 		if (propbuf[0] !=3D '\0')
 			(void) strlcat(propbuf, ",", proplen);
 		(void) strlcat(propbuf, nvpair_name(pair), proplen);
 	}
 
 	return (0);
 }
 
 struct get_clones_arg {
 	uint64_t numclones;
 	nvlist_t *value;
 	const char *origin;
 	char buf[ZFS_MAXNAMELEN];
 };
 
 int
 get_clones_cb(zfs_handle_t *zhp, void *arg)
 {
 	struct get_clones_arg *gca =3D arg;
 
 	if (gca->numclones =3D=3D 0) {
 		zfs_close(zhp);
 		return (0);
 	}
 
 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, gca->buf, sizeof (gca->buf),
 	    NULL, NULL, 0, B_TRUE) !=3D 0)
 		goto out;
 	if (strcmp(gca->buf, gca->origin) =3D=3D 0) {
 		if (nvlist_add_boolean(gca->value, zfs_get_name(zhp)) !=3D 0) {
 			zfs_close(zhp);
 			return (no_memory(zhp->zfs_hdl));
 		}
 		gca->numclones--;
 	}
 
 out:
 	(void) zfs_iter_children(zhp, get_clones_cb, gca);
 	zfs_close(zhp);
 	return (0);
 }
 
 nvlist_t *
 zfs_get_clones_nvl(zfs_handle_t *zhp)
 {
 	nvlist_t *nv, *value;
 
 	if (nvlist_lookup_nvlist(zhp->zfs_props,
 	    zfs_prop_to_name(ZFS_PROP_CLONES), &nv) !=3D 0) {
 		struct get_clones_arg gca;
 
 		/*
 		 * if this is a snapshot, then the kernel wasn't able
 		 * to get the clones.  Do it by slowly iterating.
 		 */
 		if (zhp->zfs_type !=3D ZFS_TYPE_SNAPSHOT)
 			return (NULL);
 		if (nvlist_alloc(&nv, NV_UNIQUE_NAME, 0) !=3D 0)
 			return (NULL);
 		if (nvlist_alloc(&value, NV_UNIQUE_NAME, 0) !=3D 0) {
 			nvlist_free(nv);
 			return (NULL);
 		}
 
 		gca.numclones =3D zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES);
 		gca.value =3D value;
 		gca.origin =3D zhp->zfs_name;
 
 		if (gca.numclones !=3D 0) {
 			zfs_handle_t *root;
 			char pool[ZFS_MAXNAMELEN];
 			char *cp =3D pool;
 
 			/* get the pool name */
 			(void) strlcpy(pool, zhp->zfs_name, sizeof (pool));
 			(void) strsep(&cp, "/@");
 			root =3D zfs_open(zhp->zfs_hdl, pool,
 			    ZFS_TYPE_FILESYSTEM);
 
 			(void) get_clones_cb(root, &gca);
 		}
 
 		if (gca.numclones !=3D 0 ||
 		    nvlist_add_nvlist(nv, ZPROP_VALUE, value) !=3D 0 ||
 		    nvlist_add_nvlist(zhp->zfs_props,
 		    zfs_prop_to_name(ZFS_PROP_CLONES), nv) !=3D 0) {
 			nvlist_free(nv);
 			nvlist_free(value);
 			return (NULL);
 		}
 		nvlist_free(nv);
 		nvlist_free(value);
 		verify(0 =3D=3D nvlist_lookup_nvlist(zhp->zfs_props,
 		    zfs_prop_to_name(ZFS_PROP_CLONES), &nv));
 	}
 
 	verify(nvlist_lookup_nvlist(nv, ZPROP_VALUE, &value) =3D=3D 0);
 
 	return (value);
 }
 
 /*
  * Retrieve a property from the given object.  If 'literal' is specified, t=
 hen
  * numbers are left as exact values.  Otherwise, numbers are converted to a
  * human-readable form.
  *
  * Returns 0 on success, or -1 on error.
  */
 int
 zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t prop=
 len,
     zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
 {
 	char *source =3D NULL;
 	uint64_t val;
 	char *str;
 	const char *strval;
 	boolean_t received =3D zfs_is_recvd_props_mode(zhp);
 
 	/*
 	 * Check to see if this property applies to our object
 	 */
 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
 		return (-1);
 
 	if (received && zfs_prop_readonly(prop))
 		return (-1);
 
 	if (src)
 		*src =3D ZPROP_SRC_NONE;
 
 	switch (prop) {
 	case ZFS_PROP_CREATION:
 		/*
 		 * 'creation' is a time_t stored in the statistics.  We convert
 		 * this into a string unless 'literal' is specified.
 		 */
 		{
 			val =3D getprop_uint64(zhp, prop, &source);
 			time_t time =3D (time_t)val;
 			struct tm t;
 
 			if (literal ||
 			    localtime_r(&time, &t) =3D=3D NULL ||
 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
 			    &t) =3D=3D 0)
 				(void) snprintf(propbuf, proplen, "%llu", val);
 		}
 		break;
 
 	case ZFS_PROP_MOUNTPOINT:
 		/*
 		 * Getting the precise mountpoint can be tricky.
 		 *
 		 *  - for 'none' or 'legacy', return those values.
 		 *  - for inherited mountpoints, we want to take everything
 		 *    after our ancestor and append it to the inherited value.
 		 *
 		 * If the pool has an alternate root, we want to prepend that
 		 * root to any values we return.
 		 */
 
 		str =3D getprop_string(zhp, prop, &source);
 
 		if (str[0] =3D=3D '/') {
 			char buf[MAXPATHLEN];
 			char *root =3D buf;
 			const char *relpath;
 
 			/*
 			 * If we inherit the mountpoint, even from a dataset
 			 * with a received value, the source will be the path of
 			 * the dataset we inherit from. If source is
 			 * ZPROP_SOURCE_VAL_RECVD, the received value is not
 			 * inherited.
 			 */
 			if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) =3D=3D 0) {
 				relpath =3D "";
 			} else {
 				relpath =3D zhp->zfs_name + strlen(source);
 				if (relpath[0] =3D=3D '/')
 					relpath++;
 			}
 
 			if ((zpool_get_prop(zhp->zpool_hdl,
 			    ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
 			    (strcmp(root, "-") =3D=3D 0))
 				root[0] =3D '\0';
 			/*
 			 * Special case an alternate root of '/'. This will
 			 * avoid having multiple leading slashes in the
 			 * mountpoint path.
 			 */
 			if (strcmp(root, "/") =3D=3D 0)
 				root++;
 
 			/*
 			 * If the mountpoint is '/' then skip over this
 			 * if we are obtaining either an alternate root or
 			 * an inherited mountpoint.
 			 */
 			if (str[1] =3D=3D '\0' && (root[0] !=3D '\0' ||
 			    relpath[0] !=3D '\0'))
 				str++;
 
 			if (relpath[0] =3D=3D '\0')
 				(void) snprintf(propbuf, proplen, "%s%s",
 				    root, str);
 			else
 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
 				    root, str, relpath[0] =3D=3D '@' ? "" : "/",
 				    relpath);
 		} else {
 			/* 'legacy' or 'none' */
 			(void) strlcpy(propbuf, str, 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] =3D=3D '\0')
 			return (-1);
 		break;
 
 	case ZFS_PROP_CLONES:
 		if (get_clones_string(zhp, propbuf, proplen) !=3D 0)
 			return (-1);
 		break;
 
 	case ZFS_PROP_QUOTA:
 	case ZFS_PROP_REFQUOTA:
 	case ZFS_PROP_RESERVATION:
 	case ZFS_PROP_REFRESERVATION:
 
 		if (get_numeric_property(zhp, prop, src, &source, &val) !=3D 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
 		 * value.  Otherwise, we print the number nicely and indicate
 		 * that its set locally.
 		 */
 		if (val =3D=3D 0) {
 			if (literal)
 				(void) strlcpy(propbuf, "0", proplen);
 			else
 				(void) strlcpy(propbuf, "none", proplen);
 		} else {
 			if (literal)
 				(void) snprintf(propbuf, proplen, "%llu",
 				    (u_longlong_t)val);
 			else
 				zfs_nicenum(val, propbuf, proplen);
 		}
 		break;
 
 	case ZFS_PROP_REFRATIO:
 	case ZFS_PROP_COMPRESSRATIO:
 		if (get_numeric_property(zhp, prop, src, &source, &val) !=3D 0)
 			return (-1);
 		(void) snprintf(propbuf, proplen, "%llu.%02llux",
 		    (u_longlong_t)(val / 100),
 		    (u_longlong_t)(val % 100));
 		break;
 
 	case ZFS_PROP_TYPE:
 		switch (zhp->zfs_type) {
 		case ZFS_TYPE_FILESYSTEM:
 			str =3D "filesystem";
 			break;
 		case ZFS_TYPE_VOLUME:
 			str =3D "volume";
 			break;
 		case ZFS_TYPE_SNAPSHOT:
 			str =3D "snapshot";
 			break;
 		default:
 			abort();
 		}
 		(void) snprintf(propbuf, proplen, "%s", str);
 		break;
 
 	case ZFS_PROP_MOUNTED:
 		/*
 		 * The 'mounted' property is a pseudo-property that described
 		 * whether the filesystem is currently mounted.  Even though
 		 * it's a boolean value, the typical values of "on" and "off"
 		 * don't make sense, so we translate to "yes" and "no".
 		 */
 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
 		    src, &source, &val) !=3D 0)
 			return (-1);
 		if (val)
 			(void) strlcpy(propbuf, "yes", proplen);
 		else
 			(void) strlcpy(propbuf, "no", proplen);
 		break;
 
 	case ZFS_PROP_NAME:
 		/*
 		 * The 'name' property is a pseudo-property derived from the
 		 * dataset name.  It is presented as a real property to simplify
 		 * consumers.
 		 */
 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
 		break;
 
 	case ZFS_PROP_MLSLABEL:
 		{
 #ifdef sun
 			m_label_t *new_sl =3D NULL;
 			char *ascii =3D NULL;	/* human readable label */
 
 			(void) strlcpy(propbuf,
 			    getprop_string(zhp, prop, &source), proplen);
 
 			if (literal || (strcasecmp(propbuf,
 			    ZFS_MLSLABEL_DEFAULT) =3D=3D 0))
 				break;
 
 			/*
 			 * Try to translate the internal hex string to
 			 * human-readable output.  If there are any
 			 * problems just use the hex string.
 			 */
 
 			if (str_to_label(propbuf, &new_sl, MAC_LABEL,
 			    L_NO_CORRECTION, NULL) =3D=3D -1) {
 				m_label_free(new_sl);
 				break;
 			}
 
 			if (label_to_str(new_sl, &ascii, M_LABEL,
 			    DEF_NAMES) !=3D 0) {
 				if (ascii)
 					free(ascii);
 				m_label_free(new_sl);
 				break;
 			}
 			m_label_free(new_sl);
 
 			(void) strlcpy(propbuf, ascii, proplen);
 			free(ascii);
 #else	/* !sun */
 			propbuf[0] =3D '\0';
 #endif	/* !sun */
 		}
 		break;
 
 	case ZFS_PROP_GUID:
 		/*
 		 * GUIDs are stored as numbers, but they are identifiers.
 		 * We don't want them to be pretty printed, because pretty
 		 * printing mangles the ID into a truncated and useless value.
 		 */
 		if (get_numeric_property(zhp, prop, src, &source, &val) !=3D 0)
 			return (-1);
 		(void) snprintf(propbuf, proplen, "%llu", (u_longlong_t)val);
 		break;
 
 	default:
 		switch (zfs_prop_get_type(prop)) {
 		case PROP_TYPE_NUMBER:
 			if (get_numeric_property(zhp, prop, src,
 			    &source, &val) !=3D 0)
 				return (-1);
 			if (literal)
 				(void) snprintf(propbuf, proplen, "%llu",
 				    (u_longlong_t)val);
 			else
 				zfs_nicenum(val, propbuf, proplen);
 			break;
 
 		case PROP_TYPE_STRING:
 			(void) strlcpy(propbuf,
 			    getprop_string(zhp, prop, &source), proplen);
 			break;
 
 		case PROP_TYPE_INDEX:
 			if (get_numeric_property(zhp, prop, src,
 			    &source, &val) !=3D 0)
 				return (-1);
 			if (zfs_prop_index_to_string(prop, val, &strval) !=3D 0)
 				return (-1);
 			(void) strlcpy(propbuf, strval, proplen);
 			break;
 
 		default:
 			abort();
 		}
 	}
 
 	get_source(zhp, src, source, statbuf, statlen);
 
 	return (0);
 }
 
 /*
  * Utility function to get the given numeric property.  Does no validation =
 that
  * the given property is the appropriate type; should only be used with
  * hard-coded property types.
  */
 uint64_t
 zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
 {
 	char *source;
 	uint64_t val;
 
 	(void) get_numeric_property(zhp, prop, NULL, &source, &val);
 
 	return (val);
 }
 
 int
 zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
 {
 	char buf[64];
 
 	(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
 	return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
 }
 
 /*
  * Similar to zfs_prop_get(), but returns the value as an integer.
  */
 int
 zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
     zprop_source_t *src, char *statbuf, size_t statlen)
 {
 	char *source;
 
 	/*
 	 * Check to see if this property applies to our object
 	 */
 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
 		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
 		    zfs_prop_to_name(prop)));
 	}
 
 	if (src)
 		*src =3D ZPROP_SRC_NONE;
 
 	if (get_numeric_property(zhp, prop, src, &source, value) !=3D 0)
 		return (-1);
 
 	get_source(zhp, src, source, statbuf, statlen);
 
 	return (0);
 }
 
 static int
 idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
     char **domainp, idmap_rid_t *ridp)
 {
 #ifdef sun
 	idmap_get_handle_t *get_hdl =3D NULL;
 	idmap_stat status;
 	int err =3D EINVAL;
 
 	if (idmap_get_create(&get_hdl) !=3D IDMAP_SUCCESS)
 		goto out;
 
 	if (isuser) {
 		err =3D idmap_get_sidbyuid(get_hdl, id,
 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
 	} else {
 		err =3D idmap_get_sidbygid(get_hdl, id,
 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
 	}
 	if (err =3D=3D IDMAP_SUCCESS &&
 	    idmap_get_mappings(get_hdl) =3D=3D IDMAP_SUCCESS &&
 	    status =3D=3D IDMAP_SUCCESS)
 		err =3D 0;
 	else
 		err =3D EINVAL;
 out:
 	if (get_hdl)
 		idmap_get_destroy(get_hdl);
 	return (err);
 #else	/* !sun */
 	assert(!"invalid code path");
 #endif	/* !sun */
 }
 
 /*
  * convert the propname into parameters needed by kernel
  * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
  * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
  */
 static int
 userquota_propname_decode(const char *propname, boolean_t zoned,
     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *rid=
 p)
 {
 	zfs_userquota_prop_t type;
 	char *cp, *end;
 	char *numericsid =3D NULL;
 	boolean_t isuser;
 
 	domain[0] =3D '\0';
 
 	/* Figure out the property type ({user|group}{quota|space}) */
 	for (type =3D 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
 		if (strncmp(propname, zfs_userquota_prop_prefixes[type],
 		    strlen(zfs_userquota_prop_prefixes[type])) =3D=3D 0)
 			break;
 	}
 	if (type =3D=3D ZFS_NUM_USERQUOTA_PROPS)
 		return (EINVAL);
 	*typep =3D type;
 
 	isuser =3D (type =3D=3D ZFS_PROP_USERQUOTA ||
 	    type =3D=3D ZFS_PROP_USERUSED);
 
 	cp =3D strchr(propname, '@') + 1;
 
 	if (strchr(cp, '@')) {
 #ifdef sun
 		/*
 		 * It's a SID name (eg "user@domain") that needs to be
 		 * turned into S-1-domainID-RID.
 		 */
 		directory_error_t e;
 		if (zoned && getzoneid() =3D=3D GLOBAL_ZONEID)
 			return (ENOENT);
 		if (isuser) {
 			e =3D directory_sid_from_user_name(NULL,
 			    cp, &numericsid);
 		} else {
 			e =3D directory_sid_from_group_name(NULL,
 			    cp, &numericsid);
 		}
 		if (e !=3D NULL) {
 			directory_error_free(e);
 			return (ENOENT);
 		}
 		if (numericsid =3D=3D NULL)
 			return (ENOENT);
 		cp =3D numericsid;
 		/* will be further decoded below */
 #else	/* !sun */
 		return (ENOENT);
 #endif	/* !sun */
 	}
 
 	if (strncmp(cp, "S-1-", 4) =3D=3D 0) {
 		/* It's a numeric SID (eg "S-1-234-567-89") */
 		(void) strlcpy(domain, cp, domainlen);
 		cp =3D strrchr(domain, '-');
 		*cp =3D '\0';
 		cp++;
 
 		errno =3D 0;
 		*ridp =3D strtoull(cp, &end, 10);
 		if (numericsid) {
 			free(numericsid);
 			numericsid =3D NULL;
 		}
 		if (errno !=3D 0 || *end !=3D '\0')
 			return (EINVAL);
 	} else if (!isdigit(*cp)) {
 		/*
 		 * It's a user/group name (eg "user") that needs to be
 		 * turned into a uid/gid
 		 */
 		if (zoned && getzoneid() =3D=3D GLOBAL_ZONEID)
 			return (ENOENT);
 		if (isuser) {
 			struct passwd *pw;
 			pw =3D getpwnam(cp);
 			if (pw =3D=3D NULL)
 				return (ENOENT);
 			*ridp =3D pw->pw_uid;
 		} else {
 			struct group *gr;
 			gr =3D getgrnam(cp);
 			if (gr =3D=3D NULL)
 				return (ENOENT);
 			*ridp =3D gr->gr_gid;
 		}
 	} else {
 		/* It's a user/group ID (eg "12345"). */
 		uid_t id =3D strtoul(cp, &end, 10);
 		idmap_rid_t rid;
 		char *mapdomain;
 
 		if (*end !=3D '\0')
 			return (EINVAL);
 		if (id > MAXUID) {
 			/* It's an ephemeral ID. */
 			if (idmap_id_to_numeric_domain_rid(id, isuser,
 			    &mapdomain, &rid) !=3D 0)
 				return (ENOENT);
 			(void) strlcpy(domain, mapdomain, domainlen);
 			*ridp =3D rid;
 		} else {
 			*ridp =3D id;
 		}
 	}
 
 	ASSERT3P(numericsid, =3D=3D, NULL);
 	return (0);
 }
 
 static int
 zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue, zfs_userquota_prop_t *typep)
 {
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	err =3D userquota_propname_decode(propname,
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
 	    typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
 	zc.zc_objset_type =3D *typep;
 	if (err)
 		return (err);
 
 	err =3D ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
 	if (err)
 		return (err);
 
 	*propvalue =3D zc.zc_cookie;
 	return (0);
 }
 
 int
 zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue)
 {
 	zfs_userquota_prop_t type;
 
 	return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
 	    &type));
 }
 
 int
 zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
     char *propbuf, int proplen, boolean_t literal)
 {
 	int err;
 	uint64_t propvalue;
 	zfs_userquota_prop_t type;
 
 	err =3D zfs_prop_get_userquota_common(zhp, propname, &propvalue,
 	    &type);
 
 	if (err)
 		return (err);
 
 	if (literal) {
 		(void) snprintf(propbuf, proplen, "%llu", propvalue);
 	} else if (propvalue =3D=3D 0 &&
 	    (type =3D=3D ZFS_PROP_USERQUOTA || type =3D=3D ZFS_PROP_GROUPQUOTA)) {
 		(void) strlcpy(propbuf, "none", proplen);
 	} else {
 		zfs_nicenum(propvalue, propbuf, proplen);
 	}
 	return (0);
 }
 
 int
 zfs_prop_get_written_int(zfs_handle_t *zhp, const char *propname,
     uint64_t *propvalue)
 {
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 	const char *snapname;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	snapname =3D strchr(propname, '@') + 1;
 	if (strchr(snapname, '@')) {
 		(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 	} else {
 		/* snapname is the short name, append it to zhp's fsname */
 		char *cp;
 
 		(void) strlcpy(zc.zc_value, zhp->zfs_name,
 		    sizeof (zc.zc_value));
 		cp =3D strchr(zc.zc_value, '@');
 		if (cp !=3D NULL)
 			*cp =3D '\0';
 		(void) strlcat(zc.zc_value, "@", sizeof (zc.zc_value));
 		(void) strlcat(zc.zc_value, snapname, sizeof (zc.zc_value));
 	}
 
 	err =3D ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_WRITTEN, &zc);
 	if (err)
 		return (err);
 
 	*propvalue =3D zc.zc_cookie;
 	return (0);
 }
 
 int
 zfs_prop_get_written(zfs_handle_t *zhp, const char *propname,
     char *propbuf, int proplen, boolean_t literal)
 {
 	int err;
 	uint64_t propvalue;
 
 	err =3D zfs_prop_get_written_int(zhp, propname, &propvalue);
 
 	if (err)
 		return (err);
 
 	if (literal) {
 		(void) snprintf(propbuf, proplen, "%llu", propvalue);
 	} else {
 		zfs_nicenum(propvalue, propbuf, proplen);
 	}
 	return (0);
 }
 
 int
 zfs_get_snapused_int(zfs_handle_t *firstsnap, zfs_handle_t *lastsnap,
     uint64_t *usedp)
 {
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, lastsnap->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, firstsnap->zfs_name, sizeof (zc.zc_value));
 
 	err =3D ioctl(lastsnap->zfs_hdl->libzfs_fd, ZFS_IOC_SPACE_SNAPS, &zc);
 	if (err)
 		return (err);
 
 	*usedp =3D zc.zc_cookie;
 
 	return (0);
 }
 
 /*
  * Returns the name of the given zfs handle.
  */
 const char *
 zfs_get_name(const zfs_handle_t *zhp)
 {
 	return (zhp->zfs_name);
 }
 
 /*
  * Returns the type of the given zfs handle.
  */
 zfs_type_t
 zfs_get_type(const zfs_handle_t *zhp)
 {
 	return (zhp->zfs_type);
 }
 
 /*
  * Is one dataset name a child dataset of another?
  *
  * Needs to handle these cases:
  * Dataset 1	"a/foo"		"a/foo"		"a/foo"		"a/foo"
  * Dataset 2	"a/fo"		"a/foobar"	"a/bar/baz"	"a/foo/bar"
  * Descendant?	No.		No.		No.		Yes.
  */
 static boolean_t
 is_descendant(const char *ds1, const char *ds2)
 {
 	size_t d1len =3D strlen(ds1);
 
 	/* ds2 can't be a descendant if it's smaller */
 	if (strlen(ds2) < d1len)
 		return (B_FALSE);
 
 	/* otherwise, compare strings and verify that there's a '/' char */
 	return (ds2[d1len] =3D=3D '/' && (strncmp(ds1, ds2, d1len) =3D=3D 0));
 }
 
 /*
  * Given a complete name, return just the portion that refers to the parent=
 .
  * Will return -1 if there is no parent (path is just the name of the
  * pool).
  */
 static int
 parent_name(const char *path, char *buf, size_t buflen)
 {
 	char *slashp;
 
 	(void) strlcpy(buf, path, buflen);
 
 	if ((slashp =3D strrchr(buf, '/')) =3D=3D NULL)
 		return (-1);
 	*slashp =3D '\0';
 
 	return (0);
 }
 
 /*
  * 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
  * closest existing ancestor for the given path.  In prefixlen return the
  * length of already existing prefix of the given path.  We also fetch the
  * 'zoned' property, which is used to validate property settings when creat=
 ing
  * new datasets.
  */
 static int
 check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
     boolean_t accept_ancestor, int *prefixlen)
 {
 	zfs_cmd_t zc =3D { 0 };
 	char parent[ZFS_MAXNAMELEN];
 	char *slash;
 	zfs_handle_t *zhp;
 	char errbuf[1024];
 	uint64_t is_zoned;
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
 
 	/* get parent, and check to see if this is just a pool */
 	if (parent_name(path, parent, sizeof (parent)) !=3D 0) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "missing dataset name"));
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 	}
 
 	/* check to see if the pool exists */
 	if ((slash =3D strchr(parent, '/')) =3D=3D NULL)
 		slash =3D parent + strlen(parent);
 	(void) strncpy(zc.zc_name, parent, slash - parent);
 	zc.zc_name[slash - parent] =3D '\0';
 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) !=3D 0 &&
 	    errno =3D=3D ENOENT) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "no such pool '%s'"), zc.zc_name);
 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
 	}
 
 	/* check to see if the parent dataset exists */
 	while ((zhp =3D make_dataset_handle(hdl, parent)) =3D=3D NULL) {
 		if (errno =3D=3D ENOENT && accept_ancestor) {
 			/*
 			 * Go deeper to find an ancestor, give up on top level.
 			 */
 			if (parent_name(parent, parent, sizeof (parent)) !=3D 0) {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "no such pool '%s'"), zc.zc_name);
 				return (zfs_error(hdl, EZFS_NOENT, errbuf));
 			}
 		} else if (errno =3D=3D ENOENT) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "parent does not exist"));
 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
 		} else
 			return (zfs_standard_error(hdl, errno, errbuf));
 	}
 
 	is_zoned =3D zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
 	if (zoned !=3D NULL)
 		*zoned =3D is_zoned;
 
 	/* we are in a non-global zone, but parent is in the global zone */
 	if (getzoneid() !=3D GLOBAL_ZONEID && !is_zoned) {
 		(void) zfs_standard_error(hdl, EPERM, errbuf);
 		zfs_close(zhp);
 		return (-1);
 	}
 
 	/* make sure parent is a filesystem */
 	if (zfs_get_type(zhp) !=3D ZFS_TYPE_FILESYSTEM) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "parent is not a filesystem"));
 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
 		zfs_close(zhp);
 		return (-1);
 	}
 
 	zfs_close(zhp);
 	if (prefixlen !=3D NULL)
 		*prefixlen =3D strlen(parent);
 	return (0);
 }
 
 /*
  * Finds whether the dataset of the given type(s) exists.
  */
 boolean_t
 zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types=
 )
 {
 	zfs_handle_t *zhp;
 
 	if (!zfs_validate_name(hdl, path, types, B_FALSE))
 		return (B_FALSE);
 
 	/*
 	 * Try to get stats for the dataset, which will tell us if it exists.
 	 */
 	if ((zhp =3D make_dataset_handle(hdl, path)) !=3D NULL) {
 		int ds_type =3D zhp->zfs_type;
 
 		zfs_close(zhp);
 		if (types & ds_type)
 			return (B_TRUE);
 	}
 	return (B_FALSE);
 }
 
 /*
  * Given a path to 'target', create all the ancestors between
  * the prefixlen portion of the path, and the target itself.
  * Fail if the initial prefixlen-ancestor does not already exist.
  */
 int
 create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
 {
 	zfs_handle_t *h;
 	char *cp;
 	const char *opname;
 
 	/* make sure prefix exists */
 	cp =3D target + prefixlen;
 	if (*cp !=3D '/') {
 		assert(strchr(cp, '/') =3D=3D NULL);
 		h =3D zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
 	} else {
 		*cp =3D '\0';
 		h =3D zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
 		*cp =3D '/';
 	}
 	if (h =3D=3D NULL)
 		return (-1);
 	zfs_close(h);
 
 	/*
 	 * Attempt to create, mount, and share any ancestor filesystems,
 	 * up to the prefixlen-long one.
 	 */
 	for (cp =3D target + prefixlen + 1;
 	    cp =3D strchr(cp, '/'); *cp =3D '/', cp++) {
 		char *logstr;
 
 		*cp =3D '\0';
 
 		h =3D make_dataset_handle(hdl, target);
 		if (h) {
 			/* it already exists, nothing to do here */
 			zfs_close(h);
 			continue;
 		}
 
 		logstr =3D hdl->libzfs_log_str;
 		hdl->libzfs_log_str =3D NULL;
 		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
 		    NULL) !=3D 0) {
 			hdl->libzfs_log_str =3D logstr;
 			opname =3D dgettext(TEXT_DOMAIN, "create");
 			goto ancestorerr;
 		}
 
 		hdl->libzfs_log_str =3D logstr;
 		h =3D zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
 		if (h =3D=3D NULL) {
 			opname =3D dgettext(TEXT_DOMAIN, "open");
 			goto ancestorerr;
 		}
 
 		if (zfs_mount(h, NULL, 0) !=3D 0) {
 			opname =3D dgettext(TEXT_DOMAIN, "mount");
 			goto ancestorerr;
 		}
 
 		if (zfs_share(h) !=3D 0) {
 			opname =3D dgettext(TEXT_DOMAIN, "share");
 			goto ancestorerr;
 		}
 
 		zfs_close(h);
 	}
 
 	return (0);
 
 ancestorerr:
 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 	    "failed to %s ancestor '%s'"), opname, target);
 	return (-1);
 }
 
 /*
  * Creates non-existing ancestors of the given path.
  */
 int
 zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
 {
 	int prefix;
 	char *path_copy;
 	int rc;
 
 	if (check_parents(hdl, path, NULL, B_TRUE, &prefix) !=3D 0)
 		return (-1);
 
 	if ((path_copy =3D strdup(path)) !=3D NULL) {
 		rc =3D create_parents(hdl, path_copy, prefix);
 		free(path_copy);
 	}
 	if (path_copy =3D=3D NULL || rc !=3D 0)
 		return (-1);
 
 	return (0);
 }
 
 /*
  * Create a new filesystem or volume.
  */
 int
 zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
     nvlist_t *props)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int ret;
 	uint64_t size =3D 0;
 	uint64_t blocksize =3D zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
 	char errbuf[1024];
 	uint64_t zoned;
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot create '%s'"), path);
 
 	/* validate the path, taking care to note the extended error message */
 	if (!zfs_validate_name(hdl, path, type, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	/* validate parents exist */
 	if (check_parents(hdl, path, &zoned, B_FALSE, NULL) !=3D 0)
 		return (-1);
 
 	/*
 	 * The failure modes when creating a dataset of a different type over
 	 * one that already exists is a little strange.  In particular, if you
 	 * try to create a dataset on top of an existing dataset, the ioctl()
 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
 	 * first try to see if the dataset exists.
 	 */
 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
 	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "dataset already exists"));
 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 	}
 
 	if (type =3D=3D ZFS_TYPE_VOLUME)
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 
 	if (props && (props =3D zfs_valid_proplist(hdl, type, props,
 	    zoned, NULL, errbuf)) =3D=3D 0)
 		return (-1);
 
 	if (type =3D=3D ZFS_TYPE_VOLUME) {
 		/*
 		 * If we are creating a volume, the size and block size must
 		 * satisfy a few restraints.  First, the blocksize must be a
 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
 		 * volsize must be a multiple of the block size, and cannot be
 		 * zero.
 		 */
 		if (props =3D=3D NULL || nvlist_lookup_uint64(props,
 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) !=3D 0) {
 			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "missing volume size"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		if ((ret =3D nvlist_lookup_uint64(props,
 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
 		    &blocksize)) !=3D 0) {
 			if (ret =3D=3D ENOENT) {
 				blocksize =3D zfs_prop_default_numeric(
 				    ZFS_PROP_VOLBLOCKSIZE);
 			} else {
 				nvlist_free(props);
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "missing volume block size"));
 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 			}
 		}
 
 		if (size =3D=3D 0) {
 			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "volume size cannot be zero"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 
 		if (size % blocksize !=3D 0) {
 			nvlist_free(props);
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "volume size must be a multiple of volume block "
 			    "size"));
 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
 		}
 	}
 
 	if (props && zcmd_write_src_nvlist(hdl, &zc, props) !=3D 0)
 		return (-1);
 	nvlist_free(props);
 
 	/* create the dataset */
 	ret =3D zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
 
 	zcmd_free_nvlists(&zc);
 
 	/* check for failure */
 	if (ret !=3D 0) {
 		char parent[ZFS_MAXNAMELEN];
 		(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 EDOM:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "volume block size must be power of 2 from "
 			    "%u to %uk"),
 			    (uint_t)SPA_MINBLOCKSIZE,
 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
 
 			return (zfs_error(hdl, EZFS_BADPROP, 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));
 #ifdef _ILP32
 		case EOVERFLOW:
 			/*
 			 * This platform can't address a volume this big.
 			 */
 			if (type =3D=3D ZFS_TYPE_VOLUME)
 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
 				    errbuf));
 #endif
 			/* FALLTHROUGH */
 		default:
 			return (zfs_standard_error(hdl, errno, errbuf));
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Destroys the given dataset.  The caller must make sure that the filesyst=
 em
  * isn't mounted, and that there are no active dependents.
  */
 int
 zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
 {
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (ZFS_IS_VOLUME(zhp)) {
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	} else {
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 	}
 
 	zc.zc_defer_destroy =3D defer;
 	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) !=3D 0) {
 		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
 		    zhp->zfs_name));
 	}
 
 	remove_mountpoint(zhp);
 
 	return (0);
 }
 
 struct destroydata {
 	nvlist_t *nvl;
 	const char *snapname;
 };
 
 static int
 zfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
 {
 	struct destroydata *dd =3D arg;
 	zfs_handle_t *szhp;
 	char name[ZFS_MAXNAMELEN];
 	int rv =3D 0;
 
 	(void) snprintf(name, sizeof (name),
 	    "%s@%s", zhp->zfs_name, dd->snapname);
 
 	szhp =3D make_dataset_handle(zhp->zfs_hdl, name);
 	if (szhp) {
 		verify(nvlist_add_boolean(dd->nvl, name) =3D=3D 0);
 		zfs_close(szhp);
 	}
 
 	rv =3D zfs_iter_filesystems(zhp, zfs_check_snap_cb, dd);
 	zfs_close(zhp);
 	return (rv);
 }
 
 /*
  * Destroys all snapshots with the given name in zhp & descendants.
  */
 int
 zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
 {
 	int ret;
 	struct destroydata dd =3D { 0 };
 
 	dd.snapname =3D snapname;
 	verify(nvlist_alloc(&dd.nvl, NV_UNIQUE_NAME, 0) =3D=3D 0);
 	(void) zfs_check_snap_cb(zfs_handle_dup(zhp), &dd);
 
 	if (nvlist_next_nvpair(dd.nvl, NULL) =3D=3D NULL) {
 		ret =3D zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
 		    zhp->zfs_name, snapname);
 	} else {
 		ret =3D zfs_destroy_snaps_nvl(zhp, dd.nvl, defer);
 	}
 	nvlist_free(dd.nvl);
 	return (ret);
 }
 
 /*
  * Destroys all the snapshots named in the nvlist.  They must be underneath
  * the zhp (either snapshots of it, or snapshots of its descendants).
  */
 int
 zfs_destroy_snaps_nvl(zfs_handle_t *zhp, nvlist_t *snaps, boolean_t defer)
 {
 	int ret;
 	zfs_cmd_t zc =3D { 0 };
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	if (zcmd_write_src_nvlist(zhp->zfs_hdl, &zc, snaps) !=3D 0)
 		return (-1);
 	zc.zc_defer_destroy =3D defer;
 
 	ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS_NVL, &zc);
 	if (ret !=3D 0) {
 		char errbuf[1024];
 
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot destroy snapshots in %s"), zc.zc_name);
 
 		switch (errno) {
 		case EEXIST:
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "snapshot is cloned"));
 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
 
 		default:
 			return (zfs_standard_error(zhp->zfs_hdl, errno,
 			    errbuf));
 		}
 	}
 
 	return (0);
 }
 
 /*
  * Clones the given dataset.  The target must be of the same type as the so=
 urce.
  */
 int
 zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
 {
 	zfs_cmd_t zc =3D { 0 };
 	char parent[ZFS_MAXNAMELEN];
 	int ret;
 	char errbuf[1024];
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zfs_type_t type;
 	uint64_t zoned;
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT);
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot create '%s'"), target);
 
 	/* validate the target/clone name */
 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	/* validate parents exist */
 	if (check_parents(hdl, target, &zoned, B_FALSE, NULL) !=3D 0)
 		return (-1);
 
 	(void) parent_name(target, parent, sizeof (parent));
 
 	/* do the clone */
 	if (ZFS_IS_VOLUME(zhp)) {
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 		type =3D ZFS_TYPE_VOLUME;
 	} else {
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 		type =3D ZFS_TYPE_FILESYSTEM;
 	}
 
 	if (props) {
 		if ((props =3D zfs_valid_proplist(hdl, type, props, zoned,
 		    zhp, errbuf)) =3D=3D NULL)
 			return (-1);
 
 		if (zcmd_write_src_nvlist(hdl, &zc, props) !=3D 0) {
 			nvlist_free(props);
 			return (-1);
 		}
 
 		nvlist_free(props);
 	}
 
 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
 	ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
 
 	zcmd_free_nvlists(&zc);
 
 	if (ret !=3D 0) {
 		switch (errno) {
 
 		case ENOENT:
 			/*
 			 * The parent doesn't exist.  We should have caught this
 			 * above, but there may a race condition that has since
 			 * destroyed the parent.
 			 *
 			 * At this point, we don't know whether it's the source
 			 * that doesn't exist anymore, or whether the target
 			 * dataset doesn't exist.
 			 */
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "no such parent '%s'"), parent);
 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
 
 		case EXDEV:
 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
 			    "source and target pools differ"));
 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
 			    errbuf));
 
 		default:
 			return (zfs_standard_error(zhp->zfs_hdl, errno,
 			    errbuf));
 		}
 	}
 
 	return (ret);
 }
 
 /*
  * Promotes the given clone fs to be the clone parent.
  */
 int
 zfs_promote(zfs_handle_t *zhp)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zfs_cmd_t zc =3D { 0 };
 	char parent[MAXPATHLEN];
 	int ret;
 	char errbuf[1024];
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot promote '%s'"), zhp->zfs_name);
 
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "snapshots can not be promoted"));
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	}
 
 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
 	if (parent[0] =3D=3D '\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 =3D zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
 
 	if (ret !=3D 0) {
 		int save_errno =3D errno;
 
 		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);
 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
 
 		default:
 			return (zfs_standard_error(hdl, save_errno, errbuf));
 		}
 	}
 	return (ret);
 }
 
 /*
  * Takes a snapshot of the given dataset.
  */
 int
 zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
     nvlist_t *props)
 {
 	const char *delim;
 	char parent[ZFS_MAXNAMELEN];
 	zfs_handle_t *zhp;
 	zfs_cmd_t zc =3D { 0 };
 	int ret;
 	char errbuf[1024];
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot snapshot '%s'"), path);
 
 	/* validate the target name */
 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 	if (props) {
 		if ((props =3D zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
 		    props, B_FALSE, NULL, errbuf)) =3D=3D NULL)
 			return (-1);
 
 		if (zcmd_write_src_nvlist(hdl, &zc, props) !=3D 0) {
 			nvlist_free(props);
 			return (-1);
 		}
 
 		nvlist_free(props);
 	}
 
 	/* make sure the parent exists and is of the appropriate type */
 	delim =3D strchr(path, '@');
 	(void) strncpy(parent, path, delim - path);
 	parent[delim - path] =3D '\0';
 
 	if ((zhp =3D zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
 	    ZFS_TYPE_VOLUME)) =3D=3D NULL) {
 		zcmd_free_nvlists(&zc);
 		return (-1);
 	}
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
 	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 	zc.zc_cookie =3D recursive;
 	ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
 
 	zcmd_free_nvlists(&zc);
 
 	/*
 	 * if it was recursive, the one that actually failed will be in
 	 * zc.zc_name.
 	 */
 	if (ret !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
 		(void) zfs_standard_error(hdl, errno, errbuf);
 	}
 
 	zfs_close(zhp);
 
 	return (ret);
 }
 
 /*
  * Destroy any more recent snapshots.  We invoke this callback on any depen=
 dents
  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then t=
 his
  * is a dependent and we should just destroy it without checking the transa=
 ction
  * group.
  */
 typedef struct rollback_data {
 	const char	*cb_target;		/* the snapshot */
 	uint64_t	cb_create;		/* creation time reference */
 	boolean_t	cb_error;
 	boolean_t	cb_dependent;
 	boolean_t	cb_force;
 } rollback_data_t;
 
 static int
 rollback_destroy(zfs_handle_t *zhp, void *data)
 {
 	rollback_data_t *cbp =3D data;
 
 	if (!cbp->cb_dependent) {
 		if (strcmp(zhp->zfs_name, cbp->cb_target) !=3D 0 &&
 		    zfs_get_type(zhp) =3D=3D ZFS_TYPE_SNAPSHOT &&
 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
 		    cbp->cb_create) {
 			char *logstr;
 
 			cbp->cb_dependent =3D B_TRUE;
 			cbp->cb_error |=3D zfs_iter_dependents(zhp, B_FALSE,
 			    rollback_destroy, cbp);
 			cbp->cb_dependent =3D B_FALSE;
 
 			logstr =3D zhp->zfs_hdl->libzfs_log_str;
 			zhp->zfs_hdl->libzfs_log_str =3D NULL;
 			cbp->cb_error |=3D zfs_destroy(zhp, B_FALSE);
 			zhp->zfs_hdl->libzfs_log_str =3D logstr;
 		}
 	} else {
 		/* We must destroy this clone; first unmount it */
 		prop_changelist_t *clp;
 
 		clp =3D changelist_gather(zhp, ZFS_PROP_NAME, 0,
 		    cbp->cb_force ? MS_FORCE: 0);
 		if (clp =3D=3D NULL || changelist_prefix(clp) !=3D 0) {
 			cbp->cb_error =3D B_TRUE;
 			zfs_close(zhp);
 			return (0);
 		}
 		if (zfs_destroy(zhp, B_FALSE) !=3D 0)
 			cbp->cb_error =3D B_TRUE;
 		else
 			changelist_remove(clp, zhp->zfs_name);
 		(void) changelist_postfix(clp);
 		changelist_free(clp);
 	}
 
 	zfs_close(zhp);
 	return (0);
 }
 
 /*
  * Given a dataset, rollback to a specific snapshot, discarding any
  * data changes since then and making it the active dataset.
  *
  * Any snapshots more recent than the target are destroyed, along with
  * their dependents.
  */
 int
 zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
 {
 	rollback_data_t cb =3D { 0 };
 	int err;
 	zfs_cmd_t zc =3D { 0 };
 	boolean_t restore_resv =3D 0;
 	uint64_t old_volsize, new_volsize;
 	zfs_prop_t resv_prop;
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM ||
 	    zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME);
 
 	/*
 	 * Destroy all recent snapshots and its dependends.
 	 */
 	cb.cb_force =3D force;
 	cb.cb_target =3D snap->zfs_name;
 	cb.cb_create =3D zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
 
 	if (cb.cb_error)
 		return (-1);
 
 	/*
 	 * Now that we have verified that the snapshot is the latest,
 	 * rollback to the given snapshot.
 	 */
 
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME) {
 		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
 			return (-1);
 		old_volsize =3D zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
 		restore_resv =3D
 		    (old_volsize =3D=3D zfs_prop_get_int(zhp, resv_prop));
 	}
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 
 	/*
 	 * 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.
 	 *
 	 */
 	if ((err =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) !=3D 0) {
 		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
 		    zhp->zfs_name);
 		return (err);
 	}
 
 	/*
 	 * For volumes, if the pre-rollback volsize matched the pre-
 	 * rollback reservation and the volsize has changed then set
 	 * the reservation property to the post-rollback volsize.
 	 * Make a new handle since the rollback closed the dataset.
 	 */
 	if ((zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME) &&
 	    (zhp =3D make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
 		if (restore_resv) {
 			new_volsize =3D zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
 			if (old_volsize !=3D new_volsize)
 				err =3D zfs_prop_set_int(zhp, resv_prop,
 				    new_volsize);
 		}
 		zfs_close(zhp);
 	}
 	return (err);
 }
 
 /*
  * Renames the given dataset.
  */
 int
 zfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
 {
 	int ret;
 	zfs_cmd_t zc =3D { 0 };
 	char *delim;
 	prop_changelist_t *cl =3D NULL;
 	zfs_handle_t *zhrp =3D NULL;
 	char *parentname =3D NULL;
 	char parent[ZFS_MAXNAMELEN];
 	char property[ZFS_MAXPROPLEN];
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	char errbuf[1024];
 
 	/* if we have the same exact name, just return success */
 	if (strcmp(zhp->zfs_name, target) =3D=3D 0)
 		return (0);
 
 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 	    "cannot rename to '%s'"), target);
 
 	/*
 	 * Make sure the target name is valid
 	 */
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT) {
 		if ((strchr(target, '@') =3D=3D NULL) ||
 		    *target =3D=3D '@') {
 			/*
 			 * Snapshot target name is abbreviated,
 			 * reconstruct full dataset name
 			 */
 			(void) strlcpy(parent, zhp->zfs_name,
 			    sizeof (parent));
 			delim =3D strchr(parent, '@');
 			if (strchr(target, '@') =3D=3D NULL)
 				*(++delim) =3D '\0';
 			else
 				*delim =3D '\0';
 			(void) strlcat(parent, target, sizeof (parent));
 			target =3D parent;
 		} else {
 			/*
 			 * Make sure we're renaming within the same dataset.
 			 */
 			delim =3D strchr(target, '@');
 			if (strncmp(zhp->zfs_name, target, delim - target)
 			    !=3D 0 || zhp->zfs_name[delim - target] !=3D '@') {
 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 				    "snapshots must be part of same "
 				    "dataset"));
 				return (zfs_error(hdl, EZFS_CROSSTARGET,
 				    errbuf));
 			}
 		}
 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 	} else {
 		if (flags.recurse) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "recursive rename must be a snapshot"));
 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 		}
 
 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 
 		/* validate parents */
 		if (check_parents(hdl, target, NULL, B_FALSE, NULL) !=3D 0)
 			return (-1);
 
 		/* make sure we're in the same pool */
 		verify((delim =3D strchr(target, '/')) !=3D NULL);
 		if (strncmp(zhp->zfs_name, target, delim - target) !=3D 0 ||
 		    zhp->zfs_name[delim - target] !=3D '/') {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "datasets must be within same pool"));
 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
 		}
 
 		/* new name cannot be a child of the current dataset name */
 		if (is_descendant(zhp->zfs_name, target)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "New dataset name cannot be a descendant of "
 			    "current dataset name"));
 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
 		}
 	}
 
 	(void) snprintf(errbuf, sizeof (errbuf),
 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
 
 	if (getzoneid() =3D=3D GLOBAL_ZONEID &&
 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "dataset is used in a non-global zone"));
 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
 	}
 
 	/*
 	 * Avoid unmounting file systems with mountpoint property set to
 	 * 'legacy' or 'none' even if -u option is not given.
 	 */
 	if (zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM &&
 	    !flags.recurse && !flags.nounmount &&
 	    zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
 	    sizeof (property), NULL, NULL, 0, B_FALSE) =3D=3D 0 &&
 	    (strcmp(property, "legacy") =3D=3D 0 ||
 	     strcmp(property, "none") =3D=3D 0)) {
 		flags.nounmount =3D B_TRUE;
 	}
 
 	if (flags.recurse) {
 
 		parentname =3D zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
 		if (parentname =3D=3D NULL) {
 			ret =3D -1;
 			goto error;
 		}
 		delim =3D strchr(parentname, '@');
 		*delim =3D '\0';
 		zhrp =3D zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
 		if (zhrp =3D=3D NULL) {
 			ret =3D -1;
 			goto error;
 		}
 
 	} else {
 		if ((cl =3D changelist_gather(zhp, ZFS_PROP_NAME,
 		    flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0,
 		    flags.forceunmount ? MS_FORCE : 0)) =3D=3D NULL) {
 			return (-1);
 		}
 
 		if (changelist_haszonedchild(cl)) {
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "child dataset with inherited mountpoint is used "
 			    "in a non-global zone"));
 			(void) zfs_error(hdl, EZFS_ZONED, errbuf);
 			goto error;
 		}
 
 		if ((ret =3D changelist_prefix(cl)) !=3D 0)
 			goto error;
 	}
 
 	if (ZFS_IS_VOLUME(zhp))
 		zc.zc_objset_type =3D DMU_OST_ZVOL;
 	else
 		zc.zc_objset_type =3D DMU_OST_ZFS;
 
 	(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 =3D flags.recurse ? 1 : 0;
 	if (flags.nounmount)
 		zc.zc_cookie |=3D 2;
 
 	if ((ret =3D zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) !=3D 0) {
 		/*
 		 * if it was recursive, the one that actually failed will
 		 * be in zc.zc_name
 		 */
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot rename '%s'"), zc.zc_name);
 
 		if (flags.recurse && errno =3D=3D 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 {
 			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
 		}
 
 		/*
 		 * On failure, we still want to remount any filesystems that
 		 * were previously mounted, so we don't alter the system state.
 		 */
 		if (!flags.recurse)
 			(void) changelist_postfix(cl);
 	} else {
 		if (!flags.recurse) {
 			changelist_rename(cl, zfs_get_name(zhp), target);
 			ret =3D changelist_postfix(cl);
 		}
 	}
 
 error:
 	if (parentname) {
 		free(parentname);
 	}
 	if (zhrp) {
 		zfs_close(zhrp);
 	}
 	if (cl) {
 		changelist_free(cl);
 	}
 	return (ret);
 }
 
 nvlist_t *
 zfs_get_user_props(zfs_handle_t *zhp)
 {
 	return (zhp->zfs_user_props);
 }
 
 nvlist_t *
 zfs_get_recvd_props(zfs_handle_t *zhp)
 {
 	if (zhp->zfs_recvd_props =3D=3D NULL)
 		if (get_recvd_props_ioctl(zhp) !=3D 0)
 			return (NULL);
 	return (zhp->zfs_recvd_props);
 }
 
 /*
  * This function is used by 'zfs list' to determine the exact set of column=
 s to
  * display, and their maximum widths.  This does two main things:
  *
  *      - If this is a list of all properties, then expand the list to incl=
 ude
  *        all native properties, and set a flag so that for each dataset we=
  look
  *        for new unique user properties and add them to the list.
  *
  *      - For non fixed-width properties, keep track of the maximum width s=
 een
  *        so that we can size the column appropriately. If the user has
  *        requested received property values, we also need to compute the w=
 idth
  *        of the RECEIVED column.
  */
 int
 zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t receiv=
 ed)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zprop_list_t *entry;
 	zprop_list_t **last, **start;
 	nvlist_t *userprops, *propval;
 	nvpair_t *elem;
 	char *strval;
 	char buf[ZFS_MAXPROPLEN];
 
 	if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) !=3D 0)
 		return (-1);
 
 	userprops =3D zfs_get_user_props(zhp);
 
 	entry =3D *plp;
 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) !=3D NULL) {
 		/*
 		 * Go through and add any user properties as necessary.  We
 		 * start by incrementing our list pointer to the first
 		 * non-native property.
 		 */
 		start =3D plp;
 		while (*start !=3D NULL) {
 			if ((*start)->pl_prop =3D=3D ZPROP_INVAL)
 				break;
 			start =3D &(*start)->pl_next;
 		}
 
 		elem =3D NULL;
 		while ((elem =3D nvlist_next_nvpair(userprops, elem)) !=3D NULL) {
 			/*
 			 * See if we've already found this property in our list.
 			 */
 			for (last =3D start; *last !=3D NULL;
 			    last =3D &(*last)->pl_next) {
 				if (strcmp((*last)->pl_user_prop,
 				    nvpair_name(elem)) =3D=3D 0)
 					break;
 			}
 
 			if (*last =3D=3D NULL) {
 				if ((entry =3D zfs_alloc(hdl,
 				    sizeof (zprop_list_t))) =3D=3D NULL ||
 				    ((entry->pl_user_prop =3D zfs_strdup(hdl,
 				    nvpair_name(elem)))) =3D=3D NULL) {
 					free(entry);
 					return (-1);
 				}
 
 				entry->pl_prop =3D ZPROP_INVAL;
 				entry->pl_width =3D strlen(nvpair_name(elem));
 				entry->pl_all =3D B_TRUE;
 				*last =3D entry;
 			}
 		}
 	}
 
 	/*
 	 * Now go through and check the width of any non-fixed columns
 	 */
 	for (entry =3D *plp; entry !=3D NULL; entry =3D entry->pl_next) {
 		if (entry->pl_fixed)
 			continue;
 
 		if (entry->pl_prop !=3D ZPROP_INVAL) {
 			if (zfs_prop_get(zhp, entry->pl_prop,
 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) =3D=3D 0) {
 				if (strlen(buf) > entry->pl_width)
 					entry->pl_width =3D strlen(buf);
 			}
 			if (received && zfs_prop_get_recvd(zhp,
 			    zfs_prop_to_name(entry->pl_prop),
 			    buf, sizeof (buf), B_FALSE) =3D=3D 0)
 				if (strlen(buf) > entry->pl_recvd_width)
 					entry->pl_recvd_width =3D strlen(buf);
 		} else {
 			if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
 			    &propval) =3D=3D 0) {
 				verify(nvlist_lookup_string(propval,
 				    ZPROP_VALUE, &strval) =3D=3D 0);
 				if (strlen(strval) > entry->pl_width)
 					entry->pl_width =3D strlen(strval);
 			}
 			if (received && zfs_prop_get_recvd(zhp,
 			    entry->pl_user_prop,
 			    buf, sizeof (buf), B_FALSE) =3D=3D 0)
 				if (strlen(buf) > entry->pl_recvd_width)
 					entry->pl_recvd_width =3D strlen(buf);
 		}
 	}
 
 	return (0);
 }
 
 int
 zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
     char *resource, void *export, void *sharetab,
     int sharemax, zfs_share_op_t operation)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int error;
 
 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
 	if (resource)
 		(void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
 	zc.zc_share.z_sharedata =3D (uint64_t)(uintptr_t)sharetab;
 	zc.zc_share.z_exportdata =3D (uint64_t)(uintptr_t)export;
 	zc.zc_share.z_sharetype =3D operation;
 	zc.zc_share.z_sharemax =3D sharemax;
 	error =3D ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
 	return (error);
 }
 
 void
 zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
 {
 	nvpair_t *curr;
 
 	/*
 	 * Keep a reference to the props-table against which we prune the
 	 * properties.
 	 */
 	zhp->zfs_props_table =3D props;
 
 	curr =3D nvlist_next_nvpair(zhp->zfs_props, NULL);
 
 	while (curr) {
 		zfs_prop_t zfs_prop =3D zfs_name_to_prop(nvpair_name(curr));
 		nvpair_t *next =3D nvlist_next_nvpair(zhp->zfs_props, curr);
 
 		/*
 		 * User properties will result in 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 !=3D ZPROP_INVAL && props[zfs_prop] =3D=3D B_FALSE)
 			(void) nvlist_remove(zhp->zfs_props,
 			    nvpair_name(curr), nvpair_type(curr));
 		curr =3D next;
 	}
 }
 
 #ifdef sun
 static int
 zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
     zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
 {
 	zfs_cmd_t zc =3D { 0 };
 	nvlist_t *nvlist =3D NULL;
 	int error;
 
 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
 	zc.zc_cookie =3D (uint64_t)cmd;
 
 	if (cmd =3D=3D ZFS_SMB_ACL_RENAME) {
 		if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) !=3D 0) {
 			(void) no_memory(hdl);
 			return (NULL);
 		}
 	}
 
 	switch (cmd) {
 	case ZFS_SMB_ACL_ADD:
 	case ZFS_SMB_ACL_REMOVE:
 		(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
 		break;
 	case ZFS_SMB_ACL_RENAME:
 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
 		    resource1) !=3D 0) {
 				(void) no_memory(hdl);
 				return (-1);
 		}
 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
 		    resource2) !=3D 0) {
 				(void) no_memory(hdl);
 				return (-1);
 		}
 		if (zcmd_write_src_nvlist(hdl, &zc, nvlist) !=3D 0) {
 			nvlist_free(nvlist);
 			return (-1);
 		}
 		break;
 	case ZFS_SMB_ACL_PURGE:
 		break;
 	default:
 		return (-1);
 	}
 	error =3D ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
 	if (nvlist)
 		nvlist_free(nvlist);
 	return (error);
 }
 
 int
 zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
     char *path, char *resource)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
 	    resource, NULL));
 }
 
 int
 zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
     char *path, char *resource)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
 	    resource, NULL));
 }
 
 int
 zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
 	    NULL, NULL));
 }
 
 int
 zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
     char *oldname, char *newname)
 {
 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
 	    oldname, newname));
 }
 #endif	/* sun */
 
 int
 zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
     zfs_userspace_cb_t func, void *arg)
 {
 	zfs_cmd_t zc =3D { 0 };
 	int error;
 	zfs_useracct_t buf[100];
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	zc.zc_objset_type =3D type;
 	zc.zc_nvlist_dst =3D (uintptr_t)buf;
 
 	/* CONSTCOND */
 	while (1) {
 		zfs_useracct_t *zua =3D buf;
 
 		zc.zc_nvlist_dst_size =3D sizeof (buf);
 		error =3D ioctl(zhp->zfs_hdl->libzfs_fd,
 		    ZFS_IOC_USERSPACE_MANY, &zc);
 		if (error || zc.zc_nvlist_dst_size =3D=3D 0)
 			break;
 
 		while (zc.zc_nvlist_dst_size > 0) {
 			error =3D func(arg, zua->zu_domain, zua->zu_rid,
 			    zua->zu_space);
 			if (error !=3D 0)
 				return (error);
 			zua++;
 			zc.zc_nvlist_dst_size -=3D sizeof (zfs_useracct_t);
 		}
 	}
 
 	return (error);
 }
 
 int
 zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
     boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
     int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 
 	ASSERT(!recursive || dsobj =3D=3D 0);
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
 	    >=3D sizeof (zc.zc_string))
 		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
 	zc.zc_cookie =3D recursive;
 	zc.zc_temphold =3D temphold;
 	zc.zc_cleanup_fd =3D cleanup_fd;
 	zc.zc_sendobj =3D dsobj;
 	zc.zc_createtxg =3D createtxg;
 
 	if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) !=3D 0) {
 		char errbuf[ZFS_MAXNAMELEN+32];
 
 		/*
 		 * if it was recursive, the one that actually failed will be in
 		 * zc.zc_name.
 		 */
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot hold '%s@%s'"), zc.zc_name, snapname);
 		switch (errno) {
 		case E2BIG:
 			/*
 			 * Temporary tags wind up having the ds object id
 			 * prepended. So even if we passed the length check
 			 * above, it's still possible for the tag to wind
 			 * up being slightly too long.
 			 */
 			return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf));
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
 		case EINVAL:
 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 		case EEXIST:
 			return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
 		case ENOENT:
 			if (enoent_ok)
 				return (ENOENT);
 			/* FALLTHROUGH */
 		default:
 			return (zfs_standard_error_fmt(hdl, errno, errbuf));
 		}
 	}
 
 	return (0);
 }
 
 int
 zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
     boolean_t recursive)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
 	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
 	    >=3D sizeof (zc.zc_string))
 		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
 	zc.zc_cookie =3D recursive;
 
 	if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) !=3D 0) {
 		char errbuf[ZFS_MAXNAMELEN+32];
 
 		/*
 		 * if it was recursive, the one that actually failed will be in
 		 * zc.zc_name.
 		 */
 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
 		    "cannot release '%s' from '%s@%s'"), tag, zc.zc_name,
 		    snapname);
 		switch (errno) {
 		case ESRCH:
 			return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
 		case EINVAL:
 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 		default:
 			return (zfs_standard_error_fmt(hdl, errno, errbuf));
 		}
 	}
 
 	return (0);
 }
 
 int
 zfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	int nvsz =3D 2048;
 	void *nvbuf;
 	int err =3D 0;
 	char errbuf[ZFS_MAXNAMELEN+32];
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME ||
 	    zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM);
 
 tryagain:
 
 	nvbuf =3D malloc(nvsz);
 	if (nvbuf =3D=3D NULL) {
 		err =3D (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
 		goto out;
 	}
 
 	zc.zc_nvlist_dst_size =3D nvsz;
 	zc.zc_nvlist_dst =3D (uintptr_t)nvbuf;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
 
 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_GET_FSACL, &zc) !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
 		    zc.zc_name);
 		switch (errno) {
 		case ENOMEM:
 			free(nvbuf);
 			nvsz =3D zc.zc_nvlist_dst_size;
 			goto tryagain;
 
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			err =3D zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 		case EINVAL:
 			err =3D zfs_error(hdl, EZFS_BADTYPE, errbuf);
 			break;
 		case ENOENT:
 			err =3D zfs_error(hdl, EZFS_NOENT, errbuf);
 			break;
 		default:
 			err =3D zfs_standard_error_fmt(hdl, errno, errbuf);
 			break;
 		}
 	} else {
 		/* success */
 		int rc =3D nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
 		if (rc) {
 			(void) snprintf(errbuf, sizeof (errbuf), dgettext(
 			    TEXT_DOMAIN, "cannot get permissions on '%s'"),
 			    zc.zc_name);
 			err =3D zfs_standard_error_fmt(hdl, rc, errbuf);
 		}
 	}
 
 	free(nvbuf);
 out:
 	return (err);
 }
 
 int
 zfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	char *nvbuf;
 	char errbuf[ZFS_MAXNAMELEN+32];
 	size_t nvsz;
 	int err;
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_VOLUME ||
 	    zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM);
 
 	err =3D nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
 	assert(err =3D=3D 0);
 
 	nvbuf =3D malloc(nvsz);
 
 	err =3D nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
 	assert(err =3D=3D 0);
 
 	zc.zc_nvlist_src_size =3D nvsz;
 	zc.zc_nvlist_src =3D (uintptr_t)nvbuf;
 	zc.zc_perm_action =3D un;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 
 	if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
 		    zc.zc_name);
 		switch (errno) {
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			err =3D zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 		case EINVAL:
 			err =3D zfs_error(hdl, EZFS_BADTYPE, errbuf);
 			break;
 		case ENOENT:
 			err =3D zfs_error(hdl, EZFS_NOENT, errbuf);
 			break;
 		default:
 			err =3D zfs_standard_error_fmt(hdl, errno, errbuf);
 			break;
 		}
 	}
 
 	free(nvbuf);
 
 	return (err);
 }
 
 int
 zfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
 {
 	zfs_cmd_t zc =3D { 0 };
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	int nvsz =3D 2048;
 	void *nvbuf;
 	int err =3D 0;
 	char errbuf[ZFS_MAXNAMELEN+32];
 
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_SNAPSHOT);
 
 tryagain:
 
 	nvbuf =3D malloc(nvsz);
 	if (nvbuf =3D=3D NULL) {
 		err =3D (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
 		goto out;
 	}
 
 	zc.zc_nvlist_dst_size =3D nvsz;
 	zc.zc_nvlist_dst =3D (uintptr_t)nvbuf;
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
 
 	if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) !=3D 0) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
 		    zc.zc_name);
 		switch (errno) {
 		case ENOMEM:
 			free(nvbuf);
 			nvsz =3D zc.zc_nvlist_dst_size;
 			goto tryagain;
 
 		case ENOTSUP:
 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 			    "pool must be upgraded"));
 			err =3D zfs_error(hdl, EZFS_BADVERSION, errbuf);
 			break;
 		case EINVAL:
 			err =3D zfs_error(hdl, EZFS_BADTYPE, errbuf);
 			break;
 		case ENOENT:
 			err =3D zfs_error(hdl, EZFS_NOENT, errbuf);
 			break;
 		default:
 			err =3D zfs_standard_error_fmt(hdl, errno, errbuf);
 			break;
 		}
 	} else {
 		/* success */
 		int rc =3D nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
 		if (rc) {
 			(void) snprintf(errbuf, sizeof (errbuf),
 			    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
 			    zc.zc_name);
 			err =3D zfs_standard_error_fmt(hdl, rc, errbuf);
 		}
 	}
 
 	free(nvbuf);
 out:
 	return (err);
 }
 
 uint64_t
 zvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
 {
 	uint64_t numdb;
 	uint64_t nblocks, volblocksize;
 	int ncopies;
 	char *strval;
 
 	if (nvlist_lookup_string(props,
 	    zfs_prop_to_name(ZFS_PROP_COPIES), &strval) =3D=3D 0)
 		ncopies =3D atoi(strval);
 	else
 		ncopies =3D 1;
 	if (nvlist_lookup_uint64(props,
 	    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
 	    &volblocksize) !=3D 0)
 		volblocksize =3D ZVOL_DEFAULT_BLOCKSIZE;
 	nblocks =3D volsize/volblocksize;
 	/* start with metadnode L0-L6 */
 	numdb =3D 7;
 	/* calculate number of indirects */
 	while (nblocks > 1) {
 		nblocks +=3D DNODES_PER_LEVEL - 1;
 		nblocks /=3D DNODES_PER_LEVEL;
 		numdb +=3D nblocks;
 	}
 	numdb *=3D MIN(SPA_DVAS_PER_BP, ncopies + 1);
 	volsize *=3D ncopies;
 	/*
 	 * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
 	 * compressed, but in practice they compress down to about
 	 * 1100 bytes
 	 */
 	numdb *=3D 1ULL << DN_MAX_INDBLKSHIFT;
 	volsize +=3D numdb;
 	return (volsize);
 }
 
 /*
  * Attach/detach the given filesystem to/from the given jail.
  */
 int
 zfs_jail(zfs_handle_t *zhp, int jailid, int attach)
 {
 	libzfs_handle_t *hdl =3D zhp->zfs_hdl;
 	zfs_cmd_t zc =3D { 0 };
 	char errbuf[1024];
 	unsigned long cmd;
 	int ret;
 
 	if (attach) {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
 	} else {
 		(void) snprintf(errbuf, sizeof (errbuf),
 		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
 	}
 
 	switch (zhp->zfs_type) {
 	case ZFS_TYPE_VOLUME:
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "volumes can not be jailed"));
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	case ZFS_TYPE_SNAPSHOT:
 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
 		    "snapshots can not be jailed"));
 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
 	}
 	assert(zhp->zfs_type =3D=3D ZFS_TYPE_FILESYSTEM);
 
 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
 	zc.zc_objset_type =3D DMU_OST_ZFS;
 	zc.zc_jailid =3D jailid;
 
 	cmd =3D attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
 	if ((ret =3D ioctl(hdl->libzfs_fd, cmd, &zc)) !=3D 0)
 		zfs_standard_error(hdl, errno, errbuf);
 
 	return (ret);
 }
 
 
 --=-eOAg1opG/d7eXMURa76A--

From: Bryan Drewery <bryan@shatow.net>
To: vermaden <vermaden@interia.pl>
Cc: bug-followup@FreeBSD.org, Pawel Jakub Dawidek <pjd@FreeBSD.org>, 
 mm@freebsd.org
Subject: Re: kern/167905: [zfs] zfs set canmount=on UNMOUNTS dataset
Date: Wed, 13 Jun 2012 11:04:23 -0500

 On 6/13/2012 1:37 AM, vermaden wrote:
 > at last I had some time to check Your work, but the patch did not applied to 9-STABLE @ r236934:
 
 It applies to HEAD and 9/stable for me.
 
 HEAD/cddl# patch -p1 -C < ../patch
 Hmm...  Looks like a unified diff to me...
 The text leading up to this was:
 --------------------------
 |--- cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c.orig
 2012-06-12 00:10:11.000000000 -0500
 |+++ cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 2012-06-12 00:17:34.000000000 -0500
 --------------------------
 Patching file contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 using Plan A...
 Hunk #1 succeeded at 1485 (offset 18 lines).
 done
 
 
 -- 
 Regards,
 Bryan Drewery
 bdrewery@freenode/EFNet
Responsible-Changed-From-To: freebsd-fs->mm 
Responsible-Changed-By: mm 
Responsible-Changed-When: Thu Jun 14 12:04:57 UTC 2012 
Responsible-Changed-Why:  
I'll take it. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=167905 

From: Martin Matuska <mm@FreeBSD.org>
To: bug-followup@FreeBSD.org, vermaden@interia.pl
Cc:  
Subject: Re: kern/167905: [zfs] zfs set canmount=on UNMOUNTS dataset
Date: Thu, 14 Jun 2012 14:39:06 +0200

 This is a multi-part message in MIME format.
 --------------080407040901020506040606
 Content-Type: text/plain; charset=UTF-8
 Content-Transfer-Encoding: 7bit
 
 I agree to your change, I have modified it only cosmetically.
 
 CDDL requires copyright information, so you have two choices:
 a) you provide me with your full name and will be added to the Copyright
 section with your full name
 b) you pass the copyright to me and allow me to commit without changing
 the Copyright header (my Copyright for 2012 is already included)
 
 Thanks,
 mm
 
 -- 
 Martin Matuska
 FreeBSD committer
 http://blog.vx.sk
 
 
 --------------080407040901020506040606
 Content-Type: text/plain; charset=windows-1250;
  name="canmount_on.patch"
 Content-Transfer-Encoding: base64
 Content-Disposition: attachment;
  filename="canmount_on.patch"
 
 SW5kZXg6IGNkZGwvY29udHJpYi9vcGVuc29sYXJpcy9saWIvbGliemZzL2NvbW1vbi9saWJ6
 ZnNfZGF0YXNldC5jCj09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09
 PT09PT09PT09PT09PT09PT09PT09PT09PT0KLS0tIGNkZGwvY29udHJpYi9vcGVuc29sYXJp
 cy9saWIvbGliemZzL2NvbW1vbi9saWJ6ZnNfZGF0YXNldC5jCShyZXZpc2lvbiAyMzY4ODkp
 CisrKyBjZGRsL2NvbnRyaWIvb3BlbnNvbGFyaXMvbGliL2xpYnpmcy9jb21tb24vbGliemZz
 X2RhdGFzZXQuYwkod29ya2luZyBjb3B5KQpAQCAtMTQ4NSwxMSArMTQ4NSwxMyBAQAogCiAJ
 LyoKIAkgKiBJZiB0aGUgZGF0YXNldCdzIGNhbm1vdW50IHByb3BlcnR5IGlzIGJlaW5nIHNl
 dCB0byBub2F1dG8sCisJICogb3IgYmVpbmcgc2V0IHRvIG9uIGFuZCB0aGUgZGF0YXNldCBp
 cyBhbHJlYWR5IG1vdW50ZWQsCiAJICogdGhlbiB3ZSB3YW50IHRvIHByZXZlbnQgdW5tb3Vu
 dGluZyAmIHJlbW91bnRpbmcgaXQuCiAJICovCiAJZG9fcHJlZml4ID0gISgocHJvcCA9PSBa
 RlNfUFJPUF9DQU5NT1VOVCkgJiYKIAkgICAgKHpwcm9wX3N0cmluZ190b19pbmRleChwcm9w
 LCBwcm9wdmFsLCAmaWR4LAotCSAgICBaRlNfVFlQRV9EQVRBU0VUKSA9PSAwKSAmJiAoaWR4
 ID09IFpGU19DQU5NT1VOVF9OT0FVVE8pKTsKKwkgICAgWkZTX1RZUEVfREFUQVNFVCkgPT0g
 MCkgJiYgKGlkeCA9PSBaRlNfQ0FOTU9VTlRfTk9BVVRPIHx8CisJICAgIChpZHggPT0gWkZT
 X0NBTk1PVU5UX09OICYmIHpmc19pc19tb3VudGVkKHpocCwgTlVMTCkpKSk7CiAKIAlpZiAo
 ZG9fcHJlZml4ICYmIChyZXQgPSBjaGFuZ2VsaXN0X3ByZWZpeChjbCkpICE9IDApCiAJCWdv
 dG8gZXJyb3I7Cg==
 --------------080407040901020506040606--

From: vermaden <vermaden@interia.pl>
To: Martin Matuska <mm@FreeBSD.org>
Cc: bug-followup@FreeBSD.org, Bryan Drewery <bryan@shatow.net>
Subject: Re: kern/167905: [zfs] zfs set canmount=on UNMOUNTS dataset
Date: Thu, 14 Jun 2012 19:38:57 +0200

 Hi,
 
 Bryan Drewery (CC) is author of that patch, ask him about
 the copyright line, I am the author of beadm on FreeBSD.
 
 Regards,
 vermaden
 
 "Martin Matuska" <mm@FreeBSD.org> pisze:
 > I agree to your change, I have modified it only cosmetically.
 >=20
 > CDDL requires copyright information, so you have two choices:
 > a) you provide me with your full name and will be added to the Copyright
 > section with your full name
 > b) you pass the copyright to me and allow me to commit without changing
 > the Copyright header (my Copyright for 2012 is already included)
 >=20
 > Thanks,
 > mm
 >=20
 > --=20
 > Martin Matuska
 > FreeBSD committer
 > http://blog.vx.sk
 >=20
 >=20
 
 
 
 --=20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 ...

From: Bryan Drewery <bryan@shatow.net>
To: bug-followup@FreeBSD.org, mm@freebsd.org
Cc: vermaden@interia.pl
Subject: Re: kern/167905: [zfs] zfs set canmount=on UNMOUNTS dataset
Date: Thu, 14 Jun 2012 12:48:45 -0500

 This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
 --------------enigC3322CDD9DD5F4B3D05D3A89
 Content-Type: text/plain; charset=ISO-8859-1
 Content-Transfer-Encoding: quoted-printable
 
 I'm fine passing the copyright to you.
 
 Thanks,
 Bryan Drewery
 
 
 --------------enigC3322CDD9DD5F4B3D05D3A89
 Content-Type: application/pgp-signature; name="signature.asc"
 Content-Description: OpenPGP digital signature
 Content-Disposition: attachment; filename="signature.asc"
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.9 (MingW32)
 Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/
 
 iQIcBAEBAgAGBQJP2iQFAAoJEG54KsA8mwz5Fn0QAKjFV9G9EofYp6DxJVtO49Ok
 KPz33Ddfu0OA7FjE7LQukemcZ0Gnei5j2ilZ6ereEowKmaylxNE0duA6CUEd9DPk
 EGaEb2ODykpspXy6EcDML/pp2VryIJg2X6nd3KHFabBtPCR8Aq+J6p4yKeJyL30Y
 cJyNgXz2omJTy7Ofprqj8LTK8ItLLGuYsdtiMiybv8u43SdDNOSB5futj2ozeT84
 uY8pVLV+qBGYuh2+DrsG3ACdEtm89Q/BGscGHKRsuyXQKwDlJjo8eb3opPsDjgm4
 fvaJClL2wJS/eANf/g3YjBsNY2DoV7MNOT493FPxon19iQso6RcWuiV+fmVjaHxg
 nsh/cg+39UWHZaJgYujO5C/PCZXnJweTR/kjbkDyUwb5rtLdhn1xzzGU0D9UYWPM
 WIJ+F3VubAFDLci5Bc0/3lsAPpLTYG1CltERf14+JShz67lFG83rZdjlNJ9W9oMO
 aMZHWwqFUxsCkS4dtSNP4q4glRZQIypA6KsV3E2o8aeDS7/60D5aLbqKU3EPFRnO
 lbEKWz9H2+/jZdvA8cj8UnTKIyn6V8Vns5lYtEopUN3choEJTnfnuXDt+j+V4qWz
 Z/ZnX2ebLsGZuFA3r7FQnlVppR6V71n9hs49cTHyCGSZnXVtj+1WA1qMQiihyzDS
 SEt2GGjHy13z2Q5lspsK
 =tDc9
 -----END PGP SIGNATURE-----
 
 --------------enigC3322CDD9DD5F4B3D05D3A89--

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/167905: commit references a PR
Date: Fri, 15 Jun 2012 07:38:35 +0000 (UTC)

 Author: mm
 Date: Fri Jun 15 07:38:21 2012
 New Revision: 237119
 URL: http://svn.freebsd.org/changeset/base/237119
 
 Log:
   Do not remount ZFS dataset if changing canmount property to "on" and
   dataset is already mounted.
   
   PR:		167905
   Submitted by:	Bryan Drewery <bryan@shatow.net>
   MFC after:	1 week
 
 Modified:
   head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 
 Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 ==============================================================================
 --- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Fri Jun 15 07:26:39 2012	(r237118)
 +++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Fri Jun 15 07:38:21 2012	(r237119)
 @@ -1485,11 +1485,13 @@ zfs_prop_set(zfs_handle_t *zhp, const ch
  
  	/*
  	 * If the dataset's canmount property is being set to noauto,
 +	 * or being set to on and the dataset is already mounted,
  	 * then we want to prevent unmounting & remounting it.
  	 */
  	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
  	    (zprop_string_to_index(prop, propval, &idx,
 -	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
 +	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO ||
 +	    (idx == ZFS_CANMOUNT_ON && zfs_is_mounted(zhp, NULL))));
  
  	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
  		goto error;
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/167905: commit references a PR
Date: Fri, 22 Jun 2012 20:38:12 +0000 (UTC)

 Author: mm
 Date: Fri Jun 22 20:38:00 2012
 New Revision: 237456
 URL: http://svn.freebsd.org/changeset/base/237456
 
 Log:
   MFC r237119:
   
   Do not remount ZFS dataset if changing canmount property to "on" and
   dataset is already mounted.
   
   PR:		167905
   Submitted by:	Bryan Drewery <bryan@shatow.net>
 
 Modified:
   stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 Directory Properties:
   stable/9/cddl/contrib/opensolaris/   (props changed)
 
 Modified: stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 ==============================================================================
 --- stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Fri Jun 22 19:19:58 2012	(r237455)
 +++ stable/9/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Fri Jun 22 20:38:00 2012	(r237456)
 @@ -1485,11 +1485,13 @@ zfs_prop_set(zfs_handle_t *zhp, const ch
  
  	/*
  	 * If the dataset's canmount property is being set to noauto,
 +	 * or being set to on and the dataset is already mounted,
  	 * then we want to prevent unmounting & remounting it.
  	 */
  	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
  	    (zprop_string_to_index(prop, propval, &idx,
 -	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
 +	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO ||
 +	    (idx == ZFS_CANMOUNT_ON && zfs_is_mounted(zhp, NULL))));
  
  	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
  		goto error;
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/167905: commit references a PR
Date: Fri, 22 Jun 2012 20:38:26 +0000 (UTC)

 Author: mm
 Date: Fri Jun 22 20:38:08 2012
 New Revision: 237457
 URL: http://svn.freebsd.org/changeset/base/237457
 
 Log:
   MFC r237119:
   
   Do not remount ZFS dataset if changing canmount property to "on" and
   dataset is already mounted.
   
   PR:		167905
   Submitted by:	Bryan Drewery <bryan@shatow.net>
 
 Modified:
   stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 Directory Properties:
   stable/8/cddl/contrib/opensolaris/   (props changed)
 
 Modified: stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 ==============================================================================
 --- stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Fri Jun 22 20:38:00 2012	(r237456)
 +++ stable/8/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Fri Jun 22 20:38:08 2012	(r237457)
 @@ -1485,11 +1485,13 @@ zfs_prop_set(zfs_handle_t *zhp, const ch
  
  	/*
  	 * If the dataset's canmount property is being set to noauto,
 +	 * or being set to on and the dataset is already mounted,
  	 * then we want to prevent unmounting & remounting it.
  	 */
  	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
  	    (zprop_string_to_index(prop, propval, &idx,
 -	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
 +	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO ||
 +	    (idx == ZFS_CANMOUNT_ON && zfs_is_mounted(zhp, NULL))));
  
  	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
  		goto error;
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->closed 
State-Changed-By: mm 
State-Changed-When: Fri Jun 22 20:41:25 UTC 2012 
State-Changed-Why:  
Committed. Thanks! 

http://www.freebsd.org/cgi/query-pr.cgi?pr=167905 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/167905: commit references a PR
Date: Thu, 12 Jul 2012 06:30:17 +0000 (UTC)

 Author: mm
 Date: Thu Jul 12 06:29:54 2012
 New Revision: 238391
 URL: http://svn.freebsd.org/changeset/base/238391
 
 Log:
   Change behavior introduced in r237119 to vendor solution
   
   References:
   https://www.illumos.org/issues/2883
   
   PR:		167905
   Obtained from:	illumos (issue #2883)
   MFC after:	2 weeks
 
 Modified:
   head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 
 Modified: head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c
 ==============================================================================
 --- head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Thu Jul 12 04:23:11 2012	(r238390)
 +++ head/cddl/contrib/opensolaris/lib/libzfs/common/libzfs_dataset.c	Thu Jul 12 06:29:54 2012	(r238391)
 @@ -1484,14 +1484,17 @@ zfs_prop_set(zfs_handle_t *zhp, const ch
  	}
  
  	/*
 -	 * If the dataset's canmount property is being set to noauto,
 -	 * or being set to on and the dataset is already mounted,
 -	 * then we want to prevent unmounting & remounting it.
 -	 */
 -	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
 -	    (zprop_string_to_index(prop, propval, &idx,
 -	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO ||
 -	    (idx == ZFS_CANMOUNT_ON && zfs_is_mounted(zhp, NULL))));
 +	 * 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) {
 +		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;
 +	}
  
  	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
  		goto error;
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
>Unformatted:
