xref: /illumos-gate/usr/src/lib/libzfs/common/libzfs_dataset.c (revision fda77a98c5f7fd534bf18ad9ab85e2b79d8670d8)
1fa9e4066Sahrens /*
2fa9e4066Sahrens  * CDDL HEADER START
3fa9e4066Sahrens  *
4fa9e4066Sahrens  * The contents of this file are subject to the terms of the
5ea8dc4b6Seschrock  * Common Development and Distribution License (the "License").
6ea8dc4b6Seschrock  * You may not use this file except in compliance with the License.
7fa9e4066Sahrens  *
8fa9e4066Sahrens  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9fa9e4066Sahrens  * or http://www.opensolaris.org/os/licensing.
10fa9e4066Sahrens  * See the License for the specific language governing permissions
11fa9e4066Sahrens  * and limitations under the License.
12fa9e4066Sahrens  *
13fa9e4066Sahrens  * When distributing Covered Code, include this CDDL HEADER in each
14fa9e4066Sahrens  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15fa9e4066Sahrens  * If applicable, add the following below this CDDL HEADER, with the
16fa9e4066Sahrens  * fields enclosed by brackets "[]" replaced with your own identifying
17fa9e4066Sahrens  * information: Portions Copyright [yyyy] [name of copyright owner]
18fa9e4066Sahrens  *
19fa9e4066Sahrens  * CDDL HEADER END
20fa9e4066Sahrens  */
21f3861e1aSahl 
22fa9e4066Sahrens /*
23798d5834Sgw25295  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
24fa9e4066Sahrens  * Use is subject to license terms.
25fa9e4066Sahrens  */
26fa9e4066Sahrens 
27fa9e4066Sahrens #pragma ident	"%Z%%M%	%I%	%E% SMI"
28fa9e4066Sahrens 
29fa9e4066Sahrens #include <assert.h>
30fa9e4066Sahrens #include <ctype.h>
31fa9e4066Sahrens #include <errno.h>
32fa9e4066Sahrens #include <libdevinfo.h>
33fa9e4066Sahrens #include <libintl.h>
34fa9e4066Sahrens #include <math.h>
35fa9e4066Sahrens #include <stdio.h>
36fa9e4066Sahrens #include <stdlib.h>
37fa9e4066Sahrens #include <strings.h>
38fa9e4066Sahrens #include <unistd.h>
39fa9e4066Sahrens #include <zone.h>
4099653d4eSeschrock #include <fcntl.h>
41fa9e4066Sahrens #include <sys/mntent.h>
42fa9e4066Sahrens #include <sys/mnttab.h>
43b12a1c38Slling #include <sys/mount.h>
44fa9e4066Sahrens 
45fa9e4066Sahrens #include <sys/spa.h>
46fa9e4066Sahrens #include <sys/zio.h>
47e9dbad6fSeschrock #include <sys/zap.h>
48fa9e4066Sahrens #include <libzfs.h>
49fa9e4066Sahrens 
50fa9e4066Sahrens #include "zfs_namecheck.h"
51fa9e4066Sahrens #include "zfs_prop.h"
52fa9e4066Sahrens #include "libzfs_impl.h"
53fa9e4066Sahrens 
54fa9e4066Sahrens /*
55fa9e4066Sahrens  * Given a single type (not a mask of types), return the type in a human
56fa9e4066Sahrens  * readable form.
57fa9e4066Sahrens  */
58fa9e4066Sahrens const char *
59fa9e4066Sahrens zfs_type_to_name(zfs_type_t type)
60fa9e4066Sahrens {
61fa9e4066Sahrens 	switch (type) {
62fa9e4066Sahrens 	case ZFS_TYPE_FILESYSTEM:
63fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
64fa9e4066Sahrens 	case ZFS_TYPE_SNAPSHOT:
65fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
66fa9e4066Sahrens 	case ZFS_TYPE_VOLUME:
67fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
68fa9e4066Sahrens 	}
69fa9e4066Sahrens 
70fa9e4066Sahrens 	return (NULL);
71fa9e4066Sahrens }
72fa9e4066Sahrens 
73fa9e4066Sahrens /*
74fa9e4066Sahrens  * Given a path and mask of ZFS types, return a string describing this dataset.
75fa9e4066Sahrens  * This is used when we fail to open a dataset and we cannot get an exact type.
76fa9e4066Sahrens  * We guess what the type would have been based on the path and the mask of
77fa9e4066Sahrens  * acceptable types.
78fa9e4066Sahrens  */
79fa9e4066Sahrens static const char *
80fa9e4066Sahrens path_to_str(const char *path, int types)
81fa9e4066Sahrens {
82fa9e4066Sahrens 	/*
83fa9e4066Sahrens 	 * When given a single type, always report the exact type.
84fa9e4066Sahrens 	 */
85fa9e4066Sahrens 	if (types == ZFS_TYPE_SNAPSHOT)
86fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "snapshot"));
87fa9e4066Sahrens 	if (types == ZFS_TYPE_FILESYSTEM)
88fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
89fa9e4066Sahrens 	if (types == ZFS_TYPE_VOLUME)
90fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "volume"));
91fa9e4066Sahrens 
92fa9e4066Sahrens 	/*
93fa9e4066Sahrens 	 * The user is requesting more than one type of dataset.  If this is the
94fa9e4066Sahrens 	 * case, consult the path itself.  If we're looking for a snapshot, and
95fa9e4066Sahrens 	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
96fa9e4066Sahrens 	 * snapshot attribute and try again.
97fa9e4066Sahrens 	 */
98fa9e4066Sahrens 	if (types & ZFS_TYPE_SNAPSHOT) {
99fa9e4066Sahrens 		if (strchr(path, '@') != NULL)
100fa9e4066Sahrens 			return (dgettext(TEXT_DOMAIN, "snapshot"));
101fa9e4066Sahrens 		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
102fa9e4066Sahrens 	}
103fa9e4066Sahrens 
104fa9e4066Sahrens 
105fa9e4066Sahrens 	/*
106fa9e4066Sahrens 	 * The user has requested either filesystems or volumes.
107fa9e4066Sahrens 	 * We have no way of knowing a priori what type this would be, so always
108fa9e4066Sahrens 	 * report it as "filesystem" or "volume", our two primitive types.
109fa9e4066Sahrens 	 */
110fa9e4066Sahrens 	if (types & ZFS_TYPE_FILESYSTEM)
111fa9e4066Sahrens 		return (dgettext(TEXT_DOMAIN, "filesystem"));
112fa9e4066Sahrens 
113fa9e4066Sahrens 	assert(types & ZFS_TYPE_VOLUME);
114fa9e4066Sahrens 	return (dgettext(TEXT_DOMAIN, "volume"));
115fa9e4066Sahrens }
116fa9e4066Sahrens 
117fa9e4066Sahrens /*
118fa9e4066Sahrens  * Validate a ZFS path.  This is used even before trying to open the dataset, to
119fa9e4066Sahrens  * provide a more meaningful error message.  We place a more useful message in
120fa9e4066Sahrens  * 'buf' detailing exactly why the name was not valid.
121fa9e4066Sahrens  */
122fa9e4066Sahrens static int
12399653d4eSeschrock zfs_validate_name(libzfs_handle_t *hdl, const char *path, int type)
124fa9e4066Sahrens {
125fa9e4066Sahrens 	namecheck_err_t why;
126fa9e4066Sahrens 	char what;
127fa9e4066Sahrens 
128fa9e4066Sahrens 	if (dataset_namecheck(path, &why, &what) != 0) {
12999653d4eSeschrock 		if (hdl != NULL) {
130fa9e4066Sahrens 			switch (why) {
131b81d61a6Slling 			case NAME_ERR_TOOLONG:
13299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
13399653d4eSeschrock 				    "name is too long"));
134b81d61a6Slling 				break;
135b81d61a6Slling 
136fa9e4066Sahrens 			case NAME_ERR_LEADING_SLASH:
13799653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
13899653d4eSeschrock 				    "leading slash in name"));
139fa9e4066Sahrens 				break;
140fa9e4066Sahrens 
141fa9e4066Sahrens 			case NAME_ERR_EMPTY_COMPONENT:
14299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14399653d4eSeschrock 				    "empty component in name"));
144fa9e4066Sahrens 				break;
145fa9e4066Sahrens 
146fa9e4066Sahrens 			case NAME_ERR_TRAILING_SLASH:
14799653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
14899653d4eSeschrock 				    "trailing slash in name"));
149fa9e4066Sahrens 				break;
150fa9e4066Sahrens 
151fa9e4066Sahrens 			case NAME_ERR_INVALCHAR:
15299653d4eSeschrock 				zfs_error_aux(hdl,
153fa9e4066Sahrens 				    dgettext(TEXT_DOMAIN, "invalid character "
15499653d4eSeschrock 				    "'%c' in name"), what);
155fa9e4066Sahrens 				break;
156fa9e4066Sahrens 
157fa9e4066Sahrens 			case NAME_ERR_MULTIPLE_AT:
15899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
15999653d4eSeschrock 				    "multiple '@' delimiters in name"));
160fa9e4066Sahrens 				break;
1615ad82045Snd150628 
1625ad82045Snd150628 			case NAME_ERR_NOLETTER:
1635ad82045Snd150628 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1645ad82045Snd150628 				    "pool doesn't begin with a letter"));
1655ad82045Snd150628 				break;
1665ad82045Snd150628 
1675ad82045Snd150628 			case NAME_ERR_RESERVED:
1685ad82045Snd150628 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1695ad82045Snd150628 				    "name is reserved"));
1705ad82045Snd150628 				break;
1715ad82045Snd150628 
1725ad82045Snd150628 			case NAME_ERR_DISKLIKE:
1735ad82045Snd150628 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1745ad82045Snd150628 				    "reserved disk name"));
1755ad82045Snd150628 				break;
176fa9e4066Sahrens 			}
177fa9e4066Sahrens 		}
178fa9e4066Sahrens 
179fa9e4066Sahrens 		return (0);
180fa9e4066Sahrens 	}
181fa9e4066Sahrens 
182fa9e4066Sahrens 	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
18399653d4eSeschrock 		if (hdl != NULL)
18499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
18599653d4eSeschrock 			    "snapshot delimiter '@' in filesystem name"));
186fa9e4066Sahrens 		return (0);
187fa9e4066Sahrens 	}
188fa9e4066Sahrens 
1891d452cf5Sahrens 	if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
1901d452cf5Sahrens 		if (hdl != NULL)
1911d452cf5Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
192d7d4af51Smmusante 			    "missing '@' delimiter in snapshot name"));
1931d452cf5Sahrens 		return (0);
1941d452cf5Sahrens 	}
1951d452cf5Sahrens 
19699653d4eSeschrock 	return (-1);
197fa9e4066Sahrens }
198fa9e4066Sahrens 
199fa9e4066Sahrens int
200fa9e4066Sahrens zfs_name_valid(const char *name, zfs_type_t type)
201fa9e4066Sahrens {
20299653d4eSeschrock 	return (zfs_validate_name(NULL, name, type));
203fa9e4066Sahrens }
204fa9e4066Sahrens 
205fa9e4066Sahrens /*
206e9dbad6fSeschrock  * This function takes the raw DSL properties, and filters out the user-defined
207e9dbad6fSeschrock  * properties into a separate nvlist.
208e9dbad6fSeschrock  */
209e9dbad6fSeschrock static int
210e9dbad6fSeschrock process_user_props(zfs_handle_t *zhp)
211e9dbad6fSeschrock {
212e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
213e9dbad6fSeschrock 	nvpair_t *elem;
214e9dbad6fSeschrock 	nvlist_t *propval;
215e9dbad6fSeschrock 
216e9dbad6fSeschrock 	nvlist_free(zhp->zfs_user_props);
217e9dbad6fSeschrock 
218e9dbad6fSeschrock 	if (nvlist_alloc(&zhp->zfs_user_props, NV_UNIQUE_NAME, 0) != 0)
219e9dbad6fSeschrock 		return (no_memory(hdl));
220e9dbad6fSeschrock 
221e9dbad6fSeschrock 	elem = NULL;
222e9dbad6fSeschrock 	while ((elem = nvlist_next_nvpair(zhp->zfs_props, elem)) != NULL) {
223e9dbad6fSeschrock 		if (!zfs_prop_user(nvpair_name(elem)))
224e9dbad6fSeschrock 			continue;
225e9dbad6fSeschrock 
226e9dbad6fSeschrock 		verify(nvpair_value_nvlist(elem, &propval) == 0);
227e9dbad6fSeschrock 		if (nvlist_add_nvlist(zhp->zfs_user_props,
228e9dbad6fSeschrock 		    nvpair_name(elem), propval) != 0)
229e9dbad6fSeschrock 			return (no_memory(hdl));
230e9dbad6fSeschrock 	}
231e9dbad6fSeschrock 
232e9dbad6fSeschrock 	return (0);
233e9dbad6fSeschrock }
234e9dbad6fSeschrock 
235e9dbad6fSeschrock /*
236fa9e4066Sahrens  * Utility function to gather stats (objset and zpl) for the given object.
237fa9e4066Sahrens  */
238fa9e4066Sahrens static int
239fa9e4066Sahrens get_stats(zfs_handle_t *zhp)
240fa9e4066Sahrens {
241fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
242e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
243fa9e4066Sahrens 
244fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
245fa9e4066Sahrens 
246e9dbad6fSeschrock 	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
24799653d4eSeschrock 		return (-1);
2487f7322feSeschrock 
24999653d4eSeschrock 	while (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0) {
2507f7322feSeschrock 		if (errno == ENOMEM) {
251e9dbad6fSeschrock 			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
252e9dbad6fSeschrock 				zcmd_free_nvlists(&zc);
25399653d4eSeschrock 				return (-1);
254e9dbad6fSeschrock 			}
2557f7322feSeschrock 		} else {
256e9dbad6fSeschrock 			zcmd_free_nvlists(&zc);
257fa9e4066Sahrens 			return (-1);
2587f7322feSeschrock 		}
2597f7322feSeschrock 	}
260fa9e4066Sahrens 
261a2eea2e1Sahrens 	zhp->zfs_dmustats = zc.zc_objset_stats; /* structure assignment */
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 	if (process_user_props(zhp) != 0)
278e9dbad6fSeschrock 		return (-1);
27999653d4eSeschrock 
280fa9e4066Sahrens 	return (0);
281fa9e4066Sahrens }
282fa9e4066Sahrens 
283fa9e4066Sahrens /*
284fa9e4066Sahrens  * Refresh the properties currently stored in the handle.
285fa9e4066Sahrens  */
286fa9e4066Sahrens void
287fa9e4066Sahrens zfs_refresh_properties(zfs_handle_t *zhp)
288fa9e4066Sahrens {
289fa9e4066Sahrens 	(void) get_stats(zhp);
290fa9e4066Sahrens }
291fa9e4066Sahrens 
292fa9e4066Sahrens /*
293fa9e4066Sahrens  * Makes a handle from the given dataset name.  Used by zfs_open() and
294fa9e4066Sahrens  * zfs_iter_* to create child handles on the fly.
295fa9e4066Sahrens  */
296fa9e4066Sahrens zfs_handle_t *
29799653d4eSeschrock make_dataset_handle(libzfs_handle_t *hdl, const char *path)
298fa9e4066Sahrens {
29999653d4eSeschrock 	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
30099653d4eSeschrock 
30199653d4eSeschrock 	if (zhp == NULL)
30299653d4eSeschrock 		return (NULL);
30399653d4eSeschrock 
30499653d4eSeschrock 	zhp->zfs_hdl = hdl;
305fa9e4066Sahrens 
30631fd60d3Sahrens top:
307fa9e4066Sahrens 	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
308fa9e4066Sahrens 
309fa9e4066Sahrens 	if (get_stats(zhp) != 0) {
310fa9e4066Sahrens 		free(zhp);
311fa9e4066Sahrens 		return (NULL);
312fa9e4066Sahrens 	}
313fa9e4066Sahrens 
31431fd60d3Sahrens 	if (zhp->zfs_dmustats.dds_inconsistent) {
31531fd60d3Sahrens 		zfs_cmd_t zc = { 0 };
31631fd60d3Sahrens 
31731fd60d3Sahrens 		/*
31831fd60d3Sahrens 		 * If it is dds_inconsistent, then we've caught it in
31931fd60d3Sahrens 		 * the middle of a 'zfs receive' or 'zfs destroy', and
32031fd60d3Sahrens 		 * it is inconsistent from the ZPL's point of view, so
32131fd60d3Sahrens 		 * can't be mounted.  However, it could also be that we
32231fd60d3Sahrens 		 * have crashed in the middle of one of those
32331fd60d3Sahrens 		 * operations, in which case we need to get rid of the
32431fd60d3Sahrens 		 * inconsistent state.  We do that by either rolling
32531fd60d3Sahrens 		 * back to the previous snapshot (which will fail if
32631fd60d3Sahrens 		 * there is none), or destroying the filesystem.  Note
32731fd60d3Sahrens 		 * that if we are still in the middle of an active
32831fd60d3Sahrens 		 * 'receive' or 'destroy', then the rollback and destroy
32931fd60d3Sahrens 		 * will fail with EBUSY and we will drive on as usual.
33031fd60d3Sahrens 		 */
33131fd60d3Sahrens 
33231fd60d3Sahrens 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
33331fd60d3Sahrens 
334a2eea2e1Sahrens 		if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL) {
33599653d4eSeschrock 			(void) zvol_remove_link(hdl, zhp->zfs_name);
33631fd60d3Sahrens 			zc.zc_objset_type = DMU_OST_ZVOL;
33731fd60d3Sahrens 		} else {
33831fd60d3Sahrens 			zc.zc_objset_type = DMU_OST_ZFS;
33931fd60d3Sahrens 		}
34031fd60d3Sahrens 
34131fd60d3Sahrens 		/* If we can successfully roll it back, reget the stats */
34299653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_ROLLBACK, &zc) == 0)
34331fd60d3Sahrens 			goto top;
34431fd60d3Sahrens 		/*
34531fd60d3Sahrens 		 * If we can sucessfully destroy it, pretend that it
34631fd60d3Sahrens 		 * never existed.
34731fd60d3Sahrens 		 */
34899653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) == 0) {
34931fd60d3Sahrens 			free(zhp);
35031fd60d3Sahrens 			errno = ENOENT;
35131fd60d3Sahrens 			return (NULL);
35231fd60d3Sahrens 		}
35331fd60d3Sahrens 	}
35431fd60d3Sahrens 
355fa9e4066Sahrens 	/*
356fa9e4066Sahrens 	 * We've managed to open the dataset and gather statistics.  Determine
357fa9e4066Sahrens 	 * the high-level type.
358fa9e4066Sahrens 	 */
359a2eea2e1Sahrens 	if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
360a2eea2e1Sahrens 		zhp->zfs_head_type = ZFS_TYPE_VOLUME;
361a2eea2e1Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
362a2eea2e1Sahrens 		zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
363a2eea2e1Sahrens 	else
364a2eea2e1Sahrens 		abort();
365a2eea2e1Sahrens 
366fa9e4066Sahrens 	if (zhp->zfs_dmustats.dds_is_snapshot)
367fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
368fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
369fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_VOLUME;
370fa9e4066Sahrens 	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
371fa9e4066Sahrens 		zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
372fa9e4066Sahrens 	else
37399653d4eSeschrock 		abort();	/* we should never see any other types */
374fa9e4066Sahrens 
375fa9e4066Sahrens 	return (zhp);
376fa9e4066Sahrens }
377fa9e4066Sahrens 
378fa9e4066Sahrens /*
379fa9e4066Sahrens  * Opens the given snapshot, filesystem, or volume.   The 'types'
380fa9e4066Sahrens  * argument is a mask of acceptable types.  The function will print an
381fa9e4066Sahrens  * appropriate error message and return NULL if it can't be opened.
382fa9e4066Sahrens  */
383fa9e4066Sahrens zfs_handle_t *
38499653d4eSeschrock zfs_open(libzfs_handle_t *hdl, const char *path, int types)
385fa9e4066Sahrens {
386fa9e4066Sahrens 	zfs_handle_t *zhp;
38799653d4eSeschrock 	char errbuf[1024];
38899653d4eSeschrock 
38999653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
39099653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
391fa9e4066Sahrens 
392fa9e4066Sahrens 	/*
39399653d4eSeschrock 	 * Validate the name before we even try to open it.
394fa9e4066Sahrens 	 */
39599653d4eSeschrock 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_ANY)) {
39699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
39799653d4eSeschrock 		    "invalid dataset name"));
39899653d4eSeschrock 		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
399fa9e4066Sahrens 		return (NULL);
400fa9e4066Sahrens 	}
401fa9e4066Sahrens 
402fa9e4066Sahrens 	/*
403fa9e4066Sahrens 	 * Try to get stats for the dataset, which will tell us if it exists.
404fa9e4066Sahrens 	 */
405fa9e4066Sahrens 	errno = 0;
40699653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
407ece3d9b3Slling 		(void) zfs_standard_error(hdl, errno, errbuf);
408fa9e4066Sahrens 		return (NULL);
409fa9e4066Sahrens 	}
410fa9e4066Sahrens 
411fa9e4066Sahrens 	if (!(types & zhp->zfs_type)) {
41299653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
41394de1d4cSeschrock 		zfs_close(zhp);
414fa9e4066Sahrens 		return (NULL);
415fa9e4066Sahrens 	}
416fa9e4066Sahrens 
417fa9e4066Sahrens 	return (zhp);
418fa9e4066Sahrens }
419fa9e4066Sahrens 
420fa9e4066Sahrens /*
421fa9e4066Sahrens  * Release a ZFS handle.  Nothing to do but free the associated memory.
422fa9e4066Sahrens  */
423fa9e4066Sahrens void
424fa9e4066Sahrens zfs_close(zfs_handle_t *zhp)
425fa9e4066Sahrens {
426fa9e4066Sahrens 	if (zhp->zfs_mntopts)
427fa9e4066Sahrens 		free(zhp->zfs_mntopts);
42899653d4eSeschrock 	nvlist_free(zhp->zfs_props);
429e9dbad6fSeschrock 	nvlist_free(zhp->zfs_user_props);
430fa9e4066Sahrens 	free(zhp);
431fa9e4066Sahrens }
432fa9e4066Sahrens 
433fa9e4066Sahrens /*
434fa9e4066Sahrens  * Given a numeric suffix, convert the value into a number of bits that the
435fa9e4066Sahrens  * resulting value must be shifted.
436fa9e4066Sahrens  */
437fa9e4066Sahrens static int
43899653d4eSeschrock str2shift(libzfs_handle_t *hdl, const char *buf)
439fa9e4066Sahrens {
440fa9e4066Sahrens 	const char *ends = "BKMGTPEZ";
441fa9e4066Sahrens 	int i;
442fa9e4066Sahrens 
443fa9e4066Sahrens 	if (buf[0] == '\0')
444fa9e4066Sahrens 		return (0);
445fa9e4066Sahrens 	for (i = 0; i < strlen(ends); i++) {
446fa9e4066Sahrens 		if (toupper(buf[0]) == ends[i])
447fa9e4066Sahrens 			break;
448fa9e4066Sahrens 	}
449fa9e4066Sahrens 	if (i == strlen(ends)) {
45099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
45199653d4eSeschrock 		    "invalid numeric suffix '%s'"), buf);
452fa9e4066Sahrens 		return (-1);
453fa9e4066Sahrens 	}
454fa9e4066Sahrens 
455fa9e4066Sahrens 	/*
456fa9e4066Sahrens 	 * We want to allow trailing 'b' characters for 'GB' or 'Mb'.  But don't
457fa9e4066Sahrens 	 * allow 'BB' - that's just weird.
458fa9e4066Sahrens 	 */
459fa9e4066Sahrens 	if (buf[1] == '\0' || (toupper(buf[1]) == 'B' && buf[2] == '\0' &&
46099653d4eSeschrock 	    toupper(buf[0]) != 'B'))
461fa9e4066Sahrens 		return (10*i);
462fa9e4066Sahrens 
46399653d4eSeschrock 	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
46499653d4eSeschrock 	    "invalid numeric suffix '%s'"), buf);
465fa9e4066Sahrens 	return (-1);
466fa9e4066Sahrens }
467fa9e4066Sahrens 
468fa9e4066Sahrens /*
469fa9e4066Sahrens  * Convert a string of the form '100G' into a real number.  Used when setting
470fa9e4066Sahrens  * properties or creating a volume.  'buf' is used to place an extended error
471fa9e4066Sahrens  * message for the caller to use.
472fa9e4066Sahrens  */
473fa9e4066Sahrens static int
47499653d4eSeschrock nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
475fa9e4066Sahrens {
476fa9e4066Sahrens 	char *end;
477fa9e4066Sahrens 	int shift;
478fa9e4066Sahrens 
479fa9e4066Sahrens 	*num = 0;
480fa9e4066Sahrens 
481fa9e4066Sahrens 	/* Check to see if this looks like a number.  */
482fa9e4066Sahrens 	if ((value[0] < '0' || value[0] > '9') && value[0] != '.') {
48399653d4eSeschrock 		if (hdl)
48499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
48599653d4eSeschrock 			    "bad numeric value '%s'"), value);
486fa9e4066Sahrens 		return (-1);
487fa9e4066Sahrens 	}
488fa9e4066Sahrens 
489fa9e4066Sahrens 	/* Rely on stroll() to process the numeric portion.  */
490fa9e4066Sahrens 	errno = 0;
491fa9e4066Sahrens 	*num = strtoll(value, &end, 10);
492fa9e4066Sahrens 
493fa9e4066Sahrens 	/*
494fa9e4066Sahrens 	 * Check for ERANGE, which indicates that the value is too large to fit
495fa9e4066Sahrens 	 * in a 64-bit value.
496fa9e4066Sahrens 	 */
497fa9e4066Sahrens 	if (errno == ERANGE) {
49899653d4eSeschrock 		if (hdl)
49999653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
50099653d4eSeschrock 			    "numeric value is too large"));
501fa9e4066Sahrens 		return (-1);
502fa9e4066Sahrens 	}
503fa9e4066Sahrens 
504fa9e4066Sahrens 	/*
505fa9e4066Sahrens 	 * If we have a decimal value, then do the computation with floating
506fa9e4066Sahrens 	 * point arithmetic.  Otherwise, use standard arithmetic.
507fa9e4066Sahrens 	 */
508fa9e4066Sahrens 	if (*end == '.') {
509fa9e4066Sahrens 		double fval = strtod(value, &end);
510fa9e4066Sahrens 
51199653d4eSeschrock 		if ((shift = str2shift(hdl, end)) == -1)
512fa9e4066Sahrens 			return (-1);
513fa9e4066Sahrens 
514fa9e4066Sahrens 		fval *= pow(2, shift);
515fa9e4066Sahrens 
516fa9e4066Sahrens 		if (fval > UINT64_MAX) {
51799653d4eSeschrock 			if (hdl)
51899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
51999653d4eSeschrock 				    "numeric value is too large"));
520fa9e4066Sahrens 			return (-1);
521fa9e4066Sahrens 		}
522fa9e4066Sahrens 
523fa9e4066Sahrens 		*num = (uint64_t)fval;
524fa9e4066Sahrens 	} else {
52599653d4eSeschrock 		if ((shift = str2shift(hdl, end)) == -1)
526fa9e4066Sahrens 			return (-1);
527fa9e4066Sahrens 
528fa9e4066Sahrens 		/* Check for overflow */
529fa9e4066Sahrens 		if (shift >= 64 || (*num << shift) >> shift != *num) {
53099653d4eSeschrock 			if (hdl)
53199653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
53299653d4eSeschrock 				    "numeric value is too large"));
533fa9e4066Sahrens 			return (-1);
534fa9e4066Sahrens 		}
535fa9e4066Sahrens 
536fa9e4066Sahrens 		*num <<= shift;
537fa9e4066Sahrens 	}
538fa9e4066Sahrens 
539fa9e4066Sahrens 	return (0);
540fa9e4066Sahrens }
541fa9e4066Sahrens 
542fa9e4066Sahrens int
543e9dbad6fSeschrock zfs_nicestrtonum(libzfs_handle_t *hdl, const char *str, uint64_t *val)
544fa9e4066Sahrens {
545e9dbad6fSeschrock 	return (nicestrtonum(hdl, str, val));
546fa9e4066Sahrens }
547fa9e4066Sahrens 
548fa9e4066Sahrens /*
549e9dbad6fSeschrock  * The prop_parse_*() functions are designed to allow flexibility in callers
550e9dbad6fSeschrock  * when setting properties.  At the DSL layer, all properties are either 64-bit
551e9dbad6fSeschrock  * numbers or strings.  We want the user to be able to ignore this fact and
552e9dbad6fSeschrock  * specify properties as native values (boolean, for example) or as strings (to
553e9dbad6fSeschrock  * simplify command line utilities).  This also handles converting index types
554e9dbad6fSeschrock  * (compression, checksum, etc) from strings to their on-disk index.
555fa9e4066Sahrens  */
556e9dbad6fSeschrock 
557e9dbad6fSeschrock static int
558e9dbad6fSeschrock prop_parse_boolean(libzfs_handle_t *hdl, nvpair_t *elem, uint64_t *val)
559fa9e4066Sahrens {
560e9dbad6fSeschrock 	uint64_t ret;
561fa9e4066Sahrens 
562e9dbad6fSeschrock 	switch (nvpair_type(elem)) {
563e9dbad6fSeschrock 	case DATA_TYPE_STRING:
564e9dbad6fSeschrock 		{
565e9dbad6fSeschrock 			char *value;
566798d5834Sgw25295 			verify(nvpair_value_string(elem, &value) == 0);
56799653d4eSeschrock 
568fa9e4066Sahrens 			if (strcmp(value, "on") == 0) {
569e9dbad6fSeschrock 				ret = 1;
570fa9e4066Sahrens 			} else if (strcmp(value, "off") == 0) {
571e9dbad6fSeschrock 				ret = 0;
572fa9e4066Sahrens 			} else {
57399653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
574e9dbad6fSeschrock 				    "property '%s' must be 'on' or 'off'"),
575e9dbad6fSeschrock 				    nvpair_name(elem));
576e9dbad6fSeschrock 				return (-1);
577fa9e4066Sahrens 			}
578fa9e4066Sahrens 			break;
579e9dbad6fSeschrock 		}
580fa9e4066Sahrens 
581e9dbad6fSeschrock 	case DATA_TYPE_UINT64:
582e9dbad6fSeschrock 		{
583798d5834Sgw25295 			verify(nvpair_value_uint64(elem, &ret) == 0);
584e9dbad6fSeschrock 			if (ret > 1) {
585e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
586e9dbad6fSeschrock 				    "'%s' must be a boolean value"),
587e9dbad6fSeschrock 				    nvpair_name(elem));
588e9dbad6fSeschrock 				return (-1);
589e9dbad6fSeschrock 			}
590e9dbad6fSeschrock 			break;
591e9dbad6fSeschrock 		}
592e9dbad6fSeschrock 
593e9dbad6fSeschrock 	case DATA_TYPE_BOOLEAN_VALUE:
594e9dbad6fSeschrock 		{
595e9dbad6fSeschrock 			boolean_t value;
596798d5834Sgw25295 			verify(nvpair_value_boolean_value(elem, &value) == 0);
597e9dbad6fSeschrock 			ret = value;
598e9dbad6fSeschrock 			break;
599e9dbad6fSeschrock 		}
600e9dbad6fSeschrock 
601e9dbad6fSeschrock 	default:
602e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
603e9dbad6fSeschrock 		    "'%s' must be a boolean value"),
604e9dbad6fSeschrock 		    nvpair_name(elem));
605e9dbad6fSeschrock 		return (-1);
606e9dbad6fSeschrock 	}
607e9dbad6fSeschrock 
608e9dbad6fSeschrock 	*val = ret;
609e9dbad6fSeschrock 	return (0);
610e9dbad6fSeschrock }
611e9dbad6fSeschrock 
612e9dbad6fSeschrock static int
613e9dbad6fSeschrock prop_parse_number(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
614e9dbad6fSeschrock     uint64_t *val)
615e9dbad6fSeschrock {
616e9dbad6fSeschrock 	uint64_t ret;
617e9dbad6fSeschrock 	boolean_t isnone = B_FALSE;
618e9dbad6fSeschrock 
619e9dbad6fSeschrock 	switch (nvpair_type(elem)) {
620e9dbad6fSeschrock 	case DATA_TYPE_STRING:
621e9dbad6fSeschrock 		{
622e9dbad6fSeschrock 			char *value;
623e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &value);
624fa9e4066Sahrens 			if (strcmp(value, "none") == 0) {
625e9dbad6fSeschrock 				isnone = B_TRUE;
626e9dbad6fSeschrock 				ret = 0;
627e9dbad6fSeschrock 			} else if (nicestrtonum(hdl, value, &ret) != 0) {
628e9dbad6fSeschrock 				return (-1);
629e9dbad6fSeschrock 			}
630fa9e4066Sahrens 			break;
631fa9e4066Sahrens 		}
632fa9e4066Sahrens 
633e9dbad6fSeschrock 	case DATA_TYPE_UINT64:
634e9dbad6fSeschrock 		(void) nvpair_value_uint64(elem, &ret);
635fa9e4066Sahrens 		break;
636fa9e4066Sahrens 
637e9dbad6fSeschrock 	default:
638e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
639e9dbad6fSeschrock 		    "'%s' must be a number"),
640e9dbad6fSeschrock 		    nvpair_name(elem));
641e9dbad6fSeschrock 		return (-1);
642e9dbad6fSeschrock 	}
643e9dbad6fSeschrock 
644fa9e4066Sahrens 	/*
645e9dbad6fSeschrock 	 * Quota special: force 'none' and don't allow 0.
646fa9e4066Sahrens 	 */
647e9dbad6fSeschrock 	if (ret == 0 && !isnone && prop == ZFS_PROP_QUOTA) {
64899653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
649e9dbad6fSeschrock 		    "use 'none' to disable quota"));
650e9dbad6fSeschrock 		return (-1);
651fa9e4066Sahrens 	}
652fa9e4066Sahrens 
653e9dbad6fSeschrock 	*val = ret;
654e9dbad6fSeschrock 	return (0);
655e9dbad6fSeschrock }
656e9dbad6fSeschrock 
657e9dbad6fSeschrock static int
658e9dbad6fSeschrock prop_parse_index(libzfs_handle_t *hdl, nvpair_t *elem, zfs_prop_t prop,
659e9dbad6fSeschrock     uint64_t *val)
660e9dbad6fSeschrock {
661e9dbad6fSeschrock 	char *propname = nvpair_name(elem);
662e9dbad6fSeschrock 	char *value;
663e9dbad6fSeschrock 
664e9dbad6fSeschrock 	if (nvpair_type(elem) != DATA_TYPE_STRING) {
66599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
666e9dbad6fSeschrock 		    "'%s' must be a string"), propname);
667e9dbad6fSeschrock 		return (-1);
668fa9e4066Sahrens 	}
669fa9e4066Sahrens 
670e9dbad6fSeschrock 	(void) nvpair_value_string(elem, &value);
671e9dbad6fSeschrock 
672e9dbad6fSeschrock 	if (zfs_prop_string_to_index(prop, value, val) != 0) {
67399653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
674e9dbad6fSeschrock 		    "'%s' must be one of '%s'"), propname,
675e9dbad6fSeschrock 		    zfs_prop_values(prop));
676e9dbad6fSeschrock 		return (-1);
677fa9e4066Sahrens 	}
678fa9e4066Sahrens 
679fa9e4066Sahrens 	return (0);
680fa9e4066Sahrens }
681fa9e4066Sahrens 
682fa9e4066Sahrens /*
683e9dbad6fSeschrock  * Given an nvlist of properties to set, validates that they are correct, and
684e9dbad6fSeschrock  * parses any numeric properties (index, boolean, etc) if they are specified as
685e9dbad6fSeschrock  * strings.
686fa9e4066Sahrens  */
687e9dbad6fSeschrock static nvlist_t *
688e9dbad6fSeschrock zfs_validate_properties(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
689e9dbad6fSeschrock     uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
690fa9e4066Sahrens {
691e9dbad6fSeschrock 	nvpair_t *elem;
692e9dbad6fSeschrock 	const char *propname;
693e9dbad6fSeschrock 	zfs_prop_t prop;
694e9dbad6fSeschrock 	uint64_t intval;
695e9dbad6fSeschrock 	char *strval;
696e9dbad6fSeschrock 	nvlist_t *ret;
697fa9e4066Sahrens 
698e9dbad6fSeschrock 	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
699e9dbad6fSeschrock 		(void) no_memory(hdl);
700e9dbad6fSeschrock 		return (NULL);
701e9dbad6fSeschrock 	}
702fa9e4066Sahrens 
703e9dbad6fSeschrock 	if (type == ZFS_TYPE_SNAPSHOT) {
704e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
705d7d4af51Smmusante 		    "snapshot properties cannot be modified"));
706e9dbad6fSeschrock 		(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
707e9dbad6fSeschrock 		goto error;
708e9dbad6fSeschrock 	}
70999653d4eSeschrock 
710e9dbad6fSeschrock 	elem = NULL;
711e9dbad6fSeschrock 	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
712e9dbad6fSeschrock 		propname = nvpair_name(elem);
71399653d4eSeschrock 
714fa9e4066Sahrens 		/*
715e9dbad6fSeschrock 		 * Make sure this property is valid and applies to this type.
716fa9e4066Sahrens 		 */
717e9dbad6fSeschrock 		if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
718e9dbad6fSeschrock 			if (!zfs_prop_user(propname)) {
719e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
720e9dbad6fSeschrock 				    "invalid property '%s'"),
721e9dbad6fSeschrock 				    propname);
722e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
723e9dbad6fSeschrock 				goto error;
724e9dbad6fSeschrock 			} else {
725e9dbad6fSeschrock 				/*
726e9dbad6fSeschrock 				 * If this is a user property, make sure it's a
727e9dbad6fSeschrock 				 * string, and that it's less than
728e9dbad6fSeschrock 				 * ZAP_MAXNAMELEN.
729e9dbad6fSeschrock 				 */
730e9dbad6fSeschrock 				if (nvpair_type(elem) != DATA_TYPE_STRING) {
731e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
732e9dbad6fSeschrock 					    "'%s' must be a string"),
733e9dbad6fSeschrock 					    propname);
734e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
735e9dbad6fSeschrock 					    errbuf);
736e9dbad6fSeschrock 					goto error;
737e9dbad6fSeschrock 				}
738e9dbad6fSeschrock 
739e9dbad6fSeschrock 				if (strlen(nvpair_name(elem)) >=
740e9dbad6fSeschrock 				    ZAP_MAXNAMELEN) {
741e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
742e9dbad6fSeschrock 					    "property name '%s' is too long"),
743e9dbad6fSeschrock 					    propname);
744e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
745e9dbad6fSeschrock 					    errbuf);
746e9dbad6fSeschrock 					goto error;
747e9dbad6fSeschrock 				}
748e9dbad6fSeschrock 			}
749e9dbad6fSeschrock 
750e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &strval);
751e9dbad6fSeschrock 			if (nvlist_add_string(ret, propname, strval) != 0) {
752e9dbad6fSeschrock 				(void) no_memory(hdl);
753e9dbad6fSeschrock 				goto error;
754e9dbad6fSeschrock 			}
755e9dbad6fSeschrock 			continue;
756e9dbad6fSeschrock 		}
757fa9e4066Sahrens 
758fa9e4066Sahrens 		/*
759e9dbad6fSeschrock 		 * Normalize the name, to get rid of shorthand abbrevations.
760e9dbad6fSeschrock 		 */
761e9dbad6fSeschrock 		propname = zfs_prop_to_name(prop);
762e9dbad6fSeschrock 
763e9dbad6fSeschrock 		if (!zfs_prop_valid_for_type(prop, type)) {
764e9dbad6fSeschrock 			zfs_error_aux(hdl,
765e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' does not "
766e9dbad6fSeschrock 			    "apply to datasets of this type"), propname);
767e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
768e9dbad6fSeschrock 			goto error;
769e9dbad6fSeschrock 		}
770e9dbad6fSeschrock 
771e9dbad6fSeschrock 		if (zfs_prop_readonly(prop) &&
772e9dbad6fSeschrock 		    (prop != ZFS_PROP_VOLBLOCKSIZE || zhp != NULL)) {
773e9dbad6fSeschrock 			zfs_error_aux(hdl,
774e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
775e9dbad6fSeschrock 			    propname);
776e9dbad6fSeschrock 			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
777e9dbad6fSeschrock 			goto error;
778e9dbad6fSeschrock 		}
779e9dbad6fSeschrock 
780e9dbad6fSeschrock 		/*
781e9dbad6fSeschrock 		 * Convert any properties to the internal DSL value types.
782e9dbad6fSeschrock 		 */
783e9dbad6fSeschrock 		strval = NULL;
784e9dbad6fSeschrock 		switch (zfs_prop_get_type(prop)) {
785e9dbad6fSeschrock 		case prop_type_boolean:
786e9dbad6fSeschrock 			if (prop_parse_boolean(hdl, elem, &intval) != 0) {
787e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
788e9dbad6fSeschrock 				goto error;
789e9dbad6fSeschrock 			}
790e9dbad6fSeschrock 			break;
791e9dbad6fSeschrock 
792e9dbad6fSeschrock 		case prop_type_string:
793e9dbad6fSeschrock 			if (nvpair_type(elem) != DATA_TYPE_STRING) {
794e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
795e9dbad6fSeschrock 				    "'%s' must be a string"),
796e9dbad6fSeschrock 				    propname);
797e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
798e9dbad6fSeschrock 				goto error;
799e9dbad6fSeschrock 			}
800e9dbad6fSeschrock 			(void) nvpair_value_string(elem, &strval);
801e9dbad6fSeschrock 			if (strlen(strval) >= ZFS_MAXPROPLEN) {
802e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
803e9dbad6fSeschrock 				    "'%s' is too long"), propname);
804e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
805e9dbad6fSeschrock 				goto error;
806e9dbad6fSeschrock 			}
807e9dbad6fSeschrock 			break;
808e9dbad6fSeschrock 
809e9dbad6fSeschrock 		case prop_type_number:
810e9dbad6fSeschrock 			if (prop_parse_number(hdl, elem, prop, &intval) != 0) {
811e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
812e9dbad6fSeschrock 				goto error;
813e9dbad6fSeschrock 			}
814e9dbad6fSeschrock 			break;
815e9dbad6fSeschrock 
816e9dbad6fSeschrock 		case prop_type_index:
817e9dbad6fSeschrock 			if (prop_parse_index(hdl, elem, prop, &intval) != 0) {
818e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
819e9dbad6fSeschrock 				goto error;
820e9dbad6fSeschrock 			}
821e9dbad6fSeschrock 			break;
822e9dbad6fSeschrock 
823e9dbad6fSeschrock 		default:
824e9dbad6fSeschrock 			abort();
825e9dbad6fSeschrock 		}
826e9dbad6fSeschrock 
827e9dbad6fSeschrock 		/*
828e9dbad6fSeschrock 		 * Add the result to our return set of properties.
829e9dbad6fSeschrock 		 */
830e9dbad6fSeschrock 		if (strval) {
831e9dbad6fSeschrock 			if (nvlist_add_string(ret, propname, strval) != 0) {
832e9dbad6fSeschrock 				(void) no_memory(hdl);
833e9dbad6fSeschrock 				goto error;
834e9dbad6fSeschrock 			}
835e9dbad6fSeschrock 		} else if (nvlist_add_uint64(ret, propname, intval) != 0) {
836e9dbad6fSeschrock 			(void) no_memory(hdl);
837e9dbad6fSeschrock 			goto error;
838e9dbad6fSeschrock 		}
839e9dbad6fSeschrock 
840e9dbad6fSeschrock 		/*
841e9dbad6fSeschrock 		 * Perform some additional checks for specific properties.
842e9dbad6fSeschrock 		 */
843e9dbad6fSeschrock 		switch (prop) {
844e9dbad6fSeschrock 		case ZFS_PROP_RECORDSIZE:
845e9dbad6fSeschrock 		case ZFS_PROP_VOLBLOCKSIZE:
846e9dbad6fSeschrock 			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
847e9dbad6fSeschrock 			if (intval < SPA_MINBLOCKSIZE ||
848e9dbad6fSeschrock 			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
849e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
850e9dbad6fSeschrock 				    "'%s' must be power of 2 from %u "
851e9dbad6fSeschrock 				    "to %uk"), propname,
852e9dbad6fSeschrock 				    (uint_t)SPA_MINBLOCKSIZE,
853e9dbad6fSeschrock 				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
854e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
855e9dbad6fSeschrock 				goto error;
856e9dbad6fSeschrock 			}
857e9dbad6fSeschrock 			break;
858e9dbad6fSeschrock 
859f3861e1aSahl 		case ZFS_PROP_SHAREISCSI:
860f3861e1aSahl 			if (strcmp(strval, "off") != 0 &&
861f3861e1aSahl 			    strcmp(strval, "on") != 0 &&
862f3861e1aSahl 			    strcmp(strval, "type=disk") != 0) {
863f3861e1aSahl 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
864f3861e1aSahl 				    "'%s' must be 'on', 'off', or 'type=disk'"),
865f3861e1aSahl 				    propname);
866f3861e1aSahl 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
867f3861e1aSahl 				goto error;
868f3861e1aSahl 			}
869f3861e1aSahl 
870f3861e1aSahl 			break;
871f3861e1aSahl 
872e9dbad6fSeschrock 		case ZFS_PROP_MOUNTPOINT:
873e9dbad6fSeschrock 			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
874e9dbad6fSeschrock 			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
875e9dbad6fSeschrock 				break;
876e9dbad6fSeschrock 
877e9dbad6fSeschrock 			if (strval[0] != '/') {
878e9dbad6fSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
879e9dbad6fSeschrock 				    "'%s' must be an absolute path, "
880e9dbad6fSeschrock 				    "'none', or 'legacy'"), propname);
881e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
882e9dbad6fSeschrock 				goto error;
883e9dbad6fSeschrock 			}
884f3861e1aSahl 			/*FALLTHRU*/
885e9dbad6fSeschrock 
886f3861e1aSahl 		case ZFS_PROP_SHARENFS:
887e9dbad6fSeschrock 			/*
888f3861e1aSahl 			 * For the mountpoint and sharenfs properties, check if
889f3861e1aSahl 			 * it can be set in a global/non-global zone based on
890f3861e1aSahl 			 * the zoned property value:
891fa9e4066Sahrens 			 *
892fa9e4066Sahrens 			 *		global zone	    non-global zone
893f3861e1aSahl 			 * --------------------------------------------------
894fa9e4066Sahrens 			 * zoned=on	mountpoint (no)	    mountpoint (yes)
895fa9e4066Sahrens 			 *		sharenfs (no)	    sharenfs (no)
896fa9e4066Sahrens 			 *
897fa9e4066Sahrens 			 * zoned=off	mountpoint (yes)	N/A
898fa9e4066Sahrens 			 *		sharenfs (yes)
899fa9e4066Sahrens 			 */
900e9dbad6fSeschrock 			if (zoned) {
901fa9e4066Sahrens 				if (getzoneid() == GLOBAL_ZONEID) {
90299653d4eSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
903e9dbad6fSeschrock 					    "'%s' cannot be set on "
904e9dbad6fSeschrock 					    "dataset in a non-global zone"),
905e9dbad6fSeschrock 					    propname);
906e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
907e9dbad6fSeschrock 					    errbuf);
908e9dbad6fSeschrock 					goto error;
909fa9e4066Sahrens 				} else if (prop == ZFS_PROP_SHARENFS) {
91099653d4eSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
911e9dbad6fSeschrock 					    "'%s' cannot be set in "
912e9dbad6fSeschrock 					    "a non-global zone"), propname);
913e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_ZONED,
914e9dbad6fSeschrock 					    errbuf);
915e9dbad6fSeschrock 					goto error;
916fa9e4066Sahrens 				}
917fa9e4066Sahrens 			} else if (getzoneid() != GLOBAL_ZONEID) {
918fa9e4066Sahrens 				/*
919fa9e4066Sahrens 				 * If zoned property is 'off', this must be in
920fa9e4066Sahrens 				 * a globle zone. If not, something is wrong.
921fa9e4066Sahrens 				 */
92299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
923e9dbad6fSeschrock 				    "'%s' cannot be set while dataset "
924e9dbad6fSeschrock 				    "'zoned' property is set"), propname);
925e9dbad6fSeschrock 				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
926e9dbad6fSeschrock 				goto error;
927fa9e4066Sahrens 			}
928f3861e1aSahl 
929f3861e1aSahl 			break;
930fa9e4066Sahrens 		}
931fa9e4066Sahrens 
932e9dbad6fSeschrock 		/*
933e9dbad6fSeschrock 		 * For changes to existing volumes, we have some additional
934e9dbad6fSeschrock 		 * checks to enforce.
935e9dbad6fSeschrock 		 */
936e9dbad6fSeschrock 		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
937e9dbad6fSeschrock 			uint64_t volsize = zfs_prop_get_int(zhp,
938e9dbad6fSeschrock 			    ZFS_PROP_VOLSIZE);
939e9dbad6fSeschrock 			uint64_t blocksize = zfs_prop_get_int(zhp,
940e9dbad6fSeschrock 			    ZFS_PROP_VOLBLOCKSIZE);
941e9dbad6fSeschrock 			char buf[64];
942e9dbad6fSeschrock 
943e9dbad6fSeschrock 			switch (prop) {
944e9dbad6fSeschrock 			case ZFS_PROP_RESERVATION:
945e9dbad6fSeschrock 				if (intval > volsize) {
946e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
947e9dbad6fSeschrock 					    "'%s' is greater than current "
948e9dbad6fSeschrock 					    "volume size"), propname);
949e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
950e9dbad6fSeschrock 					    errbuf);
951e9dbad6fSeschrock 					goto error;
952e9dbad6fSeschrock 				}
953e9dbad6fSeschrock 				break;
954e9dbad6fSeschrock 
955e9dbad6fSeschrock 			case ZFS_PROP_VOLSIZE:
956e9dbad6fSeschrock 				if (intval % blocksize != 0) {
957e9dbad6fSeschrock 					zfs_nicenum(blocksize, buf,
958e9dbad6fSeschrock 					    sizeof (buf));
959e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
960e9dbad6fSeschrock 					    "'%s' must be a multiple of "
961e9dbad6fSeschrock 					    "volume block size (%s)"),
962e9dbad6fSeschrock 					    propname, buf);
963e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
964e9dbad6fSeschrock 					    errbuf);
965e9dbad6fSeschrock 					goto error;
966e9dbad6fSeschrock 				}
967e9dbad6fSeschrock 
968e9dbad6fSeschrock 				if (intval == 0) {
969e9dbad6fSeschrock 					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
970e9dbad6fSeschrock 					    "'%s' cannot be zero"),
971e9dbad6fSeschrock 					    propname);
972e9dbad6fSeschrock 					(void) zfs_error(hdl, EZFS_BADPROP,
973e9dbad6fSeschrock 					    errbuf);
974e9dbad6fSeschrock 					goto error;
975e9dbad6fSeschrock 				}
976f3861e1aSahl 				break;
977e9dbad6fSeschrock 			}
978e9dbad6fSeschrock 		}
979e9dbad6fSeschrock 	}
980e9dbad6fSeschrock 
981e9dbad6fSeschrock 	/*
982e9dbad6fSeschrock 	 * If this is an existing volume, and someone is setting the volsize,
983e9dbad6fSeschrock 	 * make sure that it matches the reservation, or add it if necessary.
984e9dbad6fSeschrock 	 */
985e9dbad6fSeschrock 	if (zhp != NULL && type == ZFS_TYPE_VOLUME &&
986e9dbad6fSeschrock 	    nvlist_lookup_uint64(ret, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
987e9dbad6fSeschrock 	    &intval) == 0) {
988e9dbad6fSeschrock 		uint64_t old_volsize = zfs_prop_get_int(zhp,
989e9dbad6fSeschrock 		    ZFS_PROP_VOLSIZE);
990e9dbad6fSeschrock 		uint64_t old_reservation = zfs_prop_get_int(zhp,
991e9dbad6fSeschrock 		    ZFS_PROP_RESERVATION);
992e9dbad6fSeschrock 		uint64_t new_reservation;
993e9dbad6fSeschrock 
994e9dbad6fSeschrock 		if (old_volsize == old_reservation &&
995e9dbad6fSeschrock 		    nvlist_lookup_uint64(ret,
996e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_RESERVATION),
997e9dbad6fSeschrock 		    &new_reservation) != 0) {
998e9dbad6fSeschrock 			if (nvlist_add_uint64(ret,
999e9dbad6fSeschrock 			    zfs_prop_to_name(ZFS_PROP_RESERVATION),
1000e9dbad6fSeschrock 			    intval) != 0) {
1001e9dbad6fSeschrock 				(void) no_memory(hdl);
1002e9dbad6fSeschrock 				goto error;
1003e9dbad6fSeschrock 			}
1004e9dbad6fSeschrock 		}
1005e9dbad6fSeschrock 	}
1006e9dbad6fSeschrock 
1007e9dbad6fSeschrock 	return (ret);
1008e9dbad6fSeschrock 
1009e9dbad6fSeschrock error:
1010e9dbad6fSeschrock 	nvlist_free(ret);
1011e9dbad6fSeschrock 	return (NULL);
1012e9dbad6fSeschrock }
1013e9dbad6fSeschrock 
1014e9dbad6fSeschrock /*
1015e9dbad6fSeschrock  * Given a property name and value, set the property for the given dataset.
1016e9dbad6fSeschrock  */
1017e9dbad6fSeschrock int
1018e9dbad6fSeschrock zfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1019e9dbad6fSeschrock {
1020e9dbad6fSeschrock 	zfs_cmd_t zc = { 0 };
1021e9dbad6fSeschrock 	int ret = -1;
1022e9dbad6fSeschrock 	prop_changelist_t *cl = NULL;
1023e9dbad6fSeschrock 	char errbuf[1024];
1024e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
1025e9dbad6fSeschrock 	nvlist_t *nvl = NULL, *realprops;
1026e9dbad6fSeschrock 	zfs_prop_t prop;
1027e9dbad6fSeschrock 
1028e9dbad6fSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
1029e9dbad6fSeschrock 	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
1030e9dbad6fSeschrock 	    zhp->zfs_name);
1031e9dbad6fSeschrock 
1032e9dbad6fSeschrock 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1033e9dbad6fSeschrock 	    nvlist_add_string(nvl, propname, propval) != 0) {
1034e9dbad6fSeschrock 		(void) no_memory(hdl);
1035e9dbad6fSeschrock 		goto error;
1036e9dbad6fSeschrock 	}
1037e9dbad6fSeschrock 
1038e9dbad6fSeschrock 	if ((realprops = zfs_validate_properties(hdl, zhp->zfs_type, nvl,
1039e9dbad6fSeschrock 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1040e9dbad6fSeschrock 		goto error;
1041e9dbad6fSeschrock 	nvlist_free(nvl);
1042e9dbad6fSeschrock 	nvl = realprops;
1043e9dbad6fSeschrock 
1044e9dbad6fSeschrock 	prop = zfs_name_to_prop(propname);
1045e9dbad6fSeschrock 
1046fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
1047e9dbad6fSeschrock 		goto error;
1048fa9e4066Sahrens 
1049fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
105099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1051fa9e4066Sahrens 		    "child dataset with inherited mountpoint is used "
105299653d4eSeschrock 		    "in a non-global zone"));
105399653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1054fa9e4066Sahrens 		goto error;
1055fa9e4066Sahrens 	}
1056fa9e4066Sahrens 
1057fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1058fa9e4066Sahrens 		goto error;
1059fa9e4066Sahrens 
1060fa9e4066Sahrens 	/*
1061fa9e4066Sahrens 	 * Execute the corresponding ioctl() to set this property.
1062fa9e4066Sahrens 	 */
1063fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1064fa9e4066Sahrens 
1065e9dbad6fSeschrock 	if (zcmd_write_src_nvlist(hdl, &zc, nvl, NULL) != 0)
1066e9dbad6fSeschrock 		goto error;
1067e9dbad6fSeschrock 
1068e9dbad6fSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_SET_PROP, &zc);
1069fa9e4066Sahrens 
1070fa9e4066Sahrens 	if (ret != 0) {
1071fa9e4066Sahrens 		switch (errno) {
1072fa9e4066Sahrens 
1073fa9e4066Sahrens 		case ENOSPC:
1074fa9e4066Sahrens 			/*
1075fa9e4066Sahrens 			 * For quotas and reservations, ENOSPC indicates
1076fa9e4066Sahrens 			 * something different; setting a quota or reservation
1077fa9e4066Sahrens 			 * doesn't use any disk space.
1078fa9e4066Sahrens 			 */
1079fa9e4066Sahrens 			switch (prop) {
1080fa9e4066Sahrens 			case ZFS_PROP_QUOTA:
108199653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
108299653d4eSeschrock 				    "size is less than current used or "
108399653d4eSeschrock 				    "reserved space"));
108499653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1085fa9e4066Sahrens 				break;
1086fa9e4066Sahrens 
1087fa9e4066Sahrens 			case ZFS_PROP_RESERVATION:
108899653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
108999653d4eSeschrock 				    "size is greater than available space"));
109099653d4eSeschrock 				(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1091fa9e4066Sahrens 				break;
1092fa9e4066Sahrens 
1093fa9e4066Sahrens 			default:
109499653d4eSeschrock 				(void) zfs_standard_error(hdl, errno, errbuf);
1095fa9e4066Sahrens 				break;
1096fa9e4066Sahrens 			}
1097fa9e4066Sahrens 			break;
1098fa9e4066Sahrens 
1099fa9e4066Sahrens 		case EBUSY:
110099653d4eSeschrock 			if (prop == ZFS_PROP_VOLBLOCKSIZE)
110199653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLHASDATA, errbuf);
110299653d4eSeschrock 			else
1103e9dbad6fSeschrock 				(void) zfs_standard_error(hdl, EBUSY, errbuf);
1104fa9e4066Sahrens 			break;
1105fa9e4066Sahrens 
11062a79c5feSlling 		case EROFS:
110799653d4eSeschrock 			(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
11082a79c5feSlling 			break;
11092a79c5feSlling 
1110fa9e4066Sahrens 		case EOVERFLOW:
1111fa9e4066Sahrens 			/*
1112fa9e4066Sahrens 			 * This platform can't address a volume this big.
1113fa9e4066Sahrens 			 */
1114fa9e4066Sahrens #ifdef _ILP32
1115fa9e4066Sahrens 			if (prop == ZFS_PROP_VOLSIZE) {
111699653d4eSeschrock 				(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1117fa9e4066Sahrens 				break;
1118fa9e4066Sahrens 			}
1119fa9e4066Sahrens #endif
112099653d4eSeschrock 			/* FALLTHROUGH */
1121fa9e4066Sahrens 		default:
112299653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
1123fa9e4066Sahrens 		}
1124fa9e4066Sahrens 	} else {
1125fa9e4066Sahrens 		/*
1126fa9e4066Sahrens 		 * Refresh the statistics so the new property value
1127fa9e4066Sahrens 		 * is reflected.
1128fa9e4066Sahrens 		 */
1129e9dbad6fSeschrock 		if ((ret = changelist_postfix(cl)) == 0)
1130fa9e4066Sahrens 			(void) get_stats(zhp);
1131fa9e4066Sahrens 	}
1132fa9e4066Sahrens 
1133fa9e4066Sahrens error:
1134e9dbad6fSeschrock 	nvlist_free(nvl);
1135e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
1136e9dbad6fSeschrock 	if (cl)
1137fa9e4066Sahrens 		changelist_free(cl);
1138fa9e4066Sahrens 	return (ret);
1139fa9e4066Sahrens }
1140fa9e4066Sahrens 
1141fa9e4066Sahrens /*
1142fa9e4066Sahrens  * Given a property, inherit the value from the parent dataset.
1143fa9e4066Sahrens  */
1144fa9e4066Sahrens int
1145e9dbad6fSeschrock zfs_prop_inherit(zfs_handle_t *zhp, const char *propname)
1146fa9e4066Sahrens {
1147fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1148fa9e4066Sahrens 	int ret;
1149fa9e4066Sahrens 	prop_changelist_t *cl;
115099653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
115199653d4eSeschrock 	char errbuf[1024];
1152e9dbad6fSeschrock 	zfs_prop_t prop;
115399653d4eSeschrock 
115499653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
115599653d4eSeschrock 	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1156fa9e4066Sahrens 
1157e9dbad6fSeschrock 	if ((prop = zfs_name_to_prop(propname)) == ZFS_PROP_INVAL) {
1158e9dbad6fSeschrock 		/*
1159e9dbad6fSeschrock 		 * For user properties, the amount of work we have to do is very
1160e9dbad6fSeschrock 		 * small, so just do it here.
1161e9dbad6fSeschrock 		 */
1162e9dbad6fSeschrock 		if (!zfs_prop_user(propname)) {
1163e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1164e9dbad6fSeschrock 			    "invalid property"));
1165e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1166e9dbad6fSeschrock 		}
1167e9dbad6fSeschrock 
1168e9dbad6fSeschrock 		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1169e9dbad6fSeschrock 		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1170e9dbad6fSeschrock 
1171e9dbad6fSeschrock 		if (ioctl(zhp->zfs_hdl->libzfs_fd,
1172e9dbad6fSeschrock 		    ZFS_IOC_SET_PROP, &zc) != 0)
1173e9dbad6fSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1174e9dbad6fSeschrock 
1175e9dbad6fSeschrock 		return (0);
1176e9dbad6fSeschrock 	}
1177e9dbad6fSeschrock 
1178fa9e4066Sahrens 	/*
1179fa9e4066Sahrens 	 * Verify that this property is inheritable.
1180fa9e4066Sahrens 	 */
118199653d4eSeschrock 	if (zfs_prop_readonly(prop))
118299653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1183fa9e4066Sahrens 
118499653d4eSeschrock 	if (!zfs_prop_inheritable(prop))
118599653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1186fa9e4066Sahrens 
1187fa9e4066Sahrens 	/*
1188fa9e4066Sahrens 	 * Check to see if the value applies to this type
1189fa9e4066Sahrens 	 */
119099653d4eSeschrock 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
119199653d4eSeschrock 		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1192fa9e4066Sahrens 
1193fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1194e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1195fa9e4066Sahrens 
1196fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1197fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
119899653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
119999653d4eSeschrock 		    "dataset is used in a non-global zone"));
120099653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1201fa9e4066Sahrens 	}
1202fa9e4066Sahrens 
1203fa9e4066Sahrens 	/*
1204fa9e4066Sahrens 	 * Determine datasets which will be affected by this change, if any.
1205fa9e4066Sahrens 	 */
1206fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
1207fa9e4066Sahrens 		return (-1);
1208fa9e4066Sahrens 
1209fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
121099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
121199653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
121299653d4eSeschrock 		    "in a non-global zone"));
121399653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1214fa9e4066Sahrens 		goto error;
1215fa9e4066Sahrens 	}
1216fa9e4066Sahrens 
1217fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1218fa9e4066Sahrens 		goto error;
1219fa9e4066Sahrens 
122099653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd,
122199653d4eSeschrock 	    ZFS_IOC_SET_PROP, &zc)) != 0) {
122299653d4eSeschrock 		return (zfs_standard_error(hdl, errno, errbuf));
1223fa9e4066Sahrens 	} else {
1224fa9e4066Sahrens 
1225efc555ebSnd150628 		if ((ret = changelist_postfix(cl)) != 0)
1226fa9e4066Sahrens 			goto error;
1227fa9e4066Sahrens 
1228fa9e4066Sahrens 		/*
1229fa9e4066Sahrens 		 * Refresh the statistics so the new property is reflected.
1230fa9e4066Sahrens 		 */
1231fa9e4066Sahrens 		(void) get_stats(zhp);
1232fa9e4066Sahrens 	}
1233fa9e4066Sahrens 
1234fa9e4066Sahrens error:
1235fa9e4066Sahrens 	changelist_free(cl);
1236fa9e4066Sahrens 	return (ret);
1237fa9e4066Sahrens }
1238fa9e4066Sahrens 
1239fa9e4066Sahrens static void
1240fa9e4066Sahrens nicebool(int value, char *buf, size_t buflen)
1241fa9e4066Sahrens {
1242fa9e4066Sahrens 	if (value)
1243fa9e4066Sahrens 		(void) strlcpy(buf, "on", buflen);
1244fa9e4066Sahrens 	else
1245fa9e4066Sahrens 		(void) strlcpy(buf, "off", buflen);
1246fa9e4066Sahrens }
1247fa9e4066Sahrens 
1248fa9e4066Sahrens /*
12497f7322feSeschrock  * True DSL properties are stored in an nvlist.  The following two functions
12507f7322feSeschrock  * extract them appropriately.
12517f7322feSeschrock  */
12527f7322feSeschrock static uint64_t
12537f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
12547f7322feSeschrock {
12557f7322feSeschrock 	nvlist_t *nv;
12567f7322feSeschrock 	uint64_t value;
12577f7322feSeschrock 
1258a2eea2e1Sahrens 	*source = NULL;
12597f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
12607f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
12617f7322feSeschrock 		verify(nvlist_lookup_uint64(nv, ZFS_PROP_VALUE, &value) == 0);
1262a2eea2e1Sahrens 		(void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
12637f7322feSeschrock 	} else {
12647f7322feSeschrock 		value = zfs_prop_default_numeric(prop);
12657f7322feSeschrock 		*source = "";
12667f7322feSeschrock 	}
12677f7322feSeschrock 
12687f7322feSeschrock 	return (value);
12697f7322feSeschrock }
12707f7322feSeschrock 
12717f7322feSeschrock static char *
12727f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
12737f7322feSeschrock {
12747f7322feSeschrock 	nvlist_t *nv;
12757f7322feSeschrock 	char *value;
12767f7322feSeschrock 
1277a2eea2e1Sahrens 	*source = NULL;
12787f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
12797f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
12807f7322feSeschrock 		verify(nvlist_lookup_string(nv, ZFS_PROP_VALUE, &value) == 0);
1281a2eea2e1Sahrens 		(void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
12827f7322feSeschrock 	} else {
12837f7322feSeschrock 		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
12847f7322feSeschrock 			value = "";
12857f7322feSeschrock 		*source = "";
12867f7322feSeschrock 	}
12877f7322feSeschrock 
12887f7322feSeschrock 	return (value);
12897f7322feSeschrock }
12907f7322feSeschrock 
12917f7322feSeschrock /*
1292fa9e4066Sahrens  * Internal function for getting a numeric property.  Both zfs_prop_get() and
1293fa9e4066Sahrens  * zfs_prop_get_int() are built using this interface.
1294fa9e4066Sahrens  *
1295fa9e4066Sahrens  * Certain properties can be overridden using 'mount -o'.  In this case, scan
1296fa9e4066Sahrens  * the contents of the /etc/mnttab entry, searching for the appropriate options.
1297fa9e4066Sahrens  * If they differ from the on-disk values, report the current values and mark
1298fa9e4066Sahrens  * the source "temporary".
1299fa9e4066Sahrens  */
130099653d4eSeschrock static int
1301fa9e4066Sahrens get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
130299653d4eSeschrock     char **source, uint64_t *val)
1303fa9e4066Sahrens {
1304fa9e4066Sahrens 	struct mnttab mnt;
13053ccfa83cSahrens 	char *mntopt_on = NULL;
13063ccfa83cSahrens 	char *mntopt_off = NULL;
1307fa9e4066Sahrens 
1308fa9e4066Sahrens 	*source = NULL;
1309fa9e4066Sahrens 
13103ccfa83cSahrens 	switch (prop) {
13113ccfa83cSahrens 	case ZFS_PROP_ATIME:
13123ccfa83cSahrens 		mntopt_on = MNTOPT_ATIME;
13133ccfa83cSahrens 		mntopt_off = MNTOPT_NOATIME;
13143ccfa83cSahrens 		break;
13153ccfa83cSahrens 
13163ccfa83cSahrens 	case ZFS_PROP_DEVICES:
13173ccfa83cSahrens 		mntopt_on = MNTOPT_DEVICES;
13183ccfa83cSahrens 		mntopt_off = MNTOPT_NODEVICES;
13193ccfa83cSahrens 		break;
13203ccfa83cSahrens 
13213ccfa83cSahrens 	case ZFS_PROP_EXEC:
13223ccfa83cSahrens 		mntopt_on = MNTOPT_EXEC;
13233ccfa83cSahrens 		mntopt_off = MNTOPT_NOEXEC;
13243ccfa83cSahrens 		break;
13253ccfa83cSahrens 
13263ccfa83cSahrens 	case ZFS_PROP_READONLY:
13273ccfa83cSahrens 		mntopt_on = MNTOPT_RO;
13283ccfa83cSahrens 		mntopt_off = MNTOPT_RW;
13293ccfa83cSahrens 		break;
13303ccfa83cSahrens 
13313ccfa83cSahrens 	case ZFS_PROP_SETUID:
13323ccfa83cSahrens 		mntopt_on = MNTOPT_SETUID;
13333ccfa83cSahrens 		mntopt_off = MNTOPT_NOSETUID;
13343ccfa83cSahrens 		break;
13353ccfa83cSahrens 
13363ccfa83cSahrens 	case ZFS_PROP_XATTR:
13373ccfa83cSahrens 		mntopt_on = MNTOPT_XATTR;
13383ccfa83cSahrens 		mntopt_off = MNTOPT_NOXATTR;
13393ccfa83cSahrens 		break;
13403ccfa83cSahrens 	}
13413ccfa83cSahrens 
13423bb79becSeschrock 	/*
13433bb79becSeschrock 	 * Because looking up the mount options is potentially expensive
13443bb79becSeschrock 	 * (iterating over all of /etc/mnttab), we defer its calculation until
13453bb79becSeschrock 	 * we're looking up a property which requires its presence.
13463bb79becSeschrock 	 */
13473bb79becSeschrock 	if (!zhp->zfs_mntcheck &&
13483ccfa83cSahrens 	    (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
13493ccfa83cSahrens 		struct mnttab entry, search = { 0 };
13503ccfa83cSahrens 		FILE *mnttab = zhp->zfs_hdl->libzfs_mnttab;
13513bb79becSeschrock 
13523bb79becSeschrock 		search.mnt_special = (char *)zhp->zfs_name;
13533bb79becSeschrock 		search.mnt_fstype = MNTTYPE_ZFS;
13543ccfa83cSahrens 		rewind(mnttab);
13553bb79becSeschrock 
13563ccfa83cSahrens 		if (getmntany(mnttab, &entry, &search) == 0) {
13573ccfa83cSahrens 			zhp->zfs_mntopts = zfs_strdup(zhp->zfs_hdl,
13583ccfa83cSahrens 			    entry.mnt_mntopts);
13593ccfa83cSahrens 			if (zhp->zfs_mntopts == NULL)
13603bb79becSeschrock 				return (-1);
13613ccfa83cSahrens 		}
13623bb79becSeschrock 
13633bb79becSeschrock 		zhp->zfs_mntcheck = B_TRUE;
13643bb79becSeschrock 	}
13653bb79becSeschrock 
1366fa9e4066Sahrens 	if (zhp->zfs_mntopts == NULL)
1367fa9e4066Sahrens 		mnt.mnt_mntopts = "";
1368fa9e4066Sahrens 	else
1369fa9e4066Sahrens 		mnt.mnt_mntopts = zhp->zfs_mntopts;
1370fa9e4066Sahrens 
1371fa9e4066Sahrens 	switch (prop) {
1372fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1373fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1374fa9e4066Sahrens 	case ZFS_PROP_EXEC:
13753ccfa83cSahrens 	case ZFS_PROP_READONLY:
13763ccfa83cSahrens 	case ZFS_PROP_SETUID:
13773ccfa83cSahrens 	case ZFS_PROP_XATTR:
137899653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1379fa9e4066Sahrens 
13803ccfa83cSahrens 		if (hasmntopt(&mnt, mntopt_on) && !*val) {
138199653d4eSeschrock 			*val = B_TRUE;
1382fa9e4066Sahrens 			if (src)
1383fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
13843ccfa83cSahrens 		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
138599653d4eSeschrock 			*val = B_FALSE;
1386fa9e4066Sahrens 			if (src)
1387fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1388fa9e4066Sahrens 		}
138999653d4eSeschrock 		break;
1390fa9e4066Sahrens 
1391fa9e4066Sahrens 	case ZFS_PROP_RECORDSIZE:
1392fa9e4066Sahrens 	case ZFS_PROP_COMPRESSION:
13937f7322feSeschrock 	case ZFS_PROP_ZONED:
1394a2eea2e1Sahrens 	case ZFS_PROP_CREATION:
1395a2eea2e1Sahrens 	case ZFS_PROP_COMPRESSRATIO:
1396a2eea2e1Sahrens 	case ZFS_PROP_REFERENCED:
1397a2eea2e1Sahrens 	case ZFS_PROP_USED:
1398a2eea2e1Sahrens 	case ZFS_PROP_CREATETXG:
1399a2eea2e1Sahrens 	case ZFS_PROP_AVAILABLE:
1400a2eea2e1Sahrens 	case ZFS_PROP_VOLSIZE:
1401a2eea2e1Sahrens 	case ZFS_PROP_VOLBLOCKSIZE:
1402*fda77a98Srm160521 		*val = getprop_uint64(zhp, prop, source);
1403*fda77a98Srm160521 		break;
1404*fda77a98Srm160521 
14053ccfa83cSahrens 	case ZFS_PROP_CANMOUNT:
140699653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1407*fda77a98Srm160521 		if (*val == 0)
1408*fda77a98Srm160521 			*source = zhp->zfs_name;
1409*fda77a98Srm160521 		else
1410*fda77a98Srm160521 			*source = "";	/* default */
141199653d4eSeschrock 		break;
1412fa9e4066Sahrens 
1413fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1414fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1415a2eea2e1Sahrens 		*val = getprop_uint64(zhp, prop, source);
1416a2eea2e1Sahrens 		if (*val == 0)
1417fa9e4066Sahrens 			*source = "";	/* default */
1418fa9e4066Sahrens 		else
1419fa9e4066Sahrens 			*source = zhp->zfs_name;
142099653d4eSeschrock 		break;
1421fa9e4066Sahrens 
1422fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
142399653d4eSeschrock 		*val = (zhp->zfs_mntopts != NULL);
142499653d4eSeschrock 		break;
1425fa9e4066Sahrens 
142639c23413Seschrock 	case ZFS_PROP_NUMCLONES:
142739c23413Seschrock 		*val = zhp->zfs_dmustats.dds_num_clones;
142839c23413Seschrock 		break;
142939c23413Seschrock 
1430fa9e4066Sahrens 	default:
143199653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
143299653d4eSeschrock 		    "cannot get non-numeric property"));
143399653d4eSeschrock 		return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
143499653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "internal error")));
1435fa9e4066Sahrens 	}
1436fa9e4066Sahrens 
1437fa9e4066Sahrens 	return (0);
1438fa9e4066Sahrens }
1439fa9e4066Sahrens 
1440fa9e4066Sahrens /*
1441fa9e4066Sahrens  * Calculate the source type, given the raw source string.
1442fa9e4066Sahrens  */
1443fa9e4066Sahrens static void
1444fa9e4066Sahrens get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source,
1445fa9e4066Sahrens     char *statbuf, size_t statlen)
1446fa9e4066Sahrens {
1447fa9e4066Sahrens 	if (statbuf == NULL || *srctype == ZFS_SRC_TEMPORARY)
1448fa9e4066Sahrens 		return;
1449fa9e4066Sahrens 
1450fa9e4066Sahrens 	if (source == NULL) {
1451fa9e4066Sahrens 		*srctype = ZFS_SRC_NONE;
1452fa9e4066Sahrens 	} else if (source[0] == '\0') {
1453fa9e4066Sahrens 		*srctype = ZFS_SRC_DEFAULT;
1454fa9e4066Sahrens 	} else {
1455fa9e4066Sahrens 		if (strcmp(source, zhp->zfs_name) == 0) {
1456fa9e4066Sahrens 			*srctype = ZFS_SRC_LOCAL;
1457fa9e4066Sahrens 		} else {
1458fa9e4066Sahrens 			(void) strlcpy(statbuf, source, statlen);
1459fa9e4066Sahrens 			*srctype = ZFS_SRC_INHERITED;
1460fa9e4066Sahrens 		}
1461fa9e4066Sahrens 	}
1462fa9e4066Sahrens 
1463fa9e4066Sahrens }
1464fa9e4066Sahrens 
1465fa9e4066Sahrens /*
1466fa9e4066Sahrens  * Retrieve a property from the given object.  If 'literal' is specified, then
1467fa9e4066Sahrens  * numbers are left as exact values.  Otherwise, numbers are converted to a
1468fa9e4066Sahrens  * human-readable form.
1469fa9e4066Sahrens  *
1470fa9e4066Sahrens  * Returns 0 on success, or -1 on error.
1471fa9e4066Sahrens  */
1472fa9e4066Sahrens int
1473fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
147499653d4eSeschrock     zfs_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1475fa9e4066Sahrens {
1476fa9e4066Sahrens 	char *source = NULL;
1477fa9e4066Sahrens 	uint64_t val;
1478fa9e4066Sahrens 	char *str;
1479fa9e4066Sahrens 	const char *root;
1480e9dbad6fSeschrock 	const char *strval;
1481fa9e4066Sahrens 
1482fa9e4066Sahrens 	/*
1483fa9e4066Sahrens 	 * Check to see if this property applies to our object
1484fa9e4066Sahrens 	 */
1485fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1486fa9e4066Sahrens 		return (-1);
1487fa9e4066Sahrens 
1488fa9e4066Sahrens 	if (src)
1489fa9e4066Sahrens 		*src = ZFS_SRC_NONE;
1490fa9e4066Sahrens 
1491fa9e4066Sahrens 	switch (prop) {
1492fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1493fa9e4066Sahrens 	case ZFS_PROP_READONLY:
1494fa9e4066Sahrens 	case ZFS_PROP_SETUID:
1495fa9e4066Sahrens 	case ZFS_PROP_ZONED:
1496fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1497fa9e4066Sahrens 	case ZFS_PROP_EXEC:
1498e9dbad6fSeschrock 	case ZFS_PROP_CANMOUNT:
14997b55fa8eSck153898 	case ZFS_PROP_XATTR:
1500fa9e4066Sahrens 		/*
1501fa9e4066Sahrens 		 * Basic boolean values are built on top of
1502fa9e4066Sahrens 		 * get_numeric_property().
1503fa9e4066Sahrens 		 */
150499653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
150599653d4eSeschrock 			return (-1);
150699653d4eSeschrock 		nicebool(val, propbuf, proplen);
1507fa9e4066Sahrens 
1508fa9e4066Sahrens 		break;
1509fa9e4066Sahrens 
1510fa9e4066Sahrens 	case ZFS_PROP_AVAILABLE:
1511fa9e4066Sahrens 	case ZFS_PROP_RECORDSIZE:
1512fa9e4066Sahrens 	case ZFS_PROP_CREATETXG:
1513fa9e4066Sahrens 	case ZFS_PROP_REFERENCED:
1514fa9e4066Sahrens 	case ZFS_PROP_USED:
1515fa9e4066Sahrens 	case ZFS_PROP_VOLSIZE:
1516fa9e4066Sahrens 	case ZFS_PROP_VOLBLOCKSIZE:
151739c23413Seschrock 	case ZFS_PROP_NUMCLONES:
1518fa9e4066Sahrens 		/*
1519fa9e4066Sahrens 		 * Basic numeric values are built on top of
1520fa9e4066Sahrens 		 * get_numeric_property().
1521fa9e4066Sahrens 		 */
152299653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
152399653d4eSeschrock 			return (-1);
1524fa9e4066Sahrens 		if (literal)
15255ad82045Snd150628 			(void) snprintf(propbuf, proplen, "%llu",
15265ad82045Snd150628 			(u_longlong_t)val);
1527fa9e4066Sahrens 		else
1528fa9e4066Sahrens 			zfs_nicenum(val, propbuf, proplen);
1529fa9e4066Sahrens 		break;
1530fa9e4066Sahrens 
1531fa9e4066Sahrens 	case ZFS_PROP_COMPRESSION:
1532fa9e4066Sahrens 	case ZFS_PROP_CHECKSUM:
1533fa9e4066Sahrens 	case ZFS_PROP_SNAPDIR:
1534fa9e4066Sahrens 	case ZFS_PROP_ACLMODE:
1535fa9e4066Sahrens 	case ZFS_PROP_ACLINHERIT:
15367f7322feSeschrock 		val = getprop_uint64(zhp, prop, &source);
1537e9dbad6fSeschrock 		verify(zfs_prop_index_to_string(prop, val, &strval) == 0);
1538e9dbad6fSeschrock 		(void) strlcpy(propbuf, strval, proplen);
1539fa9e4066Sahrens 		break;
1540fa9e4066Sahrens 
1541fa9e4066Sahrens 	case ZFS_PROP_CREATION:
1542fa9e4066Sahrens 		/*
1543fa9e4066Sahrens 		 * 'creation' is a time_t stored in the statistics.  We convert
1544fa9e4066Sahrens 		 * this into a string unless 'literal' is specified.
1545fa9e4066Sahrens 		 */
1546fa9e4066Sahrens 		{
1547a2eea2e1Sahrens 			val = getprop_uint64(zhp, prop, &source);
1548a2eea2e1Sahrens 			time_t time = (time_t)val;
1549fa9e4066Sahrens 			struct tm t;
1550fa9e4066Sahrens 
1551fa9e4066Sahrens 			if (literal ||
1552fa9e4066Sahrens 			    localtime_r(&time, &t) == NULL ||
1553fa9e4066Sahrens 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1554fa9e4066Sahrens 			    &t) == 0)
1555a2eea2e1Sahrens 				(void) snprintf(propbuf, proplen, "%llu", val);
1556fa9e4066Sahrens 		}
1557fa9e4066Sahrens 		break;
1558fa9e4066Sahrens 
1559fa9e4066Sahrens 	case ZFS_PROP_MOUNTPOINT:
1560fa9e4066Sahrens 		/*
1561fa9e4066Sahrens 		 * Getting the precise mountpoint can be tricky.
1562fa9e4066Sahrens 		 *
1563fa9e4066Sahrens 		 *  - for 'none' or 'legacy', return those values.
1564fa9e4066Sahrens 		 *  - for default mountpoints, construct it as /zfs/<dataset>
1565fa9e4066Sahrens 		 *  - for inherited mountpoints, we want to take everything
1566fa9e4066Sahrens 		 *    after our ancestor and append it to the inherited value.
1567fa9e4066Sahrens 		 *
1568fa9e4066Sahrens 		 * If the pool has an alternate root, we want to prepend that
1569fa9e4066Sahrens 		 * root to any values we return.
1570fa9e4066Sahrens 		 */
1571ea8dc4b6Seschrock 		root = zhp->zfs_root;
15727f7322feSeschrock 		str = getprop_string(zhp, prop, &source);
1573fa9e4066Sahrens 
15747f7322feSeschrock 		if (str[0] == '\0') {
1575fa9e4066Sahrens 			(void) snprintf(propbuf, proplen, "%s/zfs/%s",
1576fa9e4066Sahrens 			    root, zhp->zfs_name);
15777f7322feSeschrock 		} else if (str[0] == '/') {
15787f7322feSeschrock 			const char *relpath = zhp->zfs_name + strlen(source);
1579fa9e4066Sahrens 
1580fa9e4066Sahrens 			if (relpath[0] == '/')
1581fa9e4066Sahrens 				relpath++;
15827f7322feSeschrock 			if (str[1] == '\0')
15837f7322feSeschrock 				str++;
1584fa9e4066Sahrens 
1585fa9e4066Sahrens 			if (relpath[0] == '\0')
1586fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s",
15877f7322feSeschrock 				    root, str);
1588fa9e4066Sahrens 			else
1589fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
15907f7322feSeschrock 				    root, str, relpath[0] == '@' ? "" : "/",
1591fa9e4066Sahrens 				    relpath);
1592fa9e4066Sahrens 		} else {
1593fa9e4066Sahrens 			/* 'legacy' or 'none' */
15947f7322feSeschrock 			(void) strlcpy(propbuf, str, proplen);
1595fa9e4066Sahrens 		}
1596fa9e4066Sahrens 
1597fa9e4066Sahrens 		break;
1598fa9e4066Sahrens 
1599fa9e4066Sahrens 	case ZFS_PROP_SHARENFS:
1600f3861e1aSahl 	case ZFS_PROP_SHAREISCSI:
1601f3861e1aSahl 	case ZFS_PROP_ISCSIOPTIONS:
16027f7322feSeschrock 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
16037f7322feSeschrock 		    proplen);
1604fa9e4066Sahrens 		break;
1605fa9e4066Sahrens 
1606fa9e4066Sahrens 	case ZFS_PROP_ORIGIN:
1607a2eea2e1Sahrens 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
1608fa9e4066Sahrens 		    proplen);
1609fa9e4066Sahrens 		/*
1610fa9e4066Sahrens 		 * If there is no parent at all, return failure to indicate that
1611fa9e4066Sahrens 		 * it doesn't apply to this dataset.
1612fa9e4066Sahrens 		 */
1613fa9e4066Sahrens 		if (propbuf[0] == '\0')
1614fa9e4066Sahrens 			return (-1);
1615fa9e4066Sahrens 		break;
1616fa9e4066Sahrens 
1617fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1618fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
161999653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
162099653d4eSeschrock 			return (-1);
1621fa9e4066Sahrens 
1622fa9e4066Sahrens 		/*
1623fa9e4066Sahrens 		 * If quota or reservation is 0, we translate this into 'none'
1624fa9e4066Sahrens 		 * (unless literal is set), and indicate that it's the default
1625fa9e4066Sahrens 		 * value.  Otherwise, we print the number nicely and indicate
1626fa9e4066Sahrens 		 * that its set locally.
1627fa9e4066Sahrens 		 */
1628fa9e4066Sahrens 		if (val == 0) {
1629fa9e4066Sahrens 			if (literal)
1630fa9e4066Sahrens 				(void) strlcpy(propbuf, "0", proplen);
1631fa9e4066Sahrens 			else
1632fa9e4066Sahrens 				(void) strlcpy(propbuf, "none", proplen);
1633fa9e4066Sahrens 		} else {
1634fa9e4066Sahrens 			if (literal)
16355ad82045Snd150628 				(void) snprintf(propbuf, proplen, "%llu",
16365ad82045Snd150628 				(u_longlong_t)val);
1637fa9e4066Sahrens 			else
1638fa9e4066Sahrens 				zfs_nicenum(val, propbuf, proplen);
1639fa9e4066Sahrens 		}
1640fa9e4066Sahrens 		break;
1641fa9e4066Sahrens 
1642fa9e4066Sahrens 	case ZFS_PROP_COMPRESSRATIO:
164399653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
164499653d4eSeschrock 			return (-1);
16455ad82045Snd150628 		(void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t)
16465ad82045Snd150628 		    val / 100, (longlong_t)val % 100);
1647fa9e4066Sahrens 		break;
1648fa9e4066Sahrens 
1649fa9e4066Sahrens 	case ZFS_PROP_TYPE:
1650fa9e4066Sahrens 		switch (zhp->zfs_type) {
1651fa9e4066Sahrens 		case ZFS_TYPE_FILESYSTEM:
1652fa9e4066Sahrens 			str = "filesystem";
1653fa9e4066Sahrens 			break;
1654fa9e4066Sahrens 		case ZFS_TYPE_VOLUME:
1655fa9e4066Sahrens 			str = "volume";
1656fa9e4066Sahrens 			break;
1657fa9e4066Sahrens 		case ZFS_TYPE_SNAPSHOT:
1658fa9e4066Sahrens 			str = "snapshot";
1659fa9e4066Sahrens 			break;
1660fa9e4066Sahrens 		default:
166199653d4eSeschrock 			abort();
1662fa9e4066Sahrens 		}
1663fa9e4066Sahrens 		(void) snprintf(propbuf, proplen, "%s", str);
1664fa9e4066Sahrens 		break;
1665fa9e4066Sahrens 
1666fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
1667fa9e4066Sahrens 		/*
1668fa9e4066Sahrens 		 * The 'mounted' property is a pseudo-property that described
1669fa9e4066Sahrens 		 * whether the filesystem is currently mounted.  Even though
1670fa9e4066Sahrens 		 * it's a boolean value, the typical values of "on" and "off"
1671fa9e4066Sahrens 		 * don't make sense, so we translate to "yes" and "no".
1672fa9e4066Sahrens 		 */
167399653d4eSeschrock 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
167499653d4eSeschrock 		    src, &source, &val) != 0)
167599653d4eSeschrock 			return (-1);
167699653d4eSeschrock 		if (val)
1677fa9e4066Sahrens 			(void) strlcpy(propbuf, "yes", proplen);
1678fa9e4066Sahrens 		else
1679fa9e4066Sahrens 			(void) strlcpy(propbuf, "no", proplen);
1680fa9e4066Sahrens 		break;
1681fa9e4066Sahrens 
1682fa9e4066Sahrens 	case ZFS_PROP_NAME:
1683fa9e4066Sahrens 		/*
1684fa9e4066Sahrens 		 * The 'name' property is a pseudo-property derived from the
1685fa9e4066Sahrens 		 * dataset name.  It is presented as a real property to simplify
1686fa9e4066Sahrens 		 * consumers.
1687fa9e4066Sahrens 		 */
1688fa9e4066Sahrens 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
1689fa9e4066Sahrens 		break;
1690fa9e4066Sahrens 
1691fa9e4066Sahrens 	default:
169299653d4eSeschrock 		abort();
1693fa9e4066Sahrens 	}
1694fa9e4066Sahrens 
1695fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1696fa9e4066Sahrens 
1697fa9e4066Sahrens 	return (0);
1698fa9e4066Sahrens }
1699fa9e4066Sahrens 
1700fa9e4066Sahrens /*
1701fa9e4066Sahrens  * Utility function to get the given numeric property.  Does no validation that
1702fa9e4066Sahrens  * the given property is the appropriate type; should only be used with
1703fa9e4066Sahrens  * hard-coded property types.
1704fa9e4066Sahrens  */
1705fa9e4066Sahrens uint64_t
1706fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
1707fa9e4066Sahrens {
1708fa9e4066Sahrens 	char *source;
1709fa9e4066Sahrens 	zfs_source_t sourcetype = ZFS_SRC_NONE;
171099653d4eSeschrock 	uint64_t val;
1711fa9e4066Sahrens 
171299653d4eSeschrock 	(void) get_numeric_property(zhp, prop, &sourcetype, &source, &val);
171399653d4eSeschrock 
171499653d4eSeschrock 	return (val);
1715fa9e4066Sahrens }
1716fa9e4066Sahrens 
1717fa9e4066Sahrens /*
1718fa9e4066Sahrens  * Similar to zfs_prop_get(), but returns the value as an integer.
1719fa9e4066Sahrens  */
1720fa9e4066Sahrens int
1721fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
1722fa9e4066Sahrens     zfs_source_t *src, char *statbuf, size_t statlen)
1723fa9e4066Sahrens {
1724fa9e4066Sahrens 	char *source;
1725fa9e4066Sahrens 
1726fa9e4066Sahrens 	/*
1727fa9e4066Sahrens 	 * Check to see if this property applies to our object
1728fa9e4066Sahrens 	 */
1729fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1730ece3d9b3Slling 		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
173199653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
173299653d4eSeschrock 		    zfs_prop_to_name(prop)));
1733fa9e4066Sahrens 
1734fa9e4066Sahrens 	if (src)
1735fa9e4066Sahrens 		*src = ZFS_SRC_NONE;
1736fa9e4066Sahrens 
173799653d4eSeschrock 	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
173899653d4eSeschrock 		return (-1);
1739fa9e4066Sahrens 
1740fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1741fa9e4066Sahrens 
1742fa9e4066Sahrens 	return (0);
1743fa9e4066Sahrens }
1744fa9e4066Sahrens 
1745fa9e4066Sahrens /*
1746fa9e4066Sahrens  * Returns the name of the given zfs handle.
1747fa9e4066Sahrens  */
1748fa9e4066Sahrens const char *
1749fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp)
1750fa9e4066Sahrens {
1751fa9e4066Sahrens 	return (zhp->zfs_name);
1752fa9e4066Sahrens }
1753fa9e4066Sahrens 
1754fa9e4066Sahrens /*
1755fa9e4066Sahrens  * Returns the type of the given zfs handle.
1756fa9e4066Sahrens  */
1757fa9e4066Sahrens zfs_type_t
1758fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp)
1759fa9e4066Sahrens {
1760fa9e4066Sahrens 	return (zhp->zfs_type);
1761fa9e4066Sahrens }
1762fa9e4066Sahrens 
1763fa9e4066Sahrens /*
17647f7322feSeschrock  * Iterate over all child filesystems
1765fa9e4066Sahrens  */
1766fa9e4066Sahrens int
17677f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
1768fa9e4066Sahrens {
1769fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1770fa9e4066Sahrens 	zfs_handle_t *nzhp;
1771fa9e4066Sahrens 	int ret;
1772fa9e4066Sahrens 
1773fa9e4066Sahrens 	for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
177499653d4eSeschrock 	    ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
1775fa9e4066Sahrens 	    (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
1776fa9e4066Sahrens 		/*
1777fa9e4066Sahrens 		 * Ignore private dataset names.
1778fa9e4066Sahrens 		 */
1779fa9e4066Sahrens 		if (dataset_name_hidden(zc.zc_name))
1780fa9e4066Sahrens 			continue;
1781fa9e4066Sahrens 
1782fa9e4066Sahrens 		/*
1783fa9e4066Sahrens 		 * Silently ignore errors, as the only plausible explanation is
1784fa9e4066Sahrens 		 * that the pool has since been removed.
1785fa9e4066Sahrens 		 */
178699653d4eSeschrock 		if ((nzhp = make_dataset_handle(zhp->zfs_hdl,
178799653d4eSeschrock 		    zc.zc_name)) == NULL)
1788fa9e4066Sahrens 			continue;
1789fa9e4066Sahrens 
1790fa9e4066Sahrens 		if ((ret = func(nzhp, data)) != 0)
1791fa9e4066Sahrens 			return (ret);
1792fa9e4066Sahrens 	}
1793fa9e4066Sahrens 
1794fa9e4066Sahrens 	/*
1795fa9e4066Sahrens 	 * An errno value of ESRCH indicates normal completion.  If ENOENT is
1796fa9e4066Sahrens 	 * returned, then the underlying dataset has been removed since we
1797fa9e4066Sahrens 	 * obtained the handle.
1798fa9e4066Sahrens 	 */
1799fa9e4066Sahrens 	if (errno != ESRCH && errno != ENOENT)
180099653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
180199653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot iterate filesystems")));
1802fa9e4066Sahrens 
18037f7322feSeschrock 	return (0);
18047f7322feSeschrock }
18057f7322feSeschrock 
18067f7322feSeschrock /*
18077f7322feSeschrock  * Iterate over all snapshots
18087f7322feSeschrock  */
18097f7322feSeschrock int
18107f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
18117f7322feSeschrock {
18127f7322feSeschrock 	zfs_cmd_t zc = { 0 };
18137f7322feSeschrock 	zfs_handle_t *nzhp;
18147f7322feSeschrock 	int ret;
1815fa9e4066Sahrens 
1816fa9e4066Sahrens 	for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
181799653d4eSeschrock 	    ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
181899653d4eSeschrock 	    &zc) == 0;
1819fa9e4066Sahrens 	    (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
1820fa9e4066Sahrens 
182199653d4eSeschrock 		if ((nzhp = make_dataset_handle(zhp->zfs_hdl,
182299653d4eSeschrock 		    zc.zc_name)) == NULL)
1823fa9e4066Sahrens 			continue;
1824fa9e4066Sahrens 
1825fa9e4066Sahrens 		if ((ret = func(nzhp, data)) != 0)
1826fa9e4066Sahrens 			return (ret);
1827fa9e4066Sahrens 	}
1828fa9e4066Sahrens 
1829fa9e4066Sahrens 	/*
1830fa9e4066Sahrens 	 * An errno value of ESRCH indicates normal completion.  If ENOENT is
1831fa9e4066Sahrens 	 * returned, then the underlying dataset has been removed since we
1832fa9e4066Sahrens 	 * obtained the handle.  Silently ignore this case, and return success.
1833fa9e4066Sahrens 	 */
1834fa9e4066Sahrens 	if (errno != ESRCH && errno != ENOENT)
183599653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
183699653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot iterate filesystems")));
1837fa9e4066Sahrens 
1838fa9e4066Sahrens 	return (0);
1839fa9e4066Sahrens }
1840fa9e4066Sahrens 
1841fa9e4066Sahrens /*
18427f7322feSeschrock  * Iterate over all children, snapshots and filesystems
18437f7322feSeschrock  */
18447f7322feSeschrock int
18457f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
18467f7322feSeschrock {
18477f7322feSeschrock 	int ret;
18487f7322feSeschrock 
18497f7322feSeschrock 	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
18507f7322feSeschrock 		return (ret);
18517f7322feSeschrock 
18527f7322feSeschrock 	return (zfs_iter_snapshots(zhp, func, data));
18537f7322feSeschrock }
18547f7322feSeschrock 
18557f7322feSeschrock /*
1856fa9e4066Sahrens  * Given a complete name, return just the portion that refers to the parent.
1857fa9e4066Sahrens  * Can return NULL if this is a pool.
1858fa9e4066Sahrens  */
1859fa9e4066Sahrens static int
1860fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen)
1861fa9e4066Sahrens {
1862fa9e4066Sahrens 	char *loc;
1863fa9e4066Sahrens 
1864fa9e4066Sahrens 	if ((loc = strrchr(path, '/')) == NULL)
1865fa9e4066Sahrens 		return (-1);
1866fa9e4066Sahrens 
1867fa9e4066Sahrens 	(void) strncpy(buf, path, MIN(buflen, loc - path));
1868fa9e4066Sahrens 	buf[loc - path] = '\0';
1869fa9e4066Sahrens 
1870fa9e4066Sahrens 	return (0);
1871fa9e4066Sahrens }
1872fa9e4066Sahrens 
1873fa9e4066Sahrens /*
1874e9dbad6fSeschrock  * Checks to make sure that the given path has a parent, and that it exists.  We
1875e9dbad6fSeschrock  * also fetch the 'zoned' property, which is used to validate property settings
1876e9dbad6fSeschrock  * when creating new datasets.
1877fa9e4066Sahrens  */
1878fa9e4066Sahrens static int
1879e9dbad6fSeschrock check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
1880fa9e4066Sahrens {
1881fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1882fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
1883fa9e4066Sahrens 	char *slash;
18847f7322feSeschrock 	zfs_handle_t *zhp;
188599653d4eSeschrock 	char errbuf[1024];
188699653d4eSeschrock 
188799653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), "cannot create '%s'",
188899653d4eSeschrock 	    path);
1889fa9e4066Sahrens 
1890fa9e4066Sahrens 	/* get parent, and check to see if this is just a pool */
1891fa9e4066Sahrens 	if (parent_name(path, parent, sizeof (parent)) != 0) {
189299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
189399653d4eSeschrock 		    "missing dataset name"));
189499653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
1895fa9e4066Sahrens 	}
1896fa9e4066Sahrens 
1897fa9e4066Sahrens 	/* check to see if the pool exists */
1898fa9e4066Sahrens 	if ((slash = strchr(parent, '/')) == NULL)
1899fa9e4066Sahrens 		slash = parent + strlen(parent);
1900fa9e4066Sahrens 	(void) strncpy(zc.zc_name, parent, slash - parent);
1901fa9e4066Sahrens 	zc.zc_name[slash - parent] = '\0';
190299653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
1903fa9e4066Sahrens 	    errno == ENOENT) {
190499653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
190599653d4eSeschrock 		    "no such pool '%s'"), zc.zc_name);
190699653d4eSeschrock 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
1907fa9e4066Sahrens 	}
1908fa9e4066Sahrens 
1909fa9e4066Sahrens 	/* check to see if the parent dataset exists */
191099653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
1911fa9e4066Sahrens 		switch (errno) {
1912fa9e4066Sahrens 		case ENOENT:
191399653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
191499653d4eSeschrock 			    "parent does not exist"));
191599653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
1916fa9e4066Sahrens 
1917fa9e4066Sahrens 		default:
191899653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1919fa9e4066Sahrens 		}
1920fa9e4066Sahrens 	}
1921fa9e4066Sahrens 
1922e9dbad6fSeschrock 	*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
1923fa9e4066Sahrens 	/* we are in a non-global zone, but parent is in the global zone */
1924e9dbad6fSeschrock 	if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) {
192599653d4eSeschrock 		(void) zfs_standard_error(hdl, EPERM, errbuf);
19267f7322feSeschrock 		zfs_close(zhp);
1927fa9e4066Sahrens 		return (-1);
1928fa9e4066Sahrens 	}
1929fa9e4066Sahrens 
1930fa9e4066Sahrens 	/* make sure parent is a filesystem */
19317f7322feSeschrock 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
193299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
193399653d4eSeschrock 		    "parent is not a filesystem"));
193499653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
19357f7322feSeschrock 		zfs_close(zhp);
1936fa9e4066Sahrens 		return (-1);
1937fa9e4066Sahrens 	}
1938fa9e4066Sahrens 
19397f7322feSeschrock 	zfs_close(zhp);
1940fa9e4066Sahrens 	return (0);
1941fa9e4066Sahrens }
1942fa9e4066Sahrens 
1943fa9e4066Sahrens /*
1944e9dbad6fSeschrock  * Create a new filesystem or volume.
1945fa9e4066Sahrens  */
1946fa9e4066Sahrens int
194799653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
1948e9dbad6fSeschrock     nvlist_t *props)
1949fa9e4066Sahrens {
1950fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1951fa9e4066Sahrens 	int ret;
1952fa9e4066Sahrens 	uint64_t size = 0;
1953fa9e4066Sahrens 	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
195499653d4eSeschrock 	char errbuf[1024];
1955e9dbad6fSeschrock 	uint64_t zoned;
195699653d4eSeschrock 
195799653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
195899653d4eSeschrock 	    "cannot create '%s'"), path);
1959fa9e4066Sahrens 
1960fa9e4066Sahrens 	/* validate the path, taking care to note the extended error message */
196199653d4eSeschrock 	if (!zfs_validate_name(hdl, path, type))
196299653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
1963fa9e4066Sahrens 
1964fa9e4066Sahrens 	/* validate parents exist */
1965e9dbad6fSeschrock 	if (check_parents(hdl, path, &zoned) != 0)
1966fa9e4066Sahrens 		return (-1);
1967fa9e4066Sahrens 
1968fa9e4066Sahrens 	/*
1969fa9e4066Sahrens 	 * The failure modes when creating a dataset of a different type over
1970fa9e4066Sahrens 	 * one that already exists is a little strange.  In particular, if you
1971fa9e4066Sahrens 	 * try to create a dataset on top of an existing dataset, the ioctl()
1972fa9e4066Sahrens 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
1973fa9e4066Sahrens 	 * first try to see if the dataset exists.
1974fa9e4066Sahrens 	 */
1975fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
197699653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
197799653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
197899653d4eSeschrock 		    "dataset already exists"));
197999653d4eSeschrock 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
1980fa9e4066Sahrens 	}
1981fa9e4066Sahrens 
1982fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME)
1983fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
1984fa9e4066Sahrens 	else
1985fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
1986fa9e4066Sahrens 
1987e9dbad6fSeschrock 	if (props && (props = zfs_validate_properties(hdl, type, props, zoned,
1988e9dbad6fSeschrock 	    NULL, errbuf)) == 0)
1989e9dbad6fSeschrock 		return (-1);
1990e9dbad6fSeschrock 
1991fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME) {
19925c5460e9Seschrock 		/*
19935c5460e9Seschrock 		 * If we are creating a volume, the size and block size must
19945c5460e9Seschrock 		 * satisfy a few restraints.  First, the blocksize must be a
19955c5460e9Seschrock 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
19965c5460e9Seschrock 		 * volsize must be a multiple of the block size, and cannot be
19975c5460e9Seschrock 		 * zero.
19985c5460e9Seschrock 		 */
1999e9dbad6fSeschrock 		if (props == NULL || nvlist_lookup_uint64(props,
2000e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2001e9dbad6fSeschrock 			nvlist_free(props);
200299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2003e9dbad6fSeschrock 			    "missing volume size"));
2004e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2005fa9e4066Sahrens 		}
2006fa9e4066Sahrens 
2007e9dbad6fSeschrock 		if ((ret = nvlist_lookup_uint64(props,
2008e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2009e9dbad6fSeschrock 		    &blocksize)) != 0) {
2010e9dbad6fSeschrock 			if (ret == ENOENT) {
2011e9dbad6fSeschrock 				blocksize = zfs_prop_default_numeric(
2012e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2013e9dbad6fSeschrock 			} else {
2014e9dbad6fSeschrock 				nvlist_free(props);
201599653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2016e9dbad6fSeschrock 				    "missing volume block size"));
2017e9dbad6fSeschrock 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2018e9dbad6fSeschrock 			}
2019e9dbad6fSeschrock 		}
2020e9dbad6fSeschrock 
2021e9dbad6fSeschrock 		if (size == 0) {
2022e9dbad6fSeschrock 			nvlist_free(props);
2023e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2024e9dbad6fSeschrock 			    "volume size cannot be zero"));
2025e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
20265c5460e9Seschrock 		}
20275c5460e9Seschrock 
20285c5460e9Seschrock 		if (size % blocksize != 0) {
2029e9dbad6fSeschrock 			nvlist_free(props);
203099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2031e9dbad6fSeschrock 			    "volume size must be a multiple of volume block "
2032e9dbad6fSeschrock 			    "size"));
2033e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2034e9dbad6fSeschrock 		}
20355c5460e9Seschrock 	}
20365c5460e9Seschrock 
2037e9dbad6fSeschrock 	if (props &&
2038e9dbad6fSeschrock 	    zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0)
2039e9dbad6fSeschrock 		return (-1);
2040e9dbad6fSeschrock 	nvlist_free(props);
2041fa9e4066Sahrens 
2042fa9e4066Sahrens 	/* create the dataset */
204399653d4eSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
2044fa9e4066Sahrens 
2045fa9e4066Sahrens 	if (ret == 0 && type == ZFS_TYPE_VOLUME)
204699653d4eSeschrock 		ret = zvol_create_link(hdl, path);
2047fa9e4066Sahrens 
2048e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2049e9dbad6fSeschrock 
2050fa9e4066Sahrens 	/* check for failure */
2051fa9e4066Sahrens 	if (ret != 0) {
2052fa9e4066Sahrens 		char parent[ZFS_MAXNAMELEN];
2053fa9e4066Sahrens 		(void) parent_name(path, parent, sizeof (parent));
2054fa9e4066Sahrens 
2055fa9e4066Sahrens 		switch (errno) {
2056fa9e4066Sahrens 		case ENOENT:
205799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
205899653d4eSeschrock 			    "no such parent '%s'"), parent);
205999653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2060fa9e4066Sahrens 
2061fa9e4066Sahrens 		case EINVAL:
206299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2063d7d4af51Smmusante 			    "parent '%s' is not a filesystem"), parent);
206499653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2065fa9e4066Sahrens 
2066fa9e4066Sahrens 		case EDOM:
206799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2068e9dbad6fSeschrock 			    "volume block size must be power of 2 from "
2069e9dbad6fSeschrock 			    "%u to %uk"),
2070fa9e4066Sahrens 			    (uint_t)SPA_MINBLOCKSIZE,
2071fa9e4066Sahrens 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
207299653d4eSeschrock 
2073e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
207499653d4eSeschrock 
2075fa9e4066Sahrens #ifdef _ILP32
2076fa9e4066Sahrens 		case EOVERFLOW:
2077fa9e4066Sahrens 			/*
2078fa9e4066Sahrens 			 * This platform can't address a volume this big.
2079fa9e4066Sahrens 			 */
208099653d4eSeschrock 			if (type == ZFS_TYPE_VOLUME)
208199653d4eSeschrock 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
208299653d4eSeschrock 				    errbuf));
2083fa9e4066Sahrens #endif
208499653d4eSeschrock 			/* FALLTHROUGH */
2085fa9e4066Sahrens 		default:
208699653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2087fa9e4066Sahrens 		}
2088fa9e4066Sahrens 	}
2089fa9e4066Sahrens 
2090fa9e4066Sahrens 	return (0);
2091fa9e4066Sahrens }
2092fa9e4066Sahrens 
2093fa9e4066Sahrens /*
2094fa9e4066Sahrens  * Destroys the given dataset.  The caller must make sure that the filesystem
2095fa9e4066Sahrens  * isn't mounted, and that there are no active dependents.
2096fa9e4066Sahrens  */
2097fa9e4066Sahrens int
2098fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp)
2099fa9e4066Sahrens {
2100fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2101fa9e4066Sahrens 
2102fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2103fa9e4066Sahrens 
2104e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2105f3861e1aSahl 		/*
2106f3861e1aSahl 		 * Unconditionally unshare this zvol ignoring failure as it
2107f3861e1aSahl 		 * indicates only that the volume wasn't shared initially.
2108f3861e1aSahl 		 */
2109f3861e1aSahl 		(void) zfs_unshare_iscsi(zhp);
2110f3861e1aSahl 
211199653d4eSeschrock 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2112fa9e4066Sahrens 			return (-1);
2113fa9e4066Sahrens 
2114fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2115fa9e4066Sahrens 	} else {
2116fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2117fa9e4066Sahrens 	}
2118fa9e4066Sahrens 
2119f3861e1aSahl 	if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) != 0) {
2120ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
212199653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
212299653d4eSeschrock 		    zhp->zfs_name));
21231d452cf5Sahrens 	}
2124fa9e4066Sahrens 
2125fa9e4066Sahrens 	remove_mountpoint(zhp);
2126fa9e4066Sahrens 
2127fa9e4066Sahrens 	return (0);
2128fa9e4066Sahrens }
2129fa9e4066Sahrens 
21301d452cf5Sahrens struct destroydata {
21311d452cf5Sahrens 	char *snapname;
21321d452cf5Sahrens 	boolean_t gotone;
21333ccfa83cSahrens 	boolean_t closezhp;
21341d452cf5Sahrens };
21351d452cf5Sahrens 
21361d452cf5Sahrens static int
21371d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg)
21381d452cf5Sahrens {
21391d452cf5Sahrens 	struct destroydata *dd = arg;
21401d452cf5Sahrens 	zfs_handle_t *szhp;
21411d452cf5Sahrens 	char name[ZFS_MAXNAMELEN];
21423ccfa83cSahrens 	boolean_t closezhp = dd->closezhp;
21433ccfa83cSahrens 	int rv;
21441d452cf5Sahrens 
2145e9dbad6fSeschrock 	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2146e9dbad6fSeschrock 	(void) strlcat(name, "@", sizeof (name));
2147e9dbad6fSeschrock 	(void) strlcat(name, dd->snapname, sizeof (name));
21481d452cf5Sahrens 
21491d452cf5Sahrens 	szhp = make_dataset_handle(zhp->zfs_hdl, name);
21501d452cf5Sahrens 	if (szhp) {
21511d452cf5Sahrens 		dd->gotone = B_TRUE;
21521d452cf5Sahrens 		zfs_close(szhp);
21531d452cf5Sahrens 	}
21541d452cf5Sahrens 
21551d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
21561d452cf5Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, name);
21571d452cf5Sahrens 		/*
21581d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
21591d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
21601d452cf5Sahrens 		 * the volumes.
21611d452cf5Sahrens 		 */
21621d452cf5Sahrens 	}
21631d452cf5Sahrens 
21643ccfa83cSahrens 	dd->closezhp = B_TRUE;
21653ccfa83cSahrens 	rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg);
21663ccfa83cSahrens 	if (closezhp)
21673ccfa83cSahrens 		zfs_close(zhp);
21683ccfa83cSahrens 	return (rv);
21691d452cf5Sahrens }
21701d452cf5Sahrens 
21711d452cf5Sahrens /*
21721d452cf5Sahrens  * Destroys all snapshots with the given name in zhp & descendants.
21731d452cf5Sahrens  */
21741d452cf5Sahrens int
21751d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname)
21761d452cf5Sahrens {
21771d452cf5Sahrens 	zfs_cmd_t zc = { 0 };
21781d452cf5Sahrens 	int ret;
21791d452cf5Sahrens 	struct destroydata dd = { 0 };
21801d452cf5Sahrens 
21811d452cf5Sahrens 	dd.snapname = snapname;
21821d452cf5Sahrens 	(void) zfs_remove_link_cb(zhp, &dd);
21831d452cf5Sahrens 
21841d452cf5Sahrens 	if (!dd.gotone) {
2185ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
21861d452cf5Sahrens 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
21871d452cf5Sahrens 		    zhp->zfs_name, snapname));
21881d452cf5Sahrens 	}
21891d452cf5Sahrens 
21901d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2191e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
21921d452cf5Sahrens 
21931d452cf5Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc);
21941d452cf5Sahrens 	if (ret != 0) {
21951d452cf5Sahrens 		char errbuf[1024];
21961d452cf5Sahrens 
21971d452cf5Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
21981d452cf5Sahrens 		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
21991d452cf5Sahrens 
22001d452cf5Sahrens 		switch (errno) {
22011d452cf5Sahrens 		case EEXIST:
22021d452cf5Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
22031d452cf5Sahrens 			    "snapshot is cloned"));
22041d452cf5Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
22051d452cf5Sahrens 
22061d452cf5Sahrens 		default:
22071d452cf5Sahrens 			return (zfs_standard_error(zhp->zfs_hdl, errno,
22081d452cf5Sahrens 			    errbuf));
22091d452cf5Sahrens 		}
22101d452cf5Sahrens 	}
22111d452cf5Sahrens 
22121d452cf5Sahrens 	return (0);
22131d452cf5Sahrens }
22141d452cf5Sahrens 
2215fa9e4066Sahrens /*
2216fa9e4066Sahrens  * Clones the given dataset.  The target must be of the same type as the source.
2217fa9e4066Sahrens  */
2218fa9e4066Sahrens int
2219e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
2220fa9e4066Sahrens {
2221fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2222fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2223fa9e4066Sahrens 	int ret;
222499653d4eSeschrock 	char errbuf[1024];
222599653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
2226e9dbad6fSeschrock 	zfs_type_t type;
2227e9dbad6fSeschrock 	uint64_t zoned;
2228fa9e4066Sahrens 
2229fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
2230fa9e4066Sahrens 
223199653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
223299653d4eSeschrock 	    "cannot create '%s'"), target);
223399653d4eSeschrock 
2234fa9e4066Sahrens 	/* validate the target name */
223599653d4eSeschrock 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM))
223699653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2237fa9e4066Sahrens 
2238fa9e4066Sahrens 	/* validate parents exist */
2239e9dbad6fSeschrock 	if (check_parents(hdl, target, &zoned) != 0)
2240fa9e4066Sahrens 		return (-1);
2241fa9e4066Sahrens 
2242fa9e4066Sahrens 	(void) parent_name(target, parent, sizeof (parent));
2243fa9e4066Sahrens 
2244fa9e4066Sahrens 	/* do the clone */
2245e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2246fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
22475f8e1617Snn35248 		type = ZFS_TYPE_VOLUME;
2248e9dbad6fSeschrock 	} else {
2249fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
22505f8e1617Snn35248 		type = ZFS_TYPE_FILESYSTEM;
2251e9dbad6fSeschrock 	}
2252e9dbad6fSeschrock 
2253e9dbad6fSeschrock 	if (props) {
2254e9dbad6fSeschrock 		if ((props = zfs_validate_properties(hdl, type, props, zoned,
2255e9dbad6fSeschrock 		    zhp, errbuf)) == NULL)
2256e9dbad6fSeschrock 			return (-1);
2257e9dbad6fSeschrock 
2258e9dbad6fSeschrock 		if (zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) {
2259e9dbad6fSeschrock 			nvlist_free(props);
2260e9dbad6fSeschrock 			return (-1);
2261e9dbad6fSeschrock 		}
2262e9dbad6fSeschrock 
2263e9dbad6fSeschrock 		nvlist_free(props);
2264e9dbad6fSeschrock 	}
2265fa9e4066Sahrens 
2266fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
2267e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
226899653d4eSeschrock 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
2269fa9e4066Sahrens 
2270e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2271e9dbad6fSeschrock 
2272fa9e4066Sahrens 	if (ret != 0) {
2273fa9e4066Sahrens 		switch (errno) {
2274fa9e4066Sahrens 
2275fa9e4066Sahrens 		case ENOENT:
2276fa9e4066Sahrens 			/*
2277fa9e4066Sahrens 			 * The parent doesn't exist.  We should have caught this
2278fa9e4066Sahrens 			 * above, but there may a race condition that has since
2279fa9e4066Sahrens 			 * destroyed the parent.
2280fa9e4066Sahrens 			 *
2281fa9e4066Sahrens 			 * At this point, we don't know whether it's the source
2282fa9e4066Sahrens 			 * that doesn't exist anymore, or whether the target
2283fa9e4066Sahrens 			 * dataset doesn't exist.
2284fa9e4066Sahrens 			 */
228599653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
228699653d4eSeschrock 			    "no such parent '%s'"), parent);
228799653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
2288fa9e4066Sahrens 
228999653d4eSeschrock 		case EXDEV:
229099653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
229199653d4eSeschrock 			    "source and target pools differ"));
229299653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
229399653d4eSeschrock 			    errbuf));
229499653d4eSeschrock 
229599653d4eSeschrock 		default:
229699653d4eSeschrock 			return (zfs_standard_error(zhp->zfs_hdl, errno,
229799653d4eSeschrock 			    errbuf));
229899653d4eSeschrock 		}
2299e9dbad6fSeschrock 	} else if (ZFS_IS_VOLUME(zhp)) {
230099653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, target);
230199653d4eSeschrock 	}
230299653d4eSeschrock 
230399653d4eSeschrock 	return (ret);
230499653d4eSeschrock }
230599653d4eSeschrock 
230699653d4eSeschrock typedef struct promote_data {
230799653d4eSeschrock 	char cb_mountpoint[MAXPATHLEN];
230899653d4eSeschrock 	const char *cb_target;
230999653d4eSeschrock 	const char *cb_errbuf;
231099653d4eSeschrock 	uint64_t cb_pivot_txg;
231199653d4eSeschrock } promote_data_t;
231299653d4eSeschrock 
231399653d4eSeschrock static int
231499653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data)
231599653d4eSeschrock {
231699653d4eSeschrock 	promote_data_t *pd = data;
231799653d4eSeschrock 	zfs_handle_t *szhp;
231899653d4eSeschrock 	char snapname[MAXPATHLEN];
23193ccfa83cSahrens 	int rv = 0;
232099653d4eSeschrock 
232199653d4eSeschrock 	/* We don't care about snapshots after the pivot point */
23223ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) {
23233ccfa83cSahrens 		zfs_close(zhp);
232499653d4eSeschrock 		return (0);
23253ccfa83cSahrens 	}
232699653d4eSeschrock 
23270b69c2f0Sahrens 	/* Remove the device link if it's a zvol. */
2328e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
23290b69c2f0Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
233099653d4eSeschrock 
233199653d4eSeschrock 	/* Check for conflicting names */
2332e9dbad6fSeschrock 	(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
2333e9dbad6fSeschrock 	(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
233499653d4eSeschrock 	szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
233599653d4eSeschrock 	if (szhp != NULL) {
233699653d4eSeschrock 		zfs_close(szhp);
233799653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
233899653d4eSeschrock 		    "snapshot name '%s' from origin \n"
233999653d4eSeschrock 		    "conflicts with '%s' from target"),
234099653d4eSeschrock 		    zhp->zfs_name, snapname);
23413ccfa83cSahrens 		rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf);
234299653d4eSeschrock 	}
23433ccfa83cSahrens 	zfs_close(zhp);
23443ccfa83cSahrens 	return (rv);
234599653d4eSeschrock }
234699653d4eSeschrock 
23470b69c2f0Sahrens static int
23480b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data)
23490b69c2f0Sahrens {
23500b69c2f0Sahrens 	promote_data_t *pd = data;
23510b69c2f0Sahrens 
23520b69c2f0Sahrens 	/* We don't care about snapshots after the pivot point */
23533ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) {
23540b69c2f0Sahrens 		/* Create the device link if it's a zvol. */
2355e9dbad6fSeschrock 		if (ZFS_IS_VOLUME(zhp))
23560b69c2f0Sahrens 			(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
23573ccfa83cSahrens 	}
23580b69c2f0Sahrens 
23593ccfa83cSahrens 	zfs_close(zhp);
23600b69c2f0Sahrens 	return (0);
23610b69c2f0Sahrens }
23620b69c2f0Sahrens 
236399653d4eSeschrock /*
236499653d4eSeschrock  * Promotes the given clone fs to be the clone parent.
236599653d4eSeschrock  */
236699653d4eSeschrock int
236799653d4eSeschrock zfs_promote(zfs_handle_t *zhp)
236899653d4eSeschrock {
236999653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
237099653d4eSeschrock 	zfs_cmd_t zc = { 0 };
237199653d4eSeschrock 	char parent[MAXPATHLEN];
237299653d4eSeschrock 	char *cp;
237399653d4eSeschrock 	int ret;
237499653d4eSeschrock 	zfs_handle_t *pzhp;
237599653d4eSeschrock 	promote_data_t pd;
237699653d4eSeschrock 	char errbuf[1024];
237799653d4eSeschrock 
237899653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
237999653d4eSeschrock 	    "cannot promote '%s'"), zhp->zfs_name);
238099653d4eSeschrock 
238199653d4eSeschrock 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
238299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
238399653d4eSeschrock 		    "snapshots can not be promoted"));
238499653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
238599653d4eSeschrock 	}
238699653d4eSeschrock 
2387e9dbad6fSeschrock 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_clone_of, sizeof (parent));
238899653d4eSeschrock 	if (parent[0] == '\0') {
238999653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
239099653d4eSeschrock 		    "not a cloned filesystem"));
239199653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
239299653d4eSeschrock 	}
239399653d4eSeschrock 	cp = strchr(parent, '@');
239499653d4eSeschrock 	*cp = '\0';
239599653d4eSeschrock 
239699653d4eSeschrock 	/* Walk the snapshots we will be moving */
239799653d4eSeschrock 	pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_clone_of, ZFS_TYPE_SNAPSHOT);
239899653d4eSeschrock 	if (pzhp == NULL)
239999653d4eSeschrock 		return (-1);
240099653d4eSeschrock 	pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
240199653d4eSeschrock 	zfs_close(pzhp);
240299653d4eSeschrock 	pd.cb_target = zhp->zfs_name;
240399653d4eSeschrock 	pd.cb_errbuf = errbuf;
240499653d4eSeschrock 	pzhp = zfs_open(hdl, parent, ZFS_TYPE_ANY);
240599653d4eSeschrock 	if (pzhp == NULL)
240699653d4eSeschrock 		return (-1);
240799653d4eSeschrock 	(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
240899653d4eSeschrock 	    sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
240999653d4eSeschrock 	ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
24100b69c2f0Sahrens 	if (ret != 0) {
24110b69c2f0Sahrens 		zfs_close(pzhp);
241299653d4eSeschrock 		return (-1);
24130b69c2f0Sahrens 	}
241499653d4eSeschrock 
241599653d4eSeschrock 	/* issue the ioctl */
2416e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of,
2417e9dbad6fSeschrock 	    sizeof (zc.zc_value));
241899653d4eSeschrock 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
241999653d4eSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc);
242099653d4eSeschrock 
242199653d4eSeschrock 	if (ret != 0) {
24220b69c2f0Sahrens 		int save_errno = errno;
2423fa9e4066Sahrens 
24240b69c2f0Sahrens 		(void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
24250b69c2f0Sahrens 		zfs_close(pzhp);
24260b69c2f0Sahrens 
24270b69c2f0Sahrens 		switch (save_errno) {
2428fa9e4066Sahrens 		case EEXIST:
2429fa9e4066Sahrens 			/*
243099653d4eSeschrock 			 * There is a conflicting snapshot name.  We
243199653d4eSeschrock 			 * should have caught this above, but they could
243299653d4eSeschrock 			 * have renamed something in the mean time.
2433fa9e4066Sahrens 			 */
243499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
243599653d4eSeschrock 			    "conflicting snapshot name from parent '%s'"),
243699653d4eSeschrock 			    parent);
243799653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2438fa9e4066Sahrens 
2439fa9e4066Sahrens 		default:
24400b69c2f0Sahrens 			return (zfs_standard_error(hdl, save_errno, errbuf));
2441fa9e4066Sahrens 		}
24420b69c2f0Sahrens 	} else {
24430b69c2f0Sahrens 		(void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
2444fa9e4066Sahrens 	}
2445fa9e4066Sahrens 
24460b69c2f0Sahrens 	zfs_close(pzhp);
2447fa9e4066Sahrens 	return (ret);
2448fa9e4066Sahrens }
2449fa9e4066Sahrens 
24501d452cf5Sahrens static int
24511d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
24521d452cf5Sahrens {
24531d452cf5Sahrens 	char *snapname = arg;
2454e9dbad6fSeschrock 	int ret;
24551d452cf5Sahrens 
24561d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
24571d452cf5Sahrens 		char name[MAXPATHLEN];
24581d452cf5Sahrens 
2459e9dbad6fSeschrock 		(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2460e9dbad6fSeschrock 		(void) strlcat(name, "@", sizeof (name));
2461e9dbad6fSeschrock 		(void) strlcat(name, snapname, sizeof (name));
24621d452cf5Sahrens 		(void) zvol_create_link(zhp->zfs_hdl, name);
24631d452cf5Sahrens 		/*
24641d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
24651d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
24661d452cf5Sahrens 		 * the volumes.
24671d452cf5Sahrens 		 */
24681d452cf5Sahrens 	}
2469e9dbad6fSeschrock 
2470e9dbad6fSeschrock 	ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname);
2471e9dbad6fSeschrock 
2472e9dbad6fSeschrock 	zfs_close(zhp);
2473e9dbad6fSeschrock 
2474e9dbad6fSeschrock 	return (ret);
24751d452cf5Sahrens }
24761d452cf5Sahrens 
2477fa9e4066Sahrens /*
2478fa9e4066Sahrens  * Takes a snapshot of the given dataset
2479fa9e4066Sahrens  */
2480fa9e4066Sahrens int
24811d452cf5Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
2482fa9e4066Sahrens {
2483fa9e4066Sahrens 	const char *delim;
2484fa9e4066Sahrens 	char *parent;
2485fa9e4066Sahrens 	zfs_handle_t *zhp;
2486fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2487fa9e4066Sahrens 	int ret;
248899653d4eSeschrock 	char errbuf[1024];
2489fa9e4066Sahrens 
249099653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
249199653d4eSeschrock 	    "cannot snapshot '%s'"), path);
249299653d4eSeschrock 
249399653d4eSeschrock 	/* validate the target name */
249499653d4eSeschrock 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT))
249599653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2496fa9e4066Sahrens 
2497fa9e4066Sahrens 	/* make sure the parent exists and is of the appropriate type */
24981d452cf5Sahrens 	delim = strchr(path, '@');
249999653d4eSeschrock 	if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL)
250099653d4eSeschrock 		return (-1);
2501fa9e4066Sahrens 	(void) strncpy(parent, path, delim - path);
2502fa9e4066Sahrens 	parent[delim - path] = '\0';
2503fa9e4066Sahrens 
250499653d4eSeschrock 	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
2505fa9e4066Sahrens 	    ZFS_TYPE_VOLUME)) == NULL) {
2506fa9e4066Sahrens 		free(parent);
2507fa9e4066Sahrens 		return (-1);
2508fa9e4066Sahrens 	}
2509fa9e4066Sahrens 
25101d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2511e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
25121d452cf5Sahrens 	zc.zc_cookie = recursive;
25131d452cf5Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc);
2514fa9e4066Sahrens 
25151d452cf5Sahrens 	/*
25161d452cf5Sahrens 	 * if it was recursive, the one that actually failed will be in
25171d452cf5Sahrens 	 * zc.zc_name.
25181d452cf5Sahrens 	 */
25191d452cf5Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2520e9dbad6fSeschrock 	    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
25211d452cf5Sahrens 	if (ret == 0 && recursive) {
25221d452cf5Sahrens 		(void) zfs_iter_filesystems(zhp,
25231d452cf5Sahrens 		    zfs_create_link_cb, (char *)delim+1);
25241d452cf5Sahrens 	}
2525fa9e4066Sahrens 	if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
252699653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, path);
25271d452cf5Sahrens 		if (ret != 0) {
252899653d4eSeschrock 			(void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY,
252999653d4eSeschrock 			    &zc);
2530fa9e4066Sahrens 		}
25311d452cf5Sahrens 	}
2532fa9e4066Sahrens 
253399653d4eSeschrock 	if (ret != 0)
253499653d4eSeschrock 		(void) zfs_standard_error(hdl, errno, errbuf);
2535fa9e4066Sahrens 
2536fa9e4066Sahrens 	free(parent);
2537fa9e4066Sahrens 	zfs_close(zhp);
2538fa9e4066Sahrens 
2539fa9e4066Sahrens 	return (ret);
2540fa9e4066Sahrens }
2541fa9e4066Sahrens 
2542fa9e4066Sahrens /*
2543fa9e4066Sahrens  * Dumps a backup of tosnap, incremental from fromsnap if it isn't NULL.
2544fa9e4066Sahrens  */
2545fa9e4066Sahrens int
2546a2eea2e1Sahrens zfs_send(zfs_handle_t *zhp, const char *fromsnap)
2547fa9e4066Sahrens {
2548fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2549fa9e4066Sahrens 	int ret;
255099653d4eSeschrock 	char errbuf[1024];
2551a2eea2e1Sahrens 	libzfs_handle_t *hdl = zhp->zfs_hdl;
255299653d4eSeschrock 
255399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2554a2eea2e1Sahrens 	    "cannot send '%s'"), zhp->zfs_name);
2555fa9e4066Sahrens 
2556fa9e4066Sahrens 	/* do the ioctl() */
2557a2eea2e1Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2558a2eea2e1Sahrens 	if (fromsnap)
2559a2eea2e1Sahrens 		(void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_name));
2560fa9e4066Sahrens 	zc.zc_cookie = STDOUT_FILENO;
2561fa9e4066Sahrens 
2562a2eea2e1Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc);
2563fa9e4066Sahrens 	if (ret != 0) {
2564fa9e4066Sahrens 		switch (errno) {
2565fa9e4066Sahrens 
2566fa9e4066Sahrens 		case EXDEV:
256799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2568d7d4af51Smmusante 			    "not an earlier snapshot from the same fs"));
256999653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
2570fa9e4066Sahrens 
2571fa9e4066Sahrens 		case EDQUOT:
2572fa9e4066Sahrens 		case EFBIG:
2573fa9e4066Sahrens 		case EIO:
2574fa9e4066Sahrens 		case ENOLINK:
2575fa9e4066Sahrens 		case ENOSPC:
2576fa9e4066Sahrens 		case ENOSTR:
2577fa9e4066Sahrens 		case ENXIO:
2578fa9e4066Sahrens 		case EPIPE:
2579fa9e4066Sahrens 		case ERANGE:
2580fa9e4066Sahrens 		case EFAULT:
2581fa9e4066Sahrens 		case EROFS:
258299653d4eSeschrock 			zfs_error_aux(hdl, strerror(errno));
258399653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
2584fa9e4066Sahrens 
2585fa9e4066Sahrens 		default:
258699653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2587fa9e4066Sahrens 		}
2588fa9e4066Sahrens 	}
2589fa9e4066Sahrens 
2590fa9e4066Sahrens 	return (ret);
2591fa9e4066Sahrens }
2592fa9e4066Sahrens 
2593fa9e4066Sahrens /*
2594a2eea2e1Sahrens  * Create ancestors of 'target', but not target itself, and not
2595a2eea2e1Sahrens  * ancestors whose names are shorter than prefixlen.  Die if
2596a2eea2e1Sahrens  * prefixlen-ancestor does not exist.
2597a2eea2e1Sahrens  */
2598a2eea2e1Sahrens static int
2599a2eea2e1Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
2600a2eea2e1Sahrens {
2601a2eea2e1Sahrens 	zfs_handle_t *h;
2602a2eea2e1Sahrens 	char *cp;
2603a2eea2e1Sahrens 
2604a2eea2e1Sahrens 	/* make sure prefix exists */
2605a2eea2e1Sahrens 	cp = strchr(target + prefixlen, '/');
2606a2eea2e1Sahrens 	*cp = '\0';
2607a2eea2e1Sahrens 	h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2608a2eea2e1Sahrens 	*cp = '/';
2609a2eea2e1Sahrens 	if (h == NULL)
2610a2eea2e1Sahrens 		return (-1);
2611a2eea2e1Sahrens 	zfs_close(h);
2612a2eea2e1Sahrens 
2613a2eea2e1Sahrens 	/*
2614a2eea2e1Sahrens 	 * Attempt to create, mount, and share any ancestor filesystems,
2615a2eea2e1Sahrens 	 * up to the prefixlen-long one.
2616a2eea2e1Sahrens 	 */
2617a2eea2e1Sahrens 	for (cp = target + prefixlen + 1;
2618a2eea2e1Sahrens 	    cp = strchr(cp, '/'); *cp = '/', cp++) {
2619a2eea2e1Sahrens 		const char *opname;
2620a2eea2e1Sahrens 
2621a2eea2e1Sahrens 		*cp = '\0';
2622a2eea2e1Sahrens 
2623a2eea2e1Sahrens 		h = make_dataset_handle(hdl, target);
2624a2eea2e1Sahrens 		if (h) {
2625a2eea2e1Sahrens 			/* it already exists, nothing to do here */
2626a2eea2e1Sahrens 			zfs_close(h);
2627a2eea2e1Sahrens 			continue;
2628a2eea2e1Sahrens 		}
2629a2eea2e1Sahrens 
2630a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "create");
2631a2eea2e1Sahrens 		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
2632a2eea2e1Sahrens 		    NULL) != 0)
2633a2eea2e1Sahrens 			goto ancestorerr;
2634a2eea2e1Sahrens 
2635a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "open");
2636a2eea2e1Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2637a2eea2e1Sahrens 		if (h == NULL)
2638a2eea2e1Sahrens 			goto ancestorerr;
2639a2eea2e1Sahrens 
2640a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "mount");
2641a2eea2e1Sahrens 		if (zfs_mount(h, NULL, 0) != 0)
2642a2eea2e1Sahrens 			goto ancestorerr;
2643a2eea2e1Sahrens 
2644a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "share");
2645a2eea2e1Sahrens 		if (zfs_share(h) != 0)
2646a2eea2e1Sahrens 			goto ancestorerr;
2647a2eea2e1Sahrens 
2648a2eea2e1Sahrens 		zfs_close(h);
2649a2eea2e1Sahrens 
2650a2eea2e1Sahrens 		continue;
2651a2eea2e1Sahrens ancestorerr:
2652a2eea2e1Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2653a2eea2e1Sahrens 		    "failed to %s ancestor '%s'"), opname, target);
2654a2eea2e1Sahrens 		return (-1);
2655a2eea2e1Sahrens 	}
2656a2eea2e1Sahrens 
2657a2eea2e1Sahrens 	return (0);
2658a2eea2e1Sahrens }
2659a2eea2e1Sahrens 
2660a2eea2e1Sahrens /*
2661fa9e4066Sahrens  * Restores a backup of tosnap from stdin.
2662fa9e4066Sahrens  */
2663fa9e4066Sahrens int
266499653d4eSeschrock zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
266598579b20Snd150628     int verbose, int dryrun, boolean_t force)
2666fa9e4066Sahrens {
2667fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2668fa9e4066Sahrens 	time_t begin_time;
2669a2eea2e1Sahrens 	int ioctl_err, err, bytes, size, choplen;
2670fa9e4066Sahrens 	char *cp;
2671fa9e4066Sahrens 	dmu_replay_record_t drr;
2672fa9e4066Sahrens 	struct drr_begin *drrb = &zc.zc_begin_record;
267399653d4eSeschrock 	char errbuf[1024];
267498579b20Snd150628 	prop_changelist_t *clp;
2675a2eea2e1Sahrens 	char chopprefix[ZFS_MAXNAMELEN];
2676fa9e4066Sahrens 
2677fa9e4066Sahrens 	begin_time = time(NULL);
2678fa9e4066Sahrens 
267999653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
268099653d4eSeschrock 	    "cannot receive"));
268199653d4eSeschrock 
2682fa9e4066Sahrens 	/* read in the BEGIN record */
2683fa9e4066Sahrens 	cp = (char *)&drr;
2684fa9e4066Sahrens 	bytes = 0;
2685fa9e4066Sahrens 	do {
26869b4f025eSahrens 		size = read(STDIN_FILENO, cp, sizeof (drr) - bytes);
26879b4f025eSahrens 		cp += size;
26889b4f025eSahrens 		bytes += size;
26899b4f025eSahrens 	} while (size > 0);
2690fa9e4066Sahrens 
26919b4f025eSahrens 	if (size < 0 || bytes != sizeof (drr)) {
269299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
269399653d4eSeschrock 		    "stream (failed to read first record)"));
269499653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2695fa9e4066Sahrens 	}
2696fa9e4066Sahrens 
2697fa9e4066Sahrens 	zc.zc_begin_record = drr.drr_u.drr_begin;
2698fa9e4066Sahrens 
2699fa9e4066Sahrens 	if (drrb->drr_magic != DMU_BACKUP_MAGIC &&
2700fa9e4066Sahrens 	    drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) {
270199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
270299653d4eSeschrock 		    "stream (bad magic number)"));
270399653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2704fa9e4066Sahrens 	}
2705fa9e4066Sahrens 
2706fa9e4066Sahrens 	if (drrb->drr_version != DMU_BACKUP_VERSION &&
2707fa9e4066Sahrens 	    drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) {
270899653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only version "
270999653d4eSeschrock 		    "0x%llx is supported (stream is version 0x%llx)"),
2710fa9e4066Sahrens 		    DMU_BACKUP_VERSION, drrb->drr_version);
271199653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2712fa9e4066Sahrens 	}
2713fa9e4066Sahrens 
2714a2eea2e1Sahrens 	if (strchr(drr.drr_u.drr_begin.drr_toname, '@') == NULL) {
2715a2eea2e1Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
2716a2eea2e1Sahrens 			    "stream (bad snapshot name)"));
2717a2eea2e1Sahrens 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2718fa9e4066Sahrens 	}
2719a2eea2e1Sahrens 	/*
2720a2eea2e1Sahrens 	 * Determine how much of the snapshot name stored in the stream
2721a2eea2e1Sahrens 	 * we are going to tack on to the name they specified on the
2722a2eea2e1Sahrens 	 * command line, and how much we are going to chop off.
2723a2eea2e1Sahrens 	 *
2724a2eea2e1Sahrens 	 * If they specified a snapshot, chop the entire name stored in
2725a2eea2e1Sahrens 	 * the stream.
2726a2eea2e1Sahrens 	 */
2727a2eea2e1Sahrens 	(void) strcpy(chopprefix, drr.drr_u.drr_begin.drr_toname);
2728a2eea2e1Sahrens 	if (isprefix) {
2729a2eea2e1Sahrens 		/*
2730a2eea2e1Sahrens 		 * They specified a fs with -d, we want to tack on
2731a2eea2e1Sahrens 		 * everything but the pool name stored in the stream
2732a2eea2e1Sahrens 		 */
2733a2eea2e1Sahrens 		if (strchr(tosnap, '@')) {
2734a2eea2e1Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
2735a2eea2e1Sahrens 			    "argument - snapshot not allowed with -d"));
2736a2eea2e1Sahrens 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2737a2eea2e1Sahrens 		}
2738a2eea2e1Sahrens 		cp = strchr(chopprefix, '/');
2739fa9e4066Sahrens 		if (cp == NULL)
2740a2eea2e1Sahrens 			cp = strchr(chopprefix, '@');
2741a2eea2e1Sahrens 		*cp = '\0';
2742fa9e4066Sahrens 	} else if (strchr(tosnap, '@') == NULL) {
2743fa9e4066Sahrens 		/*
2744a2eea2e1Sahrens 		 * If they specified a filesystem without -d, we want to
2745a2eea2e1Sahrens 		 * tack on everything after the fs specified in the
2746a2eea2e1Sahrens 		 * first name from the stream.
2747fa9e4066Sahrens 		 */
2748a2eea2e1Sahrens 		cp = strchr(chopprefix, '@');
2749fa9e4066Sahrens 		*cp = '\0';
2750a2eea2e1Sahrens 	}
2751a2eea2e1Sahrens 	choplen = strlen(chopprefix);
2752a2eea2e1Sahrens 
2753a2eea2e1Sahrens 	/*
2754a2eea2e1Sahrens 	 * Determine name of destination snapshot, store in zc_value.
2755a2eea2e1Sahrens 	 */
2756a2eea2e1Sahrens 	(void) strcpy(zc.zc_value, tosnap);
2757a2eea2e1Sahrens 	(void) strncat(zc.zc_value, drr.drr_u.drr_begin.drr_toname+choplen,
2758a2eea2e1Sahrens 	    sizeof (zc.zc_value));
27593ccfa83cSahrens 	if (!zfs_validate_name(hdl, zc.zc_value, ZFS_TYPE_SNAPSHOT))
27603ccfa83cSahrens 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2761a2eea2e1Sahrens 
2762a2eea2e1Sahrens 	(void) strcpy(zc.zc_name, zc.zc_value);
2763a2eea2e1Sahrens 	if (drrb->drr_fromguid) {
2764a2eea2e1Sahrens 		/* incremental backup stream */
2765a2eea2e1Sahrens 		zfs_handle_t *h;
2766a2eea2e1Sahrens 
2767a2eea2e1Sahrens 		/* do the recvbackup ioctl to the containing fs */
2768a2eea2e1Sahrens 		*strchr(zc.zc_name, '@') = '\0';
2769fa9e4066Sahrens 
2770fa9e4066Sahrens 		/* make sure destination fs exists */
277199653d4eSeschrock 		h = zfs_open(hdl, zc.zc_name,
277299653d4eSeschrock 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
277399653d4eSeschrock 		if (h == NULL)
2774fa9e4066Sahrens 			return (-1);
27759b4f025eSahrens 		if (!dryrun) {
277698579b20Snd150628 			/*
277798579b20Snd150628 			 * We need to unmount all the dependents of the dataset
277898579b20Snd150628 			 * and the dataset itself. If it's a volume
277998579b20Snd150628 			 * then remove device link.
278098579b20Snd150628 			 */
27819b4f025eSahrens 			if (h->zfs_type == ZFS_TYPE_FILESYSTEM) {
278298579b20Snd150628 				clp = changelist_gather(h, ZFS_PROP_NAME, 0);
278398579b20Snd150628 				if (clp == NULL)
278498579b20Snd150628 					return (-1);
278598579b20Snd150628 				if (changelist_prefix(clp) != 0) {
278698579b20Snd150628 					changelist_free(clp);
278798579b20Snd150628 					return (-1);
278898579b20Snd150628 				}
27899b4f025eSahrens 			} else {
279099653d4eSeschrock 				(void) zvol_remove_link(hdl, h->zfs_name);
27919b4f025eSahrens 			}
27929b4f025eSahrens 		}
2793fa9e4066Sahrens 		zfs_close(h);
2794fa9e4066Sahrens 	} else {
2795fa9e4066Sahrens 		/* full backup stream */
2796fa9e4066Sahrens 
27979b4f025eSahrens 		/* Make sure destination fs does not exist */
2798a2eea2e1Sahrens 		*strchr(zc.zc_name, '@') = '\0';
279999653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
280099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
280199653d4eSeschrock 			    "destination '%s' exists"), zc.zc_name);
280299653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
28039b4f025eSahrens 		}
28049b4f025eSahrens 
2805a2eea2e1Sahrens 		if (strchr(zc.zc_name, '/') == NULL) {
2806a2eea2e1Sahrens 			/*
2807a2eea2e1Sahrens 			 * they're trying to do a recv into a
2808a2eea2e1Sahrens 			 * nonexistant topmost filesystem.
2809a2eea2e1Sahrens 			 */
2810a2eea2e1Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2811a2eea2e1Sahrens 			    "destination does not exist"), zc.zc_name);
2812a2eea2e1Sahrens 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2813a2eea2e1Sahrens 		}
2814a2eea2e1Sahrens 
28159b4f025eSahrens 		/* Do the recvbackup ioctl to the fs's parent. */
2816a2eea2e1Sahrens 		*strrchr(zc.zc_name, '/') = '\0';
2817a2eea2e1Sahrens 
2818a2eea2e1Sahrens 		if (isprefix && (err = create_parents(hdl,
2819a2eea2e1Sahrens 		    zc.zc_value, strlen(tosnap))) != 0) {
2820a2eea2e1Sahrens 			return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
2821a2eea2e1Sahrens 		}
2822a2eea2e1Sahrens 
2823fa9e4066Sahrens 	}
2824fa9e4066Sahrens 
2825fa9e4066Sahrens 	zc.zc_cookie = STDIN_FILENO;
2826e9dbad6fSeschrock 	zc.zc_guid = force;
2827fa9e4066Sahrens 	if (verbose) {
2828f2a3c691Sahrens 		(void) printf("%s %s stream of %s into %s\n",
2829f2a3c691Sahrens 		    dryrun ? "would receive" : "receiving",
2830fa9e4066Sahrens 		    drrb->drr_fromguid ? "incremental" : "full",
2831fa9e4066Sahrens 		    drr.drr_u.drr_begin.drr_toname,
2832e9dbad6fSeschrock 		    zc.zc_value);
2833fa9e4066Sahrens 		(void) fflush(stdout);
2834fa9e4066Sahrens 	}
2835fa9e4066Sahrens 	if (dryrun)
2836fa9e4066Sahrens 		return (0);
283799653d4eSeschrock 	err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc);
28389b4f025eSahrens 	if (ioctl_err != 0) {
2839fa9e4066Sahrens 		switch (errno) {
2840fa9e4066Sahrens 		case ENODEV:
284199653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
284299653d4eSeschrock 			    "most recent snapshot does not match incremental "
284399653d4eSeschrock 			    "source"));
284499653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
2845fa9e4066Sahrens 			break;
2846fa9e4066Sahrens 		case ETXTBSY:
284799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
284899653d4eSeschrock 			    "destination has been modified since most recent "
284999653d4eSeschrock 			    "snapshot"));
285099653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
2851fa9e4066Sahrens 			break;
2852fa9e4066Sahrens 		case EEXIST:
2853fa9e4066Sahrens 			if (drrb->drr_fromguid == 0) {
2854fa9e4066Sahrens 				/* it's the containing fs that exists */
2855e9dbad6fSeschrock 				cp = strchr(zc.zc_value, '@');
2856fa9e4066Sahrens 				*cp = '\0';
2857fa9e4066Sahrens 			}
285899653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
285999653d4eSeschrock 			    "destination already exists"));
2860ece3d9b3Slling 			(void) zfs_error_fmt(hdl, EZFS_EXISTS,
2861ece3d9b3Slling 			    dgettext(TEXT_DOMAIN, "cannot restore to %s"),
2862ece3d9b3Slling 			    zc.zc_value);
2863fa9e4066Sahrens 			break;
2864fa9e4066Sahrens 		case EINVAL:
286599653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
28669b4f025eSahrens 			break;
2867ea8dc4b6Seschrock 		case ECKSUM:
286899653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
286999653d4eSeschrock 			    "invalid stream (checksum mismatch)"));
287099653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
2871fa9e4066Sahrens 			break;
2872fa9e4066Sahrens 		default:
287399653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
2874fa9e4066Sahrens 		}
2875fa9e4066Sahrens 	}
2876fa9e4066Sahrens 
2877fa9e4066Sahrens 	/*
28789b4f025eSahrens 	 * Mount or recreate the /dev links for the target filesystem
28799b4f025eSahrens 	 * (if created, or if we tore them down to do an incremental
28809b4f025eSahrens 	 * restore), and the /dev links for the new snapshot (if
288198579b20Snd150628 	 * created). Also mount any children of the target filesystem
288298579b20Snd150628 	 * if we did an incremental receive.
2883fa9e4066Sahrens 	 */
2884e9dbad6fSeschrock 	cp = strchr(zc.zc_value, '@');
28859b4f025eSahrens 	if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) {
2886fa9e4066Sahrens 		zfs_handle_t *h;
2887fa9e4066Sahrens 
2888fa9e4066Sahrens 		*cp = '\0';
2889e9dbad6fSeschrock 		h = zfs_open(hdl, zc.zc_value,
2890fa9e4066Sahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
2891fa9e4066Sahrens 		*cp = '@';
2892fa9e4066Sahrens 		if (h) {
289398579b20Snd150628 			if (h->zfs_type == ZFS_TYPE_VOLUME) {
289499653d4eSeschrock 				err = zvol_create_link(hdl, h->zfs_name);
2895ea8dc4b6Seschrock 				if (err == 0 && ioctl_err == 0)
289699653d4eSeschrock 					err = zvol_create_link(hdl,
2897e9dbad6fSeschrock 					    zc.zc_value);
289898579b20Snd150628 			} else {
289998579b20Snd150628 				if (drrb->drr_fromguid) {
290098579b20Snd150628 					err = changelist_postfix(clp);
290198579b20Snd150628 					changelist_free(clp);
290298579b20Snd150628 				} else {
290398579b20Snd150628 					err = zfs_mount(h, NULL, 0);
290498579b20Snd150628 				}
29059b4f025eSahrens 			}
2906fa9e4066Sahrens 		zfs_close(h);
2907fa9e4066Sahrens 		}
2908fa9e4066Sahrens 	}
2909fa9e4066Sahrens 
29109b4f025eSahrens 	if (err || ioctl_err)
29119b4f025eSahrens 		return (-1);
2912fa9e4066Sahrens 
2913fa9e4066Sahrens 	if (verbose) {
2914fa9e4066Sahrens 		char buf1[64];
2915fa9e4066Sahrens 		char buf2[64];
2916fa9e4066Sahrens 		uint64_t bytes = zc.zc_cookie;
2917fa9e4066Sahrens 		time_t delta = time(NULL) - begin_time;
2918fa9e4066Sahrens 		if (delta == 0)
2919fa9e4066Sahrens 			delta = 1;
2920fa9e4066Sahrens 		zfs_nicenum(bytes, buf1, sizeof (buf1));
2921fa9e4066Sahrens 		zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
2922fa9e4066Sahrens 
2923f2a3c691Sahrens 		(void) printf("received %sb stream in %lu seconds (%sb/sec)\n",
2924fa9e4066Sahrens 		    buf1, delta, buf2);
2925fa9e4066Sahrens 	}
292698579b20Snd150628 
2927fa9e4066Sahrens 	return (0);
2928fa9e4066Sahrens }
2929fa9e4066Sahrens 
2930fa9e4066Sahrens /*
2931b12a1c38Slling  * Destroy any more recent snapshots.  We invoke this callback on any dependents
2932b12a1c38Slling  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
2933b12a1c38Slling  * is a dependent and we should just destroy it without checking the transaction
2934b12a1c38Slling  * group.
2935fa9e4066Sahrens  */
2936b12a1c38Slling typedef struct rollback_data {
2937b12a1c38Slling 	const char	*cb_target;		/* the snapshot */
2938b12a1c38Slling 	uint64_t	cb_create;		/* creation time reference */
2939b12a1c38Slling 	prop_changelist_t *cb_clp;		/* changelist pointer */
2940b12a1c38Slling 	int		cb_error;
294199653d4eSeschrock 	boolean_t	cb_dependent;
2942b12a1c38Slling } rollback_data_t;
2943b12a1c38Slling 
2944b12a1c38Slling static int
2945b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data)
2946b12a1c38Slling {
2947b12a1c38Slling 	rollback_data_t *cbp = data;
2948b12a1c38Slling 
2949b12a1c38Slling 	if (!cbp->cb_dependent) {
2950b12a1c38Slling 		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
2951b12a1c38Slling 		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
2952b12a1c38Slling 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
2953b12a1c38Slling 		    cbp->cb_create) {
2954b12a1c38Slling 
295599653d4eSeschrock 			cbp->cb_dependent = B_TRUE;
29563bb79becSeschrock 			if (zfs_iter_dependents(zhp, B_FALSE, rollback_destroy,
29573bb79becSeschrock 			    cbp) != 0)
29583bb79becSeschrock 				cbp->cb_error = 1;
295999653d4eSeschrock 			cbp->cb_dependent = B_FALSE;
2960b12a1c38Slling 
2961b12a1c38Slling 			if (zfs_destroy(zhp) != 0)
2962b12a1c38Slling 				cbp->cb_error = 1;
2963b12a1c38Slling 			else
2964b12a1c38Slling 				changelist_remove(zhp, cbp->cb_clp);
2965b12a1c38Slling 		}
2966b12a1c38Slling 	} else {
2967b12a1c38Slling 		if (zfs_destroy(zhp) != 0)
2968b12a1c38Slling 			cbp->cb_error = 1;
2969b12a1c38Slling 		else
2970b12a1c38Slling 			changelist_remove(zhp, cbp->cb_clp);
2971b12a1c38Slling 	}
2972b12a1c38Slling 
2973b12a1c38Slling 	zfs_close(zhp);
2974b12a1c38Slling 	return (0);
2975b12a1c38Slling }
2976b12a1c38Slling 
2977b12a1c38Slling /*
2978b12a1c38Slling  * Rollback the dataset to its latest snapshot.
2979b12a1c38Slling  */
2980b12a1c38Slling static int
2981b12a1c38Slling do_rollback(zfs_handle_t *zhp)
2982fa9e4066Sahrens {
2983fa9e4066Sahrens 	int ret;
2984fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2985fa9e4066Sahrens 
2986fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
2987fa9e4066Sahrens 	    zhp->zfs_type == ZFS_TYPE_VOLUME);
2988fa9e4066Sahrens 
2989fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME &&
299099653d4eSeschrock 	    zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2991fa9e4066Sahrens 		return (-1);
2992fa9e4066Sahrens 
2993fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2994fa9e4066Sahrens 
2995e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
2996fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2997fa9e4066Sahrens 	else
2998fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2999fa9e4066Sahrens 
3000fa9e4066Sahrens 	/*
3001fa9e4066Sahrens 	 * We rely on the consumer to verify that there are no newer snapshots
3002fa9e4066Sahrens 	 * for the given dataset.  Given these constraints, we can simply pass
3003fa9e4066Sahrens 	 * the name on to the ioctl() call.  There is still an unlikely race
3004fa9e4066Sahrens 	 * condition where the user has taken a snapshot since we verified that
3005fa9e4066Sahrens 	 * this was the most recent.
3006fa9e4066Sahrens 	 */
300799653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK,
300899653d4eSeschrock 	    &zc)) != 0) {
3009ece3d9b3Slling 		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
301099653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
301199653d4eSeschrock 		    zhp->zfs_name);
3012fa9e4066Sahrens 	} else if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
301399653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
3014fa9e4066Sahrens 	}
3015fa9e4066Sahrens 
3016fa9e4066Sahrens 	return (ret);
3017fa9e4066Sahrens }
3018fa9e4066Sahrens 
3019fa9e4066Sahrens /*
3020b12a1c38Slling  * Given a dataset, rollback to a specific snapshot, discarding any
3021b12a1c38Slling  * data changes since then and making it the active dataset.
3022b12a1c38Slling  *
3023b12a1c38Slling  * Any snapshots more recent than the target are destroyed, along with
3024b12a1c38Slling  * their dependents.
3025b12a1c38Slling  */
3026b12a1c38Slling int
3027b12a1c38Slling zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, int flag)
3028b12a1c38Slling {
3029b12a1c38Slling 	int ret;
3030b12a1c38Slling 	rollback_data_t cb = { 0 };
3031b12a1c38Slling 	prop_changelist_t *clp;
3032b12a1c38Slling 
3033b12a1c38Slling 	/*
3034b12a1c38Slling 	 * Unmount all dependendents of the dataset and the dataset itself.
3035b12a1c38Slling 	 * The list we need to gather is the same as for doing rename
3036b12a1c38Slling 	 */
3037b12a1c38Slling 	clp = changelist_gather(zhp, ZFS_PROP_NAME, flag ? MS_FORCE: 0);
3038b12a1c38Slling 	if (clp == NULL)
3039b12a1c38Slling 		return (-1);
3040b12a1c38Slling 
3041b12a1c38Slling 	if ((ret = changelist_prefix(clp)) != 0)
3042b12a1c38Slling 		goto out;
3043b12a1c38Slling 
3044b12a1c38Slling 	/*
3045b12a1c38Slling 	 * Destroy all recent snapshots and its dependends.
3046b12a1c38Slling 	 */
3047b12a1c38Slling 	cb.cb_target = snap->zfs_name;
3048b12a1c38Slling 	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3049b12a1c38Slling 	cb.cb_clp = clp;
3050b12a1c38Slling 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3051b12a1c38Slling 
3052b12a1c38Slling 	if ((ret = cb.cb_error) != 0) {
3053b12a1c38Slling 		(void) changelist_postfix(clp);
3054b12a1c38Slling 		goto out;
3055b12a1c38Slling 	}
3056b12a1c38Slling 
3057b12a1c38Slling 	/*
3058b12a1c38Slling 	 * Now that we have verified that the snapshot is the latest,
3059b12a1c38Slling 	 * rollback to the given snapshot.
3060b12a1c38Slling 	 */
3061b12a1c38Slling 	ret = do_rollback(zhp);
3062b12a1c38Slling 
3063b12a1c38Slling 	if (ret != 0) {
3064b12a1c38Slling 		(void) changelist_postfix(clp);
3065b12a1c38Slling 		goto out;
3066b12a1c38Slling 	}
3067b12a1c38Slling 
3068b12a1c38Slling 	/*
3069b12a1c38Slling 	 * We only want to re-mount the filesystem if it was mounted in the
3070b12a1c38Slling 	 * first place.
3071b12a1c38Slling 	 */
3072b12a1c38Slling 	ret = changelist_postfix(clp);
3073b12a1c38Slling 
3074b12a1c38Slling out:
3075b12a1c38Slling 	changelist_free(clp);
3076b12a1c38Slling 	return (ret);
3077b12a1c38Slling }
3078b12a1c38Slling 
3079b12a1c38Slling /*
3080fa9e4066Sahrens  * Iterate over all dependents for a given dataset.  This includes both
3081fa9e4066Sahrens  * hierarchical dependents (children) and data dependents (snapshots and
3082fa9e4066Sahrens  * clones).  The bulk of the processing occurs in get_dependents() in
3083fa9e4066Sahrens  * libzfs_graph.c.
3084fa9e4066Sahrens  */
3085fa9e4066Sahrens int
30863bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
30873bb79becSeschrock     zfs_iter_f func, void *data)
3088fa9e4066Sahrens {
3089fa9e4066Sahrens 	char **dependents;
3090fa9e4066Sahrens 	size_t count;
3091fa9e4066Sahrens 	int i;
3092fa9e4066Sahrens 	zfs_handle_t *child;
3093fa9e4066Sahrens 	int ret = 0;
3094fa9e4066Sahrens 
30953bb79becSeschrock 	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
30963bb79becSeschrock 	    &dependents, &count) != 0)
30973bb79becSeschrock 		return (-1);
30983bb79becSeschrock 
3099fa9e4066Sahrens 	for (i = 0; i < count; i++) {
310099653d4eSeschrock 		if ((child = make_dataset_handle(zhp->zfs_hdl,
310199653d4eSeschrock 		    dependents[i])) == NULL)
3102fa9e4066Sahrens 			continue;
3103fa9e4066Sahrens 
3104fa9e4066Sahrens 		if ((ret = func(child, data)) != 0)
3105fa9e4066Sahrens 			break;
3106fa9e4066Sahrens 	}
3107fa9e4066Sahrens 
3108fa9e4066Sahrens 	for (i = 0; i < count; i++)
3109fa9e4066Sahrens 		free(dependents[i]);
3110fa9e4066Sahrens 	free(dependents);
3111fa9e4066Sahrens 
3112fa9e4066Sahrens 	return (ret);
3113fa9e4066Sahrens }
3114fa9e4066Sahrens 
3115fa9e4066Sahrens /*
3116fa9e4066Sahrens  * Renames the given dataset.
3117fa9e4066Sahrens  */
3118fa9e4066Sahrens int
3119fa9e4066Sahrens zfs_rename(zfs_handle_t *zhp, const char *target)
3120fa9e4066Sahrens {
3121fa9e4066Sahrens 	int ret;
3122fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3123fa9e4066Sahrens 	char *delim;
3124fa9e4066Sahrens 	prop_changelist_t *cl;
3125fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
312699653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
312799653d4eSeschrock 	char errbuf[1024];
3128fa9e4066Sahrens 
3129fa9e4066Sahrens 	/* if we have the same exact name, just return success */
3130fa9e4066Sahrens 	if (strcmp(zhp->zfs_name, target) == 0)
3131fa9e4066Sahrens 		return (0);
3132fa9e4066Sahrens 
313399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
313499653d4eSeschrock 	    "cannot rename to '%s'"), target);
313599653d4eSeschrock 
3136fa9e4066Sahrens 	/*
3137fa9e4066Sahrens 	 * Make sure the target name is valid
3138fa9e4066Sahrens 	 */
3139fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
314098579b20Snd150628 		if ((strchr(target, '@') == NULL) ||
314198579b20Snd150628 		    *target == '@') {
314298579b20Snd150628 			/*
314398579b20Snd150628 			 * Snapshot target name is abbreviated,
314498579b20Snd150628 			 * reconstruct full dataset name
314598579b20Snd150628 			 */
314698579b20Snd150628 			(void) strlcpy(parent, zhp->zfs_name,
314798579b20Snd150628 			    sizeof (parent));
314898579b20Snd150628 			delim = strchr(parent, '@');
314998579b20Snd150628 			if (strchr(target, '@') == NULL)
315098579b20Snd150628 				*(++delim) = '\0';
315198579b20Snd150628 			else
315298579b20Snd150628 				*delim = '\0';
315398579b20Snd150628 			(void) strlcat(parent, target, sizeof (parent));
315498579b20Snd150628 			target = parent;
315598579b20Snd150628 		} else {
3156fa9e4066Sahrens 			/*
3157fa9e4066Sahrens 			 * Make sure we're renaming within the same dataset.
3158fa9e4066Sahrens 			 */
315998579b20Snd150628 			delim = strchr(target, '@');
316098579b20Snd150628 			if (strncmp(zhp->zfs_name, target, delim - target)
316198579b20Snd150628 			    != 0 || zhp->zfs_name[delim - target] != '@') {
316299653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
316398579b20Snd150628 				    "snapshots must be part of same "
316498579b20Snd150628 				    "dataset"));
316598579b20Snd150628 				return (zfs_error(hdl, EZFS_CROSSTARGET,
316698579b20Snd150628 					    errbuf));
3167fa9e4066Sahrens 			}
316898579b20Snd150628 		}
316998579b20Snd150628 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
317098579b20Snd150628 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3171fa9e4066Sahrens 	} else {
317298579b20Snd150628 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
317398579b20Snd150628 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3174e9dbad6fSeschrock 		uint64_t unused;
3175e9dbad6fSeschrock 
3176fa9e4066Sahrens 		/* validate parents */
3177e9dbad6fSeschrock 		if (check_parents(hdl, target, &unused) != 0)
3178fa9e4066Sahrens 			return (-1);
3179fa9e4066Sahrens 
3180fa9e4066Sahrens 		(void) parent_name(target, parent, sizeof (parent));
3181fa9e4066Sahrens 
3182fa9e4066Sahrens 		/* make sure we're in the same pool */
3183fa9e4066Sahrens 		verify((delim = strchr(target, '/')) != NULL);
3184fa9e4066Sahrens 		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3185fa9e4066Sahrens 		    zhp->zfs_name[delim - target] != '/') {
318699653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
318799653d4eSeschrock 			    "datasets must be within same pool"));
318899653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3189fa9e4066Sahrens 		}
3190f2fdf992Snd150628 
3191f2fdf992Snd150628 		/* new name cannot be a child of the current dataset name */
3192f2fdf992Snd150628 		if (strncmp(parent, zhp->zfs_name,
3193f2fdf992Snd150628 			    strlen(zhp->zfs_name)) == 0) {
3194f2fdf992Snd150628 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3195f2fdf992Snd150628 			    "New dataset name cannot be a descendent of "
3196f2fdf992Snd150628 			    "current dataset name"));
3197f2fdf992Snd150628 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3198f2fdf992Snd150628 		}
3199fa9e4066Sahrens 	}
3200fa9e4066Sahrens 
320199653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
320299653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
320399653d4eSeschrock 
3204fa9e4066Sahrens 	if (getzoneid() == GLOBAL_ZONEID &&
3205fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
320699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
320799653d4eSeschrock 		    "dataset is used in a non-global zone"));
320899653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3209fa9e4066Sahrens 	}
3210fa9e4066Sahrens 
3211fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
321299653d4eSeschrock 		return (-1);
3213fa9e4066Sahrens 
3214fa9e4066Sahrens 	if (changelist_haszonedchild(cl)) {
321599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
321699653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
321799653d4eSeschrock 		    "in a non-global zone"));
3218e9dbad6fSeschrock 		(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3219fa9e4066Sahrens 		goto error;
3220fa9e4066Sahrens 	}
3221fa9e4066Sahrens 
3222fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
3223fa9e4066Sahrens 		goto error;
3224fa9e4066Sahrens 
3225e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
3226fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
3227fa9e4066Sahrens 	else
3228fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3229fa9e4066Sahrens 
323098579b20Snd150628 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3231e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
323298579b20Snd150628 
323399653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
323499653d4eSeschrock 		(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3235fa9e4066Sahrens 
3236fa9e4066Sahrens 		/*
3237fa9e4066Sahrens 		 * On failure, we still want to remount any filesystems that
3238fa9e4066Sahrens 		 * were previously mounted, so we don't alter the system state.
3239fa9e4066Sahrens 		 */
3240fa9e4066Sahrens 		(void) changelist_postfix(cl);
3241fa9e4066Sahrens 	} else {
3242fa9e4066Sahrens 		changelist_rename(cl, zfs_get_name(zhp), target);
3243fa9e4066Sahrens 
3244fa9e4066Sahrens 		ret = changelist_postfix(cl);
3245fa9e4066Sahrens 	}
3246fa9e4066Sahrens 
3247fa9e4066Sahrens error:
3248fa9e4066Sahrens 	changelist_free(cl);
3249fa9e4066Sahrens 	return (ret);
3250fa9e4066Sahrens }
3251fa9e4066Sahrens 
3252fa9e4066Sahrens /*
3253fa9e4066Sahrens  * Given a zvol dataset, issue the ioctl to create the appropriate minor node,
3254fa9e4066Sahrens  * poke devfsadm to create the /dev link, and then wait for the link to appear.
3255fa9e4066Sahrens  */
3256fa9e4066Sahrens int
325799653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
3258fa9e4066Sahrens {
3259fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
326099653d4eSeschrock 	di_devlink_handle_t dhdl;
3261fa9e4066Sahrens 
3262fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3263fa9e4066Sahrens 
3264fa9e4066Sahrens 	/*
3265fa9e4066Sahrens 	 * Issue the appropriate ioctl.
3266fa9e4066Sahrens 	 */
326799653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
3268fa9e4066Sahrens 		switch (errno) {
3269fa9e4066Sahrens 		case EEXIST:
3270fa9e4066Sahrens 			/*
3271fa9e4066Sahrens 			 * Silently ignore the case where the link already
3272fa9e4066Sahrens 			 * exists.  This allows 'zfs volinit' to be run multiple
3273fa9e4066Sahrens 			 * times without errors.
3274fa9e4066Sahrens 			 */
3275fa9e4066Sahrens 			return (0);
3276fa9e4066Sahrens 
3277fa9e4066Sahrens 		default:
3278ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
327999653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot create device links "
328099653d4eSeschrock 			    "for '%s'"), dataset));
3281fa9e4066Sahrens 		}
3282fa9e4066Sahrens 	}
3283fa9e4066Sahrens 
3284fa9e4066Sahrens 	/*
3285fa9e4066Sahrens 	 * Call devfsadm and wait for the links to magically appear.
3286fa9e4066Sahrens 	 */
328799653d4eSeschrock 	if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) {
328899653d4eSeschrock 		zfs_error_aux(hdl, strerror(errno));
3289ece3d9b3Slling 		(void) zfs_error_fmt(hdl, EZFS_DEVLINKS,
329099653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot create device links "
329199653d4eSeschrock 		    "for '%s'"), dataset);
329299653d4eSeschrock 		(void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
3293fa9e4066Sahrens 		return (-1);
3294fa9e4066Sahrens 	} else {
329599653d4eSeschrock 		(void) di_devlink_fini(&dhdl);
3296fa9e4066Sahrens 	}
3297fa9e4066Sahrens 
3298fa9e4066Sahrens 	return (0);
3299fa9e4066Sahrens }
3300fa9e4066Sahrens 
3301fa9e4066Sahrens /*
3302fa9e4066Sahrens  * Remove a minor node for the given zvol and the associated /dev links.
3303fa9e4066Sahrens  */
3304fa9e4066Sahrens int
330599653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
3306fa9e4066Sahrens {
3307fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3308fa9e4066Sahrens 
3309fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3310fa9e4066Sahrens 
331199653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
3312fa9e4066Sahrens 		switch (errno) {
3313fa9e4066Sahrens 		case ENXIO:
3314fa9e4066Sahrens 			/*
3315fa9e4066Sahrens 			 * Silently ignore the case where the link no longer
3316fa9e4066Sahrens 			 * exists, so that 'zfs volfini' can be run multiple
3317fa9e4066Sahrens 			 * times without errors.
3318fa9e4066Sahrens 			 */
3319fa9e4066Sahrens 			return (0);
3320fa9e4066Sahrens 
3321fa9e4066Sahrens 		default:
3322ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
332399653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot remove device "
332499653d4eSeschrock 			    "links for '%s'"), dataset));
3325fa9e4066Sahrens 		}
3326fa9e4066Sahrens 	}
3327fa9e4066Sahrens 
3328fa9e4066Sahrens 	return (0);
3329fa9e4066Sahrens }
3330e9dbad6fSeschrock 
3331e9dbad6fSeschrock nvlist_t *
3332e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp)
3333e9dbad6fSeschrock {
3334e9dbad6fSeschrock 	return (zhp->zfs_user_props);
3335e9dbad6fSeschrock }
3336e9dbad6fSeschrock 
3337e9dbad6fSeschrock /*
3338e9dbad6fSeschrock  * Given a comma-separated list of properties, contruct a property list
3339e9dbad6fSeschrock  * containing both user-defined and native properties.  This function will
3340e9dbad6fSeschrock  * return a NULL list if 'all' is specified, which can later be expanded on a
3341e9dbad6fSeschrock  * per-dataset basis by zfs_expand_proplist().
3342e9dbad6fSeschrock  */
3343e9dbad6fSeschrock int
3344e9dbad6fSeschrock zfs_get_proplist(libzfs_handle_t *hdl, char *fields, zfs_proplist_t **listp)
3345e9dbad6fSeschrock {
3346e9dbad6fSeschrock 	int i;
3347e9dbad6fSeschrock 	size_t len;
3348e9dbad6fSeschrock 	char *s, *p;
3349e9dbad6fSeschrock 	char c;
3350e9dbad6fSeschrock 	zfs_prop_t prop;
3351e9dbad6fSeschrock 	zfs_proplist_t *entry;
3352e9dbad6fSeschrock 	zfs_proplist_t **last;
3353e9dbad6fSeschrock 
3354e9dbad6fSeschrock 	*listp = NULL;
3355e9dbad6fSeschrock 	last = listp;
3356e9dbad6fSeschrock 
3357e9dbad6fSeschrock 	/*
3358e9dbad6fSeschrock 	 * If 'all' is specified, return a NULL list.
3359e9dbad6fSeschrock 	 */
3360e9dbad6fSeschrock 	if (strcmp(fields, "all") == 0)
3361e9dbad6fSeschrock 		return (0);
3362e9dbad6fSeschrock 
3363e9dbad6fSeschrock 	/*
3364e9dbad6fSeschrock 	 * If no fields were specified, return an error.
3365e9dbad6fSeschrock 	 */
3366e9dbad6fSeschrock 	if (fields[0] == '\0') {
3367e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3368e9dbad6fSeschrock 		    "no properties specified"));
3369e9dbad6fSeschrock 		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
3370e9dbad6fSeschrock 		    "bad property list")));
3371e9dbad6fSeschrock 	}
3372e9dbad6fSeschrock 
3373e9dbad6fSeschrock 	/*
3374e9dbad6fSeschrock 	 * It would be nice to use getsubopt() here, but the inclusion of column
3375e9dbad6fSeschrock 	 * aliases makes this more effort than it's worth.
3376e9dbad6fSeschrock 	 */
3377e9dbad6fSeschrock 	s = fields;
3378e9dbad6fSeschrock 	while (*s != '\0') {
3379e9dbad6fSeschrock 		if ((p = strchr(s, ',')) == NULL) {
3380e9dbad6fSeschrock 			len = strlen(s);
3381e9dbad6fSeschrock 			p = s + len;
3382e9dbad6fSeschrock 		} else {
3383e9dbad6fSeschrock 			len = p - s;
3384e9dbad6fSeschrock 		}
3385e9dbad6fSeschrock 
3386e9dbad6fSeschrock 		/*
3387e9dbad6fSeschrock 		 * Check for empty options.
3388e9dbad6fSeschrock 		 */
3389e9dbad6fSeschrock 		if (len == 0) {
3390e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3391e9dbad6fSeschrock 			    "empty property name"));
3392e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP,
3393e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "bad property list")));
3394e9dbad6fSeschrock 		}
3395e9dbad6fSeschrock 
3396e9dbad6fSeschrock 		/*
3397e9dbad6fSeschrock 		 * Check all regular property names.
3398e9dbad6fSeschrock 		 */
3399e9dbad6fSeschrock 		c = s[len];
3400e9dbad6fSeschrock 		s[len] = '\0';
3401e9dbad6fSeschrock 		for (i = 0; i < ZFS_NPROP_ALL; i++) {
3402e9dbad6fSeschrock 			if ((prop = zfs_name_to_prop(s)) != ZFS_PROP_INVAL)
3403e9dbad6fSeschrock 				break;
3404e9dbad6fSeschrock 		}
3405e9dbad6fSeschrock 
3406e9dbad6fSeschrock 		/*
3407e9dbad6fSeschrock 		 * If no column is specified, and this isn't a user property,
3408e9dbad6fSeschrock 		 * return failure.
3409e9dbad6fSeschrock 		 */
3410e9dbad6fSeschrock 		if (i == ZFS_NPROP_ALL && !zfs_prop_user(s)) {
3411e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3412e9dbad6fSeschrock 			    "invalid property '%s'"), s);
3413e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP,
3414e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "bad property list")));
3415e9dbad6fSeschrock 		}
3416e9dbad6fSeschrock 
3417e9dbad6fSeschrock 		if ((entry = zfs_alloc(hdl, sizeof (zfs_proplist_t))) == NULL)
3418e9dbad6fSeschrock 			return (-1);
3419e9dbad6fSeschrock 
3420e9dbad6fSeschrock 		entry->pl_prop = prop;
3421e9dbad6fSeschrock 		if (prop == ZFS_PROP_INVAL) {
3422e9dbad6fSeschrock 			if ((entry->pl_user_prop =
3423e9dbad6fSeschrock 			    zfs_strdup(hdl, s)) == NULL) {
3424e9dbad6fSeschrock 				free(entry);
3425e9dbad6fSeschrock 				return (-1);
3426e9dbad6fSeschrock 			}
3427e9dbad6fSeschrock 			entry->pl_width = strlen(s);
3428e9dbad6fSeschrock 		} else {
3429e9dbad6fSeschrock 			entry->pl_width = zfs_prop_width(prop,
3430e9dbad6fSeschrock 			    &entry->pl_fixed);
3431e9dbad6fSeschrock 		}
3432e9dbad6fSeschrock 
3433e9dbad6fSeschrock 		*last = entry;
3434e9dbad6fSeschrock 		last = &entry->pl_next;
3435e9dbad6fSeschrock 
3436e9dbad6fSeschrock 		s = p;
3437e9dbad6fSeschrock 		if (c == ',')
3438e9dbad6fSeschrock 			s++;
3439e9dbad6fSeschrock 	}
3440e9dbad6fSeschrock 
3441e9dbad6fSeschrock 	return (0);
3442e9dbad6fSeschrock }
3443e9dbad6fSeschrock 
3444e9dbad6fSeschrock void
3445e9dbad6fSeschrock zfs_free_proplist(zfs_proplist_t *pl)
3446e9dbad6fSeschrock {
3447e9dbad6fSeschrock 	zfs_proplist_t *next;
3448e9dbad6fSeschrock 
3449e9dbad6fSeschrock 	while (pl != NULL) {
3450e9dbad6fSeschrock 		next = pl->pl_next;
3451e9dbad6fSeschrock 		free(pl->pl_user_prop);
3452e9dbad6fSeschrock 		free(pl);
3453e9dbad6fSeschrock 		pl = next;
3454e9dbad6fSeschrock 	}
3455e9dbad6fSeschrock }
3456e9dbad6fSeschrock 
3457e9dbad6fSeschrock /*
3458e9dbad6fSeschrock  * This function is used by 'zfs list' to determine the exact set of columns to
3459e9dbad6fSeschrock  * display, and their maximum widths.  This does two main things:
3460e9dbad6fSeschrock  *
3461e9dbad6fSeschrock  * 	- If this is a list of all properties, then expand the list to include
3462e9dbad6fSeschrock  *	  all native properties, and set a flag so that for each dataset we look
3463e9dbad6fSeschrock  *	  for new unique user properties and add them to the list.
3464e9dbad6fSeschrock  *
3465e9dbad6fSeschrock  * 	- For non fixed-width properties, keep track of the maximum width seen
3466e9dbad6fSeschrock  *	  so that we can size the column appropriately.
3467e9dbad6fSeschrock  */
3468e9dbad6fSeschrock int
3469e9dbad6fSeschrock zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
3470e9dbad6fSeschrock {
3471e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
3472e9dbad6fSeschrock 	zfs_prop_t prop;
3473e9dbad6fSeschrock 	zfs_proplist_t *entry;
3474e9dbad6fSeschrock 	zfs_proplist_t **last, **start;
3475e9dbad6fSeschrock 	nvlist_t *userprops, *propval;
3476e9dbad6fSeschrock 	nvpair_t *elem;
3477e9dbad6fSeschrock 	char *strval;
3478e9dbad6fSeschrock 	char buf[ZFS_MAXPROPLEN];
3479e9dbad6fSeschrock 
3480e9dbad6fSeschrock 	if (*plp == NULL) {
3481e9dbad6fSeschrock 		/*
3482e9dbad6fSeschrock 		 * If this is the very first time we've been called for an 'all'
3483e9dbad6fSeschrock 		 * specification, expand the list to include all native
3484e9dbad6fSeschrock 		 * properties.
3485e9dbad6fSeschrock 		 */
3486e9dbad6fSeschrock 		last = plp;
3487e9dbad6fSeschrock 		for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) {
3488e9dbad6fSeschrock 			if ((entry = zfs_alloc(hdl,
3489e9dbad6fSeschrock 			    sizeof (zfs_proplist_t))) == NULL)
3490e9dbad6fSeschrock 				return (-1);
3491e9dbad6fSeschrock 
3492e9dbad6fSeschrock 			entry->pl_prop = prop;
3493e9dbad6fSeschrock 			entry->pl_width = zfs_prop_width(prop,
3494e9dbad6fSeschrock 			    &entry->pl_fixed);
3495e9dbad6fSeschrock 			entry->pl_all = B_TRUE;
3496e9dbad6fSeschrock 
3497e9dbad6fSeschrock 			*last = entry;
3498e9dbad6fSeschrock 			last = &entry->pl_next;
3499e9dbad6fSeschrock 		}
3500e9dbad6fSeschrock 
3501e9dbad6fSeschrock 		/*
3502e9dbad6fSeschrock 		 * Add 'name' to the beginning of the list, which is handled
3503e9dbad6fSeschrock 		 * specially.
3504e9dbad6fSeschrock 		 */
3505e9dbad6fSeschrock 		if ((entry = zfs_alloc(hdl,
3506e9dbad6fSeschrock 		    sizeof (zfs_proplist_t))) == NULL)
3507e9dbad6fSeschrock 			return (-1);
3508e9dbad6fSeschrock 
3509e9dbad6fSeschrock 		entry->pl_prop = ZFS_PROP_NAME;
3510e9dbad6fSeschrock 		entry->pl_width = zfs_prop_width(ZFS_PROP_NAME,
3511e9dbad6fSeschrock 		    &entry->pl_fixed);
3512e9dbad6fSeschrock 		entry->pl_all = B_TRUE;
3513e9dbad6fSeschrock 		entry->pl_next = *plp;
3514e9dbad6fSeschrock 		*plp = entry;
3515e9dbad6fSeschrock 	}
3516e9dbad6fSeschrock 
3517e9dbad6fSeschrock 	userprops = zfs_get_user_props(zhp);
3518e9dbad6fSeschrock 
3519e9dbad6fSeschrock 	entry = *plp;
3520e9dbad6fSeschrock 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3521e9dbad6fSeschrock 		/*
3522e9dbad6fSeschrock 		 * Go through and add any user properties as necessary.  We
3523e9dbad6fSeschrock 		 * start by incrementing our list pointer to the first
3524e9dbad6fSeschrock 		 * non-native property.
3525e9dbad6fSeschrock 		 */
3526e9dbad6fSeschrock 		start = plp;
3527e9dbad6fSeschrock 		while (*start != NULL) {
3528e9dbad6fSeschrock 			if ((*start)->pl_prop == ZFS_PROP_INVAL)
3529e9dbad6fSeschrock 				break;
3530e9dbad6fSeschrock 			start = &(*start)->pl_next;
3531e9dbad6fSeschrock 		}
3532e9dbad6fSeschrock 
3533e9dbad6fSeschrock 		elem = NULL;
3534e9dbad6fSeschrock 		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3535e9dbad6fSeschrock 			/*
3536e9dbad6fSeschrock 			 * See if we've already found this property in our list.
3537e9dbad6fSeschrock 			 */
3538e9dbad6fSeschrock 			for (last = start; *last != NULL;
3539e9dbad6fSeschrock 			    last = &(*last)->pl_next) {
3540e9dbad6fSeschrock 				if (strcmp((*last)->pl_user_prop,
3541e9dbad6fSeschrock 				    nvpair_name(elem)) == 0)
3542e9dbad6fSeschrock 					break;
3543e9dbad6fSeschrock 			}
3544e9dbad6fSeschrock 
3545e9dbad6fSeschrock 			if (*last == NULL) {
3546e9dbad6fSeschrock 				if ((entry = zfs_alloc(hdl,
3547e9dbad6fSeschrock 				    sizeof (zfs_proplist_t))) == NULL ||
3548e9dbad6fSeschrock 				    ((entry->pl_user_prop = zfs_strdup(hdl,
3549e9dbad6fSeschrock 				    nvpair_name(elem)))) == NULL) {
3550e9dbad6fSeschrock 					free(entry);
3551e9dbad6fSeschrock 					return (-1);
3552e9dbad6fSeschrock 				}
3553e9dbad6fSeschrock 
3554e9dbad6fSeschrock 				entry->pl_prop = ZFS_PROP_INVAL;
3555e9dbad6fSeschrock 				entry->pl_width = strlen(nvpair_name(elem));
3556e9dbad6fSeschrock 				entry->pl_all = B_TRUE;
3557e9dbad6fSeschrock 				*last = entry;
3558e9dbad6fSeschrock 			}
3559e9dbad6fSeschrock 		}
3560e9dbad6fSeschrock 	}
3561e9dbad6fSeschrock 
3562e9dbad6fSeschrock 	/*
3563e9dbad6fSeschrock 	 * Now go through and check the width of any non-fixed columns
3564e9dbad6fSeschrock 	 */
3565e9dbad6fSeschrock 	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3566e9dbad6fSeschrock 		if (entry->pl_fixed)
3567e9dbad6fSeschrock 			continue;
3568e9dbad6fSeschrock 
3569e9dbad6fSeschrock 		if (entry->pl_prop != ZFS_PROP_INVAL) {
3570e9dbad6fSeschrock 			if (zfs_prop_get(zhp, entry->pl_prop,
3571e9dbad6fSeschrock 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3572e9dbad6fSeschrock 				if (strlen(buf) > entry->pl_width)
3573e9dbad6fSeschrock 					entry->pl_width = strlen(buf);
3574e9dbad6fSeschrock 			}
3575e9dbad6fSeschrock 		} else if (nvlist_lookup_nvlist(userprops,
3576e9dbad6fSeschrock 		    entry->pl_user_prop, &propval)  == 0) {
3577e9dbad6fSeschrock 			verify(nvlist_lookup_string(propval,
3578e9dbad6fSeschrock 			    ZFS_PROP_VALUE, &strval) == 0);
3579e9dbad6fSeschrock 			if (strlen(strval) > entry->pl_width)
3580e9dbad6fSeschrock 				entry->pl_width = strlen(strval);
3581e9dbad6fSeschrock 		}
3582e9dbad6fSeschrock 	}
3583e9dbad6fSeschrock 
3584e9dbad6fSeschrock 	return (0);
3585e9dbad6fSeschrock }
3586