1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21f3861e1aSahl 
22fa9e4066Sahrens /*
232e5e9e19SSanjeev Bagewadi  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24fa9e4066Sahrens  * Use is subject to license terms.
25fa9e4066Sahrens  */
26fa9e4066Sahrens 
27fa9e4066Sahrens #include <assert.h>
28fa9e4066Sahrens #include <ctype.h>
29fa9e4066Sahrens #include <errno.h>
30fa9e4066Sahrens #include <libdevinfo.h>
31fa9e4066Sahrens #include <libintl.h>
32fa9e4066Sahrens #include <math.h>
33fa9e4066Sahrens #include <stdio.h>
34fa9e4066Sahrens #include <stdlib.h>
35fa9e4066Sahrens #include <strings.h>
36fa9e4066Sahrens #include <unistd.h>
373cb34c60Sahrens #include <stddef.h>
38fa9e4066Sahrens #include <zone.h>
3999653d4eSeschrock #include <fcntl.h>
40fa9e4066Sahrens #include <sys/mntent.h>
41b12a1c38Slling #include <sys/mount.h>
42ecd6cf80Smarks #include <sys/avl.h>
43ecd6cf80Smarks #include <priv.h>
44ecd6cf80Smarks #include <pwd.h>
45ecd6cf80Smarks #include <grp.h>
46ecd6cf80Smarks #include <stddef.h>
47ecd6cf80Smarks #include <ucred.h>
48*14843421SMatthew Ahrens #include <idmap.h>
49*14843421SMatthew Ahrens #include <aclutils.h>
50fa9e4066Sahrens 
51fa9e4066Sahrens #include <sys/spa.h>
52e9dbad6fSeschrock #include <sys/zap.h>
53fa9e4066Sahrens #include <libzfs.h>
54fa9e4066Sahrens 
55fa9e4066Sahrens #include "zfs_namecheck.h"
56fa9e4066Sahrens #include "zfs_prop.h"
57fa9e4066Sahrens #include "libzfs_impl.h"
58ecd6cf80Smarks #include "zfs_deleg.h"
59fa9e4066Sahrens 
60cdf5b4caSmmusante static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
61*14843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned,
62*14843421SMatthew Ahrens     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
63cdf5b4caSmmusante 
64fa9e4066Sahrens /*
65fa9e4066Sahrens  * Given a single type (not a mask of types), return the type in a human
66fa9e4066Sahrens  * readable form.
67fa9e4066Sahrens  */
68fa9e4066Sahrens const char *
69fa9e4066Sahrens zfs_type_to_name(zfs_type_t type)
70fa9e4066Sahrens {
71fa9e4066Sahrens 	switch (type) {
72fa9e4066Sahrens 	case ZFS_TYPE_FILESYSTEM:
73fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
74fa9e4066Sahrens 	case ZFS_TYPE_SNAPSHOT:
75fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
76fa9e4066Sahrens 	case ZFS_TYPE_VOLUME:
77fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
78fa9e4066Sahrens 	}
79fa9e4066Sahrens 
80fa9e4066Sahrens 	return (NULL);
81fa9e4066Sahrens }
82fa9e4066Sahrens 
83fa9e4066Sahrens /*
84fa9e4066Sahrens  * Given a path and mask of ZFS types, return a string describing this dataset.
85fa9e4066Sahrens  * This is used when we fail to open a dataset and we cannot get an exact type.
86fa9e4066Sahrens  * We guess what the type would have been based on the path and the mask of
87fa9e4066Sahrens  * acceptable types.
88fa9e4066Sahrens  */
89fa9e4066Sahrens static const char *
90fa9e4066Sahrens path_to_str(const char *path, int types)
91fa9e4066Sahrens {
92fa9e4066Sahrens 	/*
93fa9e4066Sahrens 	 * When given a single type, always report the exact type.
94fa9e4066Sahrens 	 */
95fa9e4066Sahrens 	if (types == ZFS_TYPE_SNAPSHOT)
96fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
97fa9e4066Sahrens 	if (types == ZFS_TYPE_FILESYSTEM)
98fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
99fa9e4066Sahrens 	if (types == ZFS_TYPE_VOLUME)
100fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
101fa9e4066Sahrens 
102fa9e4066Sahrens 	/*
103fa9e4066Sahrens 	 * The user is requesting more than one type of dataset.  If this is the
104fa9e4066Sahrens 	 * case, consult the path itself.  If we're looking for a snapshot, and
105fa9e4066Sahrens 	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
106fa9e4066Sahrens 	 * snapshot attribute and try again.
107fa9e4066Sahrens 	 */
108fa9e4066Sahrens 	if (types & ZFS_TYPE_SNAPSHOT) {
109fa9e4066Sahrens 		if (strchr(path, '@') != NULL)
110fa9e4066Sahrens 			return (dgettext(TEXT_DOMAIN, "snapshot"));
111fa9e4066Sahrens 		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
112fa9e4066Sahrens 	}
113fa9e4066Sahrens 
114fa9e4066Sahrens 	/*
115fa9e4066Sahrens 	 * The user has requested either filesystems or volumes.
116fa9e4066Sahrens 	 * We have no way of knowing a priori what type this would be, so always
117fa9e4066Sahrens 	 * report it as "filesystem" or "volume", our two primitive types.
118fa9e4066Sahrens 	 */
119fa9e4066Sahrens 	if (types & ZFS_TYPE_FILESYSTEM)
120fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
121fa9e4066Sahrens 
122fa9e4066Sahrens 	assert(types & ZFS_TYPE_VOLUME);
123fa9e4066Sahrens 	return (dgettext(TEXT_DOMAIN, "volume"));
124fa9e4066Sahrens }
125fa9e4066Sahrens 
126fa9e4066Sahrens /*
127fa9e4066Sahrens  * Validate a ZFS path.  This is used even before trying to open the dataset, to
128*14843421SMatthew Ahrens  * provide a more meaningful error message.  We call zfs_error_aux() to
129*14843421SMatthew Ahrens  * explain exactly why the name was not valid.
130fa9e4066Sahrens  */
131fa9e4066Sahrens static int
132f18faf3fSek zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
133f18faf3fSek     boolean_t modifying)
134fa9e4066Sahrens {
135fa9e4066Sahrens 	namecheck_err_t why;
136fa9e4066Sahrens 	char what;
137fa9e4066Sahrens 
138fa9e4066Sahrens 	if (dataset_namecheck(path, &why, &what) != 0) {
13999653d4eSeschrock 		if (hdl != NULL) {
140fa9e4066Sahrens 			switch (why) {
141b81d61a6Slling 			case NAME_ERR_TOOLONG:
14299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14399653d4eSeschrock 				    "name is too long"));
144b81d61a6Slling 				break;
145b81d61a6Slling 
146fa9e4066Sahrens 			case NAME_ERR_LEADING_SLASH:
14799653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14899653d4eSeschrock 				    "leading slash in name"));
149fa9e4066Sahrens 				break;
150fa9e4066Sahrens 
151fa9e4066Sahrens 			case NAME_ERR_EMPTY_COMPONENT:
15299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15399653d4eSeschrock 				    "empty component in name"));
154fa9e4066Sahrens 				break;
155fa9e4066Sahrens 
156fa9e4066Sahrens 			case NAME_ERR_TRAILING_SLASH:
15799653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15899653d4eSeschrock 				    "trailing slash in name"));
159fa9e4066Sahrens 				break;
160fa9e4066Sahrens 
161fa9e4066Sahrens 			case NAME_ERR_INVALCHAR:
16299653d4eSeschrock 				zfs_error_aux(hdl,
163fa9e4066Sahrens 				    dgettext(TEXT_DOMAIN, "invalid character "
16499653d4eSeschrock 				    "'%c' in name"), what);
165fa9e4066Sahrens 				break;
166fa9e4066Sahrens 
167fa9e4066Sahrens 			case NAME_ERR_MULTIPLE_AT:
16899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
16999653d4eSeschrock 				    "multiple '@' delimiters in name"));
170fa9e4066Sahrens 				break;
1715ad82045Snd 
1725ad82045Snd 			case NAME_ERR_NOLETTER:
1735ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1745ad82045Snd 				    "pool doesn't begin with a letter"));
1755ad82045Snd 				break;
1765ad82045Snd 
1775ad82045Snd 			case NAME_ERR_RESERVED:
1785ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1795ad82045Snd 				    "name is reserved"));
1805ad82045Snd 				break;
1815ad82045Snd 
1825ad82045Snd 			case NAME_ERR_DISKLIKE:
1835ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1845ad82045Snd 				    "reserved disk name"));
1855ad82045Snd 				break;
186fa9e4066Sahrens 			}
187fa9e4066Sahrens 		}
188fa9e4066Sahrens 
189fa9e4066Sahrens 		return (0);
190fa9e4066Sahrens 	}
191fa9e4066Sahrens 
192fa9e4066Sahrens 	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
19399653d4eSeschrock 		if (hdl != NULL)
19499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
19599653d4eSeschrock 			    "snapshot delimiter '@' in filesystem name"));
196fa9e4066Sahrens 		return (0);
197fa9e4066Sahrens 	}
198fa9e4066Sahrens 
1991d452cf5Sahrens 	if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
2001d452cf5Sahrens 		if (hdl != NULL)
2011d452cf5Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
202d7d4af51Smmusante 			    "missing '@' delimiter in snapshot name"));
2031d452cf5Sahrens 		return (0);
2041d452cf5Sahrens 	}
2051d452cf5Sahrens 
206f18faf3fSek 	if (modifying && strchr(path, '%') != NULL) {
207f18faf3fSek 		if (hdl != NULL)
208f18faf3fSek 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
209f18faf3fSek 			    "invalid character %c in name"), '%');
210f18faf3fSek 		return (0);
211f18faf3fSek 	}
212f18faf3fSek 
21399653d4eSeschrock 	return (-1);
214fa9e4066Sahrens }
215fa9e4066Sahrens 
216fa9e4066Sahrens int
217fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type)
218fa9e4066Sahrens {
219e7cbe64fSgw 	if (type == ZFS_TYPE_POOL)
220e7cbe64fSgw 		return (zpool_name_valid(NULL, B_FALSE, name));
221f18faf3fSek 	return (zfs_validate_name(NULL, name, type, B_FALSE));
222fa9e4066Sahrens }
223fa9e4066Sahrens 
224e9dbad6fSeschrock /*
225e9dbad6fSeschrock  * This function takes the raw DSL properties, and filters out the user-defined
226e9dbad6fSeschrock  * properties into a separate nvlist.
227e9dbad6fSeschrock  */
228fac3008cSeschrock static nvlist_t *
229fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props)
230e9dbad6fSeschrock {
231e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
232e9dbad6fSeschrock 	nvpair_t *elem;
233e9dbad6fSeschrock 	nvlist_t *propval;
234fac3008cSeschrock 	nvlist_t *nvl;
235e9dbad6fSeschrock 
236fac3008cSeschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
237fac3008cSeschrock 		(void) no_memory(hdl);
238fac3008cSeschrock 		return (NULL);
239fac3008cSeschrock 	}
240e9dbad6fSeschrock 
241e9dbad6fSeschrock 	elem = NULL;
242fac3008cSeschrock 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
243e9dbad6fSeschrock 		if (!zfs_prop_user(nvpair_name(elem)))
244e9dbad6fSeschrock 			continue;
245e9dbad6fSeschrock 
246e9dbad6fSeschrock 		verify(nvpair_value_nvlist(elem, &propval) == 0);
247fac3008cSeschrock 		if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
248fac3008cSeschrock 			nvlist_free(nvl);
249fac3008cSeschrock 			(void) no_memory(hdl);
250fac3008cSeschrock 			return (NULL);
251fac3008cSeschrock 		}
252e9dbad6fSeschrock 	}
253e9dbad6fSeschrock 
254fac3008cSeschrock 	return (nvl);
255e9dbad6fSeschrock }
256e9dbad6fSeschrock 
25729ab75c9Srm static zpool_handle_t *
25829ab75c9Srm zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
25929ab75c9Srm {
26029ab75c9Srm 	libzfs_handle_t *hdl = zhp->zfs_hdl;
26129ab75c9Srm 	zpool_handle_t *zph;
26229ab75c9Srm 
26329ab75c9Srm 	if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
26429ab75c9Srm 		if (hdl->libzfs_pool_handles != NULL)
26529ab75c9Srm 			zph->zpool_next = hdl->libzfs_pool_handles;
26629ab75c9Srm 		hdl->libzfs_pool_handles = zph;
26729ab75c9Srm 	}
26829ab75c9Srm 	return (zph);
26929ab75c9Srm }
27029ab75c9Srm 
27129ab75c9Srm static zpool_handle_t *
27229ab75c9Srm zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
27329ab75c9Srm {
27429ab75c9Srm 	libzfs_handle_t *hdl = zhp->zfs_hdl;
27529ab75c9Srm 	zpool_handle_t *zph = hdl->libzfs_pool_handles;
27629ab75c9Srm 
27729ab75c9Srm 	while ((zph != NULL) &&
27829ab75c9Srm 	    (strncmp(pool_name, zpool_get_name(zph), len) != 0))
27929ab75c9Srm 		zph = zph->zpool_next;
28029ab75c9Srm 	return (zph);
28129ab75c9Srm }
28229ab75c9Srm 
28329ab75c9Srm /*
28429ab75c9Srm  * Returns a handle to the pool that contains the provided dataset.
28529ab75c9Srm  * If a handle to that pool already exists then that handle is returned.
28629ab75c9Srm  * Otherwise, a new handle is created and added to the list of handles.
28729ab75c9Srm  */
28829ab75c9Srm static zpool_handle_t *
28929ab75c9Srm zpool_handle(zfs_handle_t *zhp)
29029ab75c9Srm {
29129ab75c9Srm 	char *pool_name;
29229ab75c9Srm 	int len;
29329ab75c9Srm 	zpool_handle_t *zph;
29429ab75c9Srm 
29529ab75c9Srm 	len = strcspn(zhp->zfs_name, "/@") + 1;
29629ab75c9Srm 	pool_name = zfs_alloc(zhp->zfs_hdl, len);
29729ab75c9Srm 	(void) strlcpy(pool_name, zhp->zfs_name, len);
29829ab75c9Srm 
29929ab75c9Srm 	zph = zpool_find_handle(zhp, pool_name, len);
30029ab75c9Srm 	if (zph == NULL)
30129ab75c9Srm 		zph = zpool_add_handle(zhp, pool_name);
30229ab75c9Srm 
30329ab75c9Srm 	free(pool_name);
30429ab75c9Srm 	return (zph);
30529ab75c9Srm }
30629ab75c9Srm 
30729ab75c9Srm void
30829ab75c9Srm zpool_free_handles(libzfs_handle_t *hdl)
30929ab75c9Srm {
31029ab75c9Srm 	zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
31129ab75c9Srm 
31229ab75c9Srm 	while (zph != NULL) {
31329ab75c9Srm 		next = zph->zpool_next;
31429ab75c9Srm 		zpool_close(zph);
31529ab75c9Srm 		zph = next;
31629ab75c9Srm 	}
31729ab75c9Srm 	hdl->libzfs_pool_handles = NULL;
31829ab75c9Srm }
31929ab75c9Srm 
320fa9e4066Sahrens /*
321fa9e4066Sahrens  * Utility function to gather stats (objset and zpl) for the given object.
322fa9e4066Sahrens  */
323fa9e4066Sahrens static int
324ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
325fa9e4066Sahrens {
326e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
327fa9e4066Sahrens 
328ebedde84SEric Taylor 	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
329fa9e4066Sahrens 
330ebedde84SEric Taylor 	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
3317f7322feSeschrock 		if (errno == ENOMEM) {
332ebedde84SEric Taylor 			if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
33399653d4eSeschrock 				return (-1);
334e9dbad6fSeschrock 			}
3357f7322feSeschrock 		} else {
3367f7322feSeschrock 			return (-1);
3377f7322feSeschrock 		}
3387f7322feSeschrock 	}
339ebedde84SEric Taylor 	return (0);
340ebedde84SEric Taylor }
341fa9e4066Sahrens 
342ebedde84SEric Taylor static int
343ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
344ebedde84SEric Taylor {
345ebedde84SEric Taylor 	nvlist_t *allprops, *userprops;
346fa9e4066Sahrens 
347ebedde84SEric Taylor 	zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
348ebedde84SEric Taylor 
349ebedde84SEric Taylor 	if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
35099653d4eSeschrock 		return (-1);
35199653d4eSeschrock 	}
352fa9e4066Sahrens 
353*14843421SMatthew Ahrens 	/*
354*14843421SMatthew Ahrens 	 * XXX Why do we store the user props separately, in addition to
355*14843421SMatthew Ahrens 	 * storing them in zfs_props?
356*14843421SMatthew Ahrens 	 */
357fac3008cSeschrock 	if ((userprops = process_user_props(zhp, allprops)) == NULL) {
358fac3008cSeschrock 		nvlist_free(allprops);
359e9dbad6fSeschrock 		return (-1);
360fac3008cSeschrock 	}
361fac3008cSeschrock 
362fac3008cSeschrock 	nvlist_free(zhp->zfs_props);
363fac3008cSeschrock 	nvlist_free(zhp->zfs_user_props);
364fac3008cSeschrock 
365fac3008cSeschrock 	zhp->zfs_props = allprops;
366fac3008cSeschrock 	zhp->zfs_user_props = userprops;
36799653d4eSeschrock 
368fa9e4066Sahrens 	return (0);
369fa9e4066Sahrens }
370fa9e4066Sahrens 
371ebedde84SEric Taylor static int
372ebedde84SEric Taylor get_stats(zfs_handle_t *zhp)
373ebedde84SEric Taylor {
374ebedde84SEric Taylor 	int rc = 0;
375ebedde84SEric Taylor 	zfs_cmd_t zc = { 0 };
376ebedde84SEric Taylor 
377ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
378ebedde84SEric Taylor 		return (-1);
379ebedde84SEric Taylor 	if (get_stats_ioctl(zhp, &zc) != 0)
380ebedde84SEric Taylor 		rc = -1;
381ebedde84SEric Taylor 	else if (put_stats_zhdl(zhp, &zc) != 0)
382ebedde84SEric Taylor 		rc = -1;
383ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
384ebedde84SEric Taylor 	return (rc);
385ebedde84SEric Taylor }
386ebedde84SEric Taylor 
387fa9e4066Sahrens /*
388fa9e4066Sahrens  * Refresh the properties currently stored in the handle.
389fa9e4066Sahrens  */
390fa9e4066Sahrens void
391fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp)
392fa9e4066Sahrens {
393fa9e4066Sahrens 	(void) get_stats(zhp);
394fa9e4066Sahrens }
395fa9e4066Sahrens 
396fa9e4066Sahrens /*
397fa9e4066Sahrens  * Makes a handle from the given dataset name.  Used by zfs_open() and
398fa9e4066Sahrens  * zfs_iter_* to create child handles on the fly.
399fa9e4066Sahrens  */
400ebedde84SEric Taylor static int
401ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
402fa9e4066Sahrens {
403ecd6cf80Smarks 	char *logstr;
404ebedde84SEric Taylor 	libzfs_handle_t *hdl = zhp->zfs_hdl;
405fa9e4066Sahrens 
406ecd6cf80Smarks 	/*
407ecd6cf80Smarks 	 * Preserve history log string.
408ecd6cf80Smarks 	 * any changes performed here will be
409ecd6cf80Smarks 	 * logged as an internal event.
410ecd6cf80Smarks 	 */
411ecd6cf80Smarks 	logstr = zhp->zfs_hdl->libzfs_log_str;
412ecd6cf80Smarks 	zhp->zfs_hdl->libzfs_log_str = NULL;
413fa9e4066Sahrens 
414ebedde84SEric Taylor top:
415ebedde84SEric Taylor 	if (put_stats_zhdl(zhp, zc) != 0) {
416ecd6cf80Smarks 		zhp->zfs_hdl->libzfs_log_str = logstr;
417ebedde84SEric Taylor 		return (-1);
418fa9e4066Sahrens 	}
419fa9e4066Sahrens 
420ebedde84SEric Taylor 
42131fd60d3Sahrens 	if (zhp->zfs_dmustats.dds_inconsistent) {
422ebedde84SEric Taylor 		zfs_cmd_t zc2 = { 0 };
42331fd60d3Sahrens 
42431fd60d3Sahrens 		/*
42531fd60d3Sahrens 		 * If it is dds_inconsistent, then we've caught it in
42631fd60d3Sahrens 		 * the middle of a 'zfs receive' or 'zfs destroy', and
42731fd60d3Sahrens 		 * it is inconsistent from the ZPL's point of view, so
42831fd60d3Sahrens 		 * can't be mounted.  However, it could also be that we
42931fd60d3Sahrens 		 * have crashed in the middle of one of those
43031fd60d3Sahrens 		 * operations, in which case we need to get rid of the
43131fd60d3Sahrens 		 * inconsistent state.  We do that by either rolling
43231fd60d3Sahrens 		 * back to the previous snapshot (which will fail if
43331fd60d3Sahrens 		 * there is none), or destroying the filesystem.  Note
43431fd60d3Sahrens 		 * that if we are still in the middle of an active
43531fd60d3Sahrens 		 * 'receive' or 'destroy', then the rollback and destroy
43631fd60d3Sahrens 		 * will fail with EBUSY and we will drive on as usual.
43731fd60d3Sahrens 		 */
43831fd60d3Sahrens 
439ebedde84SEric Taylor 		(void) strlcpy(zc2.zc_name, zhp->zfs_name,
440ebedde84SEric Taylor 		    sizeof (zc2.zc_name));
44131fd60d3Sahrens 
442a2eea2e1Sahrens 		if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) {
44399653d4eSeschrock 			(void) zvol_remove_link(hdl, zhp->zfs_name);
444ebedde84SEric Taylor 			zc2.zc_objset_type = DMU_OST_ZVOL;
44531fd60d3Sahrens 		} else {
446ebedde84SEric Taylor 			zc2.zc_objset_type = DMU_OST_ZFS;
44731fd60d3Sahrens 		}
44831fd60d3Sahrens 
44931fd60d3Sahrens 		/*
450da6c28aaSamw 		 * If we can successfully destroy it, pretend that it
45131fd60d3Sahrens 		 * never existed.
45231fd60d3Sahrens 		 */
453ebedde84SEric Taylor 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc2) == 0) {
454ecd6cf80Smarks 			zhp->zfs_hdl->libzfs_log_str = logstr;
45531fd60d3Sahrens 			errno = ENOENT;
456ebedde84SEric Taylor 			return (-1);
45731fd60d3Sahrens 		}
458ebedde84SEric Taylor 		/* If we can successfully roll it back, reset the stats */
459ebedde84SEric Taylor 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc2) == 0) {
460ebedde84SEric Taylor 			if (get_stats_ioctl(zhp, zc) != 0) {
461ebedde84SEric Taylor 				zhp->zfs_hdl->libzfs_log_str = logstr;
462ebedde84SEric Taylor 				return (-1);
463ebedde84SEric Taylor 			}
4643cb34c60Sahrens 			goto top;
465ebedde84SEric Taylor 		}
46631fd60d3Sahrens 	}
46731fd60d3Sahrens 
468fa9e4066Sahrens 	/*
469fa9e4066Sahrens 	 * We've managed to open the dataset and gather statistics.  Determine
470fa9e4066Sahrens 	 * the high-level type.
471fa9e4066Sahrens 	 */
472a2eea2e1Sahrens 	if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
473a2eea2e1Sahrens 		zhp->zfs_head_type = ZFS_TYPE_VOLUME;
474a2eea2e1Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
475a2eea2e1Sahrens 		zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
476a2eea2e1Sahrens 	else
477a2eea2e1Sahrens 		abort();
478a2eea2e1Sahrens 
479fa9e4066Sahrens 	if (zhp->zfs_dmustats.dds_is_snapshot)
480fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
481fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
482fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_VOLUME;
483fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
484fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
485fa9e4066Sahrens 	else
48699653d4eSeschrock 		abort();	/* we should never see any other types */
487fa9e4066Sahrens 
488ecd6cf80Smarks 	zhp->zfs_hdl->libzfs_log_str = logstr;
48929ab75c9Srm 	zhp->zpool_hdl = zpool_handle(zhp);
490ebedde84SEric Taylor 	return (0);
491ebedde84SEric Taylor }
492ebedde84SEric Taylor 
493ebedde84SEric Taylor zfs_handle_t *
494ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path)
495ebedde84SEric Taylor {
496ebedde84SEric Taylor 	zfs_cmd_t zc = { 0 };
497ebedde84SEric Taylor 
498ebedde84SEric Taylor 	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
499ebedde84SEric Taylor 
500ebedde84SEric Taylor 	if (zhp == NULL)
501ebedde84SEric Taylor 		return (NULL);
502ebedde84SEric Taylor 
503ebedde84SEric Taylor 	zhp->zfs_hdl = hdl;
504ebedde84SEric Taylor 	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
505ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
506ebedde84SEric Taylor 		free(zhp);
507ebedde84SEric Taylor 		return (NULL);
508ebedde84SEric Taylor 	}
509ebedde84SEric Taylor 	if (get_stats_ioctl(zhp, &zc) == -1) {
510ebedde84SEric Taylor 		zcmd_free_nvlists(&zc);
511ebedde84SEric Taylor 		free(zhp);
512ebedde84SEric Taylor 		return (NULL);
513ebedde84SEric Taylor 	}
514ebedde84SEric Taylor 	if (make_dataset_handle_common(zhp, &zc) == -1) {
515ebedde84SEric Taylor 		free(zhp);
516ebedde84SEric Taylor 		zhp = NULL;
517ebedde84SEric Taylor 	}
518ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
519ebedde84SEric Taylor 	return (zhp);
520ebedde84SEric Taylor }
521ebedde84SEric Taylor 
522ebedde84SEric Taylor static zfs_handle_t *
523ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
524ebedde84SEric Taylor {
525ebedde84SEric Taylor 	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
526ebedde84SEric Taylor 
527ebedde84SEric Taylor 	if (zhp == NULL)
528ebedde84SEric Taylor 		return (NULL);
529ebedde84SEric Taylor 
530ebedde84SEric Taylor 	zhp->zfs_hdl = hdl;
531ebedde84SEric Taylor 	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
532ebedde84SEric Taylor 	if (make_dataset_handle_common(zhp, zc) == -1) {
533ebedde84SEric Taylor 		free(zhp);
534ebedde84SEric Taylor 		return (NULL);
535ebedde84SEric Taylor 	}
536fa9e4066Sahrens 	return (zhp);
537fa9e4066Sahrens }
538fa9e4066Sahrens 
539fa9e4066Sahrens /*
540fa9e4066Sahrens  * Opens the given snapshot, filesystem, or volume.   The 'types'
541fa9e4066Sahrens  * argument is a mask of acceptable types.  The function will print an
542fa9e4066Sahrens  * appropriate error message and return NULL if it can't be opened.
543fa9e4066Sahrens  */
544fa9e4066Sahrens zfs_handle_t *
54599653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types)
546fa9e4066Sahrens {
547fa9e4066Sahrens 	zfs_handle_t *zhp;
54899653d4eSeschrock 	char errbuf[1024];
54999653d4eSeschrock 
55099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
55199653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
552fa9e4066Sahrens 
553fa9e4066Sahrens 	/*
55499653d4eSeschrock 	 * Validate the name before we even try to open it.
555fa9e4066Sahrens 	 */
556f18faf3fSek 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
55799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
55899653d4eSeschrock 		    "invalid dataset name"));
55999653d4eSeschrock 		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
560fa9e4066Sahrens 		return (NULL);
561fa9e4066Sahrens 	}
562fa9e4066Sahrens 
563fa9e4066Sahrens 	/*
564fa9e4066Sahrens 	 * Try to get stats for the dataset, which will tell us if it exists.
565fa9e4066Sahrens 	 */
566fa9e4066Sahrens 	errno = 0;
56799653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
568ece3d9b3Slling 		(void) zfs_standard_error(hdl, errno, errbuf);
569fa9e4066Sahrens 		return (NULL);
570fa9e4066Sahrens 	}
571fa9e4066Sahrens 
572fa9e4066Sahrens 	if (!(types & zhp->zfs_type)) {
57399653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
57494de1d4cSeschrock 		zfs_close(zhp);
575fa9e4066Sahrens 		return (NULL);
576fa9e4066Sahrens 	}
577fa9e4066Sahrens 
578fa9e4066Sahrens 	return (zhp);
579fa9e4066Sahrens }
580fa9e4066Sahrens 
581fa9e4066Sahrens /*
582fa9e4066Sahrens  * Release a ZFS handle.  Nothing to do but free the associated memory.
583fa9e4066Sahrens  */
584fa9e4066Sahrens void
585fa9e4066Sahrens zfs_close(zfs_handle_t *zhp)
586fa9e4066Sahrens {
587fa9e4066Sahrens 	if (zhp->zfs_mntopts)
588fa9e4066Sahrens 		free(zhp->zfs_mntopts);
589e9dbad6fSeschrock 	nvlist_free(zhp->zfs_props);
590e9dbad6fSeschrock 	nvlist_free(zhp->zfs_user_props);
591fa9e4066Sahrens 	free(zhp);
592fa9e4066Sahrens }
593fa9e4066Sahrens 
594ebedde84SEric Taylor typedef struct mnttab_node {
595ebedde84SEric Taylor 	struct mnttab mtn_mt;
596ebedde84SEric Taylor 	avl_node_t mtn_node;
597ebedde84SEric Taylor } mnttab_node_t;
598ebedde84SEric Taylor 
599ebedde84SEric Taylor static int
600ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
601ebedde84SEric Taylor {
602ebedde84SEric Taylor 	const mnttab_node_t *mtn1 = arg1;
603ebedde84SEric Taylor 	const mnttab_node_t *mtn2 = arg2;
604ebedde84SEric Taylor 	int rv;
605ebedde84SEric Taylor 
606ebedde84SEric Taylor 	rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
607ebedde84SEric Taylor 
608ebedde84SEric Taylor 	if (rv == 0)
609ebedde84SEric Taylor 		return (0);
610ebedde84SEric Taylor 	return (rv > 0 ? 1 : -1);
611ebedde84SEric Taylor }
612ebedde84SEric Taylor 
613ebedde84SEric Taylor void
614ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl)
615ebedde84SEric Taylor {
616ebedde84SEric Taylor 	assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
617ebedde84SEric Taylor 	avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
618ebedde84SEric Taylor 	    sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
619b2634b9cSEric Taylor }
620b2634b9cSEric Taylor 
621b2634b9cSEric Taylor void
622b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl)
623b2634b9cSEric Taylor {
624b2634b9cSEric Taylor 	struct mnttab entry;
625ebedde84SEric Taylor 
626ebedde84SEric Taylor 	rewind(hdl->libzfs_mnttab);
627ebedde84SEric Taylor 	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
628ebedde84SEric Taylor 		mnttab_node_t *mtn;
629ebedde84SEric Taylor 
630ebedde84SEric Taylor 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
631ebedde84SEric Taylor 			continue;
632ebedde84SEric Taylor 		mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
633ebedde84SEric Taylor 		mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
634ebedde84SEric Taylor 		mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
635ebedde84SEric Taylor 		mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
636ebedde84SEric Taylor 		mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
637ebedde84SEric Taylor 		avl_add(&hdl->libzfs_mnttab_cache, mtn);
638ebedde84SEric Taylor 	}
639ebedde84SEric Taylor }
640ebedde84SEric Taylor 
641ebedde84SEric Taylor void
642ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl)
643ebedde84SEric Taylor {
644ebedde84SEric Taylor 	void *cookie = NULL;
645ebedde84SEric Taylor 	mnttab_node_t *mtn;
646ebedde84SEric Taylor 
647ebedde84SEric Taylor 	while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
648ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_special);
649ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_mountp);
650ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_fstype);
651ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_mntopts);
652ebedde84SEric Taylor 		free(mtn);
653ebedde84SEric Taylor 	}
654ebedde84SEric Taylor 	avl_destroy(&hdl->libzfs_mnttab_cache);
655ebedde84SEric Taylor }
656ebedde84SEric Taylor 
657b2634b9cSEric Taylor void
658b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
659b2634b9cSEric Taylor {
660b2634b9cSEric Taylor 	hdl->libzfs_mnttab_enable = enable;
661b2634b9cSEric Taylor }
662b2634b9cSEric Taylor 
663ebedde84SEric Taylor int
664ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
665ebedde84SEric Taylor     struct mnttab *entry)
666ebedde84SEric Taylor {
667ebedde84SEric Taylor 	mnttab_node_t find;
668ebedde84SEric Taylor 	mnttab_node_t *mtn;
669ebedde84SEric Taylor 
670b2634b9cSEric Taylor 	if (!hdl->libzfs_mnttab_enable) {
671b2634b9cSEric Taylor 		struct mnttab srch = { 0 };
672b2634b9cSEric Taylor 
673b2634b9cSEric Taylor 		if (avl_numnodes(&hdl->libzfs_mnttab_cache))
674b2634b9cSEric Taylor 			libzfs_mnttab_fini(hdl);
675b2634b9cSEric Taylor 		rewind(hdl->libzfs_mnttab);
676b2634b9cSEric Taylor 		srch.mnt_special = (char *)fsname;
677b2634b9cSEric Taylor 		srch.mnt_fstype = MNTTYPE_ZFS;
678b2634b9cSEric Taylor 		if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
679b2634b9cSEric Taylor 			return (0);
680b2634b9cSEric Taylor 		else
681b2634b9cSEric Taylor 			return (ENOENT);
682b2634b9cSEric Taylor 	}
683b2634b9cSEric Taylor 
684ebedde84SEric Taylor 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
685b2634b9cSEric Taylor 		libzfs_mnttab_update(hdl);
686ebedde84SEric Taylor 
687ebedde84SEric Taylor 	find.mtn_mt.mnt_special = (char *)fsname;
688ebedde84SEric Taylor 	mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
689ebedde84SEric Taylor 	if (mtn) {
690ebedde84SEric Taylor 		*entry = mtn->mtn_mt;
691ebedde84SEric Taylor 		return (0);
692ebedde84SEric Taylor 	}
693ebedde84SEric Taylor 	return (ENOENT);
694ebedde84SEric Taylor }
695ebedde84SEric Taylor 
696ebedde84SEric Taylor void
697ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
698ebedde84SEric Taylor     const char *mountp, const char *mntopts)
699ebedde84SEric Taylor {
700ebedde84SEric Taylor 	mnttab_node_t *mtn;
701ebedde84SEric Taylor 
702ebedde84SEric Taylor 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
703ebedde84SEric Taylor 		return;
704ebedde84SEric Taylor 	mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
705ebedde84SEric Taylor 	mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
706ebedde84SEric Taylor 	mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
707ebedde84SEric Taylor 	mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
708ebedde84SEric Taylor 	mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
709ebedde84SEric Taylor 	avl_add(&hdl->libzfs_mnttab_cache, mtn);
710ebedde84SEric Taylor }
711ebedde84SEric Taylor 
712ebedde84SEric Taylor void
713ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
714ebedde84SEric Taylor {
715ebedde84SEric Taylor 	mnttab_node_t find;
716ebedde84SEric Taylor 	mnttab_node_t *ret;
717ebedde84SEric Taylor 
718ebedde84SEric Taylor 	find.mtn_mt.mnt_special = (char *)fsname;
719ebedde84SEric Taylor 	if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
720ebedde84SEric Taylor 		avl_remove(&hdl->libzfs_mnttab_cache, ret);
721ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_special);
722ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_mountp);
723ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_fstype);
724ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_mntopts);
725ebedde84SEric Taylor 		free(ret);
726ebedde84SEric Taylor 	}
727ebedde84SEric Taylor }
728ebedde84SEric Taylor 
7297b97dc1aSrm int
7307b97dc1aSrm zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
7317b97dc1aSrm {
73229ab75c9Srm 	zpool_handle_t *zpool_handle = zhp->zpool_hdl;
7337b97dc1aSrm 
7347b97dc1aSrm 	if (zpool_handle == NULL)
7357b97dc1aSrm 		return (-1);
7367b97dc1aSrm 
7377b97dc1aSrm 	*spa_version = zpool_get_prop_int(zpool_handle,
7387b97dc1aSrm 	    ZPOOL_PROP_VERSION, NULL);
7397b97dc1aSrm 	return (0);
7407b97dc1aSrm }
7417b97dc1aSrm 
7427b97dc1aSrm /*
7437b97dc1aSrm  * The choice of reservation property depends on the SPA version.
7447b97dc1aSrm  */
7457b97dc1aSrm static int
7467b97dc1aSrm zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
7477b97dc1aSrm {
7487b97dc1aSrm 	int spa_version;
7497b97dc1aSrm 
7507b97dc1aSrm 	if (zfs_spa_version(zhp, &spa_version) < 0)
7517b97dc1aSrm 		return (-1);
7527b97dc1aSrm 
7537b97dc1aSrm 	if (spa_version >= SPA_VERSION_REFRESERVATION)
7547b97dc1aSrm 		*resv_prop = ZFS_PROP_REFRESERVATION;
7557b97dc1aSrm 	else
7567b97dc1aSrm 		*resv_prop = ZFS_PROP_RESERVATION;
7577b97dc1aSrm 
7587b97dc1aSrm 	return (0);
7597b97dc1aSrm }
7607b97dc1aSrm 
761e9dbad6fSeschrock /*
762e9dbad6fSeschrock  * Given an nvlist of properties to set, validates that they are correct, and
763e9dbad6fSeschrock  * parses any numeric properties (index, boolean, etc) if they are specified as
764e9dbad6fSeschrock  * strings.
765e9dbad6fSeschrock  */
7660a48a24eStimh nvlist_t *
7670a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
768990b4856Slling     uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
769e9dbad6fSeschrock {
770e9dbad6fSeschrock 	nvpair_t *elem;
771e9dbad6fSeschrock 	uint64_t intval;
772e9dbad6fSeschrock 	char *strval;
773990b4856Slling 	zfs_prop_t prop;
774e9dbad6fSeschrock 	nvlist_t *ret;
775da6c28aaSamw 	int chosen_normal = -1;
776da6c28aaSamw 	int chosen_utf = -1;
777e9dbad6fSeschrock 
778990b4856Slling 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
779990b4856Slling 		(void) no_memory(hdl);
780990b4856Slling 		return (NULL);
781e9dbad6fSeschrock 	}
782e9dbad6fSeschrock 
783*14843421SMatthew Ahrens 	/*
784*14843421SMatthew Ahrens 	 * Make sure this property is valid and applies to this type.
785*14843421SMatthew Ahrens 	 */
786*14843421SMatthew Ahrens 
787e9dbad6fSeschrock 	elem = NULL;
788e9dbad6fSeschrock 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
789990b4856Slling 		const char *propname = nvpair_name(elem);
790e9dbad6fSeschrock 
791*14843421SMatthew Ahrens 		prop = zfs_name_to_prop(propname);
792*14843421SMatthew Ahrens 		if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
793990b4856Slling 			/*
794*14843421SMatthew Ahrens 			 * This is a user property: make sure it's a
795990b4856Slling 			 * string, and that it's less than ZAP_MAXNAMELEN.
796990b4856Slling 			 */
797990b4856Slling 			if (nvpair_type(elem) != DATA_TYPE_STRING) {
798990b4856Slling 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
799990b4856Slling 				    "'%s' must be a string"), propname);
800990b4856Slling 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
801990b4856Slling 				goto error;
802990b4856Slling 			}
803990b4856Slling 
804990b4856Slling 			if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
805990b4856Slling 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
806990b4856Slling 				    "property name '%s' is too long"),
807990b4856Slling 				    propname);
808990b4856Slling 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
809990b4856Slling 				goto error;
810fa9e4066Sahrens 			}
811fa9e4066Sahrens 
812e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &strval);
813e9dbad6fSeschrock 			if (nvlist_add_string(ret, propname, strval) != 0) {
814e9dbad6fSeschrock 				(void) no_memory(hdl);
815e9dbad6fSeschrock 				goto error;
816e9dbad6fSeschrock 			}
817e9dbad6fSeschrock 			continue;
818e9dbad6fSeschrock 		}
819e9dbad6fSeschrock 
820*14843421SMatthew Ahrens 		/*
821*14843421SMatthew Ahrens 		 * Currently, only user properties can be modified on
822*14843421SMatthew Ahrens 		 * snapshots.
823*14843421SMatthew Ahrens 		 */
824bb0ade09Sahrens 		if (type == ZFS_TYPE_SNAPSHOT) {
825bb0ade09Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
826bb0ade09Sahrens 			    "this property can not be modified for snapshots"));
827bb0ade09Sahrens 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
828bb0ade09Sahrens 			goto error;
829bb0ade09Sahrens 		}
830bb0ade09Sahrens 
831*14843421SMatthew Ahrens 		if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
832*14843421SMatthew Ahrens 			zfs_userquota_prop_t uqtype;
833*14843421SMatthew Ahrens 			char newpropname[128];
834*14843421SMatthew Ahrens 			char domain[128];
835*14843421SMatthew Ahrens 			uint64_t rid;
836*14843421SMatthew Ahrens 			uint64_t valary[3];
837*14843421SMatthew Ahrens 
838*14843421SMatthew Ahrens 			if (userquota_propname_decode(propname, zoned,
839*14843421SMatthew Ahrens 			    &uqtype, domain, sizeof (domain), &rid) != 0) {
840*14843421SMatthew Ahrens 				zfs_error_aux(hdl,
841*14843421SMatthew Ahrens 				    dgettext(TEXT_DOMAIN,
842*14843421SMatthew Ahrens 				    "'%s' has an invalid user/group name"),
843*14843421SMatthew Ahrens 				    propname);
844*14843421SMatthew Ahrens 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
845*14843421SMatthew Ahrens 				goto error;
846*14843421SMatthew Ahrens 			}
847*14843421SMatthew Ahrens 
848*14843421SMatthew Ahrens 			if (uqtype != ZFS_PROP_USERQUOTA &&
849*14843421SMatthew Ahrens 			    uqtype != ZFS_PROP_GROUPQUOTA) {
850*14843421SMatthew Ahrens 				zfs_error_aux(hdl,
851*14843421SMatthew Ahrens 				    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
852*14843421SMatthew Ahrens 				    propname);
853*14843421SMatthew Ahrens 				(void) zfs_error(hdl, EZFS_PROPREADONLY,
854*14843421SMatthew Ahrens 				    errbuf);
855*14843421SMatthew Ahrens 				goto error;
856*14843421SMatthew Ahrens 			}
857*14843421SMatthew Ahrens 
858*14843421SMatthew Ahrens 			if (nvpair_type(elem) == DATA_TYPE_STRING) {
859*14843421SMatthew Ahrens 				(void) nvpair_value_string(elem, &strval);
860*14843421SMatthew Ahrens 				if (strcmp(strval, "none") == 0) {
861*14843421SMatthew Ahrens 					intval = 0;
862*14843421SMatthew Ahrens 				} else if (zfs_nicestrtonum(hdl,
863*14843421SMatthew Ahrens 				    strval, &intval) != 0) {
864*14843421SMatthew Ahrens 					(void) zfs_error(hdl,
865*14843421SMatthew Ahrens 					    EZFS_BADPROP, errbuf);
866*14843421SMatthew Ahrens 					goto error;
867*14843421SMatthew Ahrens 				}
868*14843421SMatthew Ahrens 			} else if (nvpair_type(elem) ==
869*14843421SMatthew Ahrens 			    DATA_TYPE_UINT64) {
870*14843421SMatthew Ahrens 				(void) nvpair_value_uint64(elem, &intval);
871*14843421SMatthew Ahrens 				if (intval == 0) {
872*14843421SMatthew Ahrens 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
873*14843421SMatthew Ahrens 					    "use 'none' to disable "
874*14843421SMatthew Ahrens 					    "userquota/groupquota"));
875*14843421SMatthew Ahrens 					goto error;
876*14843421SMatthew Ahrens 				}
877*14843421SMatthew Ahrens 			} else {
878*14843421SMatthew Ahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
879*14843421SMatthew Ahrens 				    "'%s' must be a number"), propname);
880*14843421SMatthew Ahrens 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
881*14843421SMatthew Ahrens 				goto error;
882*14843421SMatthew Ahrens 			}
883*14843421SMatthew Ahrens 
884*14843421SMatthew Ahrens 			(void) snprintf(newpropname, sizeof (newpropname),
885*14843421SMatthew Ahrens 			    "%s%s", zfs_userquota_prop_prefixes[uqtype],
886*14843421SMatthew Ahrens 			    domain);
887*14843421SMatthew Ahrens 			valary[0] = uqtype;
888*14843421SMatthew Ahrens 			valary[1] = rid;
889*14843421SMatthew Ahrens 			valary[2] = intval;
890*14843421SMatthew Ahrens 			if (nvlist_add_uint64_array(ret, newpropname,
891*14843421SMatthew Ahrens 			    valary, 3) != 0) {
892*14843421SMatthew Ahrens 				(void) no_memory(hdl);
893*14843421SMatthew Ahrens 				goto error;
894*14843421SMatthew Ahrens 			}
895*14843421SMatthew Ahrens 			continue;
896*14843421SMatthew Ahrens 		}
897*14843421SMatthew Ahrens 
898*14843421SMatthew Ahrens 		if (prop == ZPROP_INVAL) {
899*14843421SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
900*14843421SMatthew Ahrens 			    "invalid property '%s'"), propname);
901*14843421SMatthew Ahrens 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
902*14843421SMatthew Ahrens 			goto error;
903*14843421SMatthew Ahrens 		}
904*14843421SMatthew Ahrens 
905e9dbad6fSeschrock 		if (!zfs_prop_valid_for_type(prop, type)) {
906e9dbad6fSeschrock 			zfs_error_aux(hdl,
907e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' does not "
908e9dbad6fSeschrock 			    "apply to datasets of this type"), propname);
909e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
910e9dbad6fSeschrock 			goto error;
911e9dbad6fSeschrock 		}
912e9dbad6fSeschrock 
913e9dbad6fSeschrock 		if (zfs_prop_readonly(prop) &&
914da6c28aaSamw 		    (!zfs_prop_setonce(prop) || zhp != NULL)) {
915e9dbad6fSeschrock 			zfs_error_aux(hdl,
916e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
917e9dbad6fSeschrock 			    propname);
918e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
919e9dbad6fSeschrock 			goto error;
920e9dbad6fSeschrock 		}
921e9dbad6fSeschrock 
922990b4856Slling 		if (zprop_parse_value(hdl, elem, prop, type, ret,
923990b4856Slling 		    &strval, &intval, errbuf) != 0)
924e9dbad6fSeschrock 			goto error;
925fa9e4066Sahrens 
926e9dbad6fSeschrock 		/*
927e9dbad6fSeschrock 		 * Perform some additional checks for specific properties.
928e9dbad6fSeschrock 		 */
929e9dbad6fSeschrock 		switch (prop) {
930e7437265Sahrens 		case ZFS_PROP_VERSION:
931e7437265Sahrens 		{
932e7437265Sahrens 			int version;
933e7437265Sahrens 
934e7437265Sahrens 			if (zhp == NULL)
935e7437265Sahrens 				break;
936e7437265Sahrens 			version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
937e7437265Sahrens 			if (intval < version) {
938e7437265Sahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
939e7437265Sahrens 				    "Can not downgrade; already at version %u"),
940e7437265Sahrens 				    version);
941e7437265Sahrens 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
942e7437265Sahrens 				goto error;
943e7437265Sahrens 			}
944e7437265Sahrens 			break;
945e7437265Sahrens 		}
946e7437265Sahrens 
947e9dbad6fSeschrock 		case ZFS_PROP_RECORDSIZE:
948e9dbad6fSeschrock 		case ZFS_PROP_VOLBLOCKSIZE:
949e9dbad6fSeschrock 			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
950e9dbad6fSeschrock 			if (intval < SPA_MINBLOCKSIZE ||
951e9dbad6fSeschrock 			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
95299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
953e9dbad6fSeschrock 				    "'%s' must be power of 2 from %u "
954e9dbad6fSeschrock 				    "to %uk"), propname,
955e9dbad6fSeschrock 				    (uint_t)SPA_MINBLOCKSIZE,
956e9dbad6fSeschrock 				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
957e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
958e9dbad6fSeschrock 				goto error;
959fa9e4066Sahrens 			}
960fa9e4066Sahrens 			break;
961fa9e4066Sahrens 
962f3861e1aSahl 		case ZFS_PROP_SHAREISCSI:
963f3861e1aSahl 			if (strcmp(strval, "off") != 0 &&
964f3861e1aSahl 			    strcmp(strval, "on") != 0 &&
965f3861e1aSahl 			    strcmp(strval, "type=disk") != 0) {
966f3861e1aSahl 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
967f3861e1aSahl 				    "'%s' must be 'on', 'off', or 'type=disk'"),
968f3861e1aSahl 				    propname);
969f3861e1aSahl 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
970f3861e1aSahl 				goto error;
971f3861e1aSahl 			}
972f3861e1aSahl 
973f3861e1aSahl 			break;
974f3861e1aSahl 
975e9dbad6fSeschrock 		case ZFS_PROP_MOUNTPOINT:
97689eef05eSrm 		{
97789eef05eSrm 			namecheck_err_t why;
97889eef05eSrm 
979e9dbad6fSeschrock 			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
980e9dbad6fSeschrock 			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
981e9dbad6fSeschrock 				break;
982fa9e4066Sahrens 
98389eef05eSrm 			if (mountpoint_namecheck(strval, &why)) {
98489eef05eSrm 				switch (why) {
98589eef05eSrm 				case NAME_ERR_LEADING_SLASH:
98689eef05eSrm 					zfs_error_aux(hdl,
98789eef05eSrm 					    dgettext(TEXT_DOMAIN,
98889eef05eSrm 					    "'%s' must be an absolute path, "
98989eef05eSrm 					    "'none', or 'legacy'"), propname);
99089eef05eSrm 					break;
99189eef05eSrm 				case NAME_ERR_TOOLONG:
99289eef05eSrm 					zfs_error_aux(hdl,
99389eef05eSrm 					    dgettext(TEXT_DOMAIN,
99489eef05eSrm 					    "component of '%s' is too long"),
99589eef05eSrm 					    propname);
99689eef05eSrm 					break;
99789eef05eSrm 				}
998e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
999e9dbad6fSeschrock 				goto error;
1000fa9e4066Sahrens 			}
100189eef05eSrm 		}
100289eef05eSrm 
1003f3861e1aSahl 			/*FALLTHRU*/
1004fa9e4066Sahrens 
1005da6c28aaSamw 		case ZFS_PROP_SHARESMB:
1006f3861e1aSahl 		case ZFS_PROP_SHARENFS:
1007f3861e1aSahl 			/*
1008da6c28aaSamw 			 * For the mountpoint and sharenfs or sharesmb
1009da6c28aaSamw 			 * properties, check if it can be set in a
1010da6c28aaSamw 			 * global/non-global zone based on
1011f3861e1aSahl 			 * the zoned property value:
1012f3861e1aSahl 			 *
1013f3861e1aSahl 			 *		global zone	    non-global zone
1014f3861e1aSahl 			 * --------------------------------------------------
1015f3861e1aSahl 			 * zoned=on	mountpoint (no)	    mountpoint (yes)
1016f3861e1aSahl 			 *		sharenfs (no)	    sharenfs (no)
1017da6c28aaSamw 			 *		sharesmb (no)	    sharesmb (no)
1018f3861e1aSahl 			 *
1019f3861e1aSahl 			 * zoned=off	mountpoint (yes)	N/A
1020f3861e1aSahl 			 *		sharenfs (yes)
1021da6c28aaSamw 			 *		sharesmb (yes)
1022f3861e1aSahl 			 */
1023e9dbad6fSeschrock 			if (zoned) {
1024e9dbad6fSeschrock 				if (getzoneid() == GLOBAL_ZONEID) {
1025e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1026e9dbad6fSeschrock 					    "'%s' cannot be set on "
1027e9dbad6fSeschrock 					    "dataset in a non-global zone"),
1028e9dbad6fSeschrock 					    propname);
1029e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
1030e9dbad6fSeschrock 					    errbuf);
1031e9dbad6fSeschrock 					goto error;
1032da6c28aaSamw 				} else if (prop == ZFS_PROP_SHARENFS ||
1033da6c28aaSamw 				    prop == ZFS_PROP_SHARESMB) {
1034e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1035e9dbad6fSeschrock 					    "'%s' cannot be set in "
1036e9dbad6fSeschrock 					    "a non-global zone"), propname);
1037e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
1038e9dbad6fSeschrock 					    errbuf);
1039e9dbad6fSeschrock 					goto error;
1040fa9e4066Sahrens 				}
1041e9dbad6fSeschrock 			} else if (getzoneid() != GLOBAL_ZONEID) {
1042e9dbad6fSeschrock 				/*
1043e9dbad6fSeschrock 				 * If zoned property is 'off', this must be in
1044*14843421SMatthew Ahrens 				 * a global zone. If not, something is wrong.
1045e9dbad6fSeschrock 				 */
1046e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1047e9dbad6fSeschrock 				    "'%s' cannot be set while dataset "
1048e9dbad6fSeschrock 				    "'zoned' property is set"), propname);
1049e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
1050e9dbad6fSeschrock 				goto error;
1051fa9e4066Sahrens 			}
1052f3861e1aSahl 
105367331909Sdougm 			/*
105467331909Sdougm 			 * At this point, it is legitimate to set the
105567331909Sdougm 			 * property. Now we want to make sure that the
105667331909Sdougm 			 * property value is valid if it is sharenfs.
105767331909Sdougm 			 */
1058da6c28aaSamw 			if ((prop == ZFS_PROP_SHARENFS ||
1059da6c28aaSamw 			    prop == ZFS_PROP_SHARESMB) &&
1060fac3008cSeschrock 			    strcmp(strval, "on") != 0 &&
1061fac3008cSeschrock 			    strcmp(strval, "off") != 0) {
1062da6c28aaSamw 				zfs_share_proto_t proto;
1063da6c28aaSamw 
1064da6c28aaSamw 				if (prop == ZFS_PROP_SHARESMB)
1065da6c28aaSamw 					proto = PROTO_SMB;
1066da6c28aaSamw 				else
1067da6c28aaSamw 					proto = PROTO_NFS;
106867331909Sdougm 
106967331909Sdougm 				/*
1070da6c28aaSamw 				 * Must be an valid sharing protocol
1071da6c28aaSamw 				 * option string so init the libshare
1072da6c28aaSamw 				 * in order to enable the parser and
1073da6c28aaSamw 				 * then parse the options. We use the
1074da6c28aaSamw 				 * control API since we don't care about
1075da6c28aaSamw 				 * the current configuration and don't
107667331909Sdougm 				 * want the overhead of loading it
107767331909Sdougm 				 * until we actually do something.
107867331909Sdougm 				 */
107967331909Sdougm 
1080fac3008cSeschrock 				if (zfs_init_libshare(hdl,
1081fac3008cSeschrock 				    SA_INIT_CONTROL_API) != SA_OK) {
1082fac3008cSeschrock 					/*
1083fac3008cSeschrock 					 * An error occurred so we can't do
1084fac3008cSeschrock 					 * anything
1085fac3008cSeschrock 					 */
1086fac3008cSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1087fac3008cSeschrock 					    "'%s' cannot be set: problem "
1088fac3008cSeschrock 					    "in share initialization"),
1089fac3008cSeschrock 					    propname);
1090fac3008cSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1091fac3008cSeschrock 					    errbuf);
1092fac3008cSeschrock 					goto error;
1093fac3008cSeschrock 				}
109467331909Sdougm 
1095da6c28aaSamw 				if (zfs_parse_options(strval, proto) != SA_OK) {
1096fac3008cSeschrock 					/*
1097fac3008cSeschrock 					 * There was an error in parsing so
1098fac3008cSeschrock 					 * deal with it by issuing an error
1099fac3008cSeschrock 					 * message and leaving after
1100fac3008cSeschrock 					 * uninitializing the the libshare
1101fac3008cSeschrock 					 * interface.
1102fac3008cSeschrock 					 */
1103fac3008cSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1104fac3008cSeschrock 					    "'%s' cannot be set to invalid "
1105fac3008cSeschrock 					    "options"), propname);
1106fac3008cSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1107fac3008cSeschrock 					    errbuf);
1108fac3008cSeschrock 					zfs_uninit_libshare(hdl);
1109fac3008cSeschrock 					goto error;
1110fac3008cSeschrock 				}
111167331909Sdougm 				zfs_uninit_libshare(hdl);
111267331909Sdougm 			}
111367331909Sdougm 
1114da6c28aaSamw 			break;
1115da6c28aaSamw 		case ZFS_PROP_UTF8ONLY:
1116da6c28aaSamw 			chosen_utf = (int)intval;
1117da6c28aaSamw 			break;
1118da6c28aaSamw 		case ZFS_PROP_NORMALIZE:
1119da6c28aaSamw 			chosen_normal = (int)intval;
1120f3861e1aSahl 			break;
1121e9dbad6fSeschrock 		}
1122fa9e4066Sahrens 
1123e9dbad6fSeschrock 		/*
1124e9dbad6fSeschrock 		 * For changes to existing volumes, we have some additional
1125e9dbad6fSeschrock 		 * checks to enforce.
1126e9dbad6fSeschrock 		 */
1127e9dbad6fSeschrock 		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1128e9dbad6fSeschrock 			uint64_t volsize = zfs_prop_get_int(zhp,
1129e9dbad6fSeschrock 			    ZFS_PROP_VOLSIZE);
1130e9dbad6fSeschrock 			uint64_t blocksize = zfs_prop_get_int(zhp,
1131e9dbad6fSeschrock 			    ZFS_PROP_VOLBLOCKSIZE);
1132e9dbad6fSeschrock 			char buf[64];
1133e9dbad6fSeschrock 
1134e9dbad6fSeschrock 			switch (prop) {
1135e9dbad6fSeschrock 			case ZFS_PROP_RESERVATION:
1136a9799022Sck 			case ZFS_PROP_REFRESERVATION:
1137e9dbad6fSeschrock 				if (intval > volsize) {
1138e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1139e9dbad6fSeschrock 					    "'%s' is greater than current "
1140e9dbad6fSeschrock 					    "volume size"), propname);
1141e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1142e9dbad6fSeschrock 					    errbuf);
1143e9dbad6fSeschrock 					goto error;
1144e9dbad6fSeschrock 				}
1145e9dbad6fSeschrock 				break;
1146e9dbad6fSeschrock 
1147e9dbad6fSeschrock 			case ZFS_PROP_VOLSIZE:
1148e9dbad6fSeschrock 				if (intval % blocksize != 0) {
1149e9dbad6fSeschrock 					zfs_nicenum(blocksize, buf,
1150e9dbad6fSeschrock 					    sizeof (buf));
1151e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1152e9dbad6fSeschrock 					    "'%s' must be a multiple of "
1153e9dbad6fSeschrock 					    "volume block size (%s)"),
1154e9dbad6fSeschrock 					    propname, buf);
1155e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1156e9dbad6fSeschrock 					    errbuf);
1157e9dbad6fSeschrock 					goto error;
1158e9dbad6fSeschrock 				}
1159e9dbad6fSeschrock 
1160e9dbad6fSeschrock 				if (intval == 0) {
1161e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1162e9dbad6fSeschrock 					    "'%s' cannot be zero"),
1163e9dbad6fSeschrock 					    propname);
1164e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1165e9dbad6fSeschrock 					    errbuf);
1166e9dbad6fSeschrock 					goto error;
1167e9dbad6fSeschrock 				}
1168f3861e1aSahl 				break;
1169fa9e4066Sahrens 			}
1170e9dbad6fSeschrock 		}
1171e9dbad6fSeschrock 	}
1172fa9e4066Sahrens 
1173da6c28aaSamw 	/*
1174da6c28aaSamw 	 * If normalization was chosen, but no UTF8 choice was made,
1175da6c28aaSamw 	 * enforce rejection of non-UTF8 names.
1176da6c28aaSamw 	 *
1177da6c28aaSamw 	 * If normalization was chosen, but rejecting non-UTF8 names
1178da6c28aaSamw 	 * was explicitly not chosen, it is an error.
1179da6c28aaSamw 	 */
1180de8267e0Stimh 	if (chosen_normal > 0 && chosen_utf < 0) {
1181da6c28aaSamw 		if (nvlist_add_uint64(ret,
1182da6c28aaSamw 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
1183da6c28aaSamw 			(void) no_memory(hdl);
1184da6c28aaSamw 			goto error;
1185da6c28aaSamw 		}
1186de8267e0Stimh 	} else if (chosen_normal > 0 && chosen_utf == 0) {
1187da6c28aaSamw 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1188da6c28aaSamw 		    "'%s' must be set 'on' if normalization chosen"),
1189da6c28aaSamw 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1190da6c28aaSamw 		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1191da6c28aaSamw 		goto error;
1192da6c28aaSamw 	}
1193da6c28aaSamw 
1194e9dbad6fSeschrock 	/*
1195e9dbad6fSeschrock 	 * If this is an existing volume, and someone is setting the volsize,
1196e9dbad6fSeschrock 	 * make sure that it matches the reservation, or add it if necessary.
1197e9dbad6fSeschrock 	 */
1198e9dbad6fSeschrock 	if (zhp != NULL && type == ZFS_TYPE_VOLUME &&
1199e9dbad6fSeschrock 	    nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1200e9dbad6fSeschrock 	    &intval) == 0) {
1201e9dbad6fSeschrock 		uint64_t old_volsize = zfs_prop_get_int(zhp,
1202e9dbad6fSeschrock 		    ZFS_PROP_VOLSIZE);
1203a9b821a0Sck 		uint64_t old_reservation;
1204e9dbad6fSeschrock 		uint64_t new_reservation;
1205a9b821a0Sck 		zfs_prop_t resv_prop;
1206a9b821a0Sck 
12077b97dc1aSrm 		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1208a9b821a0Sck 			goto error;
1209a9b821a0Sck 		old_reservation = zfs_prop_get_int(zhp, resv_prop);
1210e9dbad6fSeschrock 
1211e9dbad6fSeschrock 		if (old_volsize == old_reservation &&
1212a9b821a0Sck 		    nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop),
1213e9dbad6fSeschrock 		    &new_reservation) != 0) {
1214e9dbad6fSeschrock 			if (nvlist_add_uint64(ret,
1215a9b821a0Sck 			    zfs_prop_to_name(resv_prop), intval) != 0) {
1216e9dbad6fSeschrock 				(void) no_memory(hdl);
1217e9dbad6fSeschrock 				goto error;
1218e9dbad6fSeschrock 			}
1219fa9e4066Sahrens 		}
1220fa9e4066Sahrens 	}
1221e9dbad6fSeschrock 	return (ret);
1222fa9e4066Sahrens 
1223e9dbad6fSeschrock error:
1224e9dbad6fSeschrock 	nvlist_free(ret);
1225e9dbad6fSeschrock 	return (NULL);
1226fa9e4066Sahrens }
1227fa9e4066Sahrens 
1228fa9e4066Sahrens /*
1229fa9e4066Sahrens  * Given a property name and value, set the property for the given dataset.
1230fa9e4066Sahrens  */
1231fa9e4066Sahrens int
1232e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1233fa9e4066Sahrens {
1234fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1235e9dbad6fSeschrock 	int ret = -1;
1236e9dbad6fSeschrock 	prop_changelist_t *cl = NULL;
123799653d4eSeschrock 	char errbuf[1024];
123899653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
1239e9dbad6fSeschrock 	nvlist_t *nvl = NULL, *realprops;
1240e9dbad6fSeschrock 	zfs_prop_t prop;
12410068372bSMark J Musante 	boolean_t do_prefix;
12420068372bSMark J Musante 	uint64_t idx;
124399653d4eSeschrock 
124499653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
1245e9dbad6fSeschrock 	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
124699653d4eSeschrock 	    zhp->zfs_name);
124799653d4eSeschrock 
1248e9dbad6fSeschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1249e9dbad6fSeschrock 	    nvlist_add_string(nvl, propname, propval) != 0) {
1250e9dbad6fSeschrock 		(void) no_memory(hdl);
1251e9dbad6fSeschrock 		goto error;
1252fa9e4066Sahrens 	}
1253fa9e4066Sahrens 
12540a48a24eStimh 	if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
1255e9dbad6fSeschrock 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1256e9dbad6fSeschrock 		goto error;
1257990b4856Slling 
1258e9dbad6fSeschrock 	nvlist_free(nvl);
1259e9dbad6fSeschrock 	nvl = realprops;
1260e9dbad6fSeschrock 
1261e9dbad6fSeschrock 	prop = zfs_name_to_prop(propname);
1262e9dbad6fSeschrock 
12630069fd67STim Haley 	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1264e9dbad6fSeschrock 		goto error;
1265fa9e4066Sahrens 
1266fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
126799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
126899653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
126999653d4eSeschrock 		    "in a non-global zone"));
127099653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1271fa9e4066Sahrens 		goto error;
1272fa9e4066Sahrens 	}
1273fa9e4066Sahrens 
12740068372bSMark J Musante 	/*
12750068372bSMark J Musante 	 * If the dataset's canmount property is being set to noauto,
12760068372bSMark J Musante 	 * then we want to prevent unmounting & remounting it.
12770068372bSMark J Musante 	 */
12780068372bSMark J Musante 	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
12790068372bSMark J Musante 	    (zprop_string_to_index(prop, propval, &idx,
12800068372bSMark J Musante 	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
1281a227b7f4Shs 
1282a227b7f4Shs 	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
12830068372bSMark J Musante 		goto error;
1284fa9e4066Sahrens 
1285fa9e4066Sahrens 	/*
1286fa9e4066Sahrens 	 * Execute the corresponding ioctl() to set this property.
1287fa9e4066Sahrens 	 */
1288fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1289fa9e4066Sahrens 
1290990b4856Slling 	if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1291e9dbad6fSeschrock 		goto error;
1292e9dbad6fSeschrock 
1293ecd6cf80Smarks 	ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1294743a77edSAlan Wright 
1295fa9e4066Sahrens 	if (ret != 0) {
1296fa9e4066Sahrens 		switch (errno) {
1297fa9e4066Sahrens 
1298fa9e4066Sahrens 		case ENOSPC:
1299fa9e4066Sahrens 			/*
1300fa9e4066Sahrens 			 * For quotas and reservations, ENOSPC indicates
1301fa9e4066Sahrens 			 * something different; setting a quota or reservation
1302fa9e4066Sahrens 			 * doesn't use any disk space.
1303fa9e4066Sahrens 			 */
1304fa9e4066Sahrens 			switch (prop) {
1305fa9e4066Sahrens 			case ZFS_PROP_QUOTA:
1306a9799022Sck 			case ZFS_PROP_REFQUOTA:
130799653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
130899653d4eSeschrock 				    "size is less than current used or "
130999653d4eSeschrock 				    "reserved space"));
131099653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1311fa9e4066Sahrens 				break;
1312fa9e4066Sahrens 
1313fa9e4066Sahrens 			case ZFS_PROP_RESERVATION:
1314a9799022Sck 			case ZFS_PROP_REFRESERVATION:
131599653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
131699653d4eSeschrock 				    "size is greater than available space"));
131799653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1318fa9e4066Sahrens 				break;
1319fa9e4066Sahrens 
1320fa9e4066Sahrens 			default:
132199653d4eSeschrock 				(void) zfs_standard_error(hdl, errno, errbuf);
1322fa9e4066Sahrens 				break;
1323fa9e4066Sahrens 			}
1324fa9e4066Sahrens 			break;
1325fa9e4066Sahrens 
1326fa9e4066Sahrens 		case EBUSY:
132799653d4eSeschrock 			if (prop == ZFS_PROP_VOLBLOCKSIZE)
132899653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf);
132999653d4eSeschrock 			else
1330e9dbad6fSeschrock 				(void) zfs_standard_error(hdl, EBUSY, errbuf);
1331fa9e4066Sahrens 			break;
1332fa9e4066Sahrens 
13332a79c5feSlling 		case EROFS:
133499653d4eSeschrock 			(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
13352a79c5feSlling 			break;
13362a79c5feSlling 
1337c9431fa1Sahl 		case ENOTSUP:
1338c9431fa1Sahl 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
13399e6eda55Smarks 			    "pool and or dataset must be upgraded to set this "
134040feaa91Sahrens 			    "property or value"));
1341c9431fa1Sahl 			(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
1342c9431fa1Sahl 			break;
1343c9431fa1Sahl 
134415e6edf1Sgw 		case ERANGE:
134515e6edf1Sgw 			if (prop == ZFS_PROP_COMPRESSION) {
134615e6edf1Sgw 				(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
134715e6edf1Sgw 				    "property setting is not allowed on "
134815e6edf1Sgw 				    "bootable datasets"));
134915e6edf1Sgw 				(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
135015e6edf1Sgw 			} else {
135115e6edf1Sgw 				(void) zfs_standard_error(hdl, errno, errbuf);
135215e6edf1Sgw 			}
135315e6edf1Sgw 			break;
135415e6edf1Sgw 
1355fa9e4066Sahrens 		case EOVERFLOW:
1356fa9e4066Sahrens 			/*
1357fa9e4066Sahrens 			 * This platform can't address a volume this big.
1358fa9e4066Sahrens 			 */
1359fa9e4066Sahrens #ifdef _ILP32
1360fa9e4066Sahrens 			if (prop == ZFS_PROP_VOLSIZE) {
136199653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1362fa9e4066Sahrens 				break;
1363fa9e4066Sahrens 			}
1364fa9e4066Sahrens #endif
136599653d4eSeschrock 			/* FALLTHROUGH */
1366fa9e4066Sahrens 		default:
136799653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
1368fa9e4066Sahrens 		}
1369fa9e4066Sahrens 	} else {
1370a227b7f4Shs 		if (do_prefix)
1371a227b7f4Shs 			ret = changelist_postfix(cl);
1372a227b7f4Shs 
1373fa9e4066Sahrens 		/*
1374fa9e4066Sahrens 		 * Refresh the statistics so the new property value
1375fa9e4066Sahrens 		 * is reflected.
1376fa9e4066Sahrens 		 */
1377a227b7f4Shs 		if (ret == 0)
1378e9dbad6fSeschrock 			(void) get_stats(zhp);
1379fa9e4066Sahrens 	}
1380fa9e4066Sahrens 
1381fa9e4066Sahrens error:
1382e9dbad6fSeschrock 	nvlist_free(nvl);
1383e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
1384e9dbad6fSeschrock 	if (cl)
1385e9dbad6fSeschrock 		changelist_free(cl);
1386fa9e4066Sahrens 	return (ret);
1387fa9e4066Sahrens }
1388fa9e4066Sahrens 
1389fa9e4066Sahrens /*
1390fa9e4066Sahrens  * Given a property, inherit the value from the parent dataset.
1391fa9e4066Sahrens  */
1392fa9e4066Sahrens int
1393e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
1394fa9e4066Sahrens {
1395fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1396fa9e4066Sahrens 	int ret;
1397fa9e4066Sahrens 	prop_changelist_t *cl;
139899653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
139999653d4eSeschrock 	char errbuf[1024];
1400e9dbad6fSeschrock 	zfs_prop_t prop;
140199653d4eSeschrock 
140299653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
140399653d4eSeschrock 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1404fa9e4066Sahrens 
1405990b4856Slling 	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
1406e9dbad6fSeschrock 		/*
1407e9dbad6fSeschrock 		 * For user properties, the amount of work we have to do is very
1408e9dbad6fSeschrock 		 * small, so just do it here.
1409e9dbad6fSeschrock 		 */
1410e9dbad6fSeschrock 		if (!zfs_prop_user(propname)) {
1411e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1412e9dbad6fSeschrock 			    "invalid property"));
1413e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1414e9dbad6fSeschrock 		}
1415e9dbad6fSeschrock 
1416e9dbad6fSeschrock 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1417e9dbad6fSeschrock 		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1418e9dbad6fSeschrock 
1419e45ce728Sahrens 		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
1420e9dbad6fSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1421e9dbad6fSeschrock 
1422e9dbad6fSeschrock 		return (0);
1423e9dbad6fSeschrock 	}
1424e9dbad6fSeschrock 
1425fa9e4066Sahrens 	/*
1426fa9e4066Sahrens 	 * Verify that this property is inheritable.
1427fa9e4066Sahrens 	 */
142899653d4eSeschrock 	if (zfs_prop_readonly(prop))
142999653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1430fa9e4066Sahrens 
143199653d4eSeschrock 	if (!zfs_prop_inheritable(prop))
143299653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1433fa9e4066Sahrens 
1434fa9e4066Sahrens 	/*
1435fa9e4066Sahrens 	 * Check to see if the value applies to this type
1436fa9e4066Sahrens 	 */
143799653d4eSeschrock 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
143899653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1439fa9e4066Sahrens 
1440bf7c2d40Srm 	/*
1441bf7c2d40Srm 	 * Normalize the name, to get rid of shorthand abbrevations.
1442bf7c2d40Srm 	 */
1443bf7c2d40Srm 	propname = zfs_prop_to_name(prop);
1444fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1445e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1446fa9e4066Sahrens 
1447fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1448fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
144999653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
145099653d4eSeschrock 		    "dataset is used in a non-global zone"));
145199653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1452fa9e4066Sahrens 	}
1453fa9e4066Sahrens 
1454fa9e4066Sahrens 	/*
1455fa9e4066Sahrens 	 * Determine datasets which will be affected by this change, if any.
1456fa9e4066Sahrens 	 */
14570069fd67STim Haley 	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1458fa9e4066Sahrens 		return (-1);
1459fa9e4066Sahrens 
1460fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
146199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
146299653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
146399653d4eSeschrock 		    "in a non-global zone"));
146499653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1465fa9e4066Sahrens 		goto error;
1466fa9e4066Sahrens 	}
1467fa9e4066Sahrens 
1468fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1469fa9e4066Sahrens 		goto error;
1470fa9e4066Sahrens 
1471e45ce728Sahrens 	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
147299653d4eSeschrock 		return (zfs_standard_error(hdl, errno, errbuf));
1473fa9e4066Sahrens 	} else {
1474fa9e4066Sahrens 
1475efc555ebSnd 		if ((ret = changelist_postfix(cl)) != 0)
1476fa9e4066Sahrens 			goto error;
1477fa9e4066Sahrens 
1478fa9e4066Sahrens 		/*
1479fa9e4066Sahrens 		 * Refresh the statistics so the new property is reflected.
1480fa9e4066Sahrens 		 */
1481fa9e4066Sahrens 		(void) get_stats(zhp);
1482fa9e4066Sahrens 	}
1483fa9e4066Sahrens 
1484fa9e4066Sahrens error:
1485fa9e4066Sahrens 	changelist_free(cl);
1486fa9e4066Sahrens 	return (ret);
1487fa9e4066Sahrens }
1488fa9e4066Sahrens 
14897f7322feSeschrock /*
14907f7322feSeschrock  * True DSL properties are stored in an nvlist.  The following two functions
14917f7322feSeschrock  * extract them appropriately.
14927f7322feSeschrock  */
14937f7322feSeschrock static uint64_t
14947f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
14957f7322feSeschrock {
14967f7322feSeschrock 	nvlist_t *nv;
14977f7322feSeschrock 	uint64_t value;
14987f7322feSeschrock 
1499a2eea2e1Sahrens 	*source = NULL;
15007f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
15017f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
1502990b4856Slling 		verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
1503990b4856Slling 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
15047f7322feSeschrock 	} else {
15052e5e9e19SSanjeev Bagewadi 		verify(!zhp->zfs_props_table ||
15062e5e9e19SSanjeev Bagewadi 		    zhp->zfs_props_table[prop] == B_TRUE);
15077f7322feSeschrock 		value = zfs_prop_default_numeric(prop);
15087f7322feSeschrock 		*source = "";
15097f7322feSeschrock 	}
15107f7322feSeschrock 
15117f7322feSeschrock 	return (value);
15127f7322feSeschrock }
15137f7322feSeschrock 
15147f7322feSeschrock static char *
15157f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
15167f7322feSeschrock {
15177f7322feSeschrock 	nvlist_t *nv;
15187f7322feSeschrock 	char *value;
15197f7322feSeschrock 
1520a2eea2e1Sahrens 	*source = NULL;
15217f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
15227f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
1523990b4856Slling 		verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
1524990b4856Slling 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
15257f7322feSeschrock 	} else {
15262e5e9e19SSanjeev Bagewadi 		verify(!zhp->zfs_props_table ||
15272e5e9e19SSanjeev Bagewadi 		    zhp->zfs_props_table[prop] == B_TRUE);
15287f7322feSeschrock 		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
15297f7322feSeschrock 			value = "";
15307f7322feSeschrock 		*source = "";
15317f7322feSeschrock 	}
15327f7322feSeschrock 
15337f7322feSeschrock 	return (value);
15347f7322feSeschrock }
15357f7322feSeschrock 
1536fa9e4066Sahrens /*
1537fa9e4066Sahrens  * Internal function for getting a numeric property.  Both zfs_prop_get() and
1538fa9e4066Sahrens  * zfs_prop_get_int() are built using this interface.
1539fa9e4066Sahrens  *
1540fa9e4066Sahrens  * Certain properties can be overridden using 'mount -o'.  In this case, scan
1541fa9e4066Sahrens  * the contents of the /etc/mnttab entry, searching for the appropriate options.
1542fa9e4066Sahrens  * If they differ from the on-disk values, report the current values and mark
1543fa9e4066Sahrens  * the source "temporary".
1544fa9e4066Sahrens  */
154599653d4eSeschrock static int
1546990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
154799653d4eSeschrock     char **source, uint64_t *val)
1548fa9e4066Sahrens {
1549bd00f61bSrm 	zfs_cmd_t zc = { 0 };
155096510749Stimh 	nvlist_t *zplprops = NULL;
1551fa9e4066Sahrens 	struct mnttab mnt;
15523ccfa83cSahrens 	char *mntopt_on = NULL;
15533ccfa83cSahrens 	char *mntopt_off = NULL;
1554fa9e4066Sahrens 
1555fa9e4066Sahrens 	*source = NULL;
1556fa9e4066Sahrens 
15573ccfa83cSahrens 	switch (prop) {
15583ccfa83cSahrens 	case ZFS_PROP_ATIME:
15593ccfa83cSahrens 		mntopt_on = MNTOPT_ATIME;
15603ccfa83cSahrens 		mntopt_off = MNTOPT_NOATIME;
15613ccfa83cSahrens 		break;
15623ccfa83cSahrens 
15633ccfa83cSahrens 	case ZFS_PROP_DEVICES:
15643ccfa83cSahrens 		mntopt_on = MNTOPT_DEVICES;
15653ccfa83cSahrens 		mntopt_off = MNTOPT_NODEVICES;
15663ccfa83cSahrens 		break;
15673ccfa83cSahrens 
15683ccfa83cSahrens 	case ZFS_PROP_EXEC:
15693ccfa83cSahrens 		mntopt_on = MNTOPT_EXEC;
15703ccfa83cSahrens 		mntopt_off = MNTOPT_NOEXEC;
15713ccfa83cSahrens 		break;
15723ccfa83cSahrens 
15733ccfa83cSahrens 	case ZFS_PROP_READONLY:
15743ccfa83cSahrens 		mntopt_on = MNTOPT_RO;
15753ccfa83cSahrens 		mntopt_off = MNTOPT_RW;
15763ccfa83cSahrens 		break;
15773ccfa83cSahrens 
15783ccfa83cSahrens 	case ZFS_PROP_SETUID:
15793ccfa83cSahrens 		mntopt_on = MNTOPT_SETUID;
15803ccfa83cSahrens 		mntopt_off = MNTOPT_NOSETUID;
15813ccfa83cSahrens 		break;
15823ccfa83cSahrens 
15833ccfa83cSahrens 	case ZFS_PROP_XATTR:
15843ccfa83cSahrens 		mntopt_on = MNTOPT_XATTR;
15853ccfa83cSahrens 		mntopt_off = MNTOPT_NOXATTR;
15863ccfa83cSahrens 		break;
1587da6c28aaSamw 
1588da6c28aaSamw 	case ZFS_PROP_NBMAND:
1589da6c28aaSamw 		mntopt_on = MNTOPT_NBMAND;
1590da6c28aaSamw 		mntopt_off = MNTOPT_NONBMAND;
1591da6c28aaSamw 		break;
15923ccfa83cSahrens 	}
15933ccfa83cSahrens 
15943bb79becSeschrock 	/*
15953bb79becSeschrock 	 * Because looking up the mount options is potentially expensive
15963bb79becSeschrock 	 * (iterating over all of /etc/mnttab), we defer its calculation until
15973bb79becSeschrock 	 * we're looking up a property which requires its presence.
15983bb79becSeschrock 	 */
15993bb79becSeschrock 	if (!zhp->zfs_mntcheck &&
16003ccfa83cSahrens 	    (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
1601ebedde84SEric Taylor 		libzfs_handle_t *hdl = zhp->zfs_hdl;
1602ebedde84SEric Taylor 		struct mnttab entry;
16033bb79becSeschrock 
1604ebedde84SEric Taylor 		if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
1605ebedde84SEric Taylor 			zhp->zfs_mntopts = zfs_strdup(hdl,
16063ccfa83cSahrens 			    entry.mnt_mntopts);
16073ccfa83cSahrens 			if (zhp->zfs_mntopts == NULL)
16083ccfa83cSahrens 				return (-1);
16093ccfa83cSahrens 		}
16103bb79becSeschrock 
16113bb79becSeschrock 		zhp->zfs_mntcheck = B_TRUE;
16123bb79becSeschrock 	}
16133bb79becSeschrock 
1614fa9e4066Sahrens 	if (zhp->zfs_mntopts == NULL)
1615fa9e4066Sahrens 		mnt.mnt_mntopts = "";
1616fa9e4066Sahrens 	else
1617fa9e4066Sahrens 		mnt.mnt_mntopts = zhp->zfs_mntopts;
1618fa9e4066Sahrens 
1619fa9e4066Sahrens 	switch (prop) {
1620fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1621fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1622fa9e4066Sahrens 	case ZFS_PROP_EXEC:
16233ccfa83cSahrens 	case ZFS_PROP_READONLY:
16243ccfa83cSahrens 	case ZFS_PROP_SETUID:
16253ccfa83cSahrens 	case ZFS_PROP_XATTR:
1626da6c28aaSamw 	case ZFS_PROP_NBMAND:
162799653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1628fa9e4066Sahrens 
16293ccfa83cSahrens 		if (hasmntopt(&mnt, mntopt_on) && !*val) {
163099653d4eSeschrock 			*val = B_TRUE;
1631fa9e4066Sahrens 			if (src)
1632990b4856Slling 				*src = ZPROP_SRC_TEMPORARY;
16333ccfa83cSahrens 		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
163499653d4eSeschrock 			*val = B_FALSE;
1635fa9e4066Sahrens 			if (src)
1636990b4856Slling 				*src = ZPROP_SRC_TEMPORARY;
1637fa9e4066Sahrens 		}
163899653d4eSeschrock 		break;
1639fa9e4066Sahrens 
16403ccfa83cSahrens 	case ZFS_PROP_CANMOUNT:
164199653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1642a227b7f4Shs 		if (*val != ZFS_CANMOUNT_ON)
1643fda77a98Srm 			*source = zhp->zfs_name;
1644fda77a98Srm 		else
1645fda77a98Srm 			*source = "";	/* default */
164699653d4eSeschrock 		break;
1647fa9e4066Sahrens 
1648fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1649a9799022Sck 	case ZFS_PROP_REFQUOTA:
1650fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1651a9799022Sck 	case ZFS_PROP_REFRESERVATION:
1652a2eea2e1Sahrens 		*val = getprop_uint64(zhp, prop, source);
1653a2eea2e1Sahrens 		if (*val == 0)
1654fa9e4066Sahrens 			*source = "";	/* default */
1655fa9e4066Sahrens 		else
1656fa9e4066Sahrens 			*source = zhp->zfs_name;
165799653d4eSeschrock 		break;
1658fa9e4066Sahrens 
1659fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
166099653d4eSeschrock 		*val = (zhp->zfs_mntopts != NULL);
166199653d4eSeschrock 		break;
1662fa9e4066Sahrens 
166339c23413Seschrock 	case ZFS_PROP_NUMCLONES:
166439c23413Seschrock 		*val = zhp->zfs_dmustats.dds_num_clones;
166539c23413Seschrock 		break;
166639c23413Seschrock 
1667bd00f61bSrm 	case ZFS_PROP_VERSION:
1668de8267e0Stimh 	case ZFS_PROP_NORMALIZE:
1669de8267e0Stimh 	case ZFS_PROP_UTF8ONLY:
1670de8267e0Stimh 	case ZFS_PROP_CASE:
1671de8267e0Stimh 		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
1672de8267e0Stimh 		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
16733d7934e1Srm 			return (-1);
1674bd00f61bSrm 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1675de8267e0Stimh 		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
1676de8267e0Stimh 			zcmd_free_nvlists(&zc);
1677bd00f61bSrm 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1678de8267e0Stimh 			    "unable to get %s property"),
1679de8267e0Stimh 			    zfs_prop_to_name(prop));
1680bd00f61bSrm 			return (zfs_error(zhp->zfs_hdl, EZFS_BADVERSION,
1681bd00f61bSrm 			    dgettext(TEXT_DOMAIN, "internal error")));
1682bd00f61bSrm 		}
1683de8267e0Stimh 		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
1684de8267e0Stimh 		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
1685de8267e0Stimh 		    val) != 0) {
1686de8267e0Stimh 			zcmd_free_nvlists(&zc);
1687de8267e0Stimh 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1688de8267e0Stimh 			    "unable to get %s property"),
1689de8267e0Stimh 			    zfs_prop_to_name(prop));
1690de8267e0Stimh 			return (zfs_error(zhp->zfs_hdl, EZFS_NOMEM,
1691de8267e0Stimh 			    dgettext(TEXT_DOMAIN, "internal error")));
1692de8267e0Stimh 		}
169396510749Stimh 		if (zplprops)
169496510749Stimh 			nvlist_free(zplprops);
1695de8267e0Stimh 		zcmd_free_nvlists(&zc);
1696bd00f61bSrm 		break;
1697bd00f61bSrm 
1698fa9e4066Sahrens 	default:
1699e7437265Sahrens 		switch (zfs_prop_get_type(prop)) {
170091ebeef5Sahrens 		case PROP_TYPE_NUMBER:
170191ebeef5Sahrens 		case PROP_TYPE_INDEX:
1702e7437265Sahrens 			*val = getprop_uint64(zhp, prop, source);
170374e7dc98SMatthew Ahrens 			/*
1704*14843421SMatthew Ahrens 			 * If we tried to use a default value for a
170574e7dc98SMatthew Ahrens 			 * readonly property, it means that it was not
170674e7dc98SMatthew Ahrens 			 * present; return an error.
170774e7dc98SMatthew Ahrens 			 */
170874e7dc98SMatthew Ahrens 			if (zfs_prop_readonly(prop) &&
170974e7dc98SMatthew Ahrens 			    *source && (*source)[0] == '\0') {
171074e7dc98SMatthew Ahrens 				return (-1);
171174e7dc98SMatthew Ahrens 			}
1712e7437265Sahrens 			break;
1713e7437265Sahrens 
171491ebeef5Sahrens 		case PROP_TYPE_STRING:
1715e7437265Sahrens 		default:
1716e7437265Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1717e7437265Sahrens 			    "cannot get non-numeric property"));
1718e7437265Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
1719e7437265Sahrens 			    dgettext(TEXT_DOMAIN, "internal error")));
1720e7437265Sahrens 		}
1721fa9e4066Sahrens 	}
1722fa9e4066Sahrens 
1723fa9e4066Sahrens 	return (0);
1724fa9e4066Sahrens }
1725fa9e4066Sahrens 
1726fa9e4066Sahrens /*
1727fa9e4066Sahrens  * Calculate the source type, given the raw source string.
1728fa9e4066Sahrens  */
1729fa9e4066Sahrens static void
1730990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
1731fa9e4066Sahrens     char *statbuf, size_t statlen)
1732fa9e4066Sahrens {
1733990b4856Slling 	if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
1734fa9e4066Sahrens 		return;
1735fa9e4066Sahrens 
1736fa9e4066Sahrens 	if (source == NULL) {
1737990b4856Slling 		*srctype = ZPROP_SRC_NONE;
1738fa9e4066Sahrens 	} else if (source[0] == '\0') {
1739990b4856Slling 		*srctype = ZPROP_SRC_DEFAULT;
1740fa9e4066Sahrens 	} else {
1741fa9e4066Sahrens 		if (strcmp(source, zhp->zfs_name) == 0) {
1742990b4856Slling 			*srctype = ZPROP_SRC_LOCAL;
1743fa9e4066Sahrens 		} else {
1744fa9e4066Sahrens 			(void) strlcpy(statbuf, source, statlen);
1745990b4856Slling 			*srctype = ZPROP_SRC_INHERITED;
1746fa9e4066Sahrens 		}
1747fa9e4066Sahrens 	}
1748fa9e4066Sahrens 
1749fa9e4066Sahrens }
1750fa9e4066Sahrens 
1751fa9e4066Sahrens /*
1752fa9e4066Sahrens  * Retrieve a property from the given object.  If 'literal' is specified, then
1753fa9e4066Sahrens  * numbers are left as exact values.  Otherwise, numbers are converted to a
1754fa9e4066Sahrens  * human-readable form.
1755fa9e4066Sahrens  *
1756fa9e4066Sahrens  * Returns 0 on success, or -1 on error.
1757fa9e4066Sahrens  */
1758fa9e4066Sahrens int
1759fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
1760990b4856Slling     zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1761fa9e4066Sahrens {
1762fa9e4066Sahrens 	char *source = NULL;
1763fa9e4066Sahrens 	uint64_t val;
1764fa9e4066Sahrens 	char *str;
1765e9dbad6fSeschrock 	const char *strval;
1766fa9e4066Sahrens 
1767fa9e4066Sahrens 	/*
1768fa9e4066Sahrens 	 * Check to see if this property applies to our object
1769fa9e4066Sahrens 	 */
1770fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1771fa9e4066Sahrens 		return (-1);
1772fa9e4066Sahrens 
1773fa9e4066Sahrens 	if (src)
1774990b4856Slling 		*src = ZPROP_SRC_NONE;
1775fa9e4066Sahrens 
1776fa9e4066Sahrens 	switch (prop) {
1777fa9e4066Sahrens 	case ZFS_PROP_CREATION:
1778fa9e4066Sahrens 		/*
1779fa9e4066Sahrens 		 * 'creation' is a time_t stored in the statistics.  We convert
1780fa9e4066Sahrens 		 * this into a string unless 'literal' is specified.
1781fa9e4066Sahrens 		 */
1782fa9e4066Sahrens 		{
1783a2eea2e1Sahrens 			val = getprop_uint64(zhp, prop, &source);
1784a2eea2e1Sahrens 			time_t time = (time_t)val;
1785fa9e4066Sahrens 			struct tm t;
1786fa9e4066Sahrens 
1787fa9e4066Sahrens 			if (literal ||
1788fa9e4066Sahrens 			    localtime_r(&time, &t) == NULL ||
1789fa9e4066Sahrens 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1790fa9e4066Sahrens 			    &t) == 0)
1791a2eea2e1Sahrens 				(void) snprintf(propbuf, proplen, "%llu", val);
1792fa9e4066Sahrens 		}
1793fa9e4066Sahrens 		break;
1794fa9e4066Sahrens 
1795fa9e4066Sahrens 	case ZFS_PROP_MOUNTPOINT:
1796fa9e4066Sahrens 		/*
1797fa9e4066Sahrens 		 * Getting the precise mountpoint can be tricky.
1798fa9e4066Sahrens 		 *
1799fa9e4066Sahrens 		 *  - for 'none' or 'legacy', return those values.
1800fa9e4066Sahrens 		 *  - for inherited mountpoints, we want to take everything
1801fa9e4066Sahrens 		 *    after our ancestor and append it to the inherited value.
1802fa9e4066Sahrens 		 *
1803fa9e4066Sahrens 		 * If the pool has an alternate root, we want to prepend that
1804fa9e4066Sahrens 		 * root to any values we return.
1805fa9e4066Sahrens 		 */
180629ab75c9Srm 
18077f7322feSeschrock 		str = getprop_string(zhp, prop, &source);
1808fa9e4066Sahrens 
1809b21718f0Sgw 		if (str[0] == '/') {
181029ab75c9Srm 			char buf[MAXPATHLEN];
181129ab75c9Srm 			char *root = buf;
18127f7322feSeschrock 			const char *relpath = zhp->zfs_name + strlen(source);
1813fa9e4066Sahrens 
1814fa9e4066Sahrens 			if (relpath[0] == '/')
1815fa9e4066Sahrens 				relpath++;
1816b21718f0Sgw 
181729ab75c9Srm 			if ((zpool_get_prop(zhp->zpool_hdl,
181829ab75c9Srm 			    ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
181929ab75c9Srm 			    (strcmp(root, "-") == 0))
182029ab75c9Srm 				root[0] = '\0';
1821b21718f0Sgw 			/*
1822b21718f0Sgw 			 * Special case an alternate root of '/'. This will
1823b21718f0Sgw 			 * avoid having multiple leading slashes in the
1824b21718f0Sgw 			 * mountpoint path.
1825b21718f0Sgw 			 */
1826b21718f0Sgw 			if (strcmp(root, "/") == 0)
1827b21718f0Sgw 				root++;
1828b21718f0Sgw 
1829b21718f0Sgw 			/*
1830b21718f0Sgw 			 * If the mountpoint is '/' then skip over this
1831b21718f0Sgw 			 * if we are obtaining either an alternate root or
1832b21718f0Sgw 			 * an inherited mountpoint.
1833b21718f0Sgw 			 */
1834b21718f0Sgw 			if (str[1] == '\0' && (root[0] != '\0' ||
1835b21718f0Sgw 			    relpath[0] != '\0'))
18367f7322feSeschrock 				str++;
1837fa9e4066Sahrens 
1838fa9e4066Sahrens 			if (relpath[0] == '\0')
1839fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s",
18407f7322feSeschrock 				    root, str);
1841fa9e4066Sahrens 			else
1842fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
18437f7322feSeschrock 				    root, str, relpath[0] == '@' ? "" : "/",
1844fa9e4066Sahrens 				    relpath);
1845fa9e4066Sahrens 		} else {
1846fa9e4066Sahrens 			/* 'legacy' or 'none' */
18477f7322feSeschrock 			(void) strlcpy(propbuf, str, proplen);
1848fa9e4066Sahrens 		}
1849fa9e4066Sahrens 
1850fa9e4066Sahrens 		break;
1851fa9e4066Sahrens 
1852fa9e4066Sahrens 	case ZFS_PROP_ORIGIN:
1853a2eea2e1Sahrens 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
1854fa9e4066Sahrens 		    proplen);
1855fa9e4066Sahrens 		/*
1856fa9e4066Sahrens 		 * If there is no parent at all, return failure to indicate that
1857fa9e4066Sahrens 		 * it doesn't apply to this dataset.
1858fa9e4066Sahrens 		 */
1859fa9e4066Sahrens 		if (propbuf[0] == '\0')
1860fa9e4066Sahrens 			return (-1);
1861fa9e4066Sahrens 		break;
1862fa9e4066Sahrens 
1863fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1864a9799022Sck 	case ZFS_PROP_REFQUOTA:
1865fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1866a9799022Sck 	case ZFS_PROP_REFRESERVATION:
1867a9799022Sck 
186899653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
186999653d4eSeschrock 			return (-1);
1870fa9e4066Sahrens 
1871fa9e4066Sahrens 		/*
1872fa9e4066Sahrens 		 * If quota or reservation is 0, we translate this into 'none'
1873fa9e4066Sahrens 		 * (unless literal is set), and indicate that it's the default
1874fa9e4066Sahrens 		 * value.  Otherwise, we print the number nicely and indicate
1875fa9e4066Sahrens 		 * that its set locally.
1876fa9e4066Sahrens 		 */
1877fa9e4066Sahrens 		if (val == 0) {
1878fa9e4066Sahrens 			if (literal)
1879fa9e4066Sahrens 				(void) strlcpy(propbuf, "0", proplen);
1880fa9e4066Sahrens 			else
1881fa9e4066Sahrens 				(void) strlcpy(propbuf, "none", proplen);
1882fa9e4066Sahrens 		} else {
1883fa9e4066Sahrens 			if (literal)
18845ad82045Snd 				(void) snprintf(propbuf, proplen, "%llu",
1885b1b8ab34Slling 				    (u_longlong_t)val);
1886fa9e4066Sahrens 			else
1887fa9e4066Sahrens 				zfs_nicenum(val, propbuf, proplen);
1888fa9e4066Sahrens 		}
1889fa9e4066Sahrens 		break;
1890fa9e4066Sahrens 
1891fa9e4066Sahrens 	case ZFS_PROP_COMPRESSRATIO:
189299653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
189399653d4eSeschrock 			return (-1);
18945ad82045Snd 		(void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t)
18955ad82045Snd 		    val / 100, (longlong_t)val % 100);
1896fa9e4066Sahrens 		break;
1897fa9e4066Sahrens 
1898fa9e4066Sahrens 	case ZFS_PROP_TYPE:
1899fa9e4066Sahrens 		switch (zhp->zfs_type) {
1900fa9e4066Sahrens 		case ZFS_TYPE_FILESYSTEM:
1901fa9e4066Sahrens 			str = "filesystem";
1902fa9e4066Sahrens 			break;
1903fa9e4066Sahrens 		case ZFS_TYPE_VOLUME:
1904fa9e4066Sahrens 			str = "volume";
1905fa9e4066Sahrens 			break;
1906fa9e4066Sahrens 		case ZFS_TYPE_SNAPSHOT:
1907fa9e4066Sahrens 			str = "snapshot";
1908fa9e4066Sahrens 			break;
1909fa9e4066Sahrens 		default:
191099653d4eSeschrock 			abort();
1911fa9e4066Sahrens 		}
1912fa9e4066Sahrens 		(void) snprintf(propbuf, proplen, "%s", str);
1913fa9e4066Sahrens 		break;
1914fa9e4066Sahrens 
1915fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
1916fa9e4066Sahrens 		/*
1917fa9e4066Sahrens 		 * The 'mounted' property is a pseudo-property that described
1918fa9e4066Sahrens 		 * whether the filesystem is currently mounted.  Even though
1919fa9e4066Sahrens 		 * it's a boolean value, the typical values of "on" and "off"
1920fa9e4066Sahrens 		 * don't make sense, so we translate to "yes" and "no".
1921fa9e4066Sahrens 		 */
192299653d4eSeschrock 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
192399653d4eSeschrock 		    src, &source, &val) != 0)
192499653d4eSeschrock 			return (-1);
192599653d4eSeschrock 		if (val)
1926fa9e4066Sahrens 			(void) strlcpy(propbuf, "yes", proplen);
1927fa9e4066Sahrens 		else
1928fa9e4066Sahrens 			(void) strlcpy(propbuf, "no", proplen);
1929fa9e4066Sahrens 		break;
1930fa9e4066Sahrens 
1931fa9e4066Sahrens 	case ZFS_PROP_NAME:
1932fa9e4066Sahrens 		/*
1933fa9e4066Sahrens 		 * The 'name' property is a pseudo-property derived from the
1934fa9e4066Sahrens 		 * dataset name.  It is presented as a real property to simplify
1935fa9e4066Sahrens 		 * consumers.
1936fa9e4066Sahrens 		 */
1937fa9e4066Sahrens 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
1938fa9e4066Sahrens 		break;
1939fa9e4066Sahrens 
1940fa9e4066Sahrens 	default:
1941e7437265Sahrens 		switch (zfs_prop_get_type(prop)) {
194291ebeef5Sahrens 		case PROP_TYPE_NUMBER:
1943e7437265Sahrens 			if (get_numeric_property(zhp, prop, src,
1944e7437265Sahrens 			    &source, &val) != 0)
1945e7437265Sahrens 				return (-1);
1946e7437265Sahrens 			if (literal)
1947e7437265Sahrens 				(void) snprintf(propbuf, proplen, "%llu",
1948e7437265Sahrens 				    (u_longlong_t)val);
1949e7437265Sahrens 			else
1950e7437265Sahrens 				zfs_nicenum(val, propbuf, proplen);
1951e7437265Sahrens 			break;
1952e7437265Sahrens 
195391ebeef5Sahrens 		case PROP_TYPE_STRING:
1954e7437265Sahrens 			(void) strlcpy(propbuf,
1955e7437265Sahrens 			    getprop_string(zhp, prop, &source), proplen);
1956e7437265Sahrens 			break;
1957e7437265Sahrens 
195891ebeef5Sahrens 		case PROP_TYPE_INDEX:
1959fe192f76Sahrens 			if (get_numeric_property(zhp, prop, src,
1960fe192f76Sahrens 			    &source, &val) != 0)
1961fe192f76Sahrens 				return (-1);
1962fe192f76Sahrens 			if (zfs_prop_index_to_string(prop, val, &strval) != 0)
1963e7437265Sahrens 				return (-1);
1964e7437265Sahrens 			(void) strlcpy(propbuf, strval, proplen);
1965e7437265Sahrens 			break;
1966e7437265Sahrens 
1967e7437265Sahrens 		default:
1968e7437265Sahrens 			abort();
1969e7437265Sahrens 		}
1970fa9e4066Sahrens 	}
1971fa9e4066Sahrens 
1972fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1973fa9e4066Sahrens 
1974fa9e4066Sahrens 	return (0);
1975fa9e4066Sahrens }
1976fa9e4066Sahrens 
1977fa9e4066Sahrens /*
1978fa9e4066Sahrens  * Utility function to get the given numeric property.  Does no validation that
1979fa9e4066Sahrens  * the given property is the appropriate type; should only be used with
1980fa9e4066Sahrens  * hard-coded property types.
1981fa9e4066Sahrens  */
1982fa9e4066Sahrens uint64_t
1983fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
1984fa9e4066Sahrens {
1985fa9e4066Sahrens 	char *source;
198699653d4eSeschrock 	uint64_t val;
198799653d4eSeschrock 
19883cb34c60Sahrens 	(void) get_numeric_property(zhp, prop, NULL, &source, &val);
1989fa9e4066Sahrens 
199099653d4eSeschrock 	return (val);
1991fa9e4066Sahrens }
1992fa9e4066Sahrens 
19937b97dc1aSrm int
19947b97dc1aSrm zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
19957b97dc1aSrm {
19967b97dc1aSrm 	char buf[64];
19977b97dc1aSrm 
1998*14843421SMatthew Ahrens 	(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
19997b97dc1aSrm 	return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
20007b97dc1aSrm }
20017b97dc1aSrm 
2002fa9e4066Sahrens /*
2003fa9e4066Sahrens  * Similar to zfs_prop_get(), but returns the value as an integer.
2004fa9e4066Sahrens  */
2005fa9e4066Sahrens int
2006fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
2007990b4856Slling     zprop_source_t *src, char *statbuf, size_t statlen)
2008fa9e4066Sahrens {
2009fa9e4066Sahrens 	char *source;
2010fa9e4066Sahrens 
2011fa9e4066Sahrens 	/*
2012fa9e4066Sahrens 	 * Check to see if this property applies to our object
2013fa9e4066Sahrens 	 */
2014e45ce728Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
2015ece3d9b3Slling 		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
201699653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
201799653d4eSeschrock 		    zfs_prop_to_name(prop)));
2018e45ce728Sahrens 	}
2019fa9e4066Sahrens 
2020fa9e4066Sahrens 	if (src)
2021990b4856Slling 		*src = ZPROP_SRC_NONE;
2022fa9e4066Sahrens 
202399653d4eSeschrock 	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
202499653d4eSeschrock 		return (-1);
2025fa9e4066Sahrens 
2026fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
2027fa9e4066Sahrens 
2028fa9e4066Sahrens 	return (0);
2029fa9e4066Sahrens }
2030fa9e4066Sahrens 
2031*14843421SMatthew Ahrens static int
2032*14843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
2033*14843421SMatthew Ahrens     char **domainp, idmap_rid_t *ridp)
2034*14843421SMatthew Ahrens {
2035*14843421SMatthew Ahrens 	idmap_handle_t *idmap_hdl = NULL;
2036*14843421SMatthew Ahrens 	idmap_get_handle_t *get_hdl = NULL;
2037*14843421SMatthew Ahrens 	idmap_stat status;
2038*14843421SMatthew Ahrens 	int err = EINVAL;
2039*14843421SMatthew Ahrens 
2040*14843421SMatthew Ahrens 	if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS)
2041*14843421SMatthew Ahrens 		goto out;
2042*14843421SMatthew Ahrens 	if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS)
2043*14843421SMatthew Ahrens 		goto out;
2044*14843421SMatthew Ahrens 
2045*14843421SMatthew Ahrens 	if (isuser) {
2046*14843421SMatthew Ahrens 		err = idmap_get_sidbyuid(get_hdl, id,
2047*14843421SMatthew Ahrens 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2048*14843421SMatthew Ahrens 	} else {
2049*14843421SMatthew Ahrens 		err = idmap_get_sidbygid(get_hdl, id,
2050*14843421SMatthew Ahrens 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2051*14843421SMatthew Ahrens 	}
2052*14843421SMatthew Ahrens 	if (err == IDMAP_SUCCESS &&
2053*14843421SMatthew Ahrens 	    idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
2054*14843421SMatthew Ahrens 	    status == IDMAP_SUCCESS)
2055*14843421SMatthew Ahrens 		err = 0;
2056*14843421SMatthew Ahrens 	else
2057*14843421SMatthew Ahrens 		err = EINVAL;
2058*14843421SMatthew Ahrens out:
2059*14843421SMatthew Ahrens 	if (get_hdl)
2060*14843421SMatthew Ahrens 		idmap_get_destroy(get_hdl);
2061*14843421SMatthew Ahrens 	if (idmap_hdl)
2062*14843421SMatthew Ahrens 		(void) idmap_fini(idmap_hdl);
2063*14843421SMatthew Ahrens 	return (err);
2064*14843421SMatthew Ahrens }
2065*14843421SMatthew Ahrens 
2066*14843421SMatthew Ahrens /*
2067*14843421SMatthew Ahrens  * convert the propname into parameters needed by kernel
2068*14843421SMatthew Ahrens  * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
2069*14843421SMatthew Ahrens  * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
2070*14843421SMatthew Ahrens  */
2071*14843421SMatthew Ahrens static int
2072*14843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned,
2073*14843421SMatthew Ahrens     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
2074*14843421SMatthew Ahrens {
2075*14843421SMatthew Ahrens 	zfs_userquota_prop_t type;
2076*14843421SMatthew Ahrens 	char *cp, *end;
2077*14843421SMatthew Ahrens 	boolean_t isuser;
2078*14843421SMatthew Ahrens 
2079*14843421SMatthew Ahrens 	domain[0] = '\0';
2080*14843421SMatthew Ahrens 
2081*14843421SMatthew Ahrens 	/* Figure out the property type ({user|group}{quota|space}) */
2082*14843421SMatthew Ahrens 	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
2083*14843421SMatthew Ahrens 		if (strncmp(propname, zfs_userquota_prop_prefixes[type],
2084*14843421SMatthew Ahrens 		    strlen(zfs_userquota_prop_prefixes[type])) == 0)
2085*14843421SMatthew Ahrens 			break;
2086*14843421SMatthew Ahrens 	}
2087*14843421SMatthew Ahrens 	if (type == ZFS_NUM_USERQUOTA_PROPS)
2088*14843421SMatthew Ahrens 		return (EINVAL);
2089*14843421SMatthew Ahrens 	*typep = type;
2090*14843421SMatthew Ahrens 
2091*14843421SMatthew Ahrens 	isuser = (type == ZFS_PROP_USERQUOTA ||
2092*14843421SMatthew Ahrens 	    type == ZFS_PROP_USERUSED);
2093*14843421SMatthew Ahrens 
2094*14843421SMatthew Ahrens 	cp = strchr(propname, '@') + 1;
2095*14843421SMatthew Ahrens 
2096*14843421SMatthew Ahrens 	if (strchr(cp, '@')) {
2097*14843421SMatthew Ahrens 		/*
2098*14843421SMatthew Ahrens 		 * It's a SID name (eg "user@domain") that needs to be
2099*14843421SMatthew Ahrens 		 * turned into S-1-domainID-RID.  There should be a
2100*14843421SMatthew Ahrens 		 * better way to do this, but for now just translate it
2101*14843421SMatthew Ahrens 		 * to the (possibly ephemeral) uid and then back to the
2102*14843421SMatthew Ahrens 		 * SID.  This is like getsidname(noresolve=TRUE).
2103*14843421SMatthew Ahrens 		 */
2104*14843421SMatthew Ahrens 		uid_t id;
2105*14843421SMatthew Ahrens 		idmap_rid_t rid;
2106*14843421SMatthew Ahrens 		char *mapdomain;
2107*14843421SMatthew Ahrens 
2108*14843421SMatthew Ahrens 		if (zoned && getzoneid() == GLOBAL_ZONEID)
2109*14843421SMatthew Ahrens 			return (ENOENT);
2110*14843421SMatthew Ahrens 		if (sid_to_id(cp, isuser, &id) != 0)
2111*14843421SMatthew Ahrens 			return (ENOENT);
2112*14843421SMatthew Ahrens 		if (idmap_id_to_numeric_domain_rid(id, isuser,
2113*14843421SMatthew Ahrens 		    &mapdomain, &rid) != 0)
2114*14843421SMatthew Ahrens 			return (ENOENT);
2115*14843421SMatthew Ahrens 		(void) strlcpy(domain, mapdomain, domainlen);
2116*14843421SMatthew Ahrens 		*ridp = rid;
2117*14843421SMatthew Ahrens 	} else if (strncmp(cp, "S-1-", 4) == 0) {
2118*14843421SMatthew Ahrens 		/* It's a numeric SID (eg "S-1-234-567-89") */
2119*14843421SMatthew Ahrens 		(void) strcpy(domain, cp);
2120*14843421SMatthew Ahrens 		cp = strrchr(domain, '-');
2121*14843421SMatthew Ahrens 		*cp = '\0';
2122*14843421SMatthew Ahrens 		cp++;
2123*14843421SMatthew Ahrens 
2124*14843421SMatthew Ahrens 		errno = 0;
2125*14843421SMatthew Ahrens 		*ridp = strtoull(cp, &end, 10);
2126*14843421SMatthew Ahrens 		if (errno == 0 || *end != '\0')
2127*14843421SMatthew Ahrens 			return (EINVAL);
2128*14843421SMatthew Ahrens 	} else if (!isdigit(*cp)) {
2129*14843421SMatthew Ahrens 		/*
2130*14843421SMatthew Ahrens 		 * It's a user/group name (eg "user") that needs to be
2131*14843421SMatthew Ahrens 		 * turned into a uid/gid
2132*14843421SMatthew Ahrens 		 */
2133*14843421SMatthew Ahrens 		if (zoned && getzoneid() == GLOBAL_ZONEID)
2134*14843421SMatthew Ahrens 			return (ENOENT);
2135*14843421SMatthew Ahrens 		if (isuser) {
2136*14843421SMatthew Ahrens 			struct passwd *pw;
2137*14843421SMatthew Ahrens 			pw = getpwnam(cp);
2138*14843421SMatthew Ahrens 			if (pw == NULL)
2139*14843421SMatthew Ahrens 				return (ENOENT);
2140*14843421SMatthew Ahrens 			*ridp = pw->pw_uid;
2141*14843421SMatthew Ahrens 		} else {
2142*14843421SMatthew Ahrens 			struct group *gr;
2143*14843421SMatthew Ahrens 			gr = getgrnam(cp);
2144*14843421SMatthew Ahrens 			if (gr == NULL)
2145*14843421SMatthew Ahrens 				return (ENOENT);
2146*14843421SMatthew Ahrens 			*ridp = gr->gr_gid;
2147*14843421SMatthew Ahrens 		}
2148*14843421SMatthew Ahrens 	} else {
2149*14843421SMatthew Ahrens 		/* It's a user/group ID (eg "12345"). */
2150*14843421SMatthew Ahrens 		uid_t id = strtoul(cp, &end, 10);
2151*14843421SMatthew Ahrens 		idmap_rid_t rid;
2152*14843421SMatthew Ahrens 		char *mapdomain;
2153*14843421SMatthew Ahrens 
2154*14843421SMatthew Ahrens 		if (*end != '\0')
2155*14843421SMatthew Ahrens 			return (EINVAL);
2156*14843421SMatthew Ahrens 		if (id > MAXUID) {
2157*14843421SMatthew Ahrens 			/* It's an ephemeral ID. */
2158*14843421SMatthew Ahrens 			if (idmap_id_to_numeric_domain_rid(id, isuser,
2159*14843421SMatthew Ahrens 			    &mapdomain, &rid) != 0)
2160*14843421SMatthew Ahrens 				return (ENOENT);
2161*14843421SMatthew Ahrens 			(void) strcpy(domain, mapdomain);
2162*14843421SMatthew Ahrens 			*ridp = rid;
2163*14843421SMatthew Ahrens 		} else {
2164*14843421SMatthew Ahrens 			*ridp = id;
2165*14843421SMatthew Ahrens 		}
2166*14843421SMatthew Ahrens 	}
2167*14843421SMatthew Ahrens 
2168*14843421SMatthew Ahrens 	return (0);
2169*14843421SMatthew Ahrens }
2170*14843421SMatthew Ahrens 
2171*14843421SMatthew Ahrens int
2172*14843421SMatthew Ahrens zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
2173*14843421SMatthew Ahrens     char *propbuf, int proplen, boolean_t literal)
2174*14843421SMatthew Ahrens {
2175*14843421SMatthew Ahrens 	int err;
2176*14843421SMatthew Ahrens 	zfs_cmd_t zc = { 0 };
2177*14843421SMatthew Ahrens 	zfs_userquota_prop_t type;
2178*14843421SMatthew Ahrens 
2179*14843421SMatthew Ahrens 	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2180*14843421SMatthew Ahrens 
2181*14843421SMatthew Ahrens 	err = userquota_propname_decode(propname,
2182*14843421SMatthew Ahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
2183*14843421SMatthew Ahrens 	    &type, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
2184*14843421SMatthew Ahrens 	zc.zc_objset_type = type;
2185*14843421SMatthew Ahrens 	if (err)
2186*14843421SMatthew Ahrens 		return (err);
2187*14843421SMatthew Ahrens 
2188*14843421SMatthew Ahrens 	err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
2189*14843421SMatthew Ahrens 	if (err)
2190*14843421SMatthew Ahrens 		return (err);
2191*14843421SMatthew Ahrens 
2192*14843421SMatthew Ahrens 	if (literal) {
2193*14843421SMatthew Ahrens 		(void) snprintf(propbuf, proplen, "%llu",
2194*14843421SMatthew Ahrens 		    (u_longlong_t)zc.zc_cookie);
2195*14843421SMatthew Ahrens 	} else if (zc.zc_cookie == 0 &&
2196*14843421SMatthew Ahrens 	    (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
2197*14843421SMatthew Ahrens 		(void) strlcpy(propbuf, "none", proplen);
2198*14843421SMatthew Ahrens 	} else {
2199*14843421SMatthew Ahrens 		zfs_nicenum(zc.zc_cookie, propbuf, proplen);
2200*14843421SMatthew Ahrens 	}
2201*14843421SMatthew Ahrens 	return (0);
2202*14843421SMatthew Ahrens }
2203*14843421SMatthew Ahrens 
2204fa9e4066Sahrens /*
2205fa9e4066Sahrens  * Returns the name of the given zfs handle.
2206fa9e4066Sahrens  */
2207fa9e4066Sahrens const char *
2208fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp)
2209fa9e4066Sahrens {
2210fa9e4066Sahrens 	return (zhp->zfs_name);
2211fa9e4066Sahrens }
2212fa9e4066Sahrens 
2213fa9e4066Sahrens /*
2214fa9e4066Sahrens  * Returns the type of the given zfs handle.
2215fa9e4066Sahrens  */
2216fa9e4066Sahrens zfs_type_t
2217fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp)
2218fa9e4066Sahrens {
2219fa9e4066Sahrens 	return (zhp->zfs_type);
2220fa9e4066Sahrens }
2221fa9e4066Sahrens 
2222ebedde84SEric Taylor static int
2223ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
2224ebedde84SEric Taylor {
2225ebedde84SEric Taylor 	int rc;
2226ebedde84SEric Taylor 	uint64_t	orig_cookie;
2227ebedde84SEric Taylor 
2228ebedde84SEric Taylor 	orig_cookie = zc->zc_cookie;
2229ebedde84SEric Taylor top:
2230ebedde84SEric Taylor 	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
2231ebedde84SEric Taylor 	rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
2232ebedde84SEric Taylor 
2233ebedde84SEric Taylor 	if (rc == -1) {
2234ebedde84SEric Taylor 		switch (errno) {
2235ebedde84SEric Taylor 		case ENOMEM:
2236ebedde84SEric Taylor 			/* expand nvlist memory and try again */
2237ebedde84SEric Taylor 			if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
2238ebedde84SEric Taylor 				zcmd_free_nvlists(zc);
2239ebedde84SEric Taylor 				return (-1);
2240ebedde84SEric Taylor 			}
2241ebedde84SEric Taylor 			zc->zc_cookie = orig_cookie;
2242ebedde84SEric Taylor 			goto top;
2243ebedde84SEric Taylor 		/*
2244ebedde84SEric Taylor 		 * An errno value of ESRCH indicates normal completion.
2245ebedde84SEric Taylor 		 * If ENOENT is returned, then the underlying dataset
2246ebedde84SEric Taylor 		 * has been removed since we obtained the handle.
2247ebedde84SEric Taylor 		 */
2248ebedde84SEric Taylor 		case ESRCH:
2249ebedde84SEric Taylor 		case ENOENT:
2250ebedde84SEric Taylor 			rc = 1;
2251ebedde84SEric Taylor 			break;
2252ebedde84SEric Taylor 		default:
2253ebedde84SEric Taylor 			rc = zfs_standard_error(zhp->zfs_hdl, errno,
2254ebedde84SEric Taylor 			    dgettext(TEXT_DOMAIN,
2255ebedde84SEric Taylor 			    "cannot iterate filesystems"));
2256ebedde84SEric Taylor 			break;
2257ebedde84SEric Taylor 		}
2258ebedde84SEric Taylor 	}
2259ebedde84SEric Taylor 	return (rc);
2260ebedde84SEric Taylor }
2261ebedde84SEric Taylor 
2262fa9e4066Sahrens /*
22637f7322feSeschrock  * Iterate over all child filesystems
2264fa9e4066Sahrens  */
2265fa9e4066Sahrens int
22667f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2267fa9e4066Sahrens {
2268fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2269fa9e4066Sahrens 	zfs_handle_t *nzhp;
2270fa9e4066Sahrens 	int ret;
2271fa9e4066Sahrens 
22723cb34c60Sahrens 	if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
22733cb34c60Sahrens 		return (0);
22743cb34c60Sahrens 
2275ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2276ebedde84SEric Taylor 		return (-1);
2277ebedde84SEric Taylor 
2278ebedde84SEric Taylor 	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
2279ebedde84SEric Taylor 	    &zc)) == 0) {
2280fa9e4066Sahrens 		/*
2281fa9e4066Sahrens 		 * Silently ignore errors, as the only plausible explanation is
2282fa9e4066Sahrens 		 * that the pool has since been removed.
2283fa9e4066Sahrens 		 */
2284ebedde84SEric Taylor 		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2285ebedde84SEric Taylor 		    &zc)) == NULL) {
2286fa9e4066Sahrens 			continue;
2287ebedde84SEric Taylor 		}
2288fa9e4066Sahrens 
2289ebedde84SEric Taylor 		if ((ret = func(nzhp, data)) != 0) {
2290ebedde84SEric Taylor 			zcmd_free_nvlists(&zc);
2291fa9e4066Sahrens 			return (ret);
2292ebedde84SEric Taylor 		}
2293fa9e4066Sahrens 	}
2294ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
2295ebedde84SEric Taylor 	return ((ret < 0) ? ret : 0);
22967f7322feSeschrock }
22977f7322feSeschrock 
22987f7322feSeschrock /*
22997f7322feSeschrock  * Iterate over all snapshots
23007f7322feSeschrock  */
23017f7322feSeschrock int
23027f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
23037f7322feSeschrock {
23047f7322feSeschrock 	zfs_cmd_t zc = { 0 };
23057f7322feSeschrock 	zfs_handle_t *nzhp;
23067f7322feSeschrock 	int ret;
2307fa9e4066Sahrens 
23083cb34c60Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
23093cb34c60Sahrens 		return (0);
23103cb34c60Sahrens 
2311ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2312ebedde84SEric Taylor 		return (-1);
2313ebedde84SEric Taylor 	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
2314ebedde84SEric Taylor 	    &zc)) == 0) {
2315fa9e4066Sahrens 
2316ebedde84SEric Taylor 		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2317ebedde84SEric Taylor 		    &zc)) == NULL) {
2318fa9e4066Sahrens 			continue;
2319ebedde84SEric Taylor 		}
2320fa9e4066Sahrens 
2321ebedde84SEric Taylor 		if ((ret = func(nzhp, data)) != 0) {
2322ebedde84SEric Taylor 			zcmd_free_nvlists(&zc);
2323fa9e4066Sahrens 			return (ret);
2324ebedde84SEric Taylor 		}
2325fa9e4066Sahrens 	}
2326ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
2327ebedde84SEric Taylor 	return ((ret < 0) ? ret : 0);
2328fa9e4066Sahrens }
2329fa9e4066Sahrens 
23307f7322feSeschrock /*
23317f7322feSeschrock  * Iterate over all children, snapshots and filesystems
23327f7322feSeschrock  */
23337f7322feSeschrock int
23347f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
23357f7322feSeschrock {
23367f7322feSeschrock 	int ret;
23377f7322feSeschrock 
23387f7322feSeschrock 	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
23397f7322feSeschrock 		return (ret);
23407f7322feSeschrock 
23417f7322feSeschrock 	return (zfs_iter_snapshots(zhp, func, data));
23427f7322feSeschrock }
23437f7322feSeschrock 
2344fa9e4066Sahrens /*
2345fa9e4066Sahrens  * Given a complete name, return just the portion that refers to the parent.
2346fa9e4066Sahrens  * Can return NULL if this is a pool.
2347fa9e4066Sahrens  */
2348fa9e4066Sahrens static int
2349fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen)
2350fa9e4066Sahrens {
2351fa9e4066Sahrens 	char *loc;
2352fa9e4066Sahrens 
2353fa9e4066Sahrens 	if ((loc = strrchr(path, '/')) == NULL)
2354fa9e4066Sahrens 		return (-1);
2355fa9e4066Sahrens 
2356fa9e4066Sahrens 	(void) strncpy(buf, path, MIN(buflen, loc - path));
2357fa9e4066Sahrens 	buf[loc - path] = '\0';
2358fa9e4066Sahrens 
2359fa9e4066Sahrens 	return (0);
2360fa9e4066Sahrens }
2361fa9e4066Sahrens 
2362fa9e4066Sahrens /*
23637f1f55eaSvb  * If accept_ancestor is false, then check to make sure that the given path has
23647f1f55eaSvb  * a parent, and that it exists.  If accept_ancestor is true, then find the
23657f1f55eaSvb  * closest existing ancestor for the given path.  In prefixlen return the
23667f1f55eaSvb  * length of already existing prefix of the given path.  We also fetch the
23677f1f55eaSvb  * 'zoned' property, which is used to validate property settings when creating
23687f1f55eaSvb  * new datasets.
2369fa9e4066Sahrens  */
2370fa9e4066Sahrens static int
23717f1f55eaSvb check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
23727f1f55eaSvb     boolean_t accept_ancestor, int *prefixlen)
2373fa9e4066Sahrens {
2374fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2375fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2376fa9e4066Sahrens 	char *slash;
23777f7322feSeschrock 	zfs_handle_t *zhp;
237899653d4eSeschrock 	char errbuf[1024];
237999653d4eSeschrock 
2380deb8317bSMark J Musante 	(void) snprintf(errbuf, sizeof (errbuf),
2381deb8317bSMark J Musante 	    dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
2382fa9e4066Sahrens 
2383fa9e4066Sahrens 	/* get parent, and check to see if this is just a pool */
2384fa9e4066Sahrens 	if (parent_name(path, parent, sizeof (parent)) != 0) {
238599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
238699653d4eSeschrock 		    "missing dataset name"));
238799653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2388fa9e4066Sahrens 	}
2389fa9e4066Sahrens 
2390fa9e4066Sahrens 	/* check to see if the pool exists */
2391fa9e4066Sahrens 	if ((slash = strchr(parent, '/')) == NULL)
2392fa9e4066Sahrens 		slash = parent + strlen(parent);
2393fa9e4066Sahrens 	(void) strncpy(zc.zc_name, parent, slash - parent);
2394fa9e4066Sahrens 	zc.zc_name[slash - parent] = '\0';
239599653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
2396fa9e4066Sahrens 	    errno == ENOENT) {
239799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
239899653d4eSeschrock 		    "no such pool '%s'"), zc.zc_name);
239999653d4eSeschrock 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
2400fa9e4066Sahrens 	}
2401fa9e4066Sahrens 
2402fa9e4066Sahrens 	/* check to see if the parent dataset exists */
24037f1f55eaSvb 	while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
24047f1f55eaSvb 		if (errno == ENOENT && accept_ancestor) {
24057f1f55eaSvb 			/*
24067f1f55eaSvb 			 * Go deeper to find an ancestor, give up on top level.
24077f1f55eaSvb 			 */
24087f1f55eaSvb 			if (parent_name(parent, parent, sizeof (parent)) != 0) {
24097f1f55eaSvb 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
24107f1f55eaSvb 				    "no such pool '%s'"), zc.zc_name);
24117f1f55eaSvb 				return (zfs_error(hdl, EZFS_NOENT, errbuf));
24127f1f55eaSvb 			}
24137f1f55eaSvb 		} else if (errno == ENOENT) {
241499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
241599653d4eSeschrock 			    "parent does not exist"));
241699653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
24177f1f55eaSvb 		} else
241899653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2419fa9e4066Sahrens 	}
2420fa9e4066Sahrens 
2421e9dbad6fSeschrock 	*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
2422fa9e4066Sahrens 	/* we are in a non-global zone, but parent is in the global zone */
2423e9dbad6fSeschrock 	if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) {
242499653d4eSeschrock 		(void) zfs_standard_error(hdl, EPERM, errbuf);
24257f7322feSeschrock 		zfs_close(zhp);
2426fa9e4066Sahrens 		return (-1);
2427fa9e4066Sahrens 	}
2428fa9e4066Sahrens 
2429fa9e4066Sahrens 	/* make sure parent is a filesystem */
24307f7322feSeschrock 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
243199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
243299653d4eSeschrock 		    "parent is not a filesystem"));
243399653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
24347f7322feSeschrock 		zfs_close(zhp);
2435fa9e4066Sahrens 		return (-1);
2436fa9e4066Sahrens 	}
2437fa9e4066Sahrens 
24387f7322feSeschrock 	zfs_close(zhp);
24397f1f55eaSvb 	if (prefixlen != NULL)
24407f1f55eaSvb 		*prefixlen = strlen(parent);
24417f1f55eaSvb 	return (0);
24427f1f55eaSvb }
24437f1f55eaSvb 
24447f1f55eaSvb /*
24457f1f55eaSvb  * Finds whether the dataset of the given type(s) exists.
24467f1f55eaSvb  */
24477f1f55eaSvb boolean_t
24487f1f55eaSvb zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
24497f1f55eaSvb {
24507f1f55eaSvb 	zfs_handle_t *zhp;
24517f1f55eaSvb 
2452f18faf3fSek 	if (!zfs_validate_name(hdl, path, types, B_FALSE))
24537f1f55eaSvb 		return (B_FALSE);
24547f1f55eaSvb 
24557f1f55eaSvb 	/*
24567f1f55eaSvb 	 * Try to get stats for the dataset, which will tell us if it exists.
24577f1f55eaSvb 	 */
24587f1f55eaSvb 	if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
24597f1f55eaSvb 		int ds_type = zhp->zfs_type;
24607f1f55eaSvb 
24617f1f55eaSvb 		zfs_close(zhp);
24627f1f55eaSvb 		if (types & ds_type)
24637f1f55eaSvb 			return (B_TRUE);
24647f1f55eaSvb 	}
24657f1f55eaSvb 	return (B_FALSE);
24667f1f55eaSvb }
24677f1f55eaSvb 
24683cb34c60Sahrens /*
24693cb34c60Sahrens  * Given a path to 'target', create all the ancestors between
24703cb34c60Sahrens  * the prefixlen portion of the path, and the target itself.
24713cb34c60Sahrens  * Fail if the initial prefixlen-ancestor does not already exist.
24723cb34c60Sahrens  */
24733cb34c60Sahrens int
24743cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
24753cb34c60Sahrens {
24763cb34c60Sahrens 	zfs_handle_t *h;
24773cb34c60Sahrens 	char *cp;
24783cb34c60Sahrens 	const char *opname;
24793cb34c60Sahrens 
24803cb34c60Sahrens 	/* make sure prefix exists */
24813cb34c60Sahrens 	cp = target + prefixlen;
24823cb34c60Sahrens 	if (*cp != '/') {
24833cb34c60Sahrens 		assert(strchr(cp, '/') == NULL);
24843cb34c60Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
24853cb34c60Sahrens 	} else {
24863cb34c60Sahrens 		*cp = '\0';
24873cb34c60Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
24883cb34c60Sahrens 		*cp = '/';
24893cb34c60Sahrens 	}
24903cb34c60Sahrens 	if (h == NULL)
24913cb34c60Sahrens 		return (-1);
24923cb34c60Sahrens 	zfs_close(h);
24933cb34c60Sahrens 
24943cb34c60Sahrens 	/*
24953cb34c60Sahrens 	 * Attempt to create, mount, and share any ancestor filesystems,
24963cb34c60Sahrens 	 * up to the prefixlen-long one.
24973cb34c60Sahrens 	 */
24983cb34c60Sahrens 	for (cp = target + prefixlen + 1;
24993cb34c60Sahrens 	    cp = strchr(cp, '/'); *cp = '/', cp++) {
25003cb34c60Sahrens 		char *logstr;
25013cb34c60Sahrens 
25023cb34c60Sahrens 		*cp = '\0';
25033cb34c60Sahrens 
25043cb34c60Sahrens 		h = make_dataset_handle(hdl, target);
25053cb34c60Sahrens 		if (h) {
25063cb34c60Sahrens 			/* it already exists, nothing to do here */
25073cb34c60Sahrens 			zfs_close(h);
25083cb34c60Sahrens 			continue;
25093cb34c60Sahrens 		}
25103cb34c60Sahrens 
25113cb34c60Sahrens 		logstr = hdl->libzfs_log_str;
25123cb34c60Sahrens 		hdl->libzfs_log_str = NULL;
25133cb34c60Sahrens 		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
25143cb34c60Sahrens 		    NULL) != 0) {
25153cb34c60Sahrens 			hdl->libzfs_log_str = logstr;
25163cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "create");
25173cb34c60Sahrens 			goto ancestorerr;
25183cb34c60Sahrens 		}
25193cb34c60Sahrens 
25203cb34c60Sahrens 		hdl->libzfs_log_str = logstr;
25213cb34c60Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
25223cb34c60Sahrens 		if (h == NULL) {
25233cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "open");
25243cb34c60Sahrens 			goto ancestorerr;
25253cb34c60Sahrens 		}
25263cb34c60Sahrens 
25273cb34c60Sahrens 		if (zfs_mount(h, NULL, 0) != 0) {
25283cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "mount");
25293cb34c60Sahrens 			goto ancestorerr;
25303cb34c60Sahrens 		}
25313cb34c60Sahrens 
25323cb34c60Sahrens 		if (zfs_share(h) != 0) {
25333cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "share");
25343cb34c60Sahrens 			goto ancestorerr;
25353cb34c60Sahrens 		}
25363cb34c60Sahrens 
25373cb34c60Sahrens 		zfs_close(h);
25383cb34c60Sahrens 	}
25393cb34c60Sahrens 
25403cb34c60Sahrens 	return (0);
25413cb34c60Sahrens 
25423cb34c60Sahrens ancestorerr:
25433cb34c60Sahrens 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
25443cb34c60Sahrens 	    "failed to %s ancestor '%s'"), opname, target);
25453cb34c60Sahrens 	return (-1);
25463cb34c60Sahrens }
25473cb34c60Sahrens 
25487f1f55eaSvb /*
25497f1f55eaSvb  * Creates non-existing ancestors of the given path.
25507f1f55eaSvb  */
25517f1f55eaSvb int
25527f1f55eaSvb zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
25537f1f55eaSvb {
25547f1f55eaSvb 	int prefix;
25557f1f55eaSvb 	uint64_t zoned;
25567f1f55eaSvb 	char *path_copy;
25577f1f55eaSvb 	int rc;
25587f1f55eaSvb 
25597f1f55eaSvb 	if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0)
25607f1f55eaSvb 		return (-1);
25617f1f55eaSvb 
25627f1f55eaSvb 	if ((path_copy = strdup(path)) != NULL) {
25637f1f55eaSvb 		rc = create_parents(hdl, path_copy, prefix);
25647f1f55eaSvb 		free(path_copy);
25657f1f55eaSvb 	}
25667f1f55eaSvb 	if (path_copy == NULL || rc != 0)
25677f1f55eaSvb 		return (-1);
25687f1f55eaSvb 
2569fa9e4066Sahrens 	return (0);
2570fa9e4066Sahrens }
2571fa9e4066Sahrens 
2572fa9e4066Sahrens /*
2573e9dbad6fSeschrock  * Create a new filesystem or volume.
2574fa9e4066Sahrens  */
2575fa9e4066Sahrens int
257699653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
2577e9dbad6fSeschrock     nvlist_t *props)
2578fa9e4066Sahrens {
2579fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2580fa9e4066Sahrens 	int ret;
2581fa9e4066Sahrens 	uint64_t size = 0;
2582fa9e4066Sahrens 	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
258399653d4eSeschrock 	char errbuf[1024];
2584e9dbad6fSeschrock 	uint64_t zoned;
258599653d4eSeschrock 
258699653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
258799653d4eSeschrock 	    "cannot create '%s'"), path);
2588fa9e4066Sahrens 
2589fa9e4066Sahrens 	/* validate the path, taking care to note the extended error message */
2590f18faf3fSek 	if (!zfs_validate_name(hdl, path, type, B_TRUE))
259199653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2592fa9e4066Sahrens 
2593fa9e4066Sahrens 	/* validate parents exist */
25947f1f55eaSvb 	if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
2595fa9e4066Sahrens 		return (-1);
2596fa9e4066Sahrens 
2597fa9e4066Sahrens 	/*
2598fa9e4066Sahrens 	 * The failure modes when creating a dataset of a different type over
2599fa9e4066Sahrens 	 * one that already exists is a little strange.  In particular, if you
2600fa9e4066Sahrens 	 * try to create a dataset on top of an existing dataset, the ioctl()
2601fa9e4066Sahrens 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
2602fa9e4066Sahrens 	 * first try to see if the dataset exists.
2603fa9e4066Sahrens 	 */
2604fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
2605990b4856Slling 	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
260699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
260799653d4eSeschrock 		    "dataset already exists"));
260899653d4eSeschrock 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2609fa9e4066Sahrens 	}
2610fa9e4066Sahrens 
2611fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME)
2612fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2613fa9e4066Sahrens 	else
2614fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2615fa9e4066Sahrens 
26160a48a24eStimh 	if (props && (props = zfs_valid_proplist(hdl, type, props,
2617b1b8ab34Slling 	    zoned, NULL, errbuf)) == 0)
2618e9dbad6fSeschrock 		return (-1);
2619e9dbad6fSeschrock 
2620fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME) {
26215c5460e9Seschrock 		/*
26225c5460e9Seschrock 		 * If we are creating a volume, the size and block size must
26235c5460e9Seschrock 		 * satisfy a few restraints.  First, the blocksize must be a
26245c5460e9Seschrock 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
26255c5460e9Seschrock 		 * volsize must be a multiple of the block size, and cannot be
26265c5460e9Seschrock 		 * zero.
26275c5460e9Seschrock 		 */
2628e9dbad6fSeschrock 		if (props == NULL || nvlist_lookup_uint64(props,
2629e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2630e9dbad6fSeschrock 			nvlist_free(props);
263199653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2632e9dbad6fSeschrock 			    "missing volume size"));
2633e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2634e9dbad6fSeschrock 		}
2635e9dbad6fSeschrock 
2636e9dbad6fSeschrock 		if ((ret = nvlist_lookup_uint64(props,
2637e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2638e9dbad6fSeschrock 		    &blocksize)) != 0) {
2639e9dbad6fSeschrock 			if (ret == ENOENT) {
2640e9dbad6fSeschrock 				blocksize = zfs_prop_default_numeric(
2641e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2642e9dbad6fSeschrock 			} else {
2643e9dbad6fSeschrock 				nvlist_free(props);
2644e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2645e9dbad6fSeschrock 				    "missing volume block size"));
2646e9dbad6fSeschrock 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2647e9dbad6fSeschrock 			}
2648fa9e4066Sahrens 		}
2649fa9e4066Sahrens 
2650e9dbad6fSeschrock 		if (size == 0) {
2651e9dbad6fSeschrock 			nvlist_free(props);
265299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2653e9dbad6fSeschrock 			    "volume size cannot be zero"));
2654e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
26555c5460e9Seschrock 		}
26565c5460e9Seschrock 
26575c5460e9Seschrock 		if (size % blocksize != 0) {
2658e9dbad6fSeschrock 			nvlist_free(props);
265999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2660e9dbad6fSeschrock 			    "volume size must be a multiple of volume block "
2661e9dbad6fSeschrock 			    "size"));
2662e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
26635c5460e9Seschrock 		}
2664fa9e4066Sahrens 	}
2665fa9e4066Sahrens 
2666990b4856Slling 	if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
2667e9dbad6fSeschrock 		return (-1);
2668e9dbad6fSeschrock 	nvlist_free(props);
2669e9dbad6fSeschrock 
2670fa9e4066Sahrens 	/* create the dataset */
2671ecd6cf80Smarks 	ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
2672fa9e4066Sahrens 
2673b1b8ab34Slling 	if (ret == 0 && type == ZFS_TYPE_VOLUME) {
267499653d4eSeschrock 		ret = zvol_create_link(hdl, path);
2675b1b8ab34Slling 		if (ret) {
2676b1b8ab34Slling 			(void) zfs_standard_error(hdl, errno,
2677b1b8ab34Slling 			    dgettext(TEXT_DOMAIN,
2678b1b8ab34Slling 			    "Volume successfully created, but device links "
2679b1b8ab34Slling 			    "were not created"));
2680b1b8ab34Slling 			zcmd_free_nvlists(&zc);
2681b1b8ab34Slling 			return (-1);
2682b1b8ab34Slling 		}
2683b1b8ab34Slling 	}
2684fa9e4066Sahrens 
2685e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2686e9dbad6fSeschrock 
2687fa9e4066Sahrens 	/* check for failure */
2688fa9e4066Sahrens 	if (ret != 0) {
2689fa9e4066Sahrens 		char parent[ZFS_MAXNAMELEN];
2690fa9e4066Sahrens 		(void) parent_name(path, parent, sizeof (parent));
2691fa9e4066Sahrens 
2692fa9e4066Sahrens 		switch (errno) {
2693fa9e4066Sahrens 		case ENOENT:
269499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
269599653d4eSeschrock 			    "no such parent '%s'"), parent);
269699653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2697fa9e4066Sahrens 
2698fa9e4066Sahrens 		case EINVAL:
269999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2700d7d4af51Smmusante 			    "parent '%s' is not a filesystem"), parent);
270199653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2702fa9e4066Sahrens 
2703fa9e4066Sahrens 		case EDOM:
270499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2705e9dbad6fSeschrock 			    "volume block size must be power of 2 from "
2706e9dbad6fSeschrock 			    "%u to %uk"),
2707fa9e4066Sahrens 			    (uint_t)SPA_MINBLOCKSIZE,
2708fa9e4066Sahrens 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
270999653d4eSeschrock 
2710e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
271199653d4eSeschrock 
271240feaa91Sahrens 		case ENOTSUP:
271340feaa91Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
271440feaa91Sahrens 			    "pool must be upgraded to set this "
271540feaa91Sahrens 			    "property or value"));
271640feaa91Sahrens 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
2717fa9e4066Sahrens #ifdef _ILP32
2718fa9e4066Sahrens 		case EOVERFLOW:
2719fa9e4066Sahrens 			/*
2720fa9e4066Sahrens 			 * This platform can't address a volume this big.
2721fa9e4066Sahrens 			 */
272299653d4eSeschrock 			if (type == ZFS_TYPE_VOLUME)
272399653d4eSeschrock 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
272499653d4eSeschrock 				    errbuf));
2725fa9e4066Sahrens #endif
272699653d4eSeschrock 			/* FALLTHROUGH */
2727fa9e4066Sahrens 		default:
272899653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2729fa9e4066Sahrens 		}
2730fa9e4066Sahrens 	}
2731fa9e4066Sahrens 
2732fa9e4066Sahrens 	return (0);
2733fa9e4066Sahrens }
2734fa9e4066Sahrens 
2735fa9e4066Sahrens /*
2736fa9e4066Sahrens  * Destroys the given dataset.  The caller must make sure that the filesystem
2737fa9e4066Sahrens  * isn't mounted, and that there are no active dependents.
2738fa9e4066Sahrens  */
2739fa9e4066Sahrens int
2740fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp)
2741fa9e4066Sahrens {
2742fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2743fa9e4066Sahrens 
2744fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2745fa9e4066Sahrens 
2746e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2747f3861e1aSahl 		/*
2748ecd6cf80Smarks 		 * If user doesn't have permissions to unshare volume, then
2749ecd6cf80Smarks 		 * abort the request.  This would only happen for a
2750ecd6cf80Smarks 		 * non-privileged user.
2751f3861e1aSahl 		 */
2752ecd6cf80Smarks 		if (zfs_unshare_iscsi(zhp) != 0) {
2753ecd6cf80Smarks 			return (-1);
2754ecd6cf80Smarks 		}
2755f3861e1aSahl 
275699653d4eSeschrock 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2757fa9e4066Sahrens 			return (-1);
2758fa9e4066Sahrens 
2759fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2760fa9e4066Sahrens 	} else {
2761fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2762fa9e4066Sahrens 	}
2763fa9e4066Sahrens 
2764ecd6cf80Smarks 	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
2765ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
276699653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
276799653d4eSeschrock 		    zhp->zfs_name));
27681d452cf5Sahrens 	}
2769fa9e4066Sahrens 
2770fa9e4066Sahrens 	remove_mountpoint(zhp);
2771fa9e4066Sahrens 
2772fa9e4066Sahrens 	return (0);
2773fa9e4066Sahrens }
2774fa9e4066Sahrens 
27751d452cf5Sahrens struct destroydata {
27761d452cf5Sahrens 	char *snapname;
27771d452cf5Sahrens 	boolean_t gotone;
27783ccfa83cSahrens 	boolean_t closezhp;
27791d452cf5Sahrens };
27801d452cf5Sahrens 
27811d452cf5Sahrens static int
27821d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg)
27831d452cf5Sahrens {
27841d452cf5Sahrens 	struct destroydata *dd = arg;
27851d452cf5Sahrens 	zfs_handle_t *szhp;
27861d452cf5Sahrens 	char name[ZFS_MAXNAMELEN];
27873ccfa83cSahrens 	boolean_t closezhp = dd->closezhp;
27883ccfa83cSahrens 	int rv;
27891d452cf5Sahrens 
2790e9dbad6fSeschrock 	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2791e9dbad6fSeschrock 	(void) strlcat(name, "@", sizeof (name));
2792e9dbad6fSeschrock 	(void) strlcat(name, dd->snapname, sizeof (name));
27931d452cf5Sahrens 
27941d452cf5Sahrens 	szhp = make_dataset_handle(zhp->zfs_hdl, name);
27951d452cf5Sahrens 	if (szhp) {
27961d452cf5Sahrens 		dd->gotone = B_TRUE;
27971d452cf5Sahrens 		zfs_close(szhp);
27981d452cf5Sahrens 	}
27991d452cf5Sahrens 
28001d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
28011d452cf5Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, name);
28021d452cf5Sahrens 		/*
28031d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
28041d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
28051d452cf5Sahrens 		 * the volumes.
28061d452cf5Sahrens 		 */
28071d452cf5Sahrens 	}
28081d452cf5Sahrens 
28093ccfa83cSahrens 	dd->closezhp = B_TRUE;
28103ccfa83cSahrens 	rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg);
28113ccfa83cSahrens 	if (closezhp)
28123ccfa83cSahrens 		zfs_close(zhp);
28133ccfa83cSahrens 	return (rv);
28141d452cf5Sahrens }
28151d452cf5Sahrens 
28161d452cf5Sahrens /*
28171d452cf5Sahrens  * Destroys all snapshots with the given name in zhp & descendants.
28181d452cf5Sahrens  */
28191d452cf5Sahrens int
28201d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname)
28211d452cf5Sahrens {
28221d452cf5Sahrens 	zfs_cmd_t zc = { 0 };
28231d452cf5Sahrens 	int ret;
28241d452cf5Sahrens 	struct destroydata dd = { 0 };
28251d452cf5Sahrens 
28261d452cf5Sahrens 	dd.snapname = snapname;
28271d452cf5Sahrens 	(void) zfs_remove_link_cb(zhp, &dd);
28281d452cf5Sahrens 
28291d452cf5Sahrens 	if (!dd.gotone) {
2830ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
28311d452cf5Sahrens 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
28321d452cf5Sahrens 		    zhp->zfs_name, snapname));
28331d452cf5Sahrens 	}
28341d452cf5Sahrens 
28351d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2836e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
28371d452cf5Sahrens 
2838ecd6cf80Smarks 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
28391d452cf5Sahrens 	if (ret != 0) {
28401d452cf5Sahrens 		char errbuf[1024];
28411d452cf5Sahrens 
28421d452cf5Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
28431d452cf5Sahrens 		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
28441d452cf5Sahrens 
28451d452cf5Sahrens 		switch (errno) {
28461d452cf5Sahrens 		case EEXIST:
28471d452cf5Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
28481d452cf5Sahrens 			    "snapshot is cloned"));
28491d452cf5Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
28501d452cf5Sahrens 
28511d452cf5Sahrens 		default:
28521d452cf5Sahrens 			return (zfs_standard_error(zhp->zfs_hdl, errno,
28531d452cf5Sahrens 			    errbuf));
28541d452cf5Sahrens 		}
28551d452cf5Sahrens 	}
28561d452cf5Sahrens 
28571d452cf5Sahrens 	return (0);
28581d452cf5Sahrens }
28591d452cf5Sahrens 
2860fa9e4066Sahrens /*
2861fa9e4066Sahrens  * Clones the given dataset.  The target must be of the same type as the source.
2862fa9e4066Sahrens  */
2863fa9e4066Sahrens int
2864e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
2865fa9e4066Sahrens {
2866fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2867fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2868fa9e4066Sahrens 	int ret;
286999653d4eSeschrock 	char errbuf[1024];
287099653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
2871e9dbad6fSeschrock 	zfs_type_t type;
2872e9dbad6fSeschrock 	uint64_t zoned;
2873fa9e4066Sahrens 
2874fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
2875fa9e4066Sahrens 
287699653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
287799653d4eSeschrock 	    "cannot create '%s'"), target);
287899653d4eSeschrock 
2879fa9e4066Sahrens 	/* validate the target name */
2880f18faf3fSek 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
288199653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2882fa9e4066Sahrens 
2883fa9e4066Sahrens 	/* validate parents exist */
28847f1f55eaSvb 	if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
2885fa9e4066Sahrens 		return (-1);
2886fa9e4066Sahrens 
2887fa9e4066Sahrens 	(void) parent_name(target, parent, sizeof (parent));
2888fa9e4066Sahrens 
2889fa9e4066Sahrens 	/* do the clone */
2890e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2891fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
28925f8e1617Snn 		type = ZFS_TYPE_VOLUME;
2893e9dbad6fSeschrock 	} else {
2894fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
28955f8e1617Snn 		type = ZFS_TYPE_FILESYSTEM;
2896e9dbad6fSeschrock 	}
2897e9dbad6fSeschrock 
2898e9dbad6fSeschrock 	if (props) {
28990a48a24eStimh 		if ((props = zfs_valid_proplist(hdl, type, props, zoned,
29000a48a24eStimh 		    zhp, errbuf)) == NULL)
2901e9dbad6fSeschrock 			return (-1);
2902e9dbad6fSeschrock 
2903990b4856Slling 		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
2904e9dbad6fSeschrock 			nvlist_free(props);
2905e9dbad6fSeschrock 			return (-1);
2906e9dbad6fSeschrock 		}
2907e9dbad6fSeschrock 
2908e9dbad6fSeschrock 		nvlist_free(props);
2909e9dbad6fSeschrock 	}
2910fa9e4066Sahrens 
2911fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
2912e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
2913ecd6cf80Smarks 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
2914fa9e4066Sahrens 
2915e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2916e9dbad6fSeschrock 
2917fa9e4066Sahrens 	if (ret != 0) {
2918fa9e4066Sahrens 		switch (errno) {
2919fa9e4066Sahrens 
2920fa9e4066Sahrens 		case ENOENT:
2921fa9e4066Sahrens 			/*
2922fa9e4066Sahrens 			 * The parent doesn't exist.  We should have caught this
2923fa9e4066Sahrens 			 * above, but there may a race condition that has since
2924fa9e4066Sahrens 			 * destroyed the parent.
2925fa9e4066Sahrens 			 *
2926fa9e4066Sahrens 			 * At this point, we don't know whether it's the source
2927fa9e4066Sahrens 			 * that doesn't exist anymore, or whether the target
2928fa9e4066Sahrens 			 * dataset doesn't exist.
2929fa9e4066Sahrens 			 */
293099653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
293199653d4eSeschrock 			    "no such parent '%s'"), parent);
293299653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
2933fa9e4066Sahrens 
293499653d4eSeschrock 		case EXDEV:
293599653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
293699653d4eSeschrock 			    "source and target pools differ"));
293799653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
293899653d4eSeschrock 			    errbuf));
2939fa9e4066Sahrens 
294099653d4eSeschrock 		default:
294199653d4eSeschrock 			return (zfs_standard_error(zhp->zfs_hdl, errno,
294299653d4eSeschrock 			    errbuf));
294399653d4eSeschrock 		}
2944e9dbad6fSeschrock 	} else if (ZFS_IS_VOLUME(zhp)) {
294599653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, target);
294699653d4eSeschrock 	}
2947fa9e4066Sahrens 
294899653d4eSeschrock 	return (ret);
294999653d4eSeschrock }
295099653d4eSeschrock 
295199653d4eSeschrock typedef struct promote_data {
295299653d4eSeschrock 	char cb_mountpoint[MAXPATHLEN];
295399653d4eSeschrock 	const char *cb_target;
295499653d4eSeschrock 	const char *cb_errbuf;
295599653d4eSeschrock 	uint64_t cb_pivot_txg;
295699653d4eSeschrock } promote_data_t;
295799653d4eSeschrock 
295899653d4eSeschrock static int
295999653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data)
296099653d4eSeschrock {
296199653d4eSeschrock 	promote_data_t *pd = data;
296299653d4eSeschrock 	zfs_handle_t *szhp;
296399653d4eSeschrock 	char snapname[MAXPATHLEN];
29643ccfa83cSahrens 	int rv = 0;
296599653d4eSeschrock 
296699653d4eSeschrock 	/* We don't care about snapshots after the pivot point */
29673ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) {
29683ccfa83cSahrens 		zfs_close(zhp);
296999653d4eSeschrock 		return (0);
29703ccfa83cSahrens 	}
297199653d4eSeschrock 
29720b69c2f0Sahrens 	/* Remove the device link if it's a zvol. */
2973e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
29740b69c2f0Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
297599653d4eSeschrock 
297699653d4eSeschrock 	/* Check for conflicting names */
2977e9dbad6fSeschrock 	(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
2978e9dbad6fSeschrock 	(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
297999653d4eSeschrock 	szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
298099653d4eSeschrock 	if (szhp != NULL) {
298199653d4eSeschrock 		zfs_close(szhp);
298299653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
298399653d4eSeschrock 		    "snapshot name '%s' from origin \n"
298499653d4eSeschrock 		    "conflicts with '%s' from target"),
298599653d4eSeschrock 		    zhp->zfs_name, snapname);
29863ccfa83cSahrens 		rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf);
298799653d4eSeschrock 	}
29883ccfa83cSahrens 	zfs_close(zhp);
29893ccfa83cSahrens 	return (rv);
299099653d4eSeschrock }
299199653d4eSeschrock 
29920b69c2f0Sahrens static int
29930b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data)
29940b69c2f0Sahrens {
29950b69c2f0Sahrens 	promote_data_t *pd = data;
29960b69c2f0Sahrens 
29970b69c2f0Sahrens 	/* We don't care about snapshots after the pivot point */
29983ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) {
29993ccfa83cSahrens 		/* Create the device link if it's a zvol. */
30003ccfa83cSahrens 		if (ZFS_IS_VOLUME(zhp))
30013ccfa83cSahrens 			(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
30023ccfa83cSahrens 	}
30030b69c2f0Sahrens 
30043ccfa83cSahrens 	zfs_close(zhp);
30050b69c2f0Sahrens 	return (0);
30060b69c2f0Sahrens }
30070b69c2f0Sahrens 
300899653d4eSeschrock /*
300999653d4eSeschrock  * Promotes the given clone fs to be the clone parent.
301099653d4eSeschrock  */
301199653d4eSeschrock int
301299653d4eSeschrock zfs_promote(zfs_handle_t *zhp)
301399653d4eSeschrock {
301499653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
301599653d4eSeschrock 	zfs_cmd_t zc = { 0 };
301699653d4eSeschrock 	char parent[MAXPATHLEN];
301799653d4eSeschrock 	char *cp;
301899653d4eSeschrock 	int ret;
301999653d4eSeschrock 	zfs_handle_t *pzhp;
302099653d4eSeschrock 	promote_data_t pd;
302199653d4eSeschrock 	char errbuf[1024];
302299653d4eSeschrock 
302399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
302499653d4eSeschrock 	    "cannot promote '%s'"), zhp->zfs_name);
302599653d4eSeschrock 
302699653d4eSeschrock 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
302799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
302899653d4eSeschrock 		    "snapshots can not be promoted"));
302999653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
303099653d4eSeschrock 	}
303199653d4eSeschrock 
30323cb34c60Sahrens 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
303399653d4eSeschrock 	if (parent[0] == '\0') {
303499653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
303599653d4eSeschrock 		    "not a cloned filesystem"));
303699653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
303799653d4eSeschrock 	}
303899653d4eSeschrock 	cp = strchr(parent, '@');
303999653d4eSeschrock 	*cp = '\0';
304099653d4eSeschrock 
304199653d4eSeschrock 	/* Walk the snapshots we will be moving */
30423cb34c60Sahrens 	pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
304399653d4eSeschrock 	if (pzhp == NULL)
304499653d4eSeschrock 		return (-1);
304599653d4eSeschrock 	pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
304699653d4eSeschrock 	zfs_close(pzhp);
304799653d4eSeschrock 	pd.cb_target = zhp->zfs_name;
304899653d4eSeschrock 	pd.cb_errbuf = errbuf;
3049990b4856Slling 	pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET);
305099653d4eSeschrock 	if (pzhp == NULL)
305199653d4eSeschrock 		return (-1);
305299653d4eSeschrock 	(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
305399653d4eSeschrock 	    sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
305499653d4eSeschrock 	ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
30550b69c2f0Sahrens 	if (ret != 0) {
30560b69c2f0Sahrens 		zfs_close(pzhp);
305799653d4eSeschrock 		return (-1);
30580b69c2f0Sahrens 	}
305999653d4eSeschrock 
306099653d4eSeschrock 	/* issue the ioctl */
30613cb34c60Sahrens 	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
3062e9dbad6fSeschrock 	    sizeof (zc.zc_value));
306399653d4eSeschrock 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3064ecd6cf80Smarks 	ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
306599653d4eSeschrock 
306699653d4eSeschrock 	if (ret != 0) {
30670b69c2f0Sahrens 		int save_errno = errno;
30680b69c2f0Sahrens 
30690b69c2f0Sahrens 		(void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
30700b69c2f0Sahrens 		zfs_close(pzhp);
307199653d4eSeschrock 
30720b69c2f0Sahrens 		switch (save_errno) {
307399653d4eSeschrock 		case EEXIST:
3074fa9e4066Sahrens 			/*
307599653d4eSeschrock 			 * There is a conflicting snapshot name.  We
307699653d4eSeschrock 			 * should have caught this above, but they could
307799653d4eSeschrock 			 * have renamed something in the mean time.
3078fa9e4066Sahrens 			 */
307999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
308099653d4eSeschrock 			    "conflicting snapshot name from parent '%s'"),
308199653d4eSeschrock 			    parent);
308299653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
3083fa9e4066Sahrens 
3084fa9e4066Sahrens 		default:
30850b69c2f0Sahrens 			return (zfs_standard_error(hdl, save_errno, errbuf));
3086fa9e4066Sahrens 		}
30870b69c2f0Sahrens 	} else {
30880b69c2f0Sahrens 		(void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
3089fa9e4066Sahrens 	}
3090fa9e4066Sahrens 
30910b69c2f0Sahrens 	zfs_close(pzhp);
3092fa9e4066Sahrens 	return (ret);
3093fa9e4066Sahrens }
3094fa9e4066Sahrens 
3095cdf5b4caSmmusante struct createdata {
3096cdf5b4caSmmusante 	const char *cd_snapname;
3097cdf5b4caSmmusante 	int cd_ifexists;
3098cdf5b4caSmmusante };
3099cdf5b4caSmmusante 
31001d452cf5Sahrens static int
31011d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
31021d452cf5Sahrens {
3103cdf5b4caSmmusante 	struct createdata *cd = arg;
3104e9dbad6fSeschrock 	int ret;
31051d452cf5Sahrens 
31061d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
31071d452cf5Sahrens 		char name[MAXPATHLEN];
31081d452cf5Sahrens 
3109e9dbad6fSeschrock 		(void) strlcpy(name, zhp->zfs_name, sizeof (name));
3110e9dbad6fSeschrock 		(void) strlcat(name, "@", sizeof (name));
3111cdf5b4caSmmusante 		(void) strlcat(name, cd->cd_snapname, sizeof (name));
3112cdf5b4caSmmusante 		(void) zvol_create_link_common(zhp->zfs_hdl, name,
3113cdf5b4caSmmusante 		    cd->cd_ifexists);
31141d452cf5Sahrens 		/*
31151d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
31161d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
31171d452cf5Sahrens 		 * the volumes.
31181d452cf5Sahrens 		 */
31191d452cf5Sahrens 	}
3120e9dbad6fSeschrock 
3121cdf5b4caSmmusante 	ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd);
3122e9dbad6fSeschrock 
3123e9dbad6fSeschrock 	zfs_close(zhp);
3124e9dbad6fSeschrock 
3125e9dbad6fSeschrock 	return (ret);
31261d452cf5Sahrens }
31271d452cf5Sahrens 
3128fa9e4066Sahrens /*
312972bdce51Sahl  * Takes a snapshot of the given dataset.
3130fa9e4066Sahrens  */
3131fa9e4066Sahrens int
3132bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
3133bb0ade09Sahrens     nvlist_t *props)
3134fa9e4066Sahrens {
3135fa9e4066Sahrens 	const char *delim;
3136bb0ade09Sahrens 	char parent[ZFS_MAXNAMELEN];
3137fa9e4066Sahrens 	zfs_handle_t *zhp;
3138fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3139fa9e4066Sahrens 	int ret;
314099653d4eSeschrock 	char errbuf[1024];
3141fa9e4066Sahrens 
314299653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
314399653d4eSeschrock 	    "cannot snapshot '%s'"), path);
314499653d4eSeschrock 
314599653d4eSeschrock 	/* validate the target name */
3146f18faf3fSek 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
314799653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3148fa9e4066Sahrens 
3149bb0ade09Sahrens 	if (props) {
3150bb0ade09Sahrens 		if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
3151bb0ade09Sahrens 		    props, B_FALSE, NULL, errbuf)) == NULL)
3152bb0ade09Sahrens 			return (-1);
3153bb0ade09Sahrens 
3154bb0ade09Sahrens 		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3155bb0ade09Sahrens 			nvlist_free(props);
3156bb0ade09Sahrens 			return (-1);
3157bb0ade09Sahrens 		}
3158bb0ade09Sahrens 
3159bb0ade09Sahrens 		nvlist_free(props);
3160bb0ade09Sahrens 	}
3161bb0ade09Sahrens 
3162fa9e4066Sahrens 	/* make sure the parent exists and is of the appropriate type */
31631d452cf5Sahrens 	delim = strchr(path, '@');
3164fa9e4066Sahrens 	(void) strncpy(parent, path, delim - path);
3165fa9e4066Sahrens 	parent[delim - path] = '\0';
3166fa9e4066Sahrens 
316799653d4eSeschrock 	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
3168fa9e4066Sahrens 	    ZFS_TYPE_VOLUME)) == NULL) {
3169bb0ade09Sahrens 		zcmd_free_nvlists(&zc);
3170fa9e4066Sahrens 		return (-1);
3171fa9e4066Sahrens 	}
3172fa9e4066Sahrens 
31731d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3174e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
3175ecd6cf80Smarks 	if (ZFS_IS_VOLUME(zhp))
3176ecd6cf80Smarks 		zc.zc_objset_type = DMU_OST_ZVOL;
3177ecd6cf80Smarks 	else
3178ecd6cf80Smarks 		zc.zc_objset_type = DMU_OST_ZFS;
31791d452cf5Sahrens 	zc.zc_cookie = recursive;
3180ecd6cf80Smarks 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
3181fa9e4066Sahrens 
3182bb0ade09Sahrens 	zcmd_free_nvlists(&zc);
3183bb0ade09Sahrens 
31841d452cf5Sahrens 	/*
31851d452cf5Sahrens 	 * if it was recursive, the one that actually failed will be in
31861d452cf5Sahrens 	 * zc.zc_name.
31871d452cf5Sahrens 	 */
3188ecd6cf80Smarks 	if (ret != 0)
3189ecd6cf80Smarks 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3190ecd6cf80Smarks 		    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
3191ecd6cf80Smarks 
31921d452cf5Sahrens 	if (ret == 0 && recursive) {
3193cdf5b4caSmmusante 		struct createdata cd;
3194cdf5b4caSmmusante 
3195cdf5b4caSmmusante 		cd.cd_snapname = delim + 1;
3196cdf5b4caSmmusante 		cd.cd_ifexists = B_FALSE;
3197cdf5b4caSmmusante 		(void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd);
31981d452cf5Sahrens 	}
3199fa9e4066Sahrens 	if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
320099653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, path);
32011d452cf5Sahrens 		if (ret != 0) {
3202ecd6cf80Smarks 			(void) zfs_standard_error(hdl, errno,
3203ecd6cf80Smarks 			    dgettext(TEXT_DOMAIN,
3204ecd6cf80Smarks 			    "Volume successfully snapshotted, but device links "
3205ecd6cf80Smarks 			    "were not created"));
3206ecd6cf80Smarks 			zfs_close(zhp);
3207ecd6cf80Smarks 			return (-1);
32081d452cf5Sahrens 		}
3209fa9e4066Sahrens 	}
3210fa9e4066Sahrens 
321199653d4eSeschrock 	if (ret != 0)
321299653d4eSeschrock 		(void) zfs_standard_error(hdl, errno, errbuf);
3213fa9e4066Sahrens 
3214fa9e4066Sahrens 	zfs_close(zhp);
3215fa9e4066Sahrens 
3216fa9e4066Sahrens 	return (ret);
3217fa9e4066Sahrens }
3218fa9e4066Sahrens 
3219fa9e4066Sahrens /*
3220b12a1c38Slling  * Destroy any more recent snapshots.  We invoke this callback on any dependents
3221b12a1c38Slling  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
3222b12a1c38Slling  * is a dependent and we should just destroy it without checking the transaction
3223b12a1c38Slling  * group.
3224fa9e4066Sahrens  */
3225b12a1c38Slling typedef struct rollback_data {
3226b12a1c38Slling 	const char	*cb_target;		/* the snapshot */
3227b12a1c38Slling 	uint64_t	cb_create;		/* creation time reference */
3228c391e322Sahrens 	boolean_t	cb_error;
322999653d4eSeschrock 	boolean_t	cb_dependent;
3230c391e322Sahrens 	boolean_t	cb_force;
3231b12a1c38Slling } rollback_data_t;
3232b12a1c38Slling 
3233b12a1c38Slling static int
3234b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data)
3235b12a1c38Slling {
3236b12a1c38Slling 	rollback_data_t *cbp = data;
3237b12a1c38Slling 
3238b12a1c38Slling 	if (!cbp->cb_dependent) {
3239b12a1c38Slling 		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
3240b12a1c38Slling 		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3241b12a1c38Slling 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3242b12a1c38Slling 		    cbp->cb_create) {
3243ecd6cf80Smarks 			char *logstr;
3244b12a1c38Slling 
324599653d4eSeschrock 			cbp->cb_dependent = B_TRUE;
32464ccbb6e7Sahrens 			cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
32474ccbb6e7Sahrens 			    rollback_destroy, cbp);
324899653d4eSeschrock 			cbp->cb_dependent = B_FALSE;
3249b12a1c38Slling 
3250ecd6cf80Smarks 			logstr = zhp->zfs_hdl->libzfs_log_str;
3251ecd6cf80Smarks 			zhp->zfs_hdl->libzfs_log_str = NULL;
32524ccbb6e7Sahrens 			cbp->cb_error |= zfs_destroy(zhp);
3253ecd6cf80Smarks 			zhp->zfs_hdl->libzfs_log_str = logstr;
3254b12a1c38Slling 		}
3255b12a1c38Slling 	} else {
3256c391e322Sahrens 		/* We must destroy this clone; first unmount it */
3257c391e322Sahrens 		prop_changelist_t *clp;
3258c391e322Sahrens 
32590069fd67STim Haley 		clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3260c391e322Sahrens 		    cbp->cb_force ? MS_FORCE: 0);
3261c391e322Sahrens 		if (clp == NULL || changelist_prefix(clp) != 0) {
3262c391e322Sahrens 			cbp->cb_error = B_TRUE;
3263c391e322Sahrens 			zfs_close(zhp);
3264c391e322Sahrens 			return (0);
3265c391e322Sahrens 		}
3266c391e322Sahrens 		if (zfs_destroy(zhp) != 0)
3267c391e322Sahrens 			cbp->cb_error = B_TRUE;
3268c391e322Sahrens 		else
3269c391e322Sahrens 			changelist_remove(clp, zhp->zfs_name);
3270ba7b046eSahrens 		(void) changelist_postfix(clp);
3271c391e322Sahrens 		changelist_free(clp);
3272b12a1c38Slling 	}
3273b12a1c38Slling 
3274b12a1c38Slling 	zfs_close(zhp);
3275b12a1c38Slling 	return (0);
3276b12a1c38Slling }
3277b12a1c38Slling 
3278b12a1c38Slling /*
3279b12a1c38Slling  * Given a dataset, rollback to a specific snapshot, discarding any
3280b12a1c38Slling  * data changes since then and making it the active dataset.
3281b12a1c38Slling  *
3282b12a1c38Slling  * Any snapshots more recent than the target are destroyed, along with
3283b12a1c38Slling  * their dependents.
3284b12a1c38Slling  */
3285b12a1c38Slling int
3286c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
3287b12a1c38Slling {
3288b12a1c38Slling 	rollback_data_t cb = { 0 };
32894ccbb6e7Sahrens 	int err;
32904ccbb6e7Sahrens 	zfs_cmd_t zc = { 0 };
32917b97dc1aSrm 	boolean_t restore_resv = 0;
32927b97dc1aSrm 	uint64_t old_volsize, new_volsize;
32937b97dc1aSrm 	zfs_prop_t resv_prop;
3294b12a1c38Slling 
32954ccbb6e7Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
32964ccbb6e7Sahrens 	    zhp->zfs_type == ZFS_TYPE_VOLUME);
3297b12a1c38Slling 
3298b12a1c38Slling 	/*
3299b12a1c38Slling 	 * Destroy all recent snapshots and its dependends.
3300b12a1c38Slling 	 */
3301c391e322Sahrens 	cb.cb_force = force;
3302b12a1c38Slling 	cb.cb_target = snap->zfs_name;
3303b12a1c38Slling 	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3304b12a1c38Slling 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3305b12a1c38Slling 
3306c391e322Sahrens 	if (cb.cb_error)
3307c391e322Sahrens 		return (-1);
3308b12a1c38Slling 
3309b12a1c38Slling 	/*
3310b12a1c38Slling 	 * Now that we have verified that the snapshot is the latest,
3311b12a1c38Slling 	 * rollback to the given snapshot.
3312b12a1c38Slling 	 */
3313b12a1c38Slling 
33147b97dc1aSrm 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
33157b97dc1aSrm 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
33167b97dc1aSrm 			return (-1);
33177b97dc1aSrm 		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
33187b97dc1aSrm 			return (-1);
33197b97dc1aSrm 		old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
33207b97dc1aSrm 		restore_resv =
33217b97dc1aSrm 		    (old_volsize == zfs_prop_get_int(zhp, resv_prop));
33227b97dc1aSrm 	}
33234ccbb6e7Sahrens 
33244ccbb6e7Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
33254ccbb6e7Sahrens 
33264ccbb6e7Sahrens 	if (ZFS_IS_VOLUME(zhp))
33274ccbb6e7Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
33284ccbb6e7Sahrens 	else
33294ccbb6e7Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3330b12a1c38Slling 
3331b12a1c38Slling 	/*
33324ccbb6e7Sahrens 	 * We rely on zfs_iter_children() to verify that there are no
33334ccbb6e7Sahrens 	 * newer snapshots for the given dataset.  Therefore, we can
33344ccbb6e7Sahrens 	 * simply pass the name on to the ioctl() call.  There is still
33354ccbb6e7Sahrens 	 * an unlikely race condition where the user has taken a
33364ccbb6e7Sahrens 	 * snapshot since we verified that this was the most recent.
33377b97dc1aSrm 	 *
3338b12a1c38Slling 	 */
33394ccbb6e7Sahrens 	if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
33404ccbb6e7Sahrens 		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
33414ccbb6e7Sahrens 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
33424ccbb6e7Sahrens 		    zhp->zfs_name);
3343b9415e83Srm 		return (err);
3344b9415e83Srm 	}
33457b97dc1aSrm 
33467b97dc1aSrm 	/*
33477b97dc1aSrm 	 * For volumes, if the pre-rollback volsize matched the pre-
33487b97dc1aSrm 	 * rollback reservation and the volsize has changed then set
33497b97dc1aSrm 	 * the reservation property to the post-rollback volsize.
33507b97dc1aSrm 	 * Make a new handle since the rollback closed the dataset.
33517b97dc1aSrm 	 */
3352b9415e83Srm 	if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
3353b9415e83Srm 	    (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
3354b9415e83Srm 		if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) {
3355b9415e83Srm 			zfs_close(zhp);
33567b97dc1aSrm 			return (err);
3357b9415e83Srm 		}
33587b97dc1aSrm 		if (restore_resv) {
33597b97dc1aSrm 			new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
33607b97dc1aSrm 			if (old_volsize != new_volsize)
3361b9415e83Srm 				err = zfs_prop_set_int(zhp, resv_prop,
3362b9415e83Srm 				    new_volsize);
33637b97dc1aSrm 		}
33647b97dc1aSrm 		zfs_close(zhp);
33654ccbb6e7Sahrens 	}
33664ccbb6e7Sahrens 	return (err);
3367b12a1c38Slling }
3368b12a1c38Slling 
3369fa9e4066Sahrens /*
3370fa9e4066Sahrens  * Iterate over all dependents for a given dataset.  This includes both
3371fa9e4066Sahrens  * hierarchical dependents (children) and data dependents (snapshots and
3372fa9e4066Sahrens  * clones).  The bulk of the processing occurs in get_dependents() in
3373fa9e4066Sahrens  * libzfs_graph.c.
3374fa9e4066Sahrens  */
3375fa9e4066Sahrens int
33763bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
33773bb79becSeschrock     zfs_iter_f func, void *data)
3378fa9e4066Sahrens {
3379fa9e4066Sahrens 	char **dependents;
3380fa9e4066Sahrens 	size_t count;
3381fa9e4066Sahrens 	int i;
3382fa9e4066Sahrens 	zfs_handle_t *child;
3383fa9e4066Sahrens 	int ret = 0;
3384fa9e4066Sahrens 
33853bb79becSeschrock 	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
33863bb79becSeschrock 	    &dependents, &count) != 0)
33873bb79becSeschrock 		return (-1);
33883bb79becSeschrock 
3389fa9e4066Sahrens 	for (i = 0; i < count; i++) {
339099653d4eSeschrock 		if ((child = make_dataset_handle(zhp->zfs_hdl,
339199653d4eSeschrock 		    dependents[i])) == NULL)
3392fa9e4066Sahrens 			continue;
3393fa9e4066Sahrens 
3394fa9e4066Sahrens 		if ((ret = func(child, data)) != 0)
3395fa9e4066Sahrens 			break;
3396fa9e4066Sahrens 	}
3397fa9e4066Sahrens 
3398fa9e4066Sahrens 	for (i = 0; i < count; i++)
3399fa9e4066Sahrens 		free(dependents[i]);
3400fa9e4066Sahrens 	free(dependents);
3401fa9e4066Sahrens 
3402fa9e4066Sahrens 	return (ret);
3403fa9e4066Sahrens }
3404fa9e4066Sahrens 
3405fa9e4066Sahrens /*
3406fa9e4066Sahrens  * Renames the given dataset.
3407fa9e4066Sahrens  */
3408fa9e4066Sahrens int
34097f1f55eaSvb zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
3410fa9e4066Sahrens {
3411fa9e4066Sahrens 	int ret;
3412fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3413fa9e4066Sahrens 	char *delim;
3414cdf5b4caSmmusante 	prop_changelist_t *cl = NULL;
3415cdf5b4caSmmusante 	zfs_handle_t *zhrp = NULL;
3416cdf5b4caSmmusante 	char *parentname = NULL;
3417fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
341899653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
341999653d4eSeschrock 	char errbuf[1024];
3420fa9e4066Sahrens 
3421fa9e4066Sahrens 	/* if we have the same exact name, just return success */
3422fa9e4066Sahrens 	if (strcmp(zhp->zfs_name, target) == 0)
3423fa9e4066Sahrens 		return (0);
3424fa9e4066Sahrens 
342599653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
342699653d4eSeschrock 	    "cannot rename to '%s'"), target);
342799653d4eSeschrock 
3428fa9e4066Sahrens 	/*
3429fa9e4066Sahrens 	 * Make sure the target name is valid
3430fa9e4066Sahrens 	 */
3431fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
343298579b20Snd 		if ((strchr(target, '@') == NULL) ||
343398579b20Snd 		    *target == '@') {
343498579b20Snd 			/*
343598579b20Snd 			 * Snapshot target name is abbreviated,
343698579b20Snd 			 * reconstruct full dataset name
343798579b20Snd 			 */
343898579b20Snd 			(void) strlcpy(parent, zhp->zfs_name,
343998579b20Snd 			    sizeof (parent));
344098579b20Snd 			delim = strchr(parent, '@');
344198579b20Snd 			if (strchr(target, '@') == NULL)
344298579b20Snd 				*(++delim) = '\0';
344398579b20Snd 			else
344498579b20Snd 				*delim = '\0';
344598579b20Snd 			(void) strlcat(parent, target, sizeof (parent));
344698579b20Snd 			target = parent;
344798579b20Snd 		} else {
344898579b20Snd 			/*
344998579b20Snd 			 * Make sure we're renaming within the same dataset.
345098579b20Snd 			 */
345198579b20Snd 			delim = strchr(target, '@');
345298579b20Snd 			if (strncmp(zhp->zfs_name, target, delim - target)
345398579b20Snd 			    != 0 || zhp->zfs_name[delim - target] != '@') {
345498579b20Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
345598579b20Snd 				    "snapshots must be part of same "
345698579b20Snd 				    "dataset"));
345798579b20Snd 				return (zfs_error(hdl, EZFS_CROSSTARGET,
3458b1b8ab34Slling 				    errbuf));
345998579b20Snd 			}
3460fa9e4066Sahrens 		}
3461f18faf3fSek 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
346298579b20Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3463fa9e4066Sahrens 	} else {
3464cdf5b4caSmmusante 		if (recursive) {
3465cdf5b4caSmmusante 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3466cdf5b4caSmmusante 			    "recursive rename must be a snapshot"));
3467cdf5b4caSmmusante 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3468cdf5b4caSmmusante 		}
3469cdf5b4caSmmusante 
3470f18faf3fSek 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
347198579b20Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3472e9dbad6fSeschrock 		uint64_t unused;
3473e9dbad6fSeschrock 
3474fa9e4066Sahrens 		/* validate parents */
34757f1f55eaSvb 		if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0)
3476fa9e4066Sahrens 			return (-1);
3477fa9e4066Sahrens 
3478fa9e4066Sahrens 		(void) parent_name(target, parent, sizeof (parent));
3479fa9e4066Sahrens 
3480fa9e4066Sahrens 		/* make sure we're in the same pool */
3481fa9e4066Sahrens 		verify((delim = strchr(target, '/')) != NULL);
3482fa9e4066Sahrens 		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3483fa9e4066Sahrens 		    zhp->zfs_name[delim - target] != '/') {
348499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
348599653d4eSeschrock 			    "datasets must be within same pool"));
348699653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3487fa9e4066Sahrens 		}
3488f2fdf992Snd 
3489f2fdf992Snd 		/* new name cannot be a child of the current dataset name */
3490f2fdf992Snd 		if (strncmp(parent, zhp->zfs_name,
3491b1b8ab34Slling 		    strlen(zhp->zfs_name)) == 0) {
3492f2fdf992Snd 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3493f2fdf992Snd 			    "New dataset name cannot be a descendent of "
3494f2fdf992Snd 			    "current dataset name"));
3495f2fdf992Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3496f2fdf992Snd 		}
3497fa9e4066Sahrens 	}
3498fa9e4066Sahrens 
349999653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
350099653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
350199653d4eSeschrock 
3502fa9e4066Sahrens 	if (getzoneid() == GLOBAL_ZONEID &&
3503fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
350499653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
350599653d4eSeschrock 		    "dataset is used in a non-global zone"));
350699653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3507fa9e4066Sahrens 	}
3508fa9e4066Sahrens 
3509cdf5b4caSmmusante 	if (recursive) {
3510cdf5b4caSmmusante 		struct destroydata dd;
3511fa9e4066Sahrens 
3512f0c5ee21Smmusante 		parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
3513f0c5ee21Smmusante 		if (parentname == NULL) {
3514f0c5ee21Smmusante 			ret = -1;
3515f0c5ee21Smmusante 			goto error;
3516f0c5ee21Smmusante 		}
3517cdf5b4caSmmusante 		delim = strchr(parentname, '@');
3518cdf5b4caSmmusante 		*delim = '\0';
3519990b4856Slling 		zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
3520cdf5b4caSmmusante 		if (zhrp == NULL) {
3521f0c5ee21Smmusante 			ret = -1;
3522f0c5ee21Smmusante 			goto error;
3523cdf5b4caSmmusante 		}
3524fa9e4066Sahrens 
3525cdf5b4caSmmusante 		dd.snapname = delim + 1;
3526cdf5b4caSmmusante 		dd.gotone = B_FALSE;
3527f0c5ee21Smmusante 		dd.closezhp = B_TRUE;
3528cdf5b4caSmmusante 
3529cdf5b4caSmmusante 		/* We remove any zvol links prior to renaming them */
3530cdf5b4caSmmusante 		ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd);
3531cdf5b4caSmmusante 		if (ret) {
3532cdf5b4caSmmusante 			goto error;
3533cdf5b4caSmmusante 		}
3534cdf5b4caSmmusante 	} else {
35350069fd67STim Haley 		if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
3536cdf5b4caSmmusante 			return (-1);
3537cdf5b4caSmmusante 
3538cdf5b4caSmmusante 		if (changelist_haszonedchild(cl)) {
3539cdf5b4caSmmusante 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3540cdf5b4caSmmusante 			    "child dataset with inherited mountpoint is used "
3541cdf5b4caSmmusante 			    "in a non-global zone"));
3542cdf5b4caSmmusante 			(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3543cdf5b4caSmmusante 			goto error;
3544cdf5b4caSmmusante 		}
3545cdf5b4caSmmusante 
3546cdf5b4caSmmusante 		if ((ret = changelist_prefix(cl)) != 0)
3547cdf5b4caSmmusante 			goto error;
3548cdf5b4caSmmusante 	}
3549fa9e4066Sahrens 
3550e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
3551fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
3552fa9e4066Sahrens 	else
3553fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3554fa9e4066Sahrens 
355598579b20Snd 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3556e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
355798579b20Snd 
3558cdf5b4caSmmusante 	zc.zc_cookie = recursive;
3559cdf5b4caSmmusante 
3560ecd6cf80Smarks 	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
3561cdf5b4caSmmusante 		/*
3562cdf5b4caSmmusante 		 * if it was recursive, the one that actually failed will
3563cdf5b4caSmmusante 		 * be in zc.zc_name
3564cdf5b4caSmmusante 		 */
3565cdf5b4caSmmusante 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
35663cb34c60Sahrens 		    "cannot rename '%s'"), zc.zc_name);
3567cdf5b4caSmmusante 
3568cdf5b4caSmmusante 		if (recursive && errno == EEXIST) {
3569cdf5b4caSmmusante 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3570cdf5b4caSmmusante 			    "a child dataset already has a snapshot "
3571cdf5b4caSmmusante 			    "with the new name"));
3572a10acbd6Seschrock 			(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3573cdf5b4caSmmusante 		} else {
3574cdf5b4caSmmusante 			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3575cdf5b4caSmmusante 		}
3576fa9e4066Sahrens 
3577fa9e4066Sahrens 		/*
3578fa9e4066Sahrens 		 * On failure, we still want to remount any filesystems that
3579fa9e4066Sahrens 		 * were previously mounted, so we don't alter the system state.
3580fa9e4066Sahrens 		 */
3581cdf5b4caSmmusante 		if (recursive) {
3582cdf5b4caSmmusante 			struct createdata cd;
3583cdf5b4caSmmusante 
3584cdf5b4caSmmusante 			/* only create links for datasets that had existed */
3585cdf5b4caSmmusante 			cd.cd_snapname = delim + 1;
3586cdf5b4caSmmusante 			cd.cd_ifexists = B_TRUE;
3587cdf5b4caSmmusante 			(void) zfs_iter_filesystems(zhrp, zfs_create_link_cb,
3588cdf5b4caSmmusante 			    &cd);
3589cdf5b4caSmmusante 		} else {
3590cdf5b4caSmmusante 			(void) changelist_postfix(cl);
3591cdf5b4caSmmusante 		}
3592fa9e4066Sahrens 	} else {
3593cdf5b4caSmmusante 		if (recursive) {
3594cdf5b4caSmmusante 			struct createdata cd;
3595cdf5b4caSmmusante 
3596cdf5b4caSmmusante 			/* only create links for datasets that had existed */
3597cdf5b4caSmmusante 			cd.cd_snapname = strchr(target, '@') + 1;
3598cdf5b4caSmmusante 			cd.cd_ifexists = B_TRUE;
3599cdf5b4caSmmusante 			ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb,
3600cdf5b4caSmmusante 			    &cd);
3601cdf5b4caSmmusante 		} else {
3602cdf5b4caSmmusante 			changelist_rename(cl, zfs_get_name(zhp), target);
3603cdf5b4caSmmusante 			ret = changelist_postfix(cl);
3604cdf5b4caSmmusante 		}
3605fa9e4066Sahrens 	}
3606fa9e4066Sahrens 
3607fa9e4066Sahrens error:
3608cdf5b4caSmmusante 	if (parentname) {
3609cdf5b4caSmmusante 		free(parentname);
3610cdf5b4caSmmusante 	}
3611cdf5b4caSmmusante 	if (zhrp) {
3612cdf5b4caSmmusante 		zfs_close(zhrp);
3613cdf5b4caSmmusante 	}
3614cdf5b4caSmmusante 	if (cl) {
3615cdf5b4caSmmusante 		changelist_free(cl);
3616cdf5b4caSmmusante 	}
3617fa9e4066Sahrens 	return (ret);
3618fa9e4066Sahrens }
3619fa9e4066Sahrens 
3620fa9e4066Sahrens /*
3621fa9e4066Sahrens  * Given a zvol dataset, issue the ioctl to create the appropriate minor node,
3622fa9e4066Sahrens  * poke devfsadm to create the /dev link, and then wait for the link to appear.
3623fa9e4066Sahrens  */
3624fa9e4066Sahrens int
362599653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
3626cdf5b4caSmmusante {
3627cdf5b4caSmmusante 	return (zvol_create_link_common(hdl, dataset, B_FALSE));
3628cdf5b4caSmmusante }
3629cdf5b4caSmmusante 
3630cdf5b4caSmmusante static int
3631cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
3632fa9e4066Sahrens {
3633fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
363499653d4eSeschrock 	di_devlink_handle_t dhdl;
3635ecd6cf80Smarks 	priv_set_t *priv_effective;
3636ecd6cf80Smarks 	int privileged;
3637fa9e4066Sahrens 
3638fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3639fa9e4066Sahrens 
3640fa9e4066Sahrens 	/*
3641fa9e4066Sahrens 	 * Issue the appropriate ioctl.
3642fa9e4066Sahrens 	 */
364399653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
3644fa9e4066Sahrens 		switch (errno) {
3645fa9e4066Sahrens 		case EEXIST:
3646fa9e4066Sahrens 			/*
3647fa9e4066Sahrens 			 * Silently ignore the case where the link already
3648fa9e4066Sahrens 			 * exists.  This allows 'zfs volinit' to be run multiple
3649fa9e4066Sahrens 			 * times without errors.
3650fa9e4066Sahrens 			 */
3651fa9e4066Sahrens 			return (0);
3652fa9e4066Sahrens 
3653cdf5b4caSmmusante 		case ENOENT:
3654cdf5b4caSmmusante 			/*
3655cdf5b4caSmmusante 			 * Dataset does not exist in the kernel.  If we
3656cdf5b4caSmmusante 			 * don't care (see zfs_rename), then ignore the
3657cdf5b4caSmmusante 			 * error quietly.
3658cdf5b4caSmmusante 			 */
3659cdf5b4caSmmusante 			if (ifexists) {
3660cdf5b4caSmmusante 				return (0);
3661cdf5b4caSmmusante 			}
3662cdf5b4caSmmusante 
3663cdf5b4caSmmusante 			/* FALLTHROUGH */
3664cdf5b4caSmmusante 
3665fa9e4066Sahrens 		default:
3666ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
366799653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot create device links "
366899653d4eSeschrock 			    "for '%s'"), dataset));
3669fa9e4066Sahrens 		}
3670fa9e4066Sahrens 	}
3671fa9e4066Sahrens 
3672fa9e4066Sahrens 	/*
3673ecd6cf80Smarks 	 * If privileged call devfsadm and wait for the links to
3674ecd6cf80Smarks 	 * magically appear.
3675ecd6cf80Smarks 	 * Otherwise, print out an informational message.
3676fa9e4066Sahrens 	 */
3677ecd6cf80Smarks 
3678ecd6cf80Smarks 	priv_effective = priv_allocset();
3679ecd6cf80Smarks 	(void) getppriv(PRIV_EFFECTIVE, priv_effective);
3680ecd6cf80Smarks 	privileged = (priv_isfullset(priv_effective) == B_TRUE);
3681ecd6cf80Smarks 	priv_freeset(priv_effective);
3682ecd6cf80Smarks 
3683ecd6cf80Smarks 	if (privileged) {
3684ecd6cf80Smarks 		if ((dhdl = di_devlink_init(ZFS_DRIVER,
3685ecd6cf80Smarks 		    DI_MAKE_LINK)) == NULL) {
3686ecd6cf80Smarks 			zfs_error_aux(hdl, strerror(errno));
3687d1e7ea07SEric Taylor 			(void) zfs_error_fmt(hdl, errno,
3688ecd6cf80Smarks 			    dgettext(TEXT_DOMAIN, "cannot create device links "
3689ecd6cf80Smarks 			    "for '%s'"), dataset);
3690ecd6cf80Smarks 			(void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
3691ecd6cf80Smarks 			return (-1);
3692ecd6cf80Smarks 		} else {
3693ecd6cf80Smarks 			(void) di_devlink_fini(&dhdl);
3694ecd6cf80Smarks 		}
3695fa9e4066Sahrens 	} else {
3696ecd6cf80Smarks 		char pathname[MAXPATHLEN];
3697ecd6cf80Smarks 		struct stat64 statbuf;
3698ecd6cf80Smarks 		int i;
3699ecd6cf80Smarks 
3700ecd6cf80Smarks #define	MAX_WAIT	10
3701ecd6cf80Smarks 
3702ecd6cf80Smarks 		/*
3703ecd6cf80Smarks 		 * This is the poor mans way of waiting for the link
3704ecd6cf80Smarks 		 * to show up.  If after 10 seconds we still don't
3705ecd6cf80Smarks 		 * have it, then print out a message.
3706ecd6cf80Smarks 		 */
3707ecd6cf80Smarks 		(void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s",
3708ecd6cf80Smarks 		    dataset);
3709ecd6cf80Smarks 
3710ecd6cf80Smarks 		for (i = 0; i != MAX_WAIT; i++) {
3711ecd6cf80Smarks 			if (stat64(pathname, &statbuf) == 0)
3712ecd6cf80Smarks 				break;
3713ecd6cf80Smarks 			(void) sleep(1);
3714ecd6cf80Smarks 		}
3715ecd6cf80Smarks 		if (i == MAX_WAIT)
3716ecd6cf80Smarks 			(void) printf(gettext("%s may not be immediately "
3717ecd6cf80Smarks 			    "available\n"), pathname);
3718fa9e4066Sahrens 	}
3719fa9e4066Sahrens 
3720fa9e4066Sahrens 	return (0);
3721fa9e4066Sahrens }
3722fa9e4066Sahrens 
3723fa9e4066Sahrens /*
3724fa9e4066Sahrens  * Remove a minor node for the given zvol and the associated /dev links.
3725fa9e4066Sahrens  */
3726fa9e4066Sahrens int
372799653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
3728fa9e4066Sahrens {
3729fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3730fa9e4066Sahrens 
3731fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3732fa9e4066Sahrens 
373399653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
3734fa9e4066Sahrens 		switch (errno) {
3735fa9e4066Sahrens 		case ENXIO:
3736fa9e4066Sahrens 			/*
3737fa9e4066Sahrens 			 * Silently ignore the case where the link no longer
3738fa9e4066Sahrens 			 * exists, so that 'zfs volfini' can be run multiple
3739fa9e4066Sahrens 			 * times without errors.
3740fa9e4066Sahrens 			 */
3741fa9e4066Sahrens 			return (0);
3742fa9e4066Sahrens 
3743fa9e4066Sahrens 		default:
3744ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
374599653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot remove device "
374699653d4eSeschrock 			    "links for '%s'"), dataset));
3747fa9e4066Sahrens 		}
3748fa9e4066Sahrens 	}
3749fa9e4066Sahrens 
3750fa9e4066Sahrens 	return (0);
3751fa9e4066Sahrens }
3752e9dbad6fSeschrock 
3753e9dbad6fSeschrock nvlist_t *
3754e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp)
3755e9dbad6fSeschrock {
3756e9dbad6fSeschrock 	return (zhp->zfs_user_props);
3757e9dbad6fSeschrock }
3758e9dbad6fSeschrock 
3759b1b8ab34Slling /*
3760b1b8ab34Slling  * This function is used by 'zfs list' to determine the exact set of columns to
3761b1b8ab34Slling  * display, and their maximum widths.  This does two main things:
3762b1b8ab34Slling  *
3763b1b8ab34Slling  *      - If this is a list of all properties, then expand the list to include
3764b1b8ab34Slling  *        all native properties, and set a flag so that for each dataset we look
3765b1b8ab34Slling  *        for new unique user properties and add them to the list.
3766b1b8ab34Slling  *
3767b1b8ab34Slling  *      - For non fixed-width properties, keep track of the maximum width seen
3768b1b8ab34Slling  *        so that we can size the column appropriately.
3769b1b8ab34Slling  */
3770b1b8ab34Slling int
3771990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp)
3772b1b8ab34Slling {
3773b1b8ab34Slling 	libzfs_handle_t *hdl = zhp->zfs_hdl;
3774990b4856Slling 	zprop_list_t *entry;
3775990b4856Slling 	zprop_list_t **last, **start;
3776b1b8ab34Slling 	nvlist_t *userprops, *propval;
3777b1b8ab34Slling 	nvpair_t *elem;
3778b1b8ab34Slling 	char *strval;
3779b1b8ab34Slling 	char buf[ZFS_MAXPROPLEN];
3780b1b8ab34Slling 
3781990b4856Slling 	if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
3782b1b8ab34Slling 		return (-1);
3783e9dbad6fSeschrock 
3784e9dbad6fSeschrock 	userprops = zfs_get_user_props(zhp);
3785e9dbad6fSeschrock 
3786e9dbad6fSeschrock 	entry = *plp;
3787e9dbad6fSeschrock 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3788e9dbad6fSeschrock 		/*
3789e9dbad6fSeschrock 		 * Go through and add any user properties as necessary.  We
3790e9dbad6fSeschrock 		 * start by incrementing our list pointer to the first
3791e9dbad6fSeschrock 		 * non-native property.
3792e9dbad6fSeschrock 		 */
3793e9dbad6fSeschrock 		start = plp;
3794e9dbad6fSeschrock 		while (*start != NULL) {
3795990b4856Slling 			if ((*start)->pl_prop == ZPROP_INVAL)
3796e9dbad6fSeschrock 				break;
3797e9dbad6fSeschrock 			start = &(*start)->pl_next;
3798e9dbad6fSeschrock 		}
3799e9dbad6fSeschrock 
3800e9dbad6fSeschrock 		elem = NULL;
3801e9dbad6fSeschrock 		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3802e9dbad6fSeschrock 			/*
3803e9dbad6fSeschrock 			 * See if we've already found this property in our list.
3804e9dbad6fSeschrock 			 */
3805e9dbad6fSeschrock 			for (last = start; *last != NULL;
3806e9dbad6fSeschrock 			    last = &(*last)->pl_next) {
3807e9dbad6fSeschrock 				if (strcmp((*last)->pl_user_prop,
3808e9dbad6fSeschrock 				    nvpair_name(elem)) == 0)
3809e9dbad6fSeschrock 					break;
3810e9dbad6fSeschrock 			}
3811e9dbad6fSeschrock 
3812e9dbad6fSeschrock 			if (*last == NULL) {
3813e9dbad6fSeschrock 				if ((entry = zfs_alloc(hdl,
3814990b4856Slling 				    sizeof (zprop_list_t))) == NULL ||
3815e9dbad6fSeschrock 				    ((entry->pl_user_prop = zfs_strdup(hdl,
3816e9dbad6fSeschrock 				    nvpair_name(elem)))) == NULL) {
3817e9dbad6fSeschrock 					free(entry);
3818e9dbad6fSeschrock 					return (-1);
3819e9dbad6fSeschrock 				}
3820e9dbad6fSeschrock 
3821990b4856Slling 				entry->pl_prop = ZPROP_INVAL;
3822e9dbad6fSeschrock 				entry->pl_width = strlen(nvpair_name(elem));
3823e9dbad6fSeschrock 				entry->pl_all = B_TRUE;
3824e9dbad6fSeschrock 				*last = entry;
3825e9dbad6fSeschrock 			}
3826e9dbad6fSeschrock 		}
3827e9dbad6fSeschrock 	}
3828e9dbad6fSeschrock 
3829e9dbad6fSeschrock 	/*
3830e9dbad6fSeschrock 	 * Now go through and check the width of any non-fixed columns
3831e9dbad6fSeschrock 	 */
3832e9dbad6fSeschrock 	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3833e9dbad6fSeschrock 		if (entry->pl_fixed)
3834e9dbad6fSeschrock 			continue;
3835e9dbad6fSeschrock 
3836990b4856Slling 		if (entry->pl_prop != ZPROP_INVAL) {
3837e9dbad6fSeschrock 			if (zfs_prop_get(zhp, entry->pl_prop,
3838e9dbad6fSeschrock 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3839e9dbad6fSeschrock 				if (strlen(buf) > entry->pl_width)
3840e9dbad6fSeschrock 					entry->pl_width = strlen(buf);
3841e9dbad6fSeschrock 			}
3842e9dbad6fSeschrock 		} else if (nvlist_lookup_nvlist(userprops,
3843e9dbad6fSeschrock 		    entry->pl_user_prop, &propval)  == 0) {
3844e9dbad6fSeschrock 			verify(nvlist_lookup_string(propval,
3845990b4856Slling 			    ZPROP_VALUE, &strval) == 0);
3846e9dbad6fSeschrock 			if (strlen(strval) > entry->pl_width)
3847e9dbad6fSeschrock 				entry->pl_width = strlen(strval);
3848e9dbad6fSeschrock 		}
3849e9dbad6fSeschrock 	}
3850e9dbad6fSeschrock 
3851e9dbad6fSeschrock 	return (0);
3852e9dbad6fSeschrock }
3853ecd6cf80Smarks 
3854ecd6cf80Smarks int
3855ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred)
3856ecd6cf80Smarks {
3857ecd6cf80Smarks 	zfs_cmd_t zc = { 0 };
3858ecd6cf80Smarks 	nvlist_t *nvp;
3859ecd6cf80Smarks 	gid_t gid;
3860ecd6cf80Smarks 	uid_t uid;
3861ecd6cf80Smarks 	const gid_t *groups;
3862ecd6cf80Smarks 	int group_cnt;
3863ecd6cf80Smarks 	int error;
3864ecd6cf80Smarks 
3865ecd6cf80Smarks 	if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0)
3866ecd6cf80Smarks 		return (no_memory(hdl));
3867ecd6cf80Smarks 
3868ecd6cf80Smarks 	uid = ucred_geteuid(cred);
3869ecd6cf80Smarks 	gid = ucred_getegid(cred);
3870ecd6cf80Smarks 	group_cnt = ucred_getgroups(cred, &groups);
3871ecd6cf80Smarks 
3872ecd6cf80Smarks 	if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1)
3873ecd6cf80Smarks 		return (1);
3874ecd6cf80Smarks 
3875ecd6cf80Smarks 	if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) {
3876ecd6cf80Smarks 		nvlist_free(nvp);
3877ecd6cf80Smarks 		return (1);
3878ecd6cf80Smarks 	}
3879ecd6cf80Smarks 
3880ecd6cf80Smarks 	if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) {
3881ecd6cf80Smarks 		nvlist_free(nvp);
3882ecd6cf80Smarks 		return (1);
3883ecd6cf80Smarks 	}
3884ecd6cf80Smarks 
3885ecd6cf80Smarks 	if (nvlist_add_uint32_array(nvp,
3886ecd6cf80Smarks 	    ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) {
3887ecd6cf80Smarks 		nvlist_free(nvp);
3888ecd6cf80Smarks 		return (1);
3889ecd6cf80Smarks 	}
3890ecd6cf80Smarks 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3891ecd6cf80Smarks 
3892990b4856Slling 	if (zcmd_write_src_nvlist(hdl, &zc, nvp))
3893ecd6cf80Smarks 		return (-1);
3894ecd6cf80Smarks 
3895ecd6cf80Smarks 	error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc);
3896ecd6cf80Smarks 	nvlist_free(nvp);
3897ecd6cf80Smarks 	return (error);
3898ecd6cf80Smarks }
3899ecd6cf80Smarks 
3900ecd6cf80Smarks int
3901ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
3902743a77edSAlan Wright     char *resource, void *export, void *sharetab,
3903743a77edSAlan Wright     int sharemax, zfs_share_op_t operation)
3904ecd6cf80Smarks {
3905ecd6cf80Smarks 	zfs_cmd_t zc = { 0 };
3906ecd6cf80Smarks 	int error;
3907ecd6cf80Smarks 
3908ecd6cf80Smarks 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3909ecd6cf80Smarks 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3910743a77edSAlan Wright 	if (resource)
3911743a77edSAlan Wright 		(void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
3912ecd6cf80Smarks 	zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
3913ecd6cf80Smarks 	zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
3914da6c28aaSamw 	zc.zc_share.z_sharetype = operation;
3915ecd6cf80Smarks 	zc.zc_share.z_sharemax = sharemax;
3916ecd6cf80Smarks 	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
3917ecd6cf80Smarks 	return (error);
3918ecd6cf80Smarks }
39192e5e9e19SSanjeev Bagewadi 
39202e5e9e19SSanjeev Bagewadi void
39212e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
39222e5e9e19SSanjeev Bagewadi {
39232e5e9e19SSanjeev Bagewadi 	nvpair_t *curr;
39242e5e9e19SSanjeev Bagewadi 
39252e5e9e19SSanjeev Bagewadi 	/*
39262e5e9e19SSanjeev Bagewadi 	 * Keep a reference to the props-table against which we prune the
39272e5e9e19SSanjeev Bagewadi 	 * properties.
39282e5e9e19SSanjeev Bagewadi 	 */
39292e5e9e19SSanjeev Bagewadi 	zhp->zfs_props_table = props;
39302e5e9e19SSanjeev Bagewadi 
39312e5e9e19SSanjeev Bagewadi 	curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
39322e5e9e19SSanjeev Bagewadi 
39332e5e9e19SSanjeev Bagewadi 	while (curr) {
39342e5e9e19SSanjeev Bagewadi 		zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
39352e5e9e19SSanjeev Bagewadi 		nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
39362e5e9e19SSanjeev Bagewadi 
3937*14843421SMatthew Ahrens 		/*
3938*14843421SMatthew Ahrens 		 * We leave user:props in the nvlist, so there will be
3939*14843421SMatthew Ahrens 		 * some ZPROP_INVAL.  To be extra safe, don't prune
3940*14843421SMatthew Ahrens 		 * those.
3941*14843421SMatthew Ahrens 		 */
3942*14843421SMatthew Ahrens 		if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
39432e5e9e19SSanjeev Bagewadi 			(void) nvlist_remove(zhp->zfs_props,
39442e5e9e19SSanjeev Bagewadi 			    nvpair_name(curr), nvpair_type(curr));
39452e5e9e19SSanjeev Bagewadi 		curr = next;
39462e5e9e19SSanjeev Bagewadi 	}
39472e5e9e19SSanjeev Bagewadi }
3948743a77edSAlan Wright 
3949743a77edSAlan Wright static int
3950743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
3951743a77edSAlan Wright     zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
3952743a77edSAlan Wright {
3953743a77edSAlan Wright 	zfs_cmd_t zc = { 0 };
3954743a77edSAlan Wright 	nvlist_t *nvlist = NULL;
3955743a77edSAlan Wright 	int error;
3956743a77edSAlan Wright 
3957743a77edSAlan Wright 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3958743a77edSAlan Wright 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3959743a77edSAlan Wright 	zc.zc_cookie = (uint64_t)cmd;
3960743a77edSAlan Wright 
3961743a77edSAlan Wright 	if (cmd == ZFS_SMB_ACL_RENAME) {
3962743a77edSAlan Wright 		if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
3963743a77edSAlan Wright 			(void) no_memory(hdl);
3964743a77edSAlan Wright 			return (NULL);
3965743a77edSAlan Wright 		}
3966743a77edSAlan Wright 	}
3967743a77edSAlan Wright 
3968743a77edSAlan Wright 	switch (cmd) {
3969743a77edSAlan Wright 	case ZFS_SMB_ACL_ADD:
3970743a77edSAlan Wright 	case ZFS_SMB_ACL_REMOVE:
3971743a77edSAlan Wright 		(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
3972743a77edSAlan Wright 		break;
3973743a77edSAlan Wright 	case ZFS_SMB_ACL_RENAME:
3974743a77edSAlan Wright 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
3975743a77edSAlan Wright 		    resource1) != 0) {
3976743a77edSAlan Wright 				(void) no_memory(hdl);
3977743a77edSAlan Wright 				return (-1);
3978743a77edSAlan Wright 		}
3979743a77edSAlan Wright 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
3980743a77edSAlan Wright 		    resource2) != 0) {
3981743a77edSAlan Wright 				(void) no_memory(hdl);
3982743a77edSAlan Wright 				return (-1);
3983743a77edSAlan Wright 		}
3984743a77edSAlan Wright 		if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
3985743a77edSAlan Wright 			nvlist_free(nvlist);
3986743a77edSAlan Wright 			return (-1);
3987743a77edSAlan Wright 		}
3988743a77edSAlan Wright 		break;
3989743a77edSAlan Wright 	case ZFS_SMB_ACL_PURGE:
3990743a77edSAlan Wright 		break;
3991743a77edSAlan Wright 	default:
3992743a77edSAlan Wright 		return (-1);
3993743a77edSAlan Wright 	}
3994743a77edSAlan Wright 	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
3995743a77edSAlan Wright 	if (nvlist)
3996743a77edSAlan Wright 		nvlist_free(nvlist);
3997743a77edSAlan Wright 	return (error);
3998743a77edSAlan Wright }
3999743a77edSAlan Wright 
4000743a77edSAlan Wright int
4001743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
4002743a77edSAlan Wright     char *path, char *resource)
4003743a77edSAlan Wright {
4004743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
4005743a77edSAlan Wright 	    resource, NULL));
4006743a77edSAlan Wright }
4007743a77edSAlan Wright 
4008743a77edSAlan Wright int
4009743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
4010743a77edSAlan Wright     char *path, char *resource)
4011743a77edSAlan Wright {
4012743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
4013743a77edSAlan Wright 	    resource, NULL));
4014743a77edSAlan Wright }
4015743a77edSAlan Wright 
4016743a77edSAlan Wright int
4017743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
4018743a77edSAlan Wright {
4019743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
4020743a77edSAlan Wright 	    NULL, NULL));
4021743a77edSAlan Wright }
4022743a77edSAlan Wright 
4023743a77edSAlan Wright int
4024743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
4025743a77edSAlan Wright     char *oldname, char *newname)
4026743a77edSAlan Wright {
4027743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
4028743a77edSAlan Wright 	    oldname, newname));
4029743a77edSAlan Wright }
4030*14843421SMatthew Ahrens 
4031*14843421SMatthew Ahrens int
4032*14843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
4033*14843421SMatthew Ahrens     zfs_userspace_cb_t func, void *arg)
4034*14843421SMatthew Ahrens {
4035*14843421SMatthew Ahrens 	zfs_cmd_t zc = { 0 };
4036*14843421SMatthew Ahrens 	int error;
4037*14843421SMatthew Ahrens 	zfs_useracct_t buf[100];
4038*14843421SMatthew Ahrens 
4039*14843421SMatthew Ahrens 	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4040*14843421SMatthew Ahrens 
4041*14843421SMatthew Ahrens 	zc.zc_objset_type = type;
4042*14843421SMatthew Ahrens 	zc.zc_nvlist_dst = (uintptr_t)buf;
4043*14843421SMatthew Ahrens 
4044*14843421SMatthew Ahrens 	/* CONSTCOND */
4045*14843421SMatthew Ahrens 	while (1) {
4046*14843421SMatthew Ahrens 		zfs_useracct_t *zua = buf;
4047*14843421SMatthew Ahrens 
4048*14843421SMatthew Ahrens 		zc.zc_nvlist_dst_size = sizeof (buf);
4049*14843421SMatthew Ahrens 		error = ioctl(zhp->zfs_hdl->libzfs_fd,
4050*14843421SMatthew Ahrens 		    ZFS_IOC_USERSPACE_MANY, &zc);
4051*14843421SMatthew Ahrens 		if (error || zc.zc_nvlist_dst_size == 0)
4052*14843421SMatthew Ahrens 			break;
4053*14843421SMatthew Ahrens 
4054*14843421SMatthew Ahrens 		while (zc.zc_nvlist_dst_size > 0) {
4055*14843421SMatthew Ahrens 			func(arg, zua->zu_domain, zua->zu_rid, zua->zu_space);
4056*14843421SMatthew Ahrens 			zua++;
4057*14843421SMatthew Ahrens 			zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
4058*14843421SMatthew Ahrens 		}
4059*14843421SMatthew Ahrens 	}
4060*14843421SMatthew Ahrens 
4061*14843421SMatthew Ahrens 	return (error);
4062*14843421SMatthew Ahrens }
4063