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>
4814843421SMatthew Ahrens #include <idmap.h>
4914843421SMatthew Ahrens #include <aclutils.h>
503b12c289SMatthew Ahrens #include <directory.h>
51fa9e4066Sahrens 
52fa9e4066Sahrens #include <sys/spa.h>
53e9dbad6fSeschrock #include <sys/zap.h>
54fa9e4066Sahrens #include <libzfs.h>
55fa9e4066Sahrens 
56fa9e4066Sahrens #include "zfs_namecheck.h"
57fa9e4066Sahrens #include "zfs_prop.h"
58fa9e4066Sahrens #include "libzfs_impl.h"
59ecd6cf80Smarks #include "zfs_deleg.h"
60fa9e4066Sahrens 
61cdf5b4caSmmusante static int zvol_create_link_common(libzfs_handle_t *, const char *, int);
6214843421SMatthew Ahrens static int userquota_propname_decode(const char *propname, boolean_t zoned,
6314843421SMatthew Ahrens     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
64cdf5b4caSmmusante 
65fa9e4066Sahrens /*
66fa9e4066Sahrens  * Given a single type (not a mask of types), return the type in a human
67fa9e4066Sahrens  * readable form.
68fa9e4066Sahrens  */
69fa9e4066Sahrens const char *
70fa9e4066Sahrens zfs_type_to_name(zfs_type_t type)
71fa9e4066Sahrens {
72fa9e4066Sahrens 	switch (type) {
73fa9e4066Sahrens 	case ZFS_TYPE_FILESYSTEM:
74fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
75fa9e4066Sahrens 	case ZFS_TYPE_SNAPSHOT:
76fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
77fa9e4066Sahrens 	case ZFS_TYPE_VOLUME:
78fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
79fa9e4066Sahrens 	}
80fa9e4066Sahrens 
81fa9e4066Sahrens 	return (NULL);
82fa9e4066Sahrens }
83fa9e4066Sahrens 
84fa9e4066Sahrens /*
85fa9e4066Sahrens  * Given a path and mask of ZFS types, return a string describing this dataset.
86fa9e4066Sahrens  * This is used when we fail to open a dataset and we cannot get an exact type.
87fa9e4066Sahrens  * We guess what the type would have been based on the path and the mask of
88fa9e4066Sahrens  * acceptable types.
89fa9e4066Sahrens  */
90fa9e4066Sahrens static const char *
91fa9e4066Sahrens path_to_str(const char *path, int types)
92fa9e4066Sahrens {
93fa9e4066Sahrens 	/*
94fa9e4066Sahrens 	 * When given a single type, always report the exact type.
95fa9e4066Sahrens 	 */
96fa9e4066Sahrens 	if (types == ZFS_TYPE_SNAPSHOT)
97fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
98fa9e4066Sahrens 	if (types == ZFS_TYPE_FILESYSTEM)
99fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
100fa9e4066Sahrens 	if (types == ZFS_TYPE_VOLUME)
101fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
102fa9e4066Sahrens 
103fa9e4066Sahrens 	/*
104fa9e4066Sahrens 	 * The user is requesting more than one type of dataset.  If this is the
105fa9e4066Sahrens 	 * case, consult the path itself.  If we're looking for a snapshot, and
106fa9e4066Sahrens 	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
107fa9e4066Sahrens 	 * snapshot attribute and try again.
108fa9e4066Sahrens 	 */
109fa9e4066Sahrens 	if (types & ZFS_TYPE_SNAPSHOT) {
110fa9e4066Sahrens 		if (strchr(path, '@') != NULL)
111fa9e4066Sahrens 			return (dgettext(TEXT_DOMAIN, "snapshot"));
112fa9e4066Sahrens 		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
113fa9e4066Sahrens 	}
114fa9e4066Sahrens 
115fa9e4066Sahrens 	/*
116fa9e4066Sahrens 	 * The user has requested either filesystems or volumes.
117fa9e4066Sahrens 	 * We have no way of knowing a priori what type this would be, so always
118fa9e4066Sahrens 	 * report it as "filesystem" or "volume", our two primitive types.
119fa9e4066Sahrens 	 */
120fa9e4066Sahrens 	if (types & ZFS_TYPE_FILESYSTEM)
121fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
122fa9e4066Sahrens 
123fa9e4066Sahrens 	assert(types & ZFS_TYPE_VOLUME);
124fa9e4066Sahrens 	return (dgettext(TEXT_DOMAIN, "volume"));
125fa9e4066Sahrens }
126fa9e4066Sahrens 
127fa9e4066Sahrens /*
128fa9e4066Sahrens  * Validate a ZFS path.  This is used even before trying to open the dataset, to
12914843421SMatthew Ahrens  * provide a more meaningful error message.  We call zfs_error_aux() to
13014843421SMatthew Ahrens  * explain exactly why the name was not valid.
131fa9e4066Sahrens  */
132fa9e4066Sahrens static int
133f18faf3fSek zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
134f18faf3fSek     boolean_t modifying)
135fa9e4066Sahrens {
136fa9e4066Sahrens 	namecheck_err_t why;
137fa9e4066Sahrens 	char what;
138fa9e4066Sahrens 
139fa9e4066Sahrens 	if (dataset_namecheck(path, &why, &what) != 0) {
14099653d4eSeschrock 		if (hdl != NULL) {
141fa9e4066Sahrens 			switch (why) {
142b81d61a6Slling 			case NAME_ERR_TOOLONG:
14399653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14499653d4eSeschrock 				    "name is too long"));
145b81d61a6Slling 				break;
146b81d61a6Slling 
147fa9e4066Sahrens 			case NAME_ERR_LEADING_SLASH:
14899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14999653d4eSeschrock 				    "leading slash in name"));
150fa9e4066Sahrens 				break;
151fa9e4066Sahrens 
152fa9e4066Sahrens 			case NAME_ERR_EMPTY_COMPONENT:
15399653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15499653d4eSeschrock 				    "empty component in name"));
155fa9e4066Sahrens 				break;
156fa9e4066Sahrens 
157fa9e4066Sahrens 			case NAME_ERR_TRAILING_SLASH:
15899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15999653d4eSeschrock 				    "trailing slash in name"));
160fa9e4066Sahrens 				break;
161fa9e4066Sahrens 
162fa9e4066Sahrens 			case NAME_ERR_INVALCHAR:
16399653d4eSeschrock 				zfs_error_aux(hdl,
164fa9e4066Sahrens 				    dgettext(TEXT_DOMAIN, "invalid character "
16599653d4eSeschrock 				    "'%c' in name"), what);
166fa9e4066Sahrens 				break;
167fa9e4066Sahrens 
168fa9e4066Sahrens 			case NAME_ERR_MULTIPLE_AT:
16999653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
17099653d4eSeschrock 				    "multiple '@' delimiters in name"));
171fa9e4066Sahrens 				break;
1725ad82045Snd 
1735ad82045Snd 			case NAME_ERR_NOLETTER:
1745ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1755ad82045Snd 				    "pool doesn't begin with a letter"));
1765ad82045Snd 				break;
1775ad82045Snd 
1785ad82045Snd 			case NAME_ERR_RESERVED:
1795ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1805ad82045Snd 				    "name is reserved"));
1815ad82045Snd 				break;
1825ad82045Snd 
1835ad82045Snd 			case NAME_ERR_DISKLIKE:
1845ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1855ad82045Snd 				    "reserved disk name"));
1865ad82045Snd 				break;
187fa9e4066Sahrens 			}
188fa9e4066Sahrens 		}
189fa9e4066Sahrens 
190fa9e4066Sahrens 		return (0);
191fa9e4066Sahrens 	}
192fa9e4066Sahrens 
193fa9e4066Sahrens 	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
19499653d4eSeschrock 		if (hdl != NULL)
19599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
19699653d4eSeschrock 			    "snapshot delimiter '@' in filesystem name"));
197fa9e4066Sahrens 		return (0);
198fa9e4066Sahrens 	}
199fa9e4066Sahrens 
2001d452cf5Sahrens 	if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
2011d452cf5Sahrens 		if (hdl != NULL)
2021d452cf5Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
203d7d4af51Smmusante 			    "missing '@' delimiter in snapshot name"));
2041d452cf5Sahrens 		return (0);
2051d452cf5Sahrens 	}
2061d452cf5Sahrens 
207f18faf3fSek 	if (modifying && strchr(path, '%') != NULL) {
208f18faf3fSek 		if (hdl != NULL)
209f18faf3fSek 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
210f18faf3fSek 			    "invalid character %c in name"), '%');
211f18faf3fSek 		return (0);
212f18faf3fSek 	}
213f18faf3fSek 
21499653d4eSeschrock 	return (-1);
215fa9e4066Sahrens }
216fa9e4066Sahrens 
217fa9e4066Sahrens int
218fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type)
219fa9e4066Sahrens {
220e7cbe64fSgw 	if (type == ZFS_TYPE_POOL)
221e7cbe64fSgw 		return (zpool_name_valid(NULL, B_FALSE, name));
222f18faf3fSek 	return (zfs_validate_name(NULL, name, type, B_FALSE));
223fa9e4066Sahrens }
224fa9e4066Sahrens 
225e9dbad6fSeschrock /*
226e9dbad6fSeschrock  * This function takes the raw DSL properties, and filters out the user-defined
227e9dbad6fSeschrock  * properties into a separate nvlist.
228e9dbad6fSeschrock  */
229fac3008cSeschrock static nvlist_t *
230fac3008cSeschrock process_user_props(zfs_handle_t *zhp, nvlist_t *props)
231e9dbad6fSeschrock {
232e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
233e9dbad6fSeschrock 	nvpair_t *elem;
234e9dbad6fSeschrock 	nvlist_t *propval;
235fac3008cSeschrock 	nvlist_t *nvl;
236e9dbad6fSeschrock 
237fac3008cSeschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
238fac3008cSeschrock 		(void) no_memory(hdl);
239fac3008cSeschrock 		return (NULL);
240fac3008cSeschrock 	}
241e9dbad6fSeschrock 
242e9dbad6fSeschrock 	elem = NULL;
243fac3008cSeschrock 	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
244e9dbad6fSeschrock 		if (!zfs_prop_user(nvpair_name(elem)))
245e9dbad6fSeschrock 			continue;
246e9dbad6fSeschrock 
247e9dbad6fSeschrock 		verify(nvpair_value_nvlist(elem, &propval) == 0);
248fac3008cSeschrock 		if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
249fac3008cSeschrock 			nvlist_free(nvl);
250fac3008cSeschrock 			(void) no_memory(hdl);
251fac3008cSeschrock 			return (NULL);
252fac3008cSeschrock 		}
253e9dbad6fSeschrock 	}
254e9dbad6fSeschrock 
255fac3008cSeschrock 	return (nvl);
256e9dbad6fSeschrock }
257e9dbad6fSeschrock 
25829ab75c9Srm static zpool_handle_t *
25929ab75c9Srm zpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
26029ab75c9Srm {
26129ab75c9Srm 	libzfs_handle_t *hdl = zhp->zfs_hdl;
26229ab75c9Srm 	zpool_handle_t *zph;
26329ab75c9Srm 
26429ab75c9Srm 	if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
26529ab75c9Srm 		if (hdl->libzfs_pool_handles != NULL)
26629ab75c9Srm 			zph->zpool_next = hdl->libzfs_pool_handles;
26729ab75c9Srm 		hdl->libzfs_pool_handles = zph;
26829ab75c9Srm 	}
26929ab75c9Srm 	return (zph);
27029ab75c9Srm }
27129ab75c9Srm 
27229ab75c9Srm static zpool_handle_t *
27329ab75c9Srm zpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
27429ab75c9Srm {
27529ab75c9Srm 	libzfs_handle_t *hdl = zhp->zfs_hdl;
27629ab75c9Srm 	zpool_handle_t *zph = hdl->libzfs_pool_handles;
27729ab75c9Srm 
27829ab75c9Srm 	while ((zph != NULL) &&
27929ab75c9Srm 	    (strncmp(pool_name, zpool_get_name(zph), len) != 0))
28029ab75c9Srm 		zph = zph->zpool_next;
28129ab75c9Srm 	return (zph);
28229ab75c9Srm }
28329ab75c9Srm 
28429ab75c9Srm /*
28529ab75c9Srm  * Returns a handle to the pool that contains the provided dataset.
28629ab75c9Srm  * If a handle to that pool already exists then that handle is returned.
28729ab75c9Srm  * Otherwise, a new handle is created and added to the list of handles.
28829ab75c9Srm  */
28929ab75c9Srm static zpool_handle_t *
29029ab75c9Srm zpool_handle(zfs_handle_t *zhp)
29129ab75c9Srm {
29229ab75c9Srm 	char *pool_name;
29329ab75c9Srm 	int len;
29429ab75c9Srm 	zpool_handle_t *zph;
29529ab75c9Srm 
29629ab75c9Srm 	len = strcspn(zhp->zfs_name, "/@") + 1;
29729ab75c9Srm 	pool_name = zfs_alloc(zhp->zfs_hdl, len);
29829ab75c9Srm 	(void) strlcpy(pool_name, zhp->zfs_name, len);
29929ab75c9Srm 
30029ab75c9Srm 	zph = zpool_find_handle(zhp, pool_name, len);
30129ab75c9Srm 	if (zph == NULL)
30229ab75c9Srm 		zph = zpool_add_handle(zhp, pool_name);
30329ab75c9Srm 
30429ab75c9Srm 	free(pool_name);
30529ab75c9Srm 	return (zph);
30629ab75c9Srm }
30729ab75c9Srm 
30829ab75c9Srm void
30929ab75c9Srm zpool_free_handles(libzfs_handle_t *hdl)
31029ab75c9Srm {
31129ab75c9Srm 	zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
31229ab75c9Srm 
31329ab75c9Srm 	while (zph != NULL) {
31429ab75c9Srm 		next = zph->zpool_next;
31529ab75c9Srm 		zpool_close(zph);
31629ab75c9Srm 		zph = next;
31729ab75c9Srm 	}
31829ab75c9Srm 	hdl->libzfs_pool_handles = NULL;
31929ab75c9Srm }
32029ab75c9Srm 
321fa9e4066Sahrens /*
322fa9e4066Sahrens  * Utility function to gather stats (objset and zpl) for the given object.
323fa9e4066Sahrens  */
324fa9e4066Sahrens static int
325ebedde84SEric Taylor get_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
326fa9e4066Sahrens {
327e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
328fa9e4066Sahrens 
329ebedde84SEric Taylor 	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
330fa9e4066Sahrens 
331ebedde84SEric Taylor 	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
3327f7322feSeschrock 		if (errno == ENOMEM) {
333ebedde84SEric Taylor 			if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
33499653d4eSeschrock 				return (-1);
335e9dbad6fSeschrock 			}
3367f7322feSeschrock 		} else {
3377f7322feSeschrock 			return (-1);
3387f7322feSeschrock 		}
3397f7322feSeschrock 	}
340ebedde84SEric Taylor 	return (0);
341ebedde84SEric Taylor }
342fa9e4066Sahrens 
343ebedde84SEric Taylor static int
344ebedde84SEric Taylor put_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
345ebedde84SEric Taylor {
346ebedde84SEric Taylor 	nvlist_t *allprops, *userprops;
347fa9e4066Sahrens 
348ebedde84SEric Taylor 	zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
349ebedde84SEric Taylor 
350ebedde84SEric Taylor 	if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
35199653d4eSeschrock 		return (-1);
35299653d4eSeschrock 	}
353fa9e4066Sahrens 
35414843421SMatthew Ahrens 	/*
35514843421SMatthew Ahrens 	 * XXX Why do we store the user props separately, in addition to
35614843421SMatthew Ahrens 	 * storing them in zfs_props?
35714843421SMatthew Ahrens 	 */
358fac3008cSeschrock 	if ((userprops = process_user_props(zhp, allprops)) == NULL) {
359fac3008cSeschrock 		nvlist_free(allprops);
360e9dbad6fSeschrock 		return (-1);
361fac3008cSeschrock 	}
362fac3008cSeschrock 
363fac3008cSeschrock 	nvlist_free(zhp->zfs_props);
364fac3008cSeschrock 	nvlist_free(zhp->zfs_user_props);
365fac3008cSeschrock 
366fac3008cSeschrock 	zhp->zfs_props = allprops;
367fac3008cSeschrock 	zhp->zfs_user_props = userprops;
36899653d4eSeschrock 
369fa9e4066Sahrens 	return (0);
370fa9e4066Sahrens }
371fa9e4066Sahrens 
372ebedde84SEric Taylor static int
373ebedde84SEric Taylor get_stats(zfs_handle_t *zhp)
374ebedde84SEric Taylor {
375ebedde84SEric Taylor 	int rc = 0;
376ebedde84SEric Taylor 	zfs_cmd_t zc = { 0 };
377ebedde84SEric Taylor 
378ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
379ebedde84SEric Taylor 		return (-1);
380ebedde84SEric Taylor 	if (get_stats_ioctl(zhp, &zc) != 0)
381ebedde84SEric Taylor 		rc = -1;
382ebedde84SEric Taylor 	else if (put_stats_zhdl(zhp, &zc) != 0)
383ebedde84SEric Taylor 		rc = -1;
384ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
385ebedde84SEric Taylor 	return (rc);
386ebedde84SEric Taylor }
387ebedde84SEric Taylor 
388fa9e4066Sahrens /*
389fa9e4066Sahrens  * Refresh the properties currently stored in the handle.
390fa9e4066Sahrens  */
391fa9e4066Sahrens void
392fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp)
393fa9e4066Sahrens {
394fa9e4066Sahrens 	(void) get_stats(zhp);
395fa9e4066Sahrens }
396fa9e4066Sahrens 
397fa9e4066Sahrens /*
398fa9e4066Sahrens  * Makes a handle from the given dataset name.  Used by zfs_open() and
399fa9e4066Sahrens  * zfs_iter_* to create child handles on the fly.
400fa9e4066Sahrens  */
401ebedde84SEric Taylor static int
402ebedde84SEric Taylor make_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
403fa9e4066Sahrens {
404*503ad85cSMatthew Ahrens 	if (put_stats_zhdl(zhp, zc) != 0)
405ebedde84SEric Taylor 		return (-1);
40631fd60d3Sahrens 
407fa9e4066Sahrens 	/*
408fa9e4066Sahrens 	 * We've managed to open the dataset and gather statistics.  Determine
409fa9e4066Sahrens 	 * the high-level type.
410fa9e4066Sahrens 	 */
411a2eea2e1Sahrens 	if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
412a2eea2e1Sahrens 		zhp->zfs_head_type = ZFS_TYPE_VOLUME;
413a2eea2e1Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
414a2eea2e1Sahrens 		zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
415a2eea2e1Sahrens 	else
416a2eea2e1Sahrens 		abort();
417a2eea2e1Sahrens 
418fa9e4066Sahrens 	if (zhp->zfs_dmustats.dds_is_snapshot)
419fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
420fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
421fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_VOLUME;
422fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
423fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
424fa9e4066Sahrens 	else
42599653d4eSeschrock 		abort();	/* we should never see any other types */
426fa9e4066Sahrens 
42729ab75c9Srm 	zhp->zpool_hdl = zpool_handle(zhp);
428ebedde84SEric Taylor 	return (0);
429ebedde84SEric Taylor }
430ebedde84SEric Taylor 
431ebedde84SEric Taylor zfs_handle_t *
432ebedde84SEric Taylor make_dataset_handle(libzfs_handle_t *hdl, const char *path)
433ebedde84SEric Taylor {
434ebedde84SEric Taylor 	zfs_cmd_t zc = { 0 };
435ebedde84SEric Taylor 
436ebedde84SEric Taylor 	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
437ebedde84SEric Taylor 
438ebedde84SEric Taylor 	if (zhp == NULL)
439ebedde84SEric Taylor 		return (NULL);
440ebedde84SEric Taylor 
441ebedde84SEric Taylor 	zhp->zfs_hdl = hdl;
442ebedde84SEric Taylor 	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
443ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
444ebedde84SEric Taylor 		free(zhp);
445ebedde84SEric Taylor 		return (NULL);
446ebedde84SEric Taylor 	}
447ebedde84SEric Taylor 	if (get_stats_ioctl(zhp, &zc) == -1) {
448ebedde84SEric Taylor 		zcmd_free_nvlists(&zc);
449ebedde84SEric Taylor 		free(zhp);
450ebedde84SEric Taylor 		return (NULL);
451ebedde84SEric Taylor 	}
452ebedde84SEric Taylor 	if (make_dataset_handle_common(zhp, &zc) == -1) {
453ebedde84SEric Taylor 		free(zhp);
454ebedde84SEric Taylor 		zhp = NULL;
455ebedde84SEric Taylor 	}
456ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
457ebedde84SEric Taylor 	return (zhp);
458ebedde84SEric Taylor }
459ebedde84SEric Taylor 
460ebedde84SEric Taylor static zfs_handle_t *
461ebedde84SEric Taylor make_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
462ebedde84SEric Taylor {
463ebedde84SEric Taylor 	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
464ebedde84SEric Taylor 
465ebedde84SEric Taylor 	if (zhp == NULL)
466ebedde84SEric Taylor 		return (NULL);
467ebedde84SEric Taylor 
468ebedde84SEric Taylor 	zhp->zfs_hdl = hdl;
469ebedde84SEric Taylor 	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
470ebedde84SEric Taylor 	if (make_dataset_handle_common(zhp, zc) == -1) {
471ebedde84SEric Taylor 		free(zhp);
472ebedde84SEric Taylor 		return (NULL);
473ebedde84SEric Taylor 	}
474fa9e4066Sahrens 	return (zhp);
475fa9e4066Sahrens }
476fa9e4066Sahrens 
477fa9e4066Sahrens /*
478fa9e4066Sahrens  * Opens the given snapshot, filesystem, or volume.   The 'types'
479fa9e4066Sahrens  * argument is a mask of acceptable types.  The function will print an
480fa9e4066Sahrens  * appropriate error message and return NULL if it can't be opened.
481fa9e4066Sahrens  */
482fa9e4066Sahrens zfs_handle_t *
48399653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types)
484fa9e4066Sahrens {
485fa9e4066Sahrens 	zfs_handle_t *zhp;
48699653d4eSeschrock 	char errbuf[1024];
48799653d4eSeschrock 
48899653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
48999653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
490fa9e4066Sahrens 
491fa9e4066Sahrens 	/*
49299653d4eSeschrock 	 * Validate the name before we even try to open it.
493fa9e4066Sahrens 	 */
494f18faf3fSek 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
49599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
49699653d4eSeschrock 		    "invalid dataset name"));
49799653d4eSeschrock 		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
498fa9e4066Sahrens 		return (NULL);
499fa9e4066Sahrens 	}
500fa9e4066Sahrens 
501fa9e4066Sahrens 	/*
502fa9e4066Sahrens 	 * Try to get stats for the dataset, which will tell us if it exists.
503fa9e4066Sahrens 	 */
504fa9e4066Sahrens 	errno = 0;
50599653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
506ece3d9b3Slling 		(void) zfs_standard_error(hdl, errno, errbuf);
507fa9e4066Sahrens 		return (NULL);
508fa9e4066Sahrens 	}
509fa9e4066Sahrens 
510fa9e4066Sahrens 	if (!(types & zhp->zfs_type)) {
51199653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
51294de1d4cSeschrock 		zfs_close(zhp);
513fa9e4066Sahrens 		return (NULL);
514fa9e4066Sahrens 	}
515fa9e4066Sahrens 
516fa9e4066Sahrens 	return (zhp);
517fa9e4066Sahrens }
518fa9e4066Sahrens 
519fa9e4066Sahrens /*
520fa9e4066Sahrens  * Release a ZFS handle.  Nothing to do but free the associated memory.
521fa9e4066Sahrens  */
522fa9e4066Sahrens void
523fa9e4066Sahrens zfs_close(zfs_handle_t *zhp)
524fa9e4066Sahrens {
525fa9e4066Sahrens 	if (zhp->zfs_mntopts)
526fa9e4066Sahrens 		free(zhp->zfs_mntopts);
527e9dbad6fSeschrock 	nvlist_free(zhp->zfs_props);
528e9dbad6fSeschrock 	nvlist_free(zhp->zfs_user_props);
529fa9e4066Sahrens 	free(zhp);
530fa9e4066Sahrens }
531fa9e4066Sahrens 
532ebedde84SEric Taylor typedef struct mnttab_node {
533ebedde84SEric Taylor 	struct mnttab mtn_mt;
534ebedde84SEric Taylor 	avl_node_t mtn_node;
535ebedde84SEric Taylor } mnttab_node_t;
536ebedde84SEric Taylor 
537ebedde84SEric Taylor static int
538ebedde84SEric Taylor libzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
539ebedde84SEric Taylor {
540ebedde84SEric Taylor 	const mnttab_node_t *mtn1 = arg1;
541ebedde84SEric Taylor 	const mnttab_node_t *mtn2 = arg2;
542ebedde84SEric Taylor 	int rv;
543ebedde84SEric Taylor 
544ebedde84SEric Taylor 	rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
545ebedde84SEric Taylor 
546ebedde84SEric Taylor 	if (rv == 0)
547ebedde84SEric Taylor 		return (0);
548ebedde84SEric Taylor 	return (rv > 0 ? 1 : -1);
549ebedde84SEric Taylor }
550ebedde84SEric Taylor 
551ebedde84SEric Taylor void
552ebedde84SEric Taylor libzfs_mnttab_init(libzfs_handle_t *hdl)
553ebedde84SEric Taylor {
554ebedde84SEric Taylor 	assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
555ebedde84SEric Taylor 	avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
556ebedde84SEric Taylor 	    sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
557b2634b9cSEric Taylor }
558b2634b9cSEric Taylor 
559b2634b9cSEric Taylor void
560b2634b9cSEric Taylor libzfs_mnttab_update(libzfs_handle_t *hdl)
561b2634b9cSEric Taylor {
562b2634b9cSEric Taylor 	struct mnttab entry;
563ebedde84SEric Taylor 
564ebedde84SEric Taylor 	rewind(hdl->libzfs_mnttab);
565ebedde84SEric Taylor 	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
566ebedde84SEric Taylor 		mnttab_node_t *mtn;
567ebedde84SEric Taylor 
568ebedde84SEric Taylor 		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
569ebedde84SEric Taylor 			continue;
570ebedde84SEric Taylor 		mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
571ebedde84SEric Taylor 		mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
572ebedde84SEric Taylor 		mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
573ebedde84SEric Taylor 		mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
574ebedde84SEric Taylor 		mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
575ebedde84SEric Taylor 		avl_add(&hdl->libzfs_mnttab_cache, mtn);
576ebedde84SEric Taylor 	}
577ebedde84SEric Taylor }
578ebedde84SEric Taylor 
579ebedde84SEric Taylor void
580ebedde84SEric Taylor libzfs_mnttab_fini(libzfs_handle_t *hdl)
581ebedde84SEric Taylor {
582ebedde84SEric Taylor 	void *cookie = NULL;
583ebedde84SEric Taylor 	mnttab_node_t *mtn;
584ebedde84SEric Taylor 
585ebedde84SEric Taylor 	while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
586ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_special);
587ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_mountp);
588ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_fstype);
589ebedde84SEric Taylor 		free(mtn->mtn_mt.mnt_mntopts);
590ebedde84SEric Taylor 		free(mtn);
591ebedde84SEric Taylor 	}
592ebedde84SEric Taylor 	avl_destroy(&hdl->libzfs_mnttab_cache);
593ebedde84SEric Taylor }
594ebedde84SEric Taylor 
595b2634b9cSEric Taylor void
596b2634b9cSEric Taylor libzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
597b2634b9cSEric Taylor {
598b2634b9cSEric Taylor 	hdl->libzfs_mnttab_enable = enable;
599b2634b9cSEric Taylor }
600b2634b9cSEric Taylor 
601ebedde84SEric Taylor int
602ebedde84SEric Taylor libzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
603ebedde84SEric Taylor     struct mnttab *entry)
604ebedde84SEric Taylor {
605ebedde84SEric Taylor 	mnttab_node_t find;
606ebedde84SEric Taylor 	mnttab_node_t *mtn;
607ebedde84SEric Taylor 
608b2634b9cSEric Taylor 	if (!hdl->libzfs_mnttab_enable) {
609b2634b9cSEric Taylor 		struct mnttab srch = { 0 };
610b2634b9cSEric Taylor 
611b2634b9cSEric Taylor 		if (avl_numnodes(&hdl->libzfs_mnttab_cache))
612b2634b9cSEric Taylor 			libzfs_mnttab_fini(hdl);
613b2634b9cSEric Taylor 		rewind(hdl->libzfs_mnttab);
614b2634b9cSEric Taylor 		srch.mnt_special = (char *)fsname;
615b2634b9cSEric Taylor 		srch.mnt_fstype = MNTTYPE_ZFS;
616b2634b9cSEric Taylor 		if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
617b2634b9cSEric Taylor 			return (0);
618b2634b9cSEric Taylor 		else
619b2634b9cSEric Taylor 			return (ENOENT);
620b2634b9cSEric Taylor 	}
621b2634b9cSEric Taylor 
622ebedde84SEric Taylor 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
623b2634b9cSEric Taylor 		libzfs_mnttab_update(hdl);
624ebedde84SEric Taylor 
625ebedde84SEric Taylor 	find.mtn_mt.mnt_special = (char *)fsname;
626ebedde84SEric Taylor 	mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
627ebedde84SEric Taylor 	if (mtn) {
628ebedde84SEric Taylor 		*entry = mtn->mtn_mt;
629ebedde84SEric Taylor 		return (0);
630ebedde84SEric Taylor 	}
631ebedde84SEric Taylor 	return (ENOENT);
632ebedde84SEric Taylor }
633ebedde84SEric Taylor 
634ebedde84SEric Taylor void
635ebedde84SEric Taylor libzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
636ebedde84SEric Taylor     const char *mountp, const char *mntopts)
637ebedde84SEric Taylor {
638ebedde84SEric Taylor 	mnttab_node_t *mtn;
639ebedde84SEric Taylor 
640ebedde84SEric Taylor 	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
641ebedde84SEric Taylor 		return;
642ebedde84SEric Taylor 	mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
643ebedde84SEric Taylor 	mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
644ebedde84SEric Taylor 	mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
645ebedde84SEric Taylor 	mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
646ebedde84SEric Taylor 	mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
647ebedde84SEric Taylor 	avl_add(&hdl->libzfs_mnttab_cache, mtn);
648ebedde84SEric Taylor }
649ebedde84SEric Taylor 
650ebedde84SEric Taylor void
651ebedde84SEric Taylor libzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
652ebedde84SEric Taylor {
653ebedde84SEric Taylor 	mnttab_node_t find;
654ebedde84SEric Taylor 	mnttab_node_t *ret;
655ebedde84SEric Taylor 
656ebedde84SEric Taylor 	find.mtn_mt.mnt_special = (char *)fsname;
657ebedde84SEric Taylor 	if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
658ebedde84SEric Taylor 		avl_remove(&hdl->libzfs_mnttab_cache, ret);
659ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_special);
660ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_mountp);
661ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_fstype);
662ebedde84SEric Taylor 		free(ret->mtn_mt.mnt_mntopts);
663ebedde84SEric Taylor 		free(ret);
664ebedde84SEric Taylor 	}
665ebedde84SEric Taylor }
666ebedde84SEric Taylor 
6677b97dc1aSrm int
6687b97dc1aSrm zfs_spa_version(zfs_handle_t *zhp, int *spa_version)
6697b97dc1aSrm {
67029ab75c9Srm 	zpool_handle_t *zpool_handle = zhp->zpool_hdl;
6717b97dc1aSrm 
6727b97dc1aSrm 	if (zpool_handle == NULL)
6737b97dc1aSrm 		return (-1);
6747b97dc1aSrm 
6757b97dc1aSrm 	*spa_version = zpool_get_prop_int(zpool_handle,
6767b97dc1aSrm 	    ZPOOL_PROP_VERSION, NULL);
6777b97dc1aSrm 	return (0);
6787b97dc1aSrm }
6797b97dc1aSrm 
6807b97dc1aSrm /*
6817b97dc1aSrm  * The choice of reservation property depends on the SPA version.
6827b97dc1aSrm  */
6837b97dc1aSrm static int
6847b97dc1aSrm zfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
6857b97dc1aSrm {
6867b97dc1aSrm 	int spa_version;
6877b97dc1aSrm 
6887b97dc1aSrm 	if (zfs_spa_version(zhp, &spa_version) < 0)
6897b97dc1aSrm 		return (-1);
6907b97dc1aSrm 
6917b97dc1aSrm 	if (spa_version >= SPA_VERSION_REFRESERVATION)
6927b97dc1aSrm 		*resv_prop = ZFS_PROP_REFRESERVATION;
6937b97dc1aSrm 	else
6947b97dc1aSrm 		*resv_prop = ZFS_PROP_RESERVATION;
6957b97dc1aSrm 
6967b97dc1aSrm 	return (0);
6977b97dc1aSrm }
6987b97dc1aSrm 
699e9dbad6fSeschrock /*
700e9dbad6fSeschrock  * Given an nvlist of properties to set, validates that they are correct, and
701e9dbad6fSeschrock  * parses any numeric properties (index, boolean, etc) if they are specified as
702e9dbad6fSeschrock  * strings.
703e9dbad6fSeschrock  */
7040a48a24eStimh nvlist_t *
7050a48a24eStimh zfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
706990b4856Slling     uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
707e9dbad6fSeschrock {
708e9dbad6fSeschrock 	nvpair_t *elem;
709e9dbad6fSeschrock 	uint64_t intval;
710e9dbad6fSeschrock 	char *strval;
711990b4856Slling 	zfs_prop_t prop;
712e9dbad6fSeschrock 	nvlist_t *ret;
713da6c28aaSamw 	int chosen_normal = -1;
714da6c28aaSamw 	int chosen_utf = -1;
715e9dbad6fSeschrock 
716990b4856Slling 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
717990b4856Slling 		(void) no_memory(hdl);
718990b4856Slling 		return (NULL);
719e9dbad6fSeschrock 	}
720e9dbad6fSeschrock 
72114843421SMatthew Ahrens 	/*
72214843421SMatthew Ahrens 	 * Make sure this property is valid and applies to this type.
72314843421SMatthew Ahrens 	 */
72414843421SMatthew Ahrens 
725e9dbad6fSeschrock 	elem = NULL;
726e9dbad6fSeschrock 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
727990b4856Slling 		const char *propname = nvpair_name(elem);
728e9dbad6fSeschrock 
72914843421SMatthew Ahrens 		prop = zfs_name_to_prop(propname);
73014843421SMatthew Ahrens 		if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
731990b4856Slling 			/*
73214843421SMatthew Ahrens 			 * This is a user property: make sure it's a
733990b4856Slling 			 * string, and that it's less than ZAP_MAXNAMELEN.
734990b4856Slling 			 */
735990b4856Slling 			if (nvpair_type(elem) != DATA_TYPE_STRING) {
736990b4856Slling 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
737990b4856Slling 				    "'%s' must be a string"), propname);
738990b4856Slling 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
739990b4856Slling 				goto error;
740990b4856Slling 			}
741990b4856Slling 
742990b4856Slling 			if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
743990b4856Slling 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
744990b4856Slling 				    "property name '%s' is too long"),
745990b4856Slling 				    propname);
746990b4856Slling 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
747990b4856Slling 				goto error;
748fa9e4066Sahrens 			}
749fa9e4066Sahrens 
750e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &strval);
751e9dbad6fSeschrock 			if (nvlist_add_string(ret, propname, strval) != 0) {
752e9dbad6fSeschrock 				(void) no_memory(hdl);
753e9dbad6fSeschrock 				goto error;
754e9dbad6fSeschrock 			}
755e9dbad6fSeschrock 			continue;
756e9dbad6fSeschrock 		}
757e9dbad6fSeschrock 
75814843421SMatthew Ahrens 		/*
75914843421SMatthew Ahrens 		 * Currently, only user properties can be modified on
76014843421SMatthew Ahrens 		 * snapshots.
76114843421SMatthew Ahrens 		 */
762bb0ade09Sahrens 		if (type == ZFS_TYPE_SNAPSHOT) {
763bb0ade09Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
764bb0ade09Sahrens 			    "this property can not be modified for snapshots"));
765bb0ade09Sahrens 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
766bb0ade09Sahrens 			goto error;
767bb0ade09Sahrens 		}
768bb0ade09Sahrens 
76914843421SMatthew Ahrens 		if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
77014843421SMatthew Ahrens 			zfs_userquota_prop_t uqtype;
77114843421SMatthew Ahrens 			char newpropname[128];
77214843421SMatthew Ahrens 			char domain[128];
77314843421SMatthew Ahrens 			uint64_t rid;
77414843421SMatthew Ahrens 			uint64_t valary[3];
77514843421SMatthew Ahrens 
77614843421SMatthew Ahrens 			if (userquota_propname_decode(propname, zoned,
77714843421SMatthew Ahrens 			    &uqtype, domain, sizeof (domain), &rid) != 0) {
77814843421SMatthew Ahrens 				zfs_error_aux(hdl,
77914843421SMatthew Ahrens 				    dgettext(TEXT_DOMAIN,
78014843421SMatthew Ahrens 				    "'%s' has an invalid user/group name"),
78114843421SMatthew Ahrens 				    propname);
78214843421SMatthew Ahrens 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
78314843421SMatthew Ahrens 				goto error;
78414843421SMatthew Ahrens 			}
78514843421SMatthew Ahrens 
78614843421SMatthew Ahrens 			if (uqtype != ZFS_PROP_USERQUOTA &&
78714843421SMatthew Ahrens 			    uqtype != ZFS_PROP_GROUPQUOTA) {
78814843421SMatthew Ahrens 				zfs_error_aux(hdl,
78914843421SMatthew Ahrens 				    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
79014843421SMatthew Ahrens 				    propname);
79114843421SMatthew Ahrens 				(void) zfs_error(hdl, EZFS_PROPREADONLY,
79214843421SMatthew Ahrens 				    errbuf);
79314843421SMatthew Ahrens 				goto error;
79414843421SMatthew Ahrens 			}
79514843421SMatthew Ahrens 
79614843421SMatthew Ahrens 			if (nvpair_type(elem) == DATA_TYPE_STRING) {
79714843421SMatthew Ahrens 				(void) nvpair_value_string(elem, &strval);
79814843421SMatthew Ahrens 				if (strcmp(strval, "none") == 0) {
79914843421SMatthew Ahrens 					intval = 0;
80014843421SMatthew Ahrens 				} else if (zfs_nicestrtonum(hdl,
80114843421SMatthew Ahrens 				    strval, &intval) != 0) {
80214843421SMatthew Ahrens 					(void) zfs_error(hdl,
80314843421SMatthew Ahrens 					    EZFS_BADPROP, errbuf);
80414843421SMatthew Ahrens 					goto error;
80514843421SMatthew Ahrens 				}
80614843421SMatthew Ahrens 			} else if (nvpair_type(elem) ==
80714843421SMatthew Ahrens 			    DATA_TYPE_UINT64) {
80814843421SMatthew Ahrens 				(void) nvpair_value_uint64(elem, &intval);
80914843421SMatthew Ahrens 				if (intval == 0) {
81014843421SMatthew Ahrens 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
81114843421SMatthew Ahrens 					    "use 'none' to disable "
81214843421SMatthew Ahrens 					    "userquota/groupquota"));
81314843421SMatthew Ahrens 					goto error;
81414843421SMatthew Ahrens 				}
81514843421SMatthew Ahrens 			} else {
81614843421SMatthew Ahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
81714843421SMatthew Ahrens 				    "'%s' must be a number"), propname);
81814843421SMatthew Ahrens 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
81914843421SMatthew Ahrens 				goto error;
82014843421SMatthew Ahrens 			}
82114843421SMatthew Ahrens 
82214843421SMatthew Ahrens 			(void) snprintf(newpropname, sizeof (newpropname),
82314843421SMatthew Ahrens 			    "%s%s", zfs_userquota_prop_prefixes[uqtype],
82414843421SMatthew Ahrens 			    domain);
82514843421SMatthew Ahrens 			valary[0] = uqtype;
82614843421SMatthew Ahrens 			valary[1] = rid;
82714843421SMatthew Ahrens 			valary[2] = intval;
82814843421SMatthew Ahrens 			if (nvlist_add_uint64_array(ret, newpropname,
82914843421SMatthew Ahrens 			    valary, 3) != 0) {
83014843421SMatthew Ahrens 				(void) no_memory(hdl);
83114843421SMatthew Ahrens 				goto error;
83214843421SMatthew Ahrens 			}
83314843421SMatthew Ahrens 			continue;
83414843421SMatthew Ahrens 		}
83514843421SMatthew Ahrens 
83614843421SMatthew Ahrens 		if (prop == ZPROP_INVAL) {
83714843421SMatthew Ahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
83814843421SMatthew Ahrens 			    "invalid property '%s'"), propname);
83914843421SMatthew Ahrens 			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
84014843421SMatthew Ahrens 			goto error;
84114843421SMatthew Ahrens 		}
84214843421SMatthew Ahrens 
843e9dbad6fSeschrock 		if (!zfs_prop_valid_for_type(prop, type)) {
844e9dbad6fSeschrock 			zfs_error_aux(hdl,
845e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' does not "
846e9dbad6fSeschrock 			    "apply to datasets of this type"), propname);
847e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
848e9dbad6fSeschrock 			goto error;
849e9dbad6fSeschrock 		}
850e9dbad6fSeschrock 
851e9dbad6fSeschrock 		if (zfs_prop_readonly(prop) &&
852da6c28aaSamw 		    (!zfs_prop_setonce(prop) || zhp != NULL)) {
853e9dbad6fSeschrock 			zfs_error_aux(hdl,
854e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
855e9dbad6fSeschrock 			    propname);
856e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
857e9dbad6fSeschrock 			goto error;
858e9dbad6fSeschrock 		}
859e9dbad6fSeschrock 
860990b4856Slling 		if (zprop_parse_value(hdl, elem, prop, type, ret,
861990b4856Slling 		    &strval, &intval, errbuf) != 0)
862e9dbad6fSeschrock 			goto error;
863fa9e4066Sahrens 
864e9dbad6fSeschrock 		/*
865e9dbad6fSeschrock 		 * Perform some additional checks for specific properties.
866e9dbad6fSeschrock 		 */
867e9dbad6fSeschrock 		switch (prop) {
868e7437265Sahrens 		case ZFS_PROP_VERSION:
869e7437265Sahrens 		{
870e7437265Sahrens 			int version;
871e7437265Sahrens 
872e7437265Sahrens 			if (zhp == NULL)
873e7437265Sahrens 				break;
874e7437265Sahrens 			version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
875e7437265Sahrens 			if (intval < version) {
876e7437265Sahrens 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
877e7437265Sahrens 				    "Can not downgrade; already at version %u"),
878e7437265Sahrens 				    version);
879e7437265Sahrens 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
880e7437265Sahrens 				goto error;
881e7437265Sahrens 			}
882e7437265Sahrens 			break;
883e7437265Sahrens 		}
884e7437265Sahrens 
885e9dbad6fSeschrock 		case ZFS_PROP_RECORDSIZE:
886e9dbad6fSeschrock 		case ZFS_PROP_VOLBLOCKSIZE:
887e9dbad6fSeschrock 			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
888e9dbad6fSeschrock 			if (intval < SPA_MINBLOCKSIZE ||
889e9dbad6fSeschrock 			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
89099653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
891e9dbad6fSeschrock 				    "'%s' must be power of 2 from %u "
892e9dbad6fSeschrock 				    "to %uk"), propname,
893e9dbad6fSeschrock 				    (uint_t)SPA_MINBLOCKSIZE,
894e9dbad6fSeschrock 				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
895e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
896e9dbad6fSeschrock 				goto error;
897fa9e4066Sahrens 			}
898fa9e4066Sahrens 			break;
899fa9e4066Sahrens 
900f3861e1aSahl 		case ZFS_PROP_SHAREISCSI:
901f3861e1aSahl 			if (strcmp(strval, "off") != 0 &&
902f3861e1aSahl 			    strcmp(strval, "on") != 0 &&
903f3861e1aSahl 			    strcmp(strval, "type=disk") != 0) {
904f3861e1aSahl 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
905f3861e1aSahl 				    "'%s' must be 'on', 'off', or 'type=disk'"),
906f3861e1aSahl 				    propname);
907f3861e1aSahl 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
908f3861e1aSahl 				goto error;
909f3861e1aSahl 			}
910f3861e1aSahl 
911f3861e1aSahl 			break;
912f3861e1aSahl 
913e9dbad6fSeschrock 		case ZFS_PROP_MOUNTPOINT:
91489eef05eSrm 		{
91589eef05eSrm 			namecheck_err_t why;
91689eef05eSrm 
917e9dbad6fSeschrock 			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
918e9dbad6fSeschrock 			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
919e9dbad6fSeschrock 				break;
920fa9e4066Sahrens 
92189eef05eSrm 			if (mountpoint_namecheck(strval, &why)) {
92289eef05eSrm 				switch (why) {
92389eef05eSrm 				case NAME_ERR_LEADING_SLASH:
92489eef05eSrm 					zfs_error_aux(hdl,
92589eef05eSrm 					    dgettext(TEXT_DOMAIN,
92689eef05eSrm 					    "'%s' must be an absolute path, "
92789eef05eSrm 					    "'none', or 'legacy'"), propname);
92889eef05eSrm 					break;
92989eef05eSrm 				case NAME_ERR_TOOLONG:
93089eef05eSrm 					zfs_error_aux(hdl,
93189eef05eSrm 					    dgettext(TEXT_DOMAIN,
93289eef05eSrm 					    "component of '%s' is too long"),
93389eef05eSrm 					    propname);
93489eef05eSrm 					break;
93589eef05eSrm 				}
936e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
937e9dbad6fSeschrock 				goto error;
938fa9e4066Sahrens 			}
93989eef05eSrm 		}
94089eef05eSrm 
941f3861e1aSahl 			/*FALLTHRU*/
942fa9e4066Sahrens 
943da6c28aaSamw 		case ZFS_PROP_SHARESMB:
944f3861e1aSahl 		case ZFS_PROP_SHARENFS:
945f3861e1aSahl 			/*
946da6c28aaSamw 			 * For the mountpoint and sharenfs or sharesmb
947da6c28aaSamw 			 * properties, check if it can be set in a
948da6c28aaSamw 			 * global/non-global zone based on
949f3861e1aSahl 			 * the zoned property value:
950f3861e1aSahl 			 *
951f3861e1aSahl 			 *		global zone	    non-global zone
952f3861e1aSahl 			 * --------------------------------------------------
953f3861e1aSahl 			 * zoned=on	mountpoint (no)	    mountpoint (yes)
954f3861e1aSahl 			 *		sharenfs (no)	    sharenfs (no)
955da6c28aaSamw 			 *		sharesmb (no)	    sharesmb (no)
956f3861e1aSahl 			 *
957f3861e1aSahl 			 * zoned=off	mountpoint (yes)	N/A
958f3861e1aSahl 			 *		sharenfs (yes)
959da6c28aaSamw 			 *		sharesmb (yes)
960f3861e1aSahl 			 */
961e9dbad6fSeschrock 			if (zoned) {
962e9dbad6fSeschrock 				if (getzoneid() == GLOBAL_ZONEID) {
963e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
964e9dbad6fSeschrock 					    "'%s' cannot be set on "
965e9dbad6fSeschrock 					    "dataset in a non-global zone"),
966e9dbad6fSeschrock 					    propname);
967e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
968e9dbad6fSeschrock 					    errbuf);
969e9dbad6fSeschrock 					goto error;
970da6c28aaSamw 				} else if (prop == ZFS_PROP_SHARENFS ||
971da6c28aaSamw 				    prop == ZFS_PROP_SHARESMB) {
972e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
973e9dbad6fSeschrock 					    "'%s' cannot be set in "
974e9dbad6fSeschrock 					    "a non-global zone"), propname);
975e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
976e9dbad6fSeschrock 					    errbuf);
977e9dbad6fSeschrock 					goto error;
978fa9e4066Sahrens 				}
979e9dbad6fSeschrock 			} else if (getzoneid() != GLOBAL_ZONEID) {
980e9dbad6fSeschrock 				/*
981e9dbad6fSeschrock 				 * If zoned property is 'off', this must be in
98214843421SMatthew Ahrens 				 * a global zone. If not, something is wrong.
983e9dbad6fSeschrock 				 */
984e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
985e9dbad6fSeschrock 				    "'%s' cannot be set while dataset "
986e9dbad6fSeschrock 				    "'zoned' property is set"), propname);
987e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
988e9dbad6fSeschrock 				goto error;
989fa9e4066Sahrens 			}
990f3861e1aSahl 
99167331909Sdougm 			/*
99267331909Sdougm 			 * At this point, it is legitimate to set the
99367331909Sdougm 			 * property. Now we want to make sure that the
99467331909Sdougm 			 * property value is valid if it is sharenfs.
99567331909Sdougm 			 */
996da6c28aaSamw 			if ((prop == ZFS_PROP_SHARENFS ||
997da6c28aaSamw 			    prop == ZFS_PROP_SHARESMB) &&
998fac3008cSeschrock 			    strcmp(strval, "on") != 0 &&
999fac3008cSeschrock 			    strcmp(strval, "off") != 0) {
1000da6c28aaSamw 				zfs_share_proto_t proto;
1001da6c28aaSamw 
1002da6c28aaSamw 				if (prop == ZFS_PROP_SHARESMB)
1003da6c28aaSamw 					proto = PROTO_SMB;
1004da6c28aaSamw 				else
1005da6c28aaSamw 					proto = PROTO_NFS;
100667331909Sdougm 
100767331909Sdougm 				/*
1008da6c28aaSamw 				 * Must be an valid sharing protocol
1009da6c28aaSamw 				 * option string so init the libshare
1010da6c28aaSamw 				 * in order to enable the parser and
1011da6c28aaSamw 				 * then parse the options. We use the
1012da6c28aaSamw 				 * control API since we don't care about
1013da6c28aaSamw 				 * the current configuration and don't
101467331909Sdougm 				 * want the overhead of loading it
101567331909Sdougm 				 * until we actually do something.
101667331909Sdougm 				 */
101767331909Sdougm 
1018fac3008cSeschrock 				if (zfs_init_libshare(hdl,
1019fac3008cSeschrock 				    SA_INIT_CONTROL_API) != SA_OK) {
1020fac3008cSeschrock 					/*
1021fac3008cSeschrock 					 * An error occurred so we can't do
1022fac3008cSeschrock 					 * anything
1023fac3008cSeschrock 					 */
1024fac3008cSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1025fac3008cSeschrock 					    "'%s' cannot be set: problem "
1026fac3008cSeschrock 					    "in share initialization"),
1027fac3008cSeschrock 					    propname);
1028fac3008cSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1029fac3008cSeschrock 					    errbuf);
1030fac3008cSeschrock 					goto error;
1031fac3008cSeschrock 				}
103267331909Sdougm 
1033da6c28aaSamw 				if (zfs_parse_options(strval, proto) != SA_OK) {
1034fac3008cSeschrock 					/*
1035fac3008cSeschrock 					 * There was an error in parsing so
1036fac3008cSeschrock 					 * deal with it by issuing an error
1037fac3008cSeschrock 					 * message and leaving after
1038fac3008cSeschrock 					 * uninitializing the the libshare
1039fac3008cSeschrock 					 * interface.
1040fac3008cSeschrock 					 */
1041fac3008cSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1042fac3008cSeschrock 					    "'%s' cannot be set to invalid "
1043fac3008cSeschrock 					    "options"), propname);
1044fac3008cSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1045fac3008cSeschrock 					    errbuf);
1046fac3008cSeschrock 					zfs_uninit_libshare(hdl);
1047fac3008cSeschrock 					goto error;
1048fac3008cSeschrock 				}
104967331909Sdougm 				zfs_uninit_libshare(hdl);
105067331909Sdougm 			}
105167331909Sdougm 
1052da6c28aaSamw 			break;
1053da6c28aaSamw 		case ZFS_PROP_UTF8ONLY:
1054da6c28aaSamw 			chosen_utf = (int)intval;
1055da6c28aaSamw 			break;
1056da6c28aaSamw 		case ZFS_PROP_NORMALIZE:
1057da6c28aaSamw 			chosen_normal = (int)intval;
1058f3861e1aSahl 			break;
1059e9dbad6fSeschrock 		}
1060fa9e4066Sahrens 
1061e9dbad6fSeschrock 		/*
1062e9dbad6fSeschrock 		 * For changes to existing volumes, we have some additional
1063e9dbad6fSeschrock 		 * checks to enforce.
1064e9dbad6fSeschrock 		 */
1065e9dbad6fSeschrock 		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1066e9dbad6fSeschrock 			uint64_t volsize = zfs_prop_get_int(zhp,
1067e9dbad6fSeschrock 			    ZFS_PROP_VOLSIZE);
1068e9dbad6fSeschrock 			uint64_t blocksize = zfs_prop_get_int(zhp,
1069e9dbad6fSeschrock 			    ZFS_PROP_VOLBLOCKSIZE);
1070e9dbad6fSeschrock 			char buf[64];
1071e9dbad6fSeschrock 
1072e9dbad6fSeschrock 			switch (prop) {
1073e9dbad6fSeschrock 			case ZFS_PROP_RESERVATION:
1074a9799022Sck 			case ZFS_PROP_REFRESERVATION:
1075e9dbad6fSeschrock 				if (intval > volsize) {
1076e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1077e9dbad6fSeschrock 					    "'%s' is greater than current "
1078e9dbad6fSeschrock 					    "volume size"), propname);
1079e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1080e9dbad6fSeschrock 					    errbuf);
1081e9dbad6fSeschrock 					goto error;
1082e9dbad6fSeschrock 				}
1083e9dbad6fSeschrock 				break;
1084e9dbad6fSeschrock 
1085e9dbad6fSeschrock 			case ZFS_PROP_VOLSIZE:
1086e9dbad6fSeschrock 				if (intval % blocksize != 0) {
1087e9dbad6fSeschrock 					zfs_nicenum(blocksize, buf,
1088e9dbad6fSeschrock 					    sizeof (buf));
1089e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1090e9dbad6fSeschrock 					    "'%s' must be a multiple of "
1091e9dbad6fSeschrock 					    "volume block size (%s)"),
1092e9dbad6fSeschrock 					    propname, buf);
1093e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1094e9dbad6fSeschrock 					    errbuf);
1095e9dbad6fSeschrock 					goto error;
1096e9dbad6fSeschrock 				}
1097e9dbad6fSeschrock 
1098e9dbad6fSeschrock 				if (intval == 0) {
1099e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1100e9dbad6fSeschrock 					    "'%s' cannot be zero"),
1101e9dbad6fSeschrock 					    propname);
1102e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
1103e9dbad6fSeschrock 					    errbuf);
1104e9dbad6fSeschrock 					goto error;
1105e9dbad6fSeschrock 				}
1106f3861e1aSahl 				break;
1107fa9e4066Sahrens 			}
1108e9dbad6fSeschrock 		}
1109e9dbad6fSeschrock 	}
1110fa9e4066Sahrens 
1111da6c28aaSamw 	/*
1112da6c28aaSamw 	 * If normalization was chosen, but no UTF8 choice was made,
1113da6c28aaSamw 	 * enforce rejection of non-UTF8 names.
1114da6c28aaSamw 	 *
1115da6c28aaSamw 	 * If normalization was chosen, but rejecting non-UTF8 names
1116da6c28aaSamw 	 * was explicitly not chosen, it is an error.
1117da6c28aaSamw 	 */
1118de8267e0Stimh 	if (chosen_normal > 0 && chosen_utf < 0) {
1119da6c28aaSamw 		if (nvlist_add_uint64(ret,
1120da6c28aaSamw 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
1121da6c28aaSamw 			(void) no_memory(hdl);
1122da6c28aaSamw 			goto error;
1123da6c28aaSamw 		}
1124de8267e0Stimh 	} else if (chosen_normal > 0 && chosen_utf == 0) {
1125da6c28aaSamw 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1126da6c28aaSamw 		    "'%s' must be set 'on' if normalization chosen"),
1127da6c28aaSamw 		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1128da6c28aaSamw 		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1129da6c28aaSamw 		goto error;
1130da6c28aaSamw 	}
1131da6c28aaSamw 
1132e9dbad6fSeschrock 	/*
1133e9dbad6fSeschrock 	 * If this is an existing volume, and someone is setting the volsize,
1134e9dbad6fSeschrock 	 * make sure that it matches the reservation, or add it if necessary.
1135e9dbad6fSeschrock 	 */
1136e9dbad6fSeschrock 	if (zhp != NULL && type == ZFS_TYPE_VOLUME &&
1137e9dbad6fSeschrock 	    nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1138e9dbad6fSeschrock 	    &intval) == 0) {
1139e9dbad6fSeschrock 		uint64_t old_volsize = zfs_prop_get_int(zhp,
1140e9dbad6fSeschrock 		    ZFS_PROP_VOLSIZE);
1141a9b821a0Sck 		uint64_t old_reservation;
1142e9dbad6fSeschrock 		uint64_t new_reservation;
1143a9b821a0Sck 		zfs_prop_t resv_prop;
1144a9b821a0Sck 
11457b97dc1aSrm 		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1146a9b821a0Sck 			goto error;
1147a9b821a0Sck 		old_reservation = zfs_prop_get_int(zhp, resv_prop);
1148e9dbad6fSeschrock 
1149e9dbad6fSeschrock 		if (old_volsize == old_reservation &&
1150a9b821a0Sck 		    nvlist_lookup_uint64(ret, zfs_prop_to_name(resv_prop),
1151e9dbad6fSeschrock 		    &new_reservation) != 0) {
1152e9dbad6fSeschrock 			if (nvlist_add_uint64(ret,
1153a9b821a0Sck 			    zfs_prop_to_name(resv_prop), intval) != 0) {
1154e9dbad6fSeschrock 				(void) no_memory(hdl);
1155e9dbad6fSeschrock 				goto error;
1156e9dbad6fSeschrock 			}
1157fa9e4066Sahrens 		}
1158fa9e4066Sahrens 	}
1159e9dbad6fSeschrock 	return (ret);
1160fa9e4066Sahrens 
1161e9dbad6fSeschrock error:
1162e9dbad6fSeschrock 	nvlist_free(ret);
1163e9dbad6fSeschrock 	return (NULL);
1164fa9e4066Sahrens }
1165fa9e4066Sahrens 
1166fa9e4066Sahrens /*
1167fa9e4066Sahrens  * Given a property name and value, set the property for the given dataset.
1168fa9e4066Sahrens  */
1169fa9e4066Sahrens int
1170e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1171fa9e4066Sahrens {
1172fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1173e9dbad6fSeschrock 	int ret = -1;
1174e9dbad6fSeschrock 	prop_changelist_t *cl = NULL;
117599653d4eSeschrock 	char errbuf[1024];
117699653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
1177e9dbad6fSeschrock 	nvlist_t *nvl = NULL, *realprops;
1178e9dbad6fSeschrock 	zfs_prop_t prop;
11790068372bSMark J Musante 	boolean_t do_prefix;
11800068372bSMark J Musante 	uint64_t idx;
118199653d4eSeschrock 
118299653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
1183e9dbad6fSeschrock 	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
118499653d4eSeschrock 	    zhp->zfs_name);
118599653d4eSeschrock 
1186e9dbad6fSeschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1187e9dbad6fSeschrock 	    nvlist_add_string(nvl, propname, propval) != 0) {
1188e9dbad6fSeschrock 		(void) no_memory(hdl);
1189e9dbad6fSeschrock 		goto error;
1190fa9e4066Sahrens 	}
1191fa9e4066Sahrens 
11920a48a24eStimh 	if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
1193e9dbad6fSeschrock 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1194e9dbad6fSeschrock 		goto error;
1195990b4856Slling 
1196e9dbad6fSeschrock 	nvlist_free(nvl);
1197e9dbad6fSeschrock 	nvl = realprops;
1198e9dbad6fSeschrock 
1199e9dbad6fSeschrock 	prop = zfs_name_to_prop(propname);
1200e9dbad6fSeschrock 
12010069fd67STim Haley 	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1202e9dbad6fSeschrock 		goto error;
1203fa9e4066Sahrens 
1204fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
120599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
120699653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
120799653d4eSeschrock 		    "in a non-global zone"));
120899653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1209fa9e4066Sahrens 		goto error;
1210fa9e4066Sahrens 	}
1211fa9e4066Sahrens 
12120068372bSMark J Musante 	/*
12130068372bSMark J Musante 	 * If the dataset's canmount property is being set to noauto,
12140068372bSMark J Musante 	 * then we want to prevent unmounting & remounting it.
12150068372bSMark J Musante 	 */
12160068372bSMark J Musante 	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
12170068372bSMark J Musante 	    (zprop_string_to_index(prop, propval, &idx,
12180068372bSMark J Musante 	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
1219a227b7f4Shs 
1220a227b7f4Shs 	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
12210068372bSMark J Musante 		goto error;
1222fa9e4066Sahrens 
1223fa9e4066Sahrens 	/*
1224fa9e4066Sahrens 	 * Execute the corresponding ioctl() to set this property.
1225fa9e4066Sahrens 	 */
1226fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1227fa9e4066Sahrens 
1228990b4856Slling 	if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1229e9dbad6fSeschrock 		goto error;
1230e9dbad6fSeschrock 
1231ecd6cf80Smarks 	ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1232743a77edSAlan Wright 
1233fa9e4066Sahrens 	if (ret != 0) {
1234fa9e4066Sahrens 		switch (errno) {
1235fa9e4066Sahrens 
1236fa9e4066Sahrens 		case ENOSPC:
1237fa9e4066Sahrens 			/*
1238fa9e4066Sahrens 			 * For quotas and reservations, ENOSPC indicates
1239fa9e4066Sahrens 			 * something different; setting a quota or reservation
1240fa9e4066Sahrens 			 * doesn't use any disk space.
1241fa9e4066Sahrens 			 */
1242fa9e4066Sahrens 			switch (prop) {
1243fa9e4066Sahrens 			case ZFS_PROP_QUOTA:
1244a9799022Sck 			case ZFS_PROP_REFQUOTA:
124599653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
124699653d4eSeschrock 				    "size is less than current used or "
124799653d4eSeschrock 				    "reserved space"));
124899653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1249fa9e4066Sahrens 				break;
1250fa9e4066Sahrens 
1251fa9e4066Sahrens 			case ZFS_PROP_RESERVATION:
1252a9799022Sck 			case ZFS_PROP_REFRESERVATION:
125399653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
125499653d4eSeschrock 				    "size is greater than available space"));
125599653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1256fa9e4066Sahrens 				break;
1257fa9e4066Sahrens 
1258fa9e4066Sahrens 			default:
125999653d4eSeschrock 				(void) zfs_standard_error(hdl, errno, errbuf);
1260fa9e4066Sahrens 				break;
1261fa9e4066Sahrens 			}
1262fa9e4066Sahrens 			break;
1263fa9e4066Sahrens 
1264fa9e4066Sahrens 		case EBUSY:
126599653d4eSeschrock 			if (prop == ZFS_PROP_VOLBLOCKSIZE)
126699653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf);
126799653d4eSeschrock 			else
1268e9dbad6fSeschrock 				(void) zfs_standard_error(hdl, EBUSY, errbuf);
1269fa9e4066Sahrens 			break;
1270fa9e4066Sahrens 
12712a79c5feSlling 		case EROFS:
127299653d4eSeschrock 			(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
12732a79c5feSlling 			break;
12742a79c5feSlling 
1275c9431fa1Sahl 		case ENOTSUP:
1276c9431fa1Sahl 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
12779e6eda55Smarks 			    "pool and or dataset must be upgraded to set this "
127840feaa91Sahrens 			    "property or value"));
1279c9431fa1Sahl 			(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
1280c9431fa1Sahl 			break;
1281c9431fa1Sahl 
128215e6edf1Sgw 		case ERANGE:
128315e6edf1Sgw 			if (prop == ZFS_PROP_COMPRESSION) {
128415e6edf1Sgw 				(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
128515e6edf1Sgw 				    "property setting is not allowed on "
128615e6edf1Sgw 				    "bootable datasets"));
128715e6edf1Sgw 				(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
128815e6edf1Sgw 			} else {
128915e6edf1Sgw 				(void) zfs_standard_error(hdl, errno, errbuf);
129015e6edf1Sgw 			}
129115e6edf1Sgw 			break;
129215e6edf1Sgw 
1293fa9e4066Sahrens 		case EOVERFLOW:
1294fa9e4066Sahrens 			/*
1295fa9e4066Sahrens 			 * This platform can't address a volume this big.
1296fa9e4066Sahrens 			 */
1297fa9e4066Sahrens #ifdef _ILP32
1298fa9e4066Sahrens 			if (prop == ZFS_PROP_VOLSIZE) {
129999653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1300fa9e4066Sahrens 				break;
1301fa9e4066Sahrens 			}
1302fa9e4066Sahrens #endif
130399653d4eSeschrock 			/* FALLTHROUGH */
1304fa9e4066Sahrens 		default:
130599653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
1306fa9e4066Sahrens 		}
1307fa9e4066Sahrens 	} else {
1308a227b7f4Shs 		if (do_prefix)
1309a227b7f4Shs 			ret = changelist_postfix(cl);
1310a227b7f4Shs 
1311fa9e4066Sahrens 		/*
1312fa9e4066Sahrens 		 * Refresh the statistics so the new property value
1313fa9e4066Sahrens 		 * is reflected.
1314fa9e4066Sahrens 		 */
1315a227b7f4Shs 		if (ret == 0)
1316e9dbad6fSeschrock 			(void) get_stats(zhp);
1317fa9e4066Sahrens 	}
1318fa9e4066Sahrens 
1319fa9e4066Sahrens error:
1320e9dbad6fSeschrock 	nvlist_free(nvl);
1321e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
1322e9dbad6fSeschrock 	if (cl)
1323e9dbad6fSeschrock 		changelist_free(cl);
1324fa9e4066Sahrens 	return (ret);
1325fa9e4066Sahrens }
1326fa9e4066Sahrens 
1327fa9e4066Sahrens /*
1328fa9e4066Sahrens  * Given a property, inherit the value from the parent dataset.
1329fa9e4066Sahrens  */
1330fa9e4066Sahrens int
1331e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
1332fa9e4066Sahrens {
1333fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1334fa9e4066Sahrens 	int ret;
1335fa9e4066Sahrens 	prop_changelist_t *cl;
133699653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
133799653d4eSeschrock 	char errbuf[1024];
1338e9dbad6fSeschrock 	zfs_prop_t prop;
133999653d4eSeschrock 
134099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
134199653d4eSeschrock 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1342fa9e4066Sahrens 
1343990b4856Slling 	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
1344e9dbad6fSeschrock 		/*
1345e9dbad6fSeschrock 		 * For user properties, the amount of work we have to do is very
1346e9dbad6fSeschrock 		 * small, so just do it here.
1347e9dbad6fSeschrock 		 */
1348e9dbad6fSeschrock 		if (!zfs_prop_user(propname)) {
1349e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1350e9dbad6fSeschrock 			    "invalid property"));
1351e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1352e9dbad6fSeschrock 		}
1353e9dbad6fSeschrock 
1354e9dbad6fSeschrock 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1355e9dbad6fSeschrock 		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1356e9dbad6fSeschrock 
1357e45ce728Sahrens 		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
1358e9dbad6fSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1359e9dbad6fSeschrock 
1360e9dbad6fSeschrock 		return (0);
1361e9dbad6fSeschrock 	}
1362e9dbad6fSeschrock 
1363fa9e4066Sahrens 	/*
1364fa9e4066Sahrens 	 * Verify that this property is inheritable.
1365fa9e4066Sahrens 	 */
136699653d4eSeschrock 	if (zfs_prop_readonly(prop))
136799653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1368fa9e4066Sahrens 
136999653d4eSeschrock 	if (!zfs_prop_inheritable(prop))
137099653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1371fa9e4066Sahrens 
1372fa9e4066Sahrens 	/*
1373fa9e4066Sahrens 	 * Check to see if the value applies to this type
1374fa9e4066Sahrens 	 */
137599653d4eSeschrock 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
137699653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1377fa9e4066Sahrens 
1378bf7c2d40Srm 	/*
1379bf7c2d40Srm 	 * Normalize the name, to get rid of shorthand abbrevations.
1380bf7c2d40Srm 	 */
1381bf7c2d40Srm 	propname = zfs_prop_to_name(prop);
1382fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1383e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1384fa9e4066Sahrens 
1385fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1386fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
138799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
138899653d4eSeschrock 		    "dataset is used in a non-global zone"));
138999653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1390fa9e4066Sahrens 	}
1391fa9e4066Sahrens 
1392fa9e4066Sahrens 	/*
1393fa9e4066Sahrens 	 * Determine datasets which will be affected by this change, if any.
1394fa9e4066Sahrens 	 */
13950069fd67STim Haley 	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1396fa9e4066Sahrens 		return (-1);
1397fa9e4066Sahrens 
1398fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
139999653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
140099653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
140199653d4eSeschrock 		    "in a non-global zone"));
140299653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1403fa9e4066Sahrens 		goto error;
1404fa9e4066Sahrens 	}
1405fa9e4066Sahrens 
1406fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1407fa9e4066Sahrens 		goto error;
1408fa9e4066Sahrens 
1409e45ce728Sahrens 	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
141099653d4eSeschrock 		return (zfs_standard_error(hdl, errno, errbuf));
1411fa9e4066Sahrens 	} else {
1412fa9e4066Sahrens 
1413efc555ebSnd 		if ((ret = changelist_postfix(cl)) != 0)
1414fa9e4066Sahrens 			goto error;
1415fa9e4066Sahrens 
1416fa9e4066Sahrens 		/*
1417fa9e4066Sahrens 		 * Refresh the statistics so the new property is reflected.
1418fa9e4066Sahrens 		 */
1419fa9e4066Sahrens 		(void) get_stats(zhp);
1420fa9e4066Sahrens 	}
1421fa9e4066Sahrens 
1422fa9e4066Sahrens error:
1423fa9e4066Sahrens 	changelist_free(cl);
1424fa9e4066Sahrens 	return (ret);
1425fa9e4066Sahrens }
1426fa9e4066Sahrens 
14277f7322feSeschrock /*
14287f7322feSeschrock  * True DSL properties are stored in an nvlist.  The following two functions
14297f7322feSeschrock  * extract them appropriately.
14307f7322feSeschrock  */
14317f7322feSeschrock static uint64_t
14327f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
14337f7322feSeschrock {
14347f7322feSeschrock 	nvlist_t *nv;
14357f7322feSeschrock 	uint64_t value;
14367f7322feSeschrock 
1437a2eea2e1Sahrens 	*source = NULL;
14387f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
14397f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
1440990b4856Slling 		verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
1441990b4856Slling 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
14427f7322feSeschrock 	} else {
14432e5e9e19SSanjeev Bagewadi 		verify(!zhp->zfs_props_table ||
14442e5e9e19SSanjeev Bagewadi 		    zhp->zfs_props_table[prop] == B_TRUE);
14457f7322feSeschrock 		value = zfs_prop_default_numeric(prop);
14467f7322feSeschrock 		*source = "";
14477f7322feSeschrock 	}
14487f7322feSeschrock 
14497f7322feSeschrock 	return (value);
14507f7322feSeschrock }
14517f7322feSeschrock 
14527f7322feSeschrock static char *
14537f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
14547f7322feSeschrock {
14557f7322feSeschrock 	nvlist_t *nv;
14567f7322feSeschrock 	char *value;
14577f7322feSeschrock 
1458a2eea2e1Sahrens 	*source = NULL;
14597f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
14607f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
1461990b4856Slling 		verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
1462990b4856Slling 		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
14637f7322feSeschrock 	} else {
14642e5e9e19SSanjeev Bagewadi 		verify(!zhp->zfs_props_table ||
14652e5e9e19SSanjeev Bagewadi 		    zhp->zfs_props_table[prop] == B_TRUE);
14667f7322feSeschrock 		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
14677f7322feSeschrock 			value = "";
14687f7322feSeschrock 		*source = "";
14697f7322feSeschrock 	}
14707f7322feSeschrock 
14717f7322feSeschrock 	return (value);
14727f7322feSeschrock }
14737f7322feSeschrock 
1474fa9e4066Sahrens /*
1475fa9e4066Sahrens  * Internal function for getting a numeric property.  Both zfs_prop_get() and
1476fa9e4066Sahrens  * zfs_prop_get_int() are built using this interface.
1477fa9e4066Sahrens  *
1478fa9e4066Sahrens  * Certain properties can be overridden using 'mount -o'.  In this case, scan
1479fa9e4066Sahrens  * the contents of the /etc/mnttab entry, searching for the appropriate options.
1480fa9e4066Sahrens  * If they differ from the on-disk values, report the current values and mark
1481fa9e4066Sahrens  * the source "temporary".
1482fa9e4066Sahrens  */
148399653d4eSeschrock static int
1484990b4856Slling get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
148599653d4eSeschrock     char **source, uint64_t *val)
1486fa9e4066Sahrens {
1487bd00f61bSrm 	zfs_cmd_t zc = { 0 };
148896510749Stimh 	nvlist_t *zplprops = NULL;
1489fa9e4066Sahrens 	struct mnttab mnt;
14903ccfa83cSahrens 	char *mntopt_on = NULL;
14913ccfa83cSahrens 	char *mntopt_off = NULL;
1492fa9e4066Sahrens 
1493fa9e4066Sahrens 	*source = NULL;
1494fa9e4066Sahrens 
14953ccfa83cSahrens 	switch (prop) {
14963ccfa83cSahrens 	case ZFS_PROP_ATIME:
14973ccfa83cSahrens 		mntopt_on = MNTOPT_ATIME;
14983ccfa83cSahrens 		mntopt_off = MNTOPT_NOATIME;
14993ccfa83cSahrens 		break;
15003ccfa83cSahrens 
15013ccfa83cSahrens 	case ZFS_PROP_DEVICES:
15023ccfa83cSahrens 		mntopt_on = MNTOPT_DEVICES;
15033ccfa83cSahrens 		mntopt_off = MNTOPT_NODEVICES;
15043ccfa83cSahrens 		break;
15053ccfa83cSahrens 
15063ccfa83cSahrens 	case ZFS_PROP_EXEC:
15073ccfa83cSahrens 		mntopt_on = MNTOPT_EXEC;
15083ccfa83cSahrens 		mntopt_off = MNTOPT_NOEXEC;
15093ccfa83cSahrens 		break;
15103ccfa83cSahrens 
15113ccfa83cSahrens 	case ZFS_PROP_READONLY:
15123ccfa83cSahrens 		mntopt_on = MNTOPT_RO;
15133ccfa83cSahrens 		mntopt_off = MNTOPT_RW;
15143ccfa83cSahrens 		break;
15153ccfa83cSahrens 
15163ccfa83cSahrens 	case ZFS_PROP_SETUID:
15173ccfa83cSahrens 		mntopt_on = MNTOPT_SETUID;
15183ccfa83cSahrens 		mntopt_off = MNTOPT_NOSETUID;
15193ccfa83cSahrens 		break;
15203ccfa83cSahrens 
15213ccfa83cSahrens 	case ZFS_PROP_XATTR:
15223ccfa83cSahrens 		mntopt_on = MNTOPT_XATTR;
15233ccfa83cSahrens 		mntopt_off = MNTOPT_NOXATTR;
15243ccfa83cSahrens 		break;
1525da6c28aaSamw 
1526da6c28aaSamw 	case ZFS_PROP_NBMAND:
1527da6c28aaSamw 		mntopt_on = MNTOPT_NBMAND;
1528da6c28aaSamw 		mntopt_off = MNTOPT_NONBMAND;
1529da6c28aaSamw 		break;
15303ccfa83cSahrens 	}
15313ccfa83cSahrens 
15323bb79becSeschrock 	/*
15333bb79becSeschrock 	 * Because looking up the mount options is potentially expensive
15343bb79becSeschrock 	 * (iterating over all of /etc/mnttab), we defer its calculation until
15353bb79becSeschrock 	 * we're looking up a property which requires its presence.
15363bb79becSeschrock 	 */
15373bb79becSeschrock 	if (!zhp->zfs_mntcheck &&
15383ccfa83cSahrens 	    (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
1539ebedde84SEric Taylor 		libzfs_handle_t *hdl = zhp->zfs_hdl;
1540ebedde84SEric Taylor 		struct mnttab entry;
15413bb79becSeschrock 
1542ebedde84SEric Taylor 		if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
1543ebedde84SEric Taylor 			zhp->zfs_mntopts = zfs_strdup(hdl,
15443ccfa83cSahrens 			    entry.mnt_mntopts);
15453ccfa83cSahrens 			if (zhp->zfs_mntopts == NULL)
15463ccfa83cSahrens 				return (-1);
15473ccfa83cSahrens 		}
15483bb79becSeschrock 
15493bb79becSeschrock 		zhp->zfs_mntcheck = B_TRUE;
15503bb79becSeschrock 	}
15513bb79becSeschrock 
1552fa9e4066Sahrens 	if (zhp->zfs_mntopts == NULL)
1553fa9e4066Sahrens 		mnt.mnt_mntopts = "";
1554fa9e4066Sahrens 	else
1555fa9e4066Sahrens 		mnt.mnt_mntopts = zhp->zfs_mntopts;
1556fa9e4066Sahrens 
1557fa9e4066Sahrens 	switch (prop) {
1558fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1559fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1560fa9e4066Sahrens 	case ZFS_PROP_EXEC:
15613ccfa83cSahrens 	case ZFS_PROP_READONLY:
15623ccfa83cSahrens 	case ZFS_PROP_SETUID:
15633ccfa83cSahrens 	case ZFS_PROP_XATTR:
1564da6c28aaSamw 	case ZFS_PROP_NBMAND:
156599653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1566fa9e4066Sahrens 
15673ccfa83cSahrens 		if (hasmntopt(&mnt, mntopt_on) && !*val) {
156899653d4eSeschrock 			*val = B_TRUE;
1569fa9e4066Sahrens 			if (src)
1570990b4856Slling 				*src = ZPROP_SRC_TEMPORARY;
15713ccfa83cSahrens 		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
157299653d4eSeschrock 			*val = B_FALSE;
1573fa9e4066Sahrens 			if (src)
1574990b4856Slling 				*src = ZPROP_SRC_TEMPORARY;
1575fa9e4066Sahrens 		}
157699653d4eSeschrock 		break;
1577fa9e4066Sahrens 
15783ccfa83cSahrens 	case ZFS_PROP_CANMOUNT:
157999653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1580a227b7f4Shs 		if (*val != ZFS_CANMOUNT_ON)
1581fda77a98Srm 			*source = zhp->zfs_name;
1582fda77a98Srm 		else
1583fda77a98Srm 			*source = "";	/* default */
158499653d4eSeschrock 		break;
1585fa9e4066Sahrens 
1586fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1587a9799022Sck 	case ZFS_PROP_REFQUOTA:
1588fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1589a9799022Sck 	case ZFS_PROP_REFRESERVATION:
1590a2eea2e1Sahrens 		*val = getprop_uint64(zhp, prop, source);
1591a2eea2e1Sahrens 		if (*val == 0)
1592fa9e4066Sahrens 			*source = "";	/* default */
1593fa9e4066Sahrens 		else
1594fa9e4066Sahrens 			*source = zhp->zfs_name;
159599653d4eSeschrock 		break;
1596fa9e4066Sahrens 
1597fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
159899653d4eSeschrock 		*val = (zhp->zfs_mntopts != NULL);
159999653d4eSeschrock 		break;
1600fa9e4066Sahrens 
160139c23413Seschrock 	case ZFS_PROP_NUMCLONES:
160239c23413Seschrock 		*val = zhp->zfs_dmustats.dds_num_clones;
160339c23413Seschrock 		break;
160439c23413Seschrock 
1605bd00f61bSrm 	case ZFS_PROP_VERSION:
1606de8267e0Stimh 	case ZFS_PROP_NORMALIZE:
1607de8267e0Stimh 	case ZFS_PROP_UTF8ONLY:
1608de8267e0Stimh 	case ZFS_PROP_CASE:
1609de8267e0Stimh 		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
1610de8267e0Stimh 		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
16113d7934e1Srm 			return (-1);
1612bd00f61bSrm 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1613de8267e0Stimh 		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
1614de8267e0Stimh 			zcmd_free_nvlists(&zc);
1615f4b94bdeSMatthew Ahrens 			return (-1);
1616bd00f61bSrm 		}
1617de8267e0Stimh 		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
1618de8267e0Stimh 		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
1619de8267e0Stimh 		    val) != 0) {
1620de8267e0Stimh 			zcmd_free_nvlists(&zc);
1621f4b94bdeSMatthew Ahrens 			return (-1);
1622de8267e0Stimh 		}
162396510749Stimh 		if (zplprops)
162496510749Stimh 			nvlist_free(zplprops);
1625de8267e0Stimh 		zcmd_free_nvlists(&zc);
1626bd00f61bSrm 		break;
1627bd00f61bSrm 
1628fa9e4066Sahrens 	default:
1629e7437265Sahrens 		switch (zfs_prop_get_type(prop)) {
163091ebeef5Sahrens 		case PROP_TYPE_NUMBER:
163191ebeef5Sahrens 		case PROP_TYPE_INDEX:
1632e7437265Sahrens 			*val = getprop_uint64(zhp, prop, source);
163374e7dc98SMatthew Ahrens 			/*
163414843421SMatthew Ahrens 			 * If we tried to use a default value for a
163574e7dc98SMatthew Ahrens 			 * readonly property, it means that it was not
163674e7dc98SMatthew Ahrens 			 * present; return an error.
163774e7dc98SMatthew Ahrens 			 */
163874e7dc98SMatthew Ahrens 			if (zfs_prop_readonly(prop) &&
163974e7dc98SMatthew Ahrens 			    *source && (*source)[0] == '\0') {
164074e7dc98SMatthew Ahrens 				return (-1);
164174e7dc98SMatthew Ahrens 			}
1642e7437265Sahrens 			break;
1643e7437265Sahrens 
164491ebeef5Sahrens 		case PROP_TYPE_STRING:
1645e7437265Sahrens 		default:
1646e7437265Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1647e7437265Sahrens 			    "cannot get non-numeric property"));
1648e7437265Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
1649e7437265Sahrens 			    dgettext(TEXT_DOMAIN, "internal error")));
1650e7437265Sahrens 		}
1651fa9e4066Sahrens 	}
1652fa9e4066Sahrens 
1653fa9e4066Sahrens 	return (0);
1654fa9e4066Sahrens }
1655fa9e4066Sahrens 
1656fa9e4066Sahrens /*
1657fa9e4066Sahrens  * Calculate the source type, given the raw source string.
1658fa9e4066Sahrens  */
1659fa9e4066Sahrens static void
1660990b4856Slling get_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
1661fa9e4066Sahrens     char *statbuf, size_t statlen)
1662fa9e4066Sahrens {
1663990b4856Slling 	if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
1664fa9e4066Sahrens 		return;
1665fa9e4066Sahrens 
1666fa9e4066Sahrens 	if (source == NULL) {
1667990b4856Slling 		*srctype = ZPROP_SRC_NONE;
1668fa9e4066Sahrens 	} else if (source[0] == '\0') {
1669990b4856Slling 		*srctype = ZPROP_SRC_DEFAULT;
1670fa9e4066Sahrens 	} else {
1671fa9e4066Sahrens 		if (strcmp(source, zhp->zfs_name) == 0) {
1672990b4856Slling 			*srctype = ZPROP_SRC_LOCAL;
1673fa9e4066Sahrens 		} else {
1674fa9e4066Sahrens 			(void) strlcpy(statbuf, source, statlen);
1675990b4856Slling 			*srctype = ZPROP_SRC_INHERITED;
1676fa9e4066Sahrens 		}
1677fa9e4066Sahrens 	}
1678fa9e4066Sahrens 
1679fa9e4066Sahrens }
1680fa9e4066Sahrens 
1681fa9e4066Sahrens /*
1682fa9e4066Sahrens  * Retrieve a property from the given object.  If 'literal' is specified, then
1683fa9e4066Sahrens  * numbers are left as exact values.  Otherwise, numbers are converted to a
1684fa9e4066Sahrens  * human-readable form.
1685fa9e4066Sahrens  *
1686fa9e4066Sahrens  * Returns 0 on success, or -1 on error.
1687fa9e4066Sahrens  */
1688fa9e4066Sahrens int
1689fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
1690990b4856Slling     zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1691fa9e4066Sahrens {
1692fa9e4066Sahrens 	char *source = NULL;
1693fa9e4066Sahrens 	uint64_t val;
1694fa9e4066Sahrens 	char *str;
1695e9dbad6fSeschrock 	const char *strval;
1696fa9e4066Sahrens 
1697fa9e4066Sahrens 	/*
1698fa9e4066Sahrens 	 * Check to see if this property applies to our object
1699fa9e4066Sahrens 	 */
1700fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1701fa9e4066Sahrens 		return (-1);
1702fa9e4066Sahrens 
1703fa9e4066Sahrens 	if (src)
1704990b4856Slling 		*src = ZPROP_SRC_NONE;
1705fa9e4066Sahrens 
1706fa9e4066Sahrens 	switch (prop) {
1707fa9e4066Sahrens 	case ZFS_PROP_CREATION:
1708fa9e4066Sahrens 		/*
1709fa9e4066Sahrens 		 * 'creation' is a time_t stored in the statistics.  We convert
1710fa9e4066Sahrens 		 * this into a string unless 'literal' is specified.
1711fa9e4066Sahrens 		 */
1712fa9e4066Sahrens 		{
1713a2eea2e1Sahrens 			val = getprop_uint64(zhp, prop, &source);
1714a2eea2e1Sahrens 			time_t time = (time_t)val;
1715fa9e4066Sahrens 			struct tm t;
1716fa9e4066Sahrens 
1717fa9e4066Sahrens 			if (literal ||
1718fa9e4066Sahrens 			    localtime_r(&time, &t) == NULL ||
1719fa9e4066Sahrens 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1720fa9e4066Sahrens 			    &t) == 0)
1721a2eea2e1Sahrens 				(void) snprintf(propbuf, proplen, "%llu", val);
1722fa9e4066Sahrens 		}
1723fa9e4066Sahrens 		break;
1724fa9e4066Sahrens 
1725fa9e4066Sahrens 	case ZFS_PROP_MOUNTPOINT:
1726fa9e4066Sahrens 		/*
1727fa9e4066Sahrens 		 * Getting the precise mountpoint can be tricky.
1728fa9e4066Sahrens 		 *
1729fa9e4066Sahrens 		 *  - for 'none' or 'legacy', return those values.
1730fa9e4066Sahrens 		 *  - for inherited mountpoints, we want to take everything
1731fa9e4066Sahrens 		 *    after our ancestor and append it to the inherited value.
1732fa9e4066Sahrens 		 *
1733fa9e4066Sahrens 		 * If the pool has an alternate root, we want to prepend that
1734fa9e4066Sahrens 		 * root to any values we return.
1735fa9e4066Sahrens 		 */
173629ab75c9Srm 
17377f7322feSeschrock 		str = getprop_string(zhp, prop, &source);
1738fa9e4066Sahrens 
1739b21718f0Sgw 		if (str[0] == '/') {
174029ab75c9Srm 			char buf[MAXPATHLEN];
174129ab75c9Srm 			char *root = buf;
17427f7322feSeschrock 			const char *relpath = zhp->zfs_name + strlen(source);
1743fa9e4066Sahrens 
1744fa9e4066Sahrens 			if (relpath[0] == '/')
1745fa9e4066Sahrens 				relpath++;
1746b21718f0Sgw 
174729ab75c9Srm 			if ((zpool_get_prop(zhp->zpool_hdl,
174829ab75c9Srm 			    ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
174929ab75c9Srm 			    (strcmp(root, "-") == 0))
175029ab75c9Srm 				root[0] = '\0';
1751b21718f0Sgw 			/*
1752b21718f0Sgw 			 * Special case an alternate root of '/'. This will
1753b21718f0Sgw 			 * avoid having multiple leading slashes in the
1754b21718f0Sgw 			 * mountpoint path.
1755b21718f0Sgw 			 */
1756b21718f0Sgw 			if (strcmp(root, "/") == 0)
1757b21718f0Sgw 				root++;
1758b21718f0Sgw 
1759b21718f0Sgw 			/*
1760b21718f0Sgw 			 * If the mountpoint is '/' then skip over this
1761b21718f0Sgw 			 * if we are obtaining either an alternate root or
1762b21718f0Sgw 			 * an inherited mountpoint.
1763b21718f0Sgw 			 */
1764b21718f0Sgw 			if (str[1] == '\0' && (root[0] != '\0' ||
1765b21718f0Sgw 			    relpath[0] != '\0'))
17667f7322feSeschrock 				str++;
1767fa9e4066Sahrens 
1768fa9e4066Sahrens 			if (relpath[0] == '\0')
1769fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s",
17707f7322feSeschrock 				    root, str);
1771fa9e4066Sahrens 			else
1772fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
17737f7322feSeschrock 				    root, str, relpath[0] == '@' ? "" : "/",
1774fa9e4066Sahrens 				    relpath);
1775fa9e4066Sahrens 		} else {
1776fa9e4066Sahrens 			/* 'legacy' or 'none' */
17777f7322feSeschrock 			(void) strlcpy(propbuf, str, proplen);
1778fa9e4066Sahrens 		}
1779fa9e4066Sahrens 
1780fa9e4066Sahrens 		break;
1781fa9e4066Sahrens 
1782fa9e4066Sahrens 	case ZFS_PROP_ORIGIN:
1783a2eea2e1Sahrens 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
1784fa9e4066Sahrens 		    proplen);
1785fa9e4066Sahrens 		/*
1786fa9e4066Sahrens 		 * If there is no parent at all, return failure to indicate that
1787fa9e4066Sahrens 		 * it doesn't apply to this dataset.
1788fa9e4066Sahrens 		 */
1789fa9e4066Sahrens 		if (propbuf[0] == '\0')
1790fa9e4066Sahrens 			return (-1);
1791fa9e4066Sahrens 		break;
1792fa9e4066Sahrens 
1793fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1794a9799022Sck 	case ZFS_PROP_REFQUOTA:
1795fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1796a9799022Sck 	case ZFS_PROP_REFRESERVATION:
1797a9799022Sck 
179899653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
179999653d4eSeschrock 			return (-1);
1800fa9e4066Sahrens 
1801fa9e4066Sahrens 		/*
1802fa9e4066Sahrens 		 * If quota or reservation is 0, we translate this into 'none'
1803fa9e4066Sahrens 		 * (unless literal is set), and indicate that it's the default
1804fa9e4066Sahrens 		 * value.  Otherwise, we print the number nicely and indicate
1805fa9e4066Sahrens 		 * that its set locally.
1806fa9e4066Sahrens 		 */
1807fa9e4066Sahrens 		if (val == 0) {
1808fa9e4066Sahrens 			if (literal)
1809fa9e4066Sahrens 				(void) strlcpy(propbuf, "0", proplen);
1810fa9e4066Sahrens 			else
1811fa9e4066Sahrens 				(void) strlcpy(propbuf, "none", proplen);
1812fa9e4066Sahrens 		} else {
1813fa9e4066Sahrens 			if (literal)
18145ad82045Snd 				(void) snprintf(propbuf, proplen, "%llu",
1815b1b8ab34Slling 				    (u_longlong_t)val);
1816fa9e4066Sahrens 			else
1817fa9e4066Sahrens 				zfs_nicenum(val, propbuf, proplen);
1818fa9e4066Sahrens 		}
1819fa9e4066Sahrens 		break;
1820fa9e4066Sahrens 
1821fa9e4066Sahrens 	case ZFS_PROP_COMPRESSRATIO:
182299653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
182399653d4eSeschrock 			return (-1);
18245ad82045Snd 		(void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t)
18255ad82045Snd 		    val / 100, (longlong_t)val % 100);
1826fa9e4066Sahrens 		break;
1827fa9e4066Sahrens 
1828fa9e4066Sahrens 	case ZFS_PROP_TYPE:
1829fa9e4066Sahrens 		switch (zhp->zfs_type) {
1830fa9e4066Sahrens 		case ZFS_TYPE_FILESYSTEM:
1831fa9e4066Sahrens 			str = "filesystem";
1832fa9e4066Sahrens 			break;
1833fa9e4066Sahrens 		case ZFS_TYPE_VOLUME:
1834fa9e4066Sahrens 			str = "volume";
1835fa9e4066Sahrens 			break;
1836fa9e4066Sahrens 		case ZFS_TYPE_SNAPSHOT:
1837fa9e4066Sahrens 			str = "snapshot";
1838fa9e4066Sahrens 			break;
1839fa9e4066Sahrens 		default:
184099653d4eSeschrock 			abort();
1841fa9e4066Sahrens 		}
1842fa9e4066Sahrens 		(void) snprintf(propbuf, proplen, "%s", str);
1843fa9e4066Sahrens 		break;
1844fa9e4066Sahrens 
1845fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
1846fa9e4066Sahrens 		/*
1847fa9e4066Sahrens 		 * The 'mounted' property is a pseudo-property that described
1848fa9e4066Sahrens 		 * whether the filesystem is currently mounted.  Even though
1849fa9e4066Sahrens 		 * it's a boolean value, the typical values of "on" and "off"
1850fa9e4066Sahrens 		 * don't make sense, so we translate to "yes" and "no".
1851fa9e4066Sahrens 		 */
185299653d4eSeschrock 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
185399653d4eSeschrock 		    src, &source, &val) != 0)
185499653d4eSeschrock 			return (-1);
185599653d4eSeschrock 		if (val)
1856fa9e4066Sahrens 			(void) strlcpy(propbuf, "yes", proplen);
1857fa9e4066Sahrens 		else
1858fa9e4066Sahrens 			(void) strlcpy(propbuf, "no", proplen);
1859fa9e4066Sahrens 		break;
1860fa9e4066Sahrens 
1861fa9e4066Sahrens 	case ZFS_PROP_NAME:
1862fa9e4066Sahrens 		/*
1863fa9e4066Sahrens 		 * The 'name' property is a pseudo-property derived from the
1864fa9e4066Sahrens 		 * dataset name.  It is presented as a real property to simplify
1865fa9e4066Sahrens 		 * consumers.
1866fa9e4066Sahrens 		 */
1867fa9e4066Sahrens 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
1868fa9e4066Sahrens 		break;
1869fa9e4066Sahrens 
1870fa9e4066Sahrens 	default:
1871e7437265Sahrens 		switch (zfs_prop_get_type(prop)) {
187291ebeef5Sahrens 		case PROP_TYPE_NUMBER:
1873e7437265Sahrens 			if (get_numeric_property(zhp, prop, src,
1874e7437265Sahrens 			    &source, &val) != 0)
1875e7437265Sahrens 				return (-1);
1876e7437265Sahrens 			if (literal)
1877e7437265Sahrens 				(void) snprintf(propbuf, proplen, "%llu",
1878e7437265Sahrens 				    (u_longlong_t)val);
1879e7437265Sahrens 			else
1880e7437265Sahrens 				zfs_nicenum(val, propbuf, proplen);
1881e7437265Sahrens 			break;
1882e7437265Sahrens 
188391ebeef5Sahrens 		case PROP_TYPE_STRING:
1884e7437265Sahrens 			(void) strlcpy(propbuf,
1885e7437265Sahrens 			    getprop_string(zhp, prop, &source), proplen);
1886e7437265Sahrens 			break;
1887e7437265Sahrens 
188891ebeef5Sahrens 		case PROP_TYPE_INDEX:
1889fe192f76Sahrens 			if (get_numeric_property(zhp, prop, src,
1890fe192f76Sahrens 			    &source, &val) != 0)
1891fe192f76Sahrens 				return (-1);
1892fe192f76Sahrens 			if (zfs_prop_index_to_string(prop, val, &strval) != 0)
1893e7437265Sahrens 				return (-1);
1894e7437265Sahrens 			(void) strlcpy(propbuf, strval, proplen);
1895e7437265Sahrens 			break;
1896e7437265Sahrens 
1897e7437265Sahrens 		default:
1898e7437265Sahrens 			abort();
1899e7437265Sahrens 		}
1900fa9e4066Sahrens 	}
1901fa9e4066Sahrens 
1902fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1903fa9e4066Sahrens 
1904fa9e4066Sahrens 	return (0);
1905fa9e4066Sahrens }
1906fa9e4066Sahrens 
1907fa9e4066Sahrens /*
1908fa9e4066Sahrens  * Utility function to get the given numeric property.  Does no validation that
1909fa9e4066Sahrens  * the given property is the appropriate type; should only be used with
1910fa9e4066Sahrens  * hard-coded property types.
1911fa9e4066Sahrens  */
1912fa9e4066Sahrens uint64_t
1913fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
1914fa9e4066Sahrens {
1915fa9e4066Sahrens 	char *source;
191699653d4eSeschrock 	uint64_t val;
191799653d4eSeschrock 
19183cb34c60Sahrens 	(void) get_numeric_property(zhp, prop, NULL, &source, &val);
1919fa9e4066Sahrens 
192099653d4eSeschrock 	return (val);
1921fa9e4066Sahrens }
1922fa9e4066Sahrens 
19237b97dc1aSrm int
19247b97dc1aSrm zfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
19257b97dc1aSrm {
19267b97dc1aSrm 	char buf[64];
19277b97dc1aSrm 
192814843421SMatthew Ahrens 	(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
19297b97dc1aSrm 	return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
19307b97dc1aSrm }
19317b97dc1aSrm 
1932fa9e4066Sahrens /*
1933fa9e4066Sahrens  * Similar to zfs_prop_get(), but returns the value as an integer.
1934fa9e4066Sahrens  */
1935fa9e4066Sahrens int
1936fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
1937990b4856Slling     zprop_source_t *src, char *statbuf, size_t statlen)
1938fa9e4066Sahrens {
1939fa9e4066Sahrens 	char *source;
1940fa9e4066Sahrens 
1941fa9e4066Sahrens 	/*
1942fa9e4066Sahrens 	 * Check to see if this property applies to our object
1943fa9e4066Sahrens 	 */
1944e45ce728Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
1945ece3d9b3Slling 		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
194699653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
194799653d4eSeschrock 		    zfs_prop_to_name(prop)));
1948e45ce728Sahrens 	}
1949fa9e4066Sahrens 
1950fa9e4066Sahrens 	if (src)
1951990b4856Slling 		*src = ZPROP_SRC_NONE;
1952fa9e4066Sahrens 
195399653d4eSeschrock 	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
195499653d4eSeschrock 		return (-1);
1955fa9e4066Sahrens 
1956fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1957fa9e4066Sahrens 
1958fa9e4066Sahrens 	return (0);
1959fa9e4066Sahrens }
1960fa9e4066Sahrens 
196114843421SMatthew Ahrens static int
196214843421SMatthew Ahrens idmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
196314843421SMatthew Ahrens     char **domainp, idmap_rid_t *ridp)
196414843421SMatthew Ahrens {
196514843421SMatthew Ahrens 	idmap_handle_t *idmap_hdl = NULL;
196614843421SMatthew Ahrens 	idmap_get_handle_t *get_hdl = NULL;
196714843421SMatthew Ahrens 	idmap_stat status;
196814843421SMatthew Ahrens 	int err = EINVAL;
196914843421SMatthew Ahrens 
197014843421SMatthew Ahrens 	if (idmap_init(&idmap_hdl) != IDMAP_SUCCESS)
197114843421SMatthew Ahrens 		goto out;
197214843421SMatthew Ahrens 	if (idmap_get_create(idmap_hdl, &get_hdl) != IDMAP_SUCCESS)
197314843421SMatthew Ahrens 		goto out;
197414843421SMatthew Ahrens 
197514843421SMatthew Ahrens 	if (isuser) {
197614843421SMatthew Ahrens 		err = idmap_get_sidbyuid(get_hdl, id,
197714843421SMatthew Ahrens 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
197814843421SMatthew Ahrens 	} else {
197914843421SMatthew Ahrens 		err = idmap_get_sidbygid(get_hdl, id,
198014843421SMatthew Ahrens 		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
198114843421SMatthew Ahrens 	}
198214843421SMatthew Ahrens 	if (err == IDMAP_SUCCESS &&
198314843421SMatthew Ahrens 	    idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
198414843421SMatthew Ahrens 	    status == IDMAP_SUCCESS)
198514843421SMatthew Ahrens 		err = 0;
198614843421SMatthew Ahrens 	else
198714843421SMatthew Ahrens 		err = EINVAL;
198814843421SMatthew Ahrens out:
198914843421SMatthew Ahrens 	if (get_hdl)
199014843421SMatthew Ahrens 		idmap_get_destroy(get_hdl);
199114843421SMatthew Ahrens 	if (idmap_hdl)
199214843421SMatthew Ahrens 		(void) idmap_fini(idmap_hdl);
199314843421SMatthew Ahrens 	return (err);
199414843421SMatthew Ahrens }
199514843421SMatthew Ahrens 
199614843421SMatthew Ahrens /*
199714843421SMatthew Ahrens  * convert the propname into parameters needed by kernel
199814843421SMatthew Ahrens  * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
199914843421SMatthew Ahrens  * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
200014843421SMatthew Ahrens  */
200114843421SMatthew Ahrens static int
200214843421SMatthew Ahrens userquota_propname_decode(const char *propname, boolean_t zoned,
200314843421SMatthew Ahrens     zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
200414843421SMatthew Ahrens {
200514843421SMatthew Ahrens 	zfs_userquota_prop_t type;
200614843421SMatthew Ahrens 	char *cp, *end;
20073b12c289SMatthew Ahrens 	char *numericsid = NULL;
200814843421SMatthew Ahrens 	boolean_t isuser;
200914843421SMatthew Ahrens 
201014843421SMatthew Ahrens 	domain[0] = '\0';
201114843421SMatthew Ahrens 
201214843421SMatthew Ahrens 	/* Figure out the property type ({user|group}{quota|space}) */
201314843421SMatthew Ahrens 	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
201414843421SMatthew Ahrens 		if (strncmp(propname, zfs_userquota_prop_prefixes[type],
201514843421SMatthew Ahrens 		    strlen(zfs_userquota_prop_prefixes[type])) == 0)
201614843421SMatthew Ahrens 			break;
201714843421SMatthew Ahrens 	}
201814843421SMatthew Ahrens 	if (type == ZFS_NUM_USERQUOTA_PROPS)
201914843421SMatthew Ahrens 		return (EINVAL);
202014843421SMatthew Ahrens 	*typep = type;
202114843421SMatthew Ahrens 
202214843421SMatthew Ahrens 	isuser = (type == ZFS_PROP_USERQUOTA ||
202314843421SMatthew Ahrens 	    type == ZFS_PROP_USERUSED);
202414843421SMatthew Ahrens 
202514843421SMatthew Ahrens 	cp = strchr(propname, '@') + 1;
202614843421SMatthew Ahrens 
202714843421SMatthew Ahrens 	if (strchr(cp, '@')) {
202814843421SMatthew Ahrens 		/*
202914843421SMatthew Ahrens 		 * It's a SID name (eg "user@domain") that needs to be
20303b12c289SMatthew Ahrens 		 * turned into S-1-domainID-RID.
203114843421SMatthew Ahrens 		 */
20323b12c289SMatthew Ahrens 		directory_error_t e;
203314843421SMatthew Ahrens 		if (zoned && getzoneid() == GLOBAL_ZONEID)
203414843421SMatthew Ahrens 			return (ENOENT);
20353b12c289SMatthew Ahrens 		if (isuser) {
20363b12c289SMatthew Ahrens 			e = directory_sid_from_user_name(NULL,
20373b12c289SMatthew Ahrens 			    cp, &numericsid);
20383b12c289SMatthew Ahrens 		} else {
20393b12c289SMatthew Ahrens 			e = directory_sid_from_group_name(NULL,
20403b12c289SMatthew Ahrens 			    cp, &numericsid);
20413b12c289SMatthew Ahrens 		}
20423b12c289SMatthew Ahrens 		if (e != NULL) {
20433b12c289SMatthew Ahrens 			directory_error_free(e);
204414843421SMatthew Ahrens 			return (ENOENT);
20453b12c289SMatthew Ahrens 		}
20463b12c289SMatthew Ahrens 		if (numericsid == NULL)
204714843421SMatthew Ahrens 			return (ENOENT);
20483b12c289SMatthew Ahrens 		cp = numericsid;
20493b12c289SMatthew Ahrens 		/* will be further decoded below */
20503b12c289SMatthew Ahrens 	}
20513b12c289SMatthew Ahrens 
20523b12c289SMatthew Ahrens 	if (strncmp(cp, "S-1-", 4) == 0) {
205314843421SMatthew Ahrens 		/* It's a numeric SID (eg "S-1-234-567-89") */
20543b12c289SMatthew Ahrens 		(void) strlcpy(domain, cp, domainlen);
205514843421SMatthew Ahrens 		cp = strrchr(domain, '-');
205614843421SMatthew Ahrens 		*cp = '\0';
205714843421SMatthew Ahrens 		cp++;
205814843421SMatthew Ahrens 
205914843421SMatthew Ahrens 		errno = 0;
206014843421SMatthew Ahrens 		*ridp = strtoull(cp, &end, 10);
20613b12c289SMatthew Ahrens 		if (numericsid) {
20623b12c289SMatthew Ahrens 			free(numericsid);
20633b12c289SMatthew Ahrens 			numericsid = NULL;
20643b12c289SMatthew Ahrens 		}
2065777badbaSMatthew Ahrens 		if (errno != 0 || *end != '\0')
206614843421SMatthew Ahrens 			return (EINVAL);
206714843421SMatthew Ahrens 	} else if (!isdigit(*cp)) {
206814843421SMatthew Ahrens 		/*
206914843421SMatthew Ahrens 		 * It's a user/group name (eg "user") that needs to be
207014843421SMatthew Ahrens 		 * turned into a uid/gid
207114843421SMatthew Ahrens 		 */
207214843421SMatthew Ahrens 		if (zoned && getzoneid() == GLOBAL_ZONEID)
207314843421SMatthew Ahrens 			return (ENOENT);
207414843421SMatthew Ahrens 		if (isuser) {
207514843421SMatthew Ahrens 			struct passwd *pw;
207614843421SMatthew Ahrens 			pw = getpwnam(cp);
207714843421SMatthew Ahrens 			if (pw == NULL)
207814843421SMatthew Ahrens 				return (ENOENT);
207914843421SMatthew Ahrens 			*ridp = pw->pw_uid;
208014843421SMatthew Ahrens 		} else {
208114843421SMatthew Ahrens 			struct group *gr;
208214843421SMatthew Ahrens 			gr = getgrnam(cp);
208314843421SMatthew Ahrens 			if (gr == NULL)
208414843421SMatthew Ahrens 				return (ENOENT);
208514843421SMatthew Ahrens 			*ridp = gr->gr_gid;
208614843421SMatthew Ahrens 		}
208714843421SMatthew Ahrens 	} else {
208814843421SMatthew Ahrens 		/* It's a user/group ID (eg "12345"). */
208914843421SMatthew Ahrens 		uid_t id = strtoul(cp, &end, 10);
209014843421SMatthew Ahrens 		idmap_rid_t rid;
209114843421SMatthew Ahrens 		char *mapdomain;
209214843421SMatthew Ahrens 
209314843421SMatthew Ahrens 		if (*end != '\0')
209414843421SMatthew Ahrens 			return (EINVAL);
209514843421SMatthew Ahrens 		if (id > MAXUID) {
209614843421SMatthew Ahrens 			/* It's an ephemeral ID. */
209714843421SMatthew Ahrens 			if (idmap_id_to_numeric_domain_rid(id, isuser,
209814843421SMatthew Ahrens 			    &mapdomain, &rid) != 0)
209914843421SMatthew Ahrens 				return (ENOENT);
21003b12c289SMatthew Ahrens 			(void) strlcpy(domain, mapdomain, domainlen);
210114843421SMatthew Ahrens 			*ridp = rid;
210214843421SMatthew Ahrens 		} else {
210314843421SMatthew Ahrens 			*ridp = id;
210414843421SMatthew Ahrens 		}
210514843421SMatthew Ahrens 	}
210614843421SMatthew Ahrens 
21073b12c289SMatthew Ahrens 	ASSERT3P(numericsid, ==, NULL);
210814843421SMatthew Ahrens 	return (0);
210914843421SMatthew Ahrens }
211014843421SMatthew Ahrens 
2111edea4b55SLin Ling static int
2112edea4b55SLin Ling zfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
2113edea4b55SLin Ling     uint64_t *propvalue, zfs_userquota_prop_t *typep)
211414843421SMatthew Ahrens {
211514843421SMatthew Ahrens 	int err;
211614843421SMatthew Ahrens 	zfs_cmd_t zc = { 0 };
211714843421SMatthew Ahrens 
211814843421SMatthew Ahrens 	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
211914843421SMatthew Ahrens 
212014843421SMatthew Ahrens 	err = userquota_propname_decode(propname,
212114843421SMatthew Ahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
2122edea4b55SLin Ling 	    typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
2123edea4b55SLin Ling 	zc.zc_objset_type = *typep;
212414843421SMatthew Ahrens 	if (err)
212514843421SMatthew Ahrens 		return (err);
212614843421SMatthew Ahrens 
212714843421SMatthew Ahrens 	err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
212814843421SMatthew Ahrens 	if (err)
212914843421SMatthew Ahrens 		return (err);
213014843421SMatthew Ahrens 
2131edea4b55SLin Ling 	*propvalue = zc.zc_cookie;
2132edea4b55SLin Ling 	return (0);
2133edea4b55SLin Ling }
2134edea4b55SLin Ling 
2135edea4b55SLin Ling int
2136edea4b55SLin Ling zfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
2137edea4b55SLin Ling     uint64_t *propvalue)
2138edea4b55SLin Ling {
2139edea4b55SLin Ling 	zfs_userquota_prop_t type;
2140edea4b55SLin Ling 
2141edea4b55SLin Ling 	return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
2142edea4b55SLin Ling 	    &type));
2143edea4b55SLin Ling }
2144edea4b55SLin Ling 
2145edea4b55SLin Ling int
2146edea4b55SLin Ling zfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
2147edea4b55SLin Ling     char *propbuf, int proplen, boolean_t literal)
2148edea4b55SLin Ling {
2149edea4b55SLin Ling 	int err;
2150edea4b55SLin Ling 	uint64_t propvalue;
2151edea4b55SLin Ling 	zfs_userquota_prop_t type;
2152edea4b55SLin Ling 
2153edea4b55SLin Ling 	err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,
2154edea4b55SLin Ling 	    &type);
2155edea4b55SLin Ling 
2156edea4b55SLin Ling 	if (err)
2157edea4b55SLin Ling 		return (err);
2158edea4b55SLin Ling 
215914843421SMatthew Ahrens 	if (literal) {
2160edea4b55SLin Ling 		(void) snprintf(propbuf, proplen, "%llu", propvalue);
2161edea4b55SLin Ling 	} else if (propvalue == 0 &&
216214843421SMatthew Ahrens 	    (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
216314843421SMatthew Ahrens 		(void) strlcpy(propbuf, "none", proplen);
216414843421SMatthew Ahrens 	} else {
2165edea4b55SLin Ling 		zfs_nicenum(propvalue, propbuf, proplen);
216614843421SMatthew Ahrens 	}
216714843421SMatthew Ahrens 	return (0);
216814843421SMatthew Ahrens }
216914843421SMatthew Ahrens 
2170fa9e4066Sahrens /*
2171fa9e4066Sahrens  * Returns the name of the given zfs handle.
2172fa9e4066Sahrens  */
2173fa9e4066Sahrens const char *
2174fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp)
2175fa9e4066Sahrens {
2176fa9e4066Sahrens 	return (zhp->zfs_name);
2177fa9e4066Sahrens }
2178fa9e4066Sahrens 
2179fa9e4066Sahrens /*
2180fa9e4066Sahrens  * Returns the type of the given zfs handle.
2181fa9e4066Sahrens  */
2182fa9e4066Sahrens zfs_type_t
2183fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp)
2184fa9e4066Sahrens {
2185fa9e4066Sahrens 	return (zhp->zfs_type);
2186fa9e4066Sahrens }
2187fa9e4066Sahrens 
2188ebedde84SEric Taylor static int
2189ebedde84SEric Taylor zfs_do_list_ioctl(zfs_handle_t *zhp, int arg, zfs_cmd_t *zc)
2190ebedde84SEric Taylor {
2191ebedde84SEric Taylor 	int rc;
2192ebedde84SEric Taylor 	uint64_t	orig_cookie;
2193ebedde84SEric Taylor 
2194ebedde84SEric Taylor 	orig_cookie = zc->zc_cookie;
2195ebedde84SEric Taylor top:
2196ebedde84SEric Taylor 	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
2197ebedde84SEric Taylor 	rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
2198ebedde84SEric Taylor 
2199ebedde84SEric Taylor 	if (rc == -1) {
2200ebedde84SEric Taylor 		switch (errno) {
2201ebedde84SEric Taylor 		case ENOMEM:
2202ebedde84SEric Taylor 			/* expand nvlist memory and try again */
2203ebedde84SEric Taylor 			if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
2204ebedde84SEric Taylor 				zcmd_free_nvlists(zc);
2205ebedde84SEric Taylor 				return (-1);
2206ebedde84SEric Taylor 			}
2207ebedde84SEric Taylor 			zc->zc_cookie = orig_cookie;
2208ebedde84SEric Taylor 			goto top;
2209ebedde84SEric Taylor 		/*
2210ebedde84SEric Taylor 		 * An errno value of ESRCH indicates normal completion.
2211ebedde84SEric Taylor 		 * If ENOENT is returned, then the underlying dataset
2212ebedde84SEric Taylor 		 * has been removed since we obtained the handle.
2213ebedde84SEric Taylor 		 */
2214ebedde84SEric Taylor 		case ESRCH:
2215ebedde84SEric Taylor 		case ENOENT:
2216ebedde84SEric Taylor 			rc = 1;
2217ebedde84SEric Taylor 			break;
2218ebedde84SEric Taylor 		default:
2219ebedde84SEric Taylor 			rc = zfs_standard_error(zhp->zfs_hdl, errno,
2220ebedde84SEric Taylor 			    dgettext(TEXT_DOMAIN,
2221ebedde84SEric Taylor 			    "cannot iterate filesystems"));
2222ebedde84SEric Taylor 			break;
2223ebedde84SEric Taylor 		}
2224ebedde84SEric Taylor 	}
2225ebedde84SEric Taylor 	return (rc);
2226ebedde84SEric Taylor }
2227ebedde84SEric Taylor 
2228fa9e4066Sahrens /*
22297f7322feSeschrock  * Iterate over all child filesystems
2230fa9e4066Sahrens  */
2231fa9e4066Sahrens int
22327f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2233fa9e4066Sahrens {
2234fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2235fa9e4066Sahrens 	zfs_handle_t *nzhp;
2236fa9e4066Sahrens 	int ret;
2237fa9e4066Sahrens 
22383cb34c60Sahrens 	if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
22393cb34c60Sahrens 		return (0);
22403cb34c60Sahrens 
2241ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2242ebedde84SEric Taylor 		return (-1);
2243ebedde84SEric Taylor 
2244ebedde84SEric Taylor 	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
2245ebedde84SEric Taylor 	    &zc)) == 0) {
2246fa9e4066Sahrens 		/*
2247fa9e4066Sahrens 		 * Silently ignore errors, as the only plausible explanation is
2248fa9e4066Sahrens 		 * that the pool has since been removed.
2249fa9e4066Sahrens 		 */
2250ebedde84SEric Taylor 		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2251ebedde84SEric Taylor 		    &zc)) == NULL) {
2252fa9e4066Sahrens 			continue;
2253ebedde84SEric Taylor 		}
2254fa9e4066Sahrens 
2255ebedde84SEric Taylor 		if ((ret = func(nzhp, data)) != 0) {
2256ebedde84SEric Taylor 			zcmd_free_nvlists(&zc);
2257fa9e4066Sahrens 			return (ret);
2258ebedde84SEric Taylor 		}
2259fa9e4066Sahrens 	}
2260ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
2261ebedde84SEric Taylor 	return ((ret < 0) ? ret : 0);
22627f7322feSeschrock }
22637f7322feSeschrock 
22647f7322feSeschrock /*
22657f7322feSeschrock  * Iterate over all snapshots
22667f7322feSeschrock  */
22677f7322feSeschrock int
22687f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
22697f7322feSeschrock {
22707f7322feSeschrock 	zfs_cmd_t zc = { 0 };
22717f7322feSeschrock 	zfs_handle_t *nzhp;
22727f7322feSeschrock 	int ret;
2273fa9e4066Sahrens 
22743cb34c60Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
22753cb34c60Sahrens 		return (0);
22763cb34c60Sahrens 
2277ebedde84SEric Taylor 	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2278ebedde84SEric Taylor 		return (-1);
2279ebedde84SEric Taylor 	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
2280ebedde84SEric Taylor 	    &zc)) == 0) {
2281fa9e4066Sahrens 
2282ebedde84SEric Taylor 		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2283ebedde84SEric Taylor 		    &zc)) == NULL) {
2284fa9e4066Sahrens 			continue;
2285ebedde84SEric Taylor 		}
2286fa9e4066Sahrens 
2287ebedde84SEric Taylor 		if ((ret = func(nzhp, data)) != 0) {
2288ebedde84SEric Taylor 			zcmd_free_nvlists(&zc);
2289fa9e4066Sahrens 			return (ret);
2290ebedde84SEric Taylor 		}
2291fa9e4066Sahrens 	}
2292ebedde84SEric Taylor 	zcmd_free_nvlists(&zc);
2293ebedde84SEric Taylor 	return ((ret < 0) ? ret : 0);
2294fa9e4066Sahrens }
2295fa9e4066Sahrens 
22967f7322feSeschrock /*
22977f7322feSeschrock  * Iterate over all children, snapshots and filesystems
22987f7322feSeschrock  */
22997f7322feSeschrock int
23007f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
23017f7322feSeschrock {
23027f7322feSeschrock 	int ret;
23037f7322feSeschrock 
23047f7322feSeschrock 	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
23057f7322feSeschrock 		return (ret);
23067f7322feSeschrock 
23077f7322feSeschrock 	return (zfs_iter_snapshots(zhp, func, data));
23087f7322feSeschrock }
23097f7322feSeschrock 
2310fa9e4066Sahrens /*
2311fa9e4066Sahrens  * Given a complete name, return just the portion that refers to the parent.
2312fa9e4066Sahrens  * Can return NULL if this is a pool.
2313fa9e4066Sahrens  */
2314fa9e4066Sahrens static int
2315fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen)
2316fa9e4066Sahrens {
2317fa9e4066Sahrens 	char *loc;
2318fa9e4066Sahrens 
2319fa9e4066Sahrens 	if ((loc = strrchr(path, '/')) == NULL)
2320fa9e4066Sahrens 		return (-1);
2321fa9e4066Sahrens 
2322fa9e4066Sahrens 	(void) strncpy(buf, path, MIN(buflen, loc - path));
2323fa9e4066Sahrens 	buf[loc - path] = '\0';
2324fa9e4066Sahrens 
2325fa9e4066Sahrens 	return (0);
2326fa9e4066Sahrens }
2327fa9e4066Sahrens 
2328fa9e4066Sahrens /*
23297f1f55eaSvb  * If accept_ancestor is false, then check to make sure that the given path has
23307f1f55eaSvb  * a parent, and that it exists.  If accept_ancestor is true, then find the
23317f1f55eaSvb  * closest existing ancestor for the given path.  In prefixlen return the
23327f1f55eaSvb  * length of already existing prefix of the given path.  We also fetch the
23337f1f55eaSvb  * 'zoned' property, which is used to validate property settings when creating
23347f1f55eaSvb  * new datasets.
2335fa9e4066Sahrens  */
2336fa9e4066Sahrens static int
23377f1f55eaSvb check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
23387f1f55eaSvb     boolean_t accept_ancestor, int *prefixlen)
2339fa9e4066Sahrens {
2340fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2341fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2342fa9e4066Sahrens 	char *slash;
23437f7322feSeschrock 	zfs_handle_t *zhp;
234499653d4eSeschrock 	char errbuf[1024];
234599653d4eSeschrock 
2346deb8317bSMark J Musante 	(void) snprintf(errbuf, sizeof (errbuf),
2347deb8317bSMark J Musante 	    dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
2348fa9e4066Sahrens 
2349fa9e4066Sahrens 	/* get parent, and check to see if this is just a pool */
2350fa9e4066Sahrens 	if (parent_name(path, parent, sizeof (parent)) != 0) {
235199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
235299653d4eSeschrock 		    "missing dataset name"));
235399653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2354fa9e4066Sahrens 	}
2355fa9e4066Sahrens 
2356fa9e4066Sahrens 	/* check to see if the pool exists */
2357fa9e4066Sahrens 	if ((slash = strchr(parent, '/')) == NULL)
2358fa9e4066Sahrens 		slash = parent + strlen(parent);
2359fa9e4066Sahrens 	(void) strncpy(zc.zc_name, parent, slash - parent);
2360fa9e4066Sahrens 	zc.zc_name[slash - parent] = '\0';
236199653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
2362fa9e4066Sahrens 	    errno == ENOENT) {
236399653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
236499653d4eSeschrock 		    "no such pool '%s'"), zc.zc_name);
236599653d4eSeschrock 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
2366fa9e4066Sahrens 	}
2367fa9e4066Sahrens 
2368fa9e4066Sahrens 	/* check to see if the parent dataset exists */
23697f1f55eaSvb 	while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
23707f1f55eaSvb 		if (errno == ENOENT && accept_ancestor) {
23717f1f55eaSvb 			/*
23727f1f55eaSvb 			 * Go deeper to find an ancestor, give up on top level.
23737f1f55eaSvb 			 */
23747f1f55eaSvb 			if (parent_name(parent, parent, sizeof (parent)) != 0) {
23757f1f55eaSvb 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
23767f1f55eaSvb 				    "no such pool '%s'"), zc.zc_name);
23777f1f55eaSvb 				return (zfs_error(hdl, EZFS_NOENT, errbuf));
23787f1f55eaSvb 			}
23797f1f55eaSvb 		} else if (errno == ENOENT) {
238099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
238199653d4eSeschrock 			    "parent does not exist"));
238299653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
23837f1f55eaSvb 		} else
238499653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2385fa9e4066Sahrens 	}
2386fa9e4066Sahrens 
2387e9dbad6fSeschrock 	*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
2388fa9e4066Sahrens 	/* we are in a non-global zone, but parent is in the global zone */
2389e9dbad6fSeschrock 	if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) {
239099653d4eSeschrock 		(void) zfs_standard_error(hdl, EPERM, errbuf);
23917f7322feSeschrock 		zfs_close(zhp);
2392fa9e4066Sahrens 		return (-1);
2393fa9e4066Sahrens 	}
2394fa9e4066Sahrens 
2395fa9e4066Sahrens 	/* make sure parent is a filesystem */
23967f7322feSeschrock 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
239799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
239899653d4eSeschrock 		    "parent is not a filesystem"));
239999653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
24007f7322feSeschrock 		zfs_close(zhp);
2401fa9e4066Sahrens 		return (-1);
2402fa9e4066Sahrens 	}
2403fa9e4066Sahrens 
24047f7322feSeschrock 	zfs_close(zhp);
24057f1f55eaSvb 	if (prefixlen != NULL)
24067f1f55eaSvb 		*prefixlen = strlen(parent);
24077f1f55eaSvb 	return (0);
24087f1f55eaSvb }
24097f1f55eaSvb 
24107f1f55eaSvb /*
24117f1f55eaSvb  * Finds whether the dataset of the given type(s) exists.
24127f1f55eaSvb  */
24137f1f55eaSvb boolean_t
24147f1f55eaSvb zfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
24157f1f55eaSvb {
24167f1f55eaSvb 	zfs_handle_t *zhp;
24177f1f55eaSvb 
2418f18faf3fSek 	if (!zfs_validate_name(hdl, path, types, B_FALSE))
24197f1f55eaSvb 		return (B_FALSE);
24207f1f55eaSvb 
24217f1f55eaSvb 	/*
24227f1f55eaSvb 	 * Try to get stats for the dataset, which will tell us if it exists.
24237f1f55eaSvb 	 */
24247f1f55eaSvb 	if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
24257f1f55eaSvb 		int ds_type = zhp->zfs_type;
24267f1f55eaSvb 
24277f1f55eaSvb 		zfs_close(zhp);
24287f1f55eaSvb 		if (types & ds_type)
24297f1f55eaSvb 			return (B_TRUE);
24307f1f55eaSvb 	}
24317f1f55eaSvb 	return (B_FALSE);
24327f1f55eaSvb }
24337f1f55eaSvb 
24343cb34c60Sahrens /*
24353cb34c60Sahrens  * Given a path to 'target', create all the ancestors between
24363cb34c60Sahrens  * the prefixlen portion of the path, and the target itself.
24373cb34c60Sahrens  * Fail if the initial prefixlen-ancestor does not already exist.
24383cb34c60Sahrens  */
24393cb34c60Sahrens int
24403cb34c60Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
24413cb34c60Sahrens {
24423cb34c60Sahrens 	zfs_handle_t *h;
24433cb34c60Sahrens 	char *cp;
24443cb34c60Sahrens 	const char *opname;
24453cb34c60Sahrens 
24463cb34c60Sahrens 	/* make sure prefix exists */
24473cb34c60Sahrens 	cp = target + prefixlen;
24483cb34c60Sahrens 	if (*cp != '/') {
24493cb34c60Sahrens 		assert(strchr(cp, '/') == NULL);
24503cb34c60Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
24513cb34c60Sahrens 	} else {
24523cb34c60Sahrens 		*cp = '\0';
24533cb34c60Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
24543cb34c60Sahrens 		*cp = '/';
24553cb34c60Sahrens 	}
24563cb34c60Sahrens 	if (h == NULL)
24573cb34c60Sahrens 		return (-1);
24583cb34c60Sahrens 	zfs_close(h);
24593cb34c60Sahrens 
24603cb34c60Sahrens 	/*
24613cb34c60Sahrens 	 * Attempt to create, mount, and share any ancestor filesystems,
24623cb34c60Sahrens 	 * up to the prefixlen-long one.
24633cb34c60Sahrens 	 */
24643cb34c60Sahrens 	for (cp = target + prefixlen + 1;
24653cb34c60Sahrens 	    cp = strchr(cp, '/'); *cp = '/', cp++) {
24663cb34c60Sahrens 		char *logstr;
24673cb34c60Sahrens 
24683cb34c60Sahrens 		*cp = '\0';
24693cb34c60Sahrens 
24703cb34c60Sahrens 		h = make_dataset_handle(hdl, target);
24713cb34c60Sahrens 		if (h) {
24723cb34c60Sahrens 			/* it already exists, nothing to do here */
24733cb34c60Sahrens 			zfs_close(h);
24743cb34c60Sahrens 			continue;
24753cb34c60Sahrens 		}
24763cb34c60Sahrens 
24773cb34c60Sahrens 		logstr = hdl->libzfs_log_str;
24783cb34c60Sahrens 		hdl->libzfs_log_str = NULL;
24793cb34c60Sahrens 		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
24803cb34c60Sahrens 		    NULL) != 0) {
24813cb34c60Sahrens 			hdl->libzfs_log_str = logstr;
24823cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "create");
24833cb34c60Sahrens 			goto ancestorerr;
24843cb34c60Sahrens 		}
24853cb34c60Sahrens 
24863cb34c60Sahrens 		hdl->libzfs_log_str = logstr;
24873cb34c60Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
24883cb34c60Sahrens 		if (h == NULL) {
24893cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "open");
24903cb34c60Sahrens 			goto ancestorerr;
24913cb34c60Sahrens 		}
24923cb34c60Sahrens 
24933cb34c60Sahrens 		if (zfs_mount(h, NULL, 0) != 0) {
24943cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "mount");
24953cb34c60Sahrens 			goto ancestorerr;
24963cb34c60Sahrens 		}
24973cb34c60Sahrens 
24983cb34c60Sahrens 		if (zfs_share(h) != 0) {
24993cb34c60Sahrens 			opname = dgettext(TEXT_DOMAIN, "share");
25003cb34c60Sahrens 			goto ancestorerr;
25013cb34c60Sahrens 		}
25023cb34c60Sahrens 
25033cb34c60Sahrens 		zfs_close(h);
25043cb34c60Sahrens 	}
25053cb34c60Sahrens 
25063cb34c60Sahrens 	return (0);
25073cb34c60Sahrens 
25083cb34c60Sahrens ancestorerr:
25093cb34c60Sahrens 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
25103cb34c60Sahrens 	    "failed to %s ancestor '%s'"), opname, target);
25113cb34c60Sahrens 	return (-1);
25123cb34c60Sahrens }
25133cb34c60Sahrens 
25147f1f55eaSvb /*
25157f1f55eaSvb  * Creates non-existing ancestors of the given path.
25167f1f55eaSvb  */
25177f1f55eaSvb int
25187f1f55eaSvb zfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
25197f1f55eaSvb {
25207f1f55eaSvb 	int prefix;
25217f1f55eaSvb 	uint64_t zoned;
25227f1f55eaSvb 	char *path_copy;
25237f1f55eaSvb 	int rc;
25247f1f55eaSvb 
25257f1f55eaSvb 	if (check_parents(hdl, path, &zoned, B_TRUE, &prefix) != 0)
25267f1f55eaSvb 		return (-1);
25277f1f55eaSvb 
25287f1f55eaSvb 	if ((path_copy = strdup(path)) != NULL) {
25297f1f55eaSvb 		rc = create_parents(hdl, path_copy, prefix);
25307f1f55eaSvb 		free(path_copy);
25317f1f55eaSvb 	}
25327f1f55eaSvb 	if (path_copy == NULL || rc != 0)
25337f1f55eaSvb 		return (-1);
25347f1f55eaSvb 
2535fa9e4066Sahrens 	return (0);
2536fa9e4066Sahrens }
2537fa9e4066Sahrens 
2538fa9e4066Sahrens /*
2539e9dbad6fSeschrock  * Create a new filesystem or volume.
2540fa9e4066Sahrens  */
2541fa9e4066Sahrens int
254299653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
2543e9dbad6fSeschrock     nvlist_t *props)
2544fa9e4066Sahrens {
2545fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2546fa9e4066Sahrens 	int ret;
2547fa9e4066Sahrens 	uint64_t size = 0;
2548fa9e4066Sahrens 	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
254999653d4eSeschrock 	char errbuf[1024];
2550e9dbad6fSeschrock 	uint64_t zoned;
255199653d4eSeschrock 
255299653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
255399653d4eSeschrock 	    "cannot create '%s'"), path);
2554fa9e4066Sahrens 
2555fa9e4066Sahrens 	/* validate the path, taking care to note the extended error message */
2556f18faf3fSek 	if (!zfs_validate_name(hdl, path, type, B_TRUE))
255799653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2558fa9e4066Sahrens 
2559fa9e4066Sahrens 	/* validate parents exist */
25607f1f55eaSvb 	if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
2561fa9e4066Sahrens 		return (-1);
2562fa9e4066Sahrens 
2563fa9e4066Sahrens 	/*
2564fa9e4066Sahrens 	 * The failure modes when creating a dataset of a different type over
2565fa9e4066Sahrens 	 * one that already exists is a little strange.  In particular, if you
2566fa9e4066Sahrens 	 * try to create a dataset on top of an existing dataset, the ioctl()
2567fa9e4066Sahrens 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
2568fa9e4066Sahrens 	 * first try to see if the dataset exists.
2569fa9e4066Sahrens 	 */
2570fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
2571990b4856Slling 	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
257299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
257399653d4eSeschrock 		    "dataset already exists"));
257499653d4eSeschrock 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2575fa9e4066Sahrens 	}
2576fa9e4066Sahrens 
2577fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME)
2578fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2579fa9e4066Sahrens 	else
2580fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2581fa9e4066Sahrens 
25820a48a24eStimh 	if (props && (props = zfs_valid_proplist(hdl, type, props,
2583b1b8ab34Slling 	    zoned, NULL, errbuf)) == 0)
2584e9dbad6fSeschrock 		return (-1);
2585e9dbad6fSeschrock 
2586fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME) {
25875c5460e9Seschrock 		/*
25885c5460e9Seschrock 		 * If we are creating a volume, the size and block size must
25895c5460e9Seschrock 		 * satisfy a few restraints.  First, the blocksize must be a
25905c5460e9Seschrock 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
25915c5460e9Seschrock 		 * volsize must be a multiple of the block size, and cannot be
25925c5460e9Seschrock 		 * zero.
25935c5460e9Seschrock 		 */
2594e9dbad6fSeschrock 		if (props == NULL || nvlist_lookup_uint64(props,
2595e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2596e9dbad6fSeschrock 			nvlist_free(props);
259799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2598e9dbad6fSeschrock 			    "missing volume size"));
2599e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2600e9dbad6fSeschrock 		}
2601e9dbad6fSeschrock 
2602e9dbad6fSeschrock 		if ((ret = nvlist_lookup_uint64(props,
2603e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2604e9dbad6fSeschrock 		    &blocksize)) != 0) {
2605e9dbad6fSeschrock 			if (ret == ENOENT) {
2606e9dbad6fSeschrock 				blocksize = zfs_prop_default_numeric(
2607e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2608e9dbad6fSeschrock 			} else {
2609e9dbad6fSeschrock 				nvlist_free(props);
2610e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2611e9dbad6fSeschrock 				    "missing volume block size"));
2612e9dbad6fSeschrock 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2613e9dbad6fSeschrock 			}
2614fa9e4066Sahrens 		}
2615fa9e4066Sahrens 
2616e9dbad6fSeschrock 		if (size == 0) {
2617e9dbad6fSeschrock 			nvlist_free(props);
261899653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2619e9dbad6fSeschrock 			    "volume size cannot be zero"));
2620e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
26215c5460e9Seschrock 		}
26225c5460e9Seschrock 
26235c5460e9Seschrock 		if (size % blocksize != 0) {
2624e9dbad6fSeschrock 			nvlist_free(props);
262599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2626e9dbad6fSeschrock 			    "volume size must be a multiple of volume block "
2627e9dbad6fSeschrock 			    "size"));
2628e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
26295c5460e9Seschrock 		}
2630fa9e4066Sahrens 	}
2631fa9e4066Sahrens 
2632990b4856Slling 	if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
2633e9dbad6fSeschrock 		return (-1);
2634e9dbad6fSeschrock 	nvlist_free(props);
2635e9dbad6fSeschrock 
2636fa9e4066Sahrens 	/* create the dataset */
2637ecd6cf80Smarks 	ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
2638fa9e4066Sahrens 
2639b1b8ab34Slling 	if (ret == 0 && type == ZFS_TYPE_VOLUME) {
264099653d4eSeschrock 		ret = zvol_create_link(hdl, path);
2641b1b8ab34Slling 		if (ret) {
2642b1b8ab34Slling 			(void) zfs_standard_error(hdl, errno,
2643b1b8ab34Slling 			    dgettext(TEXT_DOMAIN,
2644b1b8ab34Slling 			    "Volume successfully created, but device links "
2645b1b8ab34Slling 			    "were not created"));
2646b1b8ab34Slling 			zcmd_free_nvlists(&zc);
2647b1b8ab34Slling 			return (-1);
2648b1b8ab34Slling 		}
2649b1b8ab34Slling 	}
2650fa9e4066Sahrens 
2651e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2652e9dbad6fSeschrock 
2653fa9e4066Sahrens 	/* check for failure */
2654fa9e4066Sahrens 	if (ret != 0) {
2655fa9e4066Sahrens 		char parent[ZFS_MAXNAMELEN];
2656fa9e4066Sahrens 		(void) parent_name(path, parent, sizeof (parent));
2657fa9e4066Sahrens 
2658fa9e4066Sahrens 		switch (errno) {
2659fa9e4066Sahrens 		case ENOENT:
266099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
266199653d4eSeschrock 			    "no such parent '%s'"), parent);
266299653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2663fa9e4066Sahrens 
2664fa9e4066Sahrens 		case EINVAL:
266599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2666d7d4af51Smmusante 			    "parent '%s' is not a filesystem"), parent);
266799653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2668fa9e4066Sahrens 
2669fa9e4066Sahrens 		case EDOM:
267099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2671e9dbad6fSeschrock 			    "volume block size must be power of 2 from "
2672e9dbad6fSeschrock 			    "%u to %uk"),
2673fa9e4066Sahrens 			    (uint_t)SPA_MINBLOCKSIZE,
2674fa9e4066Sahrens 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
267599653d4eSeschrock 
2676e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
267799653d4eSeschrock 
267840feaa91Sahrens 		case ENOTSUP:
267940feaa91Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
268040feaa91Sahrens 			    "pool must be upgraded to set this "
268140feaa91Sahrens 			    "property or value"));
268240feaa91Sahrens 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
2683fa9e4066Sahrens #ifdef _ILP32
2684fa9e4066Sahrens 		case EOVERFLOW:
2685fa9e4066Sahrens 			/*
2686fa9e4066Sahrens 			 * This platform can't address a volume this big.
2687fa9e4066Sahrens 			 */
268899653d4eSeschrock 			if (type == ZFS_TYPE_VOLUME)
268999653d4eSeschrock 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
269099653d4eSeschrock 				    errbuf));
2691fa9e4066Sahrens #endif
269299653d4eSeschrock 			/* FALLTHROUGH */
2693fa9e4066Sahrens 		default:
269499653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2695fa9e4066Sahrens 		}
2696fa9e4066Sahrens 	}
2697fa9e4066Sahrens 
2698fa9e4066Sahrens 	return (0);
2699fa9e4066Sahrens }
2700fa9e4066Sahrens 
2701fa9e4066Sahrens /*
2702fa9e4066Sahrens  * Destroys the given dataset.  The caller must make sure that the filesystem
2703fa9e4066Sahrens  * isn't mounted, and that there are no active dependents.
2704fa9e4066Sahrens  */
2705fa9e4066Sahrens int
2706842727c2SChris Kirby zfs_destroy(zfs_handle_t *zhp, boolean_t defer)
2707fa9e4066Sahrens {
2708fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2709fa9e4066Sahrens 
2710fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2711fa9e4066Sahrens 
2712e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2713f3861e1aSahl 		/*
2714ecd6cf80Smarks 		 * If user doesn't have permissions to unshare volume, then
2715ecd6cf80Smarks 		 * abort the request.  This would only happen for a
2716ecd6cf80Smarks 		 * non-privileged user.
2717f3861e1aSahl 		 */
2718ecd6cf80Smarks 		if (zfs_unshare_iscsi(zhp) != 0) {
2719ecd6cf80Smarks 			return (-1);
2720ecd6cf80Smarks 		}
2721f3861e1aSahl 
272299653d4eSeschrock 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2723fa9e4066Sahrens 			return (-1);
2724fa9e4066Sahrens 
2725fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2726fa9e4066Sahrens 	} else {
2727fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2728fa9e4066Sahrens 	}
2729fa9e4066Sahrens 
2730842727c2SChris Kirby 	zc.zc_defer_destroy = defer;
2731ecd6cf80Smarks 	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
2732ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
273399653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
273499653d4eSeschrock 		    zhp->zfs_name));
27351d452cf5Sahrens 	}
2736fa9e4066Sahrens 
2737fa9e4066Sahrens 	remove_mountpoint(zhp);
2738fa9e4066Sahrens 
2739fa9e4066Sahrens 	return (0);
2740fa9e4066Sahrens }
2741fa9e4066Sahrens 
27421d452cf5Sahrens struct destroydata {
27431d452cf5Sahrens 	char *snapname;
27441d452cf5Sahrens 	boolean_t gotone;
27453ccfa83cSahrens 	boolean_t closezhp;
27461d452cf5Sahrens };
27471d452cf5Sahrens 
27481d452cf5Sahrens static int
27491d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg)
27501d452cf5Sahrens {
27511d452cf5Sahrens 	struct destroydata *dd = arg;
27521d452cf5Sahrens 	zfs_handle_t *szhp;
27531d452cf5Sahrens 	char name[ZFS_MAXNAMELEN];
27543ccfa83cSahrens 	boolean_t closezhp = dd->closezhp;
27553ccfa83cSahrens 	int rv;
27561d452cf5Sahrens 
2757e9dbad6fSeschrock 	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2758e9dbad6fSeschrock 	(void) strlcat(name, "@", sizeof (name));
2759e9dbad6fSeschrock 	(void) strlcat(name, dd->snapname, sizeof (name));
27601d452cf5Sahrens 
27611d452cf5Sahrens 	szhp = make_dataset_handle(zhp->zfs_hdl, name);
27621d452cf5Sahrens 	if (szhp) {
27631d452cf5Sahrens 		dd->gotone = B_TRUE;
27641d452cf5Sahrens 		zfs_close(szhp);
27651d452cf5Sahrens 	}
27661d452cf5Sahrens 
27671d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
27681d452cf5Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, name);
27691d452cf5Sahrens 		/*
27701d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
27711d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
27721d452cf5Sahrens 		 * the volumes.
27731d452cf5Sahrens 		 */
27741d452cf5Sahrens 	}
27751d452cf5Sahrens 
27763ccfa83cSahrens 	dd->closezhp = B_TRUE;
27773ccfa83cSahrens 	rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg);
27783ccfa83cSahrens 	if (closezhp)
27793ccfa83cSahrens 		zfs_close(zhp);
27803ccfa83cSahrens 	return (rv);
27811d452cf5Sahrens }
27821d452cf5Sahrens 
27831d452cf5Sahrens /*
27841d452cf5Sahrens  * Destroys all snapshots with the given name in zhp & descendants.
27851d452cf5Sahrens  */
27861d452cf5Sahrens int
2787842727c2SChris Kirby zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
27881d452cf5Sahrens {
27891d452cf5Sahrens 	zfs_cmd_t zc = { 0 };
27901d452cf5Sahrens 	int ret;
27911d452cf5Sahrens 	struct destroydata dd = { 0 };
27921d452cf5Sahrens 
27931d452cf5Sahrens 	dd.snapname = snapname;
27941d452cf5Sahrens 	(void) zfs_remove_link_cb(zhp, &dd);
27951d452cf5Sahrens 
27961d452cf5Sahrens 	if (!dd.gotone) {
2797ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
27981d452cf5Sahrens 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
27991d452cf5Sahrens 		    zhp->zfs_name, snapname));
28001d452cf5Sahrens 	}
28011d452cf5Sahrens 
28021d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2803e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
2804842727c2SChris Kirby 	zc.zc_defer_destroy = defer;
28051d452cf5Sahrens 
2806ecd6cf80Smarks 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
28071d452cf5Sahrens 	if (ret != 0) {
28081d452cf5Sahrens 		char errbuf[1024];
28091d452cf5Sahrens 
28101d452cf5Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
28111d452cf5Sahrens 		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
28121d452cf5Sahrens 
28131d452cf5Sahrens 		switch (errno) {
28141d452cf5Sahrens 		case EEXIST:
28151d452cf5Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
28161d452cf5Sahrens 			    "snapshot is cloned"));
28171d452cf5Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
28181d452cf5Sahrens 
28191d452cf5Sahrens 		default:
28201d452cf5Sahrens 			return (zfs_standard_error(zhp->zfs_hdl, errno,
28211d452cf5Sahrens 			    errbuf));
28221d452cf5Sahrens 		}
28231d452cf5Sahrens 	}
28241d452cf5Sahrens 
28251d452cf5Sahrens 	return (0);
28261d452cf5Sahrens }
28271d452cf5Sahrens 
2828fa9e4066Sahrens /*
2829fa9e4066Sahrens  * Clones the given dataset.  The target must be of the same type as the source.
2830fa9e4066Sahrens  */
2831fa9e4066Sahrens int
2832e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
2833fa9e4066Sahrens {
2834fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2835fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2836fa9e4066Sahrens 	int ret;
283799653d4eSeschrock 	char errbuf[1024];
283899653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
2839e9dbad6fSeschrock 	zfs_type_t type;
2840e9dbad6fSeschrock 	uint64_t zoned;
2841fa9e4066Sahrens 
2842fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
2843fa9e4066Sahrens 
284499653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
284599653d4eSeschrock 	    "cannot create '%s'"), target);
284699653d4eSeschrock 
2847fa9e4066Sahrens 	/* validate the target name */
2848f18faf3fSek 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
284999653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2850fa9e4066Sahrens 
2851fa9e4066Sahrens 	/* validate parents exist */
28527f1f55eaSvb 	if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
2853fa9e4066Sahrens 		return (-1);
2854fa9e4066Sahrens 
2855fa9e4066Sahrens 	(void) parent_name(target, parent, sizeof (parent));
2856fa9e4066Sahrens 
2857fa9e4066Sahrens 	/* do the clone */
2858e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2859fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
28605f8e1617Snn 		type = ZFS_TYPE_VOLUME;
2861e9dbad6fSeschrock 	} else {
2862fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
28635f8e1617Snn 		type = ZFS_TYPE_FILESYSTEM;
2864e9dbad6fSeschrock 	}
2865e9dbad6fSeschrock 
2866e9dbad6fSeschrock 	if (props) {
28670a48a24eStimh 		if ((props = zfs_valid_proplist(hdl, type, props, zoned,
28680a48a24eStimh 		    zhp, errbuf)) == NULL)
2869e9dbad6fSeschrock 			return (-1);
2870e9dbad6fSeschrock 
2871990b4856Slling 		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
2872e9dbad6fSeschrock 			nvlist_free(props);
2873e9dbad6fSeschrock 			return (-1);
2874e9dbad6fSeschrock 		}
2875e9dbad6fSeschrock 
2876e9dbad6fSeschrock 		nvlist_free(props);
2877e9dbad6fSeschrock 	}
2878fa9e4066Sahrens 
2879fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
2880e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
2881ecd6cf80Smarks 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
2882fa9e4066Sahrens 
2883e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2884e9dbad6fSeschrock 
2885fa9e4066Sahrens 	if (ret != 0) {
2886fa9e4066Sahrens 		switch (errno) {
2887fa9e4066Sahrens 
2888fa9e4066Sahrens 		case ENOENT:
2889fa9e4066Sahrens 			/*
2890fa9e4066Sahrens 			 * The parent doesn't exist.  We should have caught this
2891fa9e4066Sahrens 			 * above, but there may a race condition that has since
2892fa9e4066Sahrens 			 * destroyed the parent.
2893fa9e4066Sahrens 			 *
2894fa9e4066Sahrens 			 * At this point, we don't know whether it's the source
2895fa9e4066Sahrens 			 * that doesn't exist anymore, or whether the target
2896fa9e4066Sahrens 			 * dataset doesn't exist.
2897fa9e4066Sahrens 			 */
289899653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
289999653d4eSeschrock 			    "no such parent '%s'"), parent);
290099653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
2901fa9e4066Sahrens 
290299653d4eSeschrock 		case EXDEV:
290399653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
290499653d4eSeschrock 			    "source and target pools differ"));
290599653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
290699653d4eSeschrock 			    errbuf));
2907fa9e4066Sahrens 
290899653d4eSeschrock 		default:
290999653d4eSeschrock 			return (zfs_standard_error(zhp->zfs_hdl, errno,
291099653d4eSeschrock 			    errbuf));
291199653d4eSeschrock 		}
2912e9dbad6fSeschrock 	} else if (ZFS_IS_VOLUME(zhp)) {
291399653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, target);
291499653d4eSeschrock 	}
2915fa9e4066Sahrens 
291699653d4eSeschrock 	return (ret);
291799653d4eSeschrock }
291899653d4eSeschrock 
291999653d4eSeschrock typedef struct promote_data {
292099653d4eSeschrock 	char cb_mountpoint[MAXPATHLEN];
292199653d4eSeschrock 	const char *cb_target;
292299653d4eSeschrock 	const char *cb_errbuf;
292399653d4eSeschrock 	uint64_t cb_pivot_txg;
292499653d4eSeschrock } promote_data_t;
292599653d4eSeschrock 
292699653d4eSeschrock static int
292799653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data)
292899653d4eSeschrock {
292999653d4eSeschrock 	promote_data_t *pd = data;
293099653d4eSeschrock 	zfs_handle_t *szhp;
293199653d4eSeschrock 	char snapname[MAXPATHLEN];
29323ccfa83cSahrens 	int rv = 0;
293399653d4eSeschrock 
293499653d4eSeschrock 	/* We don't care about snapshots after the pivot point */
29353ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) {
29363ccfa83cSahrens 		zfs_close(zhp);
293799653d4eSeschrock 		return (0);
29383ccfa83cSahrens 	}
293999653d4eSeschrock 
29400b69c2f0Sahrens 	/* Remove the device link if it's a zvol. */
2941e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
29420b69c2f0Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
294399653d4eSeschrock 
294499653d4eSeschrock 	/* Check for conflicting names */
2945e9dbad6fSeschrock 	(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
2946e9dbad6fSeschrock 	(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
294799653d4eSeschrock 	szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
294899653d4eSeschrock 	if (szhp != NULL) {
294999653d4eSeschrock 		zfs_close(szhp);
295099653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
295199653d4eSeschrock 		    "snapshot name '%s' from origin \n"
295299653d4eSeschrock 		    "conflicts with '%s' from target"),
295399653d4eSeschrock 		    zhp->zfs_name, snapname);
29543ccfa83cSahrens 		rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf);
295599653d4eSeschrock 	}
29563ccfa83cSahrens 	zfs_close(zhp);
29573ccfa83cSahrens 	return (rv);
295899653d4eSeschrock }
295999653d4eSeschrock 
29600b69c2f0Sahrens static int
29610b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data)
29620b69c2f0Sahrens {
29630b69c2f0Sahrens 	promote_data_t *pd = data;
29640b69c2f0Sahrens 
29650b69c2f0Sahrens 	/* We don't care about snapshots after the pivot point */
29663ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) {
29673ccfa83cSahrens 		/* Create the device link if it's a zvol. */
29683ccfa83cSahrens 		if (ZFS_IS_VOLUME(zhp))
29693ccfa83cSahrens 			(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
29703ccfa83cSahrens 	}
29710b69c2f0Sahrens 
29723ccfa83cSahrens 	zfs_close(zhp);
29730b69c2f0Sahrens 	return (0);
29740b69c2f0Sahrens }
29750b69c2f0Sahrens 
297699653d4eSeschrock /*
297799653d4eSeschrock  * Promotes the given clone fs to be the clone parent.
297899653d4eSeschrock  */
297999653d4eSeschrock int
298099653d4eSeschrock zfs_promote(zfs_handle_t *zhp)
298199653d4eSeschrock {
298299653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
298399653d4eSeschrock 	zfs_cmd_t zc = { 0 };
298499653d4eSeschrock 	char parent[MAXPATHLEN];
298599653d4eSeschrock 	char *cp;
298699653d4eSeschrock 	int ret;
298799653d4eSeschrock 	zfs_handle_t *pzhp;
298899653d4eSeschrock 	promote_data_t pd;
298999653d4eSeschrock 	char errbuf[1024];
299099653d4eSeschrock 
299199653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
299299653d4eSeschrock 	    "cannot promote '%s'"), zhp->zfs_name);
299399653d4eSeschrock 
299499653d4eSeschrock 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
299599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
299699653d4eSeschrock 		    "snapshots can not be promoted"));
299799653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
299899653d4eSeschrock 	}
299999653d4eSeschrock 
30003cb34c60Sahrens 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
300199653d4eSeschrock 	if (parent[0] == '\0') {
300299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
300399653d4eSeschrock 		    "not a cloned filesystem"));
300499653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
300599653d4eSeschrock 	}
300699653d4eSeschrock 	cp = strchr(parent, '@');
300799653d4eSeschrock 	*cp = '\0';
300899653d4eSeschrock 
300999653d4eSeschrock 	/* Walk the snapshots we will be moving */
30103cb34c60Sahrens 	pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_origin, ZFS_TYPE_SNAPSHOT);
301199653d4eSeschrock 	if (pzhp == NULL)
301299653d4eSeschrock 		return (-1);
301399653d4eSeschrock 	pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
301499653d4eSeschrock 	zfs_close(pzhp);
301599653d4eSeschrock 	pd.cb_target = zhp->zfs_name;
301699653d4eSeschrock 	pd.cb_errbuf = errbuf;
3017990b4856Slling 	pzhp = zfs_open(hdl, parent, ZFS_TYPE_DATASET);
301899653d4eSeschrock 	if (pzhp == NULL)
301999653d4eSeschrock 		return (-1);
302099653d4eSeschrock 	(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
302199653d4eSeschrock 	    sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
302299653d4eSeschrock 	ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
30230b69c2f0Sahrens 	if (ret != 0) {
30240b69c2f0Sahrens 		zfs_close(pzhp);
302599653d4eSeschrock 		return (-1);
30260b69c2f0Sahrens 	}
302799653d4eSeschrock 
302899653d4eSeschrock 	/* issue the ioctl */
30293cb34c60Sahrens 	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
3030e9dbad6fSeschrock 	    sizeof (zc.zc_value));
303199653d4eSeschrock 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3032ecd6cf80Smarks 	ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
303399653d4eSeschrock 
303499653d4eSeschrock 	if (ret != 0) {
30350b69c2f0Sahrens 		int save_errno = errno;
30360b69c2f0Sahrens 
30370b69c2f0Sahrens 		(void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
30380b69c2f0Sahrens 		zfs_close(pzhp);
303999653d4eSeschrock 
30400b69c2f0Sahrens 		switch (save_errno) {
304199653d4eSeschrock 		case EEXIST:
3042fa9e4066Sahrens 			/*
304399653d4eSeschrock 			 * There is a conflicting snapshot name.  We
304499653d4eSeschrock 			 * should have caught this above, but they could
304599653d4eSeschrock 			 * have renamed something in the mean time.
3046fa9e4066Sahrens 			 */
304799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
304899653d4eSeschrock 			    "conflicting snapshot name from parent '%s'"),
304999653d4eSeschrock 			    parent);
305099653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
3051fa9e4066Sahrens 
3052fa9e4066Sahrens 		default:
30530b69c2f0Sahrens 			return (zfs_standard_error(hdl, save_errno, errbuf));
3054fa9e4066Sahrens 		}
30550b69c2f0Sahrens 	} else {
30560b69c2f0Sahrens 		(void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
3057fa9e4066Sahrens 	}
3058fa9e4066Sahrens 
30590b69c2f0Sahrens 	zfs_close(pzhp);
3060fa9e4066Sahrens 	return (ret);
3061fa9e4066Sahrens }
3062fa9e4066Sahrens 
3063cdf5b4caSmmusante struct createdata {
3064cdf5b4caSmmusante 	const char *cd_snapname;
3065cdf5b4caSmmusante 	int cd_ifexists;
3066cdf5b4caSmmusante };
3067cdf5b4caSmmusante 
30681d452cf5Sahrens static int
30691d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
30701d452cf5Sahrens {
3071cdf5b4caSmmusante 	struct createdata *cd = arg;
3072e9dbad6fSeschrock 	int ret;
30731d452cf5Sahrens 
30741d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
30751d452cf5Sahrens 		char name[MAXPATHLEN];
30761d452cf5Sahrens 
3077e9dbad6fSeschrock 		(void) strlcpy(name, zhp->zfs_name, sizeof (name));
3078e9dbad6fSeschrock 		(void) strlcat(name, "@", sizeof (name));
3079cdf5b4caSmmusante 		(void) strlcat(name, cd->cd_snapname, sizeof (name));
3080cdf5b4caSmmusante 		(void) zvol_create_link_common(zhp->zfs_hdl, name,
3081cdf5b4caSmmusante 		    cd->cd_ifexists);
30821d452cf5Sahrens 		/*
30831d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
30841d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
30851d452cf5Sahrens 		 * the volumes.
30861d452cf5Sahrens 		 */
30871d452cf5Sahrens 	}
3088e9dbad6fSeschrock 
3089cdf5b4caSmmusante 	ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, cd);
3090e9dbad6fSeschrock 
3091e9dbad6fSeschrock 	zfs_close(zhp);
3092e9dbad6fSeschrock 
3093e9dbad6fSeschrock 	return (ret);
30941d452cf5Sahrens }
30951d452cf5Sahrens 
3096fa9e4066Sahrens /*
309772bdce51Sahl  * Takes a snapshot of the given dataset.
3098fa9e4066Sahrens  */
3099fa9e4066Sahrens int
3100bb0ade09Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
3101bb0ade09Sahrens     nvlist_t *props)
3102fa9e4066Sahrens {
3103fa9e4066Sahrens 	const char *delim;
3104bb0ade09Sahrens 	char parent[ZFS_MAXNAMELEN];
3105fa9e4066Sahrens 	zfs_handle_t *zhp;
3106fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3107fa9e4066Sahrens 	int ret;
310899653d4eSeschrock 	char errbuf[1024];
3109fa9e4066Sahrens 
311099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
311199653d4eSeschrock 	    "cannot snapshot '%s'"), path);
311299653d4eSeschrock 
311399653d4eSeschrock 	/* validate the target name */
3114f18faf3fSek 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
311599653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3116fa9e4066Sahrens 
3117bb0ade09Sahrens 	if (props) {
3118bb0ade09Sahrens 		if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
3119bb0ade09Sahrens 		    props, B_FALSE, NULL, errbuf)) == NULL)
3120bb0ade09Sahrens 			return (-1);
3121bb0ade09Sahrens 
3122bb0ade09Sahrens 		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3123bb0ade09Sahrens 			nvlist_free(props);
3124bb0ade09Sahrens 			return (-1);
3125bb0ade09Sahrens 		}
3126bb0ade09Sahrens 
3127bb0ade09Sahrens 		nvlist_free(props);
3128bb0ade09Sahrens 	}
3129bb0ade09Sahrens 
3130fa9e4066Sahrens 	/* make sure the parent exists and is of the appropriate type */
31311d452cf5Sahrens 	delim = strchr(path, '@');
3132fa9e4066Sahrens 	(void) strncpy(parent, path, delim - path);
3133fa9e4066Sahrens 	parent[delim - path] = '\0';
3134fa9e4066Sahrens 
313599653d4eSeschrock 	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
3136fa9e4066Sahrens 	    ZFS_TYPE_VOLUME)) == NULL) {
3137bb0ade09Sahrens 		zcmd_free_nvlists(&zc);
3138fa9e4066Sahrens 		return (-1);
3139fa9e4066Sahrens 	}
3140fa9e4066Sahrens 
31411d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3142e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
3143ecd6cf80Smarks 	if (ZFS_IS_VOLUME(zhp))
3144ecd6cf80Smarks 		zc.zc_objset_type = DMU_OST_ZVOL;
3145ecd6cf80Smarks 	else
3146ecd6cf80Smarks 		zc.zc_objset_type = DMU_OST_ZFS;
31471d452cf5Sahrens 	zc.zc_cookie = recursive;
3148ecd6cf80Smarks 	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
3149fa9e4066Sahrens 
3150bb0ade09Sahrens 	zcmd_free_nvlists(&zc);
3151bb0ade09Sahrens 
31521d452cf5Sahrens 	/*
31531d452cf5Sahrens 	 * if it was recursive, the one that actually failed will be in
31541d452cf5Sahrens 	 * zc.zc_name.
31551d452cf5Sahrens 	 */
3156ecd6cf80Smarks 	if (ret != 0)
3157ecd6cf80Smarks 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3158ecd6cf80Smarks 		    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
3159ecd6cf80Smarks 
31601d452cf5Sahrens 	if (ret == 0 && recursive) {
3161cdf5b4caSmmusante 		struct createdata cd;
3162cdf5b4caSmmusante 
3163cdf5b4caSmmusante 		cd.cd_snapname = delim + 1;
3164cdf5b4caSmmusante 		cd.cd_ifexists = B_FALSE;
3165cdf5b4caSmmusante 		(void) zfs_iter_filesystems(zhp, zfs_create_link_cb, &cd);
31661d452cf5Sahrens 	}
3167fa9e4066Sahrens 	if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
316899653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, path);
31691d452cf5Sahrens 		if (ret != 0) {
3170ecd6cf80Smarks 			(void) zfs_standard_error(hdl, errno,
3171ecd6cf80Smarks 			    dgettext(TEXT_DOMAIN,
3172ecd6cf80Smarks 			    "Volume successfully snapshotted, but device links "
3173ecd6cf80Smarks 			    "were not created"));
3174ecd6cf80Smarks 			zfs_close(zhp);
3175ecd6cf80Smarks 			return (-1);
31761d452cf5Sahrens 		}
3177fa9e4066Sahrens 	}
3178fa9e4066Sahrens 
317999653d4eSeschrock 	if (ret != 0)
318099653d4eSeschrock 		(void) zfs_standard_error(hdl, errno, errbuf);
3181fa9e4066Sahrens 
3182fa9e4066Sahrens 	zfs_close(zhp);
3183fa9e4066Sahrens 
3184fa9e4066Sahrens 	return (ret);
3185fa9e4066Sahrens }
3186fa9e4066Sahrens 
3187fa9e4066Sahrens /*
3188b12a1c38Slling  * Destroy any more recent snapshots.  We invoke this callback on any dependents
3189b12a1c38Slling  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
3190b12a1c38Slling  * is a dependent and we should just destroy it without checking the transaction
3191b12a1c38Slling  * group.
3192fa9e4066Sahrens  */
3193b12a1c38Slling typedef struct rollback_data {
3194b12a1c38Slling 	const char	*cb_target;		/* the snapshot */
3195b12a1c38Slling 	uint64_t	cb_create;		/* creation time reference */
3196c391e322Sahrens 	boolean_t	cb_error;
319799653d4eSeschrock 	boolean_t	cb_dependent;
3198c391e322Sahrens 	boolean_t	cb_force;
3199b12a1c38Slling } rollback_data_t;
3200b12a1c38Slling 
3201b12a1c38Slling static int
3202b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data)
3203b12a1c38Slling {
3204b12a1c38Slling 	rollback_data_t *cbp = data;
3205b12a1c38Slling 
3206b12a1c38Slling 	if (!cbp->cb_dependent) {
3207b12a1c38Slling 		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
3208b12a1c38Slling 		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3209b12a1c38Slling 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3210b12a1c38Slling 		    cbp->cb_create) {
3211ecd6cf80Smarks 			char *logstr;
3212b12a1c38Slling 
321399653d4eSeschrock 			cbp->cb_dependent = B_TRUE;
32144ccbb6e7Sahrens 			cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
32154ccbb6e7Sahrens 			    rollback_destroy, cbp);
321699653d4eSeschrock 			cbp->cb_dependent = B_FALSE;
3217b12a1c38Slling 
3218ecd6cf80Smarks 			logstr = zhp->zfs_hdl->libzfs_log_str;
3219ecd6cf80Smarks 			zhp->zfs_hdl->libzfs_log_str = NULL;
3220842727c2SChris Kirby 			cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
3221ecd6cf80Smarks 			zhp->zfs_hdl->libzfs_log_str = logstr;
3222b12a1c38Slling 		}
3223b12a1c38Slling 	} else {
3224c391e322Sahrens 		/* We must destroy this clone; first unmount it */
3225c391e322Sahrens 		prop_changelist_t *clp;
3226c391e322Sahrens 
32270069fd67STim Haley 		clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3228c391e322Sahrens 		    cbp->cb_force ? MS_FORCE: 0);
3229c391e322Sahrens 		if (clp == NULL || changelist_prefix(clp) != 0) {
3230c391e322Sahrens 			cbp->cb_error = B_TRUE;
3231c391e322Sahrens 			zfs_close(zhp);
3232c391e322Sahrens 			return (0);
3233c391e322Sahrens 		}
3234842727c2SChris Kirby 		if (zfs_destroy(zhp, B_FALSE) != 0)
3235c391e322Sahrens 			cbp->cb_error = B_TRUE;
3236c391e322Sahrens 		else
3237c391e322Sahrens 			changelist_remove(clp, zhp->zfs_name);
3238ba7b046eSahrens 		(void) changelist_postfix(clp);
3239c391e322Sahrens 		changelist_free(clp);
3240b12a1c38Slling 	}
3241b12a1c38Slling 
3242b12a1c38Slling 	zfs_close(zhp);
3243b12a1c38Slling 	return (0);
3244b12a1c38Slling }
3245b12a1c38Slling 
3246b12a1c38Slling /*
3247b12a1c38Slling  * Given a dataset, rollback to a specific snapshot, discarding any
3248b12a1c38Slling  * data changes since then and making it the active dataset.
3249b12a1c38Slling  *
3250b12a1c38Slling  * Any snapshots more recent than the target are destroyed, along with
3251b12a1c38Slling  * their dependents.
3252b12a1c38Slling  */
3253b12a1c38Slling int
3254c391e322Sahrens zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
3255b12a1c38Slling {
3256b12a1c38Slling 	rollback_data_t cb = { 0 };
32574ccbb6e7Sahrens 	int err;
32584ccbb6e7Sahrens 	zfs_cmd_t zc = { 0 };
32597b97dc1aSrm 	boolean_t restore_resv = 0;
32607b97dc1aSrm 	uint64_t old_volsize, new_volsize;
32617b97dc1aSrm 	zfs_prop_t resv_prop;
3262b12a1c38Slling 
32634ccbb6e7Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
32644ccbb6e7Sahrens 	    zhp->zfs_type == ZFS_TYPE_VOLUME);
3265b12a1c38Slling 
3266b12a1c38Slling 	/*
3267b12a1c38Slling 	 * Destroy all recent snapshots and its dependends.
3268b12a1c38Slling 	 */
3269c391e322Sahrens 	cb.cb_force = force;
3270b12a1c38Slling 	cb.cb_target = snap->zfs_name;
3271b12a1c38Slling 	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3272b12a1c38Slling 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3273b12a1c38Slling 
3274c391e322Sahrens 	if (cb.cb_error)
3275c391e322Sahrens 		return (-1);
3276b12a1c38Slling 
3277b12a1c38Slling 	/*
3278b12a1c38Slling 	 * Now that we have verified that the snapshot is the latest,
3279b12a1c38Slling 	 * rollback to the given snapshot.
3280b12a1c38Slling 	 */
3281b12a1c38Slling 
32827b97dc1aSrm 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
32837b97dc1aSrm 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
32847b97dc1aSrm 			return (-1);
32857b97dc1aSrm 		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
32867b97dc1aSrm 			return (-1);
32877b97dc1aSrm 		old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
32887b97dc1aSrm 		restore_resv =
32897b97dc1aSrm 		    (old_volsize == zfs_prop_get_int(zhp, resv_prop));
32907b97dc1aSrm 	}
32914ccbb6e7Sahrens 
32924ccbb6e7Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
32934ccbb6e7Sahrens 
32944ccbb6e7Sahrens 	if (ZFS_IS_VOLUME(zhp))
32954ccbb6e7Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
32964ccbb6e7Sahrens 	else
32974ccbb6e7Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3298b12a1c38Slling 
3299b12a1c38Slling 	/*
33004ccbb6e7Sahrens 	 * We rely on zfs_iter_children() to verify that there are no
33014ccbb6e7Sahrens 	 * newer snapshots for the given dataset.  Therefore, we can
33024ccbb6e7Sahrens 	 * simply pass the name on to the ioctl() call.  There is still
33034ccbb6e7Sahrens 	 * an unlikely race condition where the user has taken a
33044ccbb6e7Sahrens 	 * snapshot since we verified that this was the most recent.
33057b97dc1aSrm 	 *
3306b12a1c38Slling 	 */
33074ccbb6e7Sahrens 	if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
33084ccbb6e7Sahrens 		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
33094ccbb6e7Sahrens 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
33104ccbb6e7Sahrens 		    zhp->zfs_name);
3311b9415e83Srm 		return (err);
3312b9415e83Srm 	}
33137b97dc1aSrm 
33147b97dc1aSrm 	/*
33157b97dc1aSrm 	 * For volumes, if the pre-rollback volsize matched the pre-
33167b97dc1aSrm 	 * rollback reservation and the volsize has changed then set
33177b97dc1aSrm 	 * the reservation property to the post-rollback volsize.
33187b97dc1aSrm 	 * Make a new handle since the rollback closed the dataset.
33197b97dc1aSrm 	 */
3320b9415e83Srm 	if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
3321b9415e83Srm 	    (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
3322b9415e83Srm 		if (err = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name)) {
3323b9415e83Srm 			zfs_close(zhp);
33247b97dc1aSrm 			return (err);
3325b9415e83Srm 		}
33267b97dc1aSrm 		if (restore_resv) {
33277b97dc1aSrm 			new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
33287b97dc1aSrm 			if (old_volsize != new_volsize)
3329b9415e83Srm 				err = zfs_prop_set_int(zhp, resv_prop,
3330b9415e83Srm 				    new_volsize);
33317b97dc1aSrm 		}
33327b97dc1aSrm 		zfs_close(zhp);
33334ccbb6e7Sahrens 	}
33344ccbb6e7Sahrens 	return (err);
3335b12a1c38Slling }
3336b12a1c38Slling 
3337fa9e4066Sahrens /*
3338fa9e4066Sahrens  * Iterate over all dependents for a given dataset.  This includes both
3339fa9e4066Sahrens  * hierarchical dependents (children) and data dependents (snapshots and
3340fa9e4066Sahrens  * clones).  The bulk of the processing occurs in get_dependents() in
3341fa9e4066Sahrens  * libzfs_graph.c.
3342fa9e4066Sahrens  */
3343fa9e4066Sahrens int
33443bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
33453bb79becSeschrock     zfs_iter_f func, void *data)
3346fa9e4066Sahrens {
3347fa9e4066Sahrens 	char **dependents;
3348fa9e4066Sahrens 	size_t count;
3349fa9e4066Sahrens 	int i;
3350fa9e4066Sahrens 	zfs_handle_t *child;
3351fa9e4066Sahrens 	int ret = 0;
3352fa9e4066Sahrens 
33533bb79becSeschrock 	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
33543bb79becSeschrock 	    &dependents, &count) != 0)
33553bb79becSeschrock 		return (-1);
33563bb79becSeschrock 
3357fa9e4066Sahrens 	for (i = 0; i < count; i++) {
335899653d4eSeschrock 		if ((child = make_dataset_handle(zhp->zfs_hdl,
335999653d4eSeschrock 		    dependents[i])) == NULL)
3360fa9e4066Sahrens 			continue;
3361fa9e4066Sahrens 
3362fa9e4066Sahrens 		if ((ret = func(child, data)) != 0)
3363fa9e4066Sahrens 			break;
3364fa9e4066Sahrens 	}
3365fa9e4066Sahrens 
3366fa9e4066Sahrens 	for (i = 0; i < count; i++)
3367fa9e4066Sahrens 		free(dependents[i]);
3368fa9e4066Sahrens 	free(dependents);
3369fa9e4066Sahrens 
3370fa9e4066Sahrens 	return (ret);
3371fa9e4066Sahrens }
3372fa9e4066Sahrens 
3373fa9e4066Sahrens /*
3374fa9e4066Sahrens  * Renames the given dataset.
3375fa9e4066Sahrens  */
3376fa9e4066Sahrens int
33777f1f55eaSvb zfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
3378fa9e4066Sahrens {
3379fa9e4066Sahrens 	int ret;
3380fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3381fa9e4066Sahrens 	char *delim;
3382cdf5b4caSmmusante 	prop_changelist_t *cl = NULL;
3383cdf5b4caSmmusante 	zfs_handle_t *zhrp = NULL;
3384cdf5b4caSmmusante 	char *parentname = NULL;
3385fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
338699653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
338799653d4eSeschrock 	char errbuf[1024];
3388fa9e4066Sahrens 
3389fa9e4066Sahrens 	/* if we have the same exact name, just return success */
3390fa9e4066Sahrens 	if (strcmp(zhp->zfs_name, target) == 0)
3391fa9e4066Sahrens 		return (0);
3392fa9e4066Sahrens 
339399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
339499653d4eSeschrock 	    "cannot rename to '%s'"), target);
339599653d4eSeschrock 
3396fa9e4066Sahrens 	/*
3397fa9e4066Sahrens 	 * Make sure the target name is valid
3398fa9e4066Sahrens 	 */
3399fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
340098579b20Snd 		if ((strchr(target, '@') == NULL) ||
340198579b20Snd 		    *target == '@') {
340298579b20Snd 			/*
340398579b20Snd 			 * Snapshot target name is abbreviated,
340498579b20Snd 			 * reconstruct full dataset name
340598579b20Snd 			 */
340698579b20Snd 			(void) strlcpy(parent, zhp->zfs_name,
340798579b20Snd 			    sizeof (parent));
340898579b20Snd 			delim = strchr(parent, '@');
340998579b20Snd 			if (strchr(target, '@') == NULL)
341098579b20Snd 				*(++delim) = '\0';
341198579b20Snd 			else
341298579b20Snd 				*delim = '\0';
341398579b20Snd 			(void) strlcat(parent, target, sizeof (parent));
341498579b20Snd 			target = parent;
341598579b20Snd 		} else {
341698579b20Snd 			/*
341798579b20Snd 			 * Make sure we're renaming within the same dataset.
341898579b20Snd 			 */
341998579b20Snd 			delim = strchr(target, '@');
342098579b20Snd 			if (strncmp(zhp->zfs_name, target, delim - target)
342198579b20Snd 			    != 0 || zhp->zfs_name[delim - target] != '@') {
342298579b20Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
342398579b20Snd 				    "snapshots must be part of same "
342498579b20Snd 				    "dataset"));
342598579b20Snd 				return (zfs_error(hdl, EZFS_CROSSTARGET,
3426b1b8ab34Slling 				    errbuf));
342798579b20Snd 			}
3428fa9e4066Sahrens 		}
3429f18faf3fSek 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
343098579b20Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3431fa9e4066Sahrens 	} else {
3432cdf5b4caSmmusante 		if (recursive) {
3433cdf5b4caSmmusante 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3434cdf5b4caSmmusante 			    "recursive rename must be a snapshot"));
3435cdf5b4caSmmusante 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3436cdf5b4caSmmusante 		}
3437cdf5b4caSmmusante 
3438f18faf3fSek 		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
343998579b20Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3440e9dbad6fSeschrock 		uint64_t unused;
3441e9dbad6fSeschrock 
3442fa9e4066Sahrens 		/* validate parents */
34437f1f55eaSvb 		if (check_parents(hdl, target, &unused, B_FALSE, NULL) != 0)
3444fa9e4066Sahrens 			return (-1);
3445fa9e4066Sahrens 
3446fa9e4066Sahrens 		(void) parent_name(target, parent, sizeof (parent));
3447fa9e4066Sahrens 
3448fa9e4066Sahrens 		/* make sure we're in the same pool */
3449fa9e4066Sahrens 		verify((delim = strchr(target, '/')) != NULL);
3450fa9e4066Sahrens 		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3451fa9e4066Sahrens 		    zhp->zfs_name[delim - target] != '/') {
345299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
345399653d4eSeschrock 			    "datasets must be within same pool"));
345499653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3455fa9e4066Sahrens 		}
3456f2fdf992Snd 
3457f2fdf992Snd 		/* new name cannot be a child of the current dataset name */
3458f2fdf992Snd 		if (strncmp(parent, zhp->zfs_name,
3459b1b8ab34Slling 		    strlen(zhp->zfs_name)) == 0) {
3460f2fdf992Snd 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3461f2fdf992Snd 			    "New dataset name cannot be a descendent of "
3462f2fdf992Snd 			    "current dataset name"));
3463f2fdf992Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3464f2fdf992Snd 		}
3465fa9e4066Sahrens 	}
3466fa9e4066Sahrens 
346799653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
346899653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
346999653d4eSeschrock 
3470fa9e4066Sahrens 	if (getzoneid() == GLOBAL_ZONEID &&
3471fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
347299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
347399653d4eSeschrock 		    "dataset is used in a non-global zone"));
347499653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3475fa9e4066Sahrens 	}
3476fa9e4066Sahrens 
3477cdf5b4caSmmusante 	if (recursive) {
3478cdf5b4caSmmusante 		struct destroydata dd;
3479fa9e4066Sahrens 
3480f0c5ee21Smmusante 		parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
3481f0c5ee21Smmusante 		if (parentname == NULL) {
3482f0c5ee21Smmusante 			ret = -1;
3483f0c5ee21Smmusante 			goto error;
3484f0c5ee21Smmusante 		}
3485cdf5b4caSmmusante 		delim = strchr(parentname, '@');
3486cdf5b4caSmmusante 		*delim = '\0';
3487990b4856Slling 		zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
3488cdf5b4caSmmusante 		if (zhrp == NULL) {
3489f0c5ee21Smmusante 			ret = -1;
3490f0c5ee21Smmusante 			goto error;
3491cdf5b4caSmmusante 		}
3492fa9e4066Sahrens 
3493cdf5b4caSmmusante 		dd.snapname = delim + 1;
3494cdf5b4caSmmusante 		dd.gotone = B_FALSE;
3495f0c5ee21Smmusante 		dd.closezhp = B_TRUE;
3496cdf5b4caSmmusante 
3497cdf5b4caSmmusante 		/* We remove any zvol links prior to renaming them */
3498cdf5b4caSmmusante 		ret = zfs_iter_filesystems(zhrp, zfs_remove_link_cb, &dd);
3499cdf5b4caSmmusante 		if (ret) {
3500cdf5b4caSmmusante 			goto error;
3501cdf5b4caSmmusante 		}
3502cdf5b4caSmmusante 	} else {
35030069fd67STim Haley 		if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
3504cdf5b4caSmmusante 			return (-1);
3505cdf5b4caSmmusante 
3506cdf5b4caSmmusante 		if (changelist_haszonedchild(cl)) {
3507cdf5b4caSmmusante 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3508cdf5b4caSmmusante 			    "child dataset with inherited mountpoint is used "
3509cdf5b4caSmmusante 			    "in a non-global zone"));
3510cdf5b4caSmmusante 			(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3511cdf5b4caSmmusante 			goto error;
3512cdf5b4caSmmusante 		}
3513cdf5b4caSmmusante 
3514cdf5b4caSmmusante 		if ((ret = changelist_prefix(cl)) != 0)
3515cdf5b4caSmmusante 			goto error;
3516cdf5b4caSmmusante 	}
3517fa9e4066Sahrens 
3518e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
3519fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
3520fa9e4066Sahrens 	else
3521fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3522fa9e4066Sahrens 
352398579b20Snd 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3524e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
352598579b20Snd 
3526cdf5b4caSmmusante 	zc.zc_cookie = recursive;
3527cdf5b4caSmmusante 
3528ecd6cf80Smarks 	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
3529cdf5b4caSmmusante 		/*
3530cdf5b4caSmmusante 		 * if it was recursive, the one that actually failed will
3531cdf5b4caSmmusante 		 * be in zc.zc_name
3532cdf5b4caSmmusante 		 */
3533cdf5b4caSmmusante 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
35343cb34c60Sahrens 		    "cannot rename '%s'"), zc.zc_name);
3535cdf5b4caSmmusante 
3536cdf5b4caSmmusante 		if (recursive && errno == EEXIST) {
3537cdf5b4caSmmusante 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3538cdf5b4caSmmusante 			    "a child dataset already has a snapshot "
3539cdf5b4caSmmusante 			    "with the new name"));
3540a10acbd6Seschrock 			(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3541cdf5b4caSmmusante 		} else {
3542cdf5b4caSmmusante 			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3543cdf5b4caSmmusante 		}
3544fa9e4066Sahrens 
3545fa9e4066Sahrens 		/*
3546fa9e4066Sahrens 		 * On failure, we still want to remount any filesystems that
3547fa9e4066Sahrens 		 * were previously mounted, so we don't alter the system state.
3548fa9e4066Sahrens 		 */
3549cdf5b4caSmmusante 		if (recursive) {
3550cdf5b4caSmmusante 			struct createdata cd;
3551cdf5b4caSmmusante 
3552cdf5b4caSmmusante 			/* only create links for datasets that had existed */
3553cdf5b4caSmmusante 			cd.cd_snapname = delim + 1;
3554cdf5b4caSmmusante 			cd.cd_ifexists = B_TRUE;
3555cdf5b4caSmmusante 			(void) zfs_iter_filesystems(zhrp, zfs_create_link_cb,
3556cdf5b4caSmmusante 			    &cd);
3557cdf5b4caSmmusante 		} else {
3558cdf5b4caSmmusante 			(void) changelist_postfix(cl);
3559cdf5b4caSmmusante 		}
3560fa9e4066Sahrens 	} else {
3561cdf5b4caSmmusante 		if (recursive) {
3562cdf5b4caSmmusante 			struct createdata cd;
3563cdf5b4caSmmusante 
3564cdf5b4caSmmusante 			/* only create links for datasets that had existed */
3565cdf5b4caSmmusante 			cd.cd_snapname = strchr(target, '@') + 1;
3566cdf5b4caSmmusante 			cd.cd_ifexists = B_TRUE;
3567cdf5b4caSmmusante 			ret = zfs_iter_filesystems(zhrp, zfs_create_link_cb,
3568cdf5b4caSmmusante 			    &cd);
3569cdf5b4caSmmusante 		} else {
3570cdf5b4caSmmusante 			changelist_rename(cl, zfs_get_name(zhp), target);
3571cdf5b4caSmmusante 			ret = changelist_postfix(cl);
3572cdf5b4caSmmusante 		}
3573fa9e4066Sahrens 	}
3574fa9e4066Sahrens 
3575fa9e4066Sahrens error:
3576cdf5b4caSmmusante 	if (parentname) {
3577cdf5b4caSmmusante 		free(parentname);
3578cdf5b4caSmmusante 	}
3579cdf5b4caSmmusante 	if (zhrp) {
3580cdf5b4caSmmusante 		zfs_close(zhrp);
3581cdf5b4caSmmusante 	}
3582cdf5b4caSmmusante 	if (cl) {
3583cdf5b4caSmmusante 		changelist_free(cl);
3584cdf5b4caSmmusante 	}
3585fa9e4066Sahrens 	return (ret);
3586fa9e4066Sahrens }
3587fa9e4066Sahrens 
3588fa9e4066Sahrens /*
3589fa9e4066Sahrens  * Given a zvol dataset, issue the ioctl to create the appropriate minor node,
3590fa9e4066Sahrens  * poke devfsadm to create the /dev link, and then wait for the link to appear.
3591fa9e4066Sahrens  */
3592fa9e4066Sahrens int
359399653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
3594cdf5b4caSmmusante {
3595cdf5b4caSmmusante 	return (zvol_create_link_common(hdl, dataset, B_FALSE));
3596cdf5b4caSmmusante }
3597cdf5b4caSmmusante 
3598cdf5b4caSmmusante static int
3599cdf5b4caSmmusante zvol_create_link_common(libzfs_handle_t *hdl, const char *dataset, int ifexists)
3600fa9e4066Sahrens {
3601fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
360299653d4eSeschrock 	di_devlink_handle_t dhdl;
3603ecd6cf80Smarks 	priv_set_t *priv_effective;
3604ecd6cf80Smarks 	int privileged;
3605fa9e4066Sahrens 
3606fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3607fa9e4066Sahrens 
3608fa9e4066Sahrens 	/*
3609fa9e4066Sahrens 	 * Issue the appropriate ioctl.
3610fa9e4066Sahrens 	 */
361199653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
3612fa9e4066Sahrens 		switch (errno) {
3613fa9e4066Sahrens 		case EEXIST:
3614fa9e4066Sahrens 			/*
3615fa9e4066Sahrens 			 * Silently ignore the case where the link already
3616fa9e4066Sahrens 			 * exists.  This allows 'zfs volinit' to be run multiple
3617fa9e4066Sahrens 			 * times without errors.
3618fa9e4066Sahrens 			 */
3619fa9e4066Sahrens 			return (0);
3620fa9e4066Sahrens 
3621cdf5b4caSmmusante 		case ENOENT:
3622cdf5b4caSmmusante 			/*
3623cdf5b4caSmmusante 			 * Dataset does not exist in the kernel.  If we
3624cdf5b4caSmmusante 			 * don't care (see zfs_rename), then ignore the
3625cdf5b4caSmmusante 			 * error quietly.
3626cdf5b4caSmmusante 			 */
3627cdf5b4caSmmusante 			if (ifexists) {
3628cdf5b4caSmmusante 				return (0);
3629cdf5b4caSmmusante 			}
3630cdf5b4caSmmusante 
3631cdf5b4caSmmusante 			/* FALLTHROUGH */
3632cdf5b4caSmmusante 
3633fa9e4066Sahrens 		default:
3634ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
363599653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot create device links "
363699653d4eSeschrock 			    "for '%s'"), dataset));
3637fa9e4066Sahrens 		}
3638fa9e4066Sahrens 	}
3639fa9e4066Sahrens 
3640fa9e4066Sahrens 	/*
3641ecd6cf80Smarks 	 * If privileged call devfsadm and wait for the links to
3642ecd6cf80Smarks 	 * magically appear.
3643ecd6cf80Smarks 	 * Otherwise, print out an informational message.
3644fa9e4066Sahrens 	 */
3645ecd6cf80Smarks 
3646ecd6cf80Smarks 	priv_effective = priv_allocset();
3647ecd6cf80Smarks 	(void) getppriv(PRIV_EFFECTIVE, priv_effective);
3648ecd6cf80Smarks 	privileged = (priv_isfullset(priv_effective) == B_TRUE);
3649ecd6cf80Smarks 	priv_freeset(priv_effective);
3650ecd6cf80Smarks 
3651ecd6cf80Smarks 	if (privileged) {
3652ecd6cf80Smarks 		if ((dhdl = di_devlink_init(ZFS_DRIVER,
3653ecd6cf80Smarks 		    DI_MAKE_LINK)) == NULL) {
3654ecd6cf80Smarks 			zfs_error_aux(hdl, strerror(errno));
3655d1e7ea07SEric Taylor 			(void) zfs_error_fmt(hdl, errno,
3656ecd6cf80Smarks 			    dgettext(TEXT_DOMAIN, "cannot create device links "
3657ecd6cf80Smarks 			    "for '%s'"), dataset);
3658ecd6cf80Smarks 			(void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
3659ecd6cf80Smarks 			return (-1);
3660ecd6cf80Smarks 		} else {
3661ecd6cf80Smarks 			(void) di_devlink_fini(&dhdl);
3662ecd6cf80Smarks 		}
3663fa9e4066Sahrens 	} else {
3664ecd6cf80Smarks 		char pathname[MAXPATHLEN];
3665ecd6cf80Smarks 		struct stat64 statbuf;
3666ecd6cf80Smarks 		int i;
3667ecd6cf80Smarks 
3668ecd6cf80Smarks #define	MAX_WAIT	10
3669ecd6cf80Smarks 
3670ecd6cf80Smarks 		/*
3671ecd6cf80Smarks 		 * This is the poor mans way of waiting for the link
3672ecd6cf80Smarks 		 * to show up.  If after 10 seconds we still don't
3673ecd6cf80Smarks 		 * have it, then print out a message.
3674ecd6cf80Smarks 		 */
3675ecd6cf80Smarks 		(void) snprintf(pathname, sizeof (pathname), "/dev/zvol/dsk/%s",
3676ecd6cf80Smarks 		    dataset);
3677ecd6cf80Smarks 
3678ecd6cf80Smarks 		for (i = 0; i != MAX_WAIT; i++) {
3679ecd6cf80Smarks 			if (stat64(pathname, &statbuf) == 0)
3680ecd6cf80Smarks 				break;
3681ecd6cf80Smarks 			(void) sleep(1);
3682ecd6cf80Smarks 		}
3683ecd6cf80Smarks 		if (i == MAX_WAIT)
3684ecd6cf80Smarks 			(void) printf(gettext("%s may not be immediately "
3685ecd6cf80Smarks 			    "available\n"), pathname);
3686fa9e4066Sahrens 	}
3687fa9e4066Sahrens 
3688fa9e4066Sahrens 	return (0);
3689fa9e4066Sahrens }
3690fa9e4066Sahrens 
3691fa9e4066Sahrens /*
3692fa9e4066Sahrens  * Remove a minor node for the given zvol and the associated /dev links.
3693fa9e4066Sahrens  */
3694fa9e4066Sahrens int
369599653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
3696fa9e4066Sahrens {
3697fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3698fa9e4066Sahrens 
3699fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3700fa9e4066Sahrens 
370199653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
3702fa9e4066Sahrens 		switch (errno) {
3703fa9e4066Sahrens 		case ENXIO:
3704fa9e4066Sahrens 			/*
3705fa9e4066Sahrens 			 * Silently ignore the case where the link no longer
3706fa9e4066Sahrens 			 * exists, so that 'zfs volfini' can be run multiple
3707fa9e4066Sahrens 			 * times without errors.
3708fa9e4066Sahrens 			 */
3709fa9e4066Sahrens 			return (0);
3710fa9e4066Sahrens 
3711fa9e4066Sahrens 		default:
3712ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
371399653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot remove device "
371499653d4eSeschrock 			    "links for '%s'"), dataset));
3715fa9e4066Sahrens 		}
3716fa9e4066Sahrens 	}
3717fa9e4066Sahrens 
3718fa9e4066Sahrens 	return (0);
3719fa9e4066Sahrens }
3720e9dbad6fSeschrock 
3721e9dbad6fSeschrock nvlist_t *
3722e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp)
3723e9dbad6fSeschrock {
3724e9dbad6fSeschrock 	return (zhp->zfs_user_props);
3725e9dbad6fSeschrock }
3726e9dbad6fSeschrock 
3727b1b8ab34Slling /*
3728b1b8ab34Slling  * This function is used by 'zfs list' to determine the exact set of columns to
3729b1b8ab34Slling  * display, and their maximum widths.  This does two main things:
3730b1b8ab34Slling  *
3731b1b8ab34Slling  *      - If this is a list of all properties, then expand the list to include
3732b1b8ab34Slling  *        all native properties, and set a flag so that for each dataset we look
3733b1b8ab34Slling  *        for new unique user properties and add them to the list.
3734b1b8ab34Slling  *
3735b1b8ab34Slling  *      - For non fixed-width properties, keep track of the maximum width seen
3736b1b8ab34Slling  *        so that we can size the column appropriately.
3737b1b8ab34Slling  */
3738b1b8ab34Slling int
3739990b4856Slling zfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp)
3740b1b8ab34Slling {
3741b1b8ab34Slling 	libzfs_handle_t *hdl = zhp->zfs_hdl;
3742990b4856Slling 	zprop_list_t *entry;
3743990b4856Slling 	zprop_list_t **last, **start;
3744b1b8ab34Slling 	nvlist_t *userprops, *propval;
3745b1b8ab34Slling 	nvpair_t *elem;
3746b1b8ab34Slling 	char *strval;
3747b1b8ab34Slling 	char buf[ZFS_MAXPROPLEN];
3748b1b8ab34Slling 
3749990b4856Slling 	if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
3750b1b8ab34Slling 		return (-1);
3751e9dbad6fSeschrock 
3752e9dbad6fSeschrock 	userprops = zfs_get_user_props(zhp);
3753e9dbad6fSeschrock 
3754e9dbad6fSeschrock 	entry = *plp;
3755e9dbad6fSeschrock 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3756e9dbad6fSeschrock 		/*
3757e9dbad6fSeschrock 		 * Go through and add any user properties as necessary.  We
3758e9dbad6fSeschrock 		 * start by incrementing our list pointer to the first
3759e9dbad6fSeschrock 		 * non-native property.
3760e9dbad6fSeschrock 		 */
3761e9dbad6fSeschrock 		start = plp;
3762e9dbad6fSeschrock 		while (*start != NULL) {
3763990b4856Slling 			if ((*start)->pl_prop == ZPROP_INVAL)
3764e9dbad6fSeschrock 				break;
3765e9dbad6fSeschrock 			start = &(*start)->pl_next;
3766e9dbad6fSeschrock 		}
3767e9dbad6fSeschrock 
3768e9dbad6fSeschrock 		elem = NULL;
3769e9dbad6fSeschrock 		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3770e9dbad6fSeschrock 			/*
3771e9dbad6fSeschrock 			 * See if we've already found this property in our list.
3772e9dbad6fSeschrock 			 */
3773e9dbad6fSeschrock 			for (last = start; *last != NULL;
3774e9dbad6fSeschrock 			    last = &(*last)->pl_next) {
3775e9dbad6fSeschrock 				if (strcmp((*last)->pl_user_prop,
3776e9dbad6fSeschrock 				    nvpair_name(elem)) == 0)
3777e9dbad6fSeschrock 					break;
3778e9dbad6fSeschrock 			}
3779e9dbad6fSeschrock 
3780e9dbad6fSeschrock 			if (*last == NULL) {
3781e9dbad6fSeschrock 				if ((entry = zfs_alloc(hdl,
3782990b4856Slling 				    sizeof (zprop_list_t))) == NULL ||
3783e9dbad6fSeschrock 				    ((entry->pl_user_prop = zfs_strdup(hdl,
3784e9dbad6fSeschrock 				    nvpair_name(elem)))) == NULL) {
3785e9dbad6fSeschrock 					free(entry);
3786e9dbad6fSeschrock 					return (-1);
3787e9dbad6fSeschrock 				}
3788e9dbad6fSeschrock 
3789990b4856Slling 				entry->pl_prop = ZPROP_INVAL;
3790e9dbad6fSeschrock 				entry->pl_width = strlen(nvpair_name(elem));
3791e9dbad6fSeschrock 				entry->pl_all = B_TRUE;
3792e9dbad6fSeschrock 				*last = entry;
3793e9dbad6fSeschrock 			}
3794e9dbad6fSeschrock 		}
3795e9dbad6fSeschrock 	}
3796e9dbad6fSeschrock 
3797e9dbad6fSeschrock 	/*
3798e9dbad6fSeschrock 	 * Now go through and check the width of any non-fixed columns
3799e9dbad6fSeschrock 	 */
3800e9dbad6fSeschrock 	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3801e9dbad6fSeschrock 		if (entry->pl_fixed)
3802e9dbad6fSeschrock 			continue;
3803e9dbad6fSeschrock 
3804990b4856Slling 		if (entry->pl_prop != ZPROP_INVAL) {
3805e9dbad6fSeschrock 			if (zfs_prop_get(zhp, entry->pl_prop,
3806e9dbad6fSeschrock 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3807e9dbad6fSeschrock 				if (strlen(buf) > entry->pl_width)
3808e9dbad6fSeschrock 					entry->pl_width = strlen(buf);
3809e9dbad6fSeschrock 			}
3810e9dbad6fSeschrock 		} else if (nvlist_lookup_nvlist(userprops,
3811e9dbad6fSeschrock 		    entry->pl_user_prop, &propval)  == 0) {
3812e9dbad6fSeschrock 			verify(nvlist_lookup_string(propval,
3813990b4856Slling 			    ZPROP_VALUE, &strval) == 0);
3814e9dbad6fSeschrock 			if (strlen(strval) > entry->pl_width)
3815e9dbad6fSeschrock 				entry->pl_width = strlen(strval);
3816e9dbad6fSeschrock 		}
3817e9dbad6fSeschrock 	}
3818e9dbad6fSeschrock 
3819e9dbad6fSeschrock 	return (0);
3820e9dbad6fSeschrock }
3821ecd6cf80Smarks 
3822ecd6cf80Smarks int
3823ecd6cf80Smarks zfs_iscsi_perm_check(libzfs_handle_t *hdl, char *dataset, ucred_t *cred)
3824ecd6cf80Smarks {
3825ecd6cf80Smarks 	zfs_cmd_t zc = { 0 };
3826ecd6cf80Smarks 	nvlist_t *nvp;
3827ecd6cf80Smarks 	gid_t gid;
3828ecd6cf80Smarks 	uid_t uid;
3829ecd6cf80Smarks 	const gid_t *groups;
3830ecd6cf80Smarks 	int group_cnt;
3831ecd6cf80Smarks 	int error;
3832ecd6cf80Smarks 
3833ecd6cf80Smarks 	if (nvlist_alloc(&nvp, NV_UNIQUE_NAME, 0) != 0)
3834ecd6cf80Smarks 		return (no_memory(hdl));
3835ecd6cf80Smarks 
3836ecd6cf80Smarks 	uid = ucred_geteuid(cred);
3837ecd6cf80Smarks 	gid = ucred_getegid(cred);
3838ecd6cf80Smarks 	group_cnt = ucred_getgroups(cred, &groups);
3839ecd6cf80Smarks 
3840ecd6cf80Smarks 	if (uid == (uid_t)-1 || gid == (uid_t)-1 || group_cnt == (uid_t)-1)
3841ecd6cf80Smarks 		return (1);
3842ecd6cf80Smarks 
3843ecd6cf80Smarks 	if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_UID, uid) != 0) {
3844ecd6cf80Smarks 		nvlist_free(nvp);
3845ecd6cf80Smarks 		return (1);
3846ecd6cf80Smarks 	}
3847ecd6cf80Smarks 
3848ecd6cf80Smarks 	if (nvlist_add_uint32(nvp, ZFS_DELEG_PERM_GID, gid) != 0) {
3849ecd6cf80Smarks 		nvlist_free(nvp);
3850ecd6cf80Smarks 		return (1);
3851ecd6cf80Smarks 	}
3852ecd6cf80Smarks 
3853ecd6cf80Smarks 	if (nvlist_add_uint32_array(nvp,
3854ecd6cf80Smarks 	    ZFS_DELEG_PERM_GROUPS, (uint32_t *)groups, group_cnt) != 0) {
3855ecd6cf80Smarks 		nvlist_free(nvp);
3856ecd6cf80Smarks 		return (1);
3857ecd6cf80Smarks 	}
3858ecd6cf80Smarks 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3859ecd6cf80Smarks 
3860990b4856Slling 	if (zcmd_write_src_nvlist(hdl, &zc, nvp))
3861ecd6cf80Smarks 		return (-1);
3862ecd6cf80Smarks 
3863ecd6cf80Smarks 	error = ioctl(hdl->libzfs_fd, ZFS_IOC_ISCSI_PERM_CHECK, &zc);
3864ecd6cf80Smarks 	nvlist_free(nvp);
3865ecd6cf80Smarks 	return (error);
3866ecd6cf80Smarks }
3867ecd6cf80Smarks 
3868ecd6cf80Smarks int
3869ecd6cf80Smarks zfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
3870743a77edSAlan Wright     char *resource, void *export, void *sharetab,
3871743a77edSAlan Wright     int sharemax, zfs_share_op_t operation)
3872ecd6cf80Smarks {
3873ecd6cf80Smarks 	zfs_cmd_t zc = { 0 };
3874ecd6cf80Smarks 	int error;
3875ecd6cf80Smarks 
3876ecd6cf80Smarks 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3877ecd6cf80Smarks 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3878743a77edSAlan Wright 	if (resource)
3879743a77edSAlan Wright 		(void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
3880ecd6cf80Smarks 	zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
3881ecd6cf80Smarks 	zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
3882da6c28aaSamw 	zc.zc_share.z_sharetype = operation;
3883ecd6cf80Smarks 	zc.zc_share.z_sharemax = sharemax;
3884ecd6cf80Smarks 	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
3885ecd6cf80Smarks 	return (error);
3886ecd6cf80Smarks }
38872e5e9e19SSanjeev Bagewadi 
38882e5e9e19SSanjeev Bagewadi void
38892e5e9e19SSanjeev Bagewadi zfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
38902e5e9e19SSanjeev Bagewadi {
38912e5e9e19SSanjeev Bagewadi 	nvpair_t *curr;
38922e5e9e19SSanjeev Bagewadi 
38932e5e9e19SSanjeev Bagewadi 	/*
38942e5e9e19SSanjeev Bagewadi 	 * Keep a reference to the props-table against which we prune the
38952e5e9e19SSanjeev Bagewadi 	 * properties.
38962e5e9e19SSanjeev Bagewadi 	 */
38972e5e9e19SSanjeev Bagewadi 	zhp->zfs_props_table = props;
38982e5e9e19SSanjeev Bagewadi 
38992e5e9e19SSanjeev Bagewadi 	curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
39002e5e9e19SSanjeev Bagewadi 
39012e5e9e19SSanjeev Bagewadi 	while (curr) {
39022e5e9e19SSanjeev Bagewadi 		zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
39032e5e9e19SSanjeev Bagewadi 		nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
39042e5e9e19SSanjeev Bagewadi 
390514843421SMatthew Ahrens 		/*
390614843421SMatthew Ahrens 		 * We leave user:props in the nvlist, so there will be
390714843421SMatthew Ahrens 		 * some ZPROP_INVAL.  To be extra safe, don't prune
390814843421SMatthew Ahrens 		 * those.
390914843421SMatthew Ahrens 		 */
391014843421SMatthew Ahrens 		if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
39112e5e9e19SSanjeev Bagewadi 			(void) nvlist_remove(zhp->zfs_props,
39122e5e9e19SSanjeev Bagewadi 			    nvpair_name(curr), nvpair_type(curr));
39132e5e9e19SSanjeev Bagewadi 		curr = next;
39142e5e9e19SSanjeev Bagewadi 	}
39152e5e9e19SSanjeev Bagewadi }
3916743a77edSAlan Wright 
3917743a77edSAlan Wright static int
3918743a77edSAlan Wright zfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
3919743a77edSAlan Wright     zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
3920743a77edSAlan Wright {
3921743a77edSAlan Wright 	zfs_cmd_t zc = { 0 };
3922743a77edSAlan Wright 	nvlist_t *nvlist = NULL;
3923743a77edSAlan Wright 	int error;
3924743a77edSAlan Wright 
3925743a77edSAlan Wright 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3926743a77edSAlan Wright 	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3927743a77edSAlan Wright 	zc.zc_cookie = (uint64_t)cmd;
3928743a77edSAlan Wright 
3929743a77edSAlan Wright 	if (cmd == ZFS_SMB_ACL_RENAME) {
3930743a77edSAlan Wright 		if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
3931743a77edSAlan Wright 			(void) no_memory(hdl);
3932743a77edSAlan Wright 			return (NULL);
3933743a77edSAlan Wright 		}
3934743a77edSAlan Wright 	}
3935743a77edSAlan Wright 
3936743a77edSAlan Wright 	switch (cmd) {
3937743a77edSAlan Wright 	case ZFS_SMB_ACL_ADD:
3938743a77edSAlan Wright 	case ZFS_SMB_ACL_REMOVE:
3939743a77edSAlan Wright 		(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
3940743a77edSAlan Wright 		break;
3941743a77edSAlan Wright 	case ZFS_SMB_ACL_RENAME:
3942743a77edSAlan Wright 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
3943743a77edSAlan Wright 		    resource1) != 0) {
3944743a77edSAlan Wright 				(void) no_memory(hdl);
3945743a77edSAlan Wright 				return (-1);
3946743a77edSAlan Wright 		}
3947743a77edSAlan Wright 		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
3948743a77edSAlan Wright 		    resource2) != 0) {
3949743a77edSAlan Wright 				(void) no_memory(hdl);
3950743a77edSAlan Wright 				return (-1);
3951743a77edSAlan Wright 		}
3952743a77edSAlan Wright 		if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
3953743a77edSAlan Wright 			nvlist_free(nvlist);
3954743a77edSAlan Wright 			return (-1);
3955743a77edSAlan Wright 		}
3956743a77edSAlan Wright 		break;
3957743a77edSAlan Wright 	case ZFS_SMB_ACL_PURGE:
3958743a77edSAlan Wright 		break;
3959743a77edSAlan Wright 	default:
3960743a77edSAlan Wright 		return (-1);
3961743a77edSAlan Wright 	}
3962743a77edSAlan Wright 	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
3963743a77edSAlan Wright 	if (nvlist)
3964743a77edSAlan Wright 		nvlist_free(nvlist);
3965743a77edSAlan Wright 	return (error);
3966743a77edSAlan Wright }
3967743a77edSAlan Wright 
3968743a77edSAlan Wright int
3969743a77edSAlan Wright zfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
3970743a77edSAlan Wright     char *path, char *resource)
3971743a77edSAlan Wright {
3972743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
3973743a77edSAlan Wright 	    resource, NULL));
3974743a77edSAlan Wright }
3975743a77edSAlan Wright 
3976743a77edSAlan Wright int
3977743a77edSAlan Wright zfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
3978743a77edSAlan Wright     char *path, char *resource)
3979743a77edSAlan Wright {
3980743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
3981743a77edSAlan Wright 	    resource, NULL));
3982743a77edSAlan Wright }
3983743a77edSAlan Wright 
3984743a77edSAlan Wright int
3985743a77edSAlan Wright zfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
3986743a77edSAlan Wright {
3987743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
3988743a77edSAlan Wright 	    NULL, NULL));
3989743a77edSAlan Wright }
3990743a77edSAlan Wright 
3991743a77edSAlan Wright int
3992743a77edSAlan Wright zfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
3993743a77edSAlan Wright     char *oldname, char *newname)
3994743a77edSAlan Wright {
3995743a77edSAlan Wright 	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
3996743a77edSAlan Wright 	    oldname, newname));
3997743a77edSAlan Wright }
399814843421SMatthew Ahrens 
399914843421SMatthew Ahrens int
400014843421SMatthew Ahrens zfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
400114843421SMatthew Ahrens     zfs_userspace_cb_t func, void *arg)
400214843421SMatthew Ahrens {
400314843421SMatthew Ahrens 	zfs_cmd_t zc = { 0 };
400414843421SMatthew Ahrens 	int error;
400514843421SMatthew Ahrens 	zfs_useracct_t buf[100];
400614843421SMatthew Ahrens 
400714843421SMatthew Ahrens 	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
400814843421SMatthew Ahrens 
400914843421SMatthew Ahrens 	zc.zc_objset_type = type;
401014843421SMatthew Ahrens 	zc.zc_nvlist_dst = (uintptr_t)buf;
401114843421SMatthew Ahrens 
401214843421SMatthew Ahrens 	/* CONSTCOND */
401314843421SMatthew Ahrens 	while (1) {
401414843421SMatthew Ahrens 		zfs_useracct_t *zua = buf;
401514843421SMatthew Ahrens 
401614843421SMatthew Ahrens 		zc.zc_nvlist_dst_size = sizeof (buf);
401714843421SMatthew Ahrens 		error = ioctl(zhp->zfs_hdl->libzfs_fd,
401814843421SMatthew Ahrens 		    ZFS_IOC_USERSPACE_MANY, &zc);
401914843421SMatthew Ahrens 		if (error || zc.zc_nvlist_dst_size == 0)
402014843421SMatthew Ahrens 			break;
402114843421SMatthew Ahrens 
402214843421SMatthew Ahrens 		while (zc.zc_nvlist_dst_size > 0) {
40230aea4b19SMatthew Ahrens 			error = func(arg, zua->zu_domain, zua->zu_rid,
40240aea4b19SMatthew Ahrens 			    zua->zu_space);
40250aea4b19SMatthew Ahrens 			if (error != 0)
40260aea4b19SMatthew Ahrens 				return (error);
402714843421SMatthew Ahrens 			zua++;
402814843421SMatthew Ahrens 			zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
402914843421SMatthew Ahrens 		}
403014843421SMatthew Ahrens 	}
403114843421SMatthew Ahrens 
403214843421SMatthew Ahrens 	return (error);
403314843421SMatthew Ahrens }
4034842727c2SChris Kirby 
4035842727c2SChris Kirby int
4036842727c2SChris Kirby zfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
4037842727c2SChris Kirby     boolean_t recursive)
4038842727c2SChris Kirby {
4039842727c2SChris Kirby 	zfs_cmd_t zc = { 0 };
4040842727c2SChris Kirby 	libzfs_handle_t *hdl = zhp->zfs_hdl;
4041842727c2SChris Kirby 
4042842727c2SChris Kirby 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4043842727c2SChris Kirby 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
4044842727c2SChris Kirby 	(void) strlcpy(zc.zc_string, tag, sizeof (zc.zc_string));
4045842727c2SChris Kirby 	zc.zc_cookie = recursive;
4046842727c2SChris Kirby 
4047842727c2SChris Kirby 	if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) {
4048842727c2SChris Kirby 		char errbuf[ZFS_MAXNAMELEN+32];
4049842727c2SChris Kirby 
4050842727c2SChris Kirby 		/*
4051842727c2SChris Kirby 		 * if it was recursive, the one that actually failed will be in
4052842727c2SChris Kirby 		 * zc.zc_name.
4053842727c2SChris Kirby 		 */
4054842727c2SChris Kirby 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4055842727c2SChris Kirby 		    "cannot hold '%s@%s'"), zc.zc_name, snapname);
4056842727c2SChris Kirby 		switch (errno) {
4057842727c2SChris Kirby 		case ENOTSUP:
4058842727c2SChris Kirby 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4059842727c2SChris Kirby 			    "pool must be upgraded"));
4060842727c2SChris Kirby 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
4061842727c2SChris Kirby 		case EINVAL:
4062842727c2SChris Kirby 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4063842727c2SChris Kirby 		case EEXIST:
4064842727c2SChris Kirby 			return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
4065842727c2SChris Kirby 		default:
4066842727c2SChris Kirby 			return (zfs_standard_error_fmt(hdl, errno, errbuf));
4067842727c2SChris Kirby 		}
4068842727c2SChris Kirby 	}
4069842727c2SChris Kirby 
4070842727c2SChris Kirby 	return (0);
4071842727c2SChris Kirby }
4072842727c2SChris Kirby 
4073842727c2SChris Kirby int
4074842727c2SChris Kirby zfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4075842727c2SChris Kirby     boolean_t recursive)
4076842727c2SChris Kirby {
4077842727c2SChris Kirby 	zfs_cmd_t zc = { 0 };
4078842727c2SChris Kirby 	libzfs_handle_t *hdl = zhp->zfs_hdl;
4079842727c2SChris Kirby 
4080842727c2SChris Kirby 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4081842727c2SChris Kirby 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
4082842727c2SChris Kirby 	(void) strlcpy(zc.zc_string, tag, sizeof (zc.zc_string));
4083842727c2SChris Kirby 	zc.zc_cookie = recursive;
4084842727c2SChris Kirby 
4085842727c2SChris Kirby 	if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) {
4086842727c2SChris Kirby 		char errbuf[ZFS_MAXNAMELEN+32];
4087842727c2SChris Kirby 
4088842727c2SChris Kirby 		/*
4089842727c2SChris Kirby 		 * if it was recursive, the one that actually failed will be in
4090842727c2SChris Kirby 		 * zc.zc_name.
4091842727c2SChris Kirby 		 */
4092842727c2SChris Kirby 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4093842727c2SChris Kirby 		    "cannot release '%s@%s'"), zc.zc_name, snapname);
4094842727c2SChris Kirby 		switch (errno) {
4095842727c2SChris Kirby 		case ESRCH:
4096842727c2SChris Kirby 			return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
4097842727c2SChris Kirby 		case ENOTSUP:
4098842727c2SChris Kirby 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4099842727c2SChris Kirby 			    "pool must be upgraded"));
4100842727c2SChris Kirby 			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
4101842727c2SChris Kirby 		case EINVAL:
4102842727c2SChris Kirby 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4103842727c2SChris Kirby 		default:
4104842727c2SChris Kirby 			return (zfs_standard_error_fmt(hdl, errno, errbuf));
4105842727c2SChris Kirby 		}
4106842727c2SChris Kirby 	}
4107842727c2SChris Kirby 
4108842727c2SChris Kirby 	return (0);
4109842727c2SChris Kirby }
4110