libzfs_dataset.c revision 226706
1168404Spjd/*
2168404Spjd * CDDL HEADER START
3168404Spjd *
4168404Spjd * The contents of this file are subject to the terms of the
5168404Spjd * Common Development and Distribution License (the "License").
6168404Spjd * You may not use this file except in compliance with the License.
7168404Spjd *
8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9168404Spjd * or http://www.opensolaris.org/os/licensing.
10168404Spjd * See the License for the specific language governing permissions
11168404Spjd * and limitations under the License.
12168404Spjd *
13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each
14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15168404Spjd * If applicable, add the following below this CDDL HEADER, with the
16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18168404Spjd *
19168404Spjd * CDDL HEADER END
20168404Spjd */
21168404Spjd
22168404Spjd/*
23219089Spjd * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24219089Spjd * Copyright 2010 Nexenta Systems, Inc. All rights reserved.
25223623Smm * Copyright (c) 2011 by Delphix. All rights reserved.
26226706Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>.
27226706Spjd * All rights reserved.
28168404Spjd */
29168404Spjd
30168404Spjd#include <ctype.h>
31168404Spjd#include <errno.h>
32168404Spjd#include <libintl.h>
33168404Spjd#include <math.h>
34168404Spjd#include <stdio.h>
35168404Spjd#include <stdlib.h>
36168404Spjd#include <strings.h>
37168404Spjd#include <unistd.h>
38185029Spjd#include <stddef.h>
39168404Spjd#include <zone.h>
40168404Spjd#include <fcntl.h>
41168404Spjd#include <sys/mntent.h>
42168404Spjd#include <sys/mount.h>
43185029Spjd#include <priv.h>
44185029Spjd#include <pwd.h>
45185029Spjd#include <grp.h>
46185029Spjd#include <stddef.h>
47209962Smm#include <idmap.h>
48168404Spjd
49219089Spjd#include <sys/dnode.h>
50168404Spjd#include <sys/spa.h>
51168404Spjd#include <sys/zap.h>
52209962Smm#include <sys/misc.h>
53168404Spjd#include <libzfs.h>
54168404Spjd
55168404Spjd#include "zfs_namecheck.h"
56168404Spjd#include "zfs_prop.h"
57168404Spjd#include "libzfs_impl.h"
58185029Spjd#include "zfs_deleg.h"
59168404Spjd
60209962Smmstatic int userquota_propname_decode(const char *propname, boolean_t zoned,
61209962Smm    zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
62168676Spjd
63168404Spjd/*
64168404Spjd * Given a single type (not a mask of types), return the type in a human
65168404Spjd * readable form.
66168404Spjd */
67168404Spjdconst char *
68168404Spjdzfs_type_to_name(zfs_type_t type)
69168404Spjd{
70168404Spjd	switch (type) {
71168404Spjd	case ZFS_TYPE_FILESYSTEM:
72168404Spjd		return (dgettext(TEXT_DOMAIN, "filesystem"));
73168404Spjd	case ZFS_TYPE_SNAPSHOT:
74168404Spjd		return (dgettext(TEXT_DOMAIN, "snapshot"));
75168404Spjd	case ZFS_TYPE_VOLUME:
76168404Spjd		return (dgettext(TEXT_DOMAIN, "volume"));
77168404Spjd	}
78168404Spjd
79168404Spjd	return (NULL);
80168404Spjd}
81168404Spjd
82168404Spjd/*
83168404Spjd * Given a path and mask of ZFS types, return a string describing this dataset.
84168404Spjd * This is used when we fail to open a dataset and we cannot get an exact type.
85168404Spjd * We guess what the type would have been based on the path and the mask of
86168404Spjd * acceptable types.
87168404Spjd */
88168404Spjdstatic const char *
89168404Spjdpath_to_str(const char *path, int types)
90168404Spjd{
91168404Spjd	/*
92168404Spjd	 * When given a single type, always report the exact type.
93168404Spjd	 */
94168404Spjd	if (types == ZFS_TYPE_SNAPSHOT)
95168404Spjd		return (dgettext(TEXT_DOMAIN, "snapshot"));
96168404Spjd	if (types == ZFS_TYPE_FILESYSTEM)
97168404Spjd		return (dgettext(TEXT_DOMAIN, "filesystem"));
98168404Spjd	if (types == ZFS_TYPE_VOLUME)
99168404Spjd		return (dgettext(TEXT_DOMAIN, "volume"));
100168404Spjd
101168404Spjd	/*
102168404Spjd	 * The user is requesting more than one type of dataset.  If this is the
103168404Spjd	 * case, consult the path itself.  If we're looking for a snapshot, and
104168404Spjd	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
105168404Spjd	 * snapshot attribute and try again.
106168404Spjd	 */
107168404Spjd	if (types & ZFS_TYPE_SNAPSHOT) {
108168404Spjd		if (strchr(path, '@') != NULL)
109168404Spjd			return (dgettext(TEXT_DOMAIN, "snapshot"));
110168404Spjd		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
111168404Spjd	}
112168404Spjd
113168404Spjd	/*
114168404Spjd	 * The user has requested either filesystems or volumes.
115168404Spjd	 * We have no way of knowing a priori what type this would be, so always
116168404Spjd	 * report it as "filesystem" or "volume", our two primitive types.
117168404Spjd	 */
118168404Spjd	if (types & ZFS_TYPE_FILESYSTEM)
119168404Spjd		return (dgettext(TEXT_DOMAIN, "filesystem"));
120168404Spjd
121168404Spjd	assert(types & ZFS_TYPE_VOLUME);
122168404Spjd	return (dgettext(TEXT_DOMAIN, "volume"));
123168404Spjd}
124168404Spjd
125168404Spjd/*
126168404Spjd * Validate a ZFS path.  This is used even before trying to open the dataset, to
127209962Smm * provide a more meaningful error message.  We call zfs_error_aux() to
128209962Smm * explain exactly why the name was not valid.
129168404Spjd */
130219089Spjdint
131185029Spjdzfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
132185029Spjd    boolean_t modifying)
133168404Spjd{
134168404Spjd	namecheck_err_t why;
135168404Spjd	char what;
136168404Spjd
137219089Spjd	(void) zfs_prop_get_table();
138168404Spjd	if (dataset_namecheck(path, &why, &what) != 0) {
139168404Spjd		if (hdl != NULL) {
140168404Spjd			switch (why) {
141168404Spjd			case NAME_ERR_TOOLONG:
142168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
143168404Spjd				    "name is too long"));
144168404Spjd				break;
145168404Spjd
146168404Spjd			case NAME_ERR_LEADING_SLASH:
147168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
148168404Spjd				    "leading slash in name"));
149168404Spjd				break;
150168404Spjd
151168404Spjd			case NAME_ERR_EMPTY_COMPONENT:
152168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
153168404Spjd				    "empty component in name"));
154168404Spjd				break;
155168404Spjd
156168404Spjd			case NAME_ERR_TRAILING_SLASH:
157168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
158168404Spjd				    "trailing slash in name"));
159168404Spjd				break;
160168404Spjd
161168404Spjd			case NAME_ERR_INVALCHAR:
162168404Spjd				zfs_error_aux(hdl,
163168404Spjd				    dgettext(TEXT_DOMAIN, "invalid character "
164168404Spjd				    "'%c' in name"), what);
165168404Spjd				break;
166168404Spjd
167168404Spjd			case NAME_ERR_MULTIPLE_AT:
168168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
169168404Spjd				    "multiple '@' delimiters in name"));
170168404Spjd				break;
171168404Spjd
172168404Spjd			case NAME_ERR_NOLETTER:
173168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
174168404Spjd				    "pool doesn't begin with a letter"));
175168404Spjd				break;
176168404Spjd
177168404Spjd			case NAME_ERR_RESERVED:
178168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
179168404Spjd				    "name is reserved"));
180168404Spjd				break;
181168404Spjd
182168404Spjd			case NAME_ERR_DISKLIKE:
183168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
184168404Spjd				    "reserved disk name"));
185168404Spjd				break;
186168404Spjd			}
187168404Spjd		}
188168404Spjd
189168404Spjd		return (0);
190168404Spjd	}
191168404Spjd
192168404Spjd	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
193168404Spjd		if (hdl != NULL)
194168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
195168404Spjd			    "snapshot delimiter '@' in filesystem name"));
196168404Spjd		return (0);
197168404Spjd	}
198168404Spjd
199168404Spjd	if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
200168404Spjd		if (hdl != NULL)
201168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
202168404Spjd			    "missing '@' delimiter in snapshot name"));
203168404Spjd		return (0);
204168404Spjd	}
205168404Spjd
206185029Spjd	if (modifying && strchr(path, '%') != NULL) {
207185029Spjd		if (hdl != NULL)
208185029Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
209185029Spjd			    "invalid character %c in name"), '%');
210185029Spjd		return (0);
211185029Spjd	}
212185029Spjd
213168404Spjd	return (-1);
214168404Spjd}
215168404Spjd
216168404Spjdint
217168404Spjdzfs_name_valid(const char *name, zfs_type_t type)
218168404Spjd{
219185029Spjd	if (type == ZFS_TYPE_POOL)
220185029Spjd		return (zpool_name_valid(NULL, B_FALSE, name));
221185029Spjd	return (zfs_validate_name(NULL, name, type, B_FALSE));
222168404Spjd}
223168404Spjd
224168404Spjd/*
225168404Spjd * This function takes the raw DSL properties, and filters out the user-defined
226168404Spjd * properties into a separate nvlist.
227168404Spjd */
228185029Spjdstatic nvlist_t *
229185029Spjdprocess_user_props(zfs_handle_t *zhp, nvlist_t *props)
230168404Spjd{
231168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
232168404Spjd	nvpair_t *elem;
233168404Spjd	nvlist_t *propval;
234185029Spjd	nvlist_t *nvl;
235168404Spjd
236185029Spjd	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
237185029Spjd		(void) no_memory(hdl);
238185029Spjd		return (NULL);
239185029Spjd	}
240168404Spjd
241168404Spjd	elem = NULL;
242185029Spjd	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
243168404Spjd		if (!zfs_prop_user(nvpair_name(elem)))
244168404Spjd			continue;
245168404Spjd
246168404Spjd		verify(nvpair_value_nvlist(elem, &propval) == 0);
247185029Spjd		if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
248185029Spjd			nvlist_free(nvl);
249185029Spjd			(void) no_memory(hdl);
250185029Spjd			return (NULL);
251185029Spjd		}
252168404Spjd	}
253168404Spjd
254185029Spjd	return (nvl);
255168404Spjd}
256168404Spjd
257185029Spjdstatic zpool_handle_t *
258185029Spjdzpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
259185029Spjd{
260185029Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
261185029Spjd	zpool_handle_t *zph;
262185029Spjd
263185029Spjd	if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
264185029Spjd		if (hdl->libzfs_pool_handles != NULL)
265185029Spjd			zph->zpool_next = hdl->libzfs_pool_handles;
266185029Spjd		hdl->libzfs_pool_handles = zph;
267185029Spjd	}
268185029Spjd	return (zph);
269185029Spjd}
270185029Spjd
271185029Spjdstatic zpool_handle_t *
272185029Spjdzpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
273185029Spjd{
274185029Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
275185029Spjd	zpool_handle_t *zph = hdl->libzfs_pool_handles;
276185029Spjd
277185029Spjd	while ((zph != NULL) &&
278185029Spjd	    (strncmp(pool_name, zpool_get_name(zph), len) != 0))
279185029Spjd		zph = zph->zpool_next;
280185029Spjd	return (zph);
281185029Spjd}
282185029Spjd
283168404Spjd/*
284185029Spjd * Returns a handle to the pool that contains the provided dataset.
285185029Spjd * If a handle to that pool already exists then that handle is returned.
286185029Spjd * Otherwise, a new handle is created and added to the list of handles.
287185029Spjd */
288185029Spjdstatic zpool_handle_t *
289185029Spjdzpool_handle(zfs_handle_t *zhp)
290185029Spjd{
291185029Spjd	char *pool_name;
292185029Spjd	int len;
293185029Spjd	zpool_handle_t *zph;
294185029Spjd
295185029Spjd	len = strcspn(zhp->zfs_name, "/@") + 1;
296185029Spjd	pool_name = zfs_alloc(zhp->zfs_hdl, len);
297185029Spjd	(void) strlcpy(pool_name, zhp->zfs_name, len);
298185029Spjd
299185029Spjd	zph = zpool_find_handle(zhp, pool_name, len);
300185029Spjd	if (zph == NULL)
301185029Spjd		zph = zpool_add_handle(zhp, pool_name);
302185029Spjd
303185029Spjd	free(pool_name);
304185029Spjd	return (zph);
305185029Spjd}
306185029Spjd
307185029Spjdvoid
308185029Spjdzpool_free_handles(libzfs_handle_t *hdl)
309185029Spjd{
310185029Spjd	zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
311185029Spjd
312185029Spjd	while (zph != NULL) {
313185029Spjd		next = zph->zpool_next;
314185029Spjd		zpool_close(zph);
315185029Spjd		zph = next;
316185029Spjd	}
317185029Spjd	hdl->libzfs_pool_handles = NULL;
318185029Spjd}
319185029Spjd
320185029Spjd/*
321168404Spjd * Utility function to gather stats (objset and zpl) for the given object.
322168404Spjd */
323219089Spjdstatic int
324209962Smmget_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
325168404Spjd{
326168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
327168404Spjd
328209962Smm	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
329168404Spjd
330209962Smm	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
331168404Spjd		if (errno == ENOMEM) {
332209962Smm			if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
333168404Spjd				return (-1);
334168404Spjd			}
335168404Spjd		} else {
336168404Spjd			return (-1);
337168404Spjd		}
338168404Spjd	}
339209962Smm	return (0);
340209962Smm}
341168404Spjd
342219089Spjd/*
343219089Spjd * Utility function to get the received properties of the given object.
344219089Spjd */
345209962Smmstatic int
346219089Spjdget_recvd_props_ioctl(zfs_handle_t *zhp)
347219089Spjd{
348219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
349219089Spjd	nvlist_t *recvdprops;
350219089Spjd	zfs_cmd_t zc = { 0 };
351219089Spjd	int err;
352219089Spjd
353219089Spjd	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
354219089Spjd		return (-1);
355219089Spjd
356219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
357219089Spjd
358219089Spjd	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
359219089Spjd		if (errno == ENOMEM) {
360219089Spjd			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
361219089Spjd				return (-1);
362219089Spjd			}
363219089Spjd		} else {
364219089Spjd			zcmd_free_nvlists(&zc);
365219089Spjd			return (-1);
366219089Spjd		}
367219089Spjd	}
368219089Spjd
369219089Spjd	err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
370219089Spjd	zcmd_free_nvlists(&zc);
371219089Spjd	if (err != 0)
372219089Spjd		return (-1);
373219089Spjd
374219089Spjd	nvlist_free(zhp->zfs_recvd_props);
375219089Spjd	zhp->zfs_recvd_props = recvdprops;
376219089Spjd
377219089Spjd	return (0);
378219089Spjd}
379219089Spjd
380219089Spjdstatic int
381209962Smmput_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
382209962Smm{
383209962Smm	nvlist_t *allprops, *userprops;
384168404Spjd
385209962Smm	zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
386209962Smm
387209962Smm	if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
388168404Spjd		return (-1);
389168404Spjd	}
390168404Spjd
391209962Smm	/*
392209962Smm	 * XXX Why do we store the user props separately, in addition to
393209962Smm	 * storing them in zfs_props?
394209962Smm	 */
395185029Spjd	if ((userprops = process_user_props(zhp, allprops)) == NULL) {
396185029Spjd		nvlist_free(allprops);
397168404Spjd		return (-1);
398185029Spjd	}
399168404Spjd
400185029Spjd	nvlist_free(zhp->zfs_props);
401185029Spjd	nvlist_free(zhp->zfs_user_props);
402185029Spjd
403185029Spjd	zhp->zfs_props = allprops;
404185029Spjd	zhp->zfs_user_props = userprops;
405185029Spjd
406168404Spjd	return (0);
407168404Spjd}
408168404Spjd
409209962Smmstatic int
410209962Smmget_stats(zfs_handle_t *zhp)
411209962Smm{
412209962Smm	int rc = 0;
413209962Smm	zfs_cmd_t zc = { 0 };
414209962Smm
415209962Smm	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
416209962Smm		return (-1);
417209962Smm	if (get_stats_ioctl(zhp, &zc) != 0)
418209962Smm		rc = -1;
419209962Smm	else if (put_stats_zhdl(zhp, &zc) != 0)
420209962Smm		rc = -1;
421209962Smm	zcmd_free_nvlists(&zc);
422209962Smm	return (rc);
423209962Smm}
424209962Smm
425168404Spjd/*
426168404Spjd * Refresh the properties currently stored in the handle.
427168404Spjd */
428168404Spjdvoid
429168404Spjdzfs_refresh_properties(zfs_handle_t *zhp)
430168404Spjd{
431168404Spjd	(void) get_stats(zhp);
432168404Spjd}
433168404Spjd
434168404Spjd/*
435168404Spjd * Makes a handle from the given dataset name.  Used by zfs_open() and
436168404Spjd * zfs_iter_* to create child handles on the fly.
437168404Spjd */
438209962Smmstatic int
439209962Smmmake_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
440168404Spjd{
441219089Spjd	if (put_stats_zhdl(zhp, zc) != 0)
442209962Smm		return (-1);
443168404Spjd
444168404Spjd	/*
445168404Spjd	 * We've managed to open the dataset and gather statistics.  Determine
446168404Spjd	 * the high-level type.
447168404Spjd	 */
448168404Spjd	if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
449168404Spjd		zhp->zfs_head_type = ZFS_TYPE_VOLUME;
450168404Spjd	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
451168404Spjd		zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
452168404Spjd	else
453168404Spjd		abort();
454168404Spjd
455168404Spjd	if (zhp->zfs_dmustats.dds_is_snapshot)
456168404Spjd		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
457168404Spjd	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
458168404Spjd		zhp->zfs_type = ZFS_TYPE_VOLUME;
459168404Spjd	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
460168404Spjd		zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
461168404Spjd	else
462168404Spjd		abort();	/* we should never see any other types */
463168404Spjd
464219089Spjd	if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL)
465219089Spjd		return (-1);
466219089Spjd
467209962Smm	return (0);
468209962Smm}
469209962Smm
470209962Smmzfs_handle_t *
471209962Smmmake_dataset_handle(libzfs_handle_t *hdl, const char *path)
472209962Smm{
473209962Smm	zfs_cmd_t zc = { 0 };
474209962Smm
475209962Smm	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
476209962Smm
477209962Smm	if (zhp == NULL)
478209962Smm		return (NULL);
479209962Smm
480209962Smm	zhp->zfs_hdl = hdl;
481209962Smm	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
482209962Smm	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
483209962Smm		free(zhp);
484209962Smm		return (NULL);
485209962Smm	}
486209962Smm	if (get_stats_ioctl(zhp, &zc) == -1) {
487209962Smm		zcmd_free_nvlists(&zc);
488209962Smm		free(zhp);
489209962Smm		return (NULL);
490209962Smm	}
491209962Smm	if (make_dataset_handle_common(zhp, &zc) == -1) {
492209962Smm		free(zhp);
493209962Smm		zhp = NULL;
494209962Smm	}
495209962Smm	zcmd_free_nvlists(&zc);
496168404Spjd	return (zhp);
497168404Spjd}
498168404Spjd
499209962Smmstatic zfs_handle_t *
500209962Smmmake_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
501209962Smm{
502209962Smm	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
503209962Smm
504209962Smm	if (zhp == NULL)
505209962Smm		return (NULL);
506209962Smm
507209962Smm	zhp->zfs_hdl = hdl;
508209962Smm	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
509209962Smm	if (make_dataset_handle_common(zhp, zc) == -1) {
510209962Smm		free(zhp);
511209962Smm		return (NULL);
512209962Smm	}
513209962Smm	return (zhp);
514209962Smm}
515209962Smm
516168404Spjd/*
517168404Spjd * Opens the given snapshot, filesystem, or volume.   The 'types'
518168404Spjd * argument is a mask of acceptable types.  The function will print an
519168404Spjd * appropriate error message and return NULL if it can't be opened.
520168404Spjd */
521168404Spjdzfs_handle_t *
522168404Spjdzfs_open(libzfs_handle_t *hdl, const char *path, int types)
523168404Spjd{
524168404Spjd	zfs_handle_t *zhp;
525168404Spjd	char errbuf[1024];
526168404Spjd
527168404Spjd	(void) snprintf(errbuf, sizeof (errbuf),
528168404Spjd	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
529168404Spjd
530168404Spjd	/*
531168404Spjd	 * Validate the name before we even try to open it.
532168404Spjd	 */
533185029Spjd	if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
534168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
535168404Spjd		    "invalid dataset name"));
536168404Spjd		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
537168404Spjd		return (NULL);
538168404Spjd	}
539168404Spjd
540168404Spjd	/*
541168404Spjd	 * Try to get stats for the dataset, which will tell us if it exists.
542168404Spjd	 */
543168404Spjd	errno = 0;
544168404Spjd	if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
545168404Spjd		(void) zfs_standard_error(hdl, errno, errbuf);
546168404Spjd		return (NULL);
547168404Spjd	}
548168404Spjd
549168404Spjd	if (!(types & zhp->zfs_type)) {
550168404Spjd		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
551168404Spjd		zfs_close(zhp);
552168404Spjd		return (NULL);
553168404Spjd	}
554168404Spjd
555168404Spjd	return (zhp);
556168404Spjd}
557168404Spjd
558168404Spjd/*
559168404Spjd * Release a ZFS handle.  Nothing to do but free the associated memory.
560168404Spjd */
561168404Spjdvoid
562168404Spjdzfs_close(zfs_handle_t *zhp)
563168404Spjd{
564168404Spjd	if (zhp->zfs_mntopts)
565168404Spjd		free(zhp->zfs_mntopts);
566168404Spjd	nvlist_free(zhp->zfs_props);
567168404Spjd	nvlist_free(zhp->zfs_user_props);
568219089Spjd	nvlist_free(zhp->zfs_recvd_props);
569168404Spjd	free(zhp);
570168404Spjd}
571168404Spjd
572209962Smmtypedef struct mnttab_node {
573209962Smm	struct mnttab mtn_mt;
574209962Smm	avl_node_t mtn_node;
575209962Smm} mnttab_node_t;
576209962Smm
577209962Smmstatic int
578209962Smmlibzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
579209962Smm{
580209962Smm	const mnttab_node_t *mtn1 = arg1;
581209962Smm	const mnttab_node_t *mtn2 = arg2;
582209962Smm	int rv;
583209962Smm
584209962Smm	rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
585209962Smm
586209962Smm	if (rv == 0)
587209962Smm		return (0);
588209962Smm	return (rv > 0 ? 1 : -1);
589209962Smm}
590209962Smm
591209962Smmvoid
592209962Smmlibzfs_mnttab_init(libzfs_handle_t *hdl)
593209962Smm{
594209962Smm	assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
595209962Smm	avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
596209962Smm	    sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
597209962Smm}
598209962Smm
599209962Smmvoid
600209962Smmlibzfs_mnttab_update(libzfs_handle_t *hdl)
601209962Smm{
602209962Smm	struct mnttab entry;
603209962Smm
604209962Smm	rewind(hdl->libzfs_mnttab);
605209962Smm	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
606209962Smm		mnttab_node_t *mtn;
607209962Smm
608209962Smm		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
609209962Smm			continue;
610209962Smm		mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
611209962Smm		mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
612209962Smm		mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
613209962Smm		mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
614209962Smm		mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
615209962Smm		avl_add(&hdl->libzfs_mnttab_cache, mtn);
616209962Smm	}
617209962Smm}
618209962Smm
619209962Smmvoid
620209962Smmlibzfs_mnttab_fini(libzfs_handle_t *hdl)
621209962Smm{
622209962Smm	void *cookie = NULL;
623209962Smm	mnttab_node_t *mtn;
624209962Smm
625209962Smm	while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
626209962Smm		free(mtn->mtn_mt.mnt_special);
627209962Smm		free(mtn->mtn_mt.mnt_mountp);
628209962Smm		free(mtn->mtn_mt.mnt_fstype);
629209962Smm		free(mtn->mtn_mt.mnt_mntopts);
630209962Smm		free(mtn);
631209962Smm	}
632209962Smm	avl_destroy(&hdl->libzfs_mnttab_cache);
633209962Smm}
634209962Smm
635209962Smmvoid
636209962Smmlibzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
637209962Smm{
638209962Smm	hdl->libzfs_mnttab_enable = enable;
639209962Smm}
640209962Smm
641185029Spjdint
642209962Smmlibzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
643209962Smm    struct mnttab *entry)
644209962Smm{
645209962Smm	mnttab_node_t find;
646209962Smm	mnttab_node_t *mtn;
647209962Smm
648209962Smm	if (!hdl->libzfs_mnttab_enable) {
649209962Smm		struct mnttab srch = { 0 };
650209962Smm
651209962Smm		if (avl_numnodes(&hdl->libzfs_mnttab_cache))
652209962Smm			libzfs_mnttab_fini(hdl);
653209962Smm		rewind(hdl->libzfs_mnttab);
654209962Smm		srch.mnt_special = (char *)fsname;
655209962Smm		srch.mnt_fstype = MNTTYPE_ZFS;
656209962Smm		if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
657209962Smm			return (0);
658209962Smm		else
659209962Smm			return (ENOENT);
660209962Smm	}
661209962Smm
662209962Smm	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
663209962Smm		libzfs_mnttab_update(hdl);
664209962Smm
665209962Smm	find.mtn_mt.mnt_special = (char *)fsname;
666209962Smm	mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
667209962Smm	if (mtn) {
668209962Smm		*entry = mtn->mtn_mt;
669209962Smm		return (0);
670209962Smm	}
671209962Smm	return (ENOENT);
672209962Smm}
673209962Smm
674209962Smmvoid
675209962Smmlibzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
676209962Smm    const char *mountp, const char *mntopts)
677209962Smm{
678209962Smm	mnttab_node_t *mtn;
679209962Smm
680209962Smm	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
681209962Smm		return;
682209962Smm	mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
683209962Smm	mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
684209962Smm	mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
685209962Smm	mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
686209962Smm	mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
687209962Smm	avl_add(&hdl->libzfs_mnttab_cache, mtn);
688209962Smm}
689209962Smm
690209962Smmvoid
691209962Smmlibzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
692209962Smm{
693209962Smm	mnttab_node_t find;
694209962Smm	mnttab_node_t *ret;
695209962Smm
696209962Smm	find.mtn_mt.mnt_special = (char *)fsname;
697209962Smm	if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
698209962Smm		avl_remove(&hdl->libzfs_mnttab_cache, ret);
699209962Smm		free(ret->mtn_mt.mnt_special);
700209962Smm		free(ret->mtn_mt.mnt_mountp);
701209962Smm		free(ret->mtn_mt.mnt_fstype);
702209962Smm		free(ret->mtn_mt.mnt_mntopts);
703209962Smm		free(ret);
704209962Smm	}
705209962Smm}
706209962Smm
707209962Smmint
708185029Spjdzfs_spa_version(zfs_handle_t *zhp, int *spa_version)
709168404Spjd{
710185029Spjd	zpool_handle_t *zpool_handle = zhp->zpool_hdl;
711168404Spjd
712185029Spjd	if (zpool_handle == NULL)
713168404Spjd		return (-1);
714168404Spjd
715185029Spjd	*spa_version = zpool_get_prop_int(zpool_handle,
716185029Spjd	    ZPOOL_PROP_VERSION, NULL);
717168404Spjd	return (0);
718168404Spjd}
719168404Spjd
720168404Spjd/*
721185029Spjd * The choice of reservation property depends on the SPA version.
722168404Spjd */
723168404Spjdstatic int
724185029Spjdzfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
725168404Spjd{
726185029Spjd	int spa_version;
727168404Spjd
728185029Spjd	if (zfs_spa_version(zhp, &spa_version) < 0)
729168404Spjd		return (-1);
730168404Spjd
731185029Spjd	if (spa_version >= SPA_VERSION_REFRESERVATION)
732185029Spjd		*resv_prop = ZFS_PROP_REFRESERVATION;
733185029Spjd	else
734185029Spjd		*resv_prop = ZFS_PROP_RESERVATION;
735168404Spjd
736168404Spjd	return (0);
737168404Spjd}
738168404Spjd
739168404Spjd/*
740168404Spjd * Given an nvlist of properties to set, validates that they are correct, and
741168404Spjd * parses any numeric properties (index, boolean, etc) if they are specified as
742168404Spjd * strings.
743168404Spjd */
744168404Spjdnvlist_t *
745185029Spjdzfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
746185029Spjd    uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
747168404Spjd{
748168404Spjd	nvpair_t *elem;
749168404Spjd	uint64_t intval;
750168404Spjd	char *strval;
751185029Spjd	zfs_prop_t prop;
752168404Spjd	nvlist_t *ret;
753185029Spjd	int chosen_normal = -1;
754185029Spjd	int chosen_utf = -1;
755168404Spjd
756168404Spjd	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
757168404Spjd		(void) no_memory(hdl);
758168404Spjd		return (NULL);
759168404Spjd	}
760168404Spjd
761209962Smm	/*
762209962Smm	 * Make sure this property is valid and applies to this type.
763209962Smm	 */
764209962Smm
765168404Spjd	elem = NULL;
766168404Spjd	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
767185029Spjd		const char *propname = nvpair_name(elem);
768168404Spjd
769209962Smm		prop = zfs_name_to_prop(propname);
770209962Smm		if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
771185029Spjd			/*
772209962Smm			 * This is a user property: make sure it's a
773185029Spjd			 * string, and that it's less than ZAP_MAXNAMELEN.
774185029Spjd			 */
775185029Spjd			if (nvpair_type(elem) != DATA_TYPE_STRING) {
776185029Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
777185029Spjd				    "'%s' must be a string"), propname);
778185029Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
779185029Spjd				goto error;
780168404Spjd			}
781168404Spjd
782185029Spjd			if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
783185029Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
784185029Spjd				    "property name '%s' is too long"),
785185029Spjd				    propname);
786185029Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
787185029Spjd				goto error;
788185029Spjd			}
789185029Spjd
790168404Spjd			(void) nvpair_value_string(elem, &strval);
791168404Spjd			if (nvlist_add_string(ret, propname, strval) != 0) {
792168404Spjd				(void) no_memory(hdl);
793168404Spjd				goto error;
794168404Spjd			}
795168404Spjd			continue;
796168404Spjd		}
797168404Spjd
798209962Smm		/*
799209962Smm		 * Currently, only user properties can be modified on
800209962Smm		 * snapshots.
801209962Smm		 */
802185029Spjd		if (type == ZFS_TYPE_SNAPSHOT) {
803185029Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
804185029Spjd			    "this property can not be modified for snapshots"));
805185029Spjd			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
806185029Spjd			goto error;
807185029Spjd		}
808168404Spjd
809209962Smm		if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
810209962Smm			zfs_userquota_prop_t uqtype;
811209962Smm			char newpropname[128];
812209962Smm			char domain[128];
813209962Smm			uint64_t rid;
814209962Smm			uint64_t valary[3];
815209962Smm
816209962Smm			if (userquota_propname_decode(propname, zoned,
817209962Smm			    &uqtype, domain, sizeof (domain), &rid) != 0) {
818209962Smm				zfs_error_aux(hdl,
819209962Smm				    dgettext(TEXT_DOMAIN,
820209962Smm				    "'%s' has an invalid user/group name"),
821209962Smm				    propname);
822209962Smm				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
823209962Smm				goto error;
824209962Smm			}
825209962Smm
826209962Smm			if (uqtype != ZFS_PROP_USERQUOTA &&
827209962Smm			    uqtype != ZFS_PROP_GROUPQUOTA) {
828209962Smm				zfs_error_aux(hdl,
829209962Smm				    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
830209962Smm				    propname);
831209962Smm				(void) zfs_error(hdl, EZFS_PROPREADONLY,
832209962Smm				    errbuf);
833209962Smm				goto error;
834209962Smm			}
835209962Smm
836209962Smm			if (nvpair_type(elem) == DATA_TYPE_STRING) {
837209962Smm				(void) nvpair_value_string(elem, &strval);
838209962Smm				if (strcmp(strval, "none") == 0) {
839209962Smm					intval = 0;
840209962Smm				} else if (zfs_nicestrtonum(hdl,
841209962Smm				    strval, &intval) != 0) {
842209962Smm					(void) zfs_error(hdl,
843209962Smm					    EZFS_BADPROP, errbuf);
844209962Smm					goto error;
845209962Smm				}
846209962Smm			} else if (nvpair_type(elem) ==
847209962Smm			    DATA_TYPE_UINT64) {
848209962Smm				(void) nvpair_value_uint64(elem, &intval);
849209962Smm				if (intval == 0) {
850209962Smm					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
851209962Smm					    "use 'none' to disable "
852209962Smm					    "userquota/groupquota"));
853209962Smm					goto error;
854209962Smm				}
855209962Smm			} else {
856209962Smm				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
857209962Smm				    "'%s' must be a number"), propname);
858209962Smm				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
859209962Smm				goto error;
860209962Smm			}
861209962Smm
862219089Spjd			/*
863219089Spjd			 * Encode the prop name as
864219089Spjd			 * userquota@<hex-rid>-domain, to make it easy
865219089Spjd			 * for the kernel to decode.
866219089Spjd			 */
867209962Smm			(void) snprintf(newpropname, sizeof (newpropname),
868219089Spjd			    "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype],
869219089Spjd			    (longlong_t)rid, domain);
870209962Smm			valary[0] = uqtype;
871209962Smm			valary[1] = rid;
872209962Smm			valary[2] = intval;
873209962Smm			if (nvlist_add_uint64_array(ret, newpropname,
874209962Smm			    valary, 3) != 0) {
875209962Smm				(void) no_memory(hdl);
876209962Smm				goto error;
877209962Smm			}
878209962Smm			continue;
879209962Smm		}
880209962Smm
881209962Smm		if (prop == ZPROP_INVAL) {
882209962Smm			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
883209962Smm			    "invalid property '%s'"), propname);
884209962Smm			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
885209962Smm			goto error;
886209962Smm		}
887209962Smm
888168404Spjd		if (!zfs_prop_valid_for_type(prop, type)) {
889168404Spjd			zfs_error_aux(hdl,
890168404Spjd			    dgettext(TEXT_DOMAIN, "'%s' does not "
891168404Spjd			    "apply to datasets of this type"), propname);
892168404Spjd			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
893168404Spjd			goto error;
894168404Spjd		}
895168404Spjd
896168404Spjd		if (zfs_prop_readonly(prop) &&
897185029Spjd		    (!zfs_prop_setonce(prop) || zhp != NULL)) {
898168404Spjd			zfs_error_aux(hdl,
899168404Spjd			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
900168404Spjd			    propname);
901168404Spjd			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
902168404Spjd			goto error;
903168404Spjd		}
904168404Spjd
905185029Spjd		if (zprop_parse_value(hdl, elem, prop, type, ret,
906185029Spjd		    &strval, &intval, errbuf) != 0)
907185029Spjd			goto error;
908185029Spjd
909168404Spjd		/*
910185029Spjd		 * Perform some additional checks for specific properties.
911168404Spjd		 */
912185029Spjd		switch (prop) {
913185029Spjd		case ZFS_PROP_VERSION:
914185029Spjd		{
915185029Spjd			int version;
916168404Spjd
917185029Spjd			if (zhp == NULL)
918185029Spjd				break;
919185029Spjd			version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
920185029Spjd			if (intval < version) {
921168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
922185029Spjd				    "Can not downgrade; already at version %u"),
923185029Spjd				    version);
924168404Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
925168404Spjd				goto error;
926168404Spjd			}
927168404Spjd			break;
928168404Spjd		}
929168404Spjd
930168404Spjd		case ZFS_PROP_RECORDSIZE:
931168404Spjd		case ZFS_PROP_VOLBLOCKSIZE:
932168404Spjd			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
933168404Spjd			if (intval < SPA_MINBLOCKSIZE ||
934168404Spjd			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
935168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
936168404Spjd				    "'%s' must be power of 2 from %u "
937168404Spjd				    "to %uk"), propname,
938168404Spjd				    (uint_t)SPA_MINBLOCKSIZE,
939168404Spjd				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
940168404Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
941168404Spjd				goto error;
942168404Spjd			}
943168404Spjd			break;
944168404Spjd
945219089Spjd		case ZFS_PROP_MLSLABEL:
946219089Spjd		{
947219089Spjd#ifdef sun
948219089Spjd			/*
949219089Spjd			 * Verify the mlslabel string and convert to
950219089Spjd			 * internal hex label string.
951219089Spjd			 */
952219089Spjd
953219089Spjd			m_label_t *new_sl;
954219089Spjd			char *hex = NULL;	/* internal label string */
955219089Spjd
956219089Spjd			/* Default value is already OK. */
957219089Spjd			if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
958219089Spjd				break;
959219089Spjd
960219089Spjd			/* Verify the label can be converted to binary form */
961219089Spjd			if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||
962219089Spjd			    (str_to_label(strval, &new_sl, MAC_LABEL,
963219089Spjd			    L_NO_CORRECTION, NULL) == -1)) {
964219089Spjd				goto badlabel;
965168404Spjd			}
966168404Spjd
967219089Spjd			/* Now translate to hex internal label string */
968219089Spjd			if (label_to_str(new_sl, &hex, M_INTERNAL,
969219089Spjd			    DEF_NAMES) != 0) {
970219089Spjd				if (hex)
971219089Spjd					free(hex);
972219089Spjd				goto badlabel;
973219089Spjd			}
974219089Spjd			m_label_free(new_sl);
975219089Spjd
976219089Spjd			/* If string is already in internal form, we're done. */
977219089Spjd			if (strcmp(strval, hex) == 0) {
978219089Spjd				free(hex);
979219089Spjd				break;
980219089Spjd			}
981219089Spjd
982219089Spjd			/* Replace the label string with the internal form. */
983219089Spjd			(void) nvlist_remove(ret, zfs_prop_to_name(prop),
984219089Spjd			    DATA_TYPE_STRING);
985219089Spjd			verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
986219089Spjd			    hex) == 0);
987219089Spjd			free(hex);
988219089Spjd
989168404Spjd			break;
990168404Spjd
991219089Spjdbadlabel:
992219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
993219089Spjd			    "invalid mlslabel '%s'"), strval);
994219089Spjd			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
995219089Spjd			m_label_free(new_sl);	/* OK if null */
996219089Spjd#else	/* !sun */
997219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
998219089Spjd			    "mlslabel is not supported on FreeBSD"));
999219089Spjd			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1000219089Spjd#endif	/* !sun */
1001219089Spjd			goto error;
1002219089Spjd
1003219089Spjd		}
1004219089Spjd
1005168404Spjd		case ZFS_PROP_MOUNTPOINT:
1006185029Spjd		{
1007185029Spjd			namecheck_err_t why;
1008185029Spjd
1009168404Spjd			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
1010168404Spjd			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
1011168404Spjd				break;
1012168404Spjd
1013185029Spjd			if (mountpoint_namecheck(strval, &why)) {
1014185029Spjd				switch (why) {
1015185029Spjd				case NAME_ERR_LEADING_SLASH:
1016185029Spjd					zfs_error_aux(hdl,
1017185029Spjd					    dgettext(TEXT_DOMAIN,
1018185029Spjd					    "'%s' must be an absolute path, "
1019185029Spjd					    "'none', or 'legacy'"), propname);
1020185029Spjd					break;
1021185029Spjd				case NAME_ERR_TOOLONG:
1022185029Spjd					zfs_error_aux(hdl,
1023185029Spjd					    dgettext(TEXT_DOMAIN,
1024185029Spjd					    "component of '%s' is too long"),
1025185029Spjd					    propname);
1026185029Spjd					break;
1027185029Spjd				}
1028168404Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1029168404Spjd				goto error;
1030168404Spjd			}
1031185029Spjd		}
1032185029Spjd
1033168404Spjd			/*FALLTHRU*/
1034168404Spjd
1035185029Spjd		case ZFS_PROP_SHARESMB:
1036168404Spjd		case ZFS_PROP_SHARENFS:
1037168404Spjd			/*
1038185029Spjd			 * For the mountpoint and sharenfs or sharesmb
1039185029Spjd			 * properties, check if it can be set in a
1040185029Spjd			 * global/non-global zone based on
1041168404Spjd			 * the zoned property value:
1042168404Spjd			 *
1043168404Spjd			 *		global zone	    non-global zone
1044168404Spjd			 * --------------------------------------------------
1045168404Spjd			 * zoned=on	mountpoint (no)	    mountpoint (yes)
1046168404Spjd			 *		sharenfs (no)	    sharenfs (no)
1047185029Spjd			 *		sharesmb (no)	    sharesmb (no)
1048168404Spjd			 *
1049168404Spjd			 * zoned=off	mountpoint (yes)	N/A
1050168404Spjd			 *		sharenfs (yes)
1051185029Spjd			 *		sharesmb (yes)
1052168404Spjd			 */
1053168404Spjd			if (zoned) {
1054168404Spjd				if (getzoneid() == GLOBAL_ZONEID) {
1055168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1056168404Spjd					    "'%s' cannot be set on "
1057168404Spjd					    "dataset in a non-global zone"),
1058168404Spjd					    propname);
1059168404Spjd					(void) zfs_error(hdl, EZFS_ZONED,
1060168404Spjd					    errbuf);
1061168404Spjd					goto error;
1062185029Spjd				} else if (prop == ZFS_PROP_SHARENFS ||
1063185029Spjd				    prop == ZFS_PROP_SHARESMB) {
1064168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1065168404Spjd					    "'%s' cannot be set in "
1066168404Spjd					    "a non-global zone"), propname);
1067168404Spjd					(void) zfs_error(hdl, EZFS_ZONED,
1068168404Spjd					    errbuf);
1069168404Spjd					goto error;
1070168404Spjd				}
1071168404Spjd			} else if (getzoneid() != GLOBAL_ZONEID) {
1072168404Spjd				/*
1073168404Spjd				 * If zoned property is 'off', this must be in
1074209962Smm				 * a global zone. If not, something is wrong.
1075168404Spjd				 */
1076168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1077168404Spjd				    "'%s' cannot be set while dataset "
1078168404Spjd				    "'zoned' property is set"), propname);
1079168404Spjd				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
1080168404Spjd				goto error;
1081168404Spjd			}
1082168404Spjd
1083168404Spjd			/*
1084185029Spjd			 * At this point, it is legitimate to set the
1085185029Spjd			 * property. Now we want to make sure that the
1086185029Spjd			 * property value is valid if it is sharenfs.
1087168404Spjd			 */
1088185029Spjd			if ((prop == ZFS_PROP_SHARENFS ||
1089185029Spjd			    prop == ZFS_PROP_SHARESMB) &&
1090185029Spjd			    strcmp(strval, "on") != 0 &&
1091185029Spjd			    strcmp(strval, "off") != 0) {
1092185029Spjd				zfs_share_proto_t proto;
1093168404Spjd
1094185029Spjd				if (prop == ZFS_PROP_SHARESMB)
1095185029Spjd					proto = PROTO_SMB;
1096185029Spjd				else
1097185029Spjd					proto = PROTO_NFS;
1098185029Spjd
1099185029Spjd				/*
1100185029Spjd				 * Must be an valid sharing protocol
1101185029Spjd				 * option string so init the libshare
1102185029Spjd				 * in order to enable the parser and
1103185029Spjd				 * then parse the options. We use the
1104185029Spjd				 * control API since we don't care about
1105185029Spjd				 * the current configuration and don't
1106185029Spjd				 * want the overhead of loading it
1107185029Spjd				 * until we actually do something.
1108185029Spjd				 */
1109185029Spjd
1110185029Spjd				if (zfs_init_libshare(hdl,
1111185029Spjd				    SA_INIT_CONTROL_API) != SA_OK) {
1112185029Spjd					/*
1113185029Spjd					 * An error occurred so we can't do
1114185029Spjd					 * anything
1115185029Spjd					 */
1116185029Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1117185029Spjd					    "'%s' cannot be set: problem "
1118185029Spjd					    "in share initialization"),
1119185029Spjd					    propname);
1120185029Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1121185029Spjd					    errbuf);
1122185029Spjd					goto error;
1123185029Spjd				}
1124185029Spjd
1125185029Spjd				if (zfs_parse_options(strval, proto) != SA_OK) {
1126185029Spjd					/*
1127185029Spjd					 * There was an error in parsing so
1128185029Spjd					 * deal with it by issuing an error
1129185029Spjd					 * message and leaving after
1130185029Spjd					 * uninitializing the the libshare
1131185029Spjd					 * interface.
1132185029Spjd					 */
1133185029Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1134185029Spjd					    "'%s' cannot be set to invalid "
1135185029Spjd					    "options"), propname);
1136185029Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1137185029Spjd					    errbuf);
1138185029Spjd					zfs_uninit_libshare(hdl);
1139185029Spjd					goto error;
1140185029Spjd				}
1141185029Spjd				zfs_uninit_libshare(hdl);
1142168404Spjd			}
1143185029Spjd
1144168404Spjd			break;
1145185029Spjd		case ZFS_PROP_UTF8ONLY:
1146185029Spjd			chosen_utf = (int)intval;
1147185029Spjd			break;
1148185029Spjd		case ZFS_PROP_NORMALIZE:
1149185029Spjd			chosen_normal = (int)intval;
1150185029Spjd			break;
1151168404Spjd		}
1152168404Spjd
1153168404Spjd		/*
1154168404Spjd		 * For changes to existing volumes, we have some additional
1155168404Spjd		 * checks to enforce.
1156168404Spjd		 */
1157168404Spjd		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1158168404Spjd			uint64_t volsize = zfs_prop_get_int(zhp,
1159168404Spjd			    ZFS_PROP_VOLSIZE);
1160168404Spjd			uint64_t blocksize = zfs_prop_get_int(zhp,
1161168404Spjd			    ZFS_PROP_VOLBLOCKSIZE);
1162168404Spjd			char buf[64];
1163168404Spjd
1164168404Spjd			switch (prop) {
1165168404Spjd			case ZFS_PROP_RESERVATION:
1166185029Spjd			case ZFS_PROP_REFRESERVATION:
1167168404Spjd				if (intval > volsize) {
1168168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1169168404Spjd					    "'%s' is greater than current "
1170168404Spjd					    "volume size"), propname);
1171168404Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1172168404Spjd					    errbuf);
1173168404Spjd					goto error;
1174168404Spjd				}
1175168404Spjd				break;
1176168404Spjd
1177168404Spjd			case ZFS_PROP_VOLSIZE:
1178168404Spjd				if (intval % blocksize != 0) {
1179168404Spjd					zfs_nicenum(blocksize, buf,
1180168404Spjd					    sizeof (buf));
1181168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1182168404Spjd					    "'%s' must be a multiple of "
1183168404Spjd					    "volume block size (%s)"),
1184168404Spjd					    propname, buf);
1185168404Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1186168404Spjd					    errbuf);
1187168404Spjd					goto error;
1188168404Spjd				}
1189168404Spjd
1190168404Spjd				if (intval == 0) {
1191168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1192168404Spjd					    "'%s' cannot be zero"),
1193168404Spjd					    propname);
1194168404Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1195168404Spjd					    errbuf);
1196168404Spjd					goto error;
1197168404Spjd				}
1198168404Spjd				break;
1199168404Spjd			}
1200168404Spjd		}
1201168404Spjd	}
1202168404Spjd
1203168404Spjd	/*
1204185029Spjd	 * If normalization was chosen, but no UTF8 choice was made,
1205185029Spjd	 * enforce rejection of non-UTF8 names.
1206185029Spjd	 *
1207185029Spjd	 * If normalization was chosen, but rejecting non-UTF8 names
1208185029Spjd	 * was explicitly not chosen, it is an error.
1209185029Spjd	 */
1210185029Spjd	if (chosen_normal > 0 && chosen_utf < 0) {
1211185029Spjd		if (nvlist_add_uint64(ret,
1212185029Spjd		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
1213185029Spjd			(void) no_memory(hdl);
1214185029Spjd			goto error;
1215185029Spjd		}
1216185029Spjd	} else if (chosen_normal > 0 && chosen_utf == 0) {
1217185029Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1218185029Spjd		    "'%s' must be set 'on' if normalization chosen"),
1219185029Spjd		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1220185029Spjd		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1221185029Spjd		goto error;
1222185029Spjd	}
1223219089Spjd	return (ret);
1224185029Spjd
1225219089Spjderror:
1226219089Spjd	nvlist_free(ret);
1227219089Spjd	return (NULL);
1228219089Spjd}
1229219089Spjd
1230219089Spjdint
1231219089Spjdzfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
1232219089Spjd{
1233219089Spjd	uint64_t old_volsize;
1234219089Spjd	uint64_t new_volsize;
1235219089Spjd	uint64_t old_reservation;
1236219089Spjd	uint64_t new_reservation;
1237219089Spjd	zfs_prop_t resv_prop;
1238219089Spjd
1239185029Spjd	/*
1240168404Spjd	 * If this is an existing volume, and someone is setting the volsize,
1241168404Spjd	 * make sure that it matches the reservation, or add it if necessary.
1242168404Spjd	 */
1243219089Spjd	old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
1244219089Spjd	if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1245219089Spjd		return (-1);
1246219089Spjd	old_reservation = zfs_prop_get_int(zhp, resv_prop);
1247219089Spjd	if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) !=
1248219089Spjd	    old_reservation) || nvlist_lookup_uint64(nvl,
1249219089Spjd	    zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) {
1250219089Spjd		return (0);
1251219089Spjd	}
1252219089Spjd	if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1253219089Spjd	    &new_volsize) != 0)
1254219089Spjd		return (-1);
1255219089Spjd	new_reservation = zvol_volsize_to_reservation(new_volsize,
1256219089Spjd	    zhp->zfs_props);
1257219089Spjd	if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
1258219089Spjd	    new_reservation) != 0) {
1259219089Spjd		(void) no_memory(zhp->zfs_hdl);
1260219089Spjd		return (-1);
1261219089Spjd	}
1262219089Spjd	return (1);
1263219089Spjd}
1264168404Spjd
1265219089Spjdvoid
1266219089Spjdzfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
1267219089Spjd    char *errbuf)
1268219089Spjd{
1269219089Spjd	switch (err) {
1270185029Spjd
1271219089Spjd	case ENOSPC:
1272219089Spjd		/*
1273219089Spjd		 * For quotas and reservations, ENOSPC indicates
1274219089Spjd		 * something different; setting a quota or reservation
1275219089Spjd		 * doesn't use any disk space.
1276219089Spjd		 */
1277219089Spjd		switch (prop) {
1278219089Spjd		case ZFS_PROP_QUOTA:
1279219089Spjd		case ZFS_PROP_REFQUOTA:
1280219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1281219089Spjd			    "size is less than current used or "
1282219089Spjd			    "reserved space"));
1283219089Spjd			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1284219089Spjd			break;
1285219089Spjd
1286219089Spjd		case ZFS_PROP_RESERVATION:
1287219089Spjd		case ZFS_PROP_REFRESERVATION:
1288219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1289219089Spjd			    "size is greater than available space"));
1290219089Spjd			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1291219089Spjd			break;
1292219089Spjd
1293219089Spjd		default:
1294219089Spjd			(void) zfs_standard_error(hdl, err, errbuf);
1295219089Spjd			break;
1296168404Spjd		}
1297219089Spjd		break;
1298219089Spjd
1299219089Spjd	case EBUSY:
1300219089Spjd		(void) zfs_standard_error(hdl, EBUSY, errbuf);
1301219089Spjd		break;
1302219089Spjd
1303219089Spjd	case EROFS:
1304219089Spjd		(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
1305219089Spjd		break;
1306219089Spjd
1307219089Spjd	case ENOTSUP:
1308219089Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1309219089Spjd		    "pool and or dataset must be upgraded to set this "
1310219089Spjd		    "property or value"));
1311219089Spjd		(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
1312219089Spjd		break;
1313219089Spjd
1314219089Spjd	case ERANGE:
1315219089Spjd		if (prop == ZFS_PROP_COMPRESSION) {
1316219089Spjd			(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1317219089Spjd			    "property setting is not allowed on "
1318219089Spjd			    "bootable datasets"));
1319219089Spjd			(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
1320219089Spjd		} else {
1321219089Spjd			(void) zfs_standard_error(hdl, err, errbuf);
1322219089Spjd		}
1323219089Spjd		break;
1324219089Spjd
1325219089Spjd	case EINVAL:
1326219089Spjd		if (prop == ZPROP_INVAL) {
1327219089Spjd			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1328219089Spjd		} else {
1329219089Spjd			(void) zfs_standard_error(hdl, err, errbuf);
1330219089Spjd		}
1331219089Spjd		break;
1332219089Spjd
1333219089Spjd	case EOVERFLOW:
1334219089Spjd		/*
1335219089Spjd		 * This platform can't address a volume this big.
1336219089Spjd		 */
1337219089Spjd#ifdef _ILP32
1338219089Spjd		if (prop == ZFS_PROP_VOLSIZE) {
1339219089Spjd			(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1340219089Spjd			break;
1341219089Spjd		}
1342219089Spjd#endif
1343219089Spjd		/* FALLTHROUGH */
1344219089Spjd	default:
1345219089Spjd		(void) zfs_standard_error(hdl, err, errbuf);
1346168404Spjd	}
1347168404Spjd}
1348168404Spjd
1349168404Spjd/*
1350168404Spjd * Given a property name and value, set the property for the given dataset.
1351168404Spjd */
1352168404Spjdint
1353168404Spjdzfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1354168404Spjd{
1355168404Spjd	zfs_cmd_t zc = { 0 };
1356168404Spjd	int ret = -1;
1357168404Spjd	prop_changelist_t *cl = NULL;
1358168404Spjd	char errbuf[1024];
1359168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
1360168404Spjd	nvlist_t *nvl = NULL, *realprops;
1361168404Spjd	zfs_prop_t prop;
1362185029Spjd	boolean_t do_prefix;
1363185029Spjd	uint64_t idx;
1364219089Spjd	int added_resv;
1365168404Spjd
1366168404Spjd	(void) snprintf(errbuf, sizeof (errbuf),
1367168404Spjd	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
1368168404Spjd	    zhp->zfs_name);
1369168404Spjd
1370168404Spjd	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1371168404Spjd	    nvlist_add_string(nvl, propname, propval) != 0) {
1372168404Spjd		(void) no_memory(hdl);
1373168404Spjd		goto error;
1374168404Spjd	}
1375168404Spjd
1376185029Spjd	if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
1377168404Spjd	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1378168404Spjd		goto error;
1379185029Spjd
1380168404Spjd	nvlist_free(nvl);
1381168404Spjd	nvl = realprops;
1382168404Spjd
1383168404Spjd	prop = zfs_name_to_prop(propname);
1384168404Spjd
1385168404Spjd	/* We don't support those properties on FreeBSD. */
1386168404Spjd	switch (prop) {
1387197867Strasz	case ZFS_PROP_DEVICES:
1388168404Spjd	case ZFS_PROP_ISCSIOPTIONS:
1389197867Strasz	case ZFS_PROP_XATTR:
1390197867Strasz	case ZFS_PROP_VSCAN:
1391197867Strasz	case ZFS_PROP_NBMAND:
1392219089Spjd	case ZFS_PROP_MLSLABEL:
1393168404Spjd		(void) snprintf(errbuf, sizeof (errbuf),
1394168404Spjd		    "property '%s' not supported on FreeBSD", propname);
1395168404Spjd		ret = zfs_error(hdl, EZFS_PERM, errbuf);
1396168404Spjd		goto error;
1397168404Spjd	}
1398168404Spjd
1399219089Spjd	if (prop == ZFS_PROP_VOLSIZE) {
1400219089Spjd		if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1)
1401219089Spjd			goto error;
1402219089Spjd	}
1403219089Spjd
1404185029Spjd	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1405168404Spjd		goto error;
1406168404Spjd
1407168404Spjd	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1408168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1409168404Spjd		    "child dataset with inherited mountpoint is used "
1410168404Spjd		    "in a non-global zone"));
1411168404Spjd		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1412168404Spjd		goto error;
1413168404Spjd	}
1414168404Spjd
1415185029Spjd	/*
1416185029Spjd	 * If the dataset's canmount property is being set to noauto,
1417185029Spjd	 * then we want to prevent unmounting & remounting it.
1418185029Spjd	 */
1419185029Spjd	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
1420185029Spjd	    (zprop_string_to_index(prop, propval, &idx,
1421185029Spjd	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
1422185029Spjd
1423185029Spjd	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
1424168404Spjd		goto error;
1425168404Spjd
1426168404Spjd	/*
1427168404Spjd	 * Execute the corresponding ioctl() to set this property.
1428168404Spjd	 */
1429168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1430168404Spjd
1431185029Spjd	if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1432168404Spjd		goto error;
1433168404Spjd
1434185029Spjd	ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1435209962Smm
1436168404Spjd	if (ret != 0) {
1437219089Spjd		zfs_setprop_error(hdl, prop, errno, errbuf);
1438219089Spjd		if (added_resv && errno == ENOSPC) {
1439219089Spjd			/* clean up the volsize property we tried to set */
1440219089Spjd			uint64_t old_volsize = zfs_prop_get_int(zhp,
1441219089Spjd			    ZFS_PROP_VOLSIZE);
1442219089Spjd			nvlist_free(nvl);
1443219089Spjd			zcmd_free_nvlists(&zc);
1444219089Spjd			if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
1445219089Spjd				goto error;
1446219089Spjd			if (nvlist_add_uint64(nvl,
1447219089Spjd			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1448219089Spjd			    old_volsize) != 0)
1449219089Spjd				goto error;
1450219089Spjd			if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1451219089Spjd				goto error;
1452219089Spjd			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1453168404Spjd		}
1454168404Spjd	} else {
1455185029Spjd		if (do_prefix)
1456185029Spjd			ret = changelist_postfix(cl);
1457185029Spjd
1458168404Spjd		/*
1459168404Spjd		 * Refresh the statistics so the new property value
1460168404Spjd		 * is reflected.
1461168404Spjd		 */
1462185029Spjd		if (ret == 0)
1463168404Spjd			(void) get_stats(zhp);
1464168404Spjd	}
1465168404Spjd
1466168404Spjderror:
1467168404Spjd	nvlist_free(nvl);
1468168404Spjd	zcmd_free_nvlists(&zc);
1469168404Spjd	if (cl)
1470168404Spjd		changelist_free(cl);
1471168404Spjd	return (ret);
1472168404Spjd}
1473168404Spjd
1474168404Spjd/*
1475219089Spjd * Given a property, inherit the value from the parent dataset, or if received
1476219089Spjd * is TRUE, revert to the received value, if any.
1477168404Spjd */
1478168404Spjdint
1479219089Spjdzfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
1480168404Spjd{
1481168404Spjd	zfs_cmd_t zc = { 0 };
1482168404Spjd	int ret;
1483168404Spjd	prop_changelist_t *cl;
1484168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
1485168404Spjd	char errbuf[1024];
1486168404Spjd	zfs_prop_t prop;
1487168404Spjd
1488168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
1489168404Spjd	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1490168404Spjd
1491219089Spjd	zc.zc_cookie = received;
1492185029Spjd	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
1493168404Spjd		/*
1494168404Spjd		 * For user properties, the amount of work we have to do is very
1495168404Spjd		 * small, so just do it here.
1496168404Spjd		 */
1497168404Spjd		if (!zfs_prop_user(propname)) {
1498168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1499168404Spjd			    "invalid property"));
1500168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1501168404Spjd		}
1502168404Spjd
1503168404Spjd		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1504168404Spjd		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1505168404Spjd
1506185029Spjd		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
1507168404Spjd			return (zfs_standard_error(hdl, errno, errbuf));
1508168404Spjd
1509168404Spjd		return (0);
1510168404Spjd	}
1511168404Spjd
1512168404Spjd	/*
1513168404Spjd	 * Verify that this property is inheritable.
1514168404Spjd	 */
1515168404Spjd	if (zfs_prop_readonly(prop))
1516168404Spjd		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1517168404Spjd
1518219089Spjd	if (!zfs_prop_inheritable(prop) && !received)
1519168404Spjd		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1520168404Spjd
1521168404Spjd	/*
1522168404Spjd	 * Check to see if the value applies to this type
1523168404Spjd	 */
1524168404Spjd	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1525168404Spjd		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1526168404Spjd
1527168404Spjd	/*
1528219089Spjd	 * Normalize the name, to get rid of shorthand abbreviations.
1529168404Spjd	 */
1530168404Spjd	propname = zfs_prop_to_name(prop);
1531168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1532168404Spjd	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1533168404Spjd
1534168404Spjd	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1535168404Spjd	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
1536168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1537168404Spjd		    "dataset is used in a non-global zone"));
1538168404Spjd		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1539168404Spjd	}
1540168404Spjd
1541168404Spjd	/*
1542168404Spjd	 * Determine datasets which will be affected by this change, if any.
1543168404Spjd	 */
1544185029Spjd	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1545168404Spjd		return (-1);
1546168404Spjd
1547168404Spjd	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1548168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1549168404Spjd		    "child dataset with inherited mountpoint is used "
1550168404Spjd		    "in a non-global zone"));
1551168404Spjd		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1552168404Spjd		goto error;
1553168404Spjd	}
1554168404Spjd
1555168404Spjd	if ((ret = changelist_prefix(cl)) != 0)
1556168404Spjd		goto error;
1557168404Spjd
1558185029Spjd	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
1559168404Spjd		return (zfs_standard_error(hdl, errno, errbuf));
1560168404Spjd	} else {
1561168404Spjd
1562168404Spjd		if ((ret = changelist_postfix(cl)) != 0)
1563168404Spjd			goto error;
1564168404Spjd
1565168404Spjd		/*
1566168404Spjd		 * Refresh the statistics so the new property is reflected.
1567168404Spjd		 */
1568168404Spjd		(void) get_stats(zhp);
1569168404Spjd	}
1570168404Spjd
1571168404Spjderror:
1572168404Spjd	changelist_free(cl);
1573168404Spjd	return (ret);
1574168404Spjd}
1575168404Spjd
1576168404Spjd/*
1577168404Spjd * True DSL properties are stored in an nvlist.  The following two functions
1578168404Spjd * extract them appropriately.
1579168404Spjd */
1580168404Spjdstatic uint64_t
1581168404Spjdgetprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
1582168404Spjd{
1583168404Spjd	nvlist_t *nv;
1584168404Spjd	uint64_t value;
1585168404Spjd
1586168404Spjd	*source = NULL;
1587168404Spjd	if (nvlist_lookup_nvlist(zhp->zfs_props,
1588168404Spjd	    zfs_prop_to_name(prop), &nv) == 0) {
1589185029Spjd		verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
1590185029Spjd		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
1591168404Spjd	} else {
1592205198Sdelphij		verify(!zhp->zfs_props_table ||
1593205198Sdelphij		    zhp->zfs_props_table[prop] == B_TRUE);
1594168404Spjd		value = zfs_prop_default_numeric(prop);
1595168404Spjd		*source = "";
1596168404Spjd	}
1597168404Spjd
1598168404Spjd	return (value);
1599168404Spjd}
1600168404Spjd
1601168404Spjdstatic char *
1602168404Spjdgetprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
1603168404Spjd{
1604168404Spjd	nvlist_t *nv;
1605168404Spjd	char *value;
1606168404Spjd
1607168404Spjd	*source = NULL;
1608168404Spjd	if (nvlist_lookup_nvlist(zhp->zfs_props,
1609168404Spjd	    zfs_prop_to_name(prop), &nv) == 0) {
1610185029Spjd		verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
1611185029Spjd		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
1612168404Spjd	} else {
1613205198Sdelphij		verify(!zhp->zfs_props_table ||
1614205198Sdelphij		    zhp->zfs_props_table[prop] == B_TRUE);
1615168404Spjd		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
1616168404Spjd			value = "";
1617168404Spjd		*source = "";
1618168404Spjd	}
1619168404Spjd
1620168404Spjd	return (value);
1621168404Spjd}
1622168404Spjd
1623219089Spjdstatic boolean_t
1624219089Spjdzfs_is_recvd_props_mode(zfs_handle_t *zhp)
1625219089Spjd{
1626219089Spjd	return (zhp->zfs_props == zhp->zfs_recvd_props);
1627219089Spjd}
1628219089Spjd
1629219089Spjdstatic void
1630219089Spjdzfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
1631219089Spjd{
1632219089Spjd	*cookie = (uint64_t)(uintptr_t)zhp->zfs_props;
1633219089Spjd	zhp->zfs_props = zhp->zfs_recvd_props;
1634219089Spjd}
1635219089Spjd
1636219089Spjdstatic void
1637219089Spjdzfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
1638219089Spjd{
1639219089Spjd	zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie;
1640219089Spjd	*cookie = 0;
1641219089Spjd}
1642219089Spjd
1643168404Spjd/*
1644168404Spjd * Internal function for getting a numeric property.  Both zfs_prop_get() and
1645168404Spjd * zfs_prop_get_int() are built using this interface.
1646168404Spjd *
1647168404Spjd * Certain properties can be overridden using 'mount -o'.  In this case, scan
1648168404Spjd * the contents of the /etc/mnttab entry, searching for the appropriate options.
1649168404Spjd * If they differ from the on-disk values, report the current values and mark
1650168404Spjd * the source "temporary".
1651168404Spjd */
1652168404Spjdstatic int
1653185029Spjdget_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
1654168404Spjd    char **source, uint64_t *val)
1655168404Spjd{
1656185029Spjd	zfs_cmd_t zc = { 0 };
1657185029Spjd	nvlist_t *zplprops = NULL;
1658168404Spjd	struct mnttab mnt;
1659168404Spjd	char *mntopt_on = NULL;
1660168404Spjd	char *mntopt_off = NULL;
1661219089Spjd	boolean_t received = zfs_is_recvd_props_mode(zhp);
1662168404Spjd
1663168404Spjd	*source = NULL;
1664168404Spjd
1665168404Spjd	switch (prop) {
1666168404Spjd	case ZFS_PROP_ATIME:
1667168404Spjd		mntopt_on = MNTOPT_ATIME;
1668168404Spjd		mntopt_off = MNTOPT_NOATIME;
1669168404Spjd		break;
1670168404Spjd
1671168404Spjd	case ZFS_PROP_DEVICES:
1672168404Spjd		mntopt_on = MNTOPT_DEVICES;
1673168404Spjd		mntopt_off = MNTOPT_NODEVICES;
1674168404Spjd		break;
1675168404Spjd
1676168404Spjd	case ZFS_PROP_EXEC:
1677168404Spjd		mntopt_on = MNTOPT_EXEC;
1678168404Spjd		mntopt_off = MNTOPT_NOEXEC;
1679168404Spjd		break;
1680168404Spjd
1681168404Spjd	case ZFS_PROP_READONLY:
1682168404Spjd		mntopt_on = MNTOPT_RO;
1683168404Spjd		mntopt_off = MNTOPT_RW;
1684168404Spjd		break;
1685168404Spjd
1686168404Spjd	case ZFS_PROP_SETUID:
1687168404Spjd		mntopt_on = MNTOPT_SETUID;
1688168404Spjd		mntopt_off = MNTOPT_NOSETUID;
1689168404Spjd		break;
1690168404Spjd
1691168404Spjd	case ZFS_PROP_XATTR:
1692168404Spjd		mntopt_on = MNTOPT_XATTR;
1693168404Spjd		mntopt_off = MNTOPT_NOXATTR;
1694168404Spjd		break;
1695185029Spjd
1696185029Spjd	case ZFS_PROP_NBMAND:
1697185029Spjd		mntopt_on = MNTOPT_NBMAND;
1698185029Spjd		mntopt_off = MNTOPT_NONBMAND;
1699185029Spjd		break;
1700168404Spjd	}
1701168404Spjd
1702168404Spjd	/*
1703168404Spjd	 * Because looking up the mount options is potentially expensive
1704168404Spjd	 * (iterating over all of /etc/mnttab), we defer its calculation until
1705168404Spjd	 * we're looking up a property which requires its presence.
1706168404Spjd	 */
1707168404Spjd	if (!zhp->zfs_mntcheck &&
1708168404Spjd	    (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
1709209962Smm		libzfs_handle_t *hdl = zhp->zfs_hdl;
1710209962Smm		struct mnttab entry;
1711168404Spjd
1712209962Smm		if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
1713209962Smm			zhp->zfs_mntopts = zfs_strdup(hdl,
1714168404Spjd			    entry.mnt_mntopts);
1715168404Spjd			if (zhp->zfs_mntopts == NULL)
1716168404Spjd				return (-1);
1717168404Spjd		}
1718168404Spjd
1719168404Spjd		zhp->zfs_mntcheck = B_TRUE;
1720168404Spjd	}
1721168404Spjd
1722168404Spjd	if (zhp->zfs_mntopts == NULL)
1723168404Spjd		mnt.mnt_mntopts = "";
1724168404Spjd	else
1725168404Spjd		mnt.mnt_mntopts = zhp->zfs_mntopts;
1726168404Spjd
1727168404Spjd	switch (prop) {
1728168404Spjd	case ZFS_PROP_ATIME:
1729168404Spjd	case ZFS_PROP_DEVICES:
1730168404Spjd	case ZFS_PROP_EXEC:
1731168404Spjd	case ZFS_PROP_READONLY:
1732168404Spjd	case ZFS_PROP_SETUID:
1733168404Spjd	case ZFS_PROP_XATTR:
1734185029Spjd	case ZFS_PROP_NBMAND:
1735168404Spjd		*val = getprop_uint64(zhp, prop, source);
1736168404Spjd
1737219089Spjd		if (received)
1738219089Spjd			break;
1739219089Spjd
1740168404Spjd		if (hasmntopt(&mnt, mntopt_on) && !*val) {
1741168404Spjd			*val = B_TRUE;
1742168404Spjd			if (src)
1743185029Spjd				*src = ZPROP_SRC_TEMPORARY;
1744168404Spjd		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
1745168404Spjd			*val = B_FALSE;
1746168404Spjd			if (src)
1747185029Spjd				*src = ZPROP_SRC_TEMPORARY;
1748168404Spjd		}
1749168404Spjd		break;
1750168404Spjd
1751168404Spjd	case ZFS_PROP_CANMOUNT:
1752219089Spjd	case ZFS_PROP_VOLSIZE:
1753168404Spjd	case ZFS_PROP_QUOTA:
1754185029Spjd	case ZFS_PROP_REFQUOTA:
1755168404Spjd	case ZFS_PROP_RESERVATION:
1756185029Spjd	case ZFS_PROP_REFRESERVATION:
1757168404Spjd		*val = getprop_uint64(zhp, prop, source);
1758219089Spjd
1759219089Spjd		if (*source == NULL) {
1760219089Spjd			/* not default, must be local */
1761168404Spjd			*source = zhp->zfs_name;
1762219089Spjd		}
1763168404Spjd		break;
1764168404Spjd
1765168404Spjd	case ZFS_PROP_MOUNTED:
1766168404Spjd		*val = (zhp->zfs_mntopts != NULL);
1767168404Spjd		break;
1768168404Spjd
1769168404Spjd	case ZFS_PROP_NUMCLONES:
1770168404Spjd		*val = zhp->zfs_dmustats.dds_num_clones;
1771168404Spjd		break;
1772168404Spjd
1773185029Spjd	case ZFS_PROP_VERSION:
1774185029Spjd	case ZFS_PROP_NORMALIZE:
1775185029Spjd	case ZFS_PROP_UTF8ONLY:
1776185029Spjd	case ZFS_PROP_CASE:
1777185029Spjd		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
1778185029Spjd		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
1779185029Spjd			return (-1);
1780185029Spjd		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1781185029Spjd		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
1782185029Spjd			zcmd_free_nvlists(&zc);
1783219089Spjd			return (-1);
1784185029Spjd		}
1785185029Spjd		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
1786185029Spjd		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
1787185029Spjd		    val) != 0) {
1788185029Spjd			zcmd_free_nvlists(&zc);
1789219089Spjd			return (-1);
1790185029Spjd		}
1791185029Spjd		if (zplprops)
1792185029Spjd			nvlist_free(zplprops);
1793185029Spjd		zcmd_free_nvlists(&zc);
1794185029Spjd		break;
1795185029Spjd
1796168404Spjd	default:
1797185029Spjd		switch (zfs_prop_get_type(prop)) {
1798185029Spjd		case PROP_TYPE_NUMBER:
1799185029Spjd		case PROP_TYPE_INDEX:
1800185029Spjd			*val = getprop_uint64(zhp, prop, source);
1801185029Spjd			/*
1802209962Smm			 * If we tried to use a default value for a
1803185029Spjd			 * readonly property, it means that it was not
1804219089Spjd			 * present.
1805185029Spjd			 */
1806185029Spjd			if (zfs_prop_readonly(prop) &&
1807219089Spjd			    *source != NULL && (*source)[0] == '\0') {
1808219089Spjd				*source = NULL;
1809185029Spjd			}
1810185029Spjd			break;
1811185029Spjd
1812185029Spjd		case PROP_TYPE_STRING:
1813185029Spjd		default:
1814185029Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1815185029Spjd			    "cannot get non-numeric property"));
1816185029Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
1817185029Spjd			    dgettext(TEXT_DOMAIN, "internal error")));
1818185029Spjd		}
1819168404Spjd	}
1820168404Spjd
1821168404Spjd	return (0);
1822168404Spjd}
1823168404Spjd
1824168404Spjd/*
1825168404Spjd * Calculate the source type, given the raw source string.
1826168404Spjd */
1827168404Spjdstatic void
1828185029Spjdget_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
1829168404Spjd    char *statbuf, size_t statlen)
1830168404Spjd{
1831185029Spjd	if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
1832168404Spjd		return;
1833168404Spjd
1834168404Spjd	if (source == NULL) {
1835185029Spjd		*srctype = ZPROP_SRC_NONE;
1836168404Spjd	} else if (source[0] == '\0') {
1837185029Spjd		*srctype = ZPROP_SRC_DEFAULT;
1838219089Spjd	} else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) {
1839219089Spjd		*srctype = ZPROP_SRC_RECEIVED;
1840168404Spjd	} else {
1841168404Spjd		if (strcmp(source, zhp->zfs_name) == 0) {
1842185029Spjd			*srctype = ZPROP_SRC_LOCAL;
1843168404Spjd		} else {
1844168404Spjd			(void) strlcpy(statbuf, source, statlen);
1845185029Spjd			*srctype = ZPROP_SRC_INHERITED;
1846168404Spjd		}
1847168404Spjd	}
1848168404Spjd
1849168404Spjd}
1850168404Spjd
1851219089Spjdint
1852219089Spjdzfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
1853219089Spjd    size_t proplen, boolean_t literal)
1854219089Spjd{
1855219089Spjd	zfs_prop_t prop;
1856219089Spjd	int err = 0;
1857219089Spjd
1858219089Spjd	if (zhp->zfs_recvd_props == NULL)
1859219089Spjd		if (get_recvd_props_ioctl(zhp) != 0)
1860219089Spjd			return (-1);
1861219089Spjd
1862219089Spjd	prop = zfs_name_to_prop(propname);
1863219089Spjd
1864219089Spjd	if (prop != ZPROP_INVAL) {
1865219089Spjd		uint64_t cookie;
1866219089Spjd		if (!nvlist_exists(zhp->zfs_recvd_props, propname))
1867219089Spjd			return (-1);
1868219089Spjd		zfs_set_recvd_props_mode(zhp, &cookie);
1869219089Spjd		err = zfs_prop_get(zhp, prop, propbuf, proplen,
1870219089Spjd		    NULL, NULL, 0, literal);
1871219089Spjd		zfs_unset_recvd_props_mode(zhp, &cookie);
1872219089Spjd	} else if (zfs_prop_userquota(propname)) {
1873219089Spjd		return (-1);
1874219089Spjd	} else {
1875219089Spjd		nvlist_t *propval;
1876219089Spjd		char *recvdval;
1877219089Spjd		if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
1878219089Spjd		    propname, &propval) != 0)
1879219089Spjd			return (-1);
1880219089Spjd		verify(nvlist_lookup_string(propval, ZPROP_VALUE,
1881219089Spjd		    &recvdval) == 0);
1882219089Spjd		(void) strlcpy(propbuf, recvdval, proplen);
1883219089Spjd	}
1884219089Spjd
1885219089Spjd	return (err == 0 ? 0 : -1);
1886219089Spjd}
1887219089Spjd
1888168404Spjd/*
1889168404Spjd * Retrieve a property from the given object.  If 'literal' is specified, then
1890168404Spjd * numbers are left as exact values.  Otherwise, numbers are converted to a
1891168404Spjd * human-readable form.
1892168404Spjd *
1893168404Spjd * Returns 0 on success, or -1 on error.
1894168404Spjd */
1895168404Spjdint
1896168404Spjdzfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
1897185029Spjd    zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1898168404Spjd{
1899168404Spjd	char *source = NULL;
1900168404Spjd	uint64_t val;
1901168404Spjd	char *str;
1902168404Spjd	const char *strval;
1903219089Spjd	boolean_t received = zfs_is_recvd_props_mode(zhp);
1904168404Spjd
1905168404Spjd	/*
1906168404Spjd	 * Check to see if this property applies to our object
1907168404Spjd	 */
1908168404Spjd	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1909168404Spjd		return (-1);
1910168404Spjd
1911219089Spjd	if (received && zfs_prop_readonly(prop))
1912219089Spjd		return (-1);
1913219089Spjd
1914168404Spjd	if (src)
1915185029Spjd		*src = ZPROP_SRC_NONE;
1916168404Spjd
1917168404Spjd	switch (prop) {
1918168404Spjd	case ZFS_PROP_CREATION:
1919168404Spjd		/*
1920168404Spjd		 * 'creation' is a time_t stored in the statistics.  We convert
1921168404Spjd		 * this into a string unless 'literal' is specified.
1922168404Spjd		 */
1923168404Spjd		{
1924168404Spjd			val = getprop_uint64(zhp, prop, &source);
1925168404Spjd			time_t time = (time_t)val;
1926168404Spjd			struct tm t;
1927168404Spjd
1928168404Spjd			if (literal ||
1929168404Spjd			    localtime_r(&time, &t) == NULL ||
1930168404Spjd			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1931168404Spjd			    &t) == 0)
1932168404Spjd				(void) snprintf(propbuf, proplen, "%llu", val);
1933168404Spjd		}
1934168404Spjd		break;
1935168404Spjd
1936168404Spjd	case ZFS_PROP_MOUNTPOINT:
1937168404Spjd		/*
1938168404Spjd		 * Getting the precise mountpoint can be tricky.
1939168404Spjd		 *
1940168404Spjd		 *  - for 'none' or 'legacy', return those values.
1941168404Spjd		 *  - for inherited mountpoints, we want to take everything
1942168404Spjd		 *    after our ancestor and append it to the inherited value.
1943168404Spjd		 *
1944168404Spjd		 * If the pool has an alternate root, we want to prepend that
1945168404Spjd		 * root to any values we return.
1946168404Spjd		 */
1947185029Spjd
1948168404Spjd		str = getprop_string(zhp, prop, &source);
1949168404Spjd
1950185029Spjd		if (str[0] == '/') {
1951185029Spjd			char buf[MAXPATHLEN];
1952185029Spjd			char *root = buf;
1953219089Spjd			const char *relpath;
1954168404Spjd
1955219089Spjd			/*
1956219089Spjd			 * If we inherit the mountpoint, even from a dataset
1957219089Spjd			 * with a received value, the source will be the path of
1958219089Spjd			 * the dataset we inherit from. If source is
1959219089Spjd			 * ZPROP_SOURCE_VAL_RECVD, the received value is not
1960219089Spjd			 * inherited.
1961219089Spjd			 */
1962219089Spjd			if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) {
1963219089Spjd				relpath = "";
1964219089Spjd			} else {
1965219089Spjd				relpath = zhp->zfs_name + strlen(source);
1966219089Spjd				if (relpath[0] == '/')
1967219089Spjd					relpath++;
1968219089Spjd			}
1969185029Spjd
1970185029Spjd			if ((zpool_get_prop(zhp->zpool_hdl,
1971185029Spjd			    ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
1972185029Spjd			    (strcmp(root, "-") == 0))
1973185029Spjd				root[0] = '\0';
1974185029Spjd			/*
1975185029Spjd			 * Special case an alternate root of '/'. This will
1976185029Spjd			 * avoid having multiple leading slashes in the
1977185029Spjd			 * mountpoint path.
1978185029Spjd			 */
1979185029Spjd			if (strcmp(root, "/") == 0)
1980185029Spjd				root++;
1981185029Spjd
1982185029Spjd			/*
1983185029Spjd			 * If the mountpoint is '/' then skip over this
1984185029Spjd			 * if we are obtaining either an alternate root or
1985185029Spjd			 * an inherited mountpoint.
1986185029Spjd			 */
1987185029Spjd			if (str[1] == '\0' && (root[0] != '\0' ||
1988185029Spjd			    relpath[0] != '\0'))
1989168404Spjd				str++;
1990168404Spjd
1991168404Spjd			if (relpath[0] == '\0')
1992168404Spjd				(void) snprintf(propbuf, proplen, "%s%s",
1993168404Spjd				    root, str);
1994168404Spjd			else
1995168404Spjd				(void) snprintf(propbuf, proplen, "%s%s%s%s",
1996168404Spjd				    root, str, relpath[0] == '@' ? "" : "/",
1997168404Spjd				    relpath);
1998168404Spjd		} else {
1999168404Spjd			/* 'legacy' or 'none' */
2000168404Spjd			(void) strlcpy(propbuf, str, proplen);
2001168404Spjd		}
2002168404Spjd
2003168404Spjd		break;
2004168404Spjd
2005168404Spjd	case ZFS_PROP_ORIGIN:
2006168404Spjd		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
2007168404Spjd		    proplen);
2008168404Spjd		/*
2009168404Spjd		 * If there is no parent at all, return failure to indicate that
2010168404Spjd		 * it doesn't apply to this dataset.
2011168404Spjd		 */
2012168404Spjd		if (propbuf[0] == '\0')
2013168404Spjd			return (-1);
2014168404Spjd		break;
2015168404Spjd
2016168404Spjd	case ZFS_PROP_QUOTA:
2017185029Spjd	case ZFS_PROP_REFQUOTA:
2018168404Spjd	case ZFS_PROP_RESERVATION:
2019185029Spjd	case ZFS_PROP_REFRESERVATION:
2020185029Spjd
2021168404Spjd		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2022168404Spjd			return (-1);
2023168404Spjd
2024168404Spjd		/*
2025168404Spjd		 * If quota or reservation is 0, we translate this into 'none'
2026168404Spjd		 * (unless literal is set), and indicate that it's the default
2027168404Spjd		 * value.  Otherwise, we print the number nicely and indicate
2028168404Spjd		 * that its set locally.
2029168404Spjd		 */
2030168404Spjd		if (val == 0) {
2031168404Spjd			if (literal)
2032168404Spjd				(void) strlcpy(propbuf, "0", proplen);
2033168404Spjd			else
2034168404Spjd				(void) strlcpy(propbuf, "none", proplen);
2035168404Spjd		} else {
2036168404Spjd			if (literal)
2037168404Spjd				(void) snprintf(propbuf, proplen, "%llu",
2038168404Spjd				    (u_longlong_t)val);
2039168404Spjd			else
2040168404Spjd				zfs_nicenum(val, propbuf, proplen);
2041168404Spjd		}
2042168404Spjd		break;
2043168404Spjd
2044223623Smm	case ZFS_PROP_REFRATIO:
2045168404Spjd	case ZFS_PROP_COMPRESSRATIO:
2046168404Spjd		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2047168404Spjd			return (-1);
2048219089Spjd		(void) snprintf(propbuf, proplen, "%llu.%02llux",
2049219089Spjd		    (u_longlong_t)(val / 100),
2050219089Spjd		    (u_longlong_t)(val % 100));
2051168404Spjd		break;
2052168404Spjd
2053168404Spjd	case ZFS_PROP_TYPE:
2054168404Spjd		switch (zhp->zfs_type) {
2055168404Spjd		case ZFS_TYPE_FILESYSTEM:
2056168404Spjd			str = "filesystem";
2057168404Spjd			break;
2058168404Spjd		case ZFS_TYPE_VOLUME:
2059168404Spjd			str = "volume";
2060168404Spjd			break;
2061168404Spjd		case ZFS_TYPE_SNAPSHOT:
2062168404Spjd			str = "snapshot";
2063168404Spjd			break;
2064168404Spjd		default:
2065168404Spjd			abort();
2066168404Spjd		}
2067168404Spjd		(void) snprintf(propbuf, proplen, "%s", str);
2068168404Spjd		break;
2069168404Spjd
2070168404Spjd	case ZFS_PROP_MOUNTED:
2071168404Spjd		/*
2072168404Spjd		 * The 'mounted' property is a pseudo-property that described
2073168404Spjd		 * whether the filesystem is currently mounted.  Even though
2074168404Spjd		 * it's a boolean value, the typical values of "on" and "off"
2075168404Spjd		 * don't make sense, so we translate to "yes" and "no".
2076168404Spjd		 */
2077168404Spjd		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
2078168404Spjd		    src, &source, &val) != 0)
2079168404Spjd			return (-1);
2080168404Spjd		if (val)
2081168404Spjd			(void) strlcpy(propbuf, "yes", proplen);
2082168404Spjd		else
2083168404Spjd			(void) strlcpy(propbuf, "no", proplen);
2084168404Spjd		break;
2085168404Spjd
2086168404Spjd	case ZFS_PROP_NAME:
2087168404Spjd		/*
2088168404Spjd		 * The 'name' property is a pseudo-property derived from the
2089168404Spjd		 * dataset name.  It is presented as a real property to simplify
2090168404Spjd		 * consumers.
2091168404Spjd		 */
2092168404Spjd		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
2093168404Spjd		break;
2094168404Spjd
2095219089Spjd	case ZFS_PROP_MLSLABEL:
2096219089Spjd		{
2097219089Spjd#ifdef sun
2098219089Spjd			m_label_t *new_sl = NULL;
2099219089Spjd			char *ascii = NULL;	/* human readable label */
2100219089Spjd
2101219089Spjd			(void) strlcpy(propbuf,
2102219089Spjd			    getprop_string(zhp, prop, &source), proplen);
2103219089Spjd
2104219089Spjd			if (literal || (strcasecmp(propbuf,
2105219089Spjd			    ZFS_MLSLABEL_DEFAULT) == 0))
2106219089Spjd				break;
2107219089Spjd
2108219089Spjd			/*
2109219089Spjd			 * Try to translate the internal hex string to
2110219089Spjd			 * human-readable output.  If there are any
2111219089Spjd			 * problems just use the hex string.
2112219089Spjd			 */
2113219089Spjd
2114219089Spjd			if (str_to_label(propbuf, &new_sl, MAC_LABEL,
2115219089Spjd			    L_NO_CORRECTION, NULL) == -1) {
2116219089Spjd				m_label_free(new_sl);
2117219089Spjd				break;
2118219089Spjd			}
2119219089Spjd
2120219089Spjd			if (label_to_str(new_sl, &ascii, M_LABEL,
2121219089Spjd			    DEF_NAMES) != 0) {
2122219089Spjd				if (ascii)
2123219089Spjd					free(ascii);
2124219089Spjd				m_label_free(new_sl);
2125219089Spjd				break;
2126219089Spjd			}
2127219089Spjd			m_label_free(new_sl);
2128219089Spjd
2129219089Spjd			(void) strlcpy(propbuf, ascii, proplen);
2130219089Spjd			free(ascii);
2131219089Spjd#else	/* !sun */
2132219089Spjd			propbuf[0] = '\0';
2133219089Spjd#endif	/* !sun */
2134219089Spjd		}
2135219089Spjd		break;
2136219089Spjd
2137168404Spjd	default:
2138185029Spjd		switch (zfs_prop_get_type(prop)) {
2139185029Spjd		case PROP_TYPE_NUMBER:
2140185029Spjd			if (get_numeric_property(zhp, prop, src,
2141185029Spjd			    &source, &val) != 0)
2142185029Spjd				return (-1);
2143185029Spjd			if (literal)
2144185029Spjd				(void) snprintf(propbuf, proplen, "%llu",
2145185029Spjd				    (u_longlong_t)val);
2146185029Spjd			else
2147185029Spjd				zfs_nicenum(val, propbuf, proplen);
2148185029Spjd			break;
2149185029Spjd
2150185029Spjd		case PROP_TYPE_STRING:
2151185029Spjd			(void) strlcpy(propbuf,
2152185029Spjd			    getprop_string(zhp, prop, &source), proplen);
2153185029Spjd			break;
2154185029Spjd
2155185029Spjd		case PROP_TYPE_INDEX:
2156185029Spjd			if (get_numeric_property(zhp, prop, src,
2157185029Spjd			    &source, &val) != 0)
2158185029Spjd				return (-1);
2159185029Spjd			if (zfs_prop_index_to_string(prop, val, &strval) != 0)
2160185029Spjd				return (-1);
2161185029Spjd			(void) strlcpy(propbuf, strval, proplen);
2162185029Spjd			break;
2163185029Spjd
2164185029Spjd		default:
2165185029Spjd			abort();
2166185029Spjd		}
2167168404Spjd	}
2168168404Spjd
2169168404Spjd	get_source(zhp, src, source, statbuf, statlen);
2170168404Spjd
2171168404Spjd	return (0);
2172168404Spjd}
2173168404Spjd
2174168404Spjd/*
2175168404Spjd * Utility function to get the given numeric property.  Does no validation that
2176168404Spjd * the given property is the appropriate type; should only be used with
2177168404Spjd * hard-coded property types.
2178168404Spjd */
2179168404Spjduint64_t
2180168404Spjdzfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
2181168404Spjd{
2182168404Spjd	char *source;
2183168404Spjd	uint64_t val;
2184168404Spjd
2185185029Spjd	(void) get_numeric_property(zhp, prop, NULL, &source, &val);
2186168404Spjd
2187168404Spjd	return (val);
2188168404Spjd}
2189168404Spjd
2190185029Spjdint
2191185029Spjdzfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
2192185029Spjd{
2193185029Spjd	char buf[64];
2194185029Spjd
2195209962Smm	(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
2196185029Spjd	return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
2197185029Spjd}
2198185029Spjd
2199168404Spjd/*
2200168404Spjd * Similar to zfs_prop_get(), but returns the value as an integer.
2201168404Spjd */
2202168404Spjdint
2203168404Spjdzfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
2204185029Spjd    zprop_source_t *src, char *statbuf, size_t statlen)
2205168404Spjd{
2206168404Spjd	char *source;
2207168404Spjd
2208168404Spjd	/*
2209168404Spjd	 * Check to see if this property applies to our object
2210168404Spjd	 */
2211185029Spjd	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
2212168404Spjd		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
2213168404Spjd		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
2214168404Spjd		    zfs_prop_to_name(prop)));
2215185029Spjd	}
2216168404Spjd
2217168404Spjd	if (src)
2218185029Spjd		*src = ZPROP_SRC_NONE;
2219168404Spjd
2220168404Spjd	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
2221168404Spjd		return (-1);
2222168404Spjd
2223168404Spjd	get_source(zhp, src, source, statbuf, statlen);
2224168404Spjd
2225168404Spjd	return (0);
2226168404Spjd}
2227168404Spjd
2228209962Smmstatic int
2229209962Smmidmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
2230209962Smm    char **domainp, idmap_rid_t *ridp)
2231209962Smm{
2232209962Smm#ifdef sun
2233209962Smm	idmap_get_handle_t *get_hdl = NULL;
2234209962Smm	idmap_stat status;
2235209962Smm	int err = EINVAL;
2236209962Smm
2237219089Spjd	if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)
2238209962Smm		goto out;
2239209962Smm
2240209962Smm	if (isuser) {
2241209962Smm		err = idmap_get_sidbyuid(get_hdl, id,
2242209962Smm		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2243209962Smm	} else {
2244209962Smm		err = idmap_get_sidbygid(get_hdl, id,
2245209962Smm		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2246209962Smm	}
2247209962Smm	if (err == IDMAP_SUCCESS &&
2248209962Smm	    idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
2249209962Smm	    status == IDMAP_SUCCESS)
2250209962Smm		err = 0;
2251209962Smm	else
2252209962Smm		err = EINVAL;
2253209962Smmout:
2254209962Smm	if (get_hdl)
2255209962Smm		idmap_get_destroy(get_hdl);
2256209962Smm	return (err);
2257209962Smm#else	/* !sun */
2258209962Smm	assert(!"invalid code path");
2259209962Smm#endif	/* !sun */
2260209962Smm}
2261209962Smm
2262168404Spjd/*
2263209962Smm * convert the propname into parameters needed by kernel
2264209962Smm * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
2265209962Smm * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
2266209962Smm */
2267209962Smmstatic int
2268209962Smmuserquota_propname_decode(const char *propname, boolean_t zoned,
2269209962Smm    zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
2270209962Smm{
2271209962Smm	zfs_userquota_prop_t type;
2272209962Smm	char *cp, *end;
2273209962Smm	char *numericsid = NULL;
2274209962Smm	boolean_t isuser;
2275209962Smm
2276209962Smm	domain[0] = '\0';
2277209962Smm
2278209962Smm	/* Figure out the property type ({user|group}{quota|space}) */
2279209962Smm	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
2280209962Smm		if (strncmp(propname, zfs_userquota_prop_prefixes[type],
2281209962Smm		    strlen(zfs_userquota_prop_prefixes[type])) == 0)
2282209962Smm			break;
2283209962Smm	}
2284209962Smm	if (type == ZFS_NUM_USERQUOTA_PROPS)
2285209962Smm		return (EINVAL);
2286209962Smm	*typep = type;
2287209962Smm
2288209962Smm	isuser = (type == ZFS_PROP_USERQUOTA ||
2289209962Smm	    type == ZFS_PROP_USERUSED);
2290209962Smm
2291209962Smm	cp = strchr(propname, '@') + 1;
2292209962Smm
2293209962Smm	if (strchr(cp, '@')) {
2294209962Smm#ifdef sun
2295209962Smm		/*
2296209962Smm		 * It's a SID name (eg "user@domain") that needs to be
2297209962Smm		 * turned into S-1-domainID-RID.
2298209962Smm		 */
2299209962Smm		directory_error_t e;
2300209962Smm		if (zoned && getzoneid() == GLOBAL_ZONEID)
2301209962Smm			return (ENOENT);
2302209962Smm		if (isuser) {
2303209962Smm			e = directory_sid_from_user_name(NULL,
2304209962Smm			    cp, &numericsid);
2305209962Smm		} else {
2306209962Smm			e = directory_sid_from_group_name(NULL,
2307209962Smm			    cp, &numericsid);
2308209962Smm		}
2309209962Smm		if (e != NULL) {
2310209962Smm			directory_error_free(e);
2311209962Smm			return (ENOENT);
2312209962Smm		}
2313209962Smm		if (numericsid == NULL)
2314209962Smm			return (ENOENT);
2315209962Smm		cp = numericsid;
2316209962Smm		/* will be further decoded below */
2317209962Smm#else	/* !sun */
2318219089Spjd		return (ENOENT);
2319209962Smm#endif	/* !sun */
2320209962Smm	}
2321209962Smm
2322209962Smm	if (strncmp(cp, "S-1-", 4) == 0) {
2323209962Smm		/* It's a numeric SID (eg "S-1-234-567-89") */
2324209962Smm		(void) strlcpy(domain, cp, domainlen);
2325209962Smm		cp = strrchr(domain, '-');
2326209962Smm		*cp = '\0';
2327209962Smm		cp++;
2328209962Smm
2329209962Smm		errno = 0;
2330209962Smm		*ridp = strtoull(cp, &end, 10);
2331209962Smm		if (numericsid) {
2332209962Smm			free(numericsid);
2333209962Smm			numericsid = NULL;
2334209962Smm		}
2335209962Smm		if (errno != 0 || *end != '\0')
2336209962Smm			return (EINVAL);
2337209962Smm	} else if (!isdigit(*cp)) {
2338209962Smm		/*
2339209962Smm		 * It's a user/group name (eg "user") that needs to be
2340209962Smm		 * turned into a uid/gid
2341209962Smm		 */
2342209962Smm		if (zoned && getzoneid() == GLOBAL_ZONEID)
2343209962Smm			return (ENOENT);
2344209962Smm		if (isuser) {
2345209962Smm			struct passwd *pw;
2346209962Smm			pw = getpwnam(cp);
2347209962Smm			if (pw == NULL)
2348209962Smm				return (ENOENT);
2349209962Smm			*ridp = pw->pw_uid;
2350209962Smm		} else {
2351209962Smm			struct group *gr;
2352209962Smm			gr = getgrnam(cp);
2353209962Smm			if (gr == NULL)
2354209962Smm				return (ENOENT);
2355209962Smm			*ridp = gr->gr_gid;
2356209962Smm		}
2357209962Smm	} else {
2358209962Smm		/* It's a user/group ID (eg "12345"). */
2359209962Smm		uid_t id = strtoul(cp, &end, 10);
2360209962Smm		idmap_rid_t rid;
2361209962Smm		char *mapdomain;
2362209962Smm
2363209962Smm		if (*end != '\0')
2364209962Smm			return (EINVAL);
2365209962Smm		if (id > MAXUID) {
2366209962Smm			/* It's an ephemeral ID. */
2367209962Smm			if (idmap_id_to_numeric_domain_rid(id, isuser,
2368209962Smm			    &mapdomain, &rid) != 0)
2369209962Smm				return (ENOENT);
2370209962Smm			(void) strlcpy(domain, mapdomain, domainlen);
2371209962Smm			*ridp = rid;
2372209962Smm		} else {
2373209962Smm			*ridp = id;
2374209962Smm		}
2375209962Smm	}
2376209962Smm
2377209962Smm	ASSERT3P(numericsid, ==, NULL);
2378209962Smm	return (0);
2379209962Smm}
2380209962Smm
2381209962Smmstatic int
2382209962Smmzfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
2383209962Smm    uint64_t *propvalue, zfs_userquota_prop_t *typep)
2384209962Smm{
2385209962Smm	int err;
2386209962Smm	zfs_cmd_t zc = { 0 };
2387209962Smm
2388209962Smm	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2389209962Smm
2390209962Smm	err = userquota_propname_decode(propname,
2391209962Smm	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
2392209962Smm	    typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
2393209962Smm	zc.zc_objset_type = *typep;
2394209962Smm	if (err)
2395209962Smm		return (err);
2396209962Smm
2397209962Smm	err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
2398209962Smm	if (err)
2399209962Smm		return (err);
2400209962Smm
2401209962Smm	*propvalue = zc.zc_cookie;
2402209962Smm	return (0);
2403209962Smm}
2404209962Smm
2405209962Smmint
2406209962Smmzfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
2407209962Smm    uint64_t *propvalue)
2408209962Smm{
2409209962Smm	zfs_userquota_prop_t type;
2410209962Smm
2411209962Smm	return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
2412209962Smm	    &type));
2413209962Smm}
2414209962Smm
2415209962Smmint
2416209962Smmzfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
2417209962Smm    char *propbuf, int proplen, boolean_t literal)
2418209962Smm{
2419209962Smm	int err;
2420209962Smm	uint64_t propvalue;
2421209962Smm	zfs_userquota_prop_t type;
2422209962Smm
2423209962Smm	err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,
2424209962Smm	    &type);
2425209962Smm
2426209962Smm	if (err)
2427209962Smm		return (err);
2428209962Smm
2429209962Smm	if (literal) {
2430209962Smm		(void) snprintf(propbuf, proplen, "%llu", propvalue);
2431209962Smm	} else if (propvalue == 0 &&
2432209962Smm	    (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
2433209962Smm		(void) strlcpy(propbuf, "none", proplen);
2434209962Smm	} else {
2435209962Smm		zfs_nicenum(propvalue, propbuf, proplen);
2436209962Smm	}
2437209962Smm	return (0);
2438209962Smm}
2439209962Smm
2440209962Smm/*
2441168404Spjd * Returns the name of the given zfs handle.
2442168404Spjd */
2443168404Spjdconst char *
2444168404Spjdzfs_get_name(const zfs_handle_t *zhp)
2445168404Spjd{
2446168404Spjd	return (zhp->zfs_name);
2447168404Spjd}
2448168404Spjd
2449168404Spjd/*
2450168404Spjd * Returns the type of the given zfs handle.
2451168404Spjd */
2452168404Spjdzfs_type_t
2453168404Spjdzfs_get_type(const zfs_handle_t *zhp)
2454168404Spjd{
2455168404Spjd	return (zhp->zfs_type);
2456168404Spjd}
2457168404Spjd
2458209962Smmstatic int
2459209962Smmzfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
2460209962Smm{
2461209962Smm	int rc;
2462209962Smm	uint64_t	orig_cookie;
2463209962Smm
2464209962Smm	orig_cookie = zc->zc_cookie;
2465209962Smmtop:
2466209962Smm	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
2467209962Smm	rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
2468209962Smm
2469209962Smm	if (rc == -1) {
2470209962Smm		switch (errno) {
2471209962Smm		case ENOMEM:
2472209962Smm			/* expand nvlist memory and try again */
2473209962Smm			if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
2474209962Smm				zcmd_free_nvlists(zc);
2475209962Smm				return (-1);
2476209962Smm			}
2477209962Smm			zc->zc_cookie = orig_cookie;
2478209962Smm			goto top;
2479209962Smm		/*
2480209962Smm		 * An errno value of ESRCH indicates normal completion.
2481209962Smm		 * If ENOENT is returned, then the underlying dataset
2482209962Smm		 * has been removed since we obtained the handle.
2483209962Smm		 */
2484209962Smm		case ESRCH:
2485209962Smm		case ENOENT:
2486209962Smm			rc = 1;
2487209962Smm			break;
2488209962Smm		default:
2489209962Smm			rc = zfs_standard_error(zhp->zfs_hdl, errno,
2490209962Smm			    dgettext(TEXT_DOMAIN,
2491209962Smm			    "cannot iterate filesystems"));
2492209962Smm			break;
2493209962Smm		}
2494209962Smm	}
2495209962Smm	return (rc);
2496209962Smm}
2497209962Smm
2498168404Spjd/*
2499168404Spjd * Iterate over all child filesystems
2500168404Spjd */
2501168404Spjdint
2502168404Spjdzfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2503168404Spjd{
2504168404Spjd	zfs_cmd_t zc = { 0 };
2505168404Spjd	zfs_handle_t *nzhp;
2506168404Spjd	int ret;
2507168404Spjd
2508185029Spjd	if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
2509185029Spjd		return (0);
2510185029Spjd
2511209962Smm	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2512209962Smm		return (-1);
2513209962Smm
2514209962Smm	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
2515209962Smm	    &zc)) == 0) {
2516168404Spjd		/*
2517168404Spjd		 * Silently ignore errors, as the only plausible explanation is
2518168404Spjd		 * that the pool has since been removed.
2519168404Spjd		 */
2520209962Smm		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2521209962Smm		    &zc)) == NULL) {
2522168404Spjd			continue;
2523209962Smm		}
2524168404Spjd
2525209962Smm		if ((ret = func(nzhp, data)) != 0) {
2526209962Smm			zcmd_free_nvlists(&zc);
2527168404Spjd			return (ret);
2528209962Smm		}
2529168404Spjd	}
2530209962Smm	zcmd_free_nvlists(&zc);
2531209962Smm	return ((ret < 0) ? ret : 0);
2532168404Spjd}
2533168404Spjd
2534168404Spjd/*
2535168404Spjd * Iterate over all snapshots
2536168404Spjd */
2537168404Spjdint
2538168404Spjdzfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2539168404Spjd{
2540168404Spjd	zfs_cmd_t zc = { 0 };
2541168404Spjd	zfs_handle_t *nzhp;
2542168404Spjd	int ret;
2543168404Spjd
2544185029Spjd	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
2545185029Spjd		return (0);
2546185029Spjd
2547209962Smm	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2548209962Smm		return (-1);
2549209962Smm	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
2550209962Smm	    &zc)) == 0) {
2551168404Spjd
2552209962Smm		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2553209962Smm		    &zc)) == NULL) {
2554209962Smm			continue;
2555209962Smm		}
2556209962Smm
2557209962Smm		if ((ret = func(nzhp, data)) != 0) {
2558209962Smm			zcmd_free_nvlists(&zc);
2559168404Spjd			return (ret);
2560209962Smm		}
2561168404Spjd	}
2562209962Smm	zcmd_free_nvlists(&zc);
2563209962Smm	return ((ret < 0) ? ret : 0);
2564168404Spjd}
2565168404Spjd
2566168404Spjd/*
2567168404Spjd * Iterate over all children, snapshots and filesystems
2568168404Spjd */
2569168404Spjdint
2570168404Spjdzfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2571168404Spjd{
2572168404Spjd	int ret;
2573168404Spjd
2574168404Spjd	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
2575168404Spjd		return (ret);
2576168404Spjd
2577168404Spjd	return (zfs_iter_snapshots(zhp, func, data));
2578168404Spjd}
2579168404Spjd
2580168404Spjd/*
2581219089Spjd * Is one dataset name a child dataset of another?
2582219089Spjd *
2583219089Spjd * Needs to handle these cases:
2584219089Spjd * Dataset 1	"a/foo"		"a/foo"		"a/foo"		"a/foo"
2585219089Spjd * Dataset 2	"a/fo"		"a/foobar"	"a/bar/baz"	"a/foo/bar"
2586219089Spjd * Descendant?	No.		No.		No.		Yes.
2587219089Spjd */
2588219089Spjdstatic boolean_t
2589219089Spjdis_descendant(const char *ds1, const char *ds2)
2590219089Spjd{
2591219089Spjd	size_t d1len = strlen(ds1);
2592219089Spjd
2593219089Spjd	/* ds2 can't be a descendant if it's smaller */
2594219089Spjd	if (strlen(ds2) < d1len)
2595219089Spjd		return (B_FALSE);
2596219089Spjd
2597219089Spjd	/* otherwise, compare strings and verify that there's a '/' char */
2598219089Spjd	return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0));
2599219089Spjd}
2600219089Spjd
2601219089Spjd/*
2602168404Spjd * Given a complete name, return just the portion that refers to the parent.
2603168404Spjd * Can return NULL if this is a pool.
2604168404Spjd */
2605168404Spjdstatic int
2606168404Spjdparent_name(const char *path, char *buf, size_t buflen)
2607168404Spjd{
2608168404Spjd	char *loc;
2609168404Spjd
2610168404Spjd	if ((loc = strrchr(path, '/')) == NULL)
2611168404Spjd		return (-1);
2612168404Spjd
2613168404Spjd	(void) strncpy(buf, path, MIN(buflen, loc - path));
2614168404Spjd	buf[loc - path] = '\0';
2615168404Spjd
2616168404Spjd	return (0);
2617168404Spjd}
2618168404Spjd
2619168404Spjd/*
2620185029Spjd * If accept_ancestor is false, then check to make sure that the given path has
2621185029Spjd * a parent, and that it exists.  If accept_ancestor is true, then find the
2622185029Spjd * closest existing ancestor for the given path.  In prefixlen return the
2623185029Spjd * length of already existing prefix of the given path.  We also fetch the
2624185029Spjd * 'zoned' property, which is used to validate property settings when creating
2625185029Spjd * new datasets.
2626168404Spjd */
2627168404Spjdstatic int
2628185029Spjdcheck_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
2629185029Spjd    boolean_t accept_ancestor, int *prefixlen)
2630168404Spjd{
2631168404Spjd	zfs_cmd_t zc = { 0 };
2632168404Spjd	char parent[ZFS_MAXNAMELEN];
2633168404Spjd	char *slash;
2634168404Spjd	zfs_handle_t *zhp;
2635168404Spjd	char errbuf[1024];
2636219089Spjd	uint64_t is_zoned;
2637168404Spjd
2638209962Smm	(void) snprintf(errbuf, sizeof (errbuf),
2639209962Smm	    dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
2640168404Spjd
2641168404Spjd	/* get parent, and check to see if this is just a pool */
2642168404Spjd	if (parent_name(path, parent, sizeof (parent)) != 0) {
2643168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2644168404Spjd		    "missing dataset name"));
2645168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2646168404Spjd	}
2647168404Spjd
2648168404Spjd	/* check to see if the pool exists */
2649168404Spjd	if ((slash = strchr(parent, '/')) == NULL)
2650168404Spjd		slash = parent + strlen(parent);
2651168404Spjd	(void) strncpy(zc.zc_name, parent, slash - parent);
2652168404Spjd	zc.zc_name[slash - parent] = '\0';
2653168404Spjd	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
2654168404Spjd	    errno == ENOENT) {
2655168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2656168404Spjd		    "no such pool '%s'"), zc.zc_name);
2657168404Spjd		return (zfs_error(hdl, EZFS_NOENT, errbuf));
2658168404Spjd	}
2659168404Spjd
2660168404Spjd	/* check to see if the parent dataset exists */
2661185029Spjd	while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
2662185029Spjd		if (errno == ENOENT && accept_ancestor) {
2663185029Spjd			/*
2664185029Spjd			 * Go deeper to find an ancestor, give up on top level.
2665185029Spjd			 */
2666185029Spjd			if (parent_name(parent, parent, sizeof (parent)) != 0) {
2667185029Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2668185029Spjd				    "no such pool '%s'"), zc.zc_name);
2669185029Spjd				return (zfs_error(hdl, EZFS_NOENT, errbuf));
2670185029Spjd			}
2671185029Spjd		} else if (errno == ENOENT) {
2672168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2673168404Spjd			    "parent does not exist"));
2674168404Spjd			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2675185029Spjd		} else
2676168404Spjd			return (zfs_standard_error(hdl, errno, errbuf));
2677168404Spjd	}
2678168404Spjd
2679219089Spjd	is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
2680219089Spjd	if (zoned != NULL)
2681219089Spjd		*zoned = is_zoned;
2682219089Spjd
2683168404Spjd	/* we are in a non-global zone, but parent is in the global zone */
2684219089Spjd	if (getzoneid() != GLOBAL_ZONEID && !is_zoned) {
2685168404Spjd		(void) zfs_standard_error(hdl, EPERM, errbuf);
2686168404Spjd		zfs_close(zhp);
2687168404Spjd		return (-1);
2688168404Spjd	}
2689168404Spjd
2690168404Spjd	/* make sure parent is a filesystem */
2691168404Spjd	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2692168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2693168404Spjd		    "parent is not a filesystem"));
2694168404Spjd		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
2695168404Spjd		zfs_close(zhp);
2696168404Spjd		return (-1);
2697168404Spjd	}
2698168404Spjd
2699168404Spjd	zfs_close(zhp);
2700185029Spjd	if (prefixlen != NULL)
2701185029Spjd		*prefixlen = strlen(parent);
2702168404Spjd	return (0);
2703168404Spjd}
2704168404Spjd
2705168404Spjd/*
2706185029Spjd * Finds whether the dataset of the given type(s) exists.
2707185029Spjd */
2708185029Spjdboolean_t
2709185029Spjdzfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
2710185029Spjd{
2711185029Spjd	zfs_handle_t *zhp;
2712185029Spjd
2713185029Spjd	if (!zfs_validate_name(hdl, path, types, B_FALSE))
2714185029Spjd		return (B_FALSE);
2715185029Spjd
2716185029Spjd	/*
2717185029Spjd	 * Try to get stats for the dataset, which will tell us if it exists.
2718185029Spjd	 */
2719185029Spjd	if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
2720185029Spjd		int ds_type = zhp->zfs_type;
2721185029Spjd
2722185029Spjd		zfs_close(zhp);
2723185029Spjd		if (types & ds_type)
2724185029Spjd			return (B_TRUE);
2725185029Spjd	}
2726185029Spjd	return (B_FALSE);
2727185029Spjd}
2728185029Spjd
2729185029Spjd/*
2730185029Spjd * Given a path to 'target', create all the ancestors between
2731185029Spjd * the prefixlen portion of the path, and the target itself.
2732185029Spjd * Fail if the initial prefixlen-ancestor does not already exist.
2733185029Spjd */
2734185029Spjdint
2735185029Spjdcreate_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
2736185029Spjd{
2737185029Spjd	zfs_handle_t *h;
2738185029Spjd	char *cp;
2739185029Spjd	const char *opname;
2740185029Spjd
2741185029Spjd	/* make sure prefix exists */
2742185029Spjd	cp = target + prefixlen;
2743185029Spjd	if (*cp != '/') {
2744185029Spjd		assert(strchr(cp, '/') == NULL);
2745185029Spjd		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2746185029Spjd	} else {
2747185029Spjd		*cp = '\0';
2748185029Spjd		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2749185029Spjd		*cp = '/';
2750185029Spjd	}
2751185029Spjd	if (h == NULL)
2752185029Spjd		return (-1);
2753185029Spjd	zfs_close(h);
2754185029Spjd
2755185029Spjd	/*
2756185029Spjd	 * Attempt to create, mount, and share any ancestor filesystems,
2757185029Spjd	 * up to the prefixlen-long one.
2758185029Spjd	 */
2759185029Spjd	for (cp = target + prefixlen + 1;
2760185029Spjd	    cp = strchr(cp, '/'); *cp = '/', cp++) {
2761185029Spjd		char *logstr;
2762185029Spjd
2763185029Spjd		*cp = '\0';
2764185029Spjd
2765185029Spjd		h = make_dataset_handle(hdl, target);
2766185029Spjd		if (h) {
2767185029Spjd			/* it already exists, nothing to do here */
2768185029Spjd			zfs_close(h);
2769185029Spjd			continue;
2770185029Spjd		}
2771185029Spjd
2772185029Spjd		logstr = hdl->libzfs_log_str;
2773185029Spjd		hdl->libzfs_log_str = NULL;
2774185029Spjd		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
2775185029Spjd		    NULL) != 0) {
2776185029Spjd			hdl->libzfs_log_str = logstr;
2777185029Spjd			opname = dgettext(TEXT_DOMAIN, "create");
2778185029Spjd			goto ancestorerr;
2779185029Spjd		}
2780185029Spjd
2781185029Spjd		hdl->libzfs_log_str = logstr;
2782185029Spjd		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2783185029Spjd		if (h == NULL) {
2784185029Spjd			opname = dgettext(TEXT_DOMAIN, "open");
2785185029Spjd			goto ancestorerr;
2786185029Spjd		}
2787185029Spjd
2788185029Spjd		if (zfs_mount(h, NULL, 0) != 0) {
2789185029Spjd			opname = dgettext(TEXT_DOMAIN, "mount");
2790185029Spjd			goto ancestorerr;
2791185029Spjd		}
2792185029Spjd
2793185029Spjd		if (zfs_share(h) != 0) {
2794185029Spjd			opname = dgettext(TEXT_DOMAIN, "share");
2795185029Spjd			goto ancestorerr;
2796185029Spjd		}
2797185029Spjd
2798185029Spjd		zfs_close(h);
2799185029Spjd	}
2800185029Spjd
2801185029Spjd	return (0);
2802185029Spjd
2803185029Spjdancestorerr:
2804185029Spjd	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2805185029Spjd	    "failed to %s ancestor '%s'"), opname, target);
2806185029Spjd	return (-1);
2807185029Spjd}
2808185029Spjd
2809185029Spjd/*
2810185029Spjd * Creates non-existing ancestors of the given path.
2811185029Spjd */
2812185029Spjdint
2813185029Spjdzfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
2814185029Spjd{
2815185029Spjd	int prefix;
2816185029Spjd	char *path_copy;
2817185029Spjd	int rc;
2818185029Spjd
2819219089Spjd	if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
2820185029Spjd		return (-1);
2821185029Spjd
2822185029Spjd	if ((path_copy = strdup(path)) != NULL) {
2823185029Spjd		rc = create_parents(hdl, path_copy, prefix);
2824185029Spjd		free(path_copy);
2825185029Spjd	}
2826185029Spjd	if (path_copy == NULL || rc != 0)
2827185029Spjd		return (-1);
2828185029Spjd
2829185029Spjd	return (0);
2830185029Spjd}
2831185029Spjd
2832185029Spjd/*
2833168404Spjd * Create a new filesystem or volume.
2834168404Spjd */
2835168404Spjdint
2836168404Spjdzfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
2837168404Spjd    nvlist_t *props)
2838168404Spjd{
2839168404Spjd	zfs_cmd_t zc = { 0 };
2840168404Spjd	int ret;
2841168404Spjd	uint64_t size = 0;
2842168404Spjd	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
2843168404Spjd	char errbuf[1024];
2844168404Spjd	uint64_t zoned;
2845168404Spjd
2846168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2847168404Spjd	    "cannot create '%s'"), path);
2848168404Spjd
2849168404Spjd	/* validate the path, taking care to note the extended error message */
2850185029Spjd	if (!zfs_validate_name(hdl, path, type, B_TRUE))
2851168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2852168404Spjd
2853168404Spjd	/* validate parents exist */
2854185029Spjd	if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
2855168404Spjd		return (-1);
2856168404Spjd
2857168404Spjd	/*
2858168404Spjd	 * The failure modes when creating a dataset of a different type over
2859168404Spjd	 * one that already exists is a little strange.  In particular, if you
2860168404Spjd	 * try to create a dataset on top of an existing dataset, the ioctl()
2861168404Spjd	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
2862168404Spjd	 * first try to see if the dataset exists.
2863168404Spjd	 */
2864168404Spjd	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
2865185029Spjd	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
2866168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2867168404Spjd		    "dataset already exists"));
2868168404Spjd		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2869168404Spjd	}
2870168404Spjd
2871168404Spjd	if (type == ZFS_TYPE_VOLUME)
2872168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
2873168404Spjd	else
2874168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
2875168404Spjd
2876185029Spjd	if (props && (props = zfs_valid_proplist(hdl, type, props,
2877168404Spjd	    zoned, NULL, errbuf)) == 0)
2878168404Spjd		return (-1);
2879168404Spjd
2880168404Spjd	if (type == ZFS_TYPE_VOLUME) {
2881168404Spjd		/*
2882168404Spjd		 * If we are creating a volume, the size and block size must
2883168404Spjd		 * satisfy a few restraints.  First, the blocksize must be a
2884168404Spjd		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
2885168404Spjd		 * volsize must be a multiple of the block size, and cannot be
2886168404Spjd		 * zero.
2887168404Spjd		 */
2888168404Spjd		if (props == NULL || nvlist_lookup_uint64(props,
2889168404Spjd		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2890168404Spjd			nvlist_free(props);
2891168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2892168404Spjd			    "missing volume size"));
2893168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2894168404Spjd		}
2895168404Spjd
2896168404Spjd		if ((ret = nvlist_lookup_uint64(props,
2897168404Spjd		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2898168404Spjd		    &blocksize)) != 0) {
2899168404Spjd			if (ret == ENOENT) {
2900168404Spjd				blocksize = zfs_prop_default_numeric(
2901168404Spjd				    ZFS_PROP_VOLBLOCKSIZE);
2902168404Spjd			} else {
2903168404Spjd				nvlist_free(props);
2904168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2905168404Spjd				    "missing volume block size"));
2906168404Spjd				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2907168404Spjd			}
2908168404Spjd		}
2909168404Spjd
2910168404Spjd		if (size == 0) {
2911168404Spjd			nvlist_free(props);
2912168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2913168404Spjd			    "volume size cannot be zero"));
2914168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2915168404Spjd		}
2916168404Spjd
2917168404Spjd		if (size % blocksize != 0) {
2918168404Spjd			nvlist_free(props);
2919168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2920168404Spjd			    "volume size must be a multiple of volume block "
2921168404Spjd			    "size"));
2922168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2923168404Spjd		}
2924168404Spjd	}
2925168404Spjd
2926185029Spjd	if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
2927168404Spjd		return (-1);
2928168404Spjd	nvlist_free(props);
2929168404Spjd
2930168404Spjd	/* create the dataset */
2931185029Spjd	ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
2932168404Spjd
2933168404Spjd	zcmd_free_nvlists(&zc);
2934168404Spjd
2935168404Spjd	/* check for failure */
2936168404Spjd	if (ret != 0) {
2937168404Spjd		char parent[ZFS_MAXNAMELEN];
2938168404Spjd		(void) parent_name(path, parent, sizeof (parent));
2939168404Spjd
2940168404Spjd		switch (errno) {
2941168404Spjd		case ENOENT:
2942168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2943168404Spjd			    "no such parent '%s'"), parent);
2944168404Spjd			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2945168404Spjd
2946168404Spjd		case EINVAL:
2947168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2948168404Spjd			    "parent '%s' is not a filesystem"), parent);
2949168404Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2950168404Spjd
2951168404Spjd		case EDOM:
2952168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2953168404Spjd			    "volume block size must be power of 2 from "
2954168404Spjd			    "%u to %uk"),
2955168404Spjd			    (uint_t)SPA_MINBLOCKSIZE,
2956168404Spjd			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
2957168404Spjd
2958168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2959168404Spjd
2960185029Spjd		case ENOTSUP:
2961185029Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2962185029Spjd			    "pool must be upgraded to set this "
2963185029Spjd			    "property or value"));
2964185029Spjd			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
2965168404Spjd#ifdef _ILP32
2966168404Spjd		case EOVERFLOW:
2967168404Spjd			/*
2968168404Spjd			 * This platform can't address a volume this big.
2969168404Spjd			 */
2970168404Spjd			if (type == ZFS_TYPE_VOLUME)
2971168404Spjd				return (zfs_error(hdl, EZFS_VOLTOOBIG,
2972168404Spjd				    errbuf));
2973168404Spjd#endif
2974168404Spjd			/* FALLTHROUGH */
2975168404Spjd		default:
2976168404Spjd			return (zfs_standard_error(hdl, errno, errbuf));
2977168404Spjd		}
2978168404Spjd	}
2979168404Spjd
2980168404Spjd	return (0);
2981168404Spjd}
2982168404Spjd
2983168404Spjd/*
2984168404Spjd * Destroys the given dataset.  The caller must make sure that the filesystem
2985168404Spjd * isn't mounted, and that there are no active dependents.
2986168404Spjd */
2987168404Spjdint
2988219089Spjdzfs_destroy(zfs_handle_t *zhp, boolean_t defer)
2989168404Spjd{
2990168404Spjd	zfs_cmd_t zc = { 0 };
2991168404Spjd
2992168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2993168404Spjd
2994168404Spjd	if (ZFS_IS_VOLUME(zhp)) {
2995168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
2996168404Spjd	} else {
2997168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
2998168404Spjd	}
2999168404Spjd
3000219089Spjd	zc.zc_defer_destroy = defer;
3001185029Spjd	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
3002168404Spjd		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
3003168404Spjd		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
3004168404Spjd		    zhp->zfs_name));
3005168404Spjd	}
3006168404Spjd
3007168404Spjd	remove_mountpoint(zhp);
3008168404Spjd
3009168404Spjd	return (0);
3010168404Spjd}
3011168404Spjd
3012168404Spjdstruct destroydata {
3013168404Spjd	char *snapname;
3014168404Spjd	boolean_t gotone;
3015168404Spjd	boolean_t closezhp;
3016168404Spjd};
3017168404Spjd
3018168404Spjdstatic int
3019219089Spjdzfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
3020168404Spjd{
3021168404Spjd	struct destroydata *dd = arg;
3022168404Spjd	zfs_handle_t *szhp;
3023168404Spjd	char name[ZFS_MAXNAMELEN];
3024168404Spjd	boolean_t closezhp = dd->closezhp;
3025219089Spjd	int rv = 0;
3026168404Spjd
3027168404Spjd	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
3028168404Spjd	(void) strlcat(name, "@", sizeof (name));
3029168404Spjd	(void) strlcat(name, dd->snapname, sizeof (name));
3030168404Spjd
3031168404Spjd	szhp = make_dataset_handle(zhp->zfs_hdl, name);
3032168404Spjd	if (szhp) {
3033168404Spjd		dd->gotone = B_TRUE;
3034168404Spjd		zfs_close(szhp);
3035168404Spjd	}
3036168404Spjd
3037168404Spjd	dd->closezhp = B_TRUE;
3038219089Spjd	if (!dd->gotone)
3039219089Spjd		rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
3040168404Spjd	if (closezhp)
3041168404Spjd		zfs_close(zhp);
3042168404Spjd	return (rv);
3043168404Spjd}
3044168404Spjd
3045168404Spjd/*
3046168404Spjd * Destroys all snapshots with the given name in zhp & descendants.
3047168404Spjd */
3048168404Spjdint
3049219089Spjdzfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
3050168404Spjd{
3051168404Spjd	zfs_cmd_t zc = { 0 };
3052168404Spjd	int ret;
3053168404Spjd	struct destroydata dd = { 0 };
3054168404Spjd
3055168404Spjd	dd.snapname = snapname;
3056219089Spjd	(void) zfs_check_snap_cb(zhp, &dd);
3057168404Spjd
3058168404Spjd	if (!dd.gotone) {
3059168404Spjd		return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
3060168404Spjd		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
3061168404Spjd		    zhp->zfs_name, snapname));
3062168404Spjd	}
3063168404Spjd
3064168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3065168404Spjd	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
3066219089Spjd	zc.zc_defer_destroy = defer;
3067168404Spjd
3068185029Spjd	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
3069168404Spjd	if (ret != 0) {
3070168404Spjd		char errbuf[1024];
3071168404Spjd
3072168404Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3073168404Spjd		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
3074168404Spjd
3075168404Spjd		switch (errno) {
3076168404Spjd		case EEXIST:
3077168404Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3078168404Spjd			    "snapshot is cloned"));
3079168404Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
3080168404Spjd
3081168404Spjd		default:
3082168404Spjd			return (zfs_standard_error(zhp->zfs_hdl, errno,
3083168404Spjd			    errbuf));
3084168404Spjd		}
3085168404Spjd	}
3086168404Spjd
3087168404Spjd	return (0);
3088168404Spjd}
3089168404Spjd
3090168404Spjd/*
3091168404Spjd * Clones the given dataset.  The target must be of the same type as the source.
3092168404Spjd */
3093168404Spjdint
3094168404Spjdzfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
3095168404Spjd{
3096168404Spjd	zfs_cmd_t zc = { 0 };
3097168404Spjd	char parent[ZFS_MAXNAMELEN];
3098168404Spjd	int ret;
3099168404Spjd	char errbuf[1024];
3100168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3101168404Spjd	zfs_type_t type;
3102168404Spjd	uint64_t zoned;
3103168404Spjd
3104168404Spjd	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
3105168404Spjd
3106168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3107168404Spjd	    "cannot create '%s'"), target);
3108168404Spjd
3109168404Spjd	/* validate the target name */
3110185029Spjd	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
3111168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3112168404Spjd
3113168404Spjd	/* validate parents exist */
3114185029Spjd	if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
3115168404Spjd		return (-1);
3116168404Spjd
3117168404Spjd	(void) parent_name(target, parent, sizeof (parent));
3118168404Spjd
3119168404Spjd	/* do the clone */
3120168404Spjd	if (ZFS_IS_VOLUME(zhp)) {
3121168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3122168404Spjd		type = ZFS_TYPE_VOLUME;
3123168404Spjd	} else {
3124168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3125168404Spjd		type = ZFS_TYPE_FILESYSTEM;
3126168404Spjd	}
3127168404Spjd
3128168404Spjd	if (props) {
3129185029Spjd		if ((props = zfs_valid_proplist(hdl, type, props, zoned,
3130185029Spjd		    zhp, errbuf)) == NULL)
3131168404Spjd			return (-1);
3132168404Spjd
3133185029Spjd		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3134168404Spjd			nvlist_free(props);
3135168404Spjd			return (-1);
3136168404Spjd		}
3137168404Spjd
3138168404Spjd		nvlist_free(props);
3139168404Spjd	}
3140168404Spjd
3141168404Spjd	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
3142168404Spjd	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
3143185029Spjd	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
3144168404Spjd
3145168404Spjd	zcmd_free_nvlists(&zc);
3146168404Spjd
3147168404Spjd	if (ret != 0) {
3148168404Spjd		switch (errno) {
3149168404Spjd
3150168404Spjd		case ENOENT:
3151168404Spjd			/*
3152168404Spjd			 * The parent doesn't exist.  We should have caught this
3153168404Spjd			 * above, but there may a race condition that has since
3154168404Spjd			 * destroyed the parent.
3155168404Spjd			 *
3156168404Spjd			 * At this point, we don't know whether it's the source
3157168404Spjd			 * that doesn't exist anymore, or whether the target
3158168404Spjd			 * dataset doesn't exist.
3159168404Spjd			 */
3160168404Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3161168404Spjd			    "no such parent '%s'"), parent);
3162168404Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
3163168404Spjd
3164168404Spjd		case EXDEV:
3165168404Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3166168404Spjd			    "source and target pools differ"));
3167168404Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
3168168404Spjd			    errbuf));
3169168404Spjd
3170168404Spjd		default:
3171168404Spjd			return (zfs_standard_error(zhp->zfs_hdl, errno,
3172168404Spjd			    errbuf));
3173168404Spjd		}
3174168404Spjd	}
3175168404Spjd
3176168404Spjd	return (ret);
3177168404Spjd}
3178168404Spjd
3179168404Spjd/*
3180168404Spjd * Promotes the given clone fs to be the clone parent.
3181168404Spjd */
3182168404Spjdint
3183168404Spjdzfs_promote(zfs_handle_t *zhp)
3184168404Spjd{
3185168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3186168404Spjd	zfs_cmd_t zc = { 0 };
3187168404Spjd	char parent[MAXPATHLEN];
3188168404Spjd	int ret;
3189168404Spjd	char errbuf[1024];
3190168404Spjd
3191168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3192168404Spjd	    "cannot promote '%s'"), zhp->zfs_name);
3193168404Spjd
3194168404Spjd	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
3195168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3196168404Spjd		    "snapshots can not be promoted"));
3197168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3198168404Spjd	}
3199168404Spjd
3200185029Spjd	(void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
3201168404Spjd	if (parent[0] == '\0') {
3202168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3203168404Spjd		    "not a cloned filesystem"));
3204168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3205168404Spjd	}
3206168404Spjd
3207185029Spjd	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
3208168404Spjd	    sizeof (zc.zc_value));
3209168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3210185029Spjd	ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
3211168404Spjd
3212168404Spjd	if (ret != 0) {
3213168404Spjd		int save_errno = errno;
3214168404Spjd
3215168404Spjd		switch (save_errno) {
3216168404Spjd		case EEXIST:
3217219089Spjd			/* There is a conflicting snapshot name. */
3218168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3219219089Spjd			    "conflicting snapshot '%s' from parent '%s'"),
3220219089Spjd			    zc.zc_string, parent);
3221168404Spjd			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
3222168404Spjd
3223168404Spjd		default:
3224168404Spjd			return (zfs_standard_error(hdl, save_errno, errbuf));
3225168404Spjd		}
3226168404Spjd	}
3227168404Spjd	return (ret);
3228168404Spjd}
3229168404Spjd
3230168404Spjd/*
3231168404Spjd * Takes a snapshot of the given dataset.
3232168404Spjd */
3233168404Spjdint
3234185029Spjdzfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
3235185029Spjd    nvlist_t *props)
3236168404Spjd{
3237168404Spjd	const char *delim;
3238185029Spjd	char parent[ZFS_MAXNAMELEN];
3239168404Spjd	zfs_handle_t *zhp;
3240168404Spjd	zfs_cmd_t zc = { 0 };
3241168404Spjd	int ret;
3242168404Spjd	char errbuf[1024];
3243168404Spjd
3244168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3245168404Spjd	    "cannot snapshot '%s'"), path);
3246168404Spjd
3247168404Spjd	/* validate the target name */
3248185029Spjd	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
3249168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3250168404Spjd
3251185029Spjd	if (props) {
3252185029Spjd		if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
3253185029Spjd		    props, B_FALSE, NULL, errbuf)) == NULL)
3254185029Spjd			return (-1);
3255185029Spjd
3256185029Spjd		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3257185029Spjd			nvlist_free(props);
3258185029Spjd			return (-1);
3259185029Spjd		}
3260185029Spjd
3261185029Spjd		nvlist_free(props);
3262185029Spjd	}
3263185029Spjd
3264168404Spjd	/* make sure the parent exists and is of the appropriate type */
3265168404Spjd	delim = strchr(path, '@');
3266168404Spjd	(void) strncpy(parent, path, delim - path);
3267168404Spjd	parent[delim - path] = '\0';
3268168404Spjd
3269168404Spjd	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
3270168404Spjd	    ZFS_TYPE_VOLUME)) == NULL) {
3271185029Spjd		zcmd_free_nvlists(&zc);
3272168404Spjd		return (-1);
3273168404Spjd	}
3274168404Spjd
3275168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3276168404Spjd	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
3277185029Spjd	if (ZFS_IS_VOLUME(zhp))
3278185029Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3279185029Spjd	else
3280185029Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3281168404Spjd	zc.zc_cookie = recursive;
3282185029Spjd	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
3283168404Spjd
3284185029Spjd	zcmd_free_nvlists(&zc);
3285185029Spjd
3286168404Spjd	/*
3287168404Spjd	 * if it was recursive, the one that actually failed will be in
3288168404Spjd	 * zc.zc_name.
3289168404Spjd	 */
3290219089Spjd	if (ret != 0) {
3291185029Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3292185029Spjd		    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
3293219089Spjd		(void) zfs_standard_error(hdl, errno, errbuf);
3294168404Spjd	}
3295168404Spjd
3296168404Spjd	zfs_close(zhp);
3297168404Spjd
3298168404Spjd	return (ret);
3299168404Spjd}
3300168404Spjd
3301168404Spjd/*
3302168404Spjd * Destroy any more recent snapshots.  We invoke this callback on any dependents
3303168404Spjd * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
3304168404Spjd * is a dependent and we should just destroy it without checking the transaction
3305168404Spjd * group.
3306168404Spjd */
3307168404Spjdtypedef struct rollback_data {
3308168404Spjd	const char	*cb_target;		/* the snapshot */
3309168404Spjd	uint64_t	cb_create;		/* creation time reference */
3310185029Spjd	boolean_t	cb_error;
3311168404Spjd	boolean_t	cb_dependent;
3312185029Spjd	boolean_t	cb_force;
3313168404Spjd} rollback_data_t;
3314168404Spjd
3315168404Spjdstatic int
3316168404Spjdrollback_destroy(zfs_handle_t *zhp, void *data)
3317168404Spjd{
3318168404Spjd	rollback_data_t *cbp = data;
3319168404Spjd
3320168404Spjd	if (!cbp->cb_dependent) {
3321168404Spjd		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
3322168404Spjd		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3323168404Spjd		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3324168404Spjd		    cbp->cb_create) {
3325185029Spjd			char *logstr;
3326168404Spjd
3327168404Spjd			cbp->cb_dependent = B_TRUE;
3328185029Spjd			cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
3329185029Spjd			    rollback_destroy, cbp);
3330168404Spjd			cbp->cb_dependent = B_FALSE;
3331168404Spjd
3332185029Spjd			logstr = zhp->zfs_hdl->libzfs_log_str;
3333185029Spjd			zhp->zfs_hdl->libzfs_log_str = NULL;
3334219089Spjd			cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
3335185029Spjd			zhp->zfs_hdl->libzfs_log_str = logstr;
3336168404Spjd		}
3337168404Spjd	} else {
3338185029Spjd		/* We must destroy this clone; first unmount it */
3339185029Spjd		prop_changelist_t *clp;
3340185029Spjd
3341185029Spjd		clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3342185029Spjd		    cbp->cb_force ? MS_FORCE: 0);
3343185029Spjd		if (clp == NULL || changelist_prefix(clp) != 0) {
3344185029Spjd			cbp->cb_error = B_TRUE;
3345185029Spjd			zfs_close(zhp);
3346185029Spjd			return (0);
3347185029Spjd		}
3348219089Spjd		if (zfs_destroy(zhp, B_FALSE) != 0)
3349185029Spjd			cbp->cb_error = B_TRUE;
3350168404Spjd		else
3351185029Spjd			changelist_remove(clp, zhp->zfs_name);
3352185029Spjd		(void) changelist_postfix(clp);
3353185029Spjd		changelist_free(clp);
3354168404Spjd	}
3355168404Spjd
3356168404Spjd	zfs_close(zhp);
3357168404Spjd	return (0);
3358168404Spjd}
3359168404Spjd
3360168404Spjd/*
3361168404Spjd * Given a dataset, rollback to a specific snapshot, discarding any
3362168404Spjd * data changes since then and making it the active dataset.
3363168404Spjd *
3364168404Spjd * Any snapshots more recent than the target are destroyed, along with
3365168404Spjd * their dependents.
3366168404Spjd */
3367168404Spjdint
3368185029Spjdzfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
3369168404Spjd{
3370168404Spjd	rollback_data_t cb = { 0 };
3371185029Spjd	int err;
3372185029Spjd	zfs_cmd_t zc = { 0 };
3373185029Spjd	boolean_t restore_resv = 0;
3374185029Spjd	uint64_t old_volsize, new_volsize;
3375185029Spjd	zfs_prop_t resv_prop;
3376168404Spjd
3377185029Spjd	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
3378185029Spjd	    zhp->zfs_type == ZFS_TYPE_VOLUME);
3379168404Spjd
3380168404Spjd	/*
3381168404Spjd	 * Destroy all recent snapshots and its dependends.
3382168404Spjd	 */
3383185029Spjd	cb.cb_force = force;
3384168404Spjd	cb.cb_target = snap->zfs_name;
3385168404Spjd	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3386168404Spjd	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3387168404Spjd
3388185029Spjd	if (cb.cb_error)
3389185029Spjd		return (-1);
3390168404Spjd
3391168404Spjd	/*
3392168404Spjd	 * Now that we have verified that the snapshot is the latest,
3393168404Spjd	 * rollback to the given snapshot.
3394168404Spjd	 */
3395168404Spjd
3396185029Spjd	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
3397185029Spjd		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
3398185029Spjd			return (-1);
3399185029Spjd		old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
3400185029Spjd		restore_resv =
3401185029Spjd		    (old_volsize == zfs_prop_get_int(zhp, resv_prop));
3402168404Spjd	}
3403168404Spjd
3404185029Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3405185029Spjd
3406185029Spjd	if (ZFS_IS_VOLUME(zhp))
3407185029Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3408185029Spjd	else
3409185029Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3410185029Spjd
3411168404Spjd	/*
3412185029Spjd	 * We rely on zfs_iter_children() to verify that there are no
3413185029Spjd	 * newer snapshots for the given dataset.  Therefore, we can
3414185029Spjd	 * simply pass the name on to the ioctl() call.  There is still
3415185029Spjd	 * an unlikely race condition where the user has taken a
3416185029Spjd	 * snapshot since we verified that this was the most recent.
3417185029Spjd	 *
3418168404Spjd	 */
3419185029Spjd	if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
3420185029Spjd		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
3421185029Spjd		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
3422185029Spjd		    zhp->zfs_name);
3423185029Spjd		return (err);
3424185029Spjd	}
3425168404Spjd
3426185029Spjd	/*
3427185029Spjd	 * For volumes, if the pre-rollback volsize matched the pre-
3428185029Spjd	 * rollback reservation and the volsize has changed then set
3429185029Spjd	 * the reservation property to the post-rollback volsize.
3430185029Spjd	 * Make a new handle since the rollback closed the dataset.
3431185029Spjd	 */
3432185029Spjd	if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
3433185029Spjd	    (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
3434185029Spjd		if (restore_resv) {
3435185029Spjd			new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
3436185029Spjd			if (old_volsize != new_volsize)
3437185029Spjd				err = zfs_prop_set_int(zhp, resv_prop,
3438185029Spjd				    new_volsize);
3439185029Spjd		}
3440185029Spjd		zfs_close(zhp);
3441185029Spjd	}
3442185029Spjd	return (err);
3443168404Spjd}
3444168404Spjd
3445168404Spjd/*
3446168404Spjd * Iterate over all dependents for a given dataset.  This includes both
3447168404Spjd * hierarchical dependents (children) and data dependents (snapshots and
3448168404Spjd * clones).  The bulk of the processing occurs in get_dependents() in
3449168404Spjd * libzfs_graph.c.
3450168404Spjd */
3451168404Spjdint
3452168404Spjdzfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
3453168404Spjd    zfs_iter_f func, void *data)
3454168404Spjd{
3455168404Spjd	char **dependents;
3456168404Spjd	size_t count;
3457168404Spjd	int i;
3458168404Spjd	zfs_handle_t *child;
3459168404Spjd	int ret = 0;
3460168404Spjd
3461168404Spjd	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
3462168404Spjd	    &dependents, &count) != 0)
3463168404Spjd		return (-1);
3464168404Spjd
3465168404Spjd	for (i = 0; i < count; i++) {
3466168404Spjd		if ((child = make_dataset_handle(zhp->zfs_hdl,
3467168404Spjd		    dependents[i])) == NULL)
3468168404Spjd			continue;
3469168404Spjd
3470168404Spjd		if ((ret = func(child, data)) != 0)
3471168404Spjd			break;
3472168404Spjd	}
3473168404Spjd
3474168404Spjd	for (i = 0; i < count; i++)
3475168404Spjd		free(dependents[i]);
3476168404Spjd	free(dependents);
3477168404Spjd
3478168404Spjd	return (ret);
3479168404Spjd}
3480168404Spjd
3481168404Spjd/*
3482168404Spjd * Renames the given dataset.
3483168404Spjd */
3484168404Spjdint
3485226705Spjdzfs_rename(zfs_handle_t *zhp, const char *target, renameflags_t flags)
3486168404Spjd{
3487168404Spjd	int ret;
3488168404Spjd	zfs_cmd_t zc = { 0 };
3489168404Spjd	char *delim;
3490168676Spjd	prop_changelist_t *cl = NULL;
3491168676Spjd	zfs_handle_t *zhrp = NULL;
3492168676Spjd	char *parentname = NULL;
3493168404Spjd	char parent[ZFS_MAXNAMELEN];
3494226676Spjd	char property[ZFS_MAXPROPLEN];
3495168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3496168404Spjd	char errbuf[1024];
3497168404Spjd
3498168404Spjd	/* if we have the same exact name, just return success */
3499168404Spjd	if (strcmp(zhp->zfs_name, target) == 0)
3500168404Spjd		return (0);
3501168404Spjd
3502168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3503168404Spjd	    "cannot rename to '%s'"), target);
3504168404Spjd
3505168404Spjd	/*
3506168404Spjd	 * Make sure the target name is valid
3507168404Spjd	 */
3508168404Spjd	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
3509168404Spjd		if ((strchr(target, '@') == NULL) ||
3510168404Spjd		    *target == '@') {
3511168404Spjd			/*
3512168404Spjd			 * Snapshot target name is abbreviated,
3513168404Spjd			 * reconstruct full dataset name
3514168404Spjd			 */
3515168404Spjd			(void) strlcpy(parent, zhp->zfs_name,
3516168404Spjd			    sizeof (parent));
3517168404Spjd			delim = strchr(parent, '@');
3518168404Spjd			if (strchr(target, '@') == NULL)
3519168404Spjd				*(++delim) = '\0';
3520168404Spjd			else
3521168404Spjd				*delim = '\0';
3522168404Spjd			(void) strlcat(parent, target, sizeof (parent));
3523168404Spjd			target = parent;
3524168404Spjd		} else {
3525168404Spjd			/*
3526168404Spjd			 * Make sure we're renaming within the same dataset.
3527168404Spjd			 */
3528168404Spjd			delim = strchr(target, '@');
3529168404Spjd			if (strncmp(zhp->zfs_name, target, delim - target)
3530168404Spjd			    != 0 || zhp->zfs_name[delim - target] != '@') {
3531168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3532168404Spjd				    "snapshots must be part of same "
3533168404Spjd				    "dataset"));
3534168404Spjd				return (zfs_error(hdl, EZFS_CROSSTARGET,
3535168404Spjd				    errbuf));
3536168404Spjd			}
3537168404Spjd		}
3538185029Spjd		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
3539168404Spjd			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3540168404Spjd	} else {
3541226705Spjd		if (flags.recurse) {
3542168676Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3543168676Spjd			    "recursive rename must be a snapshot"));
3544168676Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3545168676Spjd		}
3546168676Spjd
3547185029Spjd		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
3548168404Spjd			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3549168404Spjd
3550168404Spjd		/* validate parents */
3551219089Spjd		if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0)
3552168404Spjd			return (-1);
3553168404Spjd
3554168404Spjd		/* make sure we're in the same pool */
3555168404Spjd		verify((delim = strchr(target, '/')) != NULL);
3556168404Spjd		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3557168404Spjd		    zhp->zfs_name[delim - target] != '/') {
3558168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3559168404Spjd			    "datasets must be within same pool"));
3560168404Spjd			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3561168404Spjd		}
3562168404Spjd
3563168404Spjd		/* new name cannot be a child of the current dataset name */
3564219089Spjd		if (is_descendant(zhp->zfs_name, target)) {
3565168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3566219089Spjd			    "New dataset name cannot be a descendant of "
3567168404Spjd			    "current dataset name"));
3568168404Spjd			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3569168404Spjd		}
3570168404Spjd	}
3571168404Spjd
3572168404Spjd	(void) snprintf(errbuf, sizeof (errbuf),
3573168404Spjd	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
3574168404Spjd
3575168404Spjd	if (getzoneid() == GLOBAL_ZONEID &&
3576168404Spjd	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
3577168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3578168404Spjd		    "dataset is used in a non-global zone"));
3579168404Spjd		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3580168404Spjd	}
3581168404Spjd
3582226705Spjd	/*
3583226705Spjd	 * Avoid unmounting file systems with mountpoint property set to
3584226705Spjd	 * 'legacy' or 'none' even if -u option is not given.
3585226705Spjd	 */
3586226705Spjd	if (zhp->zfs_type == ZFS_TYPE_FILESYSTEM &&
3587226705Spjd	    !flags.recurse && !flags.nounmount &&
3588226705Spjd	    zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, property,
3589226705Spjd	    sizeof (property), NULL, NULL, 0, B_FALSE) == 0 &&
3590226705Spjd	    (strcmp(property, "legacy") == 0 ||
3591226705Spjd	     strcmp(property, "none") == 0)) {
3592226705Spjd		flags.nounmount = B_TRUE;
3593226705Spjd	}
3594168404Spjd
3595226705Spjd	if (flags.recurse) {
3596226705Spjd
3597185029Spjd		parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
3598185029Spjd		if (parentname == NULL) {
3599185029Spjd			ret = -1;
3600185029Spjd			goto error;
3601185029Spjd		}
3602168676Spjd		delim = strchr(parentname, '@');
3603168676Spjd		*delim = '\0';
3604185029Spjd		zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
3605168676Spjd		if (zhrp == NULL) {
3606185029Spjd			ret = -1;
3607185029Spjd			goto error;
3608168676Spjd		}
3609168676Spjd
3610168676Spjd	} else {
3611226676Spjd		if ((cl = changelist_gather(zhp, ZFS_PROP_NAME,
3612226705Spjd		    flags.nounmount ? CL_GATHER_DONT_UNMOUNT : 0, 0)) == NULL) {
3613168676Spjd			return (-1);
3614226676Spjd		}
3615168676Spjd
3616168676Spjd		if (changelist_haszonedchild(cl)) {
3617168676Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3618168676Spjd			    "child dataset with inherited mountpoint is used "
3619168676Spjd			    "in a non-global zone"));
3620168676Spjd			(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3621168676Spjd			goto error;
3622168676Spjd		}
3623168676Spjd
3624168676Spjd		if ((ret = changelist_prefix(cl)) != 0)
3625168676Spjd			goto error;
3626168404Spjd	}
3627168404Spjd
3628168404Spjd	if (ZFS_IS_VOLUME(zhp))
3629168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3630168404Spjd	else
3631168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3632168404Spjd
3633168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3634168404Spjd	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
3635168404Spjd
3636226705Spjd	zc.zc_cookie = flags.recurse ? 1 : 0;
3637226705Spjd	if (flags.nounmount)
3638226676Spjd		zc.zc_cookie |= 2;
3639168676Spjd
3640185029Spjd	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
3641168676Spjd		/*
3642168676Spjd		 * if it was recursive, the one that actually failed will
3643168676Spjd		 * be in zc.zc_name
3644168676Spjd		 */
3645168676Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3646185029Spjd		    "cannot rename '%s'"), zc.zc_name);
3647168404Spjd
3648226705Spjd		if (flags.recurse && errno == EEXIST) {
3649168676Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3650168676Spjd			    "a child dataset already has a snapshot "
3651168676Spjd			    "with the new name"));
3652185029Spjd			(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3653168676Spjd		} else {
3654168676Spjd			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3655168676Spjd		}
3656168676Spjd
3657168404Spjd		/*
3658168404Spjd		 * On failure, we still want to remount any filesystems that
3659168404Spjd		 * were previously mounted, so we don't alter the system state.
3660168404Spjd		 */
3661226705Spjd		if (!flags.recurse)
3662168676Spjd			(void) changelist_postfix(cl);
3663168404Spjd	} else {
3664226705Spjd		if (!flags.recurse) {
3665168676Spjd			changelist_rename(cl, zfs_get_name(zhp), target);
3666168676Spjd			ret = changelist_postfix(cl);
3667168676Spjd		}
3668168404Spjd	}
3669168404Spjd
3670168404Spjderror:
3671168676Spjd	if (parentname) {
3672168676Spjd		free(parentname);
3673168676Spjd	}
3674168676Spjd	if (zhrp) {
3675168676Spjd		zfs_close(zhrp);
3676168676Spjd	}
3677168676Spjd	if (cl) {
3678168676Spjd		changelist_free(cl);
3679168676Spjd	}
3680168404Spjd	return (ret);
3681168404Spjd}
3682168404Spjd
3683219089Spjdnvlist_t *
3684219089Spjdzfs_get_user_props(zfs_handle_t *zhp)
3685168404Spjd{
3686219089Spjd	return (zhp->zfs_user_props);
3687168676Spjd}
3688168676Spjd
3689168404Spjdnvlist_t *
3690219089Spjdzfs_get_recvd_props(zfs_handle_t *zhp)
3691168404Spjd{
3692219089Spjd	if (zhp->zfs_recvd_props == NULL)
3693219089Spjd		if (get_recvd_props_ioctl(zhp) != 0)
3694219089Spjd			return (NULL);
3695219089Spjd	return (zhp->zfs_recvd_props);
3696168404Spjd}
3697168404Spjd
3698168404Spjd/*
3699168404Spjd * This function is used by 'zfs list' to determine the exact set of columns to
3700168404Spjd * display, and their maximum widths.  This does two main things:
3701168404Spjd *
3702168404Spjd *      - If this is a list of all properties, then expand the list to include
3703168404Spjd *        all native properties, and set a flag so that for each dataset we look
3704168404Spjd *        for new unique user properties and add them to the list.
3705168404Spjd *
3706168404Spjd *      - For non fixed-width properties, keep track of the maximum width seen
3707219089Spjd *        so that we can size the column appropriately. If the user has
3708219089Spjd *        requested received property values, we also need to compute the width
3709219089Spjd *        of the RECEIVED column.
3710168404Spjd */
3711168404Spjdint
3712219089Spjdzfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received)
3713168404Spjd{
3714168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3715185029Spjd	zprop_list_t *entry;
3716185029Spjd	zprop_list_t **last, **start;
3717168404Spjd	nvlist_t *userprops, *propval;
3718168404Spjd	nvpair_t *elem;
3719168404Spjd	char *strval;
3720168404Spjd	char buf[ZFS_MAXPROPLEN];
3721168404Spjd
3722185029Spjd	if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
3723168404Spjd		return (-1);
3724168404Spjd
3725168404Spjd	userprops = zfs_get_user_props(zhp);
3726168404Spjd
3727168404Spjd	entry = *plp;
3728168404Spjd	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3729168404Spjd		/*
3730168404Spjd		 * Go through and add any user properties as necessary.  We
3731168404Spjd		 * start by incrementing our list pointer to the first
3732168404Spjd		 * non-native property.
3733168404Spjd		 */
3734168404Spjd		start = plp;
3735168404Spjd		while (*start != NULL) {
3736185029Spjd			if ((*start)->pl_prop == ZPROP_INVAL)
3737168404Spjd				break;
3738168404Spjd			start = &(*start)->pl_next;
3739168404Spjd		}
3740168404Spjd
3741168404Spjd		elem = NULL;
3742168404Spjd		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3743168404Spjd			/*
3744168404Spjd			 * See if we've already found this property in our list.
3745168404Spjd			 */
3746168404Spjd			for (last = start; *last != NULL;
3747168404Spjd			    last = &(*last)->pl_next) {
3748168404Spjd				if (strcmp((*last)->pl_user_prop,
3749168404Spjd				    nvpair_name(elem)) == 0)
3750168404Spjd					break;
3751168404Spjd			}
3752168404Spjd
3753168404Spjd			if (*last == NULL) {
3754168404Spjd				if ((entry = zfs_alloc(hdl,
3755185029Spjd				    sizeof (zprop_list_t))) == NULL ||
3756168404Spjd				    ((entry->pl_user_prop = zfs_strdup(hdl,
3757168404Spjd				    nvpair_name(elem)))) == NULL) {
3758168404Spjd					free(entry);
3759168404Spjd					return (-1);
3760168404Spjd				}
3761168404Spjd
3762185029Spjd				entry->pl_prop = ZPROP_INVAL;
3763168404Spjd				entry->pl_width = strlen(nvpair_name(elem));
3764168404Spjd				entry->pl_all = B_TRUE;
3765168404Spjd				*last = entry;
3766168404Spjd			}
3767168404Spjd		}
3768168404Spjd	}
3769168404Spjd
3770168404Spjd	/*
3771168404Spjd	 * Now go through and check the width of any non-fixed columns
3772168404Spjd	 */
3773168404Spjd	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3774168404Spjd		if (entry->pl_fixed)
3775168404Spjd			continue;
3776168404Spjd
3777185029Spjd		if (entry->pl_prop != ZPROP_INVAL) {
3778168404Spjd			if (zfs_prop_get(zhp, entry->pl_prop,
3779168404Spjd			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3780168404Spjd				if (strlen(buf) > entry->pl_width)
3781168404Spjd					entry->pl_width = strlen(buf);
3782168404Spjd			}
3783219089Spjd			if (received && zfs_prop_get_recvd(zhp,
3784219089Spjd			    zfs_prop_to_name(entry->pl_prop),
3785219089Spjd			    buf, sizeof (buf), B_FALSE) == 0)
3786219089Spjd				if (strlen(buf) > entry->pl_recvd_width)
3787219089Spjd					entry->pl_recvd_width = strlen(buf);
3788219089Spjd		} else {
3789219089Spjd			if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
3790219089Spjd			    &propval) == 0) {
3791219089Spjd				verify(nvlist_lookup_string(propval,
3792219089Spjd				    ZPROP_VALUE, &strval) == 0);
3793219089Spjd				if (strlen(strval) > entry->pl_width)
3794219089Spjd					entry->pl_width = strlen(strval);
3795219089Spjd			}
3796219089Spjd			if (received && zfs_prop_get_recvd(zhp,
3797219089Spjd			    entry->pl_user_prop,
3798219089Spjd			    buf, sizeof (buf), B_FALSE) == 0)
3799219089Spjd				if (strlen(buf) > entry->pl_recvd_width)
3800219089Spjd					entry->pl_recvd_width = strlen(buf);
3801168404Spjd		}
3802168404Spjd	}
3803168404Spjd
3804168404Spjd	return (0);
3805168404Spjd}
3806168404Spjd
3807185029Spjdint
3808185029Spjdzfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
3809209962Smm    char *resource, void *export, void *sharetab,
3810209962Smm    int sharemax, zfs_share_op_t operation)
3811185029Spjd{
3812185029Spjd	zfs_cmd_t zc = { 0 };
3813185029Spjd	int error;
3814185029Spjd
3815185029Spjd	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3816185029Spjd	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3817209962Smm	if (resource)
3818209962Smm		(void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
3819185029Spjd	zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
3820185029Spjd	zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
3821185029Spjd	zc.zc_share.z_sharetype = operation;
3822185029Spjd	zc.zc_share.z_sharemax = sharemax;
3823185029Spjd	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
3824185029Spjd	return (error);
3825185029Spjd}
3826185029Spjd
3827205198Sdelphijvoid
3828205198Sdelphijzfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
3829205198Sdelphij{
3830205198Sdelphij	nvpair_t *curr;
3831205198Sdelphij
3832205198Sdelphij	/*
3833205198Sdelphij	 * Keep a reference to the props-table against which we prune the
3834205198Sdelphij	 * properties.
3835205198Sdelphij	 */
3836205198Sdelphij	zhp->zfs_props_table = props;
3837205198Sdelphij
3838205198Sdelphij	curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
3839205198Sdelphij
3840205198Sdelphij	while (curr) {
3841205198Sdelphij		zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
3842205198Sdelphij		nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
3843205198Sdelphij
3844206199Sdelphij		/*
3845219089Spjd		 * User properties will result in ZPROP_INVAL, and since we
3846219089Spjd		 * only know how to prune standard ZFS properties, we always
3847219089Spjd		 * leave these in the list.  This can also happen if we
3848219089Spjd		 * encounter an unknown DSL property (when running older
3849219089Spjd		 * software, for example).
3850206199Sdelphij		 */
3851206199Sdelphij		if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
3852205198Sdelphij			(void) nvlist_remove(zhp->zfs_props,
3853205198Sdelphij			    nvpair_name(curr), nvpair_type(curr));
3854205198Sdelphij		curr = next;
3855205198Sdelphij	}
3856205198Sdelphij}
3857205198Sdelphij
3858209962Smm#ifdef sun
3859209962Smmstatic int
3860209962Smmzfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
3861209962Smm    zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
3862209962Smm{
3863209962Smm	zfs_cmd_t zc = { 0 };
3864209962Smm	nvlist_t *nvlist = NULL;
3865209962Smm	int error;
3866209962Smm
3867209962Smm	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3868209962Smm	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3869209962Smm	zc.zc_cookie = (uint64_t)cmd;
3870209962Smm
3871209962Smm	if (cmd == ZFS_SMB_ACL_RENAME) {
3872209962Smm		if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
3873209962Smm			(void) no_memory(hdl);
3874209962Smm			return (NULL);
3875209962Smm		}
3876209962Smm	}
3877209962Smm
3878209962Smm	switch (cmd) {
3879209962Smm	case ZFS_SMB_ACL_ADD:
3880209962Smm	case ZFS_SMB_ACL_REMOVE:
3881209962Smm		(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
3882209962Smm		break;
3883209962Smm	case ZFS_SMB_ACL_RENAME:
3884209962Smm		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
3885209962Smm		    resource1) != 0) {
3886209962Smm				(void) no_memory(hdl);
3887209962Smm				return (-1);
3888209962Smm		}
3889209962Smm		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
3890209962Smm		    resource2) != 0) {
3891209962Smm				(void) no_memory(hdl);
3892209962Smm				return (-1);
3893209962Smm		}
3894209962Smm		if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
3895209962Smm			nvlist_free(nvlist);
3896209962Smm			return (-1);
3897209962Smm		}
3898209962Smm		break;
3899209962Smm	case ZFS_SMB_ACL_PURGE:
3900209962Smm		break;
3901209962Smm	default:
3902209962Smm		return (-1);
3903209962Smm	}
3904209962Smm	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
3905209962Smm	if (nvlist)
3906209962Smm		nvlist_free(nvlist);
3907209962Smm	return (error);
3908209962Smm}
3909209962Smm
3910209962Smmint
3911209962Smmzfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
3912209962Smm    char *path, char *resource)
3913209962Smm{
3914209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
3915209962Smm	    resource, NULL));
3916209962Smm}
3917209962Smm
3918209962Smmint
3919209962Smmzfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
3920209962Smm    char *path, char *resource)
3921209962Smm{
3922209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
3923209962Smm	    resource, NULL));
3924209962Smm}
3925209962Smm
3926209962Smmint
3927209962Smmzfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
3928209962Smm{
3929209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
3930209962Smm	    NULL, NULL));
3931209962Smm}
3932209962Smm
3933209962Smmint
3934209962Smmzfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
3935209962Smm    char *oldname, char *newname)
3936209962Smm{
3937209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
3938209962Smm	    oldname, newname));
3939209962Smm}
3940209962Smm#endif	/* sun */
3941209962Smm
3942209962Smmint
3943209962Smmzfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
3944209962Smm    zfs_userspace_cb_t func, void *arg)
3945209962Smm{
3946209962Smm	zfs_cmd_t zc = { 0 };
3947209962Smm	int error;
3948209962Smm	zfs_useracct_t buf[100];
3949209962Smm
3950209962Smm	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3951209962Smm
3952209962Smm	zc.zc_objset_type = type;
3953209962Smm	zc.zc_nvlist_dst = (uintptr_t)buf;
3954209962Smm
3955209962Smm	/* CONSTCOND */
3956209962Smm	while (1) {
3957209962Smm		zfs_useracct_t *zua = buf;
3958209962Smm
3959209962Smm		zc.zc_nvlist_dst_size = sizeof (buf);
3960209962Smm		error = ioctl(zhp->zfs_hdl->libzfs_fd,
3961209962Smm		    ZFS_IOC_USERSPACE_MANY, &zc);
3962209962Smm		if (error || zc.zc_nvlist_dst_size == 0)
3963209962Smm			break;
3964209962Smm
3965209962Smm		while (zc.zc_nvlist_dst_size > 0) {
3966209962Smm			error = func(arg, zua->zu_domain, zua->zu_rid,
3967209962Smm			    zua->zu_space);
3968209962Smm			if (error != 0)
3969209962Smm				return (error);
3970209962Smm			zua++;
3971209962Smm			zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
3972209962Smm		}
3973209962Smm	}
3974209962Smm
3975209962Smm	return (error);
3976209962Smm}
3977209962Smm
3978219089Spjdint
3979219089Spjdzfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
3980219089Spjd    boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
3981219089Spjd    int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
3982219089Spjd{
3983219089Spjd	zfs_cmd_t zc = { 0 };
3984219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3985219089Spjd
3986219089Spjd	ASSERT(!recursive || dsobj == 0);
3987219089Spjd
3988219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3989219089Spjd	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
3990219089Spjd	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
3991219089Spjd	    >= sizeof (zc.zc_string))
3992219089Spjd		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
3993219089Spjd	zc.zc_cookie = recursive;
3994219089Spjd	zc.zc_temphold = temphold;
3995219089Spjd	zc.zc_cleanup_fd = cleanup_fd;
3996219089Spjd	zc.zc_sendobj = dsobj;
3997219089Spjd	zc.zc_createtxg = createtxg;
3998219089Spjd
3999219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) {
4000219089Spjd		char errbuf[ZFS_MAXNAMELEN+32];
4001219089Spjd
4002219089Spjd		/*
4003219089Spjd		 * if it was recursive, the one that actually failed will be in
4004219089Spjd		 * zc.zc_name.
4005219089Spjd		 */
4006219089Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4007219089Spjd		    "cannot hold '%s@%s'"), zc.zc_name, snapname);
4008219089Spjd		switch (errno) {
4009219089Spjd		case E2BIG:
4010219089Spjd			/*
4011219089Spjd			 * Temporary tags wind up having the ds object id
4012219089Spjd			 * prepended. So even if we passed the length check
4013219089Spjd			 * above, it's still possible for the tag to wind
4014219089Spjd			 * up being slightly too long.
4015219089Spjd			 */
4016219089Spjd			return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf));
4017219089Spjd		case ENOTSUP:
4018219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4019219089Spjd			    "pool must be upgraded"));
4020219089Spjd			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
4021219089Spjd		case EINVAL:
4022219089Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4023219089Spjd		case EEXIST:
4024219089Spjd			return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
4025219089Spjd		case ENOENT:
4026219089Spjd			if (enoent_ok)
4027219089Spjd				return (ENOENT);
4028219089Spjd			/* FALLTHROUGH */
4029219089Spjd		default:
4030219089Spjd			return (zfs_standard_error_fmt(hdl, errno, errbuf));
4031219089Spjd		}
4032219089Spjd	}
4033219089Spjd
4034219089Spjd	return (0);
4035219089Spjd}
4036219089Spjd
4037219089Spjdint
4038219089Spjdzfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4039219089Spjd    boolean_t recursive)
4040219089Spjd{
4041219089Spjd	zfs_cmd_t zc = { 0 };
4042219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4043219089Spjd
4044219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4045219089Spjd	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
4046219089Spjd	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
4047219089Spjd	    >= sizeof (zc.zc_string))
4048219089Spjd		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
4049219089Spjd	zc.zc_cookie = recursive;
4050219089Spjd
4051219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) {
4052219089Spjd		char errbuf[ZFS_MAXNAMELEN+32];
4053219089Spjd
4054219089Spjd		/*
4055219089Spjd		 * if it was recursive, the one that actually failed will be in
4056219089Spjd		 * zc.zc_name.
4057219089Spjd		 */
4058219089Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4059219089Spjd		    "cannot release '%s' from '%s@%s'"), tag, zc.zc_name,
4060219089Spjd		    snapname);
4061219089Spjd		switch (errno) {
4062219089Spjd		case ESRCH:
4063219089Spjd			return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
4064219089Spjd		case ENOTSUP:
4065219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4066219089Spjd			    "pool must be upgraded"));
4067219089Spjd			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
4068219089Spjd		case EINVAL:
4069219089Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4070219089Spjd		default:
4071219089Spjd			return (zfs_standard_error_fmt(hdl, errno, errbuf));
4072219089Spjd		}
4073219089Spjd	}
4074219089Spjd
4075219089Spjd	return (0);
4076219089Spjd}
4077219089Spjd
4078219089Spjdint
4079219089Spjdzfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
4080219089Spjd{
4081219089Spjd	zfs_cmd_t zc = { 0 };
4082219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4083219089Spjd	int nvsz = 2048;
4084219089Spjd	void *nvbuf;
4085219089Spjd	int err = 0;
4086219089Spjd	char errbuf[ZFS_MAXNAMELEN+32];
4087219089Spjd
4088219089Spjd	assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
4089219089Spjd	    zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4090219089Spjd
4091219089Spjdtryagain:
4092219089Spjd
4093219089Spjd	nvbuf = malloc(nvsz);
4094219089Spjd	if (nvbuf == NULL) {
4095219089Spjd		err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
4096219089Spjd		goto out;
4097219089Spjd	}
4098219089Spjd
4099219089Spjd	zc.zc_nvlist_dst_size = nvsz;
4100219089Spjd	zc.zc_nvlist_dst = (uintptr_t)nvbuf;
4101219089Spjd
4102219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
4103219089Spjd
4104219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_GET_FSACL, &zc) != 0) {
4105219089Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4106219089Spjd		    dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
4107219089Spjd		    zc.zc_name);
4108219089Spjd		switch (errno) {
4109219089Spjd		case ENOMEM:
4110219089Spjd			free(nvbuf);
4111219089Spjd			nvsz = zc.zc_nvlist_dst_size;
4112219089Spjd			goto tryagain;
4113219089Spjd
4114219089Spjd		case ENOTSUP:
4115219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4116219089Spjd			    "pool must be upgraded"));
4117219089Spjd			err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4118219089Spjd			break;
4119219089Spjd		case EINVAL:
4120219089Spjd			err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4121219089Spjd			break;
4122219089Spjd		case ENOENT:
4123219089Spjd			err = zfs_error(hdl, EZFS_NOENT, errbuf);
4124219089Spjd			break;
4125219089Spjd		default:
4126219089Spjd			err = zfs_standard_error_fmt(hdl, errno, errbuf);
4127219089Spjd			break;
4128219089Spjd		}
4129219089Spjd	} else {
4130219089Spjd		/* success */
4131219089Spjd		int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
4132219089Spjd		if (rc) {
4133219089Spjd			(void) snprintf(errbuf, sizeof (errbuf), dgettext(
4134219089Spjd			    TEXT_DOMAIN, "cannot get permissions on '%s'"),
4135219089Spjd			    zc.zc_name);
4136219089Spjd			err = zfs_standard_error_fmt(hdl, rc, errbuf);
4137219089Spjd		}
4138219089Spjd	}
4139219089Spjd
4140219089Spjd	free(nvbuf);
4141219089Spjdout:
4142219089Spjd	return (err);
4143219089Spjd}
4144219089Spjd
4145219089Spjdint
4146219089Spjdzfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
4147219089Spjd{
4148219089Spjd	zfs_cmd_t zc = { 0 };
4149219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4150219089Spjd	char *nvbuf;
4151219089Spjd	char errbuf[ZFS_MAXNAMELEN+32];
4152219089Spjd	size_t nvsz;
4153219089Spjd	int err;
4154219089Spjd
4155219089Spjd	assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
4156219089Spjd	    zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4157219089Spjd
4158219089Spjd	err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
4159219089Spjd	assert(err == 0);
4160219089Spjd
4161219089Spjd	nvbuf = malloc(nvsz);
4162219089Spjd
4163219089Spjd	err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
4164219089Spjd	assert(err == 0);
4165219089Spjd
4166219089Spjd	zc.zc_nvlist_src_size = nvsz;
4167219089Spjd	zc.zc_nvlist_src = (uintptr_t)nvbuf;
4168219089Spjd	zc.zc_perm_action = un;
4169219089Spjd
4170219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4171219089Spjd
4172219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
4173219089Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4174219089Spjd		    dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
4175219089Spjd		    zc.zc_name);
4176219089Spjd		switch (errno) {
4177219089Spjd		case ENOTSUP:
4178219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4179219089Spjd			    "pool must be upgraded"));
4180219089Spjd			err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4181219089Spjd			break;
4182219089Spjd		case EINVAL:
4183219089Spjd			err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4184219089Spjd			break;
4185219089Spjd		case ENOENT:
4186219089Spjd			err = zfs_error(hdl, EZFS_NOENT, errbuf);
4187219089Spjd			break;
4188219089Spjd		default:
4189219089Spjd			err = zfs_standard_error_fmt(hdl, errno, errbuf);
4190219089Spjd			break;
4191219089Spjd		}
4192219089Spjd	}
4193219089Spjd
4194219089Spjd	free(nvbuf);
4195219089Spjd
4196219089Spjd	return (err);
4197219089Spjd}
4198219089Spjd
4199219089Spjdint
4200219089Spjdzfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
4201219089Spjd{
4202219089Spjd	zfs_cmd_t zc = { 0 };
4203219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4204219089Spjd	int nvsz = 2048;
4205219089Spjd	void *nvbuf;
4206219089Spjd	int err = 0;
4207219089Spjd	char errbuf[ZFS_MAXNAMELEN+32];
4208219089Spjd
4209219089Spjd	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
4210219089Spjd
4211219089Spjdtryagain:
4212219089Spjd
4213219089Spjd	nvbuf = malloc(nvsz);
4214219089Spjd	if (nvbuf == NULL) {
4215219089Spjd		err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
4216219089Spjd		goto out;
4217219089Spjd	}
4218219089Spjd
4219219089Spjd	zc.zc_nvlist_dst_size = nvsz;
4220219089Spjd	zc.zc_nvlist_dst = (uintptr_t)nvbuf;
4221219089Spjd
4222219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
4223219089Spjd
4224219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) != 0) {
4225219089Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4226219089Spjd		    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
4227219089Spjd		    zc.zc_name);
4228219089Spjd		switch (errno) {
4229219089Spjd		case ENOMEM:
4230219089Spjd			free(nvbuf);
4231219089Spjd			nvsz = zc.zc_nvlist_dst_size;
4232219089Spjd			goto tryagain;
4233219089Spjd
4234219089Spjd		case ENOTSUP:
4235219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4236219089Spjd			    "pool must be upgraded"));
4237219089Spjd			err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4238219089Spjd			break;
4239219089Spjd		case EINVAL:
4240219089Spjd			err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4241219089Spjd			break;
4242219089Spjd		case ENOENT:
4243219089Spjd			err = zfs_error(hdl, EZFS_NOENT, errbuf);
4244219089Spjd			break;
4245219089Spjd		default:
4246219089Spjd			err = zfs_standard_error_fmt(hdl, errno, errbuf);
4247219089Spjd			break;
4248219089Spjd		}
4249219089Spjd	} else {
4250219089Spjd		/* success */
4251219089Spjd		int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
4252219089Spjd		if (rc) {
4253219089Spjd			(void) snprintf(errbuf, sizeof (errbuf),
4254219089Spjd			    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
4255219089Spjd			    zc.zc_name);
4256219089Spjd			err = zfs_standard_error_fmt(hdl, rc, errbuf);
4257219089Spjd		}
4258219089Spjd	}
4259219089Spjd
4260219089Spjd	free(nvbuf);
4261219089Spjdout:
4262219089Spjd	return (err);
4263219089Spjd}
4264219089Spjd
4265219089Spjduint64_t
4266219089Spjdzvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
4267219089Spjd{
4268219089Spjd	uint64_t numdb;
4269219089Spjd	uint64_t nblocks, volblocksize;
4270219089Spjd	int ncopies;
4271219089Spjd	char *strval;
4272219089Spjd
4273219089Spjd	if (nvlist_lookup_string(props,
4274219089Spjd	    zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
4275219089Spjd		ncopies = atoi(strval);
4276219089Spjd	else
4277219089Spjd		ncopies = 1;
4278219089Spjd	if (nvlist_lookup_uint64(props,
4279219089Spjd	    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
4280219089Spjd	    &volblocksize) != 0)
4281219089Spjd		volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
4282219089Spjd	nblocks = volsize/volblocksize;
4283219089Spjd	/* start with metadnode L0-L6 */
4284219089Spjd	numdb = 7;
4285219089Spjd	/* calculate number of indirects */
4286219089Spjd	while (nblocks > 1) {
4287219089Spjd		nblocks += DNODES_PER_LEVEL - 1;
4288219089Spjd		nblocks /= DNODES_PER_LEVEL;
4289219089Spjd		numdb += nblocks;
4290219089Spjd	}
4291219089Spjd	numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
4292219089Spjd	volsize *= ncopies;
4293219089Spjd	/*
4294219089Spjd	 * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
4295219089Spjd	 * compressed, but in practice they compress down to about
4296219089Spjd	 * 1100 bytes
4297219089Spjd	 */
4298219089Spjd	numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
4299219089Spjd	volsize += numdb;
4300219089Spjd	return (volsize);
4301219089Spjd}
4302219089Spjd
4303168404Spjd/*
4304168404Spjd * Attach/detach the given filesystem to/from the given jail.
4305168404Spjd */
4306168404Spjdint
4307168404Spjdzfs_jail(zfs_handle_t *zhp, int jailid, int attach)
4308168404Spjd{
4309168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4310168404Spjd	zfs_cmd_t zc = { 0 };
4311168404Spjd	char errbuf[1024];
4312224525Smm	unsigned long cmd;
4313224525Smm	int ret;
4314168404Spjd
4315168404Spjd	if (attach) {
4316168404Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4317168404Spjd		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
4318168404Spjd	} else {
4319168404Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4320168404Spjd		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
4321168404Spjd	}
4322168404Spjd
4323168404Spjd	switch (zhp->zfs_type) {
4324168404Spjd	case ZFS_TYPE_VOLUME:
4325168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4326168404Spjd		    "volumes can not be jailed"));
4327168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4328168404Spjd	case ZFS_TYPE_SNAPSHOT:
4329168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4330168404Spjd		    "snapshots can not be jailed"));
4331168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4332168404Spjd	}
4333168404Spjd	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4334168404Spjd
4335168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4336168404Spjd	zc.zc_objset_type = DMU_OST_ZFS;
4337168404Spjd	zc.zc_jailid = jailid;
4338168404Spjd
4339168404Spjd	cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
4340168404Spjd	if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0)
4341168404Spjd		zfs_standard_error(hdl, errno, errbuf);
4342168404Spjd
4343168404Spjd	return (ret);
4344168404Spjd}
4345