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  */
21fa9e4066Sahrens /*
22b12a1c38Slling  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23fa9e4066Sahrens  * Use is subject to license terms.
24fa9e4066Sahrens  */
25fa9e4066Sahrens 
26fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
27fa9e4066Sahrens 
28fa9e4066Sahrens #include <assert.h>
29fa9e4066Sahrens #include <ctype.h>
30fa9e4066Sahrens #include <errno.h>
31fa9e4066Sahrens #include <libdevinfo.h>
32fa9e4066Sahrens #include <libintl.h>
33fa9e4066Sahrens #include <math.h>
34fa9e4066Sahrens #include <stdio.h>
35fa9e4066Sahrens #include <stdlib.h>
36fa9e4066Sahrens #include <strings.h>
37fa9e4066Sahrens #include <unistd.h>
38fa9e4066Sahrens #include <zone.h>
3999653d4eSeschrock #include <fcntl.h>
40fa9e4066Sahrens #include <sys/mntent.h>
41fa9e4066Sahrens #include <sys/mnttab.h>
42b12a1c38Slling #include <sys/mount.h>
43fa9e4066Sahrens 
44fa9e4066Sahrens #include <sys/spa.h>
45fa9e4066Sahrens #include <sys/zio.h>
46e9dbad6fSeschrock #include <sys/zap.h>
47fa9e4066Sahrens #include <libzfs.h>
48fa9e4066Sahrens 
49fa9e4066Sahrens #include "zfs_namecheck.h"
50fa9e4066Sahrens #include "zfs_prop.h"
51fa9e4066Sahrens #include "libzfs_impl.h"
52fa9e4066Sahrens 
53fa9e4066Sahrens /*
54fa9e4066Sahrens  * Given a single type (not a mask of types), return the type in a human
55fa9e4066Sahrens  * readable form.
56fa9e4066Sahrens  */
57fa9e4066Sahrens const char *
58fa9e4066Sahrens zfs_type_to_name(zfs_type_t type)
59fa9e4066Sahrens {
60fa9e4066Sahrens 	switch (type) {
61fa9e4066Sahrens 	case ZFS_TYPE_FILESYSTEM:
62fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
63fa9e4066Sahrens 	case ZFS_TYPE_SNAPSHOT:
64fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
65fa9e4066Sahrens 	case ZFS_TYPE_VOLUME:
66fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
67fa9e4066Sahrens 	}
68fa9e4066Sahrens 
69fa9e4066Sahrens 	return (NULL);
70fa9e4066Sahrens }
71fa9e4066Sahrens 
72fa9e4066Sahrens /*
73fa9e4066Sahrens  * Given a path and mask of ZFS types, return a string describing this dataset.
74fa9e4066Sahrens  * This is used when we fail to open a dataset and we cannot get an exact type.
75fa9e4066Sahrens  * We guess what the type would have been based on the path and the mask of
76fa9e4066Sahrens  * acceptable types.
77fa9e4066Sahrens  */
78fa9e4066Sahrens static const char *
79fa9e4066Sahrens path_to_str(const char *path, int types)
80fa9e4066Sahrens {
81fa9e4066Sahrens 	/*
82fa9e4066Sahrens 	 * When given a single type, always report the exact type.
83fa9e4066Sahrens 	 */
84fa9e4066Sahrens 	if (types == ZFS_TYPE_SNAPSHOT)
85fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
86fa9e4066Sahrens 	if (types == ZFS_TYPE_FILESYSTEM)
87fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
88fa9e4066Sahrens 	if (types == ZFS_TYPE_VOLUME)
89fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
90fa9e4066Sahrens 
91fa9e4066Sahrens 	/*
92fa9e4066Sahrens 	 * The user is requesting more than one type of dataset.  If this is the
93fa9e4066Sahrens 	 * case, consult the path itself.  If we're looking for a snapshot, and
94fa9e4066Sahrens 	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
95fa9e4066Sahrens 	 * snapshot attribute and try again.
96fa9e4066Sahrens 	 */
97fa9e4066Sahrens 	if (types & ZFS_TYPE_SNAPSHOT) {
98fa9e4066Sahrens 		if (strchr(path, '@') != NULL)
99fa9e4066Sahrens 			return (dgettext(TEXT_DOMAIN, "snapshot"));
100fa9e4066Sahrens 		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
101fa9e4066Sahrens 	}
102fa9e4066Sahrens 
103fa9e4066Sahrens 
104fa9e4066Sahrens 	/*
105fa9e4066Sahrens 	 * The user has requested either filesystems or volumes.
106fa9e4066Sahrens 	 * We have no way of knowing a priori what type this would be, so always
107fa9e4066Sahrens 	 * report it as "filesystem" or "volume", our two primitive types.
108fa9e4066Sahrens 	 */
109fa9e4066Sahrens 	if (types & ZFS_TYPE_FILESYSTEM)
110fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
111fa9e4066Sahrens 
112fa9e4066Sahrens 	assert(types & ZFS_TYPE_VOLUME);
113fa9e4066Sahrens 	return (dgettext(TEXT_DOMAIN, "volume"));
114fa9e4066Sahrens }
115fa9e4066Sahrens 
116fa9e4066Sahrens /*
117fa9e4066Sahrens  * Validate a ZFS path.  This is used even before trying to open the dataset, to
118fa9e4066Sahrens  * provide a more meaningful error message.  We place a more useful message in
119fa9e4066Sahrens  * 'buf' detailing exactly why the name was not valid.
120fa9e4066Sahrens  */
121fa9e4066Sahrens static int
12299653d4eSeschrock zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type)
123fa9e4066Sahrens {
124fa9e4066Sahrens 	namecheck_err_t why;
125fa9e4066Sahrens 	char what;
126fa9e4066Sahrens 
127fa9e4066Sahrens 	if (dataset_namecheck(path, &why, &what) != 0) {
12899653d4eSeschrock 		if (hdl != NULL) {
129fa9e4066Sahrens 			switch (why) {
130b81d61a6Slling 			case NAME_ERR_TOOLONG:
13199653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
13299653d4eSeschrock 				    "name is too long"));
133b81d61a6Slling 				break;
134b81d61a6Slling 
135fa9e4066Sahrens 			case NAME_ERR_LEADING_SLASH:
13699653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
13799653d4eSeschrock 				    "leading slash in name"));
138fa9e4066Sahrens 				break;
139fa9e4066Sahrens 
140fa9e4066Sahrens 			case NAME_ERR_EMPTY_COMPONENT:
14199653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14299653d4eSeschrock 				    "empty component in name"));
143fa9e4066Sahrens 				break;
144fa9e4066Sahrens 
145fa9e4066Sahrens 			case NAME_ERR_TRAILING_SLASH:
14699653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14799653d4eSeschrock 				    "trailing slash in name"));
148fa9e4066Sahrens 				break;
149fa9e4066Sahrens 
150fa9e4066Sahrens 			case NAME_ERR_INVALCHAR:
15199653d4eSeschrock 				zfs_error_aux(hdl,
152fa9e4066Sahrens 				    dgettext(TEXT_DOMAIN, "invalid character "
15399653d4eSeschrock 				    "'%c' in name"), what);
154fa9e4066Sahrens 				break;
155fa9e4066Sahrens 
156fa9e4066Sahrens 			case NAME_ERR_MULTIPLE_AT:
15799653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15899653d4eSeschrock 				    "multiple '@' delimiters in name"));
159fa9e4066Sahrens 				break;
160*5ad82045Snd 
161*5ad82045Snd 			case NAME_ERR_NOLETTER:
162*5ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
163*5ad82045Snd 				    "pool doesn't begin with a letter"));
164*5ad82045Snd 				break;
165*5ad82045Snd 
166*5ad82045Snd 			case NAME_ERR_RESERVED:
167*5ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
168*5ad82045Snd 				    "name is reserved"));
169*5ad82045Snd 				break;
170*5ad82045Snd 
171*5ad82045Snd 			case NAME_ERR_DISKLIKE:
172*5ad82045Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
173*5ad82045Snd 				    "reserved disk name"));
174*5ad82045Snd 				break;
175fa9e4066Sahrens 			}
176fa9e4066Sahrens 		}
177fa9e4066Sahrens 
178fa9e4066Sahrens 		return (0);
179fa9e4066Sahrens 	}
180fa9e4066Sahrens 
181fa9e4066Sahrens 	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
18299653d4eSeschrock 		if (hdl != NULL)
18399653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
18499653d4eSeschrock 			    "snapshot delimiter '@' in filesystem name"));
185fa9e4066Sahrens 		return (0);
186fa9e4066Sahrens 	}
187fa9e4066Sahrens 
1881d452cf5Sahrens 	if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
1891d452cf5Sahrens 		if (hdl != NULL)
1901d452cf5Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1911d452cf5Sahrens 			    "missing '@' delimeter in snapshot name"));
1921d452cf5Sahrens 		return (0);
1931d452cf5Sahrens 	}
1941d452cf5Sahrens 
19599653d4eSeschrock 	return (-1);
196fa9e4066Sahrens }
197fa9e4066Sahrens 
198fa9e4066Sahrens int
199fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type)
200fa9e4066Sahrens {
20199653d4eSeschrock 	return (zfs_validate_name(NULL, name, type));
202fa9e4066Sahrens }
203fa9e4066Sahrens 
204e9dbad6fSeschrock /*
205e9dbad6fSeschrock  * This function takes the raw DSL properties, and filters out the user-defined
206e9dbad6fSeschrock  * properties into a separate nvlist.
207e9dbad6fSeschrock  */
208e9dbad6fSeschrock static int
209e9dbad6fSeschrock process_user_props(zfs_handle_t *zhp)
210e9dbad6fSeschrock {
211e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
212e9dbad6fSeschrock 	nvpair_t *elem;
213e9dbad6fSeschrock 	nvlist_t *propval;
214e9dbad6fSeschrock 
215e9dbad6fSeschrock 	nvlist_free(zhp->zfs_user_props);
216e9dbad6fSeschrock 
217e9dbad6fSeschrock 	if (nvlist_alloc(&zhp->zfs_user_props, NV_UNIQUE_NAME, 0) != 0)
218e9dbad6fSeschrock 		return (no_memory(hdl));
219e9dbad6fSeschrock 
220e9dbad6fSeschrock 	elem = NULL;
221e9dbad6fSeschrock 	while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
222e9dbad6fSeschrock 		if (!zfs_prop_user(nvpair_name(elem)))
223e9dbad6fSeschrock 			continue;
224e9dbad6fSeschrock 
225e9dbad6fSeschrock 		verify(nvpair_value_nvlist(elem, &propval) == 0);
226e9dbad6fSeschrock 		if (nvlist_add_nvlist(zhp->zfs_user_props,
227e9dbad6fSeschrock 		    nvpair_name(elem), propval) != 0)
228e9dbad6fSeschrock 			return (no_memory(hdl));
229e9dbad6fSeschrock 	}
230e9dbad6fSeschrock 
231e9dbad6fSeschrock 	return (0);
232e9dbad6fSeschrock }
233e9dbad6fSeschrock 
234fa9e4066Sahrens /*
235fa9e4066Sahrens  * Utility function to gather stats (objset and zpl) for the given object.
236fa9e4066Sahrens  */
237fa9e4066Sahrens static int
238fa9e4066Sahrens get_stats(zfs_handle_t *zhp)
239fa9e4066Sahrens {
240fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
241e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
242fa9e4066Sahrens 
243fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
244fa9e4066Sahrens 
245e9dbad6fSeschrock 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
24699653d4eSeschrock 		return (-1);
2477f7322feSeschrock 
24899653d4eSeschrock 	while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
2497f7322feSeschrock 		if (errno == ENOMEM) {
250e9dbad6fSeschrock 			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
251e9dbad6fSeschrock 				zcmd_free_nvlists(&zc);
25299653d4eSeschrock 				return (-1);
253e9dbad6fSeschrock 			}
2547f7322feSeschrock 		} else {
255e9dbad6fSeschrock 			zcmd_free_nvlists(&zc);
2567f7322feSeschrock 			return (-1);
2577f7322feSeschrock 		}
2587f7322feSeschrock 	}
259fa9e4066Sahrens 
260fa9e4066Sahrens 	bcopy(&zc.zc_objset_stats, &zhp->zfs_dmustats,
261fa9e4066Sahrens 	    sizeof (zc.zc_objset_stats));
262fa9e4066Sahrens 
263e9dbad6fSeschrock 	(void) strlcpy(zhp->zfs_root, zc.zc_value, sizeof (zhp->zfs_root));
264ea8dc4b6Seschrock 
26599653d4eSeschrock 	if (zhp->zfs_props) {
26699653d4eSeschrock 		nvlist_free(zhp->zfs_props);
26799653d4eSeschrock 		zhp->zfs_props = NULL;
26899653d4eSeschrock 	}
26999653d4eSeschrock 
270e9dbad6fSeschrock 	if (zcmd_read_dst_nvlist(hdl, &zc, &zhp->zfs_props) != 0) {
271e9dbad6fSeschrock 		zcmd_free_nvlists(&zc);
27299653d4eSeschrock 		return (-1);
27399653d4eSeschrock 	}
274fa9e4066Sahrens 
275e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
276fa9e4066Sahrens 
277e9dbad6fSeschrock 	zhp->zfs_volstats = zc.zc_vol_stats;
278e9dbad6fSeschrock 
279e9dbad6fSeschrock 	if (process_user_props(zhp) != 0)
280e9dbad6fSeschrock 		return (-1);
28199653d4eSeschrock 
282fa9e4066Sahrens 	return (0);
283fa9e4066Sahrens }
284fa9e4066Sahrens 
285fa9e4066Sahrens /*
286fa9e4066Sahrens  * Refresh the properties currently stored in the handle.
287fa9e4066Sahrens  */
288fa9e4066Sahrens void
289fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp)
290fa9e4066Sahrens {
291fa9e4066Sahrens 	(void) get_stats(zhp);
292fa9e4066Sahrens }
293fa9e4066Sahrens 
294fa9e4066Sahrens /*
295fa9e4066Sahrens  * Makes a handle from the given dataset name.  Used by zfs_open() and
296fa9e4066Sahrens  * zfs_iter_* to create child handles on the fly.
297fa9e4066Sahrens  */
298fa9e4066Sahrens zfs_handle_t *
29999653d4eSeschrock make_dataset_handle(libzfs_handle_t *hdl, const char *path)
300fa9e4066Sahrens {
30199653d4eSeschrock 	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
30299653d4eSeschrock 
30399653d4eSeschrock 	if (zhp == NULL)
30499653d4eSeschrock 		return (NULL);
30599653d4eSeschrock 
30699653d4eSeschrock 	zhp->zfs_hdl = hdl;
307fa9e4066Sahrens 
30831fd60d3Sahrens top:
309fa9e4066Sahrens 	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
310fa9e4066Sahrens 
311fa9e4066Sahrens 	if (get_stats(zhp) != 0) {
312fa9e4066Sahrens 		free(zhp);
313fa9e4066Sahrens 		return (NULL);
314fa9e4066Sahrens 	}
315fa9e4066Sahrens 
31631fd60d3Sahrens 	if (zhp->zfs_dmustats.dds_inconsistent) {
31731fd60d3Sahrens 		zfs_cmd_t zc = { 0 };
31831fd60d3Sahrens 
31931fd60d3Sahrens 		/*
32031fd60d3Sahrens 		 * If it is dds_inconsistent, then we've caught it in
32131fd60d3Sahrens 		 * the middle of a 'zfs receive' or 'zfs destroy', and
32231fd60d3Sahrens 		 * it is inconsistent from the ZPL's point of view, so
32331fd60d3Sahrens 		 * can't be mounted.  However, it could also be that we
32431fd60d3Sahrens 		 * have crashed in the middle of one of those
32531fd60d3Sahrens 		 * operations, in which case we need to get rid of the
32631fd60d3Sahrens 		 * inconsistent state.  We do that by either rolling
32731fd60d3Sahrens 		 * back to the previous snapshot (which will fail if
32831fd60d3Sahrens 		 * there is none), or destroying the filesystem.  Note
32931fd60d3Sahrens 		 * that if we are still in the middle of an active
33031fd60d3Sahrens 		 * 'receive' or 'destroy', then the rollback and destroy
33131fd60d3Sahrens 		 * will fail with EBUSY and we will drive on as usual.
33231fd60d3Sahrens 		 */
33331fd60d3Sahrens 
33431fd60d3Sahrens 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
33531fd60d3Sahrens 
33631fd60d3Sahrens 		if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
33799653d4eSeschrock 			(void) zvol_remove_link(hdl, zhp->zfs_name);
33831fd60d3Sahrens 			zc.zc_objset_type = DMU_OST_ZVOL;
33931fd60d3Sahrens 		} else {
34031fd60d3Sahrens 			zc.zc_objset_type = DMU_OST_ZFS;
34131fd60d3Sahrens 		}
34231fd60d3Sahrens 
34331fd60d3Sahrens 		/* If we can successfully roll it back, reget the stats */
34499653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0)
34531fd60d3Sahrens 			goto top;
34631fd60d3Sahrens 		/*
34731fd60d3Sahrens 		 * If we can sucessfully destroy it, pretend that it
34831fd60d3Sahrens 		 * never existed.
34931fd60d3Sahrens 		 */
35099653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) {
35131fd60d3Sahrens 			free(zhp);
35231fd60d3Sahrens 			errno = ENOENT;
35331fd60d3Sahrens 			return (NULL);
35431fd60d3Sahrens 		}
35531fd60d3Sahrens 	}
35631fd60d3Sahrens 
357fa9e4066Sahrens 	/*
358fa9e4066Sahrens 	 * We've managed to open the dataset and gather statistics.  Determine
359fa9e4066Sahrens 	 * the high-level type.
360fa9e4066Sahrens 	 */
361fa9e4066Sahrens 	if (zhp->zfs_dmustats.dds_is_snapshot)
362fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
363fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
364fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_VOLUME;
365fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
366fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
367fa9e4066Sahrens 	else
36899653d4eSeschrock 		abort();	/* we should never see any other types */
369fa9e4066Sahrens 
370fa9e4066Sahrens 	return (zhp);
371fa9e4066Sahrens }
372fa9e4066Sahrens 
373fa9e4066Sahrens /*
374fa9e4066Sahrens  * Opens the given snapshot, filesystem, or volume.   The 'types'
375fa9e4066Sahrens  * argument is a mask of acceptable types.  The function will print an
376fa9e4066Sahrens  * appropriate error message and return NULL if it can't be opened.
377fa9e4066Sahrens  */
378fa9e4066Sahrens zfs_handle_t *
37999653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types)
380fa9e4066Sahrens {
381fa9e4066Sahrens 	zfs_handle_t *zhp;
38299653d4eSeschrock 	char errbuf[1024];
38399653d4eSeschrock 
38499653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
38599653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
386fa9e4066Sahrens 
387fa9e4066Sahrens 	/*
38899653d4eSeschrock 	 * Validate the name before we even try to open it.
389fa9e4066Sahrens 	 */
39099653d4eSeschrock 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_ANY)) {
39199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
39299653d4eSeschrock 		    "invalid dataset name"));
39399653d4eSeschrock 		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
394fa9e4066Sahrens 		return (NULL);
395fa9e4066Sahrens 	}
396fa9e4066Sahrens 
397fa9e4066Sahrens 	/*
398fa9e4066Sahrens 	 * Try to get stats for the dataset, which will tell us if it exists.
399fa9e4066Sahrens 	 */
400fa9e4066Sahrens 	errno = 0;
40199653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
40299653d4eSeschrock 		(void) zfs_standard_error(hdl, errno, errbuf, path);
403fa9e4066Sahrens 		return (NULL);
404fa9e4066Sahrens 	}
405fa9e4066Sahrens 
406fa9e4066Sahrens 	if (!(types & zhp->zfs_type)) {
40799653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
40894de1d4cSeschrock 		zfs_close(zhp);
409fa9e4066Sahrens 		return (NULL);
410fa9e4066Sahrens 	}
411fa9e4066Sahrens 
412fa9e4066Sahrens 	return (zhp);
413fa9e4066Sahrens }
414fa9e4066Sahrens 
415fa9e4066Sahrens /*
416fa9e4066Sahrens  * Release a ZFS handle.  Nothing to do but free the associated memory.
417fa9e4066Sahrens  */
418fa9e4066Sahrens void
419fa9e4066Sahrens zfs_close(zfs_handle_t *zhp)
420fa9e4066Sahrens {
421fa9e4066Sahrens 	if (zhp->zfs_mntopts)
422fa9e4066Sahrens 		free(zhp->zfs_mntopts);
423e9dbad6fSeschrock 	nvlist_free(zhp->zfs_props);
424e9dbad6fSeschrock 	nvlist_free(zhp->zfs_user_props);
425fa9e4066Sahrens 	free(zhp);
426fa9e4066Sahrens }
427fa9e4066Sahrens 
428fa9e4066Sahrens /*
429fa9e4066Sahrens  * Given a numeric suffix, convert the value into a number of bits that the
430fa9e4066Sahrens  * resulting value must be shifted.
431fa9e4066Sahrens  */
432fa9e4066Sahrens static int
43399653d4eSeschrock str2shift(libzfs_handle_t *hdl, const char *buf)
434fa9e4066Sahrens {
435fa9e4066Sahrens 	const char *ends = "BKMGTPEZ";
436fa9e4066Sahrens 	int i;
437fa9e4066Sahrens 
438fa9e4066Sahrens 	if (buf[0] == '\0')
439fa9e4066Sahrens 		return (0);
440fa9e4066Sahrens 	for (i = 0; i < strlen(ends); i++) {
441fa9e4066Sahrens 		if (toupper(buf[0]) == ends[i])
442fa9e4066Sahrens 			break;
443fa9e4066Sahrens 	}
444fa9e4066Sahrens 	if (i == strlen(ends)) {
44599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
44699653d4eSeschrock 		    "invalid numeric suffix '%s'"), buf);
447fa9e4066Sahrens 		return (-1);
448fa9e4066Sahrens 	}
449fa9e4066Sahrens 
450fa9e4066Sahrens 	/*
451fa9e4066Sahrens 	 * We want to allow trailing 'b' characters for 'GB' or 'Mb'.  But don't
452fa9e4066Sahrens 	 * allow 'BB' - that's just weird.
453fa9e4066Sahrens 	 */
454fa9e4066Sahrens 	if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
45599653d4eSeschrock 	    toupper(buf[0]) != 'B'))
456fa9e4066Sahrens 		return (10*i);
457fa9e4066Sahrens 
45899653d4eSeschrock 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
45999653d4eSeschrock 	    "invalid numeric suffix '%s'"), buf);
460fa9e4066Sahrens 	return (-1);
461fa9e4066Sahrens }
462fa9e4066Sahrens 
463fa9e4066Sahrens /*
464fa9e4066Sahrens  * Convert a string of the form '100G' into a real number.  Used when setting
465fa9e4066Sahrens  * properties or creating a volume.  'buf' is used to place an extended error
466fa9e4066Sahrens  * message for the caller to use.
467fa9e4066Sahrens  */
468fa9e4066Sahrens static int
46999653d4eSeschrock nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
470fa9e4066Sahrens {
471fa9e4066Sahrens 	char *end;
472fa9e4066Sahrens 	int shift;
473fa9e4066Sahrens 
474fa9e4066Sahrens 	*num = 0;
475fa9e4066Sahrens 
476fa9e4066Sahrens 	/* Check to see if this looks like a number.  */
477fa9e4066Sahrens 	if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
47899653d4eSeschrock 		if (hdl)
47999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
48099653d4eSeschrock 			    "bad numeric value '%s'"), value);
481fa9e4066Sahrens 		return (-1);
482fa9e4066Sahrens 	}
483fa9e4066Sahrens 
484fa9e4066Sahrens 	/* Rely on stroll() to process the numeric portion.  */
485fa9e4066Sahrens 	errno = 0;
486fa9e4066Sahrens 	*num = strtoll(value, &end, 10);
487fa9e4066Sahrens 
488fa9e4066Sahrens 	/*
489fa9e4066Sahrens 	 * Check for ERANGE, which indicates that the value is too large to fit
490fa9e4066Sahrens 	 * in a 64-bit value.
491fa9e4066Sahrens 	 */
492fa9e4066Sahrens 	if (errno == ERANGE) {
49399653d4eSeschrock 		if (hdl)
49499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
49599653d4eSeschrock 			    "numeric value is too large"));
496fa9e4066Sahrens 		return (-1);
497fa9e4066Sahrens 	}
498fa9e4066Sahrens 
499fa9e4066Sahrens 	/*
500fa9e4066Sahrens 	 * If we have a decimal value, then do the computation with floating
501fa9e4066Sahrens 	 * point arithmetic.  Otherwise, use standard arithmetic.
502fa9e4066Sahrens 	 */
503fa9e4066Sahrens 	if (*end == '.') {
504fa9e4066Sahrens 		double fval = strtod(value, &end);
505fa9e4066Sahrens 
50699653d4eSeschrock 		if ((shift = str2shift(hdl, end)) == -1)
507fa9e4066Sahrens 			return (-1);
508fa9e4066Sahrens 
509fa9e4066Sahrens 		fval *= pow(2, shift);
510fa9e4066Sahrens 
511fa9e4066Sahrens 		if (fval > UINT64_MAX) {
51299653d4eSeschrock 			if (hdl)
51399653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
51499653d4eSeschrock 				    "numeric value is too large"));
515fa9e4066Sahrens 			return (-1);
516fa9e4066Sahrens 		}
517fa9e4066Sahrens 
518fa9e4066Sahrens 		*num = (uint64_t)fval;
519fa9e4066Sahrens 	} else {
52099653d4eSeschrock 		if ((shift = str2shift(hdl, end)) == -1)
521fa9e4066Sahrens 			return (-1);
522fa9e4066Sahrens 
523fa9e4066Sahrens 		/* Check for overflow */
524fa9e4066Sahrens 		if (shift >= 64 || (*num << shift) >> shift != *num) {
52599653d4eSeschrock 			if (hdl)
52699653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
52799653d4eSeschrock 				    "numeric value is too large"));
528fa9e4066Sahrens 			return (-1);
529fa9e4066Sahrens 		}
530fa9e4066Sahrens 
531fa9e4066Sahrens 		*num <<= shift;
532fa9e4066Sahrens 	}
533fa9e4066Sahrens 
534fa9e4066Sahrens 	return (0);
535fa9e4066Sahrens }
536fa9e4066Sahrens 
537fa9e4066Sahrens int
538e9dbad6fSeschrock zfs_nicestrtonum(libzfs_handle_t *hdl, const char *str, uint64_t *val)
539fa9e4066Sahrens {
540e9dbad6fSeschrock 	return (nicestrtonum(hdl, str, val));
541fa9e4066Sahrens }
542fa9e4066Sahrens 
543fa9e4066Sahrens /*
544e9dbad6fSeschrock  * The prop_parse_*() functions are designed to allow flexibility in callers
545e9dbad6fSeschrock  * when setting properties.  At the DSL layer, all properties are either 64-bit
546e9dbad6fSeschrock  * numbers or strings.  We want the user to be able to ignore this fact and
547e9dbad6fSeschrock  * specify properties as native values (boolean, for example) or as strings (to
548e9dbad6fSeschrock  * simplify command line utilities).  This also handles converting index types
549e9dbad6fSeschrock  * (compression, checksum, etc) from strings to their on-disk index.
550fa9e4066Sahrens  */
551fa9e4066Sahrens 
552e9dbad6fSeschrock static int
553e9dbad6fSeschrock prop_parse_boolean(libzfs_handle_t *hdl, nvpair_t *elem, uint64_t *val)
554e9dbad6fSeschrock {
555e9dbad6fSeschrock 	uint64_t ret;
55699653d4eSeschrock 
557e9dbad6fSeschrock 	switch (nvpair_type(elem)) {
558e9dbad6fSeschrock 	case DATA_TYPE_STRING:
559e9dbad6fSeschrock 		{
560e9dbad6fSeschrock 			char *value;
561e9dbad6fSeschrock 			VERIFY(nvpair_value_string(elem, &value) == 0);
562fa9e4066Sahrens 
563e9dbad6fSeschrock 			if (strcmp(value, "on") == 0) {
564e9dbad6fSeschrock 				ret = 1;
565e9dbad6fSeschrock 			} else if (strcmp(value, "off") == 0) {
566e9dbad6fSeschrock 				ret = 0;
567e9dbad6fSeschrock 			} else {
568e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
569e9dbad6fSeschrock 				    "property '%s' must be 'on' or 'off'"),
570e9dbad6fSeschrock 				    nvpair_name(elem));
571e9dbad6fSeschrock 				return (-1);
572e9dbad6fSeschrock 			}
573e9dbad6fSeschrock 			break;
574e9dbad6fSeschrock 		}
575fa9e4066Sahrens 
576e9dbad6fSeschrock 	case DATA_TYPE_UINT64:
577e9dbad6fSeschrock 		{
578e9dbad6fSeschrock 			VERIFY(nvpair_value_uint64(elem, &ret) == 0);
579e9dbad6fSeschrock 			if (ret > 1) {
580e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
581e9dbad6fSeschrock 				    "'%s' must be a boolean value"),
582e9dbad6fSeschrock 				    nvpair_name(elem));
583e9dbad6fSeschrock 				return (-1);
584e9dbad6fSeschrock 			}
585e9dbad6fSeschrock 			break;
586fa9e4066Sahrens 		}
587fa9e4066Sahrens 
588e9dbad6fSeschrock 	case DATA_TYPE_BOOLEAN_VALUE:
589e9dbad6fSeschrock 		{
590e9dbad6fSeschrock 			boolean_t value;
591e9dbad6fSeschrock 			VERIFY(nvpair_value_boolean_value(elem, &value) == 0);
592e9dbad6fSeschrock 			ret = value;
593fa9e4066Sahrens 			break;
594fa9e4066Sahrens 		}
595fa9e4066Sahrens 
596e9dbad6fSeschrock 	default:
597e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
598e9dbad6fSeschrock 		    "'%s' must be a boolean value"),
599e9dbad6fSeschrock 		    nvpair_name(elem));
600e9dbad6fSeschrock 		return (-1);
601e9dbad6fSeschrock 	}
602fa9e4066Sahrens 
603e9dbad6fSeschrock 	*val = ret;
604e9dbad6fSeschrock 	return (0);
605e9dbad6fSeschrock }
606fa9e4066Sahrens 
607e9dbad6fSeschrock static int
608e9dbad6fSeschrock prop_parse_number(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
609e9dbad6fSeschrock     uint64_t *val)
610e9dbad6fSeschrock {
611e9dbad6fSeschrock 	uint64_t ret;
612e9dbad6fSeschrock 	boolean_t isnone = B_FALSE;
613e9dbad6fSeschrock 
614e9dbad6fSeschrock 	switch (nvpair_type(elem)) {
615e9dbad6fSeschrock 	case DATA_TYPE_STRING:
616e9dbad6fSeschrock 		{
617e9dbad6fSeschrock 			char *value;
618e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &value);
619e9dbad6fSeschrock 			if (strcmp(value, "none") == 0) {
620e9dbad6fSeschrock 				isnone = B_TRUE;
621e9dbad6fSeschrock 				ret = 0;
622e9dbad6fSeschrock 			} else if (nicestrtonum(hdl, value, &ret) != 0) {
623e9dbad6fSeschrock 				return (-1);
624fa9e4066Sahrens 			}
625e9dbad6fSeschrock 			break;
626fa9e4066Sahrens 		}
627fa9e4066Sahrens 
628e9dbad6fSeschrock 	case DATA_TYPE_UINT64:
629e9dbad6fSeschrock 		(void) nvpair_value_uint64(elem, &ret);
630fa9e4066Sahrens 		break;
631fa9e4066Sahrens 
632e9dbad6fSeschrock 	default:
633e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
634e9dbad6fSeschrock 		    "'%s' must be a number"),
635e9dbad6fSeschrock 		    nvpair_name(elem));
636e9dbad6fSeschrock 		return (-1);
637e9dbad6fSeschrock 	}
638e9dbad6fSeschrock 
639e9dbad6fSeschrock 	/*
640e9dbad6fSeschrock 	 * Quota special: force 'none' and don't allow 0.
641e9dbad6fSeschrock 	 */
642e9dbad6fSeschrock 	if (ret == 0 && !isnone && prop == ZFS_PROP_QUOTA) {
643e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
644e9dbad6fSeschrock 		    "use 'none' to disable quota"));
645e9dbad6fSeschrock 		return (-1);
646e9dbad6fSeschrock 	}
647e9dbad6fSeschrock 
648e9dbad6fSeschrock 	*val = ret;
649e9dbad6fSeschrock 	return (0);
650e9dbad6fSeschrock }
651e9dbad6fSeschrock 
652e9dbad6fSeschrock static int
653e9dbad6fSeschrock prop_parse_index(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
654e9dbad6fSeschrock     uint64_t *val)
655e9dbad6fSeschrock {
656e9dbad6fSeschrock 	char *propname = nvpair_name(elem);
657e9dbad6fSeschrock 	char *value;
658e9dbad6fSeschrock 
659e9dbad6fSeschrock 	if (nvpair_type(elem) != DATA_TYPE_STRING) {
660e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
661e9dbad6fSeschrock 		    "'%s' must be a string"), propname);
662e9dbad6fSeschrock 		return (-1);
663e9dbad6fSeschrock 	}
664e9dbad6fSeschrock 
665e9dbad6fSeschrock 	(void) nvpair_value_string(elem, &value);
666e9dbad6fSeschrock 
667e9dbad6fSeschrock 	if (zfs_prop_string_to_index(prop, value, val) != 0) {
668e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
669e9dbad6fSeschrock 		    "'%s' must be one of '%s'"), propname,
670e9dbad6fSeschrock 		    zfs_prop_values(prop));
671e9dbad6fSeschrock 		return (-1);
672e9dbad6fSeschrock 	}
673e9dbad6fSeschrock 
674e9dbad6fSeschrock 	return (0);
675e9dbad6fSeschrock }
676e9dbad6fSeschrock 
677e9dbad6fSeschrock /*
678e9dbad6fSeschrock  * Given an nvlist of properties to set, validates that they are correct, and
679e9dbad6fSeschrock  * parses any numeric properties (index, boolean, etc) if they are specified as
680e9dbad6fSeschrock  * strings.
681e9dbad6fSeschrock  */
682e9dbad6fSeschrock static nvlist_t *
683e9dbad6fSeschrock zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
684e9dbad6fSeschrock     uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
685e9dbad6fSeschrock {
686e9dbad6fSeschrock 	nvpair_t *elem;
687e9dbad6fSeschrock 	const char *propname;
688e9dbad6fSeschrock 	zfs_prop_t prop;
689e9dbad6fSeschrock 	uint64_t intval;
690e9dbad6fSeschrock 	char *strval;
691e9dbad6fSeschrock 	nvlist_t *ret;
692e9dbad6fSeschrock 
693e9dbad6fSeschrock 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
694e9dbad6fSeschrock 		(void) no_memory(hdl);
695e9dbad6fSeschrock 		return (NULL);
696e9dbad6fSeschrock 	}
697e9dbad6fSeschrock 
698e9dbad6fSeschrock 	if (type == ZFS_TYPE_SNAPSHOT) {
699e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
700e9dbad6fSeschrock 		    "snaphot properties cannot be modified"));
701e9dbad6fSeschrock 		(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
702e9dbad6fSeschrock 		goto error;
703e9dbad6fSeschrock 	}
704e9dbad6fSeschrock 
705e9dbad6fSeschrock 	elem = NULL;
706e9dbad6fSeschrock 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
707e9dbad6fSeschrock 		propname = nvpair_name(elem);
708e9dbad6fSeschrock 
709fa9e4066Sahrens 		/*
710e9dbad6fSeschrock 		 * Make sure this property is valid and applies to this type.
711fa9e4066Sahrens 		 */
712e9dbad6fSeschrock 		if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
713e9dbad6fSeschrock 			if (!zfs_prop_user(propname)) {
71499653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
715e9dbad6fSeschrock 				    "invalid property '%s'"),
716e9dbad6fSeschrock 				    propname);
717e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
718e9dbad6fSeschrock 				goto error;
719e9dbad6fSeschrock 			} else {
720e9dbad6fSeschrock 				/*
721e9dbad6fSeschrock 				 * If this is a user property, make sure it's a
722e9dbad6fSeschrock 				 * string, and that it's less than
723e9dbad6fSeschrock 				 * ZAP_MAXNAMELEN.
724e9dbad6fSeschrock 				 */
725e9dbad6fSeschrock 				if (nvpair_type(elem) != DATA_TYPE_STRING) {
726e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
727e9dbad6fSeschrock 					    "'%s' must be a string"),
728e9dbad6fSeschrock 					    propname);
729e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
730e9dbad6fSeschrock 					    errbuf);
731e9dbad6fSeschrock 					goto error;
732e9dbad6fSeschrock 				}
733fa9e4066Sahrens 
734e9dbad6fSeschrock 				if (strlen(nvpair_name(elem)) >=
735e9dbad6fSeschrock 				    ZAP_MAXNAMELEN) {
736e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
737e9dbad6fSeschrock 					    "property name '%s' is too long"),
738e9dbad6fSeschrock 					    propname);
739e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
740e9dbad6fSeschrock 					    errbuf);
741e9dbad6fSeschrock 					goto error;
742fa9e4066Sahrens 				}
743fa9e4066Sahrens 			}
744fa9e4066Sahrens 
745e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &strval);
746e9dbad6fSeschrock 			if (nvlist_add_string(ret, propname, strval) != 0) {
747e9dbad6fSeschrock 				(void) no_memory(hdl);
748e9dbad6fSeschrock 				goto error;
749e9dbad6fSeschrock 			}
750e9dbad6fSeschrock 			continue;
751e9dbad6fSeschrock 		}
752e9dbad6fSeschrock 
753e9dbad6fSeschrock 		/*
754e9dbad6fSeschrock 		 * Normalize the name, to get rid of shorthand abbrevations.
755e9dbad6fSeschrock 		 */
756e9dbad6fSeschrock 		propname = zfs_prop_to_name(prop);
757e9dbad6fSeschrock 
758e9dbad6fSeschrock 		if (!zfs_prop_valid_for_type(prop, type)) {
759e9dbad6fSeschrock 			zfs_error_aux(hdl,
760e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' does not "
761e9dbad6fSeschrock 			    "apply to datasets of this type"), propname);
762e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
763e9dbad6fSeschrock 			goto error;
764e9dbad6fSeschrock 		}
765e9dbad6fSeschrock 
766e9dbad6fSeschrock 		if (zfs_prop_readonly(prop) &&
767e9dbad6fSeschrock 		    (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) {
768e9dbad6fSeschrock 			zfs_error_aux(hdl,
769e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
770e9dbad6fSeschrock 			    propname);
771e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
772e9dbad6fSeschrock 			goto error;
773e9dbad6fSeschrock 		}
774e9dbad6fSeschrock 
775e9dbad6fSeschrock 		/*
776e9dbad6fSeschrock 		 * Convert any properties to the internal DSL value types.
777e9dbad6fSeschrock 		 */
778e9dbad6fSeschrock 		strval = NULL;
779e9dbad6fSeschrock 		switch (zfs_prop_get_type(prop)) {
780e9dbad6fSeschrock 		case prop_type_boolean:
781e9dbad6fSeschrock 			if (prop_parse_boolean(hdl, elem, &intval) != 0) {
782e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
783e9dbad6fSeschrock 				goto error;
784e9dbad6fSeschrock 			}
785e9dbad6fSeschrock 			break;
786e9dbad6fSeschrock 
787e9dbad6fSeschrock 		case prop_type_string:
788e9dbad6fSeschrock 			if (nvpair_type(elem) != DATA_TYPE_STRING) {
78999653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
790e9dbad6fSeschrock 				    "'%s' must be a string"),
791e9dbad6fSeschrock 				    propname);
792e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
793e9dbad6fSeschrock 				goto error;
794e9dbad6fSeschrock 			}
795e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &strval);
796e9dbad6fSeschrock 			if (strlen(strval) >= ZFS_MAXPROPLEN) {
797e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
798e9dbad6fSeschrock 				    "'%s' is too long"), propname);
799e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
800e9dbad6fSeschrock 				goto error;
801fa9e4066Sahrens 			}
802fa9e4066Sahrens 			break;
803fa9e4066Sahrens 
804e9dbad6fSeschrock 		case prop_type_number:
805e9dbad6fSeschrock 			if (prop_parse_number(hdl, elem, prop, &intval) != 0) {
806e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
807e9dbad6fSeschrock 				goto error;
808fa9e4066Sahrens 			}
809e9dbad6fSeschrock 			break;
810fa9e4066Sahrens 
811e9dbad6fSeschrock 		case prop_type_index:
812e9dbad6fSeschrock 			if (prop_parse_index(hdl, elem, prop, &intval) != 0) {
813e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
814e9dbad6fSeschrock 				goto error;
815fa9e4066Sahrens 			}
816fa9e4066Sahrens 			break;
817fa9e4066Sahrens 
818e9dbad6fSeschrock 		default:
819e9dbad6fSeschrock 			abort();
820e9dbad6fSeschrock 		}
821e9dbad6fSeschrock 
822e9dbad6fSeschrock 		/*
823e9dbad6fSeschrock 		 * Add the result to our return set of properties.
824e9dbad6fSeschrock 		 */
825e9dbad6fSeschrock 		if (strval) {
826e9dbad6fSeschrock 			if (nvlist_add_string(ret, propname, strval) != 0) {
827e9dbad6fSeschrock 				(void) no_memory(hdl);
828e9dbad6fSeschrock 				goto error;
829fa9e4066Sahrens 			}
830e9dbad6fSeschrock 		} else if (nvlist_add_uint64(ret, propname, intval) != 0) {
831e9dbad6fSeschrock 			(void) no_memory(hdl);
832e9dbad6fSeschrock 			goto error;
833e9dbad6fSeschrock 		}
834fa9e4066Sahrens 
835e9dbad6fSeschrock 		/*
836e9dbad6fSeschrock 		 * Perform some additional checks for specific properties.
837e9dbad6fSeschrock 		 */
838e9dbad6fSeschrock 		switch (prop) {
839e9dbad6fSeschrock 		case ZFS_PROP_RECORDSIZE:
840e9dbad6fSeschrock 		case ZFS_PROP_VOLBLOCKSIZE:
841e9dbad6fSeschrock 			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
842e9dbad6fSeschrock 			if (intval < SPA_MINBLOCKSIZE ||
843e9dbad6fSeschrock 			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
84499653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
845e9dbad6fSeschrock 				    "'%s' must be power of 2 from %u "
846e9dbad6fSeschrock 				    "to %uk"), propname,
847e9dbad6fSeschrock 				    (uint_t)SPA_MINBLOCKSIZE,
848e9dbad6fSeschrock 				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
849e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
850e9dbad6fSeschrock 				goto error;
851fa9e4066Sahrens 			}
852fa9e4066Sahrens 			break;
853fa9e4066Sahrens 
854e9dbad6fSeschrock 		case ZFS_PROP_MOUNTPOINT:
855e9dbad6fSeschrock 			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
856e9dbad6fSeschrock 			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
857e9dbad6fSeschrock 				break;
858fa9e4066Sahrens 
859e9dbad6fSeschrock 			if (strval[0] != '/') {
86099653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
861e9dbad6fSeschrock 				    "'%s' must be an absolute path, "
862e9dbad6fSeschrock 				    "'none', or 'legacy'"), propname);
863e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
864e9dbad6fSeschrock 				goto error;
865fa9e4066Sahrens 			}
866fa9e4066Sahrens 			break;
867e9dbad6fSeschrock 		}
868fa9e4066Sahrens 
869e9dbad6fSeschrock 		/*
870e9dbad6fSeschrock 		 * For the mountpoint and sharenfs properties, check if it can
871e9dbad6fSeschrock 		 * be set in a global/non-global zone based on the zoned
872e9dbad6fSeschrock 		 * property value:
873e9dbad6fSeschrock 		 *
874e9dbad6fSeschrock 		 *		global zone	    non-global zone
875e9dbad6fSeschrock 		 * -----------------------------------------------------
876e9dbad6fSeschrock 		 * zoned=on	mountpoint (no)	    mountpoint (yes)
877e9dbad6fSeschrock 		 *		sharenfs (no)	    sharenfs (no)
878e9dbad6fSeschrock 		 *
879e9dbad6fSeschrock 		 * zoned=off	mountpoint (yes)	N/A
880e9dbad6fSeschrock 		 *		sharenfs (yes)
881e9dbad6fSeschrock 		 */
882e9dbad6fSeschrock 		if (prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
883e9dbad6fSeschrock 			if (zoned) {
884e9dbad6fSeschrock 				if (getzoneid() == GLOBAL_ZONEID) {
885e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
886e9dbad6fSeschrock 					    "'%s' cannot be set on "
887e9dbad6fSeschrock 					    "dataset in a non-global zone"),
888e9dbad6fSeschrock 					    propname);
889e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
890e9dbad6fSeschrock 					    errbuf);
891e9dbad6fSeschrock 					goto error;
892e9dbad6fSeschrock 				} else if (prop == ZFS_PROP_SHARENFS) {
893e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
894e9dbad6fSeschrock 					    "'%s' cannot be set in "
895e9dbad6fSeschrock 					    "a non-global zone"), propname);
896e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
897e9dbad6fSeschrock 					    errbuf);
898e9dbad6fSeschrock 					goto error;
899fa9e4066Sahrens 				}
900e9dbad6fSeschrock 			} else if (getzoneid() != GLOBAL_ZONEID) {
901e9dbad6fSeschrock 				/*
902e9dbad6fSeschrock 				 * If zoned property is 'off', this must be in
903e9dbad6fSeschrock 				 * a globle zone. If not, something is wrong.
904e9dbad6fSeschrock 				 */
905e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
906e9dbad6fSeschrock 				    "'%s' cannot be set while dataset "
907e9dbad6fSeschrock 				    "'zoned' property is set"), propname);
908e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
909e9dbad6fSeschrock 				goto error;
910fa9e4066Sahrens 			}
911e9dbad6fSeschrock 		}
912fa9e4066Sahrens 
913e9dbad6fSeschrock 		/*
914e9dbad6fSeschrock 		 * For changes to existing volumes, we have some additional
915e9dbad6fSeschrock 		 * checks to enforce.
916e9dbad6fSeschrock 		 */
917e9dbad6fSeschrock 		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
918e9dbad6fSeschrock 			uint64_t volsize = zfs_prop_get_int(zhp,
919e9dbad6fSeschrock 			    ZFS_PROP_VOLSIZE);
920e9dbad6fSeschrock 			uint64_t blocksize = zfs_prop_get_int(zhp,
921e9dbad6fSeschrock 			    ZFS_PROP_VOLBLOCKSIZE);
922e9dbad6fSeschrock 			char buf[64];
923e9dbad6fSeschrock 
924e9dbad6fSeschrock 			switch (prop) {
925e9dbad6fSeschrock 			case ZFS_PROP_RESERVATION:
926e9dbad6fSeschrock 				if (intval > volsize) {
927e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
928e9dbad6fSeschrock 					    "'%s' is greater than current "
929e9dbad6fSeschrock 					    "volume size"), propname);
930e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
931e9dbad6fSeschrock 					    errbuf);
932e9dbad6fSeschrock 					goto error;
933e9dbad6fSeschrock 				}
934e9dbad6fSeschrock 				break;
935e9dbad6fSeschrock 
936e9dbad6fSeschrock 			case ZFS_PROP_VOLSIZE:
937e9dbad6fSeschrock 				if (intval % blocksize != 0) {
938e9dbad6fSeschrock 					zfs_nicenum(blocksize, buf,
939e9dbad6fSeschrock 					    sizeof (buf));
940e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
941e9dbad6fSeschrock 					    "'%s' must be a multiple of "
942e9dbad6fSeschrock 					    "volume block size (%s)"),
943e9dbad6fSeschrock 					    propname, buf);
944e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
945e9dbad6fSeschrock 					    errbuf);
946e9dbad6fSeschrock 					goto error;
947e9dbad6fSeschrock 				}
948e9dbad6fSeschrock 
949e9dbad6fSeschrock 				if (intval == 0) {
950e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
951e9dbad6fSeschrock 					    "'%s' cannot be zero"),
952e9dbad6fSeschrock 					    propname);
953e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
954e9dbad6fSeschrock 					    errbuf);
955e9dbad6fSeschrock 					goto error;
956e9dbad6fSeschrock 				}
957fa9e4066Sahrens 			}
958e9dbad6fSeschrock 		}
959e9dbad6fSeschrock 	}
960fa9e4066Sahrens 
961e9dbad6fSeschrock 	/*
962e9dbad6fSeschrock 	 * If this is an existing volume, and someone is setting the volsize,
963e9dbad6fSeschrock 	 * make sure that it matches the reservation, or add it if necessary.
964e9dbad6fSeschrock 	 */
965e9dbad6fSeschrock 	if (zhp != NULL && type == ZFS_TYPE_VOLUME &&
966e9dbad6fSeschrock 	    nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
967e9dbad6fSeschrock 	    &intval) == 0) {
968e9dbad6fSeschrock 		uint64_t old_volsize = zfs_prop_get_int(zhp,
969e9dbad6fSeschrock 		    ZFS_PROP_VOLSIZE);
970e9dbad6fSeschrock 		uint64_t old_reservation = zfs_prop_get_int(zhp,
971e9dbad6fSeschrock 		    ZFS_PROP_RESERVATION);
972e9dbad6fSeschrock 		uint64_t new_reservation;
973e9dbad6fSeschrock 
974e9dbad6fSeschrock 		if (old_volsize == old_reservation &&
975e9dbad6fSeschrock 		    nvlist_lookup_uint64(ret,
976e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_RESERVATION),
977e9dbad6fSeschrock 		    &new_reservation) != 0) {
978e9dbad6fSeschrock 			if (nvlist_add_uint64(ret,
979e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_RESERVATION),
980e9dbad6fSeschrock 			    intval) != 0) {
981e9dbad6fSeschrock 				(void) no_memory(hdl);
982e9dbad6fSeschrock 				goto error;
983e9dbad6fSeschrock 			}
984fa9e4066Sahrens 		}
985fa9e4066Sahrens 	}
986fa9e4066Sahrens 
987e9dbad6fSeschrock 	return (ret);
988fa9e4066Sahrens 
989e9dbad6fSeschrock error:
990e9dbad6fSeschrock 	nvlist_free(ret);
991e9dbad6fSeschrock 	return (NULL);
992fa9e4066Sahrens }
993fa9e4066Sahrens 
994fa9e4066Sahrens /*
995fa9e4066Sahrens  * Given a property name and value, set the property for the given dataset.
996fa9e4066Sahrens  */
997fa9e4066Sahrens int
998e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
999fa9e4066Sahrens {
1000fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1001e9dbad6fSeschrock 	int ret = -1;
1002e9dbad6fSeschrock 	prop_changelist_t *cl = NULL;
100399653d4eSeschrock 	char errbuf[1024];
100499653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
1005e9dbad6fSeschrock 	nvlist_t *nvl = NULL, *realprops;
1006e9dbad6fSeschrock 	zfs_prop_t prop;
100799653d4eSeschrock 
100899653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
1009e9dbad6fSeschrock 	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
101099653d4eSeschrock 	    zhp->zfs_name);
101199653d4eSeschrock 
1012e9dbad6fSeschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1013e9dbad6fSeschrock 	    nvlist_add_string(nvl, propname, propval) != 0) {
1014e9dbad6fSeschrock 		(void) no_memory(hdl);
1015e9dbad6fSeschrock 		goto error;
1016fa9e4066Sahrens 	}
1017fa9e4066Sahrens 
1018e9dbad6fSeschrock 	if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, nvl,
1019e9dbad6fSeschrock 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1020e9dbad6fSeschrock 		goto error;
1021e9dbad6fSeschrock 	nvlist_free(nvl);
1022e9dbad6fSeschrock 	nvl = realprops;
1023e9dbad6fSeschrock 
1024e9dbad6fSeschrock 	prop = zfs_name_to_prop(propname);
1025e9dbad6fSeschrock 
1026fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
1027e9dbad6fSeschrock 		goto error;
1028fa9e4066Sahrens 
1029fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
103099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
103199653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
103299653d4eSeschrock 		    "in a non-global zone"));
103399653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1034fa9e4066Sahrens 		goto error;
1035fa9e4066Sahrens 	}
1036fa9e4066Sahrens 
1037fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1038fa9e4066Sahrens 		goto error;
1039fa9e4066Sahrens 
1040fa9e4066Sahrens 	/*
1041fa9e4066Sahrens 	 * Execute the corresponding ioctl() to set this property.
1042fa9e4066Sahrens 	 */
1043fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1044fa9e4066Sahrens 
1045e9dbad6fSeschrock 	if (zcmd_write_src_nvlist(hdl, &zc, nvl, NULL) != 0)
1046e9dbad6fSeschrock 		goto error;
1047e9dbad6fSeschrock 
1048e9dbad6fSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
1049fa9e4066Sahrens 
1050fa9e4066Sahrens 	if (ret != 0) {
1051fa9e4066Sahrens 		switch (errno) {
1052fa9e4066Sahrens 
1053fa9e4066Sahrens 		case ENOSPC:
1054fa9e4066Sahrens 			/*
1055fa9e4066Sahrens 			 * For quotas and reservations, ENOSPC indicates
1056fa9e4066Sahrens 			 * something different; setting a quota or reservation
1057fa9e4066Sahrens 			 * doesn't use any disk space.
1058fa9e4066Sahrens 			 */
1059fa9e4066Sahrens 			switch (prop) {
1060fa9e4066Sahrens 			case ZFS_PROP_QUOTA:
106199653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
106299653d4eSeschrock 				    "size is less than current used or "
106399653d4eSeschrock 				    "reserved space"));
106499653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1065fa9e4066Sahrens 				break;
1066fa9e4066Sahrens 
1067fa9e4066Sahrens 			case ZFS_PROP_RESERVATION:
106899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
106999653d4eSeschrock 				    "size is greater than available space"));
107099653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1071fa9e4066Sahrens 				break;
1072fa9e4066Sahrens 
1073fa9e4066Sahrens 			default:
107499653d4eSeschrock 				(void) zfs_standard_error(hdl, errno, errbuf);
1075fa9e4066Sahrens 				break;
1076fa9e4066Sahrens 			}
1077fa9e4066Sahrens 			break;
1078fa9e4066Sahrens 
1079fa9e4066Sahrens 		case EBUSY:
108099653d4eSeschrock 			if (prop == ZFS_PROP_VOLBLOCKSIZE)
108199653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf);
108299653d4eSeschrock 			else
1083e9dbad6fSeschrock 				(void) zfs_standard_error(hdl, EBUSY, errbuf);
1084fa9e4066Sahrens 			break;
1085fa9e4066Sahrens 
10862a79c5feSlling 		case EROFS:
108799653d4eSeschrock 			(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
10882a79c5feSlling 			break;
10892a79c5feSlling 
1090fa9e4066Sahrens 		case EOVERFLOW:
1091fa9e4066Sahrens 			/*
1092fa9e4066Sahrens 			 * This platform can't address a volume this big.
1093fa9e4066Sahrens 			 */
1094fa9e4066Sahrens #ifdef _ILP32
1095fa9e4066Sahrens 			if (prop == ZFS_PROP_VOLSIZE) {
109699653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1097fa9e4066Sahrens 				break;
1098fa9e4066Sahrens 			}
1099fa9e4066Sahrens #endif
110099653d4eSeschrock 			/* FALLTHROUGH */
1101fa9e4066Sahrens 		default:
110299653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
1103fa9e4066Sahrens 		}
1104fa9e4066Sahrens 	} else {
1105fa9e4066Sahrens 		/*
1106fa9e4066Sahrens 		 * Refresh the statistics so the new property value
1107fa9e4066Sahrens 		 * is reflected.
1108fa9e4066Sahrens 		 */
1109e9dbad6fSeschrock 		if ((ret = changelist_postfix(cl)) == 0)
1110e9dbad6fSeschrock 			(void) get_stats(zhp);
1111fa9e4066Sahrens 	}
1112fa9e4066Sahrens 
1113fa9e4066Sahrens error:
1114e9dbad6fSeschrock 	nvlist_free(nvl);
1115e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
1116e9dbad6fSeschrock 	if (cl)
1117e9dbad6fSeschrock 		changelist_free(cl);
1118fa9e4066Sahrens 	return (ret);
1119fa9e4066Sahrens }
1120fa9e4066Sahrens 
1121fa9e4066Sahrens /*
1122fa9e4066Sahrens  * Given a property, inherit the value from the parent dataset.
1123fa9e4066Sahrens  */
1124fa9e4066Sahrens int
1125e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
1126fa9e4066Sahrens {
1127fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1128fa9e4066Sahrens 	int ret;
1129fa9e4066Sahrens 	prop_changelist_t *cl;
113099653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
113199653d4eSeschrock 	char errbuf[1024];
1132e9dbad6fSeschrock 	zfs_prop_t prop;
113399653d4eSeschrock 
113499653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
113599653d4eSeschrock 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1136fa9e4066Sahrens 
1137e9dbad6fSeschrock 	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
1138e9dbad6fSeschrock 		/*
1139e9dbad6fSeschrock 		 * For user properties, the amount of work we have to do is very
1140e9dbad6fSeschrock 		 * small, so just do it here.
1141e9dbad6fSeschrock 		 */
1142e9dbad6fSeschrock 		if (!zfs_prop_user(propname)) {
1143e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1144e9dbad6fSeschrock 			    "invalid property"));
1145e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1146e9dbad6fSeschrock 		}
1147e9dbad6fSeschrock 
1148e9dbad6fSeschrock 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1149e9dbad6fSeschrock 		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1150e9dbad6fSeschrock 
1151e9dbad6fSeschrock 		if (ioctl(zhp->zfs_hdl->libzfs_fd,
1152e9dbad6fSeschrock 		    ZFS_IOC_SET_PROP, &zc) != 0)
1153e9dbad6fSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1154e9dbad6fSeschrock 
1155e9dbad6fSeschrock 		return (0);
1156e9dbad6fSeschrock 	}
1157e9dbad6fSeschrock 
1158fa9e4066Sahrens 	/*
1159fa9e4066Sahrens 	 * Verify that this property is inheritable.
1160fa9e4066Sahrens 	 */
116199653d4eSeschrock 	if (zfs_prop_readonly(prop))
116299653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1163fa9e4066Sahrens 
116499653d4eSeschrock 	if (!zfs_prop_inheritable(prop))
116599653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1166fa9e4066Sahrens 
1167fa9e4066Sahrens 	/*
1168fa9e4066Sahrens 	 * Check to see if the value applies to this type
1169fa9e4066Sahrens 	 */
117099653d4eSeschrock 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
117199653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1172fa9e4066Sahrens 
1173fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1174e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1175fa9e4066Sahrens 
1176fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1177fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
117899653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
117999653d4eSeschrock 		    "dataset is used in a non-global zone"));
118099653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1181fa9e4066Sahrens 	}
1182fa9e4066Sahrens 
1183fa9e4066Sahrens 	/*
1184fa9e4066Sahrens 	 * Determine datasets which will be affected by this change, if any.
1185fa9e4066Sahrens 	 */
1186fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
1187fa9e4066Sahrens 		return (-1);
1188fa9e4066Sahrens 
1189fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
119099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
119199653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
119299653d4eSeschrock 		    "in a non-global zone"));
119399653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1194fa9e4066Sahrens 		goto error;
1195fa9e4066Sahrens 	}
1196fa9e4066Sahrens 
1197fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1198fa9e4066Sahrens 		goto error;
1199fa9e4066Sahrens 
120099653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd,
120199653d4eSeschrock 	    ZFS_IOC_SET_PROP, &zc)) != 0) {
120299653d4eSeschrock 		return (zfs_standard_error(hdl, errno, errbuf));
1203fa9e4066Sahrens 	} else {
1204fa9e4066Sahrens 
1205efc555ebSnd 		if ((ret = changelist_postfix(cl)) != 0)
1206fa9e4066Sahrens 			goto error;
1207fa9e4066Sahrens 
1208fa9e4066Sahrens 		/*
1209fa9e4066Sahrens 		 * Refresh the statistics so the new property is reflected.
1210fa9e4066Sahrens 		 */
1211fa9e4066Sahrens 		(void) get_stats(zhp);
1212fa9e4066Sahrens 	}
1213fa9e4066Sahrens 
1214fa9e4066Sahrens error:
1215fa9e4066Sahrens 	changelist_free(cl);
1216fa9e4066Sahrens 	return (ret);
1217fa9e4066Sahrens }
1218fa9e4066Sahrens 
1219fa9e4066Sahrens static void
1220fa9e4066Sahrens nicebool(int value, char *buf, size_t buflen)
1221fa9e4066Sahrens {
1222fa9e4066Sahrens 	if (value)
1223fa9e4066Sahrens 		(void) strlcpy(buf, "on", buflen);
1224fa9e4066Sahrens 	else
1225fa9e4066Sahrens 		(void) strlcpy(buf, "off", buflen);
1226fa9e4066Sahrens }
1227fa9e4066Sahrens 
12287f7322feSeschrock /*
12297f7322feSeschrock  * True DSL properties are stored in an nvlist.  The following two functions
12307f7322feSeschrock  * extract them appropriately.
12317f7322feSeschrock  */
12327f7322feSeschrock static uint64_t
12337f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
12347f7322feSeschrock {
12357f7322feSeschrock 	nvlist_t *nv;
12367f7322feSeschrock 	uint64_t value;
12377f7322feSeschrock 
12387f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
12397f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
12407f7322feSeschrock 		verify(nvlist_lookup_uint64(nv, ZFS_PROP_VALUE, &value) == 0);
12417f7322feSeschrock 		verify(nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source) == 0);
12427f7322feSeschrock 	} else {
12437f7322feSeschrock 		value = zfs_prop_default_numeric(prop);
12447f7322feSeschrock 		*source = "";
12457f7322feSeschrock 	}
12467f7322feSeschrock 
12477f7322feSeschrock 	return (value);
12487f7322feSeschrock }
12497f7322feSeschrock 
12507f7322feSeschrock static char *
12517f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
12527f7322feSeschrock {
12537f7322feSeschrock 	nvlist_t *nv;
12547f7322feSeschrock 	char *value;
12557f7322feSeschrock 
12567f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
12577f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
12587f7322feSeschrock 		verify(nvlist_lookup_string(nv, ZFS_PROP_VALUE, &value) == 0);
12597f7322feSeschrock 		verify(nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source) == 0);
12607f7322feSeschrock 	} else {
12617f7322feSeschrock 		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
12627f7322feSeschrock 			value = "";
12637f7322feSeschrock 		*source = "";
12647f7322feSeschrock 	}
12657f7322feSeschrock 
12667f7322feSeschrock 	return (value);
12677f7322feSeschrock }
12687f7322feSeschrock 
1269fa9e4066Sahrens /*
1270fa9e4066Sahrens  * Internal function for getting a numeric property.  Both zfs_prop_get() and
1271fa9e4066Sahrens  * zfs_prop_get_int() are built using this interface.
1272fa9e4066Sahrens  *
1273fa9e4066Sahrens  * Certain properties can be overridden using 'mount -o'.  In this case, scan
1274fa9e4066Sahrens  * the contents of the /etc/mnttab entry, searching for the appropriate options.
1275fa9e4066Sahrens  * If they differ from the on-disk values, report the current values and mark
1276fa9e4066Sahrens  * the source "temporary".
1277fa9e4066Sahrens  */
127899653d4eSeschrock static int
1279fa9e4066Sahrens get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
128099653d4eSeschrock     char **source, uint64_t *val)
1281fa9e4066Sahrens {
1282fa9e4066Sahrens 	struct mnttab mnt;
1283fa9e4066Sahrens 
1284fa9e4066Sahrens 	*source = NULL;
1285fa9e4066Sahrens 
12863bb79becSeschrock 	/*
12873bb79becSeschrock 	 * Because looking up the mount options is potentially expensive
12883bb79becSeschrock 	 * (iterating over all of /etc/mnttab), we defer its calculation until
12893bb79becSeschrock 	 * we're looking up a property which requires its presence.
12903bb79becSeschrock 	 */
12913bb79becSeschrock 	if (!zhp->zfs_mntcheck &&
12923bb79becSeschrock 	    (prop == ZFS_PROP_ATIME ||
12933bb79becSeschrock 	    prop == ZFS_PROP_DEVICES ||
12943bb79becSeschrock 	    prop == ZFS_PROP_EXEC ||
12953bb79becSeschrock 	    prop == ZFS_PROP_READONLY ||
12963bb79becSeschrock 	    prop == ZFS_PROP_SETUID ||
12973bb79becSeschrock 	    prop == ZFS_PROP_MOUNTED)) {
12983bb79becSeschrock 		struct mnttab search = { 0 }, entry;
12993bb79becSeschrock 
13003bb79becSeschrock 		search.mnt_special = (char *)zhp->zfs_name;
13013bb79becSeschrock 		search.mnt_fstype = MNTTYPE_ZFS;
13023bb79becSeschrock 		rewind(zhp->zfs_hdl->libzfs_mnttab);
13033bb79becSeschrock 
13043bb79becSeschrock 		if (getmntany(zhp->zfs_hdl->libzfs_mnttab, &entry,
13053bb79becSeschrock 		    &search) == 0 && (zhp->zfs_mntopts =
13063bb79becSeschrock 		    zfs_strdup(zhp->zfs_hdl,
13073bb79becSeschrock 		    entry.mnt_mntopts)) == NULL)
13083bb79becSeschrock 			return (-1);
13093bb79becSeschrock 
13103bb79becSeschrock 		zhp->zfs_mntcheck = B_TRUE;
13113bb79becSeschrock 	}
13123bb79becSeschrock 
1313fa9e4066Sahrens 	if (zhp->zfs_mntopts == NULL)
1314fa9e4066Sahrens 		mnt.mnt_mntopts = "";
1315fa9e4066Sahrens 	else
1316fa9e4066Sahrens 		mnt.mnt_mntopts = zhp->zfs_mntopts;
1317fa9e4066Sahrens 
1318fa9e4066Sahrens 	switch (prop) {
1319fa9e4066Sahrens 	case ZFS_PROP_ATIME:
132099653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1321fa9e4066Sahrens 
132299653d4eSeschrock 		if (hasmntopt(&mnt, MNTOPT_ATIME) && !*val) {
132399653d4eSeschrock 			*val = B_TRUE;
1324fa9e4066Sahrens 			if (src)
1325fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
132699653d4eSeschrock 		} else if (hasmntopt(&mnt, MNTOPT_NOATIME) && *val) {
132799653d4eSeschrock 			*val = B_FALSE;
1328fa9e4066Sahrens 			if (src)
1329fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1330fa9e4066Sahrens 		}
133199653d4eSeschrock 		break;
1332fa9e4066Sahrens 
1333fa9e4066Sahrens 	case ZFS_PROP_AVAILABLE:
133499653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_available;
133599653d4eSeschrock 		break;
1336fa9e4066Sahrens 
1337fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
133899653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1339fa9e4066Sahrens 
134099653d4eSeschrock 		if (hasmntopt(&mnt, MNTOPT_DEVICES) && !*val) {
134199653d4eSeschrock 			*val = B_TRUE;
1342fa9e4066Sahrens 			if (src)
1343fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
134499653d4eSeschrock 		} else if (hasmntopt(&mnt, MNTOPT_NODEVICES) && *val) {
134599653d4eSeschrock 			*val = B_FALSE;
1346fa9e4066Sahrens 			if (src)
1347fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1348fa9e4066Sahrens 		}
134999653d4eSeschrock 		break;
1350fa9e4066Sahrens 
1351fa9e4066Sahrens 	case ZFS_PROP_EXEC:
135299653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1353fa9e4066Sahrens 
135499653d4eSeschrock 		if (hasmntopt(&mnt, MNTOPT_EXEC) && !*val) {
135599653d4eSeschrock 			*val = B_TRUE;
1356fa9e4066Sahrens 			if (src)
1357fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
135899653d4eSeschrock 		} else if (hasmntopt(&mnt, MNTOPT_NOEXEC) && *val) {
135999653d4eSeschrock 			*val = B_FALSE;
1360fa9e4066Sahrens 			if (src)
1361fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1362fa9e4066Sahrens 		}
136399653d4eSeschrock 		break;
1364fa9e4066Sahrens 
1365fa9e4066Sahrens 	case ZFS_PROP_RECORDSIZE:
1366fa9e4066Sahrens 	case ZFS_PROP_COMPRESSION:
13677f7322feSeschrock 	case ZFS_PROP_ZONED:
136899653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
136999653d4eSeschrock 		break;
1370fa9e4066Sahrens 
1371fa9e4066Sahrens 	case ZFS_PROP_READONLY:
137299653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1373fa9e4066Sahrens 
137499653d4eSeschrock 		if (hasmntopt(&mnt, MNTOPT_RO) && !*val) {
137599653d4eSeschrock 			*val = B_TRUE;
1376fa9e4066Sahrens 			if (src)
1377fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
137899653d4eSeschrock 		} else if (hasmntopt(&mnt, MNTOPT_RW) && *val) {
137999653d4eSeschrock 			*val = B_FALSE;
1380fa9e4066Sahrens 			if (src)
1381fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1382fa9e4066Sahrens 		}
138399653d4eSeschrock 		break;
1384fa9e4066Sahrens 
1385ea8dc4b6Seschrock 	case ZFS_PROP_CREATION:
138699653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_creation_time;
138799653d4eSeschrock 		break;
1388ea8dc4b6Seschrock 
1389fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1390fa9e4066Sahrens 		if (zhp->zfs_dmustats.dds_quota == 0)
1391fa9e4066Sahrens 			*source = "";	/* default */
1392fa9e4066Sahrens 		else
1393fa9e4066Sahrens 			*source = zhp->zfs_name;
139499653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_quota;
139599653d4eSeschrock 		break;
1396fa9e4066Sahrens 
1397fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1398fa9e4066Sahrens 		if (zhp->zfs_dmustats.dds_reserved == 0)
1399fa9e4066Sahrens 			*source = "";	/* default */
1400fa9e4066Sahrens 		else
1401fa9e4066Sahrens 			*source = zhp->zfs_name;
140299653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_reserved;
140399653d4eSeschrock 		break;
1404fa9e4066Sahrens 
1405fa9e4066Sahrens 	case ZFS_PROP_COMPRESSRATIO:
1406fa9e4066Sahrens 		/*
1407fa9e4066Sahrens 		 * Using physical space and logical space, calculate the
1408fa9e4066Sahrens 		 * compression ratio.  We return the number as a multiple of
1409fa9e4066Sahrens 		 * 100, so '2.5x' would be returned as 250.
1410fa9e4066Sahrens 		 */
1411fa9e4066Sahrens 		if (zhp->zfs_dmustats.dds_compressed_bytes == 0)
141299653d4eSeschrock 			*val = 100ULL;
1413fa9e4066Sahrens 		else
141499653d4eSeschrock 			*val =
141599653d4eSeschrock 			    (zhp->zfs_dmustats.dds_uncompressed_bytes * 100 /
1416fa9e4066Sahrens 			    zhp->zfs_dmustats.dds_compressed_bytes);
141799653d4eSeschrock 		break;
1418fa9e4066Sahrens 
1419fa9e4066Sahrens 	case ZFS_PROP_REFERENCED:
1420fa9e4066Sahrens 		/*
1421fa9e4066Sahrens 		 * 'referenced' refers to the amount of physical space
1422fa9e4066Sahrens 		 * referenced (possibly shared) by this object.
1423fa9e4066Sahrens 		 */
142499653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_space_refd;
142599653d4eSeschrock 		break;
1426fa9e4066Sahrens 
1427fa9e4066Sahrens 	case ZFS_PROP_SETUID:
142899653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1429fa9e4066Sahrens 
143099653d4eSeschrock 		if (hasmntopt(&mnt, MNTOPT_SETUID) && !*val) {
143199653d4eSeschrock 			*val = B_TRUE;
1432fa9e4066Sahrens 			if (src)
1433fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
143499653d4eSeschrock 		} else if (hasmntopt(&mnt, MNTOPT_NOSETUID) && *val) {
143599653d4eSeschrock 			*val = B_FALSE;
1436fa9e4066Sahrens 			if (src)
1437fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1438fa9e4066Sahrens 		}
143999653d4eSeschrock 		break;
1440fa9e4066Sahrens 
1441fa9e4066Sahrens 	case ZFS_PROP_VOLSIZE:
1442e9dbad6fSeschrock 		*val = zhp->zfs_volstats.zv_volsize;
144399653d4eSeschrock 		break;
1444fa9e4066Sahrens 
1445fa9e4066Sahrens 	case ZFS_PROP_VOLBLOCKSIZE:
1446e9dbad6fSeschrock 		*val = zhp->zfs_volstats.zv_volblocksize;
144799653d4eSeschrock 		break;
1448fa9e4066Sahrens 
1449fa9e4066Sahrens 	case ZFS_PROP_USED:
145099653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_space_used;
145199653d4eSeschrock 		break;
1452fa9e4066Sahrens 
1453fa9e4066Sahrens 	case ZFS_PROP_CREATETXG:
145499653d4eSeschrock 		*val = zhp->zfs_dmustats.dds_creation_txg;
145599653d4eSeschrock 		break;
1456fa9e4066Sahrens 
1457fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
145899653d4eSeschrock 		*val = (zhp->zfs_mntopts != NULL);
145999653d4eSeschrock 		break;
1460fa9e4066Sahrens 
1461e9dbad6fSeschrock 	case ZFS_PROP_CANMOUNT:
1462e9dbad6fSeschrock 		*val = getprop_uint64(zhp, prop, source);
1463e9dbad6fSeschrock 		break;
1464e9dbad6fSeschrock 
1465fa9e4066Sahrens 	default:
146699653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
146799653d4eSeschrock 		    "cannot get non-numeric property"));
146899653d4eSeschrock 		return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
146999653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "internal error")));
1470fa9e4066Sahrens 	}
1471fa9e4066Sahrens 
1472fa9e4066Sahrens 	return (0);
1473fa9e4066Sahrens }
1474fa9e4066Sahrens 
1475fa9e4066Sahrens /*
1476fa9e4066Sahrens  * Calculate the source type, given the raw source string.
1477fa9e4066Sahrens  */
1478fa9e4066Sahrens static void
1479fa9e4066Sahrens get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source,
1480fa9e4066Sahrens     char *statbuf, size_t statlen)
1481fa9e4066Sahrens {
1482fa9e4066Sahrens 	if (statbuf == NULL || *srctype == ZFS_SRC_TEMPORARY)
1483fa9e4066Sahrens 		return;
1484fa9e4066Sahrens 
1485fa9e4066Sahrens 	if (source == NULL) {
1486fa9e4066Sahrens 		*srctype = ZFS_SRC_NONE;
1487fa9e4066Sahrens 	} else if (source[0] == '\0') {
1488fa9e4066Sahrens 		*srctype = ZFS_SRC_DEFAULT;
1489fa9e4066Sahrens 	} else {
1490fa9e4066Sahrens 		if (strcmp(source, zhp->zfs_name) == 0) {
1491fa9e4066Sahrens 			*srctype = ZFS_SRC_LOCAL;
1492fa9e4066Sahrens 		} else {
1493fa9e4066Sahrens 			(void) strlcpy(statbuf, source, statlen);
1494fa9e4066Sahrens 			*srctype = ZFS_SRC_INHERITED;
1495fa9e4066Sahrens 		}
1496fa9e4066Sahrens 	}
1497fa9e4066Sahrens 
1498fa9e4066Sahrens }
1499fa9e4066Sahrens 
1500fa9e4066Sahrens /*
1501fa9e4066Sahrens  * Retrieve a property from the given object.  If 'literal' is specified, then
1502fa9e4066Sahrens  * numbers are left as exact values.  Otherwise, numbers are converted to a
1503fa9e4066Sahrens  * human-readable form.
1504fa9e4066Sahrens  *
1505fa9e4066Sahrens  * Returns 0 on success, or -1 on error.
1506fa9e4066Sahrens  */
1507fa9e4066Sahrens int
1508fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
150999653d4eSeschrock     zfs_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1510fa9e4066Sahrens {
1511fa9e4066Sahrens 	char *source = NULL;
1512fa9e4066Sahrens 	uint64_t val;
1513fa9e4066Sahrens 	char *str;
1514fa9e4066Sahrens 	const char *root;
1515e9dbad6fSeschrock 	const char *strval;
1516fa9e4066Sahrens 
1517fa9e4066Sahrens 	/*
1518fa9e4066Sahrens 	 * Check to see if this property applies to our object
1519fa9e4066Sahrens 	 */
1520fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1521fa9e4066Sahrens 		return (-1);
1522fa9e4066Sahrens 
1523fa9e4066Sahrens 	if (src)
1524fa9e4066Sahrens 		*src = ZFS_SRC_NONE;
1525fa9e4066Sahrens 
1526fa9e4066Sahrens 	switch (prop) {
1527fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1528fa9e4066Sahrens 	case ZFS_PROP_READONLY:
1529fa9e4066Sahrens 	case ZFS_PROP_SETUID:
1530fa9e4066Sahrens 	case ZFS_PROP_ZONED:
1531fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1532fa9e4066Sahrens 	case ZFS_PROP_EXEC:
1533e9dbad6fSeschrock 	case ZFS_PROP_CANMOUNT:
1534fa9e4066Sahrens 		/*
1535fa9e4066Sahrens 		 * Basic boolean values are built on top of
1536fa9e4066Sahrens 		 * get_numeric_property().
1537fa9e4066Sahrens 		 */
153899653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
153999653d4eSeschrock 			return (-1);
154099653d4eSeschrock 		nicebool(val, propbuf, proplen);
1541fa9e4066Sahrens 
1542fa9e4066Sahrens 		break;
1543fa9e4066Sahrens 
1544fa9e4066Sahrens 	case ZFS_PROP_AVAILABLE:
1545fa9e4066Sahrens 	case ZFS_PROP_RECORDSIZE:
1546fa9e4066Sahrens 	case ZFS_PROP_CREATETXG:
1547fa9e4066Sahrens 	case ZFS_PROP_REFERENCED:
1548fa9e4066Sahrens 	case ZFS_PROP_USED:
1549fa9e4066Sahrens 	case ZFS_PROP_VOLSIZE:
1550fa9e4066Sahrens 	case ZFS_PROP_VOLBLOCKSIZE:
1551fa9e4066Sahrens 		/*
1552fa9e4066Sahrens 		 * Basic numeric values are built on top of
1553fa9e4066Sahrens 		 * get_numeric_property().
1554fa9e4066Sahrens 		 */
155599653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
155699653d4eSeschrock 			return (-1);
1557fa9e4066Sahrens 		if (literal)
1558*5ad82045Snd 			(void) snprintf(propbuf, proplen, "%llu",
1559*5ad82045Snd 			(u_longlong_t)val);
1560fa9e4066Sahrens 		else
1561fa9e4066Sahrens 			zfs_nicenum(val, propbuf, proplen);
1562fa9e4066Sahrens 		break;
1563fa9e4066Sahrens 
1564fa9e4066Sahrens 	case ZFS_PROP_COMPRESSION:
1565fa9e4066Sahrens 	case ZFS_PROP_CHECKSUM:
1566fa9e4066Sahrens 	case ZFS_PROP_SNAPDIR:
1567fa9e4066Sahrens 	case ZFS_PROP_ACLMODE:
1568fa9e4066Sahrens 	case ZFS_PROP_ACLINHERIT:
15697f7322feSeschrock 		val = getprop_uint64(zhp, prop, &source);
1570e9dbad6fSeschrock 		verify(zfs_prop_index_to_string(prop, val, &strval) == 0);
1571e9dbad6fSeschrock 		(void) strlcpy(propbuf, strval, proplen);
1572fa9e4066Sahrens 		break;
1573fa9e4066Sahrens 
1574fa9e4066Sahrens 	case ZFS_PROP_CREATION:
1575fa9e4066Sahrens 		/*
1576fa9e4066Sahrens 		 * 'creation' is a time_t stored in the statistics.  We convert
1577fa9e4066Sahrens 		 * this into a string unless 'literal' is specified.
1578fa9e4066Sahrens 		 */
1579fa9e4066Sahrens 		{
1580fa9e4066Sahrens 			time_t time = (time_t)
1581fa9e4066Sahrens 			    zhp->zfs_dmustats.dds_creation_time;
1582fa9e4066Sahrens 			struct tm t;
1583fa9e4066Sahrens 
1584fa9e4066Sahrens 			if (literal ||
1585fa9e4066Sahrens 			    localtime_r(&time, &t) == NULL ||
1586fa9e4066Sahrens 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1587fa9e4066Sahrens 			    &t) == 0)
1588fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%llu",
1589*5ad82045Snd 				    (u_longlong_t)
1590fa9e4066Sahrens 				    zhp->zfs_dmustats.dds_creation_time);
1591fa9e4066Sahrens 		}
1592fa9e4066Sahrens 		break;
1593fa9e4066Sahrens 
1594fa9e4066Sahrens 	case ZFS_PROP_MOUNTPOINT:
1595fa9e4066Sahrens 		/*
1596fa9e4066Sahrens 		 * Getting the precise mountpoint can be tricky.
1597fa9e4066Sahrens 		 *
1598fa9e4066Sahrens 		 *  - for 'none' or 'legacy', return those values.
1599fa9e4066Sahrens 		 *  - for default mountpoints, construct it as /zfs/<dataset>
1600fa9e4066Sahrens 		 *  - for inherited mountpoints, we want to take everything
1601fa9e4066Sahrens 		 *    after our ancestor and append it to the inherited value.
1602fa9e4066Sahrens 		 *
1603fa9e4066Sahrens 		 * If the pool has an alternate root, we want to prepend that
1604fa9e4066Sahrens 		 * root to any values we return.
1605fa9e4066Sahrens 		 */
1606ea8dc4b6Seschrock 		root = zhp->zfs_root;
16077f7322feSeschrock 		str = getprop_string(zhp, prop, &source);
1608fa9e4066Sahrens 
16097f7322feSeschrock 		if (str[0] == '\0') {
1610fa9e4066Sahrens 			(void) snprintf(propbuf, proplen, "%s/zfs/%s",
1611fa9e4066Sahrens 			    root, zhp->zfs_name);
16127f7322feSeschrock 		} else if (str[0] == '/') {
16137f7322feSeschrock 			const char *relpath = zhp->zfs_name + strlen(source);
1614fa9e4066Sahrens 
1615fa9e4066Sahrens 			if (relpath[0] == '/')
1616fa9e4066Sahrens 				relpath++;
16177f7322feSeschrock 			if (str[1] == '\0')
16187f7322feSeschrock 				str++;
1619fa9e4066Sahrens 
1620fa9e4066Sahrens 			if (relpath[0] == '\0')
1621fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s",
16227f7322feSeschrock 				    root, str);
1623fa9e4066Sahrens 			else
1624fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
16257f7322feSeschrock 				    root, str, relpath[0] == '@' ? "" : "/",
1626fa9e4066Sahrens 				    relpath);
1627fa9e4066Sahrens 		} else {
1628fa9e4066Sahrens 			/* 'legacy' or 'none' */
16297f7322feSeschrock 			(void) strlcpy(propbuf, str, proplen);
1630fa9e4066Sahrens 		}
1631fa9e4066Sahrens 
1632fa9e4066Sahrens 		break;
1633fa9e4066Sahrens 
1634fa9e4066Sahrens 	case ZFS_PROP_SHARENFS:
16357f7322feSeschrock 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
16367f7322feSeschrock 		    proplen);
1637fa9e4066Sahrens 		break;
1638fa9e4066Sahrens 
1639fa9e4066Sahrens 	case ZFS_PROP_ORIGIN:
1640ea8dc4b6Seschrock 		(void) strlcpy(propbuf, zhp->zfs_dmustats.dds_clone_of,
1641fa9e4066Sahrens 		    proplen);
1642fa9e4066Sahrens 		/*
1643fa9e4066Sahrens 		 * If there is no parent at all, return failure to indicate that
1644fa9e4066Sahrens 		 * it doesn't apply to this dataset.
1645fa9e4066Sahrens 		 */
1646fa9e4066Sahrens 		if (propbuf[0] == '\0')
1647fa9e4066Sahrens 			return (-1);
1648fa9e4066Sahrens 		break;
1649fa9e4066Sahrens 
1650fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1651fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
165299653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
165399653d4eSeschrock 			return (-1);
1654fa9e4066Sahrens 
1655fa9e4066Sahrens 		/*
1656fa9e4066Sahrens 		 * If quota or reservation is 0, we translate this into 'none'
1657fa9e4066Sahrens 		 * (unless literal is set), and indicate that it's the default
1658fa9e4066Sahrens 		 * value.  Otherwise, we print the number nicely and indicate
1659fa9e4066Sahrens 		 * that its set locally.
1660fa9e4066Sahrens 		 */
1661fa9e4066Sahrens 		if (val == 0) {
1662fa9e4066Sahrens 			if (literal)
1663fa9e4066Sahrens 				(void) strlcpy(propbuf, "0", proplen);
1664fa9e4066Sahrens 			else
1665fa9e4066Sahrens 				(void) strlcpy(propbuf, "none", proplen);
1666fa9e4066Sahrens 		} else {
1667fa9e4066Sahrens 			if (literal)
1668*5ad82045Snd 				(void) snprintf(propbuf, proplen, "%llu",
1669*5ad82045Snd 				(u_longlong_t)val);
1670fa9e4066Sahrens 			else
1671fa9e4066Sahrens 				zfs_nicenum(val, propbuf, proplen);
1672fa9e4066Sahrens 		}
1673fa9e4066Sahrens 		break;
1674fa9e4066Sahrens 
1675fa9e4066Sahrens 	case ZFS_PROP_COMPRESSRATIO:
167699653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
167799653d4eSeschrock 			return (-1);
1678*5ad82045Snd 		(void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t)
1679*5ad82045Snd 		    val / 100, (longlong_t)val % 100);
1680fa9e4066Sahrens 		break;
1681fa9e4066Sahrens 
1682fa9e4066Sahrens 	case ZFS_PROP_TYPE:
1683fa9e4066Sahrens 		switch (zhp->zfs_type) {
1684fa9e4066Sahrens 		case ZFS_TYPE_FILESYSTEM:
1685fa9e4066Sahrens 			str = "filesystem";
1686fa9e4066Sahrens 			break;
1687fa9e4066Sahrens 		case ZFS_TYPE_VOLUME:
1688fa9e4066Sahrens 			str = "volume";
1689fa9e4066Sahrens 			break;
1690fa9e4066Sahrens 		case ZFS_TYPE_SNAPSHOT:
1691fa9e4066Sahrens 			str = "snapshot";
1692fa9e4066Sahrens 			break;
1693fa9e4066Sahrens 		default:
169499653d4eSeschrock 			abort();
1695fa9e4066Sahrens 		}
1696fa9e4066Sahrens 		(void) snprintf(propbuf, proplen, "%s", str);
1697fa9e4066Sahrens 		break;
1698fa9e4066Sahrens 
1699fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
1700fa9e4066Sahrens 		/*
1701fa9e4066Sahrens 		 * The 'mounted' property is a pseudo-property that described
1702fa9e4066Sahrens 		 * whether the filesystem is currently mounted.  Even though
1703fa9e4066Sahrens 		 * it's a boolean value, the typical values of "on" and "off"
1704fa9e4066Sahrens 		 * don't make sense, so we translate to "yes" and "no".
1705fa9e4066Sahrens 		 */
170699653d4eSeschrock 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
170799653d4eSeschrock 		    src, &source, &val) != 0)
170899653d4eSeschrock 			return (-1);
170999653d4eSeschrock 		if (val)
1710fa9e4066Sahrens 			(void) strlcpy(propbuf, "yes", proplen);
1711fa9e4066Sahrens 		else
1712fa9e4066Sahrens 			(void) strlcpy(propbuf, "no", proplen);
1713fa9e4066Sahrens 		break;
1714fa9e4066Sahrens 
1715fa9e4066Sahrens 	case ZFS_PROP_NAME:
1716fa9e4066Sahrens 		/*
1717fa9e4066Sahrens 		 * The 'name' property is a pseudo-property derived from the
1718fa9e4066Sahrens 		 * dataset name.  It is presented as a real property to simplify
1719fa9e4066Sahrens 		 * consumers.
1720fa9e4066Sahrens 		 */
1721fa9e4066Sahrens 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
1722fa9e4066Sahrens 		break;
1723fa9e4066Sahrens 
1724fa9e4066Sahrens 	default:
172599653d4eSeschrock 		abort();
1726fa9e4066Sahrens 	}
1727fa9e4066Sahrens 
1728fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1729fa9e4066Sahrens 
1730fa9e4066Sahrens 	return (0);
1731fa9e4066Sahrens }
1732fa9e4066Sahrens 
1733fa9e4066Sahrens /*
1734fa9e4066Sahrens  * Utility function to get the given numeric property.  Does no validation that
1735fa9e4066Sahrens  * the given property is the appropriate type; should only be used with
1736fa9e4066Sahrens  * hard-coded property types.
1737fa9e4066Sahrens  */
1738fa9e4066Sahrens uint64_t
1739fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
1740fa9e4066Sahrens {
1741fa9e4066Sahrens 	char *source;
1742fa9e4066Sahrens 	zfs_source_t sourcetype = ZFS_SRC_NONE;
174399653d4eSeschrock 	uint64_t val;
174499653d4eSeschrock 
174599653d4eSeschrock 	(void) get_numeric_property(zhp, prop, &sourcetype, &source, &val);
1746fa9e4066Sahrens 
174799653d4eSeschrock 	return (val);
1748fa9e4066Sahrens }
1749fa9e4066Sahrens 
1750fa9e4066Sahrens /*
1751fa9e4066Sahrens  * Similar to zfs_prop_get(), but returns the value as an integer.
1752fa9e4066Sahrens  */
1753fa9e4066Sahrens int
1754fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
1755fa9e4066Sahrens     zfs_source_t *src, char *statbuf, size_t statlen)
1756fa9e4066Sahrens {
1757fa9e4066Sahrens 	char *source;
1758fa9e4066Sahrens 
1759fa9e4066Sahrens 	/*
1760fa9e4066Sahrens 	 * Check to see if this property applies to our object
1761fa9e4066Sahrens 	 */
1762fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
176399653d4eSeschrock 		return (zfs_error(zhp->zfs_hdl, EZFS_PROPTYPE,
176499653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
176599653d4eSeschrock 		    zfs_prop_to_name(prop)));
1766fa9e4066Sahrens 
1767fa9e4066Sahrens 	if (src)
1768fa9e4066Sahrens 		*src = ZFS_SRC_NONE;
1769fa9e4066Sahrens 
177099653d4eSeschrock 	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
177199653d4eSeschrock 		return (-1);
1772fa9e4066Sahrens 
1773fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1774fa9e4066Sahrens 
1775fa9e4066Sahrens 	return (0);
1776fa9e4066Sahrens }
1777fa9e4066Sahrens 
1778fa9e4066Sahrens /*
1779fa9e4066Sahrens  * Returns the name of the given zfs handle.
1780fa9e4066Sahrens  */
1781fa9e4066Sahrens const char *
1782fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp)
1783fa9e4066Sahrens {
1784fa9e4066Sahrens 	return (zhp->zfs_name);
1785fa9e4066Sahrens }
1786fa9e4066Sahrens 
1787fa9e4066Sahrens /*
1788fa9e4066Sahrens  * Returns the type of the given zfs handle.
1789fa9e4066Sahrens  */
1790fa9e4066Sahrens zfs_type_t
1791fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp)
1792fa9e4066Sahrens {
1793fa9e4066Sahrens 	return (zhp->zfs_type);
1794fa9e4066Sahrens }
1795fa9e4066Sahrens 
1796fa9e4066Sahrens /*
17977f7322feSeschrock  * Iterate over all child filesystems
1798fa9e4066Sahrens  */
1799fa9e4066Sahrens int
18007f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
1801fa9e4066Sahrens {
1802fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1803fa9e4066Sahrens 	zfs_handle_t *nzhp;
1804fa9e4066Sahrens 	int ret;
1805fa9e4066Sahrens 
1806fa9e4066Sahrens 	for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
180799653d4eSeschrock 	    ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
1808fa9e4066Sahrens 	    (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
1809fa9e4066Sahrens 		/*
1810fa9e4066Sahrens 		 * Ignore private dataset names.
1811fa9e4066Sahrens 		 */
1812fa9e4066Sahrens 		if (dataset_name_hidden(zc.zc_name))
1813fa9e4066Sahrens 			continue;
1814fa9e4066Sahrens 
1815fa9e4066Sahrens 		/*
1816fa9e4066Sahrens 		 * Silently ignore errors, as the only plausible explanation is
1817fa9e4066Sahrens 		 * that the pool has since been removed.
1818fa9e4066Sahrens 		 */
181999653d4eSeschrock 		if ((nzhp = make_dataset_handle(zhp->zfs_hdl,
182099653d4eSeschrock 		    zc.zc_name)) == NULL)
1821fa9e4066Sahrens 			continue;
1822fa9e4066Sahrens 
1823fa9e4066Sahrens 		if ((ret = func(nzhp, data)) != 0)
1824fa9e4066Sahrens 			return (ret);
1825fa9e4066Sahrens 	}
1826fa9e4066Sahrens 
1827fa9e4066Sahrens 	/*
1828fa9e4066Sahrens 	 * An errno value of ESRCH indicates normal completion.  If ENOENT is
1829fa9e4066Sahrens 	 * returned, then the underlying dataset has been removed since we
1830fa9e4066Sahrens 	 * obtained the handle.
1831fa9e4066Sahrens 	 */
1832fa9e4066Sahrens 	if (errno != ESRCH && errno != ENOENT)
183399653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
183499653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot iterate filesystems")));
1835fa9e4066Sahrens 
18367f7322feSeschrock 	return (0);
18377f7322feSeschrock }
18387f7322feSeschrock 
18397f7322feSeschrock /*
18407f7322feSeschrock  * Iterate over all snapshots
18417f7322feSeschrock  */
18427f7322feSeschrock int
18437f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
18447f7322feSeschrock {
18457f7322feSeschrock 	zfs_cmd_t zc = { 0 };
18467f7322feSeschrock 	zfs_handle_t *nzhp;
18477f7322feSeschrock 	int ret;
1848fa9e4066Sahrens 
1849fa9e4066Sahrens 	for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
185099653d4eSeschrock 	    ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
185199653d4eSeschrock 	    &zc) == 0;
1852fa9e4066Sahrens 	    (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
1853fa9e4066Sahrens 
185499653d4eSeschrock 		if ((nzhp = make_dataset_handle(zhp->zfs_hdl,
185599653d4eSeschrock 		    zc.zc_name)) == NULL)
1856fa9e4066Sahrens 			continue;
1857fa9e4066Sahrens 
1858fa9e4066Sahrens 		if ((ret = func(nzhp, data)) != 0)
1859fa9e4066Sahrens 			return (ret);
1860fa9e4066Sahrens 	}
1861fa9e4066Sahrens 
1862fa9e4066Sahrens 	/*
1863fa9e4066Sahrens 	 * An errno value of ESRCH indicates normal completion.  If ENOENT is
1864fa9e4066Sahrens 	 * returned, then the underlying dataset has been removed since we
1865fa9e4066Sahrens 	 * obtained the handle.  Silently ignore this case, and return success.
1866fa9e4066Sahrens 	 */
1867fa9e4066Sahrens 	if (errno != ESRCH && errno != ENOENT)
186899653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
186999653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot iterate filesystems")));
1870fa9e4066Sahrens 
1871fa9e4066Sahrens 	return (0);
1872fa9e4066Sahrens }
1873fa9e4066Sahrens 
18747f7322feSeschrock /*
18757f7322feSeschrock  * Iterate over all children, snapshots and filesystems
18767f7322feSeschrock  */
18777f7322feSeschrock int
18787f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
18797f7322feSeschrock {
18807f7322feSeschrock 	int ret;
18817f7322feSeschrock 
18827f7322feSeschrock 	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
18837f7322feSeschrock 		return (ret);
18847f7322feSeschrock 
18857f7322feSeschrock 	return (zfs_iter_snapshots(zhp, func, data));
18867f7322feSeschrock }
18877f7322feSeschrock 
1888fa9e4066Sahrens /*
1889fa9e4066Sahrens  * Given a complete name, return just the portion that refers to the parent.
1890fa9e4066Sahrens  * Can return NULL if this is a pool.
1891fa9e4066Sahrens  */
1892fa9e4066Sahrens static int
1893fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen)
1894fa9e4066Sahrens {
1895fa9e4066Sahrens 	char *loc;
1896fa9e4066Sahrens 
1897fa9e4066Sahrens 	if ((loc = strrchr(path, '/')) == NULL)
1898fa9e4066Sahrens 		return (-1);
1899fa9e4066Sahrens 
1900fa9e4066Sahrens 	(void) strncpy(buf, path, MIN(buflen, loc - path));
1901fa9e4066Sahrens 	buf[loc - path] = '\0';
1902fa9e4066Sahrens 
1903fa9e4066Sahrens 	return (0);
1904fa9e4066Sahrens }
1905fa9e4066Sahrens 
1906fa9e4066Sahrens /*
1907e9dbad6fSeschrock  * Checks to make sure that the given path has a parent, and that it exists.  We
1908e9dbad6fSeschrock  * also fetch the 'zoned' property, which is used to validate property settings
1909e9dbad6fSeschrock  * when creating new datasets.
1910fa9e4066Sahrens  */
1911fa9e4066Sahrens static int
1912e9dbad6fSeschrock check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
1913fa9e4066Sahrens {
1914fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1915fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
1916fa9e4066Sahrens 	char *slash;
19177f7322feSeschrock 	zfs_handle_t *zhp;
191899653d4eSeschrock 	char errbuf[1024];
191999653d4eSeschrock 
192099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), "cannot create '%s'",
192199653d4eSeschrock 	    path);
1922fa9e4066Sahrens 
1923fa9e4066Sahrens 	/* get parent, and check to see if this is just a pool */
1924fa9e4066Sahrens 	if (parent_name(path, parent, sizeof (parent)) != 0) {
192599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
192699653d4eSeschrock 		    "missing dataset name"));
192799653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
1928fa9e4066Sahrens 	}
1929fa9e4066Sahrens 
1930fa9e4066Sahrens 	/* check to see if the pool exists */
1931fa9e4066Sahrens 	if ((slash = strchr(parent, '/')) == NULL)
1932fa9e4066Sahrens 		slash = parent + strlen(parent);
1933fa9e4066Sahrens 	(void) strncpy(zc.zc_name, parent, slash - parent);
1934fa9e4066Sahrens 	zc.zc_name[slash - parent] = '\0';
193599653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
1936fa9e4066Sahrens 	    errno == ENOENT) {
193799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
193899653d4eSeschrock 		    "no such pool '%s'"), zc.zc_name);
193999653d4eSeschrock 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
1940fa9e4066Sahrens 	}
1941fa9e4066Sahrens 
1942fa9e4066Sahrens 	/* check to see if the parent dataset exists */
194399653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
1944fa9e4066Sahrens 		switch (errno) {
1945fa9e4066Sahrens 		case ENOENT:
194699653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
194799653d4eSeschrock 			    "parent does not exist"));
194899653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
1949fa9e4066Sahrens 
1950fa9e4066Sahrens 		default:
195199653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1952fa9e4066Sahrens 		}
1953fa9e4066Sahrens 	}
1954fa9e4066Sahrens 
1955e9dbad6fSeschrock 	*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
1956fa9e4066Sahrens 	/* we are in a non-global zone, but parent is in the global zone */
1957e9dbad6fSeschrock 	if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) {
195899653d4eSeschrock 		(void) zfs_standard_error(hdl, EPERM, errbuf);
19597f7322feSeschrock 		zfs_close(zhp);
1960fa9e4066Sahrens 		return (-1);
1961fa9e4066Sahrens 	}
1962fa9e4066Sahrens 
1963fa9e4066Sahrens 	/* make sure parent is a filesystem */
19647f7322feSeschrock 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
196599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
196699653d4eSeschrock 		    "parent is not a filesystem"));
196799653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
19687f7322feSeschrock 		zfs_close(zhp);
1969fa9e4066Sahrens 		return (-1);
1970fa9e4066Sahrens 	}
1971fa9e4066Sahrens 
19727f7322feSeschrock 	zfs_close(zhp);
1973fa9e4066Sahrens 	return (0);
1974fa9e4066Sahrens }
1975fa9e4066Sahrens 
1976fa9e4066Sahrens /*
1977e9dbad6fSeschrock  * Create a new filesystem or volume.
1978fa9e4066Sahrens  */
1979fa9e4066Sahrens int
198099653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
1981e9dbad6fSeschrock     nvlist_t *props)
1982fa9e4066Sahrens {
1983fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1984fa9e4066Sahrens 	int ret;
1985fa9e4066Sahrens 	uint64_t size = 0;
1986fa9e4066Sahrens 	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
198799653d4eSeschrock 	char errbuf[1024];
1988e9dbad6fSeschrock 	uint64_t zoned;
198999653d4eSeschrock 
199099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
199199653d4eSeschrock 	    "cannot create '%s'"), path);
1992fa9e4066Sahrens 
1993fa9e4066Sahrens 	/* validate the path, taking care to note the extended error message */
199499653d4eSeschrock 	if (!zfs_validate_name(hdl, path, type))
199599653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
1996fa9e4066Sahrens 
1997fa9e4066Sahrens 	/* validate parents exist */
1998e9dbad6fSeschrock 	if (check_parents(hdl, path, &zoned) != 0)
1999fa9e4066Sahrens 		return (-1);
2000fa9e4066Sahrens 
2001fa9e4066Sahrens 	/*
2002fa9e4066Sahrens 	 * The failure modes when creating a dataset of a different type over
2003fa9e4066Sahrens 	 * one that already exists is a little strange.  In particular, if you
2004fa9e4066Sahrens 	 * try to create a dataset on top of an existing dataset, the ioctl()
2005fa9e4066Sahrens 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
2006fa9e4066Sahrens 	 * first try to see if the dataset exists.
2007fa9e4066Sahrens 	 */
2008fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
200999653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
201099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
201199653d4eSeschrock 		    "dataset already exists"));
201299653d4eSeschrock 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2013fa9e4066Sahrens 	}
2014fa9e4066Sahrens 
2015fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME)
2016fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2017fa9e4066Sahrens 	else
2018fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2019fa9e4066Sahrens 
2020e9dbad6fSeschrock 	if (props && (props = zfs_validate_properties(hdl, type, props, zoned,
2021e9dbad6fSeschrock 	    NULL, errbuf)) == 0)
2022e9dbad6fSeschrock 		return (-1);
2023e9dbad6fSeschrock 
2024fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME) {
20255c5460e9Seschrock 		/*
20265c5460e9Seschrock 		 * If we are creating a volume, the size and block size must
20275c5460e9Seschrock 		 * satisfy a few restraints.  First, the blocksize must be a
20285c5460e9Seschrock 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
20295c5460e9Seschrock 		 * volsize must be a multiple of the block size, and cannot be
20305c5460e9Seschrock 		 * zero.
20315c5460e9Seschrock 		 */
2032e9dbad6fSeschrock 		if (props == NULL || nvlist_lookup_uint64(props,
2033e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2034e9dbad6fSeschrock 			nvlist_free(props);
203599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2036e9dbad6fSeschrock 			    "missing volume size"));
2037e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2038e9dbad6fSeschrock 		}
2039e9dbad6fSeschrock 
2040e9dbad6fSeschrock 		if ((ret = nvlist_lookup_uint64(props,
2041e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2042e9dbad6fSeschrock 		    &blocksize)) != 0) {
2043e9dbad6fSeschrock 			if (ret == ENOENT) {
2044e9dbad6fSeschrock 				blocksize = zfs_prop_default_numeric(
2045e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2046e9dbad6fSeschrock 			} else {
2047e9dbad6fSeschrock 				nvlist_free(props);
2048e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2049e9dbad6fSeschrock 				    "missing volume block size"));
2050e9dbad6fSeschrock 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2051e9dbad6fSeschrock 			}
2052fa9e4066Sahrens 		}
2053fa9e4066Sahrens 
2054e9dbad6fSeschrock 		if (size == 0) {
2055e9dbad6fSeschrock 			nvlist_free(props);
205699653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2057e9dbad6fSeschrock 			    "volume size cannot be zero"));
2058e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
20595c5460e9Seschrock 		}
20605c5460e9Seschrock 
20615c5460e9Seschrock 		if (size % blocksize != 0) {
2062e9dbad6fSeschrock 			nvlist_free(props);
206399653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2064e9dbad6fSeschrock 			    "volume size must be a multiple of volume block "
2065e9dbad6fSeschrock 			    "size"));
2066e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
20675c5460e9Seschrock 		}
2068fa9e4066Sahrens 	}
2069fa9e4066Sahrens 
2070e9dbad6fSeschrock 	if (props &&
2071e9dbad6fSeschrock 	    zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0)
2072e9dbad6fSeschrock 		return (-1);
2073e9dbad6fSeschrock 	nvlist_free(props);
2074e9dbad6fSeschrock 
2075fa9e4066Sahrens 	/* create the dataset */
207699653d4eSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
2077fa9e4066Sahrens 
2078fa9e4066Sahrens 	if (ret == 0 && type == ZFS_TYPE_VOLUME)
207999653d4eSeschrock 		ret = zvol_create_link(hdl, path);
2080fa9e4066Sahrens 
2081e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2082e9dbad6fSeschrock 
2083fa9e4066Sahrens 	/* check for failure */
2084fa9e4066Sahrens 	if (ret != 0) {
2085fa9e4066Sahrens 		char parent[ZFS_MAXNAMELEN];
2086fa9e4066Sahrens 		(void) parent_name(path, parent, sizeof (parent));
2087fa9e4066Sahrens 
2088fa9e4066Sahrens 		switch (errno) {
2089fa9e4066Sahrens 		case ENOENT:
209099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
209199653d4eSeschrock 			    "no such parent '%s'"), parent);
209299653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2093fa9e4066Sahrens 
2094fa9e4066Sahrens 		case EINVAL:
209599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
209699653d4eSeschrock 			    "parent '%s' is not a filesysem"), parent);
209799653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2098fa9e4066Sahrens 
2099fa9e4066Sahrens 		case EDOM:
210099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2101e9dbad6fSeschrock 			    "volume block size must be power of 2 from "
2102e9dbad6fSeschrock 			    "%u to %uk"),
2103fa9e4066Sahrens 			    (uint_t)SPA_MINBLOCKSIZE,
2104fa9e4066Sahrens 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
210599653d4eSeschrock 
2106e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
210799653d4eSeschrock 
2108fa9e4066Sahrens #ifdef _ILP32
2109fa9e4066Sahrens 		case EOVERFLOW:
2110fa9e4066Sahrens 			/*
2111fa9e4066Sahrens 			 * This platform can't address a volume this big.
2112fa9e4066Sahrens 			 */
211399653d4eSeschrock 			if (type == ZFS_TYPE_VOLUME)
211499653d4eSeschrock 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
211599653d4eSeschrock 				    errbuf));
2116fa9e4066Sahrens #endif
211799653d4eSeschrock 			/* FALLTHROUGH */
2118fa9e4066Sahrens 		default:
211999653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2120fa9e4066Sahrens 		}
2121fa9e4066Sahrens 	}
2122fa9e4066Sahrens 
2123fa9e4066Sahrens 	return (0);
2124fa9e4066Sahrens }
2125fa9e4066Sahrens 
2126fa9e4066Sahrens /*
2127fa9e4066Sahrens  * Destroys the given dataset.  The caller must make sure that the filesystem
2128fa9e4066Sahrens  * isn't mounted, and that there are no active dependents.
2129fa9e4066Sahrens  */
2130fa9e4066Sahrens int
2131fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp)
2132fa9e4066Sahrens {
2133fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2134fa9e4066Sahrens 	int ret;
2135fa9e4066Sahrens 
2136fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2137fa9e4066Sahrens 
2138e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
213999653d4eSeschrock 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2140fa9e4066Sahrens 			return (-1);
2141fa9e4066Sahrens 
2142fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2143fa9e4066Sahrens 	} else {
2144fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2145fa9e4066Sahrens 	}
2146fa9e4066Sahrens 
214799653d4eSeschrock 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc);
21481d452cf5Sahrens 	if (ret != 0) {
214999653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
215099653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
215199653d4eSeschrock 		    zhp->zfs_name));
21521d452cf5Sahrens 	}
2153fa9e4066Sahrens 
2154fa9e4066Sahrens 	remove_mountpoint(zhp);
2155fa9e4066Sahrens 
2156fa9e4066Sahrens 	return (0);
2157fa9e4066Sahrens }
2158fa9e4066Sahrens 
21591d452cf5Sahrens struct destroydata {
21601d452cf5Sahrens 	char *snapname;
21611d452cf5Sahrens 	boolean_t gotone;
21621d452cf5Sahrens };
21631d452cf5Sahrens 
21641d452cf5Sahrens static int
21651d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg)
21661d452cf5Sahrens {
21671d452cf5Sahrens 	struct destroydata *dd = arg;
21681d452cf5Sahrens 	zfs_handle_t *szhp;
21691d452cf5Sahrens 	char name[ZFS_MAXNAMELEN];
21701d452cf5Sahrens 
2171e9dbad6fSeschrock 	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2172e9dbad6fSeschrock 	(void) strlcat(name, "@", sizeof (name));
2173e9dbad6fSeschrock 	(void) strlcat(name, dd->snapname, sizeof (name));
21741d452cf5Sahrens 
21751d452cf5Sahrens 	szhp = make_dataset_handle(zhp->zfs_hdl, name);
21761d452cf5Sahrens 	if (szhp) {
21771d452cf5Sahrens 		dd->gotone = B_TRUE;
21781d452cf5Sahrens 		zfs_close(szhp);
21791d452cf5Sahrens 	}
21801d452cf5Sahrens 
21811d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
21821d452cf5Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, name);
21831d452cf5Sahrens 		/*
21841d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
21851d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
21861d452cf5Sahrens 		 * the volumes.
21871d452cf5Sahrens 		 */
21881d452cf5Sahrens 	}
21891d452cf5Sahrens 
21901d452cf5Sahrens 	return (zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg));
21911d452cf5Sahrens }
21921d452cf5Sahrens 
21931d452cf5Sahrens /*
21941d452cf5Sahrens  * Destroys all snapshots with the given name in zhp & descendants.
21951d452cf5Sahrens  */
21961d452cf5Sahrens int
21971d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname)
21981d452cf5Sahrens {
21991d452cf5Sahrens 	zfs_cmd_t zc = { 0 };
22001d452cf5Sahrens 	int ret;
22011d452cf5Sahrens 	struct destroydata dd = { 0 };
22021d452cf5Sahrens 
22031d452cf5Sahrens 	dd.snapname = snapname;
22041d452cf5Sahrens 	(void) zfs_remove_link_cb(zhp, &dd);
22051d452cf5Sahrens 
22061d452cf5Sahrens 	if (!dd.gotone) {
22071d452cf5Sahrens 		return (zfs_standard_error(zhp->zfs_hdl, ENOENT,
22081d452cf5Sahrens 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
22091d452cf5Sahrens 		    zhp->zfs_name, snapname));
22101d452cf5Sahrens 	}
22111d452cf5Sahrens 
22121d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2213e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
22141d452cf5Sahrens 
22151d452cf5Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc);
22161d452cf5Sahrens 	if (ret != 0) {
22171d452cf5Sahrens 		char errbuf[1024];
22181d452cf5Sahrens 
22191d452cf5Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
22201d452cf5Sahrens 		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
22211d452cf5Sahrens 
22221d452cf5Sahrens 		switch (errno) {
22231d452cf5Sahrens 		case EEXIST:
22241d452cf5Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
22251d452cf5Sahrens 			    "snapshot is cloned"));
22261d452cf5Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
22271d452cf5Sahrens 
22281d452cf5Sahrens 		default:
22291d452cf5Sahrens 			return (zfs_standard_error(zhp->zfs_hdl, errno,
22301d452cf5Sahrens 			    errbuf));
22311d452cf5Sahrens 		}
22321d452cf5Sahrens 	}
22331d452cf5Sahrens 
22341d452cf5Sahrens 	return (0);
22351d452cf5Sahrens }
22361d452cf5Sahrens 
2237fa9e4066Sahrens /*
2238fa9e4066Sahrens  * Clones the given dataset.  The target must be of the same type as the source.
2239fa9e4066Sahrens  */
2240fa9e4066Sahrens int
2241e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
2242fa9e4066Sahrens {
2243fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2244fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2245fa9e4066Sahrens 	int ret;
224699653d4eSeschrock 	char errbuf[1024];
224799653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
2248e9dbad6fSeschrock 	zfs_type_t type;
2249e9dbad6fSeschrock 	uint64_t zoned;
2250fa9e4066Sahrens 
2251fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
2252fa9e4066Sahrens 
225399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
225499653d4eSeschrock 	    "cannot create '%s'"), target);
225599653d4eSeschrock 
2256fa9e4066Sahrens 	/* validate the target name */
225799653d4eSeschrock 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM))
225899653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2259fa9e4066Sahrens 
2260fa9e4066Sahrens 	/* validate parents exist */
2261e9dbad6fSeschrock 	if (check_parents(hdl, target, &zoned) != 0)
2262fa9e4066Sahrens 		return (-1);
2263fa9e4066Sahrens 
2264fa9e4066Sahrens 	(void) parent_name(target, parent, sizeof (parent));
2265fa9e4066Sahrens 
2266fa9e4066Sahrens 	/* do the clone */
2267e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2268fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
22695f8e1617Snn 		type = ZFS_TYPE_VOLUME;
2270e9dbad6fSeschrock 	} else {
2271fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
22725f8e1617Snn 		type = ZFS_TYPE_FILESYSTEM;
2273e9dbad6fSeschrock 	}
2274e9dbad6fSeschrock 
2275e9dbad6fSeschrock 	if (props) {
2276e9dbad6fSeschrock 		if ((props = zfs_validate_properties(hdl, type, props, zoned,
2277e9dbad6fSeschrock 		    zhp, errbuf)) == NULL)
2278e9dbad6fSeschrock 			return (-1);
2279e9dbad6fSeschrock 
2280e9dbad6fSeschrock 		if (zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) {
2281e9dbad6fSeschrock 			nvlist_free(props);
2282e9dbad6fSeschrock 			return (-1);
2283e9dbad6fSeschrock 		}
2284e9dbad6fSeschrock 
2285e9dbad6fSeschrock 		nvlist_free(props);
2286e9dbad6fSeschrock 	}
2287fa9e4066Sahrens 
2288fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
2289e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
229099653d4eSeschrock 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
2291fa9e4066Sahrens 
2292e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2293e9dbad6fSeschrock 
2294fa9e4066Sahrens 	if (ret != 0) {
2295fa9e4066Sahrens 		switch (errno) {
2296fa9e4066Sahrens 
2297fa9e4066Sahrens 		case ENOENT:
2298fa9e4066Sahrens 			/*
2299fa9e4066Sahrens 			 * The parent doesn't exist.  We should have caught this
2300fa9e4066Sahrens 			 * above, but there may a race condition that has since
2301fa9e4066Sahrens 			 * destroyed the parent.
2302fa9e4066Sahrens 			 *
2303fa9e4066Sahrens 			 * At this point, we don't know whether it's the source
2304fa9e4066Sahrens 			 * that doesn't exist anymore, or whether the target
2305fa9e4066Sahrens 			 * dataset doesn't exist.
2306fa9e4066Sahrens 			 */
230799653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
230899653d4eSeschrock 			    "no such parent '%s'"), parent);
230999653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
2310fa9e4066Sahrens 
231199653d4eSeschrock 		case EXDEV:
231299653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
231399653d4eSeschrock 			    "source and target pools differ"));
231499653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
231599653d4eSeschrock 			    errbuf));
2316fa9e4066Sahrens 
231799653d4eSeschrock 		default:
231899653d4eSeschrock 			return (zfs_standard_error(zhp->zfs_hdl, errno,
231999653d4eSeschrock 			    errbuf));
232099653d4eSeschrock 		}
2321e9dbad6fSeschrock 	} else if (ZFS_IS_VOLUME(zhp)) {
232299653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, target);
232399653d4eSeschrock 	}
2324fa9e4066Sahrens 
232599653d4eSeschrock 	return (ret);
232699653d4eSeschrock }
232799653d4eSeschrock 
232899653d4eSeschrock typedef struct promote_data {
232999653d4eSeschrock 	char cb_mountpoint[MAXPATHLEN];
233099653d4eSeschrock 	const char *cb_target;
233199653d4eSeschrock 	const char *cb_errbuf;
233299653d4eSeschrock 	uint64_t cb_pivot_txg;
233399653d4eSeschrock } promote_data_t;
233499653d4eSeschrock 
233599653d4eSeschrock static int
233699653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data)
233799653d4eSeschrock {
233899653d4eSeschrock 	promote_data_t *pd = data;
233999653d4eSeschrock 	zfs_handle_t *szhp;
234099653d4eSeschrock 	char snapname[MAXPATHLEN];
234199653d4eSeschrock 
234299653d4eSeschrock 	/* We don't care about snapshots after the pivot point */
234399653d4eSeschrock 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg)
234499653d4eSeschrock 		return (0);
234599653d4eSeschrock 
23460b69c2f0Sahrens 	/* Remove the device link if it's a zvol. */
2347e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
23480b69c2f0Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
234999653d4eSeschrock 
235099653d4eSeschrock 	/* Check for conflicting names */
2351e9dbad6fSeschrock 	(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
2352e9dbad6fSeschrock 	(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
235399653d4eSeschrock 	szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
235499653d4eSeschrock 	if (szhp != NULL) {
235599653d4eSeschrock 		zfs_close(szhp);
235699653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
235799653d4eSeschrock 		    "snapshot name '%s' from origin \n"
235899653d4eSeschrock 		    "conflicts with '%s' from target"),
235999653d4eSeschrock 		    zhp->zfs_name, snapname);
236099653d4eSeschrock 		return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf));
236199653d4eSeschrock 	}
236299653d4eSeschrock 	return (0);
236399653d4eSeschrock }
236499653d4eSeschrock 
23650b69c2f0Sahrens static int
23660b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data)
23670b69c2f0Sahrens {
23680b69c2f0Sahrens 	promote_data_t *pd = data;
23690b69c2f0Sahrens 
23700b69c2f0Sahrens 	/* We don't care about snapshots after the pivot point */
23710b69c2f0Sahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg)
23720b69c2f0Sahrens 		return (0);
23730b69c2f0Sahrens 
23740b69c2f0Sahrens 	/* Create the device link if it's a zvol. */
2375e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
23760b69c2f0Sahrens 		(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
23770b69c2f0Sahrens 
23780b69c2f0Sahrens 	return (0);
23790b69c2f0Sahrens }
23800b69c2f0Sahrens 
238199653d4eSeschrock /*
238299653d4eSeschrock  * Promotes the given clone fs to be the clone parent.
238399653d4eSeschrock  */
238499653d4eSeschrock int
238599653d4eSeschrock zfs_promote(zfs_handle_t *zhp)
238699653d4eSeschrock {
238799653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
238899653d4eSeschrock 	zfs_cmd_t zc = { 0 };
238999653d4eSeschrock 	char parent[MAXPATHLEN];
239099653d4eSeschrock 	char *cp;
239199653d4eSeschrock 	int ret;
239299653d4eSeschrock 	zfs_handle_t *pzhp;
239399653d4eSeschrock 	promote_data_t pd;
239499653d4eSeschrock 	char errbuf[1024];
239599653d4eSeschrock 
239699653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
239799653d4eSeschrock 	    "cannot promote '%s'"), zhp->zfs_name);
239899653d4eSeschrock 
239999653d4eSeschrock 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
240099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
240199653d4eSeschrock 		    "snapshots can not be promoted"));
240299653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
240399653d4eSeschrock 	}
240499653d4eSeschrock 
2405e9dbad6fSeschrock 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_clone_of, sizeof (parent));
240699653d4eSeschrock 	if (parent[0] == '\0') {
240799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
240899653d4eSeschrock 		    "not a cloned filesystem"));
240999653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
241099653d4eSeschrock 	}
241199653d4eSeschrock 	cp = strchr(parent, '@');
241299653d4eSeschrock 	*cp = '\0';
241399653d4eSeschrock 
241499653d4eSeschrock 	/* Walk the snapshots we will be moving */
241599653d4eSeschrock 	pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_clone_of, ZFS_TYPE_SNAPSHOT);
241699653d4eSeschrock 	if (pzhp == NULL)
241799653d4eSeschrock 		return (-1);
241899653d4eSeschrock 	pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
241999653d4eSeschrock 	zfs_close(pzhp);
242099653d4eSeschrock 	pd.cb_target = zhp->zfs_name;
242199653d4eSeschrock 	pd.cb_errbuf = errbuf;
242299653d4eSeschrock 	pzhp = zfs_open(hdl, parent, ZFS_TYPE_ANY);
242399653d4eSeschrock 	if (pzhp == NULL)
242499653d4eSeschrock 		return (-1);
242599653d4eSeschrock 	(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
242699653d4eSeschrock 	    sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
242799653d4eSeschrock 	ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
24280b69c2f0Sahrens 	if (ret != 0) {
24290b69c2f0Sahrens 		zfs_close(pzhp);
243099653d4eSeschrock 		return (-1);
24310b69c2f0Sahrens 	}
243299653d4eSeschrock 
243399653d4eSeschrock 	/* issue the ioctl */
2434e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of,
2435e9dbad6fSeschrock 	    sizeof (zc.zc_value));
243699653d4eSeschrock 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
243799653d4eSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc);
243899653d4eSeschrock 
243999653d4eSeschrock 	if (ret != 0) {
24400b69c2f0Sahrens 		int save_errno = errno;
24410b69c2f0Sahrens 
24420b69c2f0Sahrens 		(void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
24430b69c2f0Sahrens 		zfs_close(pzhp);
244499653d4eSeschrock 
24450b69c2f0Sahrens 		switch (save_errno) {
244699653d4eSeschrock 		case EEXIST:
2447fa9e4066Sahrens 			/*
244899653d4eSeschrock 			 * There is a conflicting snapshot name.  We
244999653d4eSeschrock 			 * should have caught this above, but they could
245099653d4eSeschrock 			 * have renamed something in the mean time.
2451fa9e4066Sahrens 			 */
245299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
245399653d4eSeschrock 			    "conflicting snapshot name from parent '%s'"),
245499653d4eSeschrock 			    parent);
245599653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2456fa9e4066Sahrens 
2457fa9e4066Sahrens 		default:
24580b69c2f0Sahrens 			return (zfs_standard_error(hdl, save_errno, errbuf));
2459fa9e4066Sahrens 		}
24600b69c2f0Sahrens 	} else {
24610b69c2f0Sahrens 		(void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
2462fa9e4066Sahrens 	}
2463fa9e4066Sahrens 
24640b69c2f0Sahrens 	zfs_close(pzhp);
2465fa9e4066Sahrens 	return (ret);
2466fa9e4066Sahrens }
2467fa9e4066Sahrens 
24681d452cf5Sahrens static int
24691d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
24701d452cf5Sahrens {
24711d452cf5Sahrens 	char *snapname = arg;
2472e9dbad6fSeschrock 	int ret;
24731d452cf5Sahrens 
24741d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
24751d452cf5Sahrens 		char name[MAXPATHLEN];
24761d452cf5Sahrens 
2477e9dbad6fSeschrock 		(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2478e9dbad6fSeschrock 		(void) strlcat(name, "@", sizeof (name));
2479e9dbad6fSeschrock 		(void) strlcat(name, snapname, sizeof (name));
24801d452cf5Sahrens 		(void) zvol_create_link(zhp->zfs_hdl, name);
24811d452cf5Sahrens 		/*
24821d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
24831d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
24841d452cf5Sahrens 		 * the volumes.
24851d452cf5Sahrens 		 */
24861d452cf5Sahrens 	}
2487e9dbad6fSeschrock 
2488e9dbad6fSeschrock 	ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname);
2489e9dbad6fSeschrock 
2490e9dbad6fSeschrock 	zfs_close(zhp);
2491e9dbad6fSeschrock 
2492e9dbad6fSeschrock 	return (ret);
24931d452cf5Sahrens }
24941d452cf5Sahrens 
2495fa9e4066Sahrens /*
2496fa9e4066Sahrens  * Takes a snapshot of the given dataset
2497fa9e4066Sahrens  */
2498fa9e4066Sahrens int
24991d452cf5Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
2500fa9e4066Sahrens {
2501fa9e4066Sahrens 	const char *delim;
2502fa9e4066Sahrens 	char *parent;
2503fa9e4066Sahrens 	zfs_handle_t *zhp;
2504fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2505fa9e4066Sahrens 	int ret;
250699653d4eSeschrock 	char errbuf[1024];
2507fa9e4066Sahrens 
250899653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
250999653d4eSeschrock 	    "cannot snapshot '%s'"), path);
251099653d4eSeschrock 
251199653d4eSeschrock 	/* validate the target name */
251299653d4eSeschrock 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT))
251399653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2514fa9e4066Sahrens 
2515fa9e4066Sahrens 	/* make sure the parent exists and is of the appropriate type */
25161d452cf5Sahrens 	delim = strchr(path, '@');
251799653d4eSeschrock 	if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL)
251899653d4eSeschrock 		return (-1);
2519fa9e4066Sahrens 	(void) strncpy(parent, path, delim - path);
2520fa9e4066Sahrens 	parent[delim - path] = '\0';
2521fa9e4066Sahrens 
252299653d4eSeschrock 	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
2523fa9e4066Sahrens 	    ZFS_TYPE_VOLUME)) == NULL) {
2524fa9e4066Sahrens 		free(parent);
2525fa9e4066Sahrens 		return (-1);
2526fa9e4066Sahrens 	}
2527fa9e4066Sahrens 
25281d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2529e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
25301d452cf5Sahrens 	zc.zc_cookie = recursive;
25311d452cf5Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc);
2532fa9e4066Sahrens 
25331d452cf5Sahrens 	/*
25341d452cf5Sahrens 	 * if it was recursive, the one that actually failed will be in
25351d452cf5Sahrens 	 * zc.zc_name.
25361d452cf5Sahrens 	 */
25371d452cf5Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2538e9dbad6fSeschrock 	    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
25391d452cf5Sahrens 	if (ret == 0 && recursive) {
25401d452cf5Sahrens 		(void) zfs_iter_filesystems(zhp,
25411d452cf5Sahrens 		    zfs_create_link_cb, (char *)delim+1);
25421d452cf5Sahrens 	}
2543fa9e4066Sahrens 	if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
254499653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, path);
25451d452cf5Sahrens 		if (ret != 0) {
254699653d4eSeschrock 			(void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY,
254799653d4eSeschrock 			    &zc);
25481d452cf5Sahrens 		}
2549fa9e4066Sahrens 	}
2550fa9e4066Sahrens 
255199653d4eSeschrock 	if (ret != 0)
255299653d4eSeschrock 		(void) zfs_standard_error(hdl, errno, errbuf);
2553fa9e4066Sahrens 
2554fa9e4066Sahrens 	free(parent);
2555fa9e4066Sahrens 	zfs_close(zhp);
2556fa9e4066Sahrens 
2557fa9e4066Sahrens 	return (ret);
2558fa9e4066Sahrens }
2559fa9e4066Sahrens 
2560fa9e4066Sahrens /*
2561fa9e4066Sahrens  * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
2562fa9e4066Sahrens  */
2563fa9e4066Sahrens int
2564f2a3c691Sahrens zfs_send(zfs_handle_t *zhp_to, zfs_handle_t *zhp_from)
2565fa9e4066Sahrens {
2566fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2567fa9e4066Sahrens 	int ret;
256899653d4eSeschrock 	char errbuf[1024];
256999653d4eSeschrock 	libzfs_handle_t *hdl = zhp_to->zfs_hdl;
257099653d4eSeschrock 
257199653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
257299653d4eSeschrock 	    "cannot send '%s'"), zhp_to->zfs_name);
2573fa9e4066Sahrens 
2574fa9e4066Sahrens 	/* do the ioctl() */
2575fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp_to->zfs_name, sizeof (zc.zc_name));
2576fa9e4066Sahrens 	if (zhp_from) {
2577e9dbad6fSeschrock 		(void) strlcpy(zc.zc_value, zhp_from->zfs_name,
2578fa9e4066Sahrens 		    sizeof (zc.zc_name));
2579fa9e4066Sahrens 	} else {
2580e9dbad6fSeschrock 		zc.zc_value[0] = '\0';
2581fa9e4066Sahrens 	}
2582fa9e4066Sahrens 	zc.zc_cookie = STDOUT_FILENO;
2583fa9e4066Sahrens 
258499653d4eSeschrock 	ret = ioctl(zhp_to->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc);
2585fa9e4066Sahrens 	if (ret != 0) {
2586fa9e4066Sahrens 		switch (errno) {
2587fa9e4066Sahrens 
2588fa9e4066Sahrens 		case EXDEV:
258999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
259099653d4eSeschrock 			    "not an ealier snapshot from the same fs"));
259199653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
2592fa9e4066Sahrens 
2593fa9e4066Sahrens 		case EDQUOT:
2594fa9e4066Sahrens 		case EFBIG:
2595fa9e4066Sahrens 		case EIO:
2596fa9e4066Sahrens 		case ENOLINK:
2597fa9e4066Sahrens 		case ENOSPC:
2598fa9e4066Sahrens 		case ENOSTR:
2599fa9e4066Sahrens 		case ENXIO:
2600fa9e4066Sahrens 		case EPIPE:
2601fa9e4066Sahrens 		case ERANGE:
2602fa9e4066Sahrens 		case EFAULT:
2603fa9e4066Sahrens 		case EROFS:
260499653d4eSeschrock 			zfs_error_aux(hdl, strerror(errno));
260599653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
2606fa9e4066Sahrens 
2607fa9e4066Sahrens 		default:
260899653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2609fa9e4066Sahrens 		}
2610fa9e4066Sahrens 	}
2611fa9e4066Sahrens 
2612fa9e4066Sahrens 	return (ret);
2613fa9e4066Sahrens }
2614fa9e4066Sahrens 
2615fa9e4066Sahrens /*
2616fa9e4066Sahrens  * Restores a backup of tosnap from stdin.
2617fa9e4066Sahrens  */
2618fa9e4066Sahrens int
261999653d4eSeschrock zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
262098579b20Snd     int verbose, int dryrun, boolean_t force)
2621fa9e4066Sahrens {
2622fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2623fa9e4066Sahrens 	time_t begin_time;
26249b4f025eSahrens 	int ioctl_err, err, bytes, size;
2625fa9e4066Sahrens 	char *cp;
2626fa9e4066Sahrens 	dmu_replay_record_t drr;
2627fa9e4066Sahrens 	struct drr_begin *drrb = &zc.zc_begin_record;
262899653d4eSeschrock 	char errbuf[1024];
262998579b20Snd 	prop_changelist_t *clp;
2630fa9e4066Sahrens 
2631fa9e4066Sahrens 	begin_time = time(NULL);
2632fa9e4066Sahrens 
263399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
263499653d4eSeschrock 	    "cannot receive"));
263599653d4eSeschrock 
2636fa9e4066Sahrens 	/* trim off snapname, if any */
2637e9dbad6fSeschrock 	(void) strlcpy(zc.zc_name, tosnap, sizeof (zc.zc_name));
2638fa9e4066Sahrens 	cp = strchr(zc.zc_name, '@');
2639fa9e4066Sahrens 	if (cp)
2640fa9e4066Sahrens 		*cp = '\0';
2641fa9e4066Sahrens 
2642fa9e4066Sahrens 	/* read in the BEGIN record */
2643fa9e4066Sahrens 	cp = (char *)&drr;
2644fa9e4066Sahrens 	bytes = 0;
2645fa9e4066Sahrens 	do {
26469b4f025eSahrens 		size = read(STDIN_FILENO, cp, sizeof (drr) - bytes);
26479b4f025eSahrens 		cp += size;
26489b4f025eSahrens 		bytes += size;
26499b4f025eSahrens 	} while (size > 0);
2650fa9e4066Sahrens 
26519b4f025eSahrens 	if (size < 0 || bytes != sizeof (drr)) {
265299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
265399653d4eSeschrock 		    "stream (failed to read first record)"));
265499653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2655fa9e4066Sahrens 	}
2656fa9e4066Sahrens 
2657fa9e4066Sahrens 	zc.zc_begin_record = drr.drr_u.drr_begin;
2658fa9e4066Sahrens 
2659fa9e4066Sahrens 	if (drrb->drr_magic != DMU_BACKUP_MAGIC &&
2660fa9e4066Sahrens 	    drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) {
266199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
266299653d4eSeschrock 		    "stream (bad magic number)"));
266399653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2664fa9e4066Sahrens 	}
2665fa9e4066Sahrens 
2666fa9e4066Sahrens 	if (drrb->drr_version != DMU_BACKUP_VERSION &&
2667fa9e4066Sahrens 	    drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) {
266899653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only version "
266999653d4eSeschrock 		    "0x%llx is supported (stream is version 0x%llx)"),
2670fa9e4066Sahrens 		    DMU_BACKUP_VERSION, drrb->drr_version);
267199653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2672fa9e4066Sahrens 	}
2673fa9e4066Sahrens 
2674fa9e4066Sahrens 	/*
2675fa9e4066Sahrens 	 * Determine name of destination snapshot.
2676fa9e4066Sahrens 	 */
2677e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, tosnap, sizeof (zc.zc_value));
2678fa9e4066Sahrens 	if (isprefix) {
2679fa9e4066Sahrens 		if (strchr(tosnap, '@') != NULL) {
268099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
268199653d4eSeschrock 			    "destination must be a filesystem"));
268299653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2683fa9e4066Sahrens 		}
2684fa9e4066Sahrens 
2685fa9e4066Sahrens 		cp = strchr(drr.drr_u.drr_begin.drr_toname, '/');
2686fa9e4066Sahrens 		if (cp == NULL)
2687fa9e4066Sahrens 			cp = drr.drr_u.drr_begin.drr_toname;
2688fa9e4066Sahrens 		else
2689fa9e4066Sahrens 			cp++;
2690fa9e4066Sahrens 
2691e9dbad6fSeschrock 		(void) strcat(zc.zc_value, "/");
2692e9dbad6fSeschrock 		(void) strcat(zc.zc_value, cp);
2693fa9e4066Sahrens 	} else if (strchr(tosnap, '@') == NULL) {
2694fa9e4066Sahrens 		/*
2695fa9e4066Sahrens 		 * they specified just a filesystem; tack on the
2696fa9e4066Sahrens 		 * snapname from the backup.
2697fa9e4066Sahrens 		 */
2698fa9e4066Sahrens 		cp = strchr(drr.drr_u.drr_begin.drr_toname, '@');
269999653d4eSeschrock 		if (cp == NULL || strlen(tosnap) + strlen(cp) >= MAXNAMELEN)
270099653d4eSeschrock 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2701e9dbad6fSeschrock 		(void) strcat(zc.zc_value, cp);
2702fa9e4066Sahrens 	}
2703fa9e4066Sahrens 
2704fa9e4066Sahrens 	if (drrb->drr_fromguid) {
2705fa9e4066Sahrens 		zfs_handle_t *h;
2706fa9e4066Sahrens 		/* incremental backup stream */
2707fa9e4066Sahrens 
2708fa9e4066Sahrens 		/* do the ioctl to the containing fs */
2709e9dbad6fSeschrock 		(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
2710fa9e4066Sahrens 		cp = strchr(zc.zc_name, '@');
2711fa9e4066Sahrens 		*cp = '\0';
2712fa9e4066Sahrens 
2713fa9e4066Sahrens 		/* make sure destination fs exists */
271499653d4eSeschrock 		h = zfs_open(hdl, zc.zc_name,
271599653d4eSeschrock 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
271699653d4eSeschrock 		if (h == NULL)
2717fa9e4066Sahrens 			return (-1);
27189b4f025eSahrens 		if (!dryrun) {
271998579b20Snd 			/*
272098579b20Snd 			 * We need to unmount all the dependents of the dataset
272198579b20Snd 			 * and the dataset itself. If it's a volume
272298579b20Snd 			 * then remove device link.
272398579b20Snd 			 */
27249b4f025eSahrens 			if (h->zfs_type == ZFS_TYPE_FILESYSTEM) {
272598579b20Snd 				clp = changelist_gather(h, ZFS_PROP_NAME, 0);
272698579b20Snd 				if (clp == NULL)
272798579b20Snd 					return (-1);
272898579b20Snd 				if (changelist_prefix(clp) != 0) {
272998579b20Snd 					changelist_free(clp);
273098579b20Snd 					return (-1);
273198579b20Snd 				}
27329b4f025eSahrens 			} else {
273399653d4eSeschrock 				(void) zvol_remove_link(hdl, h->zfs_name);
27349b4f025eSahrens 			}
27359b4f025eSahrens 		}
2736fa9e4066Sahrens 		zfs_close(h);
2737fa9e4066Sahrens 	} else {
2738fa9e4066Sahrens 		/* full backup stream */
2739fa9e4066Sahrens 
2740e9dbad6fSeschrock 		(void) strlcpy(zc.zc_name, zc.zc_value, sizeof (zc.zc_name));
27419b4f025eSahrens 
2742f2a3c691Sahrens 		/* make sure they aren't trying to receive into the root */
27439b4f025eSahrens 		if (strchr(zc.zc_name, '/') == NULL) {
2744fa9e4066Sahrens 			cp = strchr(zc.zc_name, '@');
2745fa9e4066Sahrens 			if (cp)
2746fa9e4066Sahrens 				*cp = '\0';
274799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
274899653d4eSeschrock 			    "destination '%s' already exists"), zc.zc_name);
274999653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2750fa9e4066Sahrens 		}
2751fa9e4066Sahrens 
2752fa9e4066Sahrens 		if (isprefix) {
2753fa9e4066Sahrens 			zfs_handle_t *h;
2754fa9e4066Sahrens 
27559b4f025eSahrens 			/* make sure prefix exists */
275699653d4eSeschrock 			h = zfs_open(hdl, tosnap, ZFS_TYPE_FILESYSTEM);
275799653d4eSeschrock 			if (h == NULL)
2758fa9e4066Sahrens 				return (-1);
2759ea8dc4b6Seschrock 			zfs_close(h);
2760fa9e4066Sahrens 
2761fa9e4066Sahrens 			/* create any necessary ancestors up to prefix */
27629b4f025eSahrens 			zc.zc_objset_type = DMU_OST_ZFS;
2763ea8dc4b6Seschrock 
27649b4f025eSahrens 			/*
27659b4f025eSahrens 			 * zc.zc_name is now the full name of the snap
2766ea8dc4b6Seschrock 			 * we're restoring into.  Attempt to create,
2767ea8dc4b6Seschrock 			 * mount, and share any ancestor filesystems, up
2768ea8dc4b6Seschrock 			 * to the one that was named.
27699b4f025eSahrens 			 */
2770ea8dc4b6Seschrock 			for (cp = zc.zc_name + strlen(tosnap) + 1;
2771ea8dc4b6Seschrock 			    cp = strchr(cp, '/'); *cp = '/', cp++) {
2772ea8dc4b6Seschrock 				const char *opname;
2773fa9e4066Sahrens 				*cp = '\0';
2774ea8dc4b6Seschrock 
277599653d4eSeschrock 				opname = dgettext(TEXT_DOMAIN, "create");
277699653d4eSeschrock 				if (zfs_create(hdl, zc.zc_name,
2777e9dbad6fSeschrock 				    ZFS_TYPE_FILESYSTEM, NULL) != 0) {
2778ea8dc4b6Seschrock 					if (errno == EEXIST)
2779ea8dc4b6Seschrock 						continue;
2780ea8dc4b6Seschrock 					goto ancestorerr;
2781fa9e4066Sahrens 				}
2782ea8dc4b6Seschrock 
278399653d4eSeschrock 				opname = dgettext(TEXT_DOMAIN, "open");
278499653d4eSeschrock 				h = zfs_open(hdl, zc.zc_name,
278599653d4eSeschrock 				    ZFS_TYPE_FILESYSTEM);
2786ea8dc4b6Seschrock 				if (h == NULL)
2787ea8dc4b6Seschrock 					goto ancestorerr;
2788ea8dc4b6Seschrock 
278999653d4eSeschrock 				opname = dgettext(TEXT_DOMAIN, "mount");
2790ea8dc4b6Seschrock 				if (zfs_mount(h, NULL, 0) != 0)
2791ea8dc4b6Seschrock 					goto ancestorerr;
2792ea8dc4b6Seschrock 
279399653d4eSeschrock 				opname = dgettext(TEXT_DOMAIN, "share");
2794ea8dc4b6Seschrock 				if (zfs_share(h) != 0)
2795ea8dc4b6Seschrock 					goto ancestorerr;
2796ea8dc4b6Seschrock 
2797ea8dc4b6Seschrock 				zfs_close(h);
2798ea8dc4b6Seschrock 
2799ea8dc4b6Seschrock 				continue;
2800ea8dc4b6Seschrock ancestorerr:
280199653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
280299653d4eSeschrock 				    "failed to %s ancestor '%s'"), opname,
280399653d4eSeschrock 				    zc.zc_name);
280499653d4eSeschrock 				return (zfs_error(hdl, EZFS_BADRESTORE,
280599653d4eSeschrock 				    errbuf));
2806fa9e4066Sahrens 			}
2807fa9e4066Sahrens 		}
28089b4f025eSahrens 
28099b4f025eSahrens 		/* Make sure destination fs does not exist */
28109b4f025eSahrens 		cp = strchr(zc.zc_name, '@');
28119b4f025eSahrens 		*cp = '\0';
281299653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
281399653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
281499653d4eSeschrock 			    "destination '%s' exists"), zc.zc_name);
281599653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
28169b4f025eSahrens 		}
28179b4f025eSahrens 
28189b4f025eSahrens 		/* Do the recvbackup ioctl to the fs's parent. */
28199b4f025eSahrens 		cp = strrchr(zc.zc_name, '/');
28209b4f025eSahrens 		*cp = '\0';
2821fa9e4066Sahrens 	}
2822fa9e4066Sahrens 
2823fa9e4066Sahrens 	zc.zc_cookie = STDIN_FILENO;
2824e9dbad6fSeschrock 	zc.zc_guid = force;
2825fa9e4066Sahrens 	if (verbose) {
2826f2a3c691Sahrens 		(void) printf("%s %s stream of %s into %s\n",
2827f2a3c691Sahrens 		    dryrun ? "would receive" : "receiving",
2828fa9e4066Sahrens 		    drrb->drr_fromguid ? "incremental" : "full",
2829fa9e4066Sahrens 		    drr.drr_u.drr_begin.drr_toname,
2830e9dbad6fSeschrock 		    zc.zc_value);
2831fa9e4066Sahrens 		(void) fflush(stdout);
2832fa9e4066Sahrens 	}
2833fa9e4066Sahrens 	if (dryrun)
2834fa9e4066Sahrens 		return (0);
283599653d4eSeschrock 	err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc);
28369b4f025eSahrens 	if (ioctl_err != 0) {
2837fa9e4066Sahrens 		switch (errno) {
2838fa9e4066Sahrens 		case ENODEV:
283999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
284099653d4eSeschrock 			    "most recent snapshot does not match incremental "
284199653d4eSeschrock 			    "source"));
284299653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
2843fa9e4066Sahrens 			break;
2844fa9e4066Sahrens 		case ETXTBSY:
284599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
284699653d4eSeschrock 			    "destination has been modified since most recent "
284799653d4eSeschrock 			    "snapshot"));
284899653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
2849fa9e4066Sahrens 			break;
2850fa9e4066Sahrens 		case EEXIST:
2851fa9e4066Sahrens 			if (drrb->drr_fromguid == 0) {
2852fa9e4066Sahrens 				/* it's the containing fs that exists */
2853e9dbad6fSeschrock 				cp = strchr(zc.zc_value, '@');
2854fa9e4066Sahrens 				*cp = '\0';
2855fa9e4066Sahrens 			}
285699653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
285799653d4eSeschrock 			    "destination already exists"));
285899653d4eSeschrock 			(void) zfs_error(hdl, EZFS_EXISTS, dgettext(TEXT_DOMAIN,
2859e9dbad6fSeschrock 			    "cannot restore to %s"), zc.zc_value);
2860fa9e4066Sahrens 			break;
2861fa9e4066Sahrens 		case EINVAL:
286299653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
28639b4f025eSahrens 			break;
2864ea8dc4b6Seschrock 		case ECKSUM:
286599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
286699653d4eSeschrock 			    "invalid stream (checksum mismatch)"));
286799653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
2868fa9e4066Sahrens 			break;
2869fa9e4066Sahrens 		default:
287099653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
2871fa9e4066Sahrens 		}
2872fa9e4066Sahrens 	}
2873fa9e4066Sahrens 
2874fa9e4066Sahrens 	/*
28759b4f025eSahrens 	 * Mount or recreate the /dev links for the target filesystem
28769b4f025eSahrens 	 * (if created, or if we tore them down to do an incremental
28779b4f025eSahrens 	 * restore), and the /dev links for the new snapshot (if
287898579b20Snd 	 * created). Also mount any children of the target filesystem
287998579b20Snd 	 * if we did an incremental receive.
2880fa9e4066Sahrens 	 */
2881e9dbad6fSeschrock 	cp = strchr(zc.zc_value, '@');
28829b4f025eSahrens 	if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) {
2883fa9e4066Sahrens 		zfs_handle_t *h;
2884fa9e4066Sahrens 
2885fa9e4066Sahrens 		*cp = '\0';
2886e9dbad6fSeschrock 		h = zfs_open(hdl, zc.zc_value,
2887fa9e4066Sahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
28889b4f025eSahrens 		*cp = '@';
2889fa9e4066Sahrens 		if (h) {
289098579b20Snd 			if (h->zfs_type == ZFS_TYPE_VOLUME) {
289199653d4eSeschrock 				err = zvol_create_link(hdl, h->zfs_name);
2892ea8dc4b6Seschrock 				if (err == 0 && ioctl_err == 0)
289399653d4eSeschrock 					err = zvol_create_link(hdl,
2894e9dbad6fSeschrock 					    zc.zc_value);
289598579b20Snd 			} else {
289698579b20Snd 				if (drrb->drr_fromguid) {
289798579b20Snd 					err = changelist_postfix(clp);
289898579b20Snd 					changelist_free(clp);
289998579b20Snd 				} else {
290098579b20Snd 					err = zfs_mount(h, NULL, 0);
290198579b20Snd 				}
29029b4f025eSahrens 			}
290398579b20Snd 		zfs_close(h);
2904fa9e4066Sahrens 		}
2905fa9e4066Sahrens 	}
2906fa9e4066Sahrens 
29079b4f025eSahrens 	if (err || ioctl_err)
29089b4f025eSahrens 		return (-1);
2909fa9e4066Sahrens 
2910fa9e4066Sahrens 	if (verbose) {
2911fa9e4066Sahrens 		char buf1[64];
2912fa9e4066Sahrens 		char buf2[64];
2913fa9e4066Sahrens 		uint64_t bytes = zc.zc_cookie;
2914fa9e4066Sahrens 		time_t delta = time(NULL) - begin_time;
2915fa9e4066Sahrens 		if (delta == 0)
2916fa9e4066Sahrens 			delta = 1;
2917fa9e4066Sahrens 		zfs_nicenum(bytes, buf1, sizeof (buf1));
2918fa9e4066Sahrens 		zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
2919fa9e4066Sahrens 
2920f2a3c691Sahrens 		(void) printf("received %sb stream in %lu seconds (%sb/sec)\n",
2921fa9e4066Sahrens 		    buf1, delta, buf2);
2922fa9e4066Sahrens 	}
292398579b20Snd 
2924fa9e4066Sahrens 	return (0);
2925fa9e4066Sahrens }
2926fa9e4066Sahrens 
2927fa9e4066Sahrens /*
2928b12a1c38Slling  * Destroy any more recent snapshots.  We invoke this callback on any dependents
2929b12a1c38Slling  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
2930b12a1c38Slling  * is a dependent and we should just destroy it without checking the transaction
2931b12a1c38Slling  * group.
2932fa9e4066Sahrens  */
2933b12a1c38Slling typedef struct rollback_data {
2934b12a1c38Slling 	const char	*cb_target;		/* the snapshot */
2935b12a1c38Slling 	uint64_t	cb_create;		/* creation time reference */
2936b12a1c38Slling 	prop_changelist_t *cb_clp;		/* changelist pointer */
2937b12a1c38Slling 	int		cb_error;
293899653d4eSeschrock 	boolean_t	cb_dependent;
2939b12a1c38Slling } rollback_data_t;
2940b12a1c38Slling 
2941b12a1c38Slling static int
2942b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data)
2943b12a1c38Slling {
2944b12a1c38Slling 	rollback_data_t *cbp = data;
2945b12a1c38Slling 
2946b12a1c38Slling 	if (!cbp->cb_dependent) {
2947b12a1c38Slling 		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
2948b12a1c38Slling 		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
2949b12a1c38Slling 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
2950b12a1c38Slling 		    cbp->cb_create) {
2951b12a1c38Slling 
295299653d4eSeschrock 			cbp->cb_dependent = B_TRUE;
29533bb79becSeschrock 			if (zfs_iter_dependents(zhp, B_FALSE, rollback_destroy,
29543bb79becSeschrock 			    cbp) != 0)
29553bb79becSeschrock 				cbp->cb_error = 1;
295699653d4eSeschrock 			cbp->cb_dependent = B_FALSE;
2957b12a1c38Slling 
2958b12a1c38Slling 			if (zfs_destroy(zhp) != 0)
2959b12a1c38Slling 				cbp->cb_error = 1;
2960b12a1c38Slling 			else
2961b12a1c38Slling 				changelist_remove(zhp, cbp->cb_clp);
2962b12a1c38Slling 		}
2963b12a1c38Slling 	} else {
2964b12a1c38Slling 		if (zfs_destroy(zhp) != 0)
2965b12a1c38Slling 			cbp->cb_error = 1;
2966b12a1c38Slling 		else
2967b12a1c38Slling 			changelist_remove(zhp, cbp->cb_clp);
2968b12a1c38Slling 	}
2969b12a1c38Slling 
2970b12a1c38Slling 	zfs_close(zhp);
2971b12a1c38Slling 	return (0);
2972b12a1c38Slling }
2973b12a1c38Slling 
2974b12a1c38Slling /*
2975b12a1c38Slling  * Rollback the dataset to its latest snapshot.
2976b12a1c38Slling  */
2977b12a1c38Slling static int
2978b12a1c38Slling do_rollback(zfs_handle_t *zhp)
2979fa9e4066Sahrens {
2980fa9e4066Sahrens 	int ret;
2981fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2982fa9e4066Sahrens 
2983fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
2984fa9e4066Sahrens 	    zhp->zfs_type == ZFS_TYPE_VOLUME);
2985fa9e4066Sahrens 
2986fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME &&
298799653d4eSeschrock 	    zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2988fa9e4066Sahrens 		return (-1);
2989fa9e4066Sahrens 
2990fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2991fa9e4066Sahrens 
2992e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
2993fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2994fa9e4066Sahrens 	else
2995fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2996fa9e4066Sahrens 
2997fa9e4066Sahrens 	/*
2998fa9e4066Sahrens 	 * We rely on the consumer to verify that there are no newer snapshots
2999fa9e4066Sahrens 	 * for the given dataset.  Given these constraints, we can simply pass
3000fa9e4066Sahrens 	 * the name on to the ioctl() call.  There is still an unlikely race
3001fa9e4066Sahrens 	 * condition where the user has taken a snapshot since we verified that
3002fa9e4066Sahrens 	 * this was the most recent.
3003fa9e4066Sahrens 	 */
300499653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK,
300599653d4eSeschrock 	    &zc)) != 0) {
300699653d4eSeschrock 		(void) zfs_standard_error(zhp->zfs_hdl, errno,
300799653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
300899653d4eSeschrock 		    zhp->zfs_name);
3009fa9e4066Sahrens 	} else if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
301099653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
3011fa9e4066Sahrens 	}
3012fa9e4066Sahrens 
3013fa9e4066Sahrens 	return (ret);
3014fa9e4066Sahrens }
3015fa9e4066Sahrens 
3016b12a1c38Slling /*
3017b12a1c38Slling  * Given a dataset, rollback to a specific snapshot, discarding any
3018b12a1c38Slling  * data changes since then and making it the active dataset.
3019b12a1c38Slling  *
3020b12a1c38Slling  * Any snapshots more recent than the target are destroyed, along with
3021b12a1c38Slling  * their dependents.
3022b12a1c38Slling  */
3023b12a1c38Slling int
3024b12a1c38Slling zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, int flag)
3025b12a1c38Slling {
3026b12a1c38Slling 	int ret;
3027b12a1c38Slling 	rollback_data_t cb = { 0 };
3028b12a1c38Slling 	prop_changelist_t *clp;
3029b12a1c38Slling 
3030b12a1c38Slling 	/*
3031b12a1c38Slling 	 * Unmount all dependendents of the dataset and the dataset itself.
3032b12a1c38Slling 	 * The list we need to gather is the same as for doing rename
3033b12a1c38Slling 	 */
3034b12a1c38Slling 	clp = changelist_gather(zhp, ZFS_PROP_NAME, flag ? MS_FORCE: 0);
3035b12a1c38Slling 	if (clp == NULL)
3036b12a1c38Slling 		return (-1);
3037b12a1c38Slling 
3038b12a1c38Slling 	if ((ret = changelist_prefix(clp)) != 0)
3039b12a1c38Slling 		goto out;
3040b12a1c38Slling 
3041b12a1c38Slling 	/*
3042b12a1c38Slling 	 * Destroy all recent snapshots and its dependends.
3043b12a1c38Slling 	 */
3044b12a1c38Slling 	cb.cb_target = snap->zfs_name;
3045b12a1c38Slling 	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3046b12a1c38Slling 	cb.cb_clp = clp;
3047b12a1c38Slling 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3048b12a1c38Slling 
3049b12a1c38Slling 	if ((ret = cb.cb_error) != 0) {
3050b12a1c38Slling 		(void) changelist_postfix(clp);
3051b12a1c38Slling 		goto out;
3052b12a1c38Slling 	}
3053b12a1c38Slling 
3054b12a1c38Slling 	/*
3055b12a1c38Slling 	 * Now that we have verified that the snapshot is the latest,
3056b12a1c38Slling 	 * rollback to the given snapshot.
3057b12a1c38Slling 	 */
3058b12a1c38Slling 	ret = do_rollback(zhp);
3059b12a1c38Slling 
3060b12a1c38Slling 	if (ret != 0) {
3061b12a1c38Slling 		(void) changelist_postfix(clp);
3062b12a1c38Slling 		goto out;
3063b12a1c38Slling 	}
3064b12a1c38Slling 
3065b12a1c38Slling 	/*
3066b12a1c38Slling 	 * We only want to re-mount the filesystem if it was mounted in the
3067b12a1c38Slling 	 * first place.
3068b12a1c38Slling 	 */
3069b12a1c38Slling 	ret = changelist_postfix(clp);
3070b12a1c38Slling 
3071b12a1c38Slling out:
3072b12a1c38Slling 	changelist_free(clp);
3073b12a1c38Slling 	return (ret);
3074b12a1c38Slling }
3075b12a1c38Slling 
3076fa9e4066Sahrens /*
3077fa9e4066Sahrens  * Iterate over all dependents for a given dataset.  This includes both
3078fa9e4066Sahrens  * hierarchical dependents (children) and data dependents (snapshots and
3079fa9e4066Sahrens  * clones).  The bulk of the processing occurs in get_dependents() in
3080fa9e4066Sahrens  * libzfs_graph.c.
3081fa9e4066Sahrens  */
3082fa9e4066Sahrens int
30833bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
30843bb79becSeschrock     zfs_iter_f func, void *data)
3085fa9e4066Sahrens {
3086fa9e4066Sahrens 	char **dependents;
3087fa9e4066Sahrens 	size_t count;
3088fa9e4066Sahrens 	int i;
3089fa9e4066Sahrens 	zfs_handle_t *child;
3090fa9e4066Sahrens 	int ret = 0;
3091fa9e4066Sahrens 
30923bb79becSeschrock 	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
30933bb79becSeschrock 	    &dependents, &count) != 0)
30943bb79becSeschrock 		return (-1);
30953bb79becSeschrock 
3096fa9e4066Sahrens 	for (i = 0; i < count; i++) {
309799653d4eSeschrock 		if ((child = make_dataset_handle(zhp->zfs_hdl,
309899653d4eSeschrock 		    dependents[i])) == NULL)
3099fa9e4066Sahrens 			continue;
3100fa9e4066Sahrens 
3101fa9e4066Sahrens 		if ((ret = func(child, data)) != 0)
3102fa9e4066Sahrens 			break;
3103fa9e4066Sahrens 	}
3104fa9e4066Sahrens 
3105fa9e4066Sahrens 	for (i = 0; i < count; i++)
3106fa9e4066Sahrens 		free(dependents[i]);
3107fa9e4066Sahrens 	free(dependents);
3108fa9e4066Sahrens 
3109fa9e4066Sahrens 	return (ret);
3110fa9e4066Sahrens }
3111fa9e4066Sahrens 
3112fa9e4066Sahrens /*
3113fa9e4066Sahrens  * Renames the given dataset.
3114fa9e4066Sahrens  */
3115fa9e4066Sahrens int
3116fa9e4066Sahrens zfs_rename(zfs_handle_t *zhp, const char *target)
3117fa9e4066Sahrens {
3118fa9e4066Sahrens 	int ret;
3119fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3120fa9e4066Sahrens 	char *delim;
3121fa9e4066Sahrens 	prop_changelist_t *cl;
3122fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
312399653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
312499653d4eSeschrock 	char errbuf[1024];
3125fa9e4066Sahrens 
3126fa9e4066Sahrens 	/* if we have the same exact name, just return success */
3127fa9e4066Sahrens 	if (strcmp(zhp->zfs_name, target) == 0)
3128fa9e4066Sahrens 		return (0);
3129fa9e4066Sahrens 
313099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
313199653d4eSeschrock 	    "cannot rename to '%s'"), target);
313299653d4eSeschrock 
3133fa9e4066Sahrens 	/*
3134fa9e4066Sahrens 	 * Make sure the target name is valid
3135fa9e4066Sahrens 	 */
3136fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
313798579b20Snd 		if ((strchr(target, '@') == NULL) ||
313898579b20Snd 		    *target == '@') {
313998579b20Snd 			/*
314098579b20Snd 			 * Snapshot target name is abbreviated,
314198579b20Snd 			 * reconstruct full dataset name
314298579b20Snd 			 */
314398579b20Snd 			(void) strlcpy(parent, zhp->zfs_name,
314498579b20Snd 			    sizeof (parent));
314598579b20Snd 			delim = strchr(parent, '@');
314698579b20Snd 			if (strchr(target, '@') == NULL)
314798579b20Snd 				*(++delim) = '\0';
314898579b20Snd 			else
314998579b20Snd 				*delim = '\0';
315098579b20Snd 			(void) strlcat(parent, target, sizeof (parent));
315198579b20Snd 			target = parent;
315298579b20Snd 		} else {
315398579b20Snd 			/*
315498579b20Snd 			 * Make sure we're renaming within the same dataset.
315598579b20Snd 			 */
315698579b20Snd 			delim = strchr(target, '@');
315798579b20Snd 			if (strncmp(zhp->zfs_name, target, delim - target)
315898579b20Snd 			    != 0 || zhp->zfs_name[delim - target] != '@') {
315998579b20Snd 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
316098579b20Snd 				    "snapshots must be part of same "
316198579b20Snd 				    "dataset"));
316298579b20Snd 				return (zfs_error(hdl, EZFS_CROSSTARGET,
316398579b20Snd 					    errbuf));
316498579b20Snd 			}
3165fa9e4066Sahrens 		}
316698579b20Snd 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
316798579b20Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3168fa9e4066Sahrens 	} else {
316998579b20Snd 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
317098579b20Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3171e9dbad6fSeschrock 		uint64_t unused;
3172e9dbad6fSeschrock 
3173fa9e4066Sahrens 		/* validate parents */
3174e9dbad6fSeschrock 		if (check_parents(hdl, target, &unused) != 0)
3175fa9e4066Sahrens 			return (-1);
3176fa9e4066Sahrens 
3177fa9e4066Sahrens 		(void) parent_name(target, parent, sizeof (parent));
3178fa9e4066Sahrens 
3179fa9e4066Sahrens 		/* make sure we're in the same pool */
3180fa9e4066Sahrens 		verify((delim = strchr(target, '/')) != NULL);
3181fa9e4066Sahrens 		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3182fa9e4066Sahrens 		    zhp->zfs_name[delim - target] != '/') {
318399653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
318499653d4eSeschrock 			    "datasets must be within same pool"));
318599653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3186fa9e4066Sahrens 		}
3187f2fdf992Snd 
3188f2fdf992Snd 		/* new name cannot be a child of the current dataset name */
3189f2fdf992Snd 		if (strncmp(parent, zhp->zfs_name,
3190f2fdf992Snd 			    strlen(zhp->zfs_name)) == 0) {
3191f2fdf992Snd 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3192f2fdf992Snd 			    "New dataset name cannot be a descendent of "
3193f2fdf992Snd 			    "current dataset name"));
3194f2fdf992Snd 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3195f2fdf992Snd 		}
3196fa9e4066Sahrens 	}
3197fa9e4066Sahrens 
319899653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
319999653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
320099653d4eSeschrock 
3201fa9e4066Sahrens 	if (getzoneid() == GLOBAL_ZONEID &&
3202fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
320399653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
320499653d4eSeschrock 		    "dataset is used in a non-global zone"));
320599653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3206fa9e4066Sahrens 	}
3207fa9e4066Sahrens 
3208fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
320999653d4eSeschrock 		return (-1);
3210fa9e4066Sahrens 
3211fa9e4066Sahrens 	if (changelist_haszonedchild(cl)) {
321299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
321399653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
321499653d4eSeschrock 		    "in a non-global zone"));
3215e9dbad6fSeschrock 		(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3216fa9e4066Sahrens 		goto error;
3217fa9e4066Sahrens 	}
3218fa9e4066Sahrens 
3219fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
3220fa9e4066Sahrens 		goto error;
3221fa9e4066Sahrens 
3222e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
3223fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
3224fa9e4066Sahrens 	else
3225fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3226fa9e4066Sahrens 
322798579b20Snd 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3228e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
322998579b20Snd 
323099653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
323199653d4eSeschrock 		(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3232fa9e4066Sahrens 
3233fa9e4066Sahrens 		/*
3234fa9e4066Sahrens 		 * On failure, we still want to remount any filesystems that
3235fa9e4066Sahrens 		 * were previously mounted, so we don't alter the system state.
3236fa9e4066Sahrens 		 */
3237fa9e4066Sahrens 		(void) changelist_postfix(cl);
3238fa9e4066Sahrens 	} else {
3239fa9e4066Sahrens 		changelist_rename(cl, zfs_get_name(zhp), target);
3240fa9e4066Sahrens 
3241fa9e4066Sahrens 		ret = changelist_postfix(cl);
3242fa9e4066Sahrens 	}
3243fa9e4066Sahrens 
3244fa9e4066Sahrens error:
3245fa9e4066Sahrens 	changelist_free(cl);
3246fa9e4066Sahrens 	return (ret);
3247fa9e4066Sahrens }
3248fa9e4066Sahrens 
3249fa9e4066Sahrens /*
3250fa9e4066Sahrens  * Given a zvol dataset, issue the ioctl to create the appropriate minor node,
3251fa9e4066Sahrens  * poke devfsadm to create the /dev link, and then wait for the link to appear.
3252fa9e4066Sahrens  */
3253fa9e4066Sahrens int
325499653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
3255fa9e4066Sahrens {
3256fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
325799653d4eSeschrock 	di_devlink_handle_t dhdl;
3258fa9e4066Sahrens 
3259fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3260fa9e4066Sahrens 
3261fa9e4066Sahrens 	/*
3262fa9e4066Sahrens 	 * Issue the appropriate ioctl.
3263fa9e4066Sahrens 	 */
326499653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
3265fa9e4066Sahrens 		switch (errno) {
3266fa9e4066Sahrens 		case EEXIST:
3267fa9e4066Sahrens 			/*
3268fa9e4066Sahrens 			 * Silently ignore the case where the link already
3269fa9e4066Sahrens 			 * exists.  This allows 'zfs volinit' to be run multiple
3270fa9e4066Sahrens 			 * times without errors.
3271fa9e4066Sahrens 			 */
3272fa9e4066Sahrens 			return (0);
3273fa9e4066Sahrens 
3274fa9e4066Sahrens 		default:
327599653d4eSeschrock 			return (zfs_standard_error(hdl, errno,
327699653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot create device links "
327799653d4eSeschrock 			    "for '%s'"), dataset));
3278fa9e4066Sahrens 		}
3279fa9e4066Sahrens 	}
3280fa9e4066Sahrens 
3281fa9e4066Sahrens 	/*
3282fa9e4066Sahrens 	 * Call devfsadm and wait for the links to magically appear.
3283fa9e4066Sahrens 	 */
328499653d4eSeschrock 	if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) {
328599653d4eSeschrock 		zfs_error_aux(hdl, strerror(errno));
328699653d4eSeschrock 		(void) zfs_error(hdl, EZFS_DEVLINKS,
328799653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot create device links "
328899653d4eSeschrock 		    "for '%s'"), dataset);
328999653d4eSeschrock 		(void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
3290fa9e4066Sahrens 		return (-1);
3291fa9e4066Sahrens 	} else {
329299653d4eSeschrock 		(void) di_devlink_fini(&dhdl);
3293fa9e4066Sahrens 	}
3294fa9e4066Sahrens 
3295fa9e4066Sahrens 	return (0);
3296fa9e4066Sahrens }
3297fa9e4066Sahrens 
3298fa9e4066Sahrens /*
3299fa9e4066Sahrens  * Remove a minor node for the given zvol and the associated /dev links.
3300fa9e4066Sahrens  */
3301fa9e4066Sahrens int
330299653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
3303fa9e4066Sahrens {
3304fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3305fa9e4066Sahrens 
3306fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3307fa9e4066Sahrens 
330899653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
3309fa9e4066Sahrens 		switch (errno) {
3310fa9e4066Sahrens 		case ENXIO:
3311fa9e4066Sahrens 			/*
3312fa9e4066Sahrens 			 * Silently ignore the case where the link no longer
3313fa9e4066Sahrens 			 * exists, so that 'zfs volfini' can be run multiple
3314fa9e4066Sahrens 			 * times without errors.
3315fa9e4066Sahrens 			 */
3316fa9e4066Sahrens 			return (0);
3317fa9e4066Sahrens 
3318fa9e4066Sahrens 		default:
331999653d4eSeschrock 			return (zfs_standard_error(hdl, errno,
332099653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot remove device "
332199653d4eSeschrock 			    "links for '%s'"), dataset));
3322fa9e4066Sahrens 		}
3323fa9e4066Sahrens 	}
3324fa9e4066Sahrens 
3325fa9e4066Sahrens 	return (0);
3326fa9e4066Sahrens }
3327e9dbad6fSeschrock 
3328e9dbad6fSeschrock nvlist_t *
3329e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp)
3330e9dbad6fSeschrock {
3331e9dbad6fSeschrock 	return (zhp->zfs_user_props);
3332e9dbad6fSeschrock }
3333e9dbad6fSeschrock 
3334e9dbad6fSeschrock /*
3335e9dbad6fSeschrock  * Given a comma-separated list of properties, contruct a property list
3336e9dbad6fSeschrock  * containing both user-defined and native properties.  This function will
3337e9dbad6fSeschrock  * return a NULL list if 'all' is specified, which can later be expanded on a
3338e9dbad6fSeschrock  * per-dataset basis by zfs_expand_proplist().
3339e9dbad6fSeschrock  */
3340e9dbad6fSeschrock int
3341e9dbad6fSeschrock zfs_get_proplist(libzfs_handle_t *hdl, char *fields, zfs_proplist_t **listp)
3342e9dbad6fSeschrock {
3343e9dbad6fSeschrock 	int i;
3344e9dbad6fSeschrock 	size_t len;
3345e9dbad6fSeschrock 	char *s, *p;
3346e9dbad6fSeschrock 	char c;
3347e9dbad6fSeschrock 	zfs_prop_t prop;
3348e9dbad6fSeschrock 	zfs_proplist_t *entry;
3349e9dbad6fSeschrock 	zfs_proplist_t **last;
3350e9dbad6fSeschrock 
3351e9dbad6fSeschrock 	*listp = NULL;
3352e9dbad6fSeschrock 	last = listp;
3353e9dbad6fSeschrock 
3354e9dbad6fSeschrock 	/*
3355e9dbad6fSeschrock 	 * If 'all' is specified, return a NULL list.
3356e9dbad6fSeschrock 	 */
3357e9dbad6fSeschrock 	if (strcmp(fields, "all") == 0)
3358e9dbad6fSeschrock 		return (0);
3359e9dbad6fSeschrock 
3360e9dbad6fSeschrock 	/*
3361e9dbad6fSeschrock 	 * If no fields were specified, return an error.
3362e9dbad6fSeschrock 	 */
3363e9dbad6fSeschrock 	if (fields[0] == '\0') {
3364e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3365e9dbad6fSeschrock 		    "no properties specified"));
3366e9dbad6fSeschrock 		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
3367e9dbad6fSeschrock 		    "bad property list")));
3368e9dbad6fSeschrock 	}
3369e9dbad6fSeschrock 
3370e9dbad6fSeschrock 	/*
3371e9dbad6fSeschrock 	 * It would be nice to use getsubopt() here, but the inclusion of column
3372e9dbad6fSeschrock 	 * aliases makes this more effort than it's worth.
3373e9dbad6fSeschrock 	 */
3374e9dbad6fSeschrock 	s = fields;
3375e9dbad6fSeschrock 	while (*s != '\0') {
3376e9dbad6fSeschrock 		if ((p = strchr(s, ',')) == NULL) {
3377e9dbad6fSeschrock 			len = strlen(s);
3378e9dbad6fSeschrock 			p = s + len;
3379e9dbad6fSeschrock 		} else {
3380e9dbad6fSeschrock 			len = p - s;
3381e9dbad6fSeschrock 		}
3382e9dbad6fSeschrock 
3383e9dbad6fSeschrock 		/*
3384e9dbad6fSeschrock 		 * Check for empty options.
3385e9dbad6fSeschrock 		 */
3386e9dbad6fSeschrock 		if (len == 0) {
3387e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3388e9dbad6fSeschrock 			    "empty property name"));
3389e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP,
3390e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "bad property list")));
3391e9dbad6fSeschrock 		}
3392e9dbad6fSeschrock 
3393e9dbad6fSeschrock 		/*
3394e9dbad6fSeschrock 		 * Check all regular property names.
3395e9dbad6fSeschrock 		 */
3396e9dbad6fSeschrock 		c = s[len];
3397e9dbad6fSeschrock 		s[len] = '\0';
3398e9dbad6fSeschrock 		for (i = 0; i < ZFS_NPROP_ALL; i++) {
3399e9dbad6fSeschrock 			if ((prop = zfs_name_to_prop(s)) != ZFS_PROP_INVAL)
3400e9dbad6fSeschrock 				break;
3401e9dbad6fSeschrock 		}
3402e9dbad6fSeschrock 
3403e9dbad6fSeschrock 		/*
3404e9dbad6fSeschrock 		 * If no column is specified, and this isn't a user property,
3405e9dbad6fSeschrock 		 * return failure.
3406e9dbad6fSeschrock 		 */
3407e9dbad6fSeschrock 		if (i == ZFS_NPROP_ALL && !zfs_prop_user(s)) {
3408e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3409e9dbad6fSeschrock 			    "invalid property '%s'"), s);
3410e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP,
3411e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "bad property list")));
3412e9dbad6fSeschrock 		}
3413e9dbad6fSeschrock 
3414e9dbad6fSeschrock 		if ((entry = zfs_alloc(hdl, sizeof (zfs_proplist_t))) == NULL)
3415e9dbad6fSeschrock 			return (-1);
3416e9dbad6fSeschrock 
3417e9dbad6fSeschrock 		entry->pl_prop = prop;
3418e9dbad6fSeschrock 		if (prop == ZFS_PROP_INVAL) {
3419e9dbad6fSeschrock 			if ((entry->pl_user_prop =
3420e9dbad6fSeschrock 			    zfs_strdup(hdl, s)) == NULL) {
3421e9dbad6fSeschrock 				free(entry);
3422e9dbad6fSeschrock 				return (-1);
3423e9dbad6fSeschrock 			}
3424e9dbad6fSeschrock 			entry->pl_width = strlen(s);
3425e9dbad6fSeschrock 		} else {
3426e9dbad6fSeschrock 			entry->pl_width = zfs_prop_width(prop,
3427e9dbad6fSeschrock 			    &entry->pl_fixed);
3428e9dbad6fSeschrock 		}
3429e9dbad6fSeschrock 
3430e9dbad6fSeschrock 		*last = entry;
3431e9dbad6fSeschrock 		last = &entry->pl_next;
3432e9dbad6fSeschrock 
3433e9dbad6fSeschrock 		s = p;
3434e9dbad6fSeschrock 		if (c == ',')
3435e9dbad6fSeschrock 			s++;
3436e9dbad6fSeschrock 	}
3437e9dbad6fSeschrock 
3438e9dbad6fSeschrock 	return (0);
3439e9dbad6fSeschrock }
3440e9dbad6fSeschrock 
3441e9dbad6fSeschrock void
3442e9dbad6fSeschrock zfs_free_proplist(zfs_proplist_t *pl)
3443e9dbad6fSeschrock {
3444e9dbad6fSeschrock 	zfs_proplist_t *next;
3445e9dbad6fSeschrock 
3446e9dbad6fSeschrock 	while (pl != NULL) {
3447e9dbad6fSeschrock 		next = pl->pl_next;
3448e9dbad6fSeschrock 		free(pl->pl_user_prop);
3449e9dbad6fSeschrock 		free(pl);
3450e9dbad6fSeschrock 		pl = next;
3451e9dbad6fSeschrock 	}
3452e9dbad6fSeschrock }
3453e9dbad6fSeschrock 
3454e9dbad6fSeschrock /*
3455e9dbad6fSeschrock  * This function is used by 'zfs list' to determine the exact set of columns to
3456e9dbad6fSeschrock  * display, and their maximum widths.  This does two main things:
3457e9dbad6fSeschrock  *
3458e9dbad6fSeschrock  * 	- If this is a list of all properties, then expand the list to include
3459e9dbad6fSeschrock  *	  all native properties, and set a flag so that for each dataset we look
3460e9dbad6fSeschrock  *	  for new unique user properties and add them to the list.
3461e9dbad6fSeschrock  *
3462e9dbad6fSeschrock  * 	- For non fixed-width properties, keep track of the maximum width seen
3463e9dbad6fSeschrock  *	  so that we can size the column appropriately.
3464e9dbad6fSeschrock  */
3465e9dbad6fSeschrock int
3466e9dbad6fSeschrock zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
3467e9dbad6fSeschrock {
3468e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
3469e9dbad6fSeschrock 	zfs_prop_t prop;
3470e9dbad6fSeschrock 	zfs_proplist_t *entry;
3471e9dbad6fSeschrock 	zfs_proplist_t **last, **start;
3472e9dbad6fSeschrock 	nvlist_t *userprops, *propval;
3473e9dbad6fSeschrock 	nvpair_t *elem;
3474e9dbad6fSeschrock 	char *strval;
3475e9dbad6fSeschrock 	char buf[ZFS_MAXPROPLEN];
3476e9dbad6fSeschrock 
3477e9dbad6fSeschrock 	if (*plp == NULL) {
3478e9dbad6fSeschrock 		/*
3479e9dbad6fSeschrock 		 * If this is the very first time we've been called for an 'all'
3480e9dbad6fSeschrock 		 * specification, expand the list to include all native
3481e9dbad6fSeschrock 		 * properties.
3482e9dbad6fSeschrock 		 */
3483e9dbad6fSeschrock 		last = plp;
3484e9dbad6fSeschrock 		for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) {
3485e9dbad6fSeschrock 			if ((entry = zfs_alloc(hdl,
3486e9dbad6fSeschrock 			    sizeof (zfs_proplist_t))) == NULL)
3487e9dbad6fSeschrock 				return (-1);
3488e9dbad6fSeschrock 
3489e9dbad6fSeschrock 			entry->pl_prop = prop;
3490e9dbad6fSeschrock 			entry->pl_width = zfs_prop_width(prop,
3491e9dbad6fSeschrock 			    &entry->pl_fixed);
3492e9dbad6fSeschrock 			entry->pl_all = B_TRUE;
3493e9dbad6fSeschrock 
3494e9dbad6fSeschrock 			*last = entry;
3495e9dbad6fSeschrock 			last = &entry->pl_next;
3496e9dbad6fSeschrock 		}
3497e9dbad6fSeschrock 
3498e9dbad6fSeschrock 		/*
3499e9dbad6fSeschrock 		 * Add 'name' to the beginning of the list, which is handled
3500e9dbad6fSeschrock 		 * specially.
3501e9dbad6fSeschrock 		 */
3502e9dbad6fSeschrock 		if ((entry = zfs_alloc(hdl,
3503e9dbad6fSeschrock 		    sizeof (zfs_proplist_t))) == NULL)
3504e9dbad6fSeschrock 			return (-1);
3505e9dbad6fSeschrock 
3506e9dbad6fSeschrock 		entry->pl_prop = ZFS_PROP_NAME;
3507e9dbad6fSeschrock 		entry->pl_width = zfs_prop_width(ZFS_PROP_NAME,
3508e9dbad6fSeschrock 		    &entry->pl_fixed);
3509e9dbad6fSeschrock 		entry->pl_all = B_TRUE;
3510e9dbad6fSeschrock 		entry->pl_next = *plp;
3511e9dbad6fSeschrock 		*plp = entry;
3512e9dbad6fSeschrock 	}
3513e9dbad6fSeschrock 
3514e9dbad6fSeschrock 	userprops = zfs_get_user_props(zhp);
3515e9dbad6fSeschrock 
3516e9dbad6fSeschrock 	entry = *plp;
3517e9dbad6fSeschrock 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3518e9dbad6fSeschrock 		/*
3519e9dbad6fSeschrock 		 * Go through and add any user properties as necessary.  We
3520e9dbad6fSeschrock 		 * start by incrementing our list pointer to the first
3521e9dbad6fSeschrock 		 * non-native property.
3522e9dbad6fSeschrock 		 */
3523e9dbad6fSeschrock 		start = plp;
3524e9dbad6fSeschrock 		while (*start != NULL) {
3525e9dbad6fSeschrock 			if ((*start)->pl_prop == ZFS_PROP_INVAL)
3526e9dbad6fSeschrock 				break;
3527e9dbad6fSeschrock 			start = &(*start)->pl_next;
3528e9dbad6fSeschrock 		}
3529e9dbad6fSeschrock 
3530e9dbad6fSeschrock 		elem = NULL;
3531e9dbad6fSeschrock 		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3532e9dbad6fSeschrock 			/*
3533e9dbad6fSeschrock 			 * See if we've already found this property in our list.
3534e9dbad6fSeschrock 			 */
3535e9dbad6fSeschrock 			for (last = start; *last != NULL;
3536e9dbad6fSeschrock 			    last = &(*last)->pl_next) {
3537e9dbad6fSeschrock 				if (strcmp((*last)->pl_user_prop,
3538e9dbad6fSeschrock 				    nvpair_name(elem)) == 0)
3539e9dbad6fSeschrock 					break;
3540e9dbad6fSeschrock 			}
3541e9dbad6fSeschrock 
3542e9dbad6fSeschrock 			if (*last == NULL) {
3543e9dbad6fSeschrock 				if ((entry = zfs_alloc(hdl,
3544e9dbad6fSeschrock 				    sizeof (zfs_proplist_t))) == NULL ||
3545e9dbad6fSeschrock 				    ((entry->pl_user_prop = zfs_strdup(hdl,
3546e9dbad6fSeschrock 				    nvpair_name(elem)))) == NULL) {
3547e9dbad6fSeschrock 					free(entry);
3548e9dbad6fSeschrock 					return (-1);
3549e9dbad6fSeschrock 				}
3550e9dbad6fSeschrock 
3551e9dbad6fSeschrock 				entry->pl_prop = ZFS_PROP_INVAL;
3552e9dbad6fSeschrock 				entry->pl_width = strlen(nvpair_name(elem));
3553e9dbad6fSeschrock 				entry->pl_all = B_TRUE;
3554e9dbad6fSeschrock 				*last = entry;
3555e9dbad6fSeschrock 			}
3556e9dbad6fSeschrock 		}
3557e9dbad6fSeschrock 	}
3558e9dbad6fSeschrock 
3559e9dbad6fSeschrock 	/*
3560e9dbad6fSeschrock 	 * Now go through and check the width of any non-fixed columns
3561e9dbad6fSeschrock 	 */
3562e9dbad6fSeschrock 	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3563e9dbad6fSeschrock 		if (entry->pl_fixed)
3564e9dbad6fSeschrock 			continue;
3565e9dbad6fSeschrock 
3566e9dbad6fSeschrock 		if (entry->pl_prop != ZFS_PROP_INVAL) {
3567e9dbad6fSeschrock 			if (zfs_prop_get(zhp, entry->pl_prop,
3568e9dbad6fSeschrock 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3569e9dbad6fSeschrock 				if (strlen(buf) > entry->pl_width)
3570e9dbad6fSeschrock 					entry->pl_width = strlen(buf);
3571e9dbad6fSeschrock 			}
3572e9dbad6fSeschrock 		} else if (nvlist_lookup_nvlist(userprops,
3573e9dbad6fSeschrock 		    entry->pl_user_prop, &propval)  == 0) {
3574e9dbad6fSeschrock 			verify(nvlist_lookup_string(propval,
3575e9dbad6fSeschrock 			    ZFS_PROP_VALUE, &strval) == 0);
3576e9dbad6fSeschrock 			if (strlen(strval) > entry->pl_width)
3577e9dbad6fSeschrock 				entry->pl_width = strlen(strval);
3578e9dbad6fSeschrock 		}
3579e9dbad6fSeschrock 	}
3580e9dbad6fSeschrock 
3581e9dbad6fSeschrock 	return (0);
3582e9dbad6fSeschrock }
3583