xref: /illumos-gate/usr/src/lib/libzfs/common/libzfs_dataset.c (revision 72bdce51192b13a20009855f749004480874291b)
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 
1193bf7c2d40Srm160521 	/*
1194bf7c2d40Srm160521 	 * Normalize the name, to get rid of shorthand abbrevations.
1195bf7c2d40Srm160521 	 */
1196bf7c2d40Srm160521 	propname = zfs_prop_to_name(prop);
1197fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1198e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1199fa9e4066Sahrens 
1200fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1201fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
120299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
120399653d4eSeschrock 		    "dataset is used in a non-global zone"));
120499653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1205fa9e4066Sahrens 	}
1206fa9e4066Sahrens 
1207fa9e4066Sahrens 	/*
1208fa9e4066Sahrens 	 * Determine datasets which will be affected by this change, if any.
1209fa9e4066Sahrens 	 */
1210fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, prop, 0)) == NULL)
1211fa9e4066Sahrens 		return (-1);
1212fa9e4066Sahrens 
1213fa9e4066Sahrens 	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
121499653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
121599653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
121699653d4eSeschrock 		    "in a non-global zone"));
121799653d4eSeschrock 		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1218fa9e4066Sahrens 		goto error;
1219fa9e4066Sahrens 	}
1220fa9e4066Sahrens 
1221fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
1222fa9e4066Sahrens 		goto error;
1223fa9e4066Sahrens 
122499653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd,
122599653d4eSeschrock 	    ZFS_IOC_SET_PROP, &zc)) != 0) {
122699653d4eSeschrock 		return (zfs_standard_error(hdl, errno, errbuf));
1227fa9e4066Sahrens 	} else {
1228fa9e4066Sahrens 
1229efc555ebSnd150628 		if ((ret = changelist_postfix(cl)) != 0)
1230fa9e4066Sahrens 			goto error;
1231fa9e4066Sahrens 
1232fa9e4066Sahrens 		/*
1233fa9e4066Sahrens 		 * Refresh the statistics so the new property is reflected.
1234fa9e4066Sahrens 		 */
1235fa9e4066Sahrens 		(void) get_stats(zhp);
1236fa9e4066Sahrens 	}
1237fa9e4066Sahrens 
1238fa9e4066Sahrens error:
1239fa9e4066Sahrens 	changelist_free(cl);
1240fa9e4066Sahrens 	return (ret);
1241fa9e4066Sahrens }
1242fa9e4066Sahrens 
1243fa9e4066Sahrens static void
1244fa9e4066Sahrens nicebool(int value, char *buf, size_t buflen)
1245fa9e4066Sahrens {
1246fa9e4066Sahrens 	if (value)
1247fa9e4066Sahrens 		(void) strlcpy(buf, "on", buflen);
1248fa9e4066Sahrens 	else
1249fa9e4066Sahrens 		(void) strlcpy(buf, "off", buflen);
1250fa9e4066Sahrens }
1251fa9e4066Sahrens 
1252fa9e4066Sahrens /*
12537f7322feSeschrock  * True DSL properties are stored in an nvlist.  The following two functions
12547f7322feSeschrock  * extract them appropriately.
12557f7322feSeschrock  */
12567f7322feSeschrock static uint64_t
12577f7322feSeschrock getprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
12587f7322feSeschrock {
12597f7322feSeschrock 	nvlist_t *nv;
12607f7322feSeschrock 	uint64_t value;
12617f7322feSeschrock 
1262a2eea2e1Sahrens 	*source = NULL;
12637f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
12647f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
12657f7322feSeschrock 		verify(nvlist_lookup_uint64(nv, ZFS_PROP_VALUE, &value) == 0);
1266a2eea2e1Sahrens 		(void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
12677f7322feSeschrock 	} else {
12687f7322feSeschrock 		value = zfs_prop_default_numeric(prop);
12697f7322feSeschrock 		*source = "";
12707f7322feSeschrock 	}
12717f7322feSeschrock 
12727f7322feSeschrock 	return (value);
12737f7322feSeschrock }
12747f7322feSeschrock 
12757f7322feSeschrock static char *
12767f7322feSeschrock getprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
12777f7322feSeschrock {
12787f7322feSeschrock 	nvlist_t *nv;
12797f7322feSeschrock 	char *value;
12807f7322feSeschrock 
1281a2eea2e1Sahrens 	*source = NULL;
12827f7322feSeschrock 	if (nvlist_lookup_nvlist(zhp->zfs_props,
12837f7322feSeschrock 	    zfs_prop_to_name(prop), &nv) == 0) {
12847f7322feSeschrock 		verify(nvlist_lookup_string(nv, ZFS_PROP_VALUE, &value) == 0);
1285a2eea2e1Sahrens 		(void) nvlist_lookup_string(nv, ZFS_PROP_SOURCE, source);
12867f7322feSeschrock 	} else {
12877f7322feSeschrock 		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
12887f7322feSeschrock 			value = "";
12897f7322feSeschrock 		*source = "";
12907f7322feSeschrock 	}
12917f7322feSeschrock 
12927f7322feSeschrock 	return (value);
12937f7322feSeschrock }
12947f7322feSeschrock 
12957f7322feSeschrock /*
1296fa9e4066Sahrens  * Internal function for getting a numeric property.  Both zfs_prop_get() and
1297fa9e4066Sahrens  * zfs_prop_get_int() are built using this interface.
1298fa9e4066Sahrens  *
1299fa9e4066Sahrens  * Certain properties can be overridden using 'mount -o'.  In this case, scan
1300fa9e4066Sahrens  * the contents of the /etc/mnttab entry, searching for the appropriate options.
1301fa9e4066Sahrens  * If they differ from the on-disk values, report the current values and mark
1302fa9e4066Sahrens  * the source "temporary".
1303fa9e4066Sahrens  */
130499653d4eSeschrock static int
1305fa9e4066Sahrens get_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zfs_source_t *src,
130699653d4eSeschrock     char **source, uint64_t *val)
1307fa9e4066Sahrens {
1308fa9e4066Sahrens 	struct mnttab mnt;
13093ccfa83cSahrens 	char *mntopt_on = NULL;
13103ccfa83cSahrens 	char *mntopt_off = NULL;
1311fa9e4066Sahrens 
1312fa9e4066Sahrens 	*source = NULL;
1313fa9e4066Sahrens 
13143ccfa83cSahrens 	switch (prop) {
13153ccfa83cSahrens 	case ZFS_PROP_ATIME:
13163ccfa83cSahrens 		mntopt_on = MNTOPT_ATIME;
13173ccfa83cSahrens 		mntopt_off = MNTOPT_NOATIME;
13183ccfa83cSahrens 		break;
13193ccfa83cSahrens 
13203ccfa83cSahrens 	case ZFS_PROP_DEVICES:
13213ccfa83cSahrens 		mntopt_on = MNTOPT_DEVICES;
13223ccfa83cSahrens 		mntopt_off = MNTOPT_NODEVICES;
13233ccfa83cSahrens 		break;
13243ccfa83cSahrens 
13253ccfa83cSahrens 	case ZFS_PROP_EXEC:
13263ccfa83cSahrens 		mntopt_on = MNTOPT_EXEC;
13273ccfa83cSahrens 		mntopt_off = MNTOPT_NOEXEC;
13283ccfa83cSahrens 		break;
13293ccfa83cSahrens 
13303ccfa83cSahrens 	case ZFS_PROP_READONLY:
13313ccfa83cSahrens 		mntopt_on = MNTOPT_RO;
13323ccfa83cSahrens 		mntopt_off = MNTOPT_RW;
13333ccfa83cSahrens 		break;
13343ccfa83cSahrens 
13353ccfa83cSahrens 	case ZFS_PROP_SETUID:
13363ccfa83cSahrens 		mntopt_on = MNTOPT_SETUID;
13373ccfa83cSahrens 		mntopt_off = MNTOPT_NOSETUID;
13383ccfa83cSahrens 		break;
13393ccfa83cSahrens 
13403ccfa83cSahrens 	case ZFS_PROP_XATTR:
13413ccfa83cSahrens 		mntopt_on = MNTOPT_XATTR;
13423ccfa83cSahrens 		mntopt_off = MNTOPT_NOXATTR;
13433ccfa83cSahrens 		break;
13443ccfa83cSahrens 	}
13453ccfa83cSahrens 
13463bb79becSeschrock 	/*
13473bb79becSeschrock 	 * Because looking up the mount options is potentially expensive
13483bb79becSeschrock 	 * (iterating over all of /etc/mnttab), we defer its calculation until
13493bb79becSeschrock 	 * we're looking up a property which requires its presence.
13503bb79becSeschrock 	 */
13513bb79becSeschrock 	if (!zhp->zfs_mntcheck &&
13523ccfa83cSahrens 	    (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
13533ccfa83cSahrens 		struct mnttab entry, search = { 0 };
13543ccfa83cSahrens 		FILE *mnttab = zhp->zfs_hdl->libzfs_mnttab;
13553bb79becSeschrock 
13563bb79becSeschrock 		search.mnt_special = (char *)zhp->zfs_name;
13573bb79becSeschrock 		search.mnt_fstype = MNTTYPE_ZFS;
13583ccfa83cSahrens 		rewind(mnttab);
13593bb79becSeschrock 
13603ccfa83cSahrens 		if (getmntany(mnttab, &entry, &search) == 0) {
13613ccfa83cSahrens 			zhp->zfs_mntopts = zfs_strdup(zhp->zfs_hdl,
13623ccfa83cSahrens 			    entry.mnt_mntopts);
13633ccfa83cSahrens 			if (zhp->zfs_mntopts == NULL)
13643bb79becSeschrock 				return (-1);
13653ccfa83cSahrens 		}
13663bb79becSeschrock 
13673bb79becSeschrock 		zhp->zfs_mntcheck = B_TRUE;
13683bb79becSeschrock 	}
13693bb79becSeschrock 
1370fa9e4066Sahrens 	if (zhp->zfs_mntopts == NULL)
1371fa9e4066Sahrens 		mnt.mnt_mntopts = "";
1372fa9e4066Sahrens 	else
1373fa9e4066Sahrens 		mnt.mnt_mntopts = zhp->zfs_mntopts;
1374fa9e4066Sahrens 
1375fa9e4066Sahrens 	switch (prop) {
1376fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1377fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1378fa9e4066Sahrens 	case ZFS_PROP_EXEC:
13793ccfa83cSahrens 	case ZFS_PROP_READONLY:
13803ccfa83cSahrens 	case ZFS_PROP_SETUID:
13813ccfa83cSahrens 	case ZFS_PROP_XATTR:
138299653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1383fa9e4066Sahrens 
13843ccfa83cSahrens 		if (hasmntopt(&mnt, mntopt_on) && !*val) {
138599653d4eSeschrock 			*val = B_TRUE;
1386fa9e4066Sahrens 			if (src)
1387fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
13883ccfa83cSahrens 		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
138999653d4eSeschrock 			*val = B_FALSE;
1390fa9e4066Sahrens 			if (src)
1391fa9e4066Sahrens 				*src = ZFS_SRC_TEMPORARY;
1392fa9e4066Sahrens 		}
139399653d4eSeschrock 		break;
1394fa9e4066Sahrens 
1395fa9e4066Sahrens 	case ZFS_PROP_RECORDSIZE:
1396fa9e4066Sahrens 	case ZFS_PROP_COMPRESSION:
13977f7322feSeschrock 	case ZFS_PROP_ZONED:
1398a2eea2e1Sahrens 	case ZFS_PROP_CREATION:
1399a2eea2e1Sahrens 	case ZFS_PROP_COMPRESSRATIO:
1400a2eea2e1Sahrens 	case ZFS_PROP_REFERENCED:
1401a2eea2e1Sahrens 	case ZFS_PROP_USED:
1402a2eea2e1Sahrens 	case ZFS_PROP_CREATETXG:
1403a2eea2e1Sahrens 	case ZFS_PROP_AVAILABLE:
1404a2eea2e1Sahrens 	case ZFS_PROP_VOLSIZE:
1405a2eea2e1Sahrens 	case ZFS_PROP_VOLBLOCKSIZE:
1406fda77a98Srm160521 		*val = getprop_uint64(zhp, prop, source);
1407fda77a98Srm160521 		break;
1408fda77a98Srm160521 
14093ccfa83cSahrens 	case ZFS_PROP_CANMOUNT:
141099653d4eSeschrock 		*val = getprop_uint64(zhp, prop, source);
1411fda77a98Srm160521 		if (*val == 0)
1412fda77a98Srm160521 			*source = zhp->zfs_name;
1413fda77a98Srm160521 		else
1414fda77a98Srm160521 			*source = "";	/* default */
141599653d4eSeschrock 		break;
1416fa9e4066Sahrens 
1417fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1418fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
1419a2eea2e1Sahrens 		*val = getprop_uint64(zhp, prop, source);
1420a2eea2e1Sahrens 		if (*val == 0)
1421fa9e4066Sahrens 			*source = "";	/* default */
1422fa9e4066Sahrens 		else
1423fa9e4066Sahrens 			*source = zhp->zfs_name;
142499653d4eSeschrock 		break;
1425fa9e4066Sahrens 
1426fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
142799653d4eSeschrock 		*val = (zhp->zfs_mntopts != NULL);
142899653d4eSeschrock 		break;
1429fa9e4066Sahrens 
143039c23413Seschrock 	case ZFS_PROP_NUMCLONES:
143139c23413Seschrock 		*val = zhp->zfs_dmustats.dds_num_clones;
143239c23413Seschrock 		break;
143339c23413Seschrock 
1434fa9e4066Sahrens 	default:
143599653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
143699653d4eSeschrock 		    "cannot get non-numeric property"));
143799653d4eSeschrock 		return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
143899653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "internal error")));
1439fa9e4066Sahrens 	}
1440fa9e4066Sahrens 
1441fa9e4066Sahrens 	return (0);
1442fa9e4066Sahrens }
1443fa9e4066Sahrens 
1444fa9e4066Sahrens /*
1445fa9e4066Sahrens  * Calculate the source type, given the raw source string.
1446fa9e4066Sahrens  */
1447fa9e4066Sahrens static void
1448fa9e4066Sahrens get_source(zfs_handle_t *zhp, zfs_source_t *srctype, char *source,
1449fa9e4066Sahrens     char *statbuf, size_t statlen)
1450fa9e4066Sahrens {
1451fa9e4066Sahrens 	if (statbuf == NULL || *srctype == ZFS_SRC_TEMPORARY)
1452fa9e4066Sahrens 		return;
1453fa9e4066Sahrens 
1454fa9e4066Sahrens 	if (source == NULL) {
1455fa9e4066Sahrens 		*srctype = ZFS_SRC_NONE;
1456fa9e4066Sahrens 	} else if (source[0] == '\0') {
1457fa9e4066Sahrens 		*srctype = ZFS_SRC_DEFAULT;
1458fa9e4066Sahrens 	} else {
1459fa9e4066Sahrens 		if (strcmp(source, zhp->zfs_name) == 0) {
1460fa9e4066Sahrens 			*srctype = ZFS_SRC_LOCAL;
1461fa9e4066Sahrens 		} else {
1462fa9e4066Sahrens 			(void) strlcpy(statbuf, source, statlen);
1463fa9e4066Sahrens 			*srctype = ZFS_SRC_INHERITED;
1464fa9e4066Sahrens 		}
1465fa9e4066Sahrens 	}
1466fa9e4066Sahrens 
1467fa9e4066Sahrens }
1468fa9e4066Sahrens 
1469fa9e4066Sahrens /*
1470fa9e4066Sahrens  * Retrieve a property from the given object.  If 'literal' is specified, then
1471fa9e4066Sahrens  * numbers are left as exact values.  Otherwise, numbers are converted to a
1472fa9e4066Sahrens  * human-readable form.
1473fa9e4066Sahrens  *
1474fa9e4066Sahrens  * Returns 0 on success, or -1 on error.
1475fa9e4066Sahrens  */
1476fa9e4066Sahrens int
1477fa9e4066Sahrens zfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
147899653d4eSeschrock     zfs_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1479fa9e4066Sahrens {
1480fa9e4066Sahrens 	char *source = NULL;
1481fa9e4066Sahrens 	uint64_t val;
1482fa9e4066Sahrens 	char *str;
1483fa9e4066Sahrens 	const char *root;
1484e9dbad6fSeschrock 	const char *strval;
1485fa9e4066Sahrens 
1486fa9e4066Sahrens 	/*
1487fa9e4066Sahrens 	 * Check to see if this property applies to our object
1488fa9e4066Sahrens 	 */
1489fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1490fa9e4066Sahrens 		return (-1);
1491fa9e4066Sahrens 
1492fa9e4066Sahrens 	if (src)
1493fa9e4066Sahrens 		*src = ZFS_SRC_NONE;
1494fa9e4066Sahrens 
1495fa9e4066Sahrens 	switch (prop) {
1496fa9e4066Sahrens 	case ZFS_PROP_ATIME:
1497fa9e4066Sahrens 	case ZFS_PROP_READONLY:
1498fa9e4066Sahrens 	case ZFS_PROP_SETUID:
1499fa9e4066Sahrens 	case ZFS_PROP_ZONED:
1500fa9e4066Sahrens 	case ZFS_PROP_DEVICES:
1501fa9e4066Sahrens 	case ZFS_PROP_EXEC:
1502e9dbad6fSeschrock 	case ZFS_PROP_CANMOUNT:
15037b55fa8eSck153898 	case ZFS_PROP_XATTR:
1504fa9e4066Sahrens 		/*
1505fa9e4066Sahrens 		 * Basic boolean values are built on top of
1506fa9e4066Sahrens 		 * get_numeric_property().
1507fa9e4066Sahrens 		 */
150899653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
150999653d4eSeschrock 			return (-1);
151099653d4eSeschrock 		nicebool(val, propbuf, proplen);
1511fa9e4066Sahrens 
1512fa9e4066Sahrens 		break;
1513fa9e4066Sahrens 
1514fa9e4066Sahrens 	case ZFS_PROP_AVAILABLE:
1515fa9e4066Sahrens 	case ZFS_PROP_RECORDSIZE:
1516fa9e4066Sahrens 	case ZFS_PROP_CREATETXG:
1517fa9e4066Sahrens 	case ZFS_PROP_REFERENCED:
1518fa9e4066Sahrens 	case ZFS_PROP_USED:
1519fa9e4066Sahrens 	case ZFS_PROP_VOLSIZE:
1520fa9e4066Sahrens 	case ZFS_PROP_VOLBLOCKSIZE:
152139c23413Seschrock 	case ZFS_PROP_NUMCLONES:
1522fa9e4066Sahrens 		/*
1523fa9e4066Sahrens 		 * Basic numeric values are built on top of
1524fa9e4066Sahrens 		 * get_numeric_property().
1525fa9e4066Sahrens 		 */
152699653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
152799653d4eSeschrock 			return (-1);
1528fa9e4066Sahrens 		if (literal)
15295ad82045Snd150628 			(void) snprintf(propbuf, proplen, "%llu",
15305ad82045Snd150628 			(u_longlong_t)val);
1531fa9e4066Sahrens 		else
1532fa9e4066Sahrens 			zfs_nicenum(val, propbuf, proplen);
1533fa9e4066Sahrens 		break;
1534fa9e4066Sahrens 
1535fa9e4066Sahrens 	case ZFS_PROP_COMPRESSION:
1536fa9e4066Sahrens 	case ZFS_PROP_CHECKSUM:
1537fa9e4066Sahrens 	case ZFS_PROP_SNAPDIR:
1538fa9e4066Sahrens 	case ZFS_PROP_ACLMODE:
1539fa9e4066Sahrens 	case ZFS_PROP_ACLINHERIT:
15407f7322feSeschrock 		val = getprop_uint64(zhp, prop, &source);
1541e9dbad6fSeschrock 		verify(zfs_prop_index_to_string(prop, val, &strval) == 0);
1542e9dbad6fSeschrock 		(void) strlcpy(propbuf, strval, proplen);
1543fa9e4066Sahrens 		break;
1544fa9e4066Sahrens 
1545fa9e4066Sahrens 	case ZFS_PROP_CREATION:
1546fa9e4066Sahrens 		/*
1547fa9e4066Sahrens 		 * 'creation' is a time_t stored in the statistics.  We convert
1548fa9e4066Sahrens 		 * this into a string unless 'literal' is specified.
1549fa9e4066Sahrens 		 */
1550fa9e4066Sahrens 		{
1551a2eea2e1Sahrens 			val = getprop_uint64(zhp, prop, &source);
1552a2eea2e1Sahrens 			time_t time = (time_t)val;
1553fa9e4066Sahrens 			struct tm t;
1554fa9e4066Sahrens 
1555fa9e4066Sahrens 			if (literal ||
1556fa9e4066Sahrens 			    localtime_r(&time, &t) == NULL ||
1557fa9e4066Sahrens 			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1558fa9e4066Sahrens 			    &t) == 0)
1559a2eea2e1Sahrens 				(void) snprintf(propbuf, proplen, "%llu", val);
1560fa9e4066Sahrens 		}
1561fa9e4066Sahrens 		break;
1562fa9e4066Sahrens 
1563fa9e4066Sahrens 	case ZFS_PROP_MOUNTPOINT:
1564fa9e4066Sahrens 		/*
1565fa9e4066Sahrens 		 * Getting the precise mountpoint can be tricky.
1566fa9e4066Sahrens 		 *
1567fa9e4066Sahrens 		 *  - for 'none' or 'legacy', return those values.
1568fa9e4066Sahrens 		 *  - for default mountpoints, construct it as /zfs/<dataset>
1569fa9e4066Sahrens 		 *  - for inherited mountpoints, we want to take everything
1570fa9e4066Sahrens 		 *    after our ancestor and append it to the inherited value.
1571fa9e4066Sahrens 		 *
1572fa9e4066Sahrens 		 * If the pool has an alternate root, we want to prepend that
1573fa9e4066Sahrens 		 * root to any values we return.
1574fa9e4066Sahrens 		 */
1575ea8dc4b6Seschrock 		root = zhp->zfs_root;
15767f7322feSeschrock 		str = getprop_string(zhp, prop, &source);
1577fa9e4066Sahrens 
15787f7322feSeschrock 		if (str[0] == '\0') {
1579fa9e4066Sahrens 			(void) snprintf(propbuf, proplen, "%s/zfs/%s",
1580fa9e4066Sahrens 			    root, zhp->zfs_name);
15817f7322feSeschrock 		} else if (str[0] == '/') {
15827f7322feSeschrock 			const char *relpath = zhp->zfs_name + strlen(source);
1583fa9e4066Sahrens 
1584fa9e4066Sahrens 			if (relpath[0] == '/')
1585fa9e4066Sahrens 				relpath++;
15867f7322feSeschrock 			if (str[1] == '\0')
15877f7322feSeschrock 				str++;
1588fa9e4066Sahrens 
1589fa9e4066Sahrens 			if (relpath[0] == '\0')
1590fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s",
15917f7322feSeschrock 				    root, str);
1592fa9e4066Sahrens 			else
1593fa9e4066Sahrens 				(void) snprintf(propbuf, proplen, "%s%s%s%s",
15947f7322feSeschrock 				    root, str, relpath[0] == '@' ? "" : "/",
1595fa9e4066Sahrens 				    relpath);
1596fa9e4066Sahrens 		} else {
1597fa9e4066Sahrens 			/* 'legacy' or 'none' */
15987f7322feSeschrock 			(void) strlcpy(propbuf, str, proplen);
1599fa9e4066Sahrens 		}
1600fa9e4066Sahrens 
1601fa9e4066Sahrens 		break;
1602fa9e4066Sahrens 
1603fa9e4066Sahrens 	case ZFS_PROP_SHARENFS:
1604f3861e1aSahl 	case ZFS_PROP_SHAREISCSI:
1605f3861e1aSahl 	case ZFS_PROP_ISCSIOPTIONS:
16067f7322feSeschrock 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
16077f7322feSeschrock 		    proplen);
1608fa9e4066Sahrens 		break;
1609fa9e4066Sahrens 
1610fa9e4066Sahrens 	case ZFS_PROP_ORIGIN:
1611a2eea2e1Sahrens 		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
1612fa9e4066Sahrens 		    proplen);
1613fa9e4066Sahrens 		/*
1614fa9e4066Sahrens 		 * If there is no parent at all, return failure to indicate that
1615fa9e4066Sahrens 		 * it doesn't apply to this dataset.
1616fa9e4066Sahrens 		 */
1617fa9e4066Sahrens 		if (propbuf[0] == '\0')
1618fa9e4066Sahrens 			return (-1);
1619fa9e4066Sahrens 		break;
1620fa9e4066Sahrens 
1621fa9e4066Sahrens 	case ZFS_PROP_QUOTA:
1622fa9e4066Sahrens 	case ZFS_PROP_RESERVATION:
162399653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
162499653d4eSeschrock 			return (-1);
1625fa9e4066Sahrens 
1626fa9e4066Sahrens 		/*
1627fa9e4066Sahrens 		 * If quota or reservation is 0, we translate this into 'none'
1628fa9e4066Sahrens 		 * (unless literal is set), and indicate that it's the default
1629fa9e4066Sahrens 		 * value.  Otherwise, we print the number nicely and indicate
1630fa9e4066Sahrens 		 * that its set locally.
1631fa9e4066Sahrens 		 */
1632fa9e4066Sahrens 		if (val == 0) {
1633fa9e4066Sahrens 			if (literal)
1634fa9e4066Sahrens 				(void) strlcpy(propbuf, "0", proplen);
1635fa9e4066Sahrens 			else
1636fa9e4066Sahrens 				(void) strlcpy(propbuf, "none", proplen);
1637fa9e4066Sahrens 		} else {
1638fa9e4066Sahrens 			if (literal)
16395ad82045Snd150628 				(void) snprintf(propbuf, proplen, "%llu",
16405ad82045Snd150628 				(u_longlong_t)val);
1641fa9e4066Sahrens 			else
1642fa9e4066Sahrens 				zfs_nicenum(val, propbuf, proplen);
1643fa9e4066Sahrens 		}
1644fa9e4066Sahrens 		break;
1645fa9e4066Sahrens 
1646fa9e4066Sahrens 	case ZFS_PROP_COMPRESSRATIO:
164799653d4eSeschrock 		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
164899653d4eSeschrock 			return (-1);
16495ad82045Snd150628 		(void) snprintf(propbuf, proplen, "%lld.%02lldx", (longlong_t)
16505ad82045Snd150628 		    val / 100, (longlong_t)val % 100);
1651fa9e4066Sahrens 		break;
1652fa9e4066Sahrens 
1653fa9e4066Sahrens 	case ZFS_PROP_TYPE:
1654fa9e4066Sahrens 		switch (zhp->zfs_type) {
1655fa9e4066Sahrens 		case ZFS_TYPE_FILESYSTEM:
1656fa9e4066Sahrens 			str = "filesystem";
1657fa9e4066Sahrens 			break;
1658fa9e4066Sahrens 		case ZFS_TYPE_VOLUME:
1659fa9e4066Sahrens 			str = "volume";
1660fa9e4066Sahrens 			break;
1661fa9e4066Sahrens 		case ZFS_TYPE_SNAPSHOT:
1662fa9e4066Sahrens 			str = "snapshot";
1663fa9e4066Sahrens 			break;
1664fa9e4066Sahrens 		default:
166599653d4eSeschrock 			abort();
1666fa9e4066Sahrens 		}
1667fa9e4066Sahrens 		(void) snprintf(propbuf, proplen, "%s", str);
1668fa9e4066Sahrens 		break;
1669fa9e4066Sahrens 
1670fa9e4066Sahrens 	case ZFS_PROP_MOUNTED:
1671fa9e4066Sahrens 		/*
1672fa9e4066Sahrens 		 * The 'mounted' property is a pseudo-property that described
1673fa9e4066Sahrens 		 * whether the filesystem is currently mounted.  Even though
1674fa9e4066Sahrens 		 * it's a boolean value, the typical values of "on" and "off"
1675fa9e4066Sahrens 		 * don't make sense, so we translate to "yes" and "no".
1676fa9e4066Sahrens 		 */
167799653d4eSeschrock 		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
167899653d4eSeschrock 		    src, &source, &val) != 0)
167999653d4eSeschrock 			return (-1);
168099653d4eSeschrock 		if (val)
1681fa9e4066Sahrens 			(void) strlcpy(propbuf, "yes", proplen);
1682fa9e4066Sahrens 		else
1683fa9e4066Sahrens 			(void) strlcpy(propbuf, "no", proplen);
1684fa9e4066Sahrens 		break;
1685fa9e4066Sahrens 
1686fa9e4066Sahrens 	case ZFS_PROP_NAME:
1687fa9e4066Sahrens 		/*
1688fa9e4066Sahrens 		 * The 'name' property is a pseudo-property derived from the
1689fa9e4066Sahrens 		 * dataset name.  It is presented as a real property to simplify
1690fa9e4066Sahrens 		 * consumers.
1691fa9e4066Sahrens 		 */
1692fa9e4066Sahrens 		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
1693fa9e4066Sahrens 		break;
1694fa9e4066Sahrens 
1695fa9e4066Sahrens 	default:
169699653d4eSeschrock 		abort();
1697fa9e4066Sahrens 	}
1698fa9e4066Sahrens 
1699fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1700fa9e4066Sahrens 
1701fa9e4066Sahrens 	return (0);
1702fa9e4066Sahrens }
1703fa9e4066Sahrens 
1704fa9e4066Sahrens /*
1705fa9e4066Sahrens  * Utility function to get the given numeric property.  Does no validation that
1706fa9e4066Sahrens  * the given property is the appropriate type; should only be used with
1707fa9e4066Sahrens  * hard-coded property types.
1708fa9e4066Sahrens  */
1709fa9e4066Sahrens uint64_t
1710fa9e4066Sahrens zfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
1711fa9e4066Sahrens {
1712fa9e4066Sahrens 	char *source;
1713fa9e4066Sahrens 	zfs_source_t sourcetype = ZFS_SRC_NONE;
171499653d4eSeschrock 	uint64_t val;
1715fa9e4066Sahrens 
171699653d4eSeschrock 	(void) get_numeric_property(zhp, prop, &sourcetype, &source, &val);
171799653d4eSeschrock 
171899653d4eSeschrock 	return (val);
1719fa9e4066Sahrens }
1720fa9e4066Sahrens 
1721fa9e4066Sahrens /*
1722fa9e4066Sahrens  * Similar to zfs_prop_get(), but returns the value as an integer.
1723fa9e4066Sahrens  */
1724fa9e4066Sahrens int
1725fa9e4066Sahrens zfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
1726fa9e4066Sahrens     zfs_source_t *src, char *statbuf, size_t statlen)
1727fa9e4066Sahrens {
1728fa9e4066Sahrens 	char *source;
1729fa9e4066Sahrens 
1730fa9e4066Sahrens 	/*
1731fa9e4066Sahrens 	 * Check to see if this property applies to our object
1732fa9e4066Sahrens 	 */
1733fa9e4066Sahrens 	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1734ece3d9b3Slling 		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
173599653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
173699653d4eSeschrock 		    zfs_prop_to_name(prop)));
1737fa9e4066Sahrens 
1738fa9e4066Sahrens 	if (src)
1739fa9e4066Sahrens 		*src = ZFS_SRC_NONE;
1740fa9e4066Sahrens 
174199653d4eSeschrock 	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
174299653d4eSeschrock 		return (-1);
1743fa9e4066Sahrens 
1744fa9e4066Sahrens 	get_source(zhp, src, source, statbuf, statlen);
1745fa9e4066Sahrens 
1746fa9e4066Sahrens 	return (0);
1747fa9e4066Sahrens }
1748fa9e4066Sahrens 
1749fa9e4066Sahrens /*
1750fa9e4066Sahrens  * Returns the name of the given zfs handle.
1751fa9e4066Sahrens  */
1752fa9e4066Sahrens const char *
1753fa9e4066Sahrens zfs_get_name(const zfs_handle_t *zhp)
1754fa9e4066Sahrens {
1755fa9e4066Sahrens 	return (zhp->zfs_name);
1756fa9e4066Sahrens }
1757fa9e4066Sahrens 
1758fa9e4066Sahrens /*
1759fa9e4066Sahrens  * Returns the type of the given zfs handle.
1760fa9e4066Sahrens  */
1761fa9e4066Sahrens zfs_type_t
1762fa9e4066Sahrens zfs_get_type(const zfs_handle_t *zhp)
1763fa9e4066Sahrens {
1764fa9e4066Sahrens 	return (zhp->zfs_type);
1765fa9e4066Sahrens }
1766fa9e4066Sahrens 
1767fa9e4066Sahrens /*
17687f7322feSeschrock  * Iterate over all child filesystems
1769fa9e4066Sahrens  */
1770fa9e4066Sahrens int
17717f7322feSeschrock zfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
1772fa9e4066Sahrens {
1773fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1774fa9e4066Sahrens 	zfs_handle_t *nzhp;
1775fa9e4066Sahrens 	int ret;
1776fa9e4066Sahrens 
1777fa9e4066Sahrens 	for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
177899653d4eSeschrock 	    ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DATASET_LIST_NEXT, &zc) == 0;
1779fa9e4066Sahrens 	    (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
1780fa9e4066Sahrens 		/*
1781fa9e4066Sahrens 		 * Ignore private dataset names.
1782fa9e4066Sahrens 		 */
1783fa9e4066Sahrens 		if (dataset_name_hidden(zc.zc_name))
1784fa9e4066Sahrens 			continue;
1785fa9e4066Sahrens 
1786fa9e4066Sahrens 		/*
1787fa9e4066Sahrens 		 * Silently ignore errors, as the only plausible explanation is
1788fa9e4066Sahrens 		 * that the pool has since been removed.
1789fa9e4066Sahrens 		 */
179099653d4eSeschrock 		if ((nzhp = make_dataset_handle(zhp->zfs_hdl,
179199653d4eSeschrock 		    zc.zc_name)) == NULL)
1792fa9e4066Sahrens 			continue;
1793fa9e4066Sahrens 
1794fa9e4066Sahrens 		if ((ret = func(nzhp, data)) != 0)
1795fa9e4066Sahrens 			return (ret);
1796fa9e4066Sahrens 	}
1797fa9e4066Sahrens 
1798fa9e4066Sahrens 	/*
1799fa9e4066Sahrens 	 * An errno value of ESRCH indicates normal completion.  If ENOENT is
1800fa9e4066Sahrens 	 * returned, then the underlying dataset has been removed since we
1801fa9e4066Sahrens 	 * obtained the handle.
1802fa9e4066Sahrens 	 */
1803fa9e4066Sahrens 	if (errno != ESRCH && errno != ENOENT)
180499653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
180599653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot iterate filesystems")));
1806fa9e4066Sahrens 
18077f7322feSeschrock 	return (0);
18087f7322feSeschrock }
18097f7322feSeschrock 
18107f7322feSeschrock /*
18117f7322feSeschrock  * Iterate over all snapshots
18127f7322feSeschrock  */
18137f7322feSeschrock int
18147f7322feSeschrock zfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
18157f7322feSeschrock {
18167f7322feSeschrock 	zfs_cmd_t zc = { 0 };
18177f7322feSeschrock 	zfs_handle_t *nzhp;
18187f7322feSeschrock 	int ret;
1819fa9e4066Sahrens 
1820fa9e4066Sahrens 	for ((void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
182199653d4eSeschrock 	    ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT_LIST_NEXT,
182299653d4eSeschrock 	    &zc) == 0;
1823fa9e4066Sahrens 	    (void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name))) {
1824fa9e4066Sahrens 
182599653d4eSeschrock 		if ((nzhp = make_dataset_handle(zhp->zfs_hdl,
182699653d4eSeschrock 		    zc.zc_name)) == NULL)
1827fa9e4066Sahrens 			continue;
1828fa9e4066Sahrens 
1829fa9e4066Sahrens 		if ((ret = func(nzhp, data)) != 0)
1830fa9e4066Sahrens 			return (ret);
1831fa9e4066Sahrens 	}
1832fa9e4066Sahrens 
1833fa9e4066Sahrens 	/*
1834fa9e4066Sahrens 	 * An errno value of ESRCH indicates normal completion.  If ENOENT is
1835fa9e4066Sahrens 	 * returned, then the underlying dataset has been removed since we
1836fa9e4066Sahrens 	 * obtained the handle.  Silently ignore this case, and return success.
1837fa9e4066Sahrens 	 */
1838fa9e4066Sahrens 	if (errno != ESRCH && errno != ENOENT)
183999653d4eSeschrock 		return (zfs_standard_error(zhp->zfs_hdl, errno,
184099653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot iterate filesystems")));
1841fa9e4066Sahrens 
1842fa9e4066Sahrens 	return (0);
1843fa9e4066Sahrens }
1844fa9e4066Sahrens 
1845fa9e4066Sahrens /*
18467f7322feSeschrock  * Iterate over all children, snapshots and filesystems
18477f7322feSeschrock  */
18487f7322feSeschrock int
18497f7322feSeschrock zfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
18507f7322feSeschrock {
18517f7322feSeschrock 	int ret;
18527f7322feSeschrock 
18537f7322feSeschrock 	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
18547f7322feSeschrock 		return (ret);
18557f7322feSeschrock 
18567f7322feSeschrock 	return (zfs_iter_snapshots(zhp, func, data));
18577f7322feSeschrock }
18587f7322feSeschrock 
18597f7322feSeschrock /*
1860fa9e4066Sahrens  * Given a complete name, return just the portion that refers to the parent.
1861fa9e4066Sahrens  * Can return NULL if this is a pool.
1862fa9e4066Sahrens  */
1863fa9e4066Sahrens static int
1864fa9e4066Sahrens parent_name(const char *path, char *buf, size_t buflen)
1865fa9e4066Sahrens {
1866fa9e4066Sahrens 	char *loc;
1867fa9e4066Sahrens 
1868fa9e4066Sahrens 	if ((loc = strrchr(path, '/')) == NULL)
1869fa9e4066Sahrens 		return (-1);
1870fa9e4066Sahrens 
1871fa9e4066Sahrens 	(void) strncpy(buf, path, MIN(buflen, loc - path));
1872fa9e4066Sahrens 	buf[loc - path] = '\0';
1873fa9e4066Sahrens 
1874fa9e4066Sahrens 	return (0);
1875fa9e4066Sahrens }
1876fa9e4066Sahrens 
1877fa9e4066Sahrens /*
1878e9dbad6fSeschrock  * Checks to make sure that the given path has a parent, and that it exists.  We
1879e9dbad6fSeschrock  * also fetch the 'zoned' property, which is used to validate property settings
1880e9dbad6fSeschrock  * when creating new datasets.
1881fa9e4066Sahrens  */
1882fa9e4066Sahrens static int
1883e9dbad6fSeschrock check_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned)
1884fa9e4066Sahrens {
1885fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1886fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
1887fa9e4066Sahrens 	char *slash;
18887f7322feSeschrock 	zfs_handle_t *zhp;
188999653d4eSeschrock 	char errbuf[1024];
189099653d4eSeschrock 
189199653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), "cannot create '%s'",
189299653d4eSeschrock 	    path);
1893fa9e4066Sahrens 
1894fa9e4066Sahrens 	/* get parent, and check to see if this is just a pool */
1895fa9e4066Sahrens 	if (parent_name(path, parent, sizeof (parent)) != 0) {
189699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
189799653d4eSeschrock 		    "missing dataset name"));
189899653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
1899fa9e4066Sahrens 	}
1900fa9e4066Sahrens 
1901fa9e4066Sahrens 	/* check to see if the pool exists */
1902fa9e4066Sahrens 	if ((slash = strchr(parent, '/')) == NULL)
1903fa9e4066Sahrens 		slash = parent + strlen(parent);
1904fa9e4066Sahrens 	(void) strncpy(zc.zc_name, parent, slash - parent);
1905fa9e4066Sahrens 	zc.zc_name[slash - parent] = '\0';
190699653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
1907fa9e4066Sahrens 	    errno == ENOENT) {
190899653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
190999653d4eSeschrock 		    "no such pool '%s'"), zc.zc_name);
191099653d4eSeschrock 		return (zfs_error(hdl, EZFS_NOENT, errbuf));
1911fa9e4066Sahrens 	}
1912fa9e4066Sahrens 
1913fa9e4066Sahrens 	/* check to see if the parent dataset exists */
191499653d4eSeschrock 	if ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
1915fa9e4066Sahrens 		switch (errno) {
1916fa9e4066Sahrens 		case ENOENT:
191799653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
191899653d4eSeschrock 			    "parent does not exist"));
191999653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
1920fa9e4066Sahrens 
1921fa9e4066Sahrens 		default:
192299653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
1923fa9e4066Sahrens 		}
1924fa9e4066Sahrens 	}
1925fa9e4066Sahrens 
1926e9dbad6fSeschrock 	*zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
1927fa9e4066Sahrens 	/* we are in a non-global zone, but parent is in the global zone */
1928e9dbad6fSeschrock 	if (getzoneid() != GLOBAL_ZONEID && !(*zoned)) {
192999653d4eSeschrock 		(void) zfs_standard_error(hdl, EPERM, errbuf);
19307f7322feSeschrock 		zfs_close(zhp);
1931fa9e4066Sahrens 		return (-1);
1932fa9e4066Sahrens 	}
1933fa9e4066Sahrens 
1934fa9e4066Sahrens 	/* make sure parent is a filesystem */
19357f7322feSeschrock 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
193699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
193799653d4eSeschrock 		    "parent is not a filesystem"));
193899653d4eSeschrock 		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
19397f7322feSeschrock 		zfs_close(zhp);
1940fa9e4066Sahrens 		return (-1);
1941fa9e4066Sahrens 	}
1942fa9e4066Sahrens 
19437f7322feSeschrock 	zfs_close(zhp);
1944fa9e4066Sahrens 	return (0);
1945fa9e4066Sahrens }
1946fa9e4066Sahrens 
1947fa9e4066Sahrens /*
1948e9dbad6fSeschrock  * Create a new filesystem or volume.
1949fa9e4066Sahrens  */
1950fa9e4066Sahrens int
195199653d4eSeschrock zfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
1952e9dbad6fSeschrock     nvlist_t *props)
1953fa9e4066Sahrens {
1954fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
1955fa9e4066Sahrens 	int ret;
1956fa9e4066Sahrens 	uint64_t size = 0;
1957fa9e4066Sahrens 	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
195899653d4eSeschrock 	char errbuf[1024];
1959e9dbad6fSeschrock 	uint64_t zoned;
196099653d4eSeschrock 
196199653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
196299653d4eSeschrock 	    "cannot create '%s'"), path);
1963fa9e4066Sahrens 
1964fa9e4066Sahrens 	/* validate the path, taking care to note the extended error message */
196599653d4eSeschrock 	if (!zfs_validate_name(hdl, path, type))
196699653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
1967fa9e4066Sahrens 
1968fa9e4066Sahrens 	/* validate parents exist */
1969e9dbad6fSeschrock 	if (check_parents(hdl, path, &zoned) != 0)
1970fa9e4066Sahrens 		return (-1);
1971fa9e4066Sahrens 
1972fa9e4066Sahrens 	/*
1973fa9e4066Sahrens 	 * The failure modes when creating a dataset of a different type over
1974fa9e4066Sahrens 	 * one that already exists is a little strange.  In particular, if you
1975fa9e4066Sahrens 	 * try to create a dataset on top of an existing dataset, the ioctl()
1976fa9e4066Sahrens 	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
1977fa9e4066Sahrens 	 * first try to see if the dataset exists.
1978fa9e4066Sahrens 	 */
1979fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
198099653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
198199653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
198299653d4eSeschrock 		    "dataset already exists"));
198399653d4eSeschrock 		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
1984fa9e4066Sahrens 	}
1985fa9e4066Sahrens 
1986fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME)
1987fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
1988fa9e4066Sahrens 	else
1989fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
1990fa9e4066Sahrens 
1991e9dbad6fSeschrock 	if (props && (props = zfs_validate_properties(hdl, type, props, zoned,
1992e9dbad6fSeschrock 	    NULL, errbuf)) == 0)
1993e9dbad6fSeschrock 		return (-1);
1994e9dbad6fSeschrock 
1995fa9e4066Sahrens 	if (type == ZFS_TYPE_VOLUME) {
19965c5460e9Seschrock 		/*
19975c5460e9Seschrock 		 * If we are creating a volume, the size and block size must
19985c5460e9Seschrock 		 * satisfy a few restraints.  First, the blocksize must be a
19995c5460e9Seschrock 		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
20005c5460e9Seschrock 		 * volsize must be a multiple of the block size, and cannot be
20015c5460e9Seschrock 		 * zero.
20025c5460e9Seschrock 		 */
2003e9dbad6fSeschrock 		if (props == NULL || nvlist_lookup_uint64(props,
2004e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2005e9dbad6fSeschrock 			nvlist_free(props);
200699653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2007e9dbad6fSeschrock 			    "missing volume size"));
2008e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2009fa9e4066Sahrens 		}
2010fa9e4066Sahrens 
2011e9dbad6fSeschrock 		if ((ret = nvlist_lookup_uint64(props,
2012e9dbad6fSeschrock 		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2013e9dbad6fSeschrock 		    &blocksize)) != 0) {
2014e9dbad6fSeschrock 			if (ret == ENOENT) {
2015e9dbad6fSeschrock 				blocksize = zfs_prop_default_numeric(
2016e9dbad6fSeschrock 				    ZFS_PROP_VOLBLOCKSIZE);
2017e9dbad6fSeschrock 			} else {
2018e9dbad6fSeschrock 				nvlist_free(props);
201999653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2020e9dbad6fSeschrock 				    "missing volume block size"));
2021e9dbad6fSeschrock 				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2022e9dbad6fSeschrock 			}
2023e9dbad6fSeschrock 		}
2024e9dbad6fSeschrock 
2025e9dbad6fSeschrock 		if (size == 0) {
2026e9dbad6fSeschrock 			nvlist_free(props);
2027e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2028e9dbad6fSeschrock 			    "volume size cannot be zero"));
2029e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
20305c5460e9Seschrock 		}
20315c5460e9Seschrock 
20325c5460e9Seschrock 		if (size % blocksize != 0) {
2033e9dbad6fSeschrock 			nvlist_free(props);
203499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2035e9dbad6fSeschrock 			    "volume size must be a multiple of volume block "
2036e9dbad6fSeschrock 			    "size"));
2037e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2038e9dbad6fSeschrock 		}
20395c5460e9Seschrock 	}
20405c5460e9Seschrock 
2041e9dbad6fSeschrock 	if (props &&
2042e9dbad6fSeschrock 	    zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0)
2043e9dbad6fSeschrock 		return (-1);
2044e9dbad6fSeschrock 	nvlist_free(props);
2045fa9e4066Sahrens 
2046fa9e4066Sahrens 	/* create the dataset */
204799653d4eSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
2048fa9e4066Sahrens 
2049fa9e4066Sahrens 	if (ret == 0 && type == ZFS_TYPE_VOLUME)
205099653d4eSeschrock 		ret = zvol_create_link(hdl, path);
2051fa9e4066Sahrens 
2052e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2053e9dbad6fSeschrock 
2054fa9e4066Sahrens 	/* check for failure */
2055fa9e4066Sahrens 	if (ret != 0) {
2056fa9e4066Sahrens 		char parent[ZFS_MAXNAMELEN];
2057fa9e4066Sahrens 		(void) parent_name(path, parent, sizeof (parent));
2058fa9e4066Sahrens 
2059fa9e4066Sahrens 		switch (errno) {
2060fa9e4066Sahrens 		case ENOENT:
206199653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
206299653d4eSeschrock 			    "no such parent '%s'"), parent);
206399653d4eSeschrock 			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2064fa9e4066Sahrens 
2065fa9e4066Sahrens 		case EINVAL:
206699653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2067d7d4af51Smmusante 			    "parent '%s' is not a filesystem"), parent);
206899653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2069fa9e4066Sahrens 
2070fa9e4066Sahrens 		case EDOM:
207199653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2072e9dbad6fSeschrock 			    "volume block size must be power of 2 from "
2073e9dbad6fSeschrock 			    "%u to %uk"),
2074fa9e4066Sahrens 			    (uint_t)SPA_MINBLOCKSIZE,
2075fa9e4066Sahrens 			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
207699653d4eSeschrock 
2077e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
207899653d4eSeschrock 
2079fa9e4066Sahrens #ifdef _ILP32
2080fa9e4066Sahrens 		case EOVERFLOW:
2081fa9e4066Sahrens 			/*
2082fa9e4066Sahrens 			 * This platform can't address a volume this big.
2083fa9e4066Sahrens 			 */
208499653d4eSeschrock 			if (type == ZFS_TYPE_VOLUME)
208599653d4eSeschrock 				return (zfs_error(hdl, EZFS_VOLTOOBIG,
208699653d4eSeschrock 				    errbuf));
2087fa9e4066Sahrens #endif
208899653d4eSeschrock 			/* FALLTHROUGH */
2089fa9e4066Sahrens 		default:
209099653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2091fa9e4066Sahrens 		}
2092fa9e4066Sahrens 	}
2093fa9e4066Sahrens 
2094fa9e4066Sahrens 	return (0);
2095fa9e4066Sahrens }
2096fa9e4066Sahrens 
2097fa9e4066Sahrens /*
2098fa9e4066Sahrens  * Destroys the given dataset.  The caller must make sure that the filesystem
2099fa9e4066Sahrens  * isn't mounted, and that there are no active dependents.
2100fa9e4066Sahrens  */
2101fa9e4066Sahrens int
2102fa9e4066Sahrens zfs_destroy(zfs_handle_t *zhp)
2103fa9e4066Sahrens {
2104fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2105fa9e4066Sahrens 
2106fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2107fa9e4066Sahrens 
2108e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2109f3861e1aSahl 		/*
2110f3861e1aSahl 		 * Unconditionally unshare this zvol ignoring failure as it
2111f3861e1aSahl 		 * indicates only that the volume wasn't shared initially.
2112f3861e1aSahl 		 */
2113f3861e1aSahl 		(void) zfs_unshare_iscsi(zhp);
2114f3861e1aSahl 
211599653d4eSeschrock 		if (zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2116fa9e4066Sahrens 			return (-1);
2117fa9e4066Sahrens 
2118fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
2119fa9e4066Sahrens 	} else {
2120fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
2121fa9e4066Sahrens 	}
2122fa9e4066Sahrens 
2123f3861e1aSahl 	if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY, &zc) != 0) {
2124ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
212599653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
212699653d4eSeschrock 		    zhp->zfs_name));
21271d452cf5Sahrens 	}
2128fa9e4066Sahrens 
2129fa9e4066Sahrens 	remove_mountpoint(zhp);
2130fa9e4066Sahrens 
2131fa9e4066Sahrens 	return (0);
2132fa9e4066Sahrens }
2133fa9e4066Sahrens 
21341d452cf5Sahrens struct destroydata {
21351d452cf5Sahrens 	char *snapname;
21361d452cf5Sahrens 	boolean_t gotone;
21373ccfa83cSahrens 	boolean_t closezhp;
21381d452cf5Sahrens };
21391d452cf5Sahrens 
21401d452cf5Sahrens static int
21411d452cf5Sahrens zfs_remove_link_cb(zfs_handle_t *zhp, void *arg)
21421d452cf5Sahrens {
21431d452cf5Sahrens 	struct destroydata *dd = arg;
21441d452cf5Sahrens 	zfs_handle_t *szhp;
21451d452cf5Sahrens 	char name[ZFS_MAXNAMELEN];
21463ccfa83cSahrens 	boolean_t closezhp = dd->closezhp;
21473ccfa83cSahrens 	int rv;
21481d452cf5Sahrens 
2149e9dbad6fSeschrock 	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2150e9dbad6fSeschrock 	(void) strlcat(name, "@", sizeof (name));
2151e9dbad6fSeschrock 	(void) strlcat(name, dd->snapname, sizeof (name));
21521d452cf5Sahrens 
21531d452cf5Sahrens 	szhp = make_dataset_handle(zhp->zfs_hdl, name);
21541d452cf5Sahrens 	if (szhp) {
21551d452cf5Sahrens 		dd->gotone = B_TRUE;
21561d452cf5Sahrens 		zfs_close(szhp);
21571d452cf5Sahrens 	}
21581d452cf5Sahrens 
21591d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
21601d452cf5Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, name);
21611d452cf5Sahrens 		/*
21621d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
21631d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
21641d452cf5Sahrens 		 * the volumes.
21651d452cf5Sahrens 		 */
21661d452cf5Sahrens 	}
21671d452cf5Sahrens 
21683ccfa83cSahrens 	dd->closezhp = B_TRUE;
21693ccfa83cSahrens 	rv = zfs_iter_filesystems(zhp, zfs_remove_link_cb, arg);
21703ccfa83cSahrens 	if (closezhp)
21713ccfa83cSahrens 		zfs_close(zhp);
21723ccfa83cSahrens 	return (rv);
21731d452cf5Sahrens }
21741d452cf5Sahrens 
21751d452cf5Sahrens /*
21761d452cf5Sahrens  * Destroys all snapshots with the given name in zhp & descendants.
21771d452cf5Sahrens  */
21781d452cf5Sahrens int
21791d452cf5Sahrens zfs_destroy_snaps(zfs_handle_t *zhp, char *snapname)
21801d452cf5Sahrens {
21811d452cf5Sahrens 	zfs_cmd_t zc = { 0 };
21821d452cf5Sahrens 	int ret;
21831d452cf5Sahrens 	struct destroydata dd = { 0 };
21841d452cf5Sahrens 
21851d452cf5Sahrens 	dd.snapname = snapname;
21861d452cf5Sahrens 	(void) zfs_remove_link_cb(zhp, &dd);
21871d452cf5Sahrens 
21881d452cf5Sahrens 	if (!dd.gotone) {
2189ece3d9b3Slling 		return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
21901d452cf5Sahrens 		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
21911d452cf5Sahrens 		    zhp->zfs_name, snapname));
21921d452cf5Sahrens 	}
21931d452cf5Sahrens 
21941d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2195e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
21961d452cf5Sahrens 
21971d452cf5Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY_SNAPS, &zc);
21981d452cf5Sahrens 	if (ret != 0) {
21991d452cf5Sahrens 		char errbuf[1024];
22001d452cf5Sahrens 
22011d452cf5Sahrens 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
22021d452cf5Sahrens 		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
22031d452cf5Sahrens 
22041d452cf5Sahrens 		switch (errno) {
22051d452cf5Sahrens 		case EEXIST:
22061d452cf5Sahrens 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
22071d452cf5Sahrens 			    "snapshot is cloned"));
22081d452cf5Sahrens 			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
22091d452cf5Sahrens 
22101d452cf5Sahrens 		default:
22111d452cf5Sahrens 			return (zfs_standard_error(zhp->zfs_hdl, errno,
22121d452cf5Sahrens 			    errbuf));
22131d452cf5Sahrens 		}
22141d452cf5Sahrens 	}
22151d452cf5Sahrens 
22161d452cf5Sahrens 	return (0);
22171d452cf5Sahrens }
22181d452cf5Sahrens 
2219fa9e4066Sahrens /*
2220fa9e4066Sahrens  * Clones the given dataset.  The target must be of the same type as the source.
2221fa9e4066Sahrens  */
2222fa9e4066Sahrens int
2223e9dbad6fSeschrock zfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
2224fa9e4066Sahrens {
2225fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2226fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
2227fa9e4066Sahrens 	int ret;
222899653d4eSeschrock 	char errbuf[1024];
222999653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
2230e9dbad6fSeschrock 	zfs_type_t type;
2231e9dbad6fSeschrock 	uint64_t zoned;
2232fa9e4066Sahrens 
2233fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
2234fa9e4066Sahrens 
223599653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
223699653d4eSeschrock 	    "cannot create '%s'"), target);
223799653d4eSeschrock 
2238fa9e4066Sahrens 	/* validate the target name */
223999653d4eSeschrock 	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM))
224099653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2241fa9e4066Sahrens 
2242fa9e4066Sahrens 	/* validate parents exist */
2243e9dbad6fSeschrock 	if (check_parents(hdl, target, &zoned) != 0)
2244fa9e4066Sahrens 		return (-1);
2245fa9e4066Sahrens 
2246fa9e4066Sahrens 	(void) parent_name(target, parent, sizeof (parent));
2247fa9e4066Sahrens 
2248fa9e4066Sahrens 	/* do the clone */
2249e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp)) {
2250fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
22515f8e1617Snn35248 		type = ZFS_TYPE_VOLUME;
2252e9dbad6fSeschrock 	} else {
2253fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
22545f8e1617Snn35248 		type = ZFS_TYPE_FILESYSTEM;
2255e9dbad6fSeschrock 	}
2256e9dbad6fSeschrock 
2257e9dbad6fSeschrock 	if (props) {
2258e9dbad6fSeschrock 		if ((props = zfs_validate_properties(hdl, type, props, zoned,
2259e9dbad6fSeschrock 		    zhp, errbuf)) == NULL)
2260e9dbad6fSeschrock 			return (-1);
2261e9dbad6fSeschrock 
2262e9dbad6fSeschrock 		if (zcmd_write_src_nvlist(hdl, &zc, props, NULL) != 0) {
2263e9dbad6fSeschrock 			nvlist_free(props);
2264e9dbad6fSeschrock 			return (-1);
2265e9dbad6fSeschrock 		}
2266e9dbad6fSeschrock 
2267e9dbad6fSeschrock 		nvlist_free(props);
2268e9dbad6fSeschrock 	}
2269fa9e4066Sahrens 
2270fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
2271e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
227299653d4eSeschrock 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_CREATE, &zc);
2273fa9e4066Sahrens 
2274e9dbad6fSeschrock 	zcmd_free_nvlists(&zc);
2275e9dbad6fSeschrock 
2276fa9e4066Sahrens 	if (ret != 0) {
2277fa9e4066Sahrens 		switch (errno) {
2278fa9e4066Sahrens 
2279fa9e4066Sahrens 		case ENOENT:
2280fa9e4066Sahrens 			/*
2281fa9e4066Sahrens 			 * The parent doesn't exist.  We should have caught this
2282fa9e4066Sahrens 			 * above, but there may a race condition that has since
2283fa9e4066Sahrens 			 * destroyed the parent.
2284fa9e4066Sahrens 			 *
2285fa9e4066Sahrens 			 * At this point, we don't know whether it's the source
2286fa9e4066Sahrens 			 * that doesn't exist anymore, or whether the target
2287fa9e4066Sahrens 			 * dataset doesn't exist.
2288fa9e4066Sahrens 			 */
228999653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
229099653d4eSeschrock 			    "no such parent '%s'"), parent);
229199653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
2292fa9e4066Sahrens 
229399653d4eSeschrock 		case EXDEV:
229499653d4eSeschrock 			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
229599653d4eSeschrock 			    "source and target pools differ"));
229699653d4eSeschrock 			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
229799653d4eSeschrock 			    errbuf));
229899653d4eSeschrock 
229999653d4eSeschrock 		default:
230099653d4eSeschrock 			return (zfs_standard_error(zhp->zfs_hdl, errno,
230199653d4eSeschrock 			    errbuf));
230299653d4eSeschrock 		}
2303e9dbad6fSeschrock 	} else if (ZFS_IS_VOLUME(zhp)) {
230499653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, target);
230599653d4eSeschrock 	}
230699653d4eSeschrock 
230799653d4eSeschrock 	return (ret);
230899653d4eSeschrock }
230999653d4eSeschrock 
231099653d4eSeschrock typedef struct promote_data {
231199653d4eSeschrock 	char cb_mountpoint[MAXPATHLEN];
231299653d4eSeschrock 	const char *cb_target;
231399653d4eSeschrock 	const char *cb_errbuf;
231499653d4eSeschrock 	uint64_t cb_pivot_txg;
231599653d4eSeschrock } promote_data_t;
231699653d4eSeschrock 
231799653d4eSeschrock static int
231899653d4eSeschrock promote_snap_cb(zfs_handle_t *zhp, void *data)
231999653d4eSeschrock {
232099653d4eSeschrock 	promote_data_t *pd = data;
232199653d4eSeschrock 	zfs_handle_t *szhp;
232299653d4eSeschrock 	char snapname[MAXPATHLEN];
23233ccfa83cSahrens 	int rv = 0;
232499653d4eSeschrock 
232599653d4eSeschrock 	/* We don't care about snapshots after the pivot point */
23263ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) > pd->cb_pivot_txg) {
23273ccfa83cSahrens 		zfs_close(zhp);
232899653d4eSeschrock 		return (0);
23293ccfa83cSahrens 	}
233099653d4eSeschrock 
23310b69c2f0Sahrens 	/* Remove the device link if it's a zvol. */
2332e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
23330b69c2f0Sahrens 		(void) zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name);
233499653d4eSeschrock 
233599653d4eSeschrock 	/* Check for conflicting names */
2336e9dbad6fSeschrock 	(void) strlcpy(snapname, pd->cb_target, sizeof (snapname));
2337e9dbad6fSeschrock 	(void) strlcat(snapname, strchr(zhp->zfs_name, '@'), sizeof (snapname));
233899653d4eSeschrock 	szhp = make_dataset_handle(zhp->zfs_hdl, snapname);
233999653d4eSeschrock 	if (szhp != NULL) {
234099653d4eSeschrock 		zfs_close(szhp);
234199653d4eSeschrock 		zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
234299653d4eSeschrock 		    "snapshot name '%s' from origin \n"
234399653d4eSeschrock 		    "conflicts with '%s' from target"),
234499653d4eSeschrock 		    zhp->zfs_name, snapname);
23453ccfa83cSahrens 		rv = zfs_error(zhp->zfs_hdl, EZFS_EXISTS, pd->cb_errbuf);
234699653d4eSeschrock 	}
23473ccfa83cSahrens 	zfs_close(zhp);
23483ccfa83cSahrens 	return (rv);
234999653d4eSeschrock }
235099653d4eSeschrock 
23510b69c2f0Sahrens static int
23520b69c2f0Sahrens promote_snap_done_cb(zfs_handle_t *zhp, void *data)
23530b69c2f0Sahrens {
23540b69c2f0Sahrens 	promote_data_t *pd = data;
23550b69c2f0Sahrens 
23560b69c2f0Sahrens 	/* We don't care about snapshots after the pivot point */
23573ccfa83cSahrens 	if (zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) <= pd->cb_pivot_txg) {
23580b69c2f0Sahrens 		/* Create the device link if it's a zvol. */
2359e9dbad6fSeschrock 		if (ZFS_IS_VOLUME(zhp))
23600b69c2f0Sahrens 			(void) zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
23613ccfa83cSahrens 	}
23620b69c2f0Sahrens 
23633ccfa83cSahrens 	zfs_close(zhp);
23640b69c2f0Sahrens 	return (0);
23650b69c2f0Sahrens }
23660b69c2f0Sahrens 
236799653d4eSeschrock /*
236899653d4eSeschrock  * Promotes the given clone fs to be the clone parent.
236999653d4eSeschrock  */
237099653d4eSeschrock int
237199653d4eSeschrock zfs_promote(zfs_handle_t *zhp)
237299653d4eSeschrock {
237399653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
237499653d4eSeschrock 	zfs_cmd_t zc = { 0 };
237599653d4eSeschrock 	char parent[MAXPATHLEN];
237699653d4eSeschrock 	char *cp;
237799653d4eSeschrock 	int ret;
237899653d4eSeschrock 	zfs_handle_t *pzhp;
237999653d4eSeschrock 	promote_data_t pd;
238099653d4eSeschrock 	char errbuf[1024];
238199653d4eSeschrock 
238299653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
238399653d4eSeschrock 	    "cannot promote '%s'"), zhp->zfs_name);
238499653d4eSeschrock 
238599653d4eSeschrock 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
238699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
238799653d4eSeschrock 		    "snapshots can not be promoted"));
238899653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
238999653d4eSeschrock 	}
239099653d4eSeschrock 
2391e9dbad6fSeschrock 	(void) strlcpy(parent, zhp->zfs_dmustats.dds_clone_of, sizeof (parent));
239299653d4eSeschrock 	if (parent[0] == '\0') {
239399653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
239499653d4eSeschrock 		    "not a cloned filesystem"));
239599653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
239699653d4eSeschrock 	}
239799653d4eSeschrock 	cp = strchr(parent, '@');
239899653d4eSeschrock 	*cp = '\0';
239999653d4eSeschrock 
240099653d4eSeschrock 	/* Walk the snapshots we will be moving */
240199653d4eSeschrock 	pzhp = zfs_open(hdl, zhp->zfs_dmustats.dds_clone_of, ZFS_TYPE_SNAPSHOT);
240299653d4eSeschrock 	if (pzhp == NULL)
240399653d4eSeschrock 		return (-1);
240499653d4eSeschrock 	pd.cb_pivot_txg = zfs_prop_get_int(pzhp, ZFS_PROP_CREATETXG);
240599653d4eSeschrock 	zfs_close(pzhp);
240699653d4eSeschrock 	pd.cb_target = zhp->zfs_name;
240799653d4eSeschrock 	pd.cb_errbuf = errbuf;
240899653d4eSeschrock 	pzhp = zfs_open(hdl, parent, ZFS_TYPE_ANY);
240999653d4eSeschrock 	if (pzhp == NULL)
241099653d4eSeschrock 		return (-1);
241199653d4eSeschrock 	(void) zfs_prop_get(pzhp, ZFS_PROP_MOUNTPOINT, pd.cb_mountpoint,
241299653d4eSeschrock 	    sizeof (pd.cb_mountpoint), NULL, NULL, 0, FALSE);
241399653d4eSeschrock 	ret = zfs_iter_snapshots(pzhp, promote_snap_cb, &pd);
24140b69c2f0Sahrens 	if (ret != 0) {
24150b69c2f0Sahrens 		zfs_close(pzhp);
241699653d4eSeschrock 		return (-1);
24170b69c2f0Sahrens 	}
241899653d4eSeschrock 
241999653d4eSeschrock 	/* issue the ioctl */
2420e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_clone_of,
2421e9dbad6fSeschrock 	    sizeof (zc.zc_value));
242299653d4eSeschrock 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
242399653d4eSeschrock 	ret = ioctl(hdl->libzfs_fd, ZFS_IOC_PROMOTE, &zc);
242499653d4eSeschrock 
242599653d4eSeschrock 	if (ret != 0) {
24260b69c2f0Sahrens 		int save_errno = errno;
2427fa9e4066Sahrens 
24280b69c2f0Sahrens 		(void) zfs_iter_snapshots(pzhp, promote_snap_done_cb, &pd);
24290b69c2f0Sahrens 		zfs_close(pzhp);
24300b69c2f0Sahrens 
24310b69c2f0Sahrens 		switch (save_errno) {
2432fa9e4066Sahrens 		case EEXIST:
2433fa9e4066Sahrens 			/*
243499653d4eSeschrock 			 * There is a conflicting snapshot name.  We
243599653d4eSeschrock 			 * should have caught this above, but they could
243699653d4eSeschrock 			 * have renamed something in the mean time.
2437fa9e4066Sahrens 			 */
243899653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
243999653d4eSeschrock 			    "conflicting snapshot name from parent '%s'"),
244099653d4eSeschrock 			    parent);
244199653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2442fa9e4066Sahrens 
2443fa9e4066Sahrens 		default:
24440b69c2f0Sahrens 			return (zfs_standard_error(hdl, save_errno, errbuf));
2445fa9e4066Sahrens 		}
24460b69c2f0Sahrens 	} else {
24470b69c2f0Sahrens 		(void) zfs_iter_snapshots(zhp, promote_snap_done_cb, &pd);
2448fa9e4066Sahrens 	}
2449fa9e4066Sahrens 
24500b69c2f0Sahrens 	zfs_close(pzhp);
2451fa9e4066Sahrens 	return (ret);
2452fa9e4066Sahrens }
2453fa9e4066Sahrens 
24541d452cf5Sahrens static int
24551d452cf5Sahrens zfs_create_link_cb(zfs_handle_t *zhp, void *arg)
24561d452cf5Sahrens {
24571d452cf5Sahrens 	char *snapname = arg;
2458e9dbad6fSeschrock 	int ret;
24591d452cf5Sahrens 
24601d452cf5Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
24611d452cf5Sahrens 		char name[MAXPATHLEN];
24621d452cf5Sahrens 
2463e9dbad6fSeschrock 		(void) strlcpy(name, zhp->zfs_name, sizeof (name));
2464e9dbad6fSeschrock 		(void) strlcat(name, "@", sizeof (name));
2465e9dbad6fSeschrock 		(void) strlcat(name, snapname, sizeof (name));
24661d452cf5Sahrens 		(void) zvol_create_link(zhp->zfs_hdl, name);
24671d452cf5Sahrens 		/*
24681d452cf5Sahrens 		 * NB: this is simply a best-effort.  We don't want to
24691d452cf5Sahrens 		 * return an error, because then we wouldn't visit all
24701d452cf5Sahrens 		 * the volumes.
24711d452cf5Sahrens 		 */
24721d452cf5Sahrens 	}
2473e9dbad6fSeschrock 
2474e9dbad6fSeschrock 	ret = zfs_iter_filesystems(zhp, zfs_create_link_cb, snapname);
2475e9dbad6fSeschrock 
2476e9dbad6fSeschrock 	zfs_close(zhp);
2477e9dbad6fSeschrock 
2478e9dbad6fSeschrock 	return (ret);
24791d452cf5Sahrens }
24801d452cf5Sahrens 
2481fa9e4066Sahrens /*
2482*72bdce51Sahl  * Takes a snapshot of the given dataset.
2483fa9e4066Sahrens  */
2484fa9e4066Sahrens int
24851d452cf5Sahrens zfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive)
2486fa9e4066Sahrens {
2487fa9e4066Sahrens 	const char *delim;
2488fa9e4066Sahrens 	char *parent;
2489fa9e4066Sahrens 	zfs_handle_t *zhp;
2490fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2491fa9e4066Sahrens 	int ret;
249299653d4eSeschrock 	char errbuf[1024];
2493fa9e4066Sahrens 
249499653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
249599653d4eSeschrock 	    "cannot snapshot '%s'"), path);
249699653d4eSeschrock 
249799653d4eSeschrock 	/* validate the target name */
249899653d4eSeschrock 	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT))
249999653d4eSeschrock 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2500fa9e4066Sahrens 
2501fa9e4066Sahrens 	/* make sure the parent exists and is of the appropriate type */
25021d452cf5Sahrens 	delim = strchr(path, '@');
250399653d4eSeschrock 	if ((parent = zfs_alloc(hdl, delim - path + 1)) == NULL)
250499653d4eSeschrock 		return (-1);
2505fa9e4066Sahrens 	(void) strncpy(parent, path, delim - path);
2506fa9e4066Sahrens 	parent[delim - path] = '\0';
2507fa9e4066Sahrens 
250899653d4eSeschrock 	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
2509fa9e4066Sahrens 	    ZFS_TYPE_VOLUME)) == NULL) {
2510fa9e4066Sahrens 		free(parent);
2511fa9e4066Sahrens 		return (-1);
2512fa9e4066Sahrens 	}
2513fa9e4066Sahrens 
25141d452cf5Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2515e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
25161d452cf5Sahrens 	zc.zc_cookie = recursive;
25171d452cf5Sahrens 	ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SNAPSHOT, &zc);
2518fa9e4066Sahrens 
25191d452cf5Sahrens 	/*
25201d452cf5Sahrens 	 * if it was recursive, the one that actually failed will be in
25211d452cf5Sahrens 	 * zc.zc_name.
25221d452cf5Sahrens 	 */
25231d452cf5Sahrens 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2524e9dbad6fSeschrock 	    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
25251d452cf5Sahrens 	if (ret == 0 && recursive) {
25261d452cf5Sahrens 		(void) zfs_iter_filesystems(zhp,
25271d452cf5Sahrens 		    zfs_create_link_cb, (char *)delim+1);
25281d452cf5Sahrens 	}
2529fa9e4066Sahrens 	if (ret == 0 && zhp->zfs_type == ZFS_TYPE_VOLUME) {
253099653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, path);
25311d452cf5Sahrens 		if (ret != 0) {
253299653d4eSeschrock 			(void) ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_DESTROY,
253399653d4eSeschrock 			    &zc);
2534fa9e4066Sahrens 		}
25351d452cf5Sahrens 	}
2536fa9e4066Sahrens 
253799653d4eSeschrock 	if (ret != 0)
253899653d4eSeschrock 		(void) zfs_standard_error(hdl, errno, errbuf);
2539fa9e4066Sahrens 
2540fa9e4066Sahrens 	free(parent);
2541fa9e4066Sahrens 	zfs_close(zhp);
2542fa9e4066Sahrens 
2543fa9e4066Sahrens 	return (ret);
2544fa9e4066Sahrens }
2545fa9e4066Sahrens 
2546fa9e4066Sahrens /*
2547*72bdce51Sahl  * Dumps a backup of the given snapshot (incremental from fromsnap if it's not
2548*72bdce51Sahl  * NULL) to the file descriptor specified by outfd.
2549fa9e4066Sahrens  */
2550fa9e4066Sahrens int
2551*72bdce51Sahl zfs_send(zfs_handle_t *zhp, const char *fromsnap, int outfd)
2552fa9e4066Sahrens {
2553fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
255499653d4eSeschrock 	char errbuf[1024];
2555a2eea2e1Sahrens 	libzfs_handle_t *hdl = zhp->zfs_hdl;
255699653d4eSeschrock 
2557*72bdce51Sahl 	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
2558fa9e4066Sahrens 
2559a2eea2e1Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2560a2eea2e1Sahrens 	if (fromsnap)
2561a2eea2e1Sahrens 		(void) strlcpy(zc.zc_value, fromsnap, sizeof (zc.zc_name));
2562*72bdce51Sahl 	zc.zc_cookie = outfd;
2563fa9e4066Sahrens 
2564*72bdce51Sahl 	if (ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_SENDBACKUP, &zc) != 0) {
2565*72bdce51Sahl 		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2566*72bdce51Sahl 		    "cannot send '%s'"), zhp->zfs_name);
2567*72bdce51Sahl 
2568fa9e4066Sahrens 		switch (errno) {
2569fa9e4066Sahrens 
2570fa9e4066Sahrens 		case EXDEV:
257199653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2572d7d4af51Smmusante 			    "not an earlier snapshot from the same fs"));
257399653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
2574fa9e4066Sahrens 
2575fa9e4066Sahrens 		case EDQUOT:
2576fa9e4066Sahrens 		case EFBIG:
2577fa9e4066Sahrens 		case EIO:
2578fa9e4066Sahrens 		case ENOLINK:
2579fa9e4066Sahrens 		case ENOSPC:
2580fa9e4066Sahrens 		case ENOSTR:
2581fa9e4066Sahrens 		case ENXIO:
2582fa9e4066Sahrens 		case EPIPE:
2583fa9e4066Sahrens 		case ERANGE:
2584fa9e4066Sahrens 		case EFAULT:
2585fa9e4066Sahrens 		case EROFS:
258699653d4eSeschrock 			zfs_error_aux(hdl, strerror(errno));
258799653d4eSeschrock 			return (zfs_error(hdl, EZFS_BADBACKUP, errbuf));
2588fa9e4066Sahrens 
2589fa9e4066Sahrens 		default:
259099653d4eSeschrock 			return (zfs_standard_error(hdl, errno, errbuf));
2591fa9e4066Sahrens 		}
2592fa9e4066Sahrens 	}
2593fa9e4066Sahrens 
2594*72bdce51Sahl 	return (0);
2595fa9e4066Sahrens }
2596fa9e4066Sahrens 
2597fa9e4066Sahrens /*
2598a2eea2e1Sahrens  * Create ancestors of 'target', but not target itself, and not
2599a2eea2e1Sahrens  * ancestors whose names are shorter than prefixlen.  Die if
2600a2eea2e1Sahrens  * prefixlen-ancestor does not exist.
2601a2eea2e1Sahrens  */
2602a2eea2e1Sahrens static int
2603a2eea2e1Sahrens create_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
2604a2eea2e1Sahrens {
2605a2eea2e1Sahrens 	zfs_handle_t *h;
2606a2eea2e1Sahrens 	char *cp;
2607a2eea2e1Sahrens 
2608a2eea2e1Sahrens 	/* make sure prefix exists */
2609a2eea2e1Sahrens 	cp = strchr(target + prefixlen, '/');
2610a2eea2e1Sahrens 	*cp = '\0';
2611a2eea2e1Sahrens 	h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2612a2eea2e1Sahrens 	*cp = '/';
2613a2eea2e1Sahrens 	if (h == NULL)
2614a2eea2e1Sahrens 		return (-1);
2615a2eea2e1Sahrens 	zfs_close(h);
2616a2eea2e1Sahrens 
2617a2eea2e1Sahrens 	/*
2618a2eea2e1Sahrens 	 * Attempt to create, mount, and share any ancestor filesystems,
2619a2eea2e1Sahrens 	 * up to the prefixlen-long one.
2620a2eea2e1Sahrens 	 */
2621a2eea2e1Sahrens 	for (cp = target + prefixlen + 1;
2622a2eea2e1Sahrens 	    cp = strchr(cp, '/'); *cp = '/', cp++) {
2623a2eea2e1Sahrens 		const char *opname;
2624a2eea2e1Sahrens 
2625a2eea2e1Sahrens 		*cp = '\0';
2626a2eea2e1Sahrens 
2627a2eea2e1Sahrens 		h = make_dataset_handle(hdl, target);
2628a2eea2e1Sahrens 		if (h) {
2629a2eea2e1Sahrens 			/* it already exists, nothing to do here */
2630a2eea2e1Sahrens 			zfs_close(h);
2631a2eea2e1Sahrens 			continue;
2632a2eea2e1Sahrens 		}
2633a2eea2e1Sahrens 
2634a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "create");
2635a2eea2e1Sahrens 		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
2636a2eea2e1Sahrens 		    NULL) != 0)
2637a2eea2e1Sahrens 			goto ancestorerr;
2638a2eea2e1Sahrens 
2639a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "open");
2640a2eea2e1Sahrens 		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2641a2eea2e1Sahrens 		if (h == NULL)
2642a2eea2e1Sahrens 			goto ancestorerr;
2643a2eea2e1Sahrens 
2644a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "mount");
2645a2eea2e1Sahrens 		if (zfs_mount(h, NULL, 0) != 0)
2646a2eea2e1Sahrens 			goto ancestorerr;
2647a2eea2e1Sahrens 
2648a2eea2e1Sahrens 		opname = dgettext(TEXT_DOMAIN, "share");
2649a2eea2e1Sahrens 		if (zfs_share(h) != 0)
2650a2eea2e1Sahrens 			goto ancestorerr;
2651a2eea2e1Sahrens 
2652a2eea2e1Sahrens 		zfs_close(h);
2653a2eea2e1Sahrens 
2654a2eea2e1Sahrens 		continue;
2655a2eea2e1Sahrens ancestorerr:
2656a2eea2e1Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2657a2eea2e1Sahrens 		    "failed to %s ancestor '%s'"), opname, target);
2658a2eea2e1Sahrens 		return (-1);
2659a2eea2e1Sahrens 	}
2660a2eea2e1Sahrens 
2661a2eea2e1Sahrens 	return (0);
2662a2eea2e1Sahrens }
2663a2eea2e1Sahrens 
2664a2eea2e1Sahrens /*
2665*72bdce51Sahl  * Restores a backup of tosnap from the file descriptor specified by infd.
2666fa9e4066Sahrens  */
2667fa9e4066Sahrens int
266899653d4eSeschrock zfs_receive(libzfs_handle_t *hdl, const char *tosnap, int isprefix,
2669*72bdce51Sahl     int verbose, int dryrun, boolean_t force, int infd)
2670fa9e4066Sahrens {
2671fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2672fa9e4066Sahrens 	time_t begin_time;
2673a2eea2e1Sahrens 	int ioctl_err, err, bytes, size, choplen;
2674fa9e4066Sahrens 	char *cp;
2675fa9e4066Sahrens 	dmu_replay_record_t drr;
2676fa9e4066Sahrens 	struct drr_begin *drrb = &zc.zc_begin_record;
267799653d4eSeschrock 	char errbuf[1024];
267898579b20Snd150628 	prop_changelist_t *clp;
2679a2eea2e1Sahrens 	char chopprefix[ZFS_MAXNAMELEN];
2680fa9e4066Sahrens 
2681fa9e4066Sahrens 	begin_time = time(NULL);
2682fa9e4066Sahrens 
268399653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
268499653d4eSeschrock 	    "cannot receive"));
268599653d4eSeschrock 
2686fa9e4066Sahrens 	/* read in the BEGIN record */
2687fa9e4066Sahrens 	cp = (char *)&drr;
2688fa9e4066Sahrens 	bytes = 0;
2689fa9e4066Sahrens 	do {
2690*72bdce51Sahl 		size = read(infd, cp, sizeof (drr) - bytes);
26919b4f025eSahrens 		cp += size;
26929b4f025eSahrens 		bytes += size;
26939b4f025eSahrens 	} while (size > 0);
2694fa9e4066Sahrens 
26959b4f025eSahrens 	if (size < 0 || bytes != sizeof (drr)) {
269699653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
269799653d4eSeschrock 		    "stream (failed to read first record)"));
269899653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2699fa9e4066Sahrens 	}
2700fa9e4066Sahrens 
2701fa9e4066Sahrens 	zc.zc_begin_record = drr.drr_u.drr_begin;
2702fa9e4066Sahrens 
2703fa9e4066Sahrens 	if (drrb->drr_magic != DMU_BACKUP_MAGIC &&
2704fa9e4066Sahrens 	    drrb->drr_magic != BSWAP_64(DMU_BACKUP_MAGIC)) {
270599653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
270699653d4eSeschrock 		    "stream (bad magic number)"));
270799653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2708fa9e4066Sahrens 	}
2709fa9e4066Sahrens 
2710fa9e4066Sahrens 	if (drrb->drr_version != DMU_BACKUP_VERSION &&
2711fa9e4066Sahrens 	    drrb->drr_version != BSWAP_64(DMU_BACKUP_VERSION)) {
271299653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "only version "
271399653d4eSeschrock 		    "0x%llx is supported (stream is version 0x%llx)"),
2714fa9e4066Sahrens 		    DMU_BACKUP_VERSION, drrb->drr_version);
271599653d4eSeschrock 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2716fa9e4066Sahrens 	}
2717fa9e4066Sahrens 
2718a2eea2e1Sahrens 	if (strchr(drr.drr_u.drr_begin.drr_toname, '@') == NULL) {
2719a2eea2e1Sahrens 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
2720a2eea2e1Sahrens 			    "stream (bad snapshot name)"));
2721a2eea2e1Sahrens 		return (zfs_error(hdl, EZFS_BADSTREAM, errbuf));
2722fa9e4066Sahrens 	}
2723a2eea2e1Sahrens 	/*
2724a2eea2e1Sahrens 	 * Determine how much of the snapshot name stored in the stream
2725a2eea2e1Sahrens 	 * we are going to tack on to the name they specified on the
2726a2eea2e1Sahrens 	 * command line, and how much we are going to chop off.
2727a2eea2e1Sahrens 	 *
2728a2eea2e1Sahrens 	 * If they specified a snapshot, chop the entire name stored in
2729a2eea2e1Sahrens 	 * the stream.
2730a2eea2e1Sahrens 	 */
2731a2eea2e1Sahrens 	(void) strcpy(chopprefix, drr.drr_u.drr_begin.drr_toname);
2732a2eea2e1Sahrens 	if (isprefix) {
2733a2eea2e1Sahrens 		/*
2734a2eea2e1Sahrens 		 * They specified a fs with -d, we want to tack on
2735a2eea2e1Sahrens 		 * everything but the pool name stored in the stream
2736a2eea2e1Sahrens 		 */
2737a2eea2e1Sahrens 		if (strchr(tosnap, '@')) {
2738a2eea2e1Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "invalid "
2739a2eea2e1Sahrens 			    "argument - snapshot not allowed with -d"));
2740a2eea2e1Sahrens 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2741a2eea2e1Sahrens 		}
2742a2eea2e1Sahrens 		cp = strchr(chopprefix, '/');
2743fa9e4066Sahrens 		if (cp == NULL)
2744a2eea2e1Sahrens 			cp = strchr(chopprefix, '@');
2745a2eea2e1Sahrens 		*cp = '\0';
2746fa9e4066Sahrens 	} else if (strchr(tosnap, '@') == NULL) {
2747fa9e4066Sahrens 		/*
2748a2eea2e1Sahrens 		 * If they specified a filesystem without -d, we want to
2749a2eea2e1Sahrens 		 * tack on everything after the fs specified in the
2750a2eea2e1Sahrens 		 * first name from the stream.
2751fa9e4066Sahrens 		 */
2752a2eea2e1Sahrens 		cp = strchr(chopprefix, '@');
2753fa9e4066Sahrens 		*cp = '\0';
2754a2eea2e1Sahrens 	}
2755a2eea2e1Sahrens 	choplen = strlen(chopprefix);
2756a2eea2e1Sahrens 
2757a2eea2e1Sahrens 	/*
2758a2eea2e1Sahrens 	 * Determine name of destination snapshot, store in zc_value.
2759a2eea2e1Sahrens 	 */
2760a2eea2e1Sahrens 	(void) strcpy(zc.zc_value, tosnap);
2761a2eea2e1Sahrens 	(void) strncat(zc.zc_value, drr.drr_u.drr_begin.drr_toname+choplen,
2762a2eea2e1Sahrens 	    sizeof (zc.zc_value));
27633ccfa83cSahrens 	if (!zfs_validate_name(hdl, zc.zc_value, ZFS_TYPE_SNAPSHOT))
27643ccfa83cSahrens 		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2765a2eea2e1Sahrens 
2766a2eea2e1Sahrens 	(void) strcpy(zc.zc_name, zc.zc_value);
2767a2eea2e1Sahrens 	if (drrb->drr_fromguid) {
2768a2eea2e1Sahrens 		/* incremental backup stream */
2769a2eea2e1Sahrens 		zfs_handle_t *h;
2770a2eea2e1Sahrens 
2771a2eea2e1Sahrens 		/* do the recvbackup ioctl to the containing fs */
2772a2eea2e1Sahrens 		*strchr(zc.zc_name, '@') = '\0';
2773fa9e4066Sahrens 
2774fa9e4066Sahrens 		/* make sure destination fs exists */
277599653d4eSeschrock 		h = zfs_open(hdl, zc.zc_name,
277699653d4eSeschrock 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
277799653d4eSeschrock 		if (h == NULL)
2778fa9e4066Sahrens 			return (-1);
27799b4f025eSahrens 		if (!dryrun) {
278098579b20Snd150628 			/*
278198579b20Snd150628 			 * We need to unmount all the dependents of the dataset
278298579b20Snd150628 			 * and the dataset itself. If it's a volume
278398579b20Snd150628 			 * then remove device link.
278498579b20Snd150628 			 */
27859b4f025eSahrens 			if (h->zfs_type == ZFS_TYPE_FILESYSTEM) {
278698579b20Snd150628 				clp = changelist_gather(h, ZFS_PROP_NAME, 0);
278798579b20Snd150628 				if (clp == NULL)
278898579b20Snd150628 					return (-1);
278998579b20Snd150628 				if (changelist_prefix(clp) != 0) {
279098579b20Snd150628 					changelist_free(clp);
279198579b20Snd150628 					return (-1);
279298579b20Snd150628 				}
27939b4f025eSahrens 			} else {
279499653d4eSeschrock 				(void) zvol_remove_link(hdl, h->zfs_name);
27959b4f025eSahrens 			}
27969b4f025eSahrens 		}
2797fa9e4066Sahrens 		zfs_close(h);
2798fa9e4066Sahrens 	} else {
2799fa9e4066Sahrens 		/* full backup stream */
2800fa9e4066Sahrens 
28019b4f025eSahrens 		/* Make sure destination fs does not exist */
2802a2eea2e1Sahrens 		*strchr(zc.zc_name, '@') = '\0';
280399653d4eSeschrock 		if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) == 0) {
280499653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
280599653d4eSeschrock 			    "destination '%s' exists"), zc.zc_name);
280699653d4eSeschrock 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
28079b4f025eSahrens 		}
28089b4f025eSahrens 
2809a2eea2e1Sahrens 		if (strchr(zc.zc_name, '/') == NULL) {
2810a2eea2e1Sahrens 			/*
2811a2eea2e1Sahrens 			 * they're trying to do a recv into a
2812a2eea2e1Sahrens 			 * nonexistant topmost filesystem.
2813a2eea2e1Sahrens 			 */
2814a2eea2e1Sahrens 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2815a2eea2e1Sahrens 			    "destination does not exist"), zc.zc_name);
2816a2eea2e1Sahrens 			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2817a2eea2e1Sahrens 		}
2818a2eea2e1Sahrens 
28199b4f025eSahrens 		/* Do the recvbackup ioctl to the fs's parent. */
2820a2eea2e1Sahrens 		*strrchr(zc.zc_name, '/') = '\0';
2821a2eea2e1Sahrens 
2822a2eea2e1Sahrens 		if (isprefix && (err = create_parents(hdl,
2823a2eea2e1Sahrens 		    zc.zc_value, strlen(tosnap))) != 0) {
2824a2eea2e1Sahrens 			return (zfs_error(hdl, EZFS_BADRESTORE, errbuf));
2825a2eea2e1Sahrens 		}
2826a2eea2e1Sahrens 
2827fa9e4066Sahrens 	}
2828fa9e4066Sahrens 
2829*72bdce51Sahl 	zc.zc_cookie = infd;
2830e9dbad6fSeschrock 	zc.zc_guid = force;
2831fa9e4066Sahrens 	if (verbose) {
2832f2a3c691Sahrens 		(void) printf("%s %s stream of %s into %s\n",
2833f2a3c691Sahrens 		    dryrun ? "would receive" : "receiving",
2834fa9e4066Sahrens 		    drrb->drr_fromguid ? "incremental" : "full",
2835fa9e4066Sahrens 		    drr.drr_u.drr_begin.drr_toname,
2836e9dbad6fSeschrock 		    zc.zc_value);
2837fa9e4066Sahrens 		(void) fflush(stdout);
2838fa9e4066Sahrens 	}
2839fa9e4066Sahrens 	if (dryrun)
2840fa9e4066Sahrens 		return (0);
284199653d4eSeschrock 	err = ioctl_err = ioctl(hdl->libzfs_fd, ZFS_IOC_RECVBACKUP, &zc);
28429b4f025eSahrens 	if (ioctl_err != 0) {
2843fa9e4066Sahrens 		switch (errno) {
2844fa9e4066Sahrens 		case ENODEV:
284599653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
284699653d4eSeschrock 			    "most recent snapshot does not match incremental "
284799653d4eSeschrock 			    "source"));
284899653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
2849fa9e4066Sahrens 			break;
2850fa9e4066Sahrens 		case ETXTBSY:
285199653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
285299653d4eSeschrock 			    "destination has been modified since most recent "
285399653d4eSeschrock 			    "snapshot"));
285499653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADRESTORE, errbuf);
2855fa9e4066Sahrens 			break;
2856fa9e4066Sahrens 		case EEXIST:
2857fa9e4066Sahrens 			if (drrb->drr_fromguid == 0) {
2858fa9e4066Sahrens 				/* it's the containing fs that exists */
2859e9dbad6fSeschrock 				cp = strchr(zc.zc_value, '@');
2860fa9e4066Sahrens 				*cp = '\0';
2861fa9e4066Sahrens 			}
286299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
286399653d4eSeschrock 			    "destination already exists"));
2864ece3d9b3Slling 			(void) zfs_error_fmt(hdl, EZFS_EXISTS,
2865ece3d9b3Slling 			    dgettext(TEXT_DOMAIN, "cannot restore to %s"),
2866ece3d9b3Slling 			    zc.zc_value);
2867fa9e4066Sahrens 			break;
2868fa9e4066Sahrens 		case EINVAL:
286999653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
28709b4f025eSahrens 			break;
2871ea8dc4b6Seschrock 		case ECKSUM:
287299653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
287399653d4eSeschrock 			    "invalid stream (checksum mismatch)"));
287499653d4eSeschrock 			(void) zfs_error(hdl, EZFS_BADSTREAM, errbuf);
2875fa9e4066Sahrens 			break;
2876fa9e4066Sahrens 		default:
287799653d4eSeschrock 			(void) zfs_standard_error(hdl, errno, errbuf);
2878fa9e4066Sahrens 		}
2879fa9e4066Sahrens 	}
2880fa9e4066Sahrens 
2881fa9e4066Sahrens 	/*
28829b4f025eSahrens 	 * Mount or recreate the /dev links for the target filesystem
28839b4f025eSahrens 	 * (if created, or if we tore them down to do an incremental
28849b4f025eSahrens 	 * restore), and the /dev links for the new snapshot (if
288598579b20Snd150628 	 * created). Also mount any children of the target filesystem
288698579b20Snd150628 	 * if we did an incremental receive.
2887fa9e4066Sahrens 	 */
2888e9dbad6fSeschrock 	cp = strchr(zc.zc_value, '@');
28899b4f025eSahrens 	if (cp && (ioctl_err == 0 || drrb->drr_fromguid)) {
2890fa9e4066Sahrens 		zfs_handle_t *h;
2891fa9e4066Sahrens 
2892fa9e4066Sahrens 		*cp = '\0';
2893e9dbad6fSeschrock 		h = zfs_open(hdl, zc.zc_value,
2894fa9e4066Sahrens 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
2895fa9e4066Sahrens 		*cp = '@';
2896fa9e4066Sahrens 		if (h) {
289798579b20Snd150628 			if (h->zfs_type == ZFS_TYPE_VOLUME) {
289899653d4eSeschrock 				err = zvol_create_link(hdl, h->zfs_name);
2899ea8dc4b6Seschrock 				if (err == 0 && ioctl_err == 0)
290099653d4eSeschrock 					err = zvol_create_link(hdl,
2901e9dbad6fSeschrock 					    zc.zc_value);
290298579b20Snd150628 			} else {
290398579b20Snd150628 				if (drrb->drr_fromguid) {
290498579b20Snd150628 					err = changelist_postfix(clp);
290598579b20Snd150628 					changelist_free(clp);
290698579b20Snd150628 				} else {
290798579b20Snd150628 					err = zfs_mount(h, NULL, 0);
290898579b20Snd150628 				}
29099b4f025eSahrens 			}
2910fa9e4066Sahrens 		zfs_close(h);
2911fa9e4066Sahrens 		}
2912fa9e4066Sahrens 	}
2913fa9e4066Sahrens 
29149b4f025eSahrens 	if (err || ioctl_err)
29159b4f025eSahrens 		return (-1);
2916fa9e4066Sahrens 
2917fa9e4066Sahrens 	if (verbose) {
2918fa9e4066Sahrens 		char buf1[64];
2919fa9e4066Sahrens 		char buf2[64];
2920fa9e4066Sahrens 		uint64_t bytes = zc.zc_cookie;
2921fa9e4066Sahrens 		time_t delta = time(NULL) - begin_time;
2922fa9e4066Sahrens 		if (delta == 0)
2923fa9e4066Sahrens 			delta = 1;
2924fa9e4066Sahrens 		zfs_nicenum(bytes, buf1, sizeof (buf1));
2925fa9e4066Sahrens 		zfs_nicenum(bytes/delta, buf2, sizeof (buf1));
2926fa9e4066Sahrens 
2927f2a3c691Sahrens 		(void) printf("received %sb stream in %lu seconds (%sb/sec)\n",
2928fa9e4066Sahrens 		    buf1, delta, buf2);
2929fa9e4066Sahrens 	}
293098579b20Snd150628 
2931fa9e4066Sahrens 	return (0);
2932fa9e4066Sahrens }
2933fa9e4066Sahrens 
2934fa9e4066Sahrens /*
2935b12a1c38Slling  * Destroy any more recent snapshots.  We invoke this callback on any dependents
2936b12a1c38Slling  * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
2937b12a1c38Slling  * is a dependent and we should just destroy it without checking the transaction
2938b12a1c38Slling  * group.
2939fa9e4066Sahrens  */
2940b12a1c38Slling typedef struct rollback_data {
2941b12a1c38Slling 	const char	*cb_target;		/* the snapshot */
2942b12a1c38Slling 	uint64_t	cb_create;		/* creation time reference */
2943b12a1c38Slling 	prop_changelist_t *cb_clp;		/* changelist pointer */
2944b12a1c38Slling 	int		cb_error;
294599653d4eSeschrock 	boolean_t	cb_dependent;
2946b12a1c38Slling } rollback_data_t;
2947b12a1c38Slling 
2948b12a1c38Slling static int
2949b12a1c38Slling rollback_destroy(zfs_handle_t *zhp, void *data)
2950b12a1c38Slling {
2951b12a1c38Slling 	rollback_data_t *cbp = data;
2952b12a1c38Slling 
2953b12a1c38Slling 	if (!cbp->cb_dependent) {
2954b12a1c38Slling 		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
2955b12a1c38Slling 		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
2956b12a1c38Slling 		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
2957b12a1c38Slling 		    cbp->cb_create) {
2958b12a1c38Slling 
295999653d4eSeschrock 			cbp->cb_dependent = B_TRUE;
29603bb79becSeschrock 			if (zfs_iter_dependents(zhp, B_FALSE, rollback_destroy,
29613bb79becSeschrock 			    cbp) != 0)
29623bb79becSeschrock 				cbp->cb_error = 1;
296399653d4eSeschrock 			cbp->cb_dependent = B_FALSE;
2964b12a1c38Slling 
2965b12a1c38Slling 			if (zfs_destroy(zhp) != 0)
2966b12a1c38Slling 				cbp->cb_error = 1;
2967b12a1c38Slling 			else
2968b12a1c38Slling 				changelist_remove(zhp, cbp->cb_clp);
2969b12a1c38Slling 		}
2970b12a1c38Slling 	} else {
2971b12a1c38Slling 		if (zfs_destroy(zhp) != 0)
2972b12a1c38Slling 			cbp->cb_error = 1;
2973b12a1c38Slling 		else
2974b12a1c38Slling 			changelist_remove(zhp, cbp->cb_clp);
2975b12a1c38Slling 	}
2976b12a1c38Slling 
2977b12a1c38Slling 	zfs_close(zhp);
2978b12a1c38Slling 	return (0);
2979b12a1c38Slling }
2980b12a1c38Slling 
2981b12a1c38Slling /*
2982b12a1c38Slling  * Rollback the dataset to its latest snapshot.
2983b12a1c38Slling  */
2984b12a1c38Slling static int
2985b12a1c38Slling do_rollback(zfs_handle_t *zhp)
2986fa9e4066Sahrens {
2987fa9e4066Sahrens 	int ret;
2988fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
2989fa9e4066Sahrens 
2990fa9e4066Sahrens 	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
2991fa9e4066Sahrens 	    zhp->zfs_type == ZFS_TYPE_VOLUME);
2992fa9e4066Sahrens 
2993fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_VOLUME &&
299499653d4eSeschrock 	    zvol_remove_link(zhp->zfs_hdl, zhp->zfs_name) != 0)
2995fa9e4066Sahrens 		return (-1);
2996fa9e4066Sahrens 
2997fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2998fa9e4066Sahrens 
2999e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
3000fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
3001fa9e4066Sahrens 	else
3002fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3003fa9e4066Sahrens 
3004fa9e4066Sahrens 	/*
3005fa9e4066Sahrens 	 * We rely on the consumer to verify that there are no newer snapshots
3006fa9e4066Sahrens 	 * for the given dataset.  Given these constraints, we can simply pass
3007fa9e4066Sahrens 	 * the name on to the ioctl() call.  There is still an unlikely race
3008fa9e4066Sahrens 	 * condition where the user has taken a snapshot since we verified that
3009fa9e4066Sahrens 	 * this was the most recent.
3010fa9e4066Sahrens 	 */
301199653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_ROLLBACK,
301299653d4eSeschrock 	    &zc)) != 0) {
3013ece3d9b3Slling 		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
301499653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
301599653d4eSeschrock 		    zhp->zfs_name);
3016fa9e4066Sahrens 	} else if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
301799653d4eSeschrock 		ret = zvol_create_link(zhp->zfs_hdl, zhp->zfs_name);
3018fa9e4066Sahrens 	}
3019fa9e4066Sahrens 
3020fa9e4066Sahrens 	return (ret);
3021fa9e4066Sahrens }
3022fa9e4066Sahrens 
3023fa9e4066Sahrens /*
3024b12a1c38Slling  * Given a dataset, rollback to a specific snapshot, discarding any
3025b12a1c38Slling  * data changes since then and making it the active dataset.
3026b12a1c38Slling  *
3027b12a1c38Slling  * Any snapshots more recent than the target are destroyed, along with
3028b12a1c38Slling  * their dependents.
3029b12a1c38Slling  */
3030b12a1c38Slling int
3031b12a1c38Slling zfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, int flag)
3032b12a1c38Slling {
3033b12a1c38Slling 	int ret;
3034b12a1c38Slling 	rollback_data_t cb = { 0 };
3035b12a1c38Slling 	prop_changelist_t *clp;
3036b12a1c38Slling 
3037b12a1c38Slling 	/*
3038b12a1c38Slling 	 * Unmount all dependendents of the dataset and the dataset itself.
3039b12a1c38Slling 	 * The list we need to gather is the same as for doing rename
3040b12a1c38Slling 	 */
3041b12a1c38Slling 	clp = changelist_gather(zhp, ZFS_PROP_NAME, flag ? MS_FORCE: 0);
3042b12a1c38Slling 	if (clp == NULL)
3043b12a1c38Slling 		return (-1);
3044b12a1c38Slling 
3045b12a1c38Slling 	if ((ret = changelist_prefix(clp)) != 0)
3046b12a1c38Slling 		goto out;
3047b12a1c38Slling 
3048b12a1c38Slling 	/*
3049b12a1c38Slling 	 * Destroy all recent snapshots and its dependends.
3050b12a1c38Slling 	 */
3051b12a1c38Slling 	cb.cb_target = snap->zfs_name;
3052b12a1c38Slling 	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3053b12a1c38Slling 	cb.cb_clp = clp;
3054b12a1c38Slling 	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3055b12a1c38Slling 
3056b12a1c38Slling 	if ((ret = cb.cb_error) != 0) {
3057b12a1c38Slling 		(void) changelist_postfix(clp);
3058b12a1c38Slling 		goto out;
3059b12a1c38Slling 	}
3060b12a1c38Slling 
3061b12a1c38Slling 	/*
3062b12a1c38Slling 	 * Now that we have verified that the snapshot is the latest,
3063b12a1c38Slling 	 * rollback to the given snapshot.
3064b12a1c38Slling 	 */
3065b12a1c38Slling 	ret = do_rollback(zhp);
3066b12a1c38Slling 
3067b12a1c38Slling 	if (ret != 0) {
3068b12a1c38Slling 		(void) changelist_postfix(clp);
3069b12a1c38Slling 		goto out;
3070b12a1c38Slling 	}
3071b12a1c38Slling 
3072b12a1c38Slling 	/*
3073b12a1c38Slling 	 * We only want to re-mount the filesystem if it was mounted in the
3074b12a1c38Slling 	 * first place.
3075b12a1c38Slling 	 */
3076b12a1c38Slling 	ret = changelist_postfix(clp);
3077b12a1c38Slling 
3078b12a1c38Slling out:
3079b12a1c38Slling 	changelist_free(clp);
3080b12a1c38Slling 	return (ret);
3081b12a1c38Slling }
3082b12a1c38Slling 
3083b12a1c38Slling /*
3084fa9e4066Sahrens  * Iterate over all dependents for a given dataset.  This includes both
3085fa9e4066Sahrens  * hierarchical dependents (children) and data dependents (snapshots and
3086fa9e4066Sahrens  * clones).  The bulk of the processing occurs in get_dependents() in
3087fa9e4066Sahrens  * libzfs_graph.c.
3088fa9e4066Sahrens  */
3089fa9e4066Sahrens int
30903bb79becSeschrock zfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
30913bb79becSeschrock     zfs_iter_f func, void *data)
3092fa9e4066Sahrens {
3093fa9e4066Sahrens 	char **dependents;
3094fa9e4066Sahrens 	size_t count;
3095fa9e4066Sahrens 	int i;
3096fa9e4066Sahrens 	zfs_handle_t *child;
3097fa9e4066Sahrens 	int ret = 0;
3098fa9e4066Sahrens 
30993bb79becSeschrock 	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
31003bb79becSeschrock 	    &dependents, &count) != 0)
31013bb79becSeschrock 		return (-1);
31023bb79becSeschrock 
3103fa9e4066Sahrens 	for (i = 0; i < count; i++) {
310499653d4eSeschrock 		if ((child = make_dataset_handle(zhp->zfs_hdl,
310599653d4eSeschrock 		    dependents[i])) == NULL)
3106fa9e4066Sahrens 			continue;
3107fa9e4066Sahrens 
3108fa9e4066Sahrens 		if ((ret = func(child, data)) != 0)
3109fa9e4066Sahrens 			break;
3110fa9e4066Sahrens 	}
3111fa9e4066Sahrens 
3112fa9e4066Sahrens 	for (i = 0; i < count; i++)
3113fa9e4066Sahrens 		free(dependents[i]);
3114fa9e4066Sahrens 	free(dependents);
3115fa9e4066Sahrens 
3116fa9e4066Sahrens 	return (ret);
3117fa9e4066Sahrens }
3118fa9e4066Sahrens 
3119fa9e4066Sahrens /*
3120fa9e4066Sahrens  * Renames the given dataset.
3121fa9e4066Sahrens  */
3122fa9e4066Sahrens int
3123fa9e4066Sahrens zfs_rename(zfs_handle_t *zhp, const char *target)
3124fa9e4066Sahrens {
3125fa9e4066Sahrens 	int ret;
3126fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3127fa9e4066Sahrens 	char *delim;
3128fa9e4066Sahrens 	prop_changelist_t *cl;
3129fa9e4066Sahrens 	char parent[ZFS_MAXNAMELEN];
313099653d4eSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
313199653d4eSeschrock 	char errbuf[1024];
3132fa9e4066Sahrens 
3133fa9e4066Sahrens 	/* if we have the same exact name, just return success */
3134fa9e4066Sahrens 	if (strcmp(zhp->zfs_name, target) == 0)
3135fa9e4066Sahrens 		return (0);
3136fa9e4066Sahrens 
313799653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
313899653d4eSeschrock 	    "cannot rename to '%s'"), target);
313999653d4eSeschrock 
3140fa9e4066Sahrens 	/*
3141fa9e4066Sahrens 	 * Make sure the target name is valid
3142fa9e4066Sahrens 	 */
3143fa9e4066Sahrens 	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
314498579b20Snd150628 		if ((strchr(target, '@') == NULL) ||
314598579b20Snd150628 		    *target == '@') {
314698579b20Snd150628 			/*
314798579b20Snd150628 			 * Snapshot target name is abbreviated,
314898579b20Snd150628 			 * reconstruct full dataset name
314998579b20Snd150628 			 */
315098579b20Snd150628 			(void) strlcpy(parent, zhp->zfs_name,
315198579b20Snd150628 			    sizeof (parent));
315298579b20Snd150628 			delim = strchr(parent, '@');
315398579b20Snd150628 			if (strchr(target, '@') == NULL)
315498579b20Snd150628 				*(++delim) = '\0';
315598579b20Snd150628 			else
315698579b20Snd150628 				*delim = '\0';
315798579b20Snd150628 			(void) strlcat(parent, target, sizeof (parent));
315898579b20Snd150628 			target = parent;
315998579b20Snd150628 		} else {
3160fa9e4066Sahrens 			/*
3161fa9e4066Sahrens 			 * Make sure we're renaming within the same dataset.
3162fa9e4066Sahrens 			 */
316398579b20Snd150628 			delim = strchr(target, '@');
316498579b20Snd150628 			if (strncmp(zhp->zfs_name, target, delim - target)
316598579b20Snd150628 			    != 0 || zhp->zfs_name[delim - target] != '@') {
316699653d4eSeschrock 				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
316798579b20Snd150628 				    "snapshots must be part of same "
316898579b20Snd150628 				    "dataset"));
316998579b20Snd150628 				return (zfs_error(hdl, EZFS_CROSSTARGET,
317098579b20Snd150628 					    errbuf));
3171fa9e4066Sahrens 			}
317298579b20Snd150628 		}
317398579b20Snd150628 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
317498579b20Snd150628 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3175fa9e4066Sahrens 	} else {
317698579b20Snd150628 		if (!zfs_validate_name(hdl, target, zhp->zfs_type))
317798579b20Snd150628 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3178e9dbad6fSeschrock 		uint64_t unused;
3179e9dbad6fSeschrock 
3180fa9e4066Sahrens 		/* validate parents */
3181e9dbad6fSeschrock 		if (check_parents(hdl, target, &unused) != 0)
3182fa9e4066Sahrens 			return (-1);
3183fa9e4066Sahrens 
3184fa9e4066Sahrens 		(void) parent_name(target, parent, sizeof (parent));
3185fa9e4066Sahrens 
3186fa9e4066Sahrens 		/* make sure we're in the same pool */
3187fa9e4066Sahrens 		verify((delim = strchr(target, '/')) != NULL);
3188fa9e4066Sahrens 		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3189fa9e4066Sahrens 		    zhp->zfs_name[delim - target] != '/') {
319099653d4eSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
319199653d4eSeschrock 			    "datasets must be within same pool"));
319299653d4eSeschrock 			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3193fa9e4066Sahrens 		}
3194f2fdf992Snd150628 
3195f2fdf992Snd150628 		/* new name cannot be a child of the current dataset name */
3196f2fdf992Snd150628 		if (strncmp(parent, zhp->zfs_name,
3197f2fdf992Snd150628 			    strlen(zhp->zfs_name)) == 0) {
3198f2fdf992Snd150628 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3199f2fdf992Snd150628 			    "New dataset name cannot be a descendent of "
3200f2fdf992Snd150628 			    "current dataset name"));
3201f2fdf992Snd150628 			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3202f2fdf992Snd150628 		}
3203fa9e4066Sahrens 	}
3204fa9e4066Sahrens 
320599653d4eSeschrock 	(void) snprintf(errbuf, sizeof (errbuf),
320699653d4eSeschrock 	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
320799653d4eSeschrock 
3208fa9e4066Sahrens 	if (getzoneid() == GLOBAL_ZONEID &&
3209fa9e4066Sahrens 	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
321099653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
321199653d4eSeschrock 		    "dataset is used in a non-global zone"));
321299653d4eSeschrock 		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3213fa9e4066Sahrens 	}
3214fa9e4066Sahrens 
3215fa9e4066Sahrens 	if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0)) == NULL)
321699653d4eSeschrock 		return (-1);
3217fa9e4066Sahrens 
3218fa9e4066Sahrens 	if (changelist_haszonedchild(cl)) {
321999653d4eSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
322099653d4eSeschrock 		    "child dataset with inherited mountpoint is used "
322199653d4eSeschrock 		    "in a non-global zone"));
3222e9dbad6fSeschrock 		(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3223fa9e4066Sahrens 		goto error;
3224fa9e4066Sahrens 	}
3225fa9e4066Sahrens 
3226fa9e4066Sahrens 	if ((ret = changelist_prefix(cl)) != 0)
3227fa9e4066Sahrens 		goto error;
3228fa9e4066Sahrens 
3229e9dbad6fSeschrock 	if (ZFS_IS_VOLUME(zhp))
3230fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZVOL;
3231fa9e4066Sahrens 	else
3232fa9e4066Sahrens 		zc.zc_objset_type = DMU_OST_ZFS;
3233fa9e4066Sahrens 
323498579b20Snd150628 	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3235e9dbad6fSeschrock 	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
323698579b20Snd150628 
323799653d4eSeschrock 	if ((ret = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_RENAME, &zc)) != 0) {
323899653d4eSeschrock 		(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3239fa9e4066Sahrens 
3240fa9e4066Sahrens 		/*
3241fa9e4066Sahrens 		 * On failure, we still want to remount any filesystems that
3242fa9e4066Sahrens 		 * were previously mounted, so we don't alter the system state.
3243fa9e4066Sahrens 		 */
3244fa9e4066Sahrens 		(void) changelist_postfix(cl);
3245fa9e4066Sahrens 	} else {
3246fa9e4066Sahrens 		changelist_rename(cl, zfs_get_name(zhp), target);
3247fa9e4066Sahrens 
3248fa9e4066Sahrens 		ret = changelist_postfix(cl);
3249fa9e4066Sahrens 	}
3250fa9e4066Sahrens 
3251fa9e4066Sahrens error:
3252fa9e4066Sahrens 	changelist_free(cl);
3253fa9e4066Sahrens 	return (ret);
3254fa9e4066Sahrens }
3255fa9e4066Sahrens 
3256fa9e4066Sahrens /*
3257fa9e4066Sahrens  * Given a zvol dataset, issue the ioctl to create the appropriate minor node,
3258fa9e4066Sahrens  * poke devfsadm to create the /dev link, and then wait for the link to appear.
3259fa9e4066Sahrens  */
3260fa9e4066Sahrens int
326199653d4eSeschrock zvol_create_link(libzfs_handle_t *hdl, const char *dataset)
3262fa9e4066Sahrens {
3263fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
326499653d4eSeschrock 	di_devlink_handle_t dhdl;
3265fa9e4066Sahrens 
3266fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3267fa9e4066Sahrens 
3268fa9e4066Sahrens 	/*
3269fa9e4066Sahrens 	 * Issue the appropriate ioctl.
3270fa9e4066Sahrens 	 */
327199653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_CREATE_MINOR, &zc) != 0) {
3272fa9e4066Sahrens 		switch (errno) {
3273fa9e4066Sahrens 		case EEXIST:
3274fa9e4066Sahrens 			/*
3275fa9e4066Sahrens 			 * Silently ignore the case where the link already
3276fa9e4066Sahrens 			 * exists.  This allows 'zfs volinit' to be run multiple
3277fa9e4066Sahrens 			 * times without errors.
3278fa9e4066Sahrens 			 */
3279fa9e4066Sahrens 			return (0);
3280fa9e4066Sahrens 
3281fa9e4066Sahrens 		default:
3282ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
328399653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot create device links "
328499653d4eSeschrock 			    "for '%s'"), dataset));
3285fa9e4066Sahrens 		}
3286fa9e4066Sahrens 	}
3287fa9e4066Sahrens 
3288fa9e4066Sahrens 	/*
3289fa9e4066Sahrens 	 * Call devfsadm and wait for the links to magically appear.
3290fa9e4066Sahrens 	 */
329199653d4eSeschrock 	if ((dhdl = di_devlink_init(ZFS_DRIVER, DI_MAKE_LINK)) == NULL) {
329299653d4eSeschrock 		zfs_error_aux(hdl, strerror(errno));
3293ece3d9b3Slling 		(void) zfs_error_fmt(hdl, EZFS_DEVLINKS,
329499653d4eSeschrock 		    dgettext(TEXT_DOMAIN, "cannot create device links "
329599653d4eSeschrock 		    "for '%s'"), dataset);
329699653d4eSeschrock 		(void) ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc);
3297fa9e4066Sahrens 		return (-1);
3298fa9e4066Sahrens 	} else {
329999653d4eSeschrock 		(void) di_devlink_fini(&dhdl);
3300fa9e4066Sahrens 	}
3301fa9e4066Sahrens 
3302fa9e4066Sahrens 	return (0);
3303fa9e4066Sahrens }
3304fa9e4066Sahrens 
3305fa9e4066Sahrens /*
3306fa9e4066Sahrens  * Remove a minor node for the given zvol and the associated /dev links.
3307fa9e4066Sahrens  */
3308fa9e4066Sahrens int
330999653d4eSeschrock zvol_remove_link(libzfs_handle_t *hdl, const char *dataset)
3310fa9e4066Sahrens {
3311fa9e4066Sahrens 	zfs_cmd_t zc = { 0 };
3312fa9e4066Sahrens 
3313fa9e4066Sahrens 	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3314fa9e4066Sahrens 
331599653d4eSeschrock 	if (ioctl(hdl->libzfs_fd, ZFS_IOC_REMOVE_MINOR, &zc) != 0) {
3316fa9e4066Sahrens 		switch (errno) {
3317fa9e4066Sahrens 		case ENXIO:
3318fa9e4066Sahrens 			/*
3319fa9e4066Sahrens 			 * Silently ignore the case where the link no longer
3320fa9e4066Sahrens 			 * exists, so that 'zfs volfini' can be run multiple
3321fa9e4066Sahrens 			 * times without errors.
3322fa9e4066Sahrens 			 */
3323fa9e4066Sahrens 			return (0);
3324fa9e4066Sahrens 
3325fa9e4066Sahrens 		default:
3326ece3d9b3Slling 			return (zfs_standard_error_fmt(hdl, errno,
332799653d4eSeschrock 			    dgettext(TEXT_DOMAIN, "cannot remove device "
332899653d4eSeschrock 			    "links for '%s'"), dataset));
3329fa9e4066Sahrens 		}
3330fa9e4066Sahrens 	}
3331fa9e4066Sahrens 
3332fa9e4066Sahrens 	return (0);
3333fa9e4066Sahrens }
3334e9dbad6fSeschrock 
3335e9dbad6fSeschrock nvlist_t *
3336e9dbad6fSeschrock zfs_get_user_props(zfs_handle_t *zhp)
3337e9dbad6fSeschrock {
3338e9dbad6fSeschrock 	return (zhp->zfs_user_props);
3339e9dbad6fSeschrock }
3340e9dbad6fSeschrock 
3341e9dbad6fSeschrock /*
3342e9dbad6fSeschrock  * Given a comma-separated list of properties, contruct a property list
3343e9dbad6fSeschrock  * containing both user-defined and native properties.  This function will
3344e9dbad6fSeschrock  * return a NULL list if 'all' is specified, which can later be expanded on a
3345e9dbad6fSeschrock  * per-dataset basis by zfs_expand_proplist().
3346e9dbad6fSeschrock  */
3347e9dbad6fSeschrock int
3348e9dbad6fSeschrock zfs_get_proplist(libzfs_handle_t *hdl, char *fields, zfs_proplist_t **listp)
3349e9dbad6fSeschrock {
3350e9dbad6fSeschrock 	int i;
3351e9dbad6fSeschrock 	size_t len;
3352e9dbad6fSeschrock 	char *s, *p;
3353e9dbad6fSeschrock 	char c;
3354e9dbad6fSeschrock 	zfs_prop_t prop;
3355e9dbad6fSeschrock 	zfs_proplist_t *entry;
3356e9dbad6fSeschrock 	zfs_proplist_t **last;
3357e9dbad6fSeschrock 
3358e9dbad6fSeschrock 	*listp = NULL;
3359e9dbad6fSeschrock 	last = listp;
3360e9dbad6fSeschrock 
3361e9dbad6fSeschrock 	/*
3362e9dbad6fSeschrock 	 * If 'all' is specified, return a NULL list.
3363e9dbad6fSeschrock 	 */
3364e9dbad6fSeschrock 	if (strcmp(fields, "all") == 0)
3365e9dbad6fSeschrock 		return (0);
3366e9dbad6fSeschrock 
3367e9dbad6fSeschrock 	/*
3368e9dbad6fSeschrock 	 * If no fields were specified, return an error.
3369e9dbad6fSeschrock 	 */
3370e9dbad6fSeschrock 	if (fields[0] == '\0') {
3371e9dbad6fSeschrock 		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3372e9dbad6fSeschrock 		    "no properties specified"));
3373e9dbad6fSeschrock 		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
3374e9dbad6fSeschrock 		    "bad property list")));
3375e9dbad6fSeschrock 	}
3376e9dbad6fSeschrock 
3377e9dbad6fSeschrock 	/*
3378e9dbad6fSeschrock 	 * It would be nice to use getsubopt() here, but the inclusion of column
3379e9dbad6fSeschrock 	 * aliases makes this more effort than it's worth.
3380e9dbad6fSeschrock 	 */
3381e9dbad6fSeschrock 	s = fields;
3382e9dbad6fSeschrock 	while (*s != '\0') {
3383e9dbad6fSeschrock 		if ((p = strchr(s, ',')) == NULL) {
3384e9dbad6fSeschrock 			len = strlen(s);
3385e9dbad6fSeschrock 			p = s + len;
3386e9dbad6fSeschrock 		} else {
3387e9dbad6fSeschrock 			len = p - s;
3388e9dbad6fSeschrock 		}
3389e9dbad6fSeschrock 
3390e9dbad6fSeschrock 		/*
3391e9dbad6fSeschrock 		 * Check for empty options.
3392e9dbad6fSeschrock 		 */
3393e9dbad6fSeschrock 		if (len == 0) {
3394e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3395e9dbad6fSeschrock 			    "empty property name"));
3396e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP,
3397e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "bad property list")));
3398e9dbad6fSeschrock 		}
3399e9dbad6fSeschrock 
3400e9dbad6fSeschrock 		/*
3401e9dbad6fSeschrock 		 * Check all regular property names.
3402e9dbad6fSeschrock 		 */
3403e9dbad6fSeschrock 		c = s[len];
3404e9dbad6fSeschrock 		s[len] = '\0';
3405e9dbad6fSeschrock 		for (i = 0; i < ZFS_NPROP_ALL; i++) {
3406e9dbad6fSeschrock 			if ((prop = zfs_name_to_prop(s)) != ZFS_PROP_INVAL)
3407e9dbad6fSeschrock 				break;
3408e9dbad6fSeschrock 		}
3409e9dbad6fSeschrock 
3410e9dbad6fSeschrock 		/*
3411e9dbad6fSeschrock 		 * If no column is specified, and this isn't a user property,
3412e9dbad6fSeschrock 		 * return failure.
3413e9dbad6fSeschrock 		 */
3414e9dbad6fSeschrock 		if (i == ZFS_NPROP_ALL && !zfs_prop_user(s)) {
3415e9dbad6fSeschrock 			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3416e9dbad6fSeschrock 			    "invalid property '%s'"), s);
3417e9dbad6fSeschrock 			return (zfs_error(hdl, EZFS_BADPROP,
3418e9dbad6fSeschrock 			    dgettext(TEXT_DOMAIN, "bad property list")));
3419e9dbad6fSeschrock 		}
3420e9dbad6fSeschrock 
3421e9dbad6fSeschrock 		if ((entry = zfs_alloc(hdl, sizeof (zfs_proplist_t))) == NULL)
3422e9dbad6fSeschrock 			return (-1);
3423e9dbad6fSeschrock 
3424e9dbad6fSeschrock 		entry->pl_prop = prop;
3425e9dbad6fSeschrock 		if (prop == ZFS_PROP_INVAL) {
3426e9dbad6fSeschrock 			if ((entry->pl_user_prop =
3427e9dbad6fSeschrock 			    zfs_strdup(hdl, s)) == NULL) {
3428e9dbad6fSeschrock 				free(entry);
3429e9dbad6fSeschrock 				return (-1);
3430e9dbad6fSeschrock 			}
3431e9dbad6fSeschrock 			entry->pl_width = strlen(s);
3432e9dbad6fSeschrock 		} else {
3433e9dbad6fSeschrock 			entry->pl_width = zfs_prop_width(prop,
3434e9dbad6fSeschrock 			    &entry->pl_fixed);
3435e9dbad6fSeschrock 		}
3436e9dbad6fSeschrock 
3437e9dbad6fSeschrock 		*last = entry;
3438e9dbad6fSeschrock 		last = &entry->pl_next;
3439e9dbad6fSeschrock 
3440e9dbad6fSeschrock 		s = p;
3441e9dbad6fSeschrock 		if (c == ',')
3442e9dbad6fSeschrock 			s++;
3443e9dbad6fSeschrock 	}
3444e9dbad6fSeschrock 
3445e9dbad6fSeschrock 	return (0);
3446e9dbad6fSeschrock }
3447e9dbad6fSeschrock 
3448e9dbad6fSeschrock void
3449e9dbad6fSeschrock zfs_free_proplist(zfs_proplist_t *pl)
3450e9dbad6fSeschrock {
3451e9dbad6fSeschrock 	zfs_proplist_t *next;
3452e9dbad6fSeschrock 
3453e9dbad6fSeschrock 	while (pl != NULL) {
3454e9dbad6fSeschrock 		next = pl->pl_next;
3455e9dbad6fSeschrock 		free(pl->pl_user_prop);
3456e9dbad6fSeschrock 		free(pl);
3457e9dbad6fSeschrock 		pl = next;
3458e9dbad6fSeschrock 	}
3459e9dbad6fSeschrock }
3460e9dbad6fSeschrock 
3461e9dbad6fSeschrock /*
3462e9dbad6fSeschrock  * This function is used by 'zfs list' to determine the exact set of columns to
3463e9dbad6fSeschrock  * display, and their maximum widths.  This does two main things:
3464e9dbad6fSeschrock  *
3465e9dbad6fSeschrock  * 	- If this is a list of all properties, then expand the list to include
3466e9dbad6fSeschrock  *	  all native properties, and set a flag so that for each dataset we look
3467e9dbad6fSeschrock  *	  for new unique user properties and add them to the list.
3468e9dbad6fSeschrock  *
3469e9dbad6fSeschrock  * 	- For non fixed-width properties, keep track of the maximum width seen
3470e9dbad6fSeschrock  *	  so that we can size the column appropriately.
3471e9dbad6fSeschrock  */
3472e9dbad6fSeschrock int
3473e9dbad6fSeschrock zfs_expand_proplist(zfs_handle_t *zhp, zfs_proplist_t **plp)
3474e9dbad6fSeschrock {
3475e9dbad6fSeschrock 	libzfs_handle_t *hdl = zhp->zfs_hdl;
3476e9dbad6fSeschrock 	zfs_prop_t prop;
3477e9dbad6fSeschrock 	zfs_proplist_t *entry;
3478e9dbad6fSeschrock 	zfs_proplist_t **last, **start;
3479e9dbad6fSeschrock 	nvlist_t *userprops, *propval;
3480e9dbad6fSeschrock 	nvpair_t *elem;
3481e9dbad6fSeschrock 	char *strval;
3482e9dbad6fSeschrock 	char buf[ZFS_MAXPROPLEN];
3483e9dbad6fSeschrock 
3484e9dbad6fSeschrock 	if (*plp == NULL) {
3485e9dbad6fSeschrock 		/*
3486e9dbad6fSeschrock 		 * If this is the very first time we've been called for an 'all'
3487e9dbad6fSeschrock 		 * specification, expand the list to include all native
3488e9dbad6fSeschrock 		 * properties.
3489e9dbad6fSeschrock 		 */
3490e9dbad6fSeschrock 		last = plp;
3491e9dbad6fSeschrock 		for (prop = 0; prop < ZFS_NPROP_VISIBLE; prop++) {
3492e9dbad6fSeschrock 			if ((entry = zfs_alloc(hdl,
3493e9dbad6fSeschrock 			    sizeof (zfs_proplist_t))) == NULL)
3494e9dbad6fSeschrock 				return (-1);
3495e9dbad6fSeschrock 
3496e9dbad6fSeschrock 			entry->pl_prop = prop;
3497e9dbad6fSeschrock 			entry->pl_width = zfs_prop_width(prop,
3498e9dbad6fSeschrock 			    &entry->pl_fixed);
3499e9dbad6fSeschrock 			entry->pl_all = B_TRUE;
3500e9dbad6fSeschrock 
3501e9dbad6fSeschrock 			*last = entry;
3502e9dbad6fSeschrock 			last = &entry->pl_next;
3503e9dbad6fSeschrock 		}
3504e9dbad6fSeschrock 
3505e9dbad6fSeschrock 		/*
3506e9dbad6fSeschrock 		 * Add 'name' to the beginning of the list, which is handled
3507e9dbad6fSeschrock 		 * specially.
3508e9dbad6fSeschrock 		 */
3509e9dbad6fSeschrock 		if ((entry = zfs_alloc(hdl,
3510e9dbad6fSeschrock 		    sizeof (zfs_proplist_t))) == NULL)
3511e9dbad6fSeschrock 			return (-1);
3512e9dbad6fSeschrock 
3513e9dbad6fSeschrock 		entry->pl_prop = ZFS_PROP_NAME;
3514e9dbad6fSeschrock 		entry->pl_width = zfs_prop_width(ZFS_PROP_NAME,
3515e9dbad6fSeschrock 		    &entry->pl_fixed);
3516e9dbad6fSeschrock 		entry->pl_all = B_TRUE;
3517e9dbad6fSeschrock 		entry->pl_next = *plp;
3518e9dbad6fSeschrock 		*plp = entry;
3519e9dbad6fSeschrock 	}
3520e9dbad6fSeschrock 
3521e9dbad6fSeschrock 	userprops = zfs_get_user_props(zhp);
3522e9dbad6fSeschrock 
3523e9dbad6fSeschrock 	entry = *plp;
3524e9dbad6fSeschrock 	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3525e9dbad6fSeschrock 		/*
3526e9dbad6fSeschrock 		 * Go through and add any user properties as necessary.  We
3527e9dbad6fSeschrock 		 * start by incrementing our list pointer to the first
3528e9dbad6fSeschrock 		 * non-native property.
3529e9dbad6fSeschrock 		 */
3530e9dbad6fSeschrock 		start = plp;
3531e9dbad6fSeschrock 		while (*start != NULL) {
3532e9dbad6fSeschrock 			if ((*start)->pl_prop == ZFS_PROP_INVAL)
3533e9dbad6fSeschrock 				break;
3534e9dbad6fSeschrock 			start = &(*start)->pl_next;
3535e9dbad6fSeschrock 		}
3536e9dbad6fSeschrock 
3537e9dbad6fSeschrock 		elem = NULL;
3538e9dbad6fSeschrock 		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3539e9dbad6fSeschrock 			/*
3540e9dbad6fSeschrock 			 * See if we've already found this property in our list.
3541e9dbad6fSeschrock 			 */
3542e9dbad6fSeschrock 			for (last = start; *last != NULL;
3543e9dbad6fSeschrock 			    last = &(*last)->pl_next) {
3544e9dbad6fSeschrock 				if (strcmp((*last)->pl_user_prop,
3545e9dbad6fSeschrock 				    nvpair_name(elem)) == 0)
3546e9dbad6fSeschrock 					break;
3547e9dbad6fSeschrock 			}
3548e9dbad6fSeschrock 
3549e9dbad6fSeschrock 			if (*last == NULL) {
3550e9dbad6fSeschrock 				if ((entry = zfs_alloc(hdl,
3551e9dbad6fSeschrock 				    sizeof (zfs_proplist_t))) == NULL ||
3552e9dbad6fSeschrock 				    ((entry->pl_user_prop = zfs_strdup(hdl,
3553e9dbad6fSeschrock 				    nvpair_name(elem)))) == NULL) {
3554e9dbad6fSeschrock 					free(entry);
3555e9dbad6fSeschrock 					return (-1);
3556e9dbad6fSeschrock 				}
3557e9dbad6fSeschrock 
3558e9dbad6fSeschrock 				entry->pl_prop = ZFS_PROP_INVAL;
3559e9dbad6fSeschrock 				entry->pl_width = strlen(nvpair_name(elem));
3560e9dbad6fSeschrock 				entry->pl_all = B_TRUE;
3561e9dbad6fSeschrock 				*last = entry;
3562e9dbad6fSeschrock 			}
3563e9dbad6fSeschrock 		}
3564e9dbad6fSeschrock 	}
3565e9dbad6fSeschrock 
3566e9dbad6fSeschrock 	/*
3567e9dbad6fSeschrock 	 * Now go through and check the width of any non-fixed columns
3568e9dbad6fSeschrock 	 */
3569e9dbad6fSeschrock 	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3570e9dbad6fSeschrock 		if (entry->pl_fixed)
3571e9dbad6fSeschrock 			continue;
3572e9dbad6fSeschrock 
3573e9dbad6fSeschrock 		if (entry->pl_prop != ZFS_PROP_INVAL) {
3574e9dbad6fSeschrock 			if (zfs_prop_get(zhp, entry->pl_prop,
3575e9dbad6fSeschrock 			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3576e9dbad6fSeschrock 				if (strlen(buf) > entry->pl_width)
3577e9dbad6fSeschrock 					entry->pl_width = strlen(buf);
3578e9dbad6fSeschrock 			}
3579e9dbad6fSeschrock 		} else if (nvlist_lookup_nvlist(userprops,
3580e9dbad6fSeschrock 		    entry->pl_user_prop, &propval)  == 0) {
3581e9dbad6fSeschrock 			verify(nvlist_lookup_string(propval,
3582e9dbad6fSeschrock 			    ZFS_PROP_VALUE, &strval) == 0);
3583e9dbad6fSeschrock 			if (strlen(strval) > entry->pl_width)
3584e9dbad6fSeschrock 				entry->pl_width = strlen(strval);
3585e9dbad6fSeschrock 		}
3586e9dbad6fSeschrock 	}
3587e9dbad6fSeschrock 
3588e9dbad6fSeschrock 	return (0);
3589e9dbad6fSeschrock }
3590