libzfs_dataset.c revision 219089
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.
25168404Spjd */
26168404Spjd
27168404Spjd#include <ctype.h>
28168404Spjd#include <errno.h>
29168404Spjd#include <libintl.h>
30168404Spjd#include <math.h>
31168404Spjd#include <stdio.h>
32168404Spjd#include <stdlib.h>
33168404Spjd#include <strings.h>
34168404Spjd#include <unistd.h>
35185029Spjd#include <stddef.h>
36168404Spjd#include <zone.h>
37168404Spjd#include <fcntl.h>
38168404Spjd#include <sys/mntent.h>
39168404Spjd#include <sys/mount.h>
40185029Spjd#include <priv.h>
41185029Spjd#include <pwd.h>
42185029Spjd#include <grp.h>
43185029Spjd#include <stddef.h>
44209962Smm#include <idmap.h>
45168404Spjd
46219089Spjd#include <sys/dnode.h>
47168404Spjd#include <sys/spa.h>
48168404Spjd#include <sys/zap.h>
49209962Smm#include <sys/misc.h>
50168404Spjd#include <libzfs.h>
51168404Spjd
52168404Spjd#include "zfs_namecheck.h"
53168404Spjd#include "zfs_prop.h"
54168404Spjd#include "libzfs_impl.h"
55185029Spjd#include "zfs_deleg.h"
56168404Spjd
57209962Smmstatic int userquota_propname_decode(const char *propname, boolean_t zoned,
58209962Smm    zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp);
59168676Spjd
60168404Spjd/*
61168404Spjd * Given a single type (not a mask of types), return the type in a human
62168404Spjd * readable form.
63168404Spjd */
64168404Spjdconst char *
65168404Spjdzfs_type_to_name(zfs_type_t type)
66168404Spjd{
67168404Spjd	switch (type) {
68168404Spjd	case ZFS_TYPE_FILESYSTEM:
69168404Spjd		return (dgettext(TEXT_DOMAIN, "filesystem"));
70168404Spjd	case ZFS_TYPE_SNAPSHOT:
71168404Spjd		return (dgettext(TEXT_DOMAIN, "snapshot"));
72168404Spjd	case ZFS_TYPE_VOLUME:
73168404Spjd		return (dgettext(TEXT_DOMAIN, "volume"));
74168404Spjd	}
75168404Spjd
76168404Spjd	return (NULL);
77168404Spjd}
78168404Spjd
79168404Spjd/*
80168404Spjd * Given a path and mask of ZFS types, return a string describing this dataset.
81168404Spjd * This is used when we fail to open a dataset and we cannot get an exact type.
82168404Spjd * We guess what the type would have been based on the path and the mask of
83168404Spjd * acceptable types.
84168404Spjd */
85168404Spjdstatic const char *
86168404Spjdpath_to_str(const char *path, int types)
87168404Spjd{
88168404Spjd	/*
89168404Spjd	 * When given a single type, always report the exact type.
90168404Spjd	 */
91168404Spjd	if (types == ZFS_TYPE_SNAPSHOT)
92168404Spjd		return (dgettext(TEXT_DOMAIN, "snapshot"));
93168404Spjd	if (types == ZFS_TYPE_FILESYSTEM)
94168404Spjd		return (dgettext(TEXT_DOMAIN, "filesystem"));
95168404Spjd	if (types == ZFS_TYPE_VOLUME)
96168404Spjd		return (dgettext(TEXT_DOMAIN, "volume"));
97168404Spjd
98168404Spjd	/*
99168404Spjd	 * The user is requesting more than one type of dataset.  If this is the
100168404Spjd	 * case, consult the path itself.  If we're looking for a snapshot, and
101168404Spjd	 * a '@' is found, then report it as "snapshot".  Otherwise, remove the
102168404Spjd	 * snapshot attribute and try again.
103168404Spjd	 */
104168404Spjd	if (types & ZFS_TYPE_SNAPSHOT) {
105168404Spjd		if (strchr(path, '@') != NULL)
106168404Spjd			return (dgettext(TEXT_DOMAIN, "snapshot"));
107168404Spjd		return (path_to_str(path, types & ~ZFS_TYPE_SNAPSHOT));
108168404Spjd	}
109168404Spjd
110168404Spjd	/*
111168404Spjd	 * The user has requested either filesystems or volumes.
112168404Spjd	 * We have no way of knowing a priori what type this would be, so always
113168404Spjd	 * report it as "filesystem" or "volume", our two primitive types.
114168404Spjd	 */
115168404Spjd	if (types & ZFS_TYPE_FILESYSTEM)
116168404Spjd		return (dgettext(TEXT_DOMAIN, "filesystem"));
117168404Spjd
118168404Spjd	assert(types & ZFS_TYPE_VOLUME);
119168404Spjd	return (dgettext(TEXT_DOMAIN, "volume"));
120168404Spjd}
121168404Spjd
122168404Spjd/*
123168404Spjd * Validate a ZFS path.  This is used even before trying to open the dataset, to
124209962Smm * provide a more meaningful error message.  We call zfs_error_aux() to
125209962Smm * explain exactly why the name was not valid.
126168404Spjd */
127219089Spjdint
128185029Spjdzfs_validate_name(libzfs_handle_t *hdl, const char *path, int type,
129185029Spjd    boolean_t modifying)
130168404Spjd{
131168404Spjd	namecheck_err_t why;
132168404Spjd	char what;
133168404Spjd
134219089Spjd	(void) zfs_prop_get_table();
135168404Spjd	if (dataset_namecheck(path, &why, &what) != 0) {
136168404Spjd		if (hdl != NULL) {
137168404Spjd			switch (why) {
138168404Spjd			case NAME_ERR_TOOLONG:
139168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
140168404Spjd				    "name is too long"));
141168404Spjd				break;
142168404Spjd
143168404Spjd			case NAME_ERR_LEADING_SLASH:
144168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
145168404Spjd				    "leading slash in name"));
146168404Spjd				break;
147168404Spjd
148168404Spjd			case NAME_ERR_EMPTY_COMPONENT:
149168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
150168404Spjd				    "empty component in name"));
151168404Spjd				break;
152168404Spjd
153168404Spjd			case NAME_ERR_TRAILING_SLASH:
154168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
155168404Spjd				    "trailing slash in name"));
156168404Spjd				break;
157168404Spjd
158168404Spjd			case NAME_ERR_INVALCHAR:
159168404Spjd				zfs_error_aux(hdl,
160168404Spjd				    dgettext(TEXT_DOMAIN, "invalid character "
161168404Spjd				    "'%c' in name"), what);
162168404Spjd				break;
163168404Spjd
164168404Spjd			case NAME_ERR_MULTIPLE_AT:
165168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
166168404Spjd				    "multiple '@' delimiters in name"));
167168404Spjd				break;
168168404Spjd
169168404Spjd			case NAME_ERR_NOLETTER:
170168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
171168404Spjd				    "pool doesn't begin with a letter"));
172168404Spjd				break;
173168404Spjd
174168404Spjd			case NAME_ERR_RESERVED:
175168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
176168404Spjd				    "name is reserved"));
177168404Spjd				break;
178168404Spjd
179168404Spjd			case NAME_ERR_DISKLIKE:
180168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
181168404Spjd				    "reserved disk name"));
182168404Spjd				break;
183168404Spjd			}
184168404Spjd		}
185168404Spjd
186168404Spjd		return (0);
187168404Spjd	}
188168404Spjd
189168404Spjd	if (!(type & ZFS_TYPE_SNAPSHOT) && strchr(path, '@') != NULL) {
190168404Spjd		if (hdl != NULL)
191168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
192168404Spjd			    "snapshot delimiter '@' in filesystem name"));
193168404Spjd		return (0);
194168404Spjd	}
195168404Spjd
196168404Spjd	if (type == ZFS_TYPE_SNAPSHOT && strchr(path, '@') == NULL) {
197168404Spjd		if (hdl != NULL)
198168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
199168404Spjd			    "missing '@' delimiter in snapshot name"));
200168404Spjd		return (0);
201168404Spjd	}
202168404Spjd
203185029Spjd	if (modifying && strchr(path, '%') != NULL) {
204185029Spjd		if (hdl != NULL)
205185029Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
206185029Spjd			    "invalid character %c in name"), '%');
207185029Spjd		return (0);
208185029Spjd	}
209185029Spjd
210168404Spjd	return (-1);
211168404Spjd}
212168404Spjd
213168404Spjdint
214168404Spjdzfs_name_valid(const char *name, zfs_type_t type)
215168404Spjd{
216185029Spjd	if (type == ZFS_TYPE_POOL)
217185029Spjd		return (zpool_name_valid(NULL, B_FALSE, name));
218185029Spjd	return (zfs_validate_name(NULL, name, type, B_FALSE));
219168404Spjd}
220168404Spjd
221168404Spjd/*
222168404Spjd * This function takes the raw DSL properties, and filters out the user-defined
223168404Spjd * properties into a separate nvlist.
224168404Spjd */
225185029Spjdstatic nvlist_t *
226185029Spjdprocess_user_props(zfs_handle_t *zhp, nvlist_t *props)
227168404Spjd{
228168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
229168404Spjd	nvpair_t *elem;
230168404Spjd	nvlist_t *propval;
231185029Spjd	nvlist_t *nvl;
232168404Spjd
233185029Spjd	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
234185029Spjd		(void) no_memory(hdl);
235185029Spjd		return (NULL);
236185029Spjd	}
237168404Spjd
238168404Spjd	elem = NULL;
239185029Spjd	while ((elem = nvlist_next_nvpair(props, elem)) != NULL) {
240168404Spjd		if (!zfs_prop_user(nvpair_name(elem)))
241168404Spjd			continue;
242168404Spjd
243168404Spjd		verify(nvpair_value_nvlist(elem, &propval) == 0);
244185029Spjd		if (nvlist_add_nvlist(nvl, nvpair_name(elem), propval) != 0) {
245185029Spjd			nvlist_free(nvl);
246185029Spjd			(void) no_memory(hdl);
247185029Spjd			return (NULL);
248185029Spjd		}
249168404Spjd	}
250168404Spjd
251185029Spjd	return (nvl);
252168404Spjd}
253168404Spjd
254185029Spjdstatic zpool_handle_t *
255185029Spjdzpool_add_handle(zfs_handle_t *zhp, const char *pool_name)
256185029Spjd{
257185029Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
258185029Spjd	zpool_handle_t *zph;
259185029Spjd
260185029Spjd	if ((zph = zpool_open_canfail(hdl, pool_name)) != NULL) {
261185029Spjd		if (hdl->libzfs_pool_handles != NULL)
262185029Spjd			zph->zpool_next = hdl->libzfs_pool_handles;
263185029Spjd		hdl->libzfs_pool_handles = zph;
264185029Spjd	}
265185029Spjd	return (zph);
266185029Spjd}
267185029Spjd
268185029Spjdstatic zpool_handle_t *
269185029Spjdzpool_find_handle(zfs_handle_t *zhp, const char *pool_name, int len)
270185029Spjd{
271185029Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
272185029Spjd	zpool_handle_t *zph = hdl->libzfs_pool_handles;
273185029Spjd
274185029Spjd	while ((zph != NULL) &&
275185029Spjd	    (strncmp(pool_name, zpool_get_name(zph), len) != 0))
276185029Spjd		zph = zph->zpool_next;
277185029Spjd	return (zph);
278185029Spjd}
279185029Spjd
280168404Spjd/*
281185029Spjd * Returns a handle to the pool that contains the provided dataset.
282185029Spjd * If a handle to that pool already exists then that handle is returned.
283185029Spjd * Otherwise, a new handle is created and added to the list of handles.
284185029Spjd */
285185029Spjdstatic zpool_handle_t *
286185029Spjdzpool_handle(zfs_handle_t *zhp)
287185029Spjd{
288185029Spjd	char *pool_name;
289185029Spjd	int len;
290185029Spjd	zpool_handle_t *zph;
291185029Spjd
292185029Spjd	len = strcspn(zhp->zfs_name, "/@") + 1;
293185029Spjd	pool_name = zfs_alloc(zhp->zfs_hdl, len);
294185029Spjd	(void) strlcpy(pool_name, zhp->zfs_name, len);
295185029Spjd
296185029Spjd	zph = zpool_find_handle(zhp, pool_name, len);
297185029Spjd	if (zph == NULL)
298185029Spjd		zph = zpool_add_handle(zhp, pool_name);
299185029Spjd
300185029Spjd	free(pool_name);
301185029Spjd	return (zph);
302185029Spjd}
303185029Spjd
304185029Spjdvoid
305185029Spjdzpool_free_handles(libzfs_handle_t *hdl)
306185029Spjd{
307185029Spjd	zpool_handle_t *next, *zph = hdl->libzfs_pool_handles;
308185029Spjd
309185029Spjd	while (zph != NULL) {
310185029Spjd		next = zph->zpool_next;
311185029Spjd		zpool_close(zph);
312185029Spjd		zph = next;
313185029Spjd	}
314185029Spjd	hdl->libzfs_pool_handles = NULL;
315185029Spjd}
316185029Spjd
317185029Spjd/*
318168404Spjd * Utility function to gather stats (objset and zpl) for the given object.
319168404Spjd */
320219089Spjdstatic int
321209962Smmget_stats_ioctl(zfs_handle_t *zhp, zfs_cmd_t *zc)
322168404Spjd{
323168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
324168404Spjd
325209962Smm	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
326168404Spjd
327209962Smm	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, zc) != 0) {
328168404Spjd		if (errno == ENOMEM) {
329209962Smm			if (zcmd_expand_dst_nvlist(hdl, zc) != 0) {
330168404Spjd				return (-1);
331168404Spjd			}
332168404Spjd		} else {
333168404Spjd			return (-1);
334168404Spjd		}
335168404Spjd	}
336209962Smm	return (0);
337209962Smm}
338168404Spjd
339219089Spjd/*
340219089Spjd * Utility function to get the received properties of the given object.
341219089Spjd */
342209962Smmstatic int
343219089Spjdget_recvd_props_ioctl(zfs_handle_t *zhp)
344219089Spjd{
345219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
346219089Spjd	nvlist_t *recvdprops;
347219089Spjd	zfs_cmd_t zc = { 0 };
348219089Spjd	int err;
349219089Spjd
350219089Spjd	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0)
351219089Spjd		return (-1);
352219089Spjd
353219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
354219089Spjd
355219089Spjd	while (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_RECVD_PROPS, &zc) != 0) {
356219089Spjd		if (errno == ENOMEM) {
357219089Spjd			if (zcmd_expand_dst_nvlist(hdl, &zc) != 0) {
358219089Spjd				return (-1);
359219089Spjd			}
360219089Spjd		} else {
361219089Spjd			zcmd_free_nvlists(&zc);
362219089Spjd			return (-1);
363219089Spjd		}
364219089Spjd	}
365219089Spjd
366219089Spjd	err = zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &recvdprops);
367219089Spjd	zcmd_free_nvlists(&zc);
368219089Spjd	if (err != 0)
369219089Spjd		return (-1);
370219089Spjd
371219089Spjd	nvlist_free(zhp->zfs_recvd_props);
372219089Spjd	zhp->zfs_recvd_props = recvdprops;
373219089Spjd
374219089Spjd	return (0);
375219089Spjd}
376219089Spjd
377219089Spjdstatic int
378209962Smmput_stats_zhdl(zfs_handle_t *zhp, zfs_cmd_t *zc)
379209962Smm{
380209962Smm	nvlist_t *allprops, *userprops;
381168404Spjd
382209962Smm	zhp->zfs_dmustats = zc->zc_objset_stats; /* structure assignment */
383209962Smm
384209962Smm	if (zcmd_read_dst_nvlist(zhp->zfs_hdl, zc, &allprops) != 0) {
385168404Spjd		return (-1);
386168404Spjd	}
387168404Spjd
388209962Smm	/*
389209962Smm	 * XXX Why do we store the user props separately, in addition to
390209962Smm	 * storing them in zfs_props?
391209962Smm	 */
392185029Spjd	if ((userprops = process_user_props(zhp, allprops)) == NULL) {
393185029Spjd		nvlist_free(allprops);
394168404Spjd		return (-1);
395185029Spjd	}
396168404Spjd
397185029Spjd	nvlist_free(zhp->zfs_props);
398185029Spjd	nvlist_free(zhp->zfs_user_props);
399185029Spjd
400185029Spjd	zhp->zfs_props = allprops;
401185029Spjd	zhp->zfs_user_props = userprops;
402185029Spjd
403168404Spjd	return (0);
404168404Spjd}
405168404Spjd
406209962Smmstatic int
407209962Smmget_stats(zfs_handle_t *zhp)
408209962Smm{
409209962Smm	int rc = 0;
410209962Smm	zfs_cmd_t zc = { 0 };
411209962Smm
412209962Smm	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
413209962Smm		return (-1);
414209962Smm	if (get_stats_ioctl(zhp, &zc) != 0)
415209962Smm		rc = -1;
416209962Smm	else if (put_stats_zhdl(zhp, &zc) != 0)
417209962Smm		rc = -1;
418209962Smm	zcmd_free_nvlists(&zc);
419209962Smm	return (rc);
420209962Smm}
421209962Smm
422168404Spjd/*
423168404Spjd * Refresh the properties currently stored in the handle.
424168404Spjd */
425168404Spjdvoid
426168404Spjdzfs_refresh_properties(zfs_handle_t *zhp)
427168404Spjd{
428168404Spjd	(void) get_stats(zhp);
429168404Spjd}
430168404Spjd
431168404Spjd/*
432168404Spjd * Makes a handle from the given dataset name.  Used by zfs_open() and
433168404Spjd * zfs_iter_* to create child handles on the fly.
434168404Spjd */
435209962Smmstatic int
436209962Smmmake_dataset_handle_common(zfs_handle_t *zhp, zfs_cmd_t *zc)
437168404Spjd{
438219089Spjd	if (put_stats_zhdl(zhp, zc) != 0)
439209962Smm		return (-1);
440168404Spjd
441168404Spjd	/*
442168404Spjd	 * We've managed to open the dataset and gather statistics.  Determine
443168404Spjd	 * the high-level type.
444168404Spjd	 */
445168404Spjd	if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
446168404Spjd		zhp->zfs_head_type = ZFS_TYPE_VOLUME;
447168404Spjd	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
448168404Spjd		zhp->zfs_head_type = ZFS_TYPE_FILESYSTEM;
449168404Spjd	else
450168404Spjd		abort();
451168404Spjd
452168404Spjd	if (zhp->zfs_dmustats.dds_is_snapshot)
453168404Spjd		zhp->zfs_type = ZFS_TYPE_SNAPSHOT;
454168404Spjd	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZVOL)
455168404Spjd		zhp->zfs_type = ZFS_TYPE_VOLUME;
456168404Spjd	else if (zhp->zfs_dmustats.dds_type == DMU_OST_ZFS)
457168404Spjd		zhp->zfs_type = ZFS_TYPE_FILESYSTEM;
458168404Spjd	else
459168404Spjd		abort();	/* we should never see any other types */
460168404Spjd
461219089Spjd	if ((zhp->zpool_hdl = zpool_handle(zhp)) == NULL)
462219089Spjd		return (-1);
463219089Spjd
464209962Smm	return (0);
465209962Smm}
466209962Smm
467209962Smmzfs_handle_t *
468209962Smmmake_dataset_handle(libzfs_handle_t *hdl, const char *path)
469209962Smm{
470209962Smm	zfs_cmd_t zc = { 0 };
471209962Smm
472209962Smm	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
473209962Smm
474209962Smm	if (zhp == NULL)
475209962Smm		return (NULL);
476209962Smm
477209962Smm	zhp->zfs_hdl = hdl;
478209962Smm	(void) strlcpy(zhp->zfs_name, path, sizeof (zhp->zfs_name));
479209962Smm	if (zcmd_alloc_dst_nvlist(hdl, &zc, 0) != 0) {
480209962Smm		free(zhp);
481209962Smm		return (NULL);
482209962Smm	}
483209962Smm	if (get_stats_ioctl(zhp, &zc) == -1) {
484209962Smm		zcmd_free_nvlists(&zc);
485209962Smm		free(zhp);
486209962Smm		return (NULL);
487209962Smm	}
488209962Smm	if (make_dataset_handle_common(zhp, &zc) == -1) {
489209962Smm		free(zhp);
490209962Smm		zhp = NULL;
491209962Smm	}
492209962Smm	zcmd_free_nvlists(&zc);
493168404Spjd	return (zhp);
494168404Spjd}
495168404Spjd
496209962Smmstatic zfs_handle_t *
497209962Smmmake_dataset_handle_zc(libzfs_handle_t *hdl, zfs_cmd_t *zc)
498209962Smm{
499209962Smm	zfs_handle_t *zhp = calloc(sizeof (zfs_handle_t), 1);
500209962Smm
501209962Smm	if (zhp == NULL)
502209962Smm		return (NULL);
503209962Smm
504209962Smm	zhp->zfs_hdl = hdl;
505209962Smm	(void) strlcpy(zhp->zfs_name, zc->zc_name, sizeof (zhp->zfs_name));
506209962Smm	if (make_dataset_handle_common(zhp, zc) == -1) {
507209962Smm		free(zhp);
508209962Smm		return (NULL);
509209962Smm	}
510209962Smm	return (zhp);
511209962Smm}
512209962Smm
513168404Spjd/*
514168404Spjd * Opens the given snapshot, filesystem, or volume.   The 'types'
515168404Spjd * argument is a mask of acceptable types.  The function will print an
516168404Spjd * appropriate error message and return NULL if it can't be opened.
517168404Spjd */
518168404Spjdzfs_handle_t *
519168404Spjdzfs_open(libzfs_handle_t *hdl, const char *path, int types)
520168404Spjd{
521168404Spjd	zfs_handle_t *zhp;
522168404Spjd	char errbuf[1024];
523168404Spjd
524168404Spjd	(void) snprintf(errbuf, sizeof (errbuf),
525168404Spjd	    dgettext(TEXT_DOMAIN, "cannot open '%s'"), path);
526168404Spjd
527168404Spjd	/*
528168404Spjd	 * Validate the name before we even try to open it.
529168404Spjd	 */
530185029Spjd	if (!zfs_validate_name(hdl, path, ZFS_TYPE_DATASET, B_FALSE)) {
531168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
532168404Spjd		    "invalid dataset name"));
533168404Spjd		(void) zfs_error(hdl, EZFS_INVALIDNAME, errbuf);
534168404Spjd		return (NULL);
535168404Spjd	}
536168404Spjd
537168404Spjd	/*
538168404Spjd	 * Try to get stats for the dataset, which will tell us if it exists.
539168404Spjd	 */
540168404Spjd	errno = 0;
541168404Spjd	if ((zhp = make_dataset_handle(hdl, path)) == NULL) {
542168404Spjd		(void) zfs_standard_error(hdl, errno, errbuf);
543168404Spjd		return (NULL);
544168404Spjd	}
545168404Spjd
546168404Spjd	if (!(types & zhp->zfs_type)) {
547168404Spjd		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
548168404Spjd		zfs_close(zhp);
549168404Spjd		return (NULL);
550168404Spjd	}
551168404Spjd
552168404Spjd	return (zhp);
553168404Spjd}
554168404Spjd
555168404Spjd/*
556168404Spjd * Release a ZFS handle.  Nothing to do but free the associated memory.
557168404Spjd */
558168404Spjdvoid
559168404Spjdzfs_close(zfs_handle_t *zhp)
560168404Spjd{
561168404Spjd	if (zhp->zfs_mntopts)
562168404Spjd		free(zhp->zfs_mntopts);
563168404Spjd	nvlist_free(zhp->zfs_props);
564168404Spjd	nvlist_free(zhp->zfs_user_props);
565219089Spjd	nvlist_free(zhp->zfs_recvd_props);
566168404Spjd	free(zhp);
567168404Spjd}
568168404Spjd
569209962Smmtypedef struct mnttab_node {
570209962Smm	struct mnttab mtn_mt;
571209962Smm	avl_node_t mtn_node;
572209962Smm} mnttab_node_t;
573209962Smm
574209962Smmstatic int
575209962Smmlibzfs_mnttab_cache_compare(const void *arg1, const void *arg2)
576209962Smm{
577209962Smm	const mnttab_node_t *mtn1 = arg1;
578209962Smm	const mnttab_node_t *mtn2 = arg2;
579209962Smm	int rv;
580209962Smm
581209962Smm	rv = strcmp(mtn1->mtn_mt.mnt_special, mtn2->mtn_mt.mnt_special);
582209962Smm
583209962Smm	if (rv == 0)
584209962Smm		return (0);
585209962Smm	return (rv > 0 ? 1 : -1);
586209962Smm}
587209962Smm
588209962Smmvoid
589209962Smmlibzfs_mnttab_init(libzfs_handle_t *hdl)
590209962Smm{
591209962Smm	assert(avl_numnodes(&hdl->libzfs_mnttab_cache) == 0);
592209962Smm	avl_create(&hdl->libzfs_mnttab_cache, libzfs_mnttab_cache_compare,
593209962Smm	    sizeof (mnttab_node_t), offsetof(mnttab_node_t, mtn_node));
594209962Smm}
595209962Smm
596209962Smmvoid
597209962Smmlibzfs_mnttab_update(libzfs_handle_t *hdl)
598209962Smm{
599209962Smm	struct mnttab entry;
600209962Smm
601209962Smm	rewind(hdl->libzfs_mnttab);
602209962Smm	while (getmntent(hdl->libzfs_mnttab, &entry) == 0) {
603209962Smm		mnttab_node_t *mtn;
604209962Smm
605209962Smm		if (strcmp(entry.mnt_fstype, MNTTYPE_ZFS) != 0)
606209962Smm			continue;
607209962Smm		mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
608209962Smm		mtn->mtn_mt.mnt_special = zfs_strdup(hdl, entry.mnt_special);
609209962Smm		mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, entry.mnt_mountp);
610209962Smm		mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, entry.mnt_fstype);
611209962Smm		mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, entry.mnt_mntopts);
612209962Smm		avl_add(&hdl->libzfs_mnttab_cache, mtn);
613209962Smm	}
614209962Smm}
615209962Smm
616209962Smmvoid
617209962Smmlibzfs_mnttab_fini(libzfs_handle_t *hdl)
618209962Smm{
619209962Smm	void *cookie = NULL;
620209962Smm	mnttab_node_t *mtn;
621209962Smm
622209962Smm	while (mtn = avl_destroy_nodes(&hdl->libzfs_mnttab_cache, &cookie)) {
623209962Smm		free(mtn->mtn_mt.mnt_special);
624209962Smm		free(mtn->mtn_mt.mnt_mountp);
625209962Smm		free(mtn->mtn_mt.mnt_fstype);
626209962Smm		free(mtn->mtn_mt.mnt_mntopts);
627209962Smm		free(mtn);
628209962Smm	}
629209962Smm	avl_destroy(&hdl->libzfs_mnttab_cache);
630209962Smm}
631209962Smm
632209962Smmvoid
633209962Smmlibzfs_mnttab_cache(libzfs_handle_t *hdl, boolean_t enable)
634209962Smm{
635209962Smm	hdl->libzfs_mnttab_enable = enable;
636209962Smm}
637209962Smm
638185029Spjdint
639209962Smmlibzfs_mnttab_find(libzfs_handle_t *hdl, const char *fsname,
640209962Smm    struct mnttab *entry)
641209962Smm{
642209962Smm	mnttab_node_t find;
643209962Smm	mnttab_node_t *mtn;
644209962Smm
645209962Smm	if (!hdl->libzfs_mnttab_enable) {
646209962Smm		struct mnttab srch = { 0 };
647209962Smm
648209962Smm		if (avl_numnodes(&hdl->libzfs_mnttab_cache))
649209962Smm			libzfs_mnttab_fini(hdl);
650209962Smm		rewind(hdl->libzfs_mnttab);
651209962Smm		srch.mnt_special = (char *)fsname;
652209962Smm		srch.mnt_fstype = MNTTYPE_ZFS;
653209962Smm		if (getmntany(hdl->libzfs_mnttab, entry, &srch) == 0)
654209962Smm			return (0);
655209962Smm		else
656209962Smm			return (ENOENT);
657209962Smm	}
658209962Smm
659209962Smm	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
660209962Smm		libzfs_mnttab_update(hdl);
661209962Smm
662209962Smm	find.mtn_mt.mnt_special = (char *)fsname;
663209962Smm	mtn = avl_find(&hdl->libzfs_mnttab_cache, &find, NULL);
664209962Smm	if (mtn) {
665209962Smm		*entry = mtn->mtn_mt;
666209962Smm		return (0);
667209962Smm	}
668209962Smm	return (ENOENT);
669209962Smm}
670209962Smm
671209962Smmvoid
672209962Smmlibzfs_mnttab_add(libzfs_handle_t *hdl, const char *special,
673209962Smm    const char *mountp, const char *mntopts)
674209962Smm{
675209962Smm	mnttab_node_t *mtn;
676209962Smm
677209962Smm	if (avl_numnodes(&hdl->libzfs_mnttab_cache) == 0)
678209962Smm		return;
679209962Smm	mtn = zfs_alloc(hdl, sizeof (mnttab_node_t));
680209962Smm	mtn->mtn_mt.mnt_special = zfs_strdup(hdl, special);
681209962Smm	mtn->mtn_mt.mnt_mountp = zfs_strdup(hdl, mountp);
682209962Smm	mtn->mtn_mt.mnt_fstype = zfs_strdup(hdl, MNTTYPE_ZFS);
683209962Smm	mtn->mtn_mt.mnt_mntopts = zfs_strdup(hdl, mntopts);
684209962Smm	avl_add(&hdl->libzfs_mnttab_cache, mtn);
685209962Smm}
686209962Smm
687209962Smmvoid
688209962Smmlibzfs_mnttab_remove(libzfs_handle_t *hdl, const char *fsname)
689209962Smm{
690209962Smm	mnttab_node_t find;
691209962Smm	mnttab_node_t *ret;
692209962Smm
693209962Smm	find.mtn_mt.mnt_special = (char *)fsname;
694209962Smm	if (ret = avl_find(&hdl->libzfs_mnttab_cache, (void *)&find, NULL)) {
695209962Smm		avl_remove(&hdl->libzfs_mnttab_cache, ret);
696209962Smm		free(ret->mtn_mt.mnt_special);
697209962Smm		free(ret->mtn_mt.mnt_mountp);
698209962Smm		free(ret->mtn_mt.mnt_fstype);
699209962Smm		free(ret->mtn_mt.mnt_mntopts);
700209962Smm		free(ret);
701209962Smm	}
702209962Smm}
703209962Smm
704209962Smmint
705185029Spjdzfs_spa_version(zfs_handle_t *zhp, int *spa_version)
706168404Spjd{
707185029Spjd	zpool_handle_t *zpool_handle = zhp->zpool_hdl;
708168404Spjd
709185029Spjd	if (zpool_handle == NULL)
710168404Spjd		return (-1);
711168404Spjd
712185029Spjd	*spa_version = zpool_get_prop_int(zpool_handle,
713185029Spjd	    ZPOOL_PROP_VERSION, NULL);
714168404Spjd	return (0);
715168404Spjd}
716168404Spjd
717168404Spjd/*
718185029Spjd * The choice of reservation property depends on the SPA version.
719168404Spjd */
720168404Spjdstatic int
721185029Spjdzfs_which_resv_prop(zfs_handle_t *zhp, zfs_prop_t *resv_prop)
722168404Spjd{
723185029Spjd	int spa_version;
724168404Spjd
725185029Spjd	if (zfs_spa_version(zhp, &spa_version) < 0)
726168404Spjd		return (-1);
727168404Spjd
728185029Spjd	if (spa_version >= SPA_VERSION_REFRESERVATION)
729185029Spjd		*resv_prop = ZFS_PROP_REFRESERVATION;
730185029Spjd	else
731185029Spjd		*resv_prop = ZFS_PROP_RESERVATION;
732168404Spjd
733168404Spjd	return (0);
734168404Spjd}
735168404Spjd
736168404Spjd/*
737168404Spjd * Given an nvlist of properties to set, validates that they are correct, and
738168404Spjd * parses any numeric properties (index, boolean, etc) if they are specified as
739168404Spjd * strings.
740168404Spjd */
741168404Spjdnvlist_t *
742185029Spjdzfs_valid_proplist(libzfs_handle_t *hdl, zfs_type_t type, nvlist_t *nvl,
743185029Spjd    uint64_t zoned, zfs_handle_t *zhp, const char *errbuf)
744168404Spjd{
745168404Spjd	nvpair_t *elem;
746168404Spjd	uint64_t intval;
747168404Spjd	char *strval;
748185029Spjd	zfs_prop_t prop;
749168404Spjd	nvlist_t *ret;
750185029Spjd	int chosen_normal = -1;
751185029Spjd	int chosen_utf = -1;
752168404Spjd
753168404Spjd	if (nvlist_alloc(&ret, NV_UNIQUE_NAME, 0) != 0) {
754168404Spjd		(void) no_memory(hdl);
755168404Spjd		return (NULL);
756168404Spjd	}
757168404Spjd
758209962Smm	/*
759209962Smm	 * Make sure this property is valid and applies to this type.
760209962Smm	 */
761209962Smm
762168404Spjd	elem = NULL;
763168404Spjd	while ((elem = nvlist_next_nvpair(nvl, elem)) != NULL) {
764185029Spjd		const char *propname = nvpair_name(elem);
765168404Spjd
766209962Smm		prop = zfs_name_to_prop(propname);
767209962Smm		if (prop == ZPROP_INVAL && zfs_prop_user(propname)) {
768185029Spjd			/*
769209962Smm			 * This is a user property: make sure it's a
770185029Spjd			 * string, and that it's less than ZAP_MAXNAMELEN.
771185029Spjd			 */
772185029Spjd			if (nvpair_type(elem) != DATA_TYPE_STRING) {
773185029Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
774185029Spjd				    "'%s' must be a string"), propname);
775185029Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
776185029Spjd				goto error;
777168404Spjd			}
778168404Spjd
779185029Spjd			if (strlen(nvpair_name(elem)) >= ZAP_MAXNAMELEN) {
780185029Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
781185029Spjd				    "property name '%s' is too long"),
782185029Spjd				    propname);
783185029Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
784185029Spjd				goto error;
785185029Spjd			}
786185029Spjd
787168404Spjd			(void) nvpair_value_string(elem, &strval);
788168404Spjd			if (nvlist_add_string(ret, propname, strval) != 0) {
789168404Spjd				(void) no_memory(hdl);
790168404Spjd				goto error;
791168404Spjd			}
792168404Spjd			continue;
793168404Spjd		}
794168404Spjd
795209962Smm		/*
796209962Smm		 * Currently, only user properties can be modified on
797209962Smm		 * snapshots.
798209962Smm		 */
799185029Spjd		if (type == ZFS_TYPE_SNAPSHOT) {
800185029Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
801185029Spjd			    "this property can not be modified for snapshots"));
802185029Spjd			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
803185029Spjd			goto error;
804185029Spjd		}
805168404Spjd
806209962Smm		if (prop == ZPROP_INVAL && zfs_prop_userquota(propname)) {
807209962Smm			zfs_userquota_prop_t uqtype;
808209962Smm			char newpropname[128];
809209962Smm			char domain[128];
810209962Smm			uint64_t rid;
811209962Smm			uint64_t valary[3];
812209962Smm
813209962Smm			if (userquota_propname_decode(propname, zoned,
814209962Smm			    &uqtype, domain, sizeof (domain), &rid) != 0) {
815209962Smm				zfs_error_aux(hdl,
816209962Smm				    dgettext(TEXT_DOMAIN,
817209962Smm				    "'%s' has an invalid user/group name"),
818209962Smm				    propname);
819209962Smm				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
820209962Smm				goto error;
821209962Smm			}
822209962Smm
823209962Smm			if (uqtype != ZFS_PROP_USERQUOTA &&
824209962Smm			    uqtype != ZFS_PROP_GROUPQUOTA) {
825209962Smm				zfs_error_aux(hdl,
826209962Smm				    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
827209962Smm				    propname);
828209962Smm				(void) zfs_error(hdl, EZFS_PROPREADONLY,
829209962Smm				    errbuf);
830209962Smm				goto error;
831209962Smm			}
832209962Smm
833209962Smm			if (nvpair_type(elem) == DATA_TYPE_STRING) {
834209962Smm				(void) nvpair_value_string(elem, &strval);
835209962Smm				if (strcmp(strval, "none") == 0) {
836209962Smm					intval = 0;
837209962Smm				} else if (zfs_nicestrtonum(hdl,
838209962Smm				    strval, &intval) != 0) {
839209962Smm					(void) zfs_error(hdl,
840209962Smm					    EZFS_BADPROP, errbuf);
841209962Smm					goto error;
842209962Smm				}
843209962Smm			} else if (nvpair_type(elem) ==
844209962Smm			    DATA_TYPE_UINT64) {
845209962Smm				(void) nvpair_value_uint64(elem, &intval);
846209962Smm				if (intval == 0) {
847209962Smm					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
848209962Smm					    "use 'none' to disable "
849209962Smm					    "userquota/groupquota"));
850209962Smm					goto error;
851209962Smm				}
852209962Smm			} else {
853209962Smm				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
854209962Smm				    "'%s' must be a number"), propname);
855209962Smm				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
856209962Smm				goto error;
857209962Smm			}
858209962Smm
859219089Spjd			/*
860219089Spjd			 * Encode the prop name as
861219089Spjd			 * userquota@<hex-rid>-domain, to make it easy
862219089Spjd			 * for the kernel to decode.
863219089Spjd			 */
864209962Smm			(void) snprintf(newpropname, sizeof (newpropname),
865219089Spjd			    "%s%llx-%s", zfs_userquota_prop_prefixes[uqtype],
866219089Spjd			    (longlong_t)rid, domain);
867209962Smm			valary[0] = uqtype;
868209962Smm			valary[1] = rid;
869209962Smm			valary[2] = intval;
870209962Smm			if (nvlist_add_uint64_array(ret, newpropname,
871209962Smm			    valary, 3) != 0) {
872209962Smm				(void) no_memory(hdl);
873209962Smm				goto error;
874209962Smm			}
875209962Smm			continue;
876209962Smm		}
877209962Smm
878209962Smm		if (prop == ZPROP_INVAL) {
879209962Smm			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
880209962Smm			    "invalid property '%s'"), propname);
881209962Smm			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
882209962Smm			goto error;
883209962Smm		}
884209962Smm
885168404Spjd		if (!zfs_prop_valid_for_type(prop, type)) {
886168404Spjd			zfs_error_aux(hdl,
887168404Spjd			    dgettext(TEXT_DOMAIN, "'%s' does not "
888168404Spjd			    "apply to datasets of this type"), propname);
889168404Spjd			(void) zfs_error(hdl, EZFS_PROPTYPE, errbuf);
890168404Spjd			goto error;
891168404Spjd		}
892168404Spjd
893168404Spjd		if (zfs_prop_readonly(prop) &&
894185029Spjd		    (!zfs_prop_setonce(prop) || zhp != NULL)) {
895168404Spjd			zfs_error_aux(hdl,
896168404Spjd			    dgettext(TEXT_DOMAIN, "'%s' is readonly"),
897168404Spjd			    propname);
898168404Spjd			(void) zfs_error(hdl, EZFS_PROPREADONLY, errbuf);
899168404Spjd			goto error;
900168404Spjd		}
901168404Spjd
902185029Spjd		if (zprop_parse_value(hdl, elem, prop, type, ret,
903185029Spjd		    &strval, &intval, errbuf) != 0)
904185029Spjd			goto error;
905185029Spjd
906168404Spjd		/*
907185029Spjd		 * Perform some additional checks for specific properties.
908168404Spjd		 */
909185029Spjd		switch (prop) {
910185029Spjd		case ZFS_PROP_VERSION:
911185029Spjd		{
912185029Spjd			int version;
913168404Spjd
914185029Spjd			if (zhp == NULL)
915185029Spjd				break;
916185029Spjd			version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
917185029Spjd			if (intval < version) {
918168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
919185029Spjd				    "Can not downgrade; already at version %u"),
920185029Spjd				    version);
921168404Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
922168404Spjd				goto error;
923168404Spjd			}
924168404Spjd			break;
925168404Spjd		}
926168404Spjd
927168404Spjd		case ZFS_PROP_RECORDSIZE:
928168404Spjd		case ZFS_PROP_VOLBLOCKSIZE:
929168404Spjd			/* must be power of two within SPA_{MIN,MAX}BLOCKSIZE */
930168404Spjd			if (intval < SPA_MINBLOCKSIZE ||
931168404Spjd			    intval > SPA_MAXBLOCKSIZE || !ISP2(intval)) {
932168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
933168404Spjd				    "'%s' must be power of 2 from %u "
934168404Spjd				    "to %uk"), propname,
935168404Spjd				    (uint_t)SPA_MINBLOCKSIZE,
936168404Spjd				    (uint_t)SPA_MAXBLOCKSIZE >> 10);
937168404Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
938168404Spjd				goto error;
939168404Spjd			}
940168404Spjd			break;
941168404Spjd
942219089Spjd		case ZFS_PROP_MLSLABEL:
943219089Spjd		{
944219089Spjd#ifdef sun
945219089Spjd			/*
946219089Spjd			 * Verify the mlslabel string and convert to
947219089Spjd			 * internal hex label string.
948219089Spjd			 */
949219089Spjd
950219089Spjd			m_label_t *new_sl;
951219089Spjd			char *hex = NULL;	/* internal label string */
952219089Spjd
953219089Spjd			/* Default value is already OK. */
954219089Spjd			if (strcasecmp(strval, ZFS_MLSLABEL_DEFAULT) == 0)
955219089Spjd				break;
956219089Spjd
957219089Spjd			/* Verify the label can be converted to binary form */
958219089Spjd			if (((new_sl = m_label_alloc(MAC_LABEL)) == NULL) ||
959219089Spjd			    (str_to_label(strval, &new_sl, MAC_LABEL,
960219089Spjd			    L_NO_CORRECTION, NULL) == -1)) {
961219089Spjd				goto badlabel;
962168404Spjd			}
963168404Spjd
964219089Spjd			/* Now translate to hex internal label string */
965219089Spjd			if (label_to_str(new_sl, &hex, M_INTERNAL,
966219089Spjd			    DEF_NAMES) != 0) {
967219089Spjd				if (hex)
968219089Spjd					free(hex);
969219089Spjd				goto badlabel;
970219089Spjd			}
971219089Spjd			m_label_free(new_sl);
972219089Spjd
973219089Spjd			/* If string is already in internal form, we're done. */
974219089Spjd			if (strcmp(strval, hex) == 0) {
975219089Spjd				free(hex);
976219089Spjd				break;
977219089Spjd			}
978219089Spjd
979219089Spjd			/* Replace the label string with the internal form. */
980219089Spjd			(void) nvlist_remove(ret, zfs_prop_to_name(prop),
981219089Spjd			    DATA_TYPE_STRING);
982219089Spjd			verify(nvlist_add_string(ret, zfs_prop_to_name(prop),
983219089Spjd			    hex) == 0);
984219089Spjd			free(hex);
985219089Spjd
986168404Spjd			break;
987168404Spjd
988219089Spjdbadlabel:
989219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
990219089Spjd			    "invalid mlslabel '%s'"), strval);
991219089Spjd			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
992219089Spjd			m_label_free(new_sl);	/* OK if null */
993219089Spjd#else	/* !sun */
994219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
995219089Spjd			    "mlslabel is not supported on FreeBSD"));
996219089Spjd			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
997219089Spjd#endif	/* !sun */
998219089Spjd			goto error;
999219089Spjd
1000219089Spjd		}
1001219089Spjd
1002168404Spjd		case ZFS_PROP_MOUNTPOINT:
1003185029Spjd		{
1004185029Spjd			namecheck_err_t why;
1005185029Spjd
1006168404Spjd			if (strcmp(strval, ZFS_MOUNTPOINT_NONE) == 0 ||
1007168404Spjd			    strcmp(strval, ZFS_MOUNTPOINT_LEGACY) == 0)
1008168404Spjd				break;
1009168404Spjd
1010185029Spjd			if (mountpoint_namecheck(strval, &why)) {
1011185029Spjd				switch (why) {
1012185029Spjd				case NAME_ERR_LEADING_SLASH:
1013185029Spjd					zfs_error_aux(hdl,
1014185029Spjd					    dgettext(TEXT_DOMAIN,
1015185029Spjd					    "'%s' must be an absolute path, "
1016185029Spjd					    "'none', or 'legacy'"), propname);
1017185029Spjd					break;
1018185029Spjd				case NAME_ERR_TOOLONG:
1019185029Spjd					zfs_error_aux(hdl,
1020185029Spjd					    dgettext(TEXT_DOMAIN,
1021185029Spjd					    "component of '%s' is too long"),
1022185029Spjd					    propname);
1023185029Spjd					break;
1024185029Spjd				}
1025168404Spjd				(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1026168404Spjd				goto error;
1027168404Spjd			}
1028185029Spjd		}
1029185029Spjd
1030168404Spjd			/*FALLTHRU*/
1031168404Spjd
1032185029Spjd		case ZFS_PROP_SHARESMB:
1033168404Spjd		case ZFS_PROP_SHARENFS:
1034168404Spjd			/*
1035185029Spjd			 * For the mountpoint and sharenfs or sharesmb
1036185029Spjd			 * properties, check if it can be set in a
1037185029Spjd			 * global/non-global zone based on
1038168404Spjd			 * the zoned property value:
1039168404Spjd			 *
1040168404Spjd			 *		global zone	    non-global zone
1041168404Spjd			 * --------------------------------------------------
1042168404Spjd			 * zoned=on	mountpoint (no)	    mountpoint (yes)
1043168404Spjd			 *		sharenfs (no)	    sharenfs (no)
1044185029Spjd			 *		sharesmb (no)	    sharesmb (no)
1045168404Spjd			 *
1046168404Spjd			 * zoned=off	mountpoint (yes)	N/A
1047168404Spjd			 *		sharenfs (yes)
1048185029Spjd			 *		sharesmb (yes)
1049168404Spjd			 */
1050168404Spjd			if (zoned) {
1051168404Spjd				if (getzoneid() == GLOBAL_ZONEID) {
1052168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1053168404Spjd					    "'%s' cannot be set on "
1054168404Spjd					    "dataset in a non-global zone"),
1055168404Spjd					    propname);
1056168404Spjd					(void) zfs_error(hdl, EZFS_ZONED,
1057168404Spjd					    errbuf);
1058168404Spjd					goto error;
1059185029Spjd				} else if (prop == ZFS_PROP_SHARENFS ||
1060185029Spjd				    prop == ZFS_PROP_SHARESMB) {
1061168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1062168404Spjd					    "'%s' cannot be set in "
1063168404Spjd					    "a non-global zone"), propname);
1064168404Spjd					(void) zfs_error(hdl, EZFS_ZONED,
1065168404Spjd					    errbuf);
1066168404Spjd					goto error;
1067168404Spjd				}
1068168404Spjd			} else if (getzoneid() != GLOBAL_ZONEID) {
1069168404Spjd				/*
1070168404Spjd				 * If zoned property is 'off', this must be in
1071209962Smm				 * a global zone. If not, something is wrong.
1072168404Spjd				 */
1073168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1074168404Spjd				    "'%s' cannot be set while dataset "
1075168404Spjd				    "'zoned' property is set"), propname);
1076168404Spjd				(void) zfs_error(hdl, EZFS_ZONED, errbuf);
1077168404Spjd				goto error;
1078168404Spjd			}
1079168404Spjd
1080168404Spjd			/*
1081185029Spjd			 * At this point, it is legitimate to set the
1082185029Spjd			 * property. Now we want to make sure that the
1083185029Spjd			 * property value is valid if it is sharenfs.
1084168404Spjd			 */
1085185029Spjd			if ((prop == ZFS_PROP_SHARENFS ||
1086185029Spjd			    prop == ZFS_PROP_SHARESMB) &&
1087185029Spjd			    strcmp(strval, "on") != 0 &&
1088185029Spjd			    strcmp(strval, "off") != 0) {
1089185029Spjd				zfs_share_proto_t proto;
1090168404Spjd
1091185029Spjd				if (prop == ZFS_PROP_SHARESMB)
1092185029Spjd					proto = PROTO_SMB;
1093185029Spjd				else
1094185029Spjd					proto = PROTO_NFS;
1095185029Spjd
1096185029Spjd				/*
1097185029Spjd				 * Must be an valid sharing protocol
1098185029Spjd				 * option string so init the libshare
1099185029Spjd				 * in order to enable the parser and
1100185029Spjd				 * then parse the options. We use the
1101185029Spjd				 * control API since we don't care about
1102185029Spjd				 * the current configuration and don't
1103185029Spjd				 * want the overhead of loading it
1104185029Spjd				 * until we actually do something.
1105185029Spjd				 */
1106185029Spjd
1107185029Spjd				if (zfs_init_libshare(hdl,
1108185029Spjd				    SA_INIT_CONTROL_API) != SA_OK) {
1109185029Spjd					/*
1110185029Spjd					 * An error occurred so we can't do
1111185029Spjd					 * anything
1112185029Spjd					 */
1113185029Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1114185029Spjd					    "'%s' cannot be set: problem "
1115185029Spjd					    "in share initialization"),
1116185029Spjd					    propname);
1117185029Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1118185029Spjd					    errbuf);
1119185029Spjd					goto error;
1120185029Spjd				}
1121185029Spjd
1122185029Spjd				if (zfs_parse_options(strval, proto) != SA_OK) {
1123185029Spjd					/*
1124185029Spjd					 * There was an error in parsing so
1125185029Spjd					 * deal with it by issuing an error
1126185029Spjd					 * message and leaving after
1127185029Spjd					 * uninitializing the the libshare
1128185029Spjd					 * interface.
1129185029Spjd					 */
1130185029Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1131185029Spjd					    "'%s' cannot be set to invalid "
1132185029Spjd					    "options"), propname);
1133185029Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1134185029Spjd					    errbuf);
1135185029Spjd					zfs_uninit_libshare(hdl);
1136185029Spjd					goto error;
1137185029Spjd				}
1138185029Spjd				zfs_uninit_libshare(hdl);
1139168404Spjd			}
1140185029Spjd
1141168404Spjd			break;
1142185029Spjd		case ZFS_PROP_UTF8ONLY:
1143185029Spjd			chosen_utf = (int)intval;
1144185029Spjd			break;
1145185029Spjd		case ZFS_PROP_NORMALIZE:
1146185029Spjd			chosen_normal = (int)intval;
1147185029Spjd			break;
1148168404Spjd		}
1149168404Spjd
1150168404Spjd		/*
1151168404Spjd		 * For changes to existing volumes, we have some additional
1152168404Spjd		 * checks to enforce.
1153168404Spjd		 */
1154168404Spjd		if (type == ZFS_TYPE_VOLUME && zhp != NULL) {
1155168404Spjd			uint64_t volsize = zfs_prop_get_int(zhp,
1156168404Spjd			    ZFS_PROP_VOLSIZE);
1157168404Spjd			uint64_t blocksize = zfs_prop_get_int(zhp,
1158168404Spjd			    ZFS_PROP_VOLBLOCKSIZE);
1159168404Spjd			char buf[64];
1160168404Spjd
1161168404Spjd			switch (prop) {
1162168404Spjd			case ZFS_PROP_RESERVATION:
1163185029Spjd			case ZFS_PROP_REFRESERVATION:
1164168404Spjd				if (intval > volsize) {
1165168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1166168404Spjd					    "'%s' is greater than current "
1167168404Spjd					    "volume size"), propname);
1168168404Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1169168404Spjd					    errbuf);
1170168404Spjd					goto error;
1171168404Spjd				}
1172168404Spjd				break;
1173168404Spjd
1174168404Spjd			case ZFS_PROP_VOLSIZE:
1175168404Spjd				if (intval % blocksize != 0) {
1176168404Spjd					zfs_nicenum(blocksize, buf,
1177168404Spjd					    sizeof (buf));
1178168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1179168404Spjd					    "'%s' must be a multiple of "
1180168404Spjd					    "volume block size (%s)"),
1181168404Spjd					    propname, buf);
1182168404Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1183168404Spjd					    errbuf);
1184168404Spjd					goto error;
1185168404Spjd				}
1186168404Spjd
1187168404Spjd				if (intval == 0) {
1188168404Spjd					zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1189168404Spjd					    "'%s' cannot be zero"),
1190168404Spjd					    propname);
1191168404Spjd					(void) zfs_error(hdl, EZFS_BADPROP,
1192168404Spjd					    errbuf);
1193168404Spjd					goto error;
1194168404Spjd				}
1195168404Spjd				break;
1196168404Spjd			}
1197168404Spjd		}
1198168404Spjd	}
1199168404Spjd
1200168404Spjd	/*
1201185029Spjd	 * If normalization was chosen, but no UTF8 choice was made,
1202185029Spjd	 * enforce rejection of non-UTF8 names.
1203185029Spjd	 *
1204185029Spjd	 * If normalization was chosen, but rejecting non-UTF8 names
1205185029Spjd	 * was explicitly not chosen, it is an error.
1206185029Spjd	 */
1207185029Spjd	if (chosen_normal > 0 && chosen_utf < 0) {
1208185029Spjd		if (nvlist_add_uint64(ret,
1209185029Spjd		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY), 1) != 0) {
1210185029Spjd			(void) no_memory(hdl);
1211185029Spjd			goto error;
1212185029Spjd		}
1213185029Spjd	} else if (chosen_normal > 0 && chosen_utf == 0) {
1214185029Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1215185029Spjd		    "'%s' must be set 'on' if normalization chosen"),
1216185029Spjd		    zfs_prop_to_name(ZFS_PROP_UTF8ONLY));
1217185029Spjd		(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1218185029Spjd		goto error;
1219185029Spjd	}
1220219089Spjd	return (ret);
1221185029Spjd
1222219089Spjderror:
1223219089Spjd	nvlist_free(ret);
1224219089Spjd	return (NULL);
1225219089Spjd}
1226219089Spjd
1227219089Spjdint
1228219089Spjdzfs_add_synthetic_resv(zfs_handle_t *zhp, nvlist_t *nvl)
1229219089Spjd{
1230219089Spjd	uint64_t old_volsize;
1231219089Spjd	uint64_t new_volsize;
1232219089Spjd	uint64_t old_reservation;
1233219089Spjd	uint64_t new_reservation;
1234219089Spjd	zfs_prop_t resv_prop;
1235219089Spjd
1236185029Spjd	/*
1237168404Spjd	 * If this is an existing volume, and someone is setting the volsize,
1238168404Spjd	 * make sure that it matches the reservation, or add it if necessary.
1239168404Spjd	 */
1240219089Spjd	old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
1241219089Spjd	if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
1242219089Spjd		return (-1);
1243219089Spjd	old_reservation = zfs_prop_get_int(zhp, resv_prop);
1244219089Spjd	if ((zvol_volsize_to_reservation(old_volsize, zhp->zfs_props) !=
1245219089Spjd	    old_reservation) || nvlist_lookup_uint64(nvl,
1246219089Spjd	    zfs_prop_to_name(resv_prop), &new_reservation) != ENOENT) {
1247219089Spjd		return (0);
1248219089Spjd	}
1249219089Spjd	if (nvlist_lookup_uint64(nvl, zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1250219089Spjd	    &new_volsize) != 0)
1251219089Spjd		return (-1);
1252219089Spjd	new_reservation = zvol_volsize_to_reservation(new_volsize,
1253219089Spjd	    zhp->zfs_props);
1254219089Spjd	if (nvlist_add_uint64(nvl, zfs_prop_to_name(resv_prop),
1255219089Spjd	    new_reservation) != 0) {
1256219089Spjd		(void) no_memory(zhp->zfs_hdl);
1257219089Spjd		return (-1);
1258219089Spjd	}
1259219089Spjd	return (1);
1260219089Spjd}
1261168404Spjd
1262219089Spjdvoid
1263219089Spjdzfs_setprop_error(libzfs_handle_t *hdl, zfs_prop_t prop, int err,
1264219089Spjd    char *errbuf)
1265219089Spjd{
1266219089Spjd	switch (err) {
1267185029Spjd
1268219089Spjd	case ENOSPC:
1269219089Spjd		/*
1270219089Spjd		 * For quotas and reservations, ENOSPC indicates
1271219089Spjd		 * something different; setting a quota or reservation
1272219089Spjd		 * doesn't use any disk space.
1273219089Spjd		 */
1274219089Spjd		switch (prop) {
1275219089Spjd		case ZFS_PROP_QUOTA:
1276219089Spjd		case ZFS_PROP_REFQUOTA:
1277219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1278219089Spjd			    "size is less than current used or "
1279219089Spjd			    "reserved space"));
1280219089Spjd			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1281219089Spjd			break;
1282219089Spjd
1283219089Spjd		case ZFS_PROP_RESERVATION:
1284219089Spjd		case ZFS_PROP_REFRESERVATION:
1285219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1286219089Spjd			    "size is greater than available space"));
1287219089Spjd			(void) zfs_error(hdl, EZFS_PROPSPACE, errbuf);
1288219089Spjd			break;
1289219089Spjd
1290219089Spjd		default:
1291219089Spjd			(void) zfs_standard_error(hdl, err, errbuf);
1292219089Spjd			break;
1293168404Spjd		}
1294219089Spjd		break;
1295219089Spjd
1296219089Spjd	case EBUSY:
1297219089Spjd		(void) zfs_standard_error(hdl, EBUSY, errbuf);
1298219089Spjd		break;
1299219089Spjd
1300219089Spjd	case EROFS:
1301219089Spjd		(void) zfs_error(hdl, EZFS_DSREADONLY, errbuf);
1302219089Spjd		break;
1303219089Spjd
1304219089Spjd	case ENOTSUP:
1305219089Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1306219089Spjd		    "pool and or dataset must be upgraded to set this "
1307219089Spjd		    "property or value"));
1308219089Spjd		(void) zfs_error(hdl, EZFS_BADVERSION, errbuf);
1309219089Spjd		break;
1310219089Spjd
1311219089Spjd	case ERANGE:
1312219089Spjd		if (prop == ZFS_PROP_COMPRESSION) {
1313219089Spjd			(void) zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1314219089Spjd			    "property setting is not allowed on "
1315219089Spjd			    "bootable datasets"));
1316219089Spjd			(void) zfs_error(hdl, EZFS_NOTSUP, errbuf);
1317219089Spjd		} else {
1318219089Spjd			(void) zfs_standard_error(hdl, err, errbuf);
1319219089Spjd		}
1320219089Spjd		break;
1321219089Spjd
1322219089Spjd	case EINVAL:
1323219089Spjd		if (prop == ZPROP_INVAL) {
1324219089Spjd			(void) zfs_error(hdl, EZFS_BADPROP, errbuf);
1325219089Spjd		} else {
1326219089Spjd			(void) zfs_standard_error(hdl, err, errbuf);
1327219089Spjd		}
1328219089Spjd		break;
1329219089Spjd
1330219089Spjd	case EOVERFLOW:
1331219089Spjd		/*
1332219089Spjd		 * This platform can't address a volume this big.
1333219089Spjd		 */
1334219089Spjd#ifdef _ILP32
1335219089Spjd		if (prop == ZFS_PROP_VOLSIZE) {
1336219089Spjd			(void) zfs_error(hdl, EZFS_VOLTOOBIG, errbuf);
1337219089Spjd			break;
1338219089Spjd		}
1339219089Spjd#endif
1340219089Spjd		/* FALLTHROUGH */
1341219089Spjd	default:
1342219089Spjd		(void) zfs_standard_error(hdl, err, errbuf);
1343168404Spjd	}
1344168404Spjd}
1345168404Spjd
1346168404Spjd/*
1347168404Spjd * Given a property name and value, set the property for the given dataset.
1348168404Spjd */
1349168404Spjdint
1350168404Spjdzfs_prop_set(zfs_handle_t *zhp, const char *propname, const char *propval)
1351168404Spjd{
1352168404Spjd	zfs_cmd_t zc = { 0 };
1353168404Spjd	int ret = -1;
1354168404Spjd	prop_changelist_t *cl = NULL;
1355168404Spjd	char errbuf[1024];
1356168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
1357168404Spjd	nvlist_t *nvl = NULL, *realprops;
1358168404Spjd	zfs_prop_t prop;
1359185029Spjd	boolean_t do_prefix;
1360185029Spjd	uint64_t idx;
1361219089Spjd	int added_resv;
1362168404Spjd
1363168404Spjd	(void) snprintf(errbuf, sizeof (errbuf),
1364168404Spjd	    dgettext(TEXT_DOMAIN, "cannot set property for '%s'"),
1365168404Spjd	    zhp->zfs_name);
1366168404Spjd
1367168404Spjd	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0 ||
1368168404Spjd	    nvlist_add_string(nvl, propname, propval) != 0) {
1369168404Spjd		(void) no_memory(hdl);
1370168404Spjd		goto error;
1371168404Spjd	}
1372168404Spjd
1373185029Spjd	if ((realprops = zfs_valid_proplist(hdl, zhp->zfs_type, nvl,
1374168404Spjd	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED), zhp, errbuf)) == NULL)
1375168404Spjd		goto error;
1376185029Spjd
1377168404Spjd	nvlist_free(nvl);
1378168404Spjd	nvl = realprops;
1379168404Spjd
1380168404Spjd	prop = zfs_name_to_prop(propname);
1381168404Spjd
1382168404Spjd	/* We don't support those properties on FreeBSD. */
1383168404Spjd	switch (prop) {
1384197867Strasz	case ZFS_PROP_DEVICES:
1385168404Spjd	case ZFS_PROP_ISCSIOPTIONS:
1386197867Strasz	case ZFS_PROP_XATTR:
1387197867Strasz	case ZFS_PROP_VSCAN:
1388197867Strasz	case ZFS_PROP_NBMAND:
1389219089Spjd	case ZFS_PROP_MLSLABEL:
1390168404Spjd		(void) snprintf(errbuf, sizeof (errbuf),
1391168404Spjd		    "property '%s' not supported on FreeBSD", propname);
1392168404Spjd		ret = zfs_error(hdl, EZFS_PERM, errbuf);
1393168404Spjd		goto error;
1394168404Spjd	}
1395168404Spjd
1396219089Spjd	if (prop == ZFS_PROP_VOLSIZE) {
1397219089Spjd		if ((added_resv = zfs_add_synthetic_resv(zhp, nvl)) == -1)
1398219089Spjd			goto error;
1399219089Spjd	}
1400219089Spjd
1401185029Spjd	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1402168404Spjd		goto error;
1403168404Spjd
1404168404Spjd	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1405168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1406168404Spjd		    "child dataset with inherited mountpoint is used "
1407168404Spjd		    "in a non-global zone"));
1408168404Spjd		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1409168404Spjd		goto error;
1410168404Spjd	}
1411168404Spjd
1412185029Spjd	/*
1413185029Spjd	 * If the dataset's canmount property is being set to noauto,
1414185029Spjd	 * then we want to prevent unmounting & remounting it.
1415185029Spjd	 */
1416185029Spjd	do_prefix = !((prop == ZFS_PROP_CANMOUNT) &&
1417185029Spjd	    (zprop_string_to_index(prop, propval, &idx,
1418185029Spjd	    ZFS_TYPE_DATASET) == 0) && (idx == ZFS_CANMOUNT_NOAUTO));
1419185029Spjd
1420185029Spjd	if (do_prefix && (ret = changelist_prefix(cl)) != 0)
1421168404Spjd		goto error;
1422168404Spjd
1423168404Spjd	/*
1424168404Spjd	 * Execute the corresponding ioctl() to set this property.
1425168404Spjd	 */
1426168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1427168404Spjd
1428185029Spjd	if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1429168404Spjd		goto error;
1430168404Spjd
1431185029Spjd	ret = zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1432209962Smm
1433168404Spjd	if (ret != 0) {
1434219089Spjd		zfs_setprop_error(hdl, prop, errno, errbuf);
1435219089Spjd		if (added_resv && errno == ENOSPC) {
1436219089Spjd			/* clean up the volsize property we tried to set */
1437219089Spjd			uint64_t old_volsize = zfs_prop_get_int(zhp,
1438219089Spjd			    ZFS_PROP_VOLSIZE);
1439219089Spjd			nvlist_free(nvl);
1440219089Spjd			zcmd_free_nvlists(&zc);
1441219089Spjd			if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
1442219089Spjd				goto error;
1443219089Spjd			if (nvlist_add_uint64(nvl,
1444219089Spjd			    zfs_prop_to_name(ZFS_PROP_VOLSIZE),
1445219089Spjd			    old_volsize) != 0)
1446219089Spjd				goto error;
1447219089Spjd			if (zcmd_write_src_nvlist(hdl, &zc, nvl) != 0)
1448219089Spjd				goto error;
1449219089Spjd			(void) zfs_ioctl(hdl, ZFS_IOC_SET_PROP, &zc);
1450168404Spjd		}
1451168404Spjd	} else {
1452185029Spjd		if (do_prefix)
1453185029Spjd			ret = changelist_postfix(cl);
1454185029Spjd
1455168404Spjd		/*
1456168404Spjd		 * Refresh the statistics so the new property value
1457168404Spjd		 * is reflected.
1458168404Spjd		 */
1459185029Spjd		if (ret == 0)
1460168404Spjd			(void) get_stats(zhp);
1461168404Spjd	}
1462168404Spjd
1463168404Spjderror:
1464168404Spjd	nvlist_free(nvl);
1465168404Spjd	zcmd_free_nvlists(&zc);
1466168404Spjd	if (cl)
1467168404Spjd		changelist_free(cl);
1468168404Spjd	return (ret);
1469168404Spjd}
1470168404Spjd
1471168404Spjd/*
1472219089Spjd * Given a property, inherit the value from the parent dataset, or if received
1473219089Spjd * is TRUE, revert to the received value, if any.
1474168404Spjd */
1475168404Spjdint
1476219089Spjdzfs_prop_inherit(zfs_handle_t *zhp, const char *propname, boolean_t received)
1477168404Spjd{
1478168404Spjd	zfs_cmd_t zc = { 0 };
1479168404Spjd	int ret;
1480168404Spjd	prop_changelist_t *cl;
1481168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
1482168404Spjd	char errbuf[1024];
1483168404Spjd	zfs_prop_t prop;
1484168404Spjd
1485168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
1486168404Spjd	    "cannot inherit %s for '%s'"), propname, zhp->zfs_name);
1487168404Spjd
1488219089Spjd	zc.zc_cookie = received;
1489185029Spjd	if ((prop = zfs_name_to_prop(propname)) == ZPROP_INVAL) {
1490168404Spjd		/*
1491168404Spjd		 * For user properties, the amount of work we have to do is very
1492168404Spjd		 * small, so just do it here.
1493168404Spjd		 */
1494168404Spjd		if (!zfs_prop_user(propname)) {
1495168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1496168404Spjd			    "invalid property"));
1497168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
1498168404Spjd		}
1499168404Spjd
1500168404Spjd		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1501168404Spjd		(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1502168404Spjd
1503185029Spjd		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc) != 0)
1504168404Spjd			return (zfs_standard_error(hdl, errno, errbuf));
1505168404Spjd
1506168404Spjd		return (0);
1507168404Spjd	}
1508168404Spjd
1509168404Spjd	/*
1510168404Spjd	 * Verify that this property is inheritable.
1511168404Spjd	 */
1512168404Spjd	if (zfs_prop_readonly(prop))
1513168404Spjd		return (zfs_error(hdl, EZFS_PROPREADONLY, errbuf));
1514168404Spjd
1515219089Spjd	if (!zfs_prop_inheritable(prop) && !received)
1516168404Spjd		return (zfs_error(hdl, EZFS_PROPNONINHERIT, errbuf));
1517168404Spjd
1518168404Spjd	/*
1519168404Spjd	 * Check to see if the value applies to this type
1520168404Spjd	 */
1521168404Spjd	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1522168404Spjd		return (zfs_error(hdl, EZFS_PROPTYPE, errbuf));
1523168404Spjd
1524168404Spjd	/*
1525219089Spjd	 * Normalize the name, to get rid of shorthand abbreviations.
1526168404Spjd	 */
1527168404Spjd	propname = zfs_prop_to_name(prop);
1528168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1529168404Spjd	(void) strlcpy(zc.zc_value, propname, sizeof (zc.zc_value));
1530168404Spjd
1531168404Spjd	if (prop == ZFS_PROP_MOUNTPOINT && getzoneid() == GLOBAL_ZONEID &&
1532168404Spjd	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
1533168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1534168404Spjd		    "dataset is used in a non-global zone"));
1535168404Spjd		return (zfs_error(hdl, EZFS_ZONED, errbuf));
1536168404Spjd	}
1537168404Spjd
1538168404Spjd	/*
1539168404Spjd	 * Determine datasets which will be affected by this change, if any.
1540168404Spjd	 */
1541185029Spjd	if ((cl = changelist_gather(zhp, prop, 0, 0)) == NULL)
1542168404Spjd		return (-1);
1543168404Spjd
1544168404Spjd	if (prop == ZFS_PROP_MOUNTPOINT && changelist_haszonedchild(cl)) {
1545168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
1546168404Spjd		    "child dataset with inherited mountpoint is used "
1547168404Spjd		    "in a non-global zone"));
1548168404Spjd		ret = zfs_error(hdl, EZFS_ZONED, errbuf);
1549168404Spjd		goto error;
1550168404Spjd	}
1551168404Spjd
1552168404Spjd	if ((ret = changelist_prefix(cl)) != 0)
1553168404Spjd		goto error;
1554168404Spjd
1555185029Spjd	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_INHERIT_PROP, &zc)) != 0) {
1556168404Spjd		return (zfs_standard_error(hdl, errno, errbuf));
1557168404Spjd	} else {
1558168404Spjd
1559168404Spjd		if ((ret = changelist_postfix(cl)) != 0)
1560168404Spjd			goto error;
1561168404Spjd
1562168404Spjd		/*
1563168404Spjd		 * Refresh the statistics so the new property is reflected.
1564168404Spjd		 */
1565168404Spjd		(void) get_stats(zhp);
1566168404Spjd	}
1567168404Spjd
1568168404Spjderror:
1569168404Spjd	changelist_free(cl);
1570168404Spjd	return (ret);
1571168404Spjd}
1572168404Spjd
1573168404Spjd/*
1574168404Spjd * True DSL properties are stored in an nvlist.  The following two functions
1575168404Spjd * extract them appropriately.
1576168404Spjd */
1577168404Spjdstatic uint64_t
1578168404Spjdgetprop_uint64(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
1579168404Spjd{
1580168404Spjd	nvlist_t *nv;
1581168404Spjd	uint64_t value;
1582168404Spjd
1583168404Spjd	*source = NULL;
1584168404Spjd	if (nvlist_lookup_nvlist(zhp->zfs_props,
1585168404Spjd	    zfs_prop_to_name(prop), &nv) == 0) {
1586185029Spjd		verify(nvlist_lookup_uint64(nv, ZPROP_VALUE, &value) == 0);
1587185029Spjd		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
1588168404Spjd	} else {
1589205198Sdelphij		verify(!zhp->zfs_props_table ||
1590205198Sdelphij		    zhp->zfs_props_table[prop] == B_TRUE);
1591168404Spjd		value = zfs_prop_default_numeric(prop);
1592168404Spjd		*source = "";
1593168404Spjd	}
1594168404Spjd
1595168404Spjd	return (value);
1596168404Spjd}
1597168404Spjd
1598168404Spjdstatic char *
1599168404Spjdgetprop_string(zfs_handle_t *zhp, zfs_prop_t prop, char **source)
1600168404Spjd{
1601168404Spjd	nvlist_t *nv;
1602168404Spjd	char *value;
1603168404Spjd
1604168404Spjd	*source = NULL;
1605168404Spjd	if (nvlist_lookup_nvlist(zhp->zfs_props,
1606168404Spjd	    zfs_prop_to_name(prop), &nv) == 0) {
1607185029Spjd		verify(nvlist_lookup_string(nv, ZPROP_VALUE, &value) == 0);
1608185029Spjd		(void) nvlist_lookup_string(nv, ZPROP_SOURCE, source);
1609168404Spjd	} else {
1610205198Sdelphij		verify(!zhp->zfs_props_table ||
1611205198Sdelphij		    zhp->zfs_props_table[prop] == B_TRUE);
1612168404Spjd		if ((value = (char *)zfs_prop_default_string(prop)) == NULL)
1613168404Spjd			value = "";
1614168404Spjd		*source = "";
1615168404Spjd	}
1616168404Spjd
1617168404Spjd	return (value);
1618168404Spjd}
1619168404Spjd
1620219089Spjdstatic boolean_t
1621219089Spjdzfs_is_recvd_props_mode(zfs_handle_t *zhp)
1622219089Spjd{
1623219089Spjd	return (zhp->zfs_props == zhp->zfs_recvd_props);
1624219089Spjd}
1625219089Spjd
1626219089Spjdstatic void
1627219089Spjdzfs_set_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
1628219089Spjd{
1629219089Spjd	*cookie = (uint64_t)(uintptr_t)zhp->zfs_props;
1630219089Spjd	zhp->zfs_props = zhp->zfs_recvd_props;
1631219089Spjd}
1632219089Spjd
1633219089Spjdstatic void
1634219089Spjdzfs_unset_recvd_props_mode(zfs_handle_t *zhp, uint64_t *cookie)
1635219089Spjd{
1636219089Spjd	zhp->zfs_props = (nvlist_t *)(uintptr_t)*cookie;
1637219089Spjd	*cookie = 0;
1638219089Spjd}
1639219089Spjd
1640168404Spjd/*
1641168404Spjd * Internal function for getting a numeric property.  Both zfs_prop_get() and
1642168404Spjd * zfs_prop_get_int() are built using this interface.
1643168404Spjd *
1644168404Spjd * Certain properties can be overridden using 'mount -o'.  In this case, scan
1645168404Spjd * the contents of the /etc/mnttab entry, searching for the appropriate options.
1646168404Spjd * If they differ from the on-disk values, report the current values and mark
1647168404Spjd * the source "temporary".
1648168404Spjd */
1649168404Spjdstatic int
1650185029Spjdget_numeric_property(zfs_handle_t *zhp, zfs_prop_t prop, zprop_source_t *src,
1651168404Spjd    char **source, uint64_t *val)
1652168404Spjd{
1653185029Spjd	zfs_cmd_t zc = { 0 };
1654185029Spjd	nvlist_t *zplprops = NULL;
1655168404Spjd	struct mnttab mnt;
1656168404Spjd	char *mntopt_on = NULL;
1657168404Spjd	char *mntopt_off = NULL;
1658219089Spjd	boolean_t received = zfs_is_recvd_props_mode(zhp);
1659168404Spjd
1660168404Spjd	*source = NULL;
1661168404Spjd
1662168404Spjd	switch (prop) {
1663168404Spjd	case ZFS_PROP_ATIME:
1664168404Spjd		mntopt_on = MNTOPT_ATIME;
1665168404Spjd		mntopt_off = MNTOPT_NOATIME;
1666168404Spjd		break;
1667168404Spjd
1668168404Spjd	case ZFS_PROP_DEVICES:
1669168404Spjd		mntopt_on = MNTOPT_DEVICES;
1670168404Spjd		mntopt_off = MNTOPT_NODEVICES;
1671168404Spjd		break;
1672168404Spjd
1673168404Spjd	case ZFS_PROP_EXEC:
1674168404Spjd		mntopt_on = MNTOPT_EXEC;
1675168404Spjd		mntopt_off = MNTOPT_NOEXEC;
1676168404Spjd		break;
1677168404Spjd
1678168404Spjd	case ZFS_PROP_READONLY:
1679168404Spjd		mntopt_on = MNTOPT_RO;
1680168404Spjd		mntopt_off = MNTOPT_RW;
1681168404Spjd		break;
1682168404Spjd
1683168404Spjd	case ZFS_PROP_SETUID:
1684168404Spjd		mntopt_on = MNTOPT_SETUID;
1685168404Spjd		mntopt_off = MNTOPT_NOSETUID;
1686168404Spjd		break;
1687168404Spjd
1688168404Spjd	case ZFS_PROP_XATTR:
1689168404Spjd		mntopt_on = MNTOPT_XATTR;
1690168404Spjd		mntopt_off = MNTOPT_NOXATTR;
1691168404Spjd		break;
1692185029Spjd
1693185029Spjd	case ZFS_PROP_NBMAND:
1694185029Spjd		mntopt_on = MNTOPT_NBMAND;
1695185029Spjd		mntopt_off = MNTOPT_NONBMAND;
1696185029Spjd		break;
1697168404Spjd	}
1698168404Spjd
1699168404Spjd	/*
1700168404Spjd	 * Because looking up the mount options is potentially expensive
1701168404Spjd	 * (iterating over all of /etc/mnttab), we defer its calculation until
1702168404Spjd	 * we're looking up a property which requires its presence.
1703168404Spjd	 */
1704168404Spjd	if (!zhp->zfs_mntcheck &&
1705168404Spjd	    (mntopt_on != NULL || prop == ZFS_PROP_MOUNTED)) {
1706209962Smm		libzfs_handle_t *hdl = zhp->zfs_hdl;
1707209962Smm		struct mnttab entry;
1708168404Spjd
1709209962Smm		if (libzfs_mnttab_find(hdl, zhp->zfs_name, &entry) == 0) {
1710209962Smm			zhp->zfs_mntopts = zfs_strdup(hdl,
1711168404Spjd			    entry.mnt_mntopts);
1712168404Spjd			if (zhp->zfs_mntopts == NULL)
1713168404Spjd				return (-1);
1714168404Spjd		}
1715168404Spjd
1716168404Spjd		zhp->zfs_mntcheck = B_TRUE;
1717168404Spjd	}
1718168404Spjd
1719168404Spjd	if (zhp->zfs_mntopts == NULL)
1720168404Spjd		mnt.mnt_mntopts = "";
1721168404Spjd	else
1722168404Spjd		mnt.mnt_mntopts = zhp->zfs_mntopts;
1723168404Spjd
1724168404Spjd	switch (prop) {
1725168404Spjd	case ZFS_PROP_ATIME:
1726168404Spjd	case ZFS_PROP_DEVICES:
1727168404Spjd	case ZFS_PROP_EXEC:
1728168404Spjd	case ZFS_PROP_READONLY:
1729168404Spjd	case ZFS_PROP_SETUID:
1730168404Spjd	case ZFS_PROP_XATTR:
1731185029Spjd	case ZFS_PROP_NBMAND:
1732168404Spjd		*val = getprop_uint64(zhp, prop, source);
1733168404Spjd
1734219089Spjd		if (received)
1735219089Spjd			break;
1736219089Spjd
1737168404Spjd		if (hasmntopt(&mnt, mntopt_on) && !*val) {
1738168404Spjd			*val = B_TRUE;
1739168404Spjd			if (src)
1740185029Spjd				*src = ZPROP_SRC_TEMPORARY;
1741168404Spjd		} else if (hasmntopt(&mnt, mntopt_off) && *val) {
1742168404Spjd			*val = B_FALSE;
1743168404Spjd			if (src)
1744185029Spjd				*src = ZPROP_SRC_TEMPORARY;
1745168404Spjd		}
1746168404Spjd		break;
1747168404Spjd
1748168404Spjd	case ZFS_PROP_CANMOUNT:
1749219089Spjd	case ZFS_PROP_VOLSIZE:
1750168404Spjd	case ZFS_PROP_QUOTA:
1751185029Spjd	case ZFS_PROP_REFQUOTA:
1752168404Spjd	case ZFS_PROP_RESERVATION:
1753185029Spjd	case ZFS_PROP_REFRESERVATION:
1754168404Spjd		*val = getprop_uint64(zhp, prop, source);
1755219089Spjd
1756219089Spjd		if (*source == NULL) {
1757219089Spjd			/* not default, must be local */
1758168404Spjd			*source = zhp->zfs_name;
1759219089Spjd		}
1760168404Spjd		break;
1761168404Spjd
1762168404Spjd	case ZFS_PROP_MOUNTED:
1763168404Spjd		*val = (zhp->zfs_mntopts != NULL);
1764168404Spjd		break;
1765168404Spjd
1766168404Spjd	case ZFS_PROP_NUMCLONES:
1767168404Spjd		*val = zhp->zfs_dmustats.dds_num_clones;
1768168404Spjd		break;
1769168404Spjd
1770185029Spjd	case ZFS_PROP_VERSION:
1771185029Spjd	case ZFS_PROP_NORMALIZE:
1772185029Spjd	case ZFS_PROP_UTF8ONLY:
1773185029Spjd	case ZFS_PROP_CASE:
1774185029Spjd		if (!zfs_prop_valid_for_type(prop, zhp->zfs_head_type) ||
1775185029Spjd		    zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
1776185029Spjd			return (-1);
1777185029Spjd		(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
1778185029Spjd		if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_OBJSET_ZPLPROPS, &zc)) {
1779185029Spjd			zcmd_free_nvlists(&zc);
1780219089Spjd			return (-1);
1781185029Spjd		}
1782185029Spjd		if (zcmd_read_dst_nvlist(zhp->zfs_hdl, &zc, &zplprops) != 0 ||
1783185029Spjd		    nvlist_lookup_uint64(zplprops, zfs_prop_to_name(prop),
1784185029Spjd		    val) != 0) {
1785185029Spjd			zcmd_free_nvlists(&zc);
1786219089Spjd			return (-1);
1787185029Spjd		}
1788185029Spjd		if (zplprops)
1789185029Spjd			nvlist_free(zplprops);
1790185029Spjd		zcmd_free_nvlists(&zc);
1791185029Spjd		break;
1792185029Spjd
1793168404Spjd	default:
1794185029Spjd		switch (zfs_prop_get_type(prop)) {
1795185029Spjd		case PROP_TYPE_NUMBER:
1796185029Spjd		case PROP_TYPE_INDEX:
1797185029Spjd			*val = getprop_uint64(zhp, prop, source);
1798185029Spjd			/*
1799209962Smm			 * If we tried to use a default value for a
1800185029Spjd			 * readonly property, it means that it was not
1801219089Spjd			 * present.
1802185029Spjd			 */
1803185029Spjd			if (zfs_prop_readonly(prop) &&
1804219089Spjd			    *source != NULL && (*source)[0] == '\0') {
1805219089Spjd				*source = NULL;
1806185029Spjd			}
1807185029Spjd			break;
1808185029Spjd
1809185029Spjd		case PROP_TYPE_STRING:
1810185029Spjd		default:
1811185029Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
1812185029Spjd			    "cannot get non-numeric property"));
1813185029Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_BADPROP,
1814185029Spjd			    dgettext(TEXT_DOMAIN, "internal error")));
1815185029Spjd		}
1816168404Spjd	}
1817168404Spjd
1818168404Spjd	return (0);
1819168404Spjd}
1820168404Spjd
1821168404Spjd/*
1822168404Spjd * Calculate the source type, given the raw source string.
1823168404Spjd */
1824168404Spjdstatic void
1825185029Spjdget_source(zfs_handle_t *zhp, zprop_source_t *srctype, char *source,
1826168404Spjd    char *statbuf, size_t statlen)
1827168404Spjd{
1828185029Spjd	if (statbuf == NULL || *srctype == ZPROP_SRC_TEMPORARY)
1829168404Spjd		return;
1830168404Spjd
1831168404Spjd	if (source == NULL) {
1832185029Spjd		*srctype = ZPROP_SRC_NONE;
1833168404Spjd	} else if (source[0] == '\0') {
1834185029Spjd		*srctype = ZPROP_SRC_DEFAULT;
1835219089Spjd	} else if (strstr(source, ZPROP_SOURCE_VAL_RECVD) != NULL) {
1836219089Spjd		*srctype = ZPROP_SRC_RECEIVED;
1837168404Spjd	} else {
1838168404Spjd		if (strcmp(source, zhp->zfs_name) == 0) {
1839185029Spjd			*srctype = ZPROP_SRC_LOCAL;
1840168404Spjd		} else {
1841168404Spjd			(void) strlcpy(statbuf, source, statlen);
1842185029Spjd			*srctype = ZPROP_SRC_INHERITED;
1843168404Spjd		}
1844168404Spjd	}
1845168404Spjd
1846168404Spjd}
1847168404Spjd
1848219089Spjdint
1849219089Spjdzfs_prop_get_recvd(zfs_handle_t *zhp, const char *propname, char *propbuf,
1850219089Spjd    size_t proplen, boolean_t literal)
1851219089Spjd{
1852219089Spjd	zfs_prop_t prop;
1853219089Spjd	int err = 0;
1854219089Spjd
1855219089Spjd	if (zhp->zfs_recvd_props == NULL)
1856219089Spjd		if (get_recvd_props_ioctl(zhp) != 0)
1857219089Spjd			return (-1);
1858219089Spjd
1859219089Spjd	prop = zfs_name_to_prop(propname);
1860219089Spjd
1861219089Spjd	if (prop != ZPROP_INVAL) {
1862219089Spjd		uint64_t cookie;
1863219089Spjd		if (!nvlist_exists(zhp->zfs_recvd_props, propname))
1864219089Spjd			return (-1);
1865219089Spjd		zfs_set_recvd_props_mode(zhp, &cookie);
1866219089Spjd		err = zfs_prop_get(zhp, prop, propbuf, proplen,
1867219089Spjd		    NULL, NULL, 0, literal);
1868219089Spjd		zfs_unset_recvd_props_mode(zhp, &cookie);
1869219089Spjd	} else if (zfs_prop_userquota(propname)) {
1870219089Spjd		return (-1);
1871219089Spjd	} else {
1872219089Spjd		nvlist_t *propval;
1873219089Spjd		char *recvdval;
1874219089Spjd		if (nvlist_lookup_nvlist(zhp->zfs_recvd_props,
1875219089Spjd		    propname, &propval) != 0)
1876219089Spjd			return (-1);
1877219089Spjd		verify(nvlist_lookup_string(propval, ZPROP_VALUE,
1878219089Spjd		    &recvdval) == 0);
1879219089Spjd		(void) strlcpy(propbuf, recvdval, proplen);
1880219089Spjd	}
1881219089Spjd
1882219089Spjd	return (err == 0 ? 0 : -1);
1883219089Spjd}
1884219089Spjd
1885168404Spjd/*
1886168404Spjd * Retrieve a property from the given object.  If 'literal' is specified, then
1887168404Spjd * numbers are left as exact values.  Otherwise, numbers are converted to a
1888168404Spjd * human-readable form.
1889168404Spjd *
1890168404Spjd * Returns 0 on success, or -1 on error.
1891168404Spjd */
1892168404Spjdint
1893168404Spjdzfs_prop_get(zfs_handle_t *zhp, zfs_prop_t prop, char *propbuf, size_t proplen,
1894185029Spjd    zprop_source_t *src, char *statbuf, size_t statlen, boolean_t literal)
1895168404Spjd{
1896168404Spjd	char *source = NULL;
1897168404Spjd	uint64_t val;
1898168404Spjd	char *str;
1899168404Spjd	const char *strval;
1900219089Spjd	boolean_t received = zfs_is_recvd_props_mode(zhp);
1901168404Spjd
1902168404Spjd	/*
1903168404Spjd	 * Check to see if this property applies to our object
1904168404Spjd	 */
1905168404Spjd	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type))
1906168404Spjd		return (-1);
1907168404Spjd
1908219089Spjd	if (received && zfs_prop_readonly(prop))
1909219089Spjd		return (-1);
1910219089Spjd
1911168404Spjd	if (src)
1912185029Spjd		*src = ZPROP_SRC_NONE;
1913168404Spjd
1914168404Spjd	switch (prop) {
1915168404Spjd	case ZFS_PROP_CREATION:
1916168404Spjd		/*
1917168404Spjd		 * 'creation' is a time_t stored in the statistics.  We convert
1918168404Spjd		 * this into a string unless 'literal' is specified.
1919168404Spjd		 */
1920168404Spjd		{
1921168404Spjd			val = getprop_uint64(zhp, prop, &source);
1922168404Spjd			time_t time = (time_t)val;
1923168404Spjd			struct tm t;
1924168404Spjd
1925168404Spjd			if (literal ||
1926168404Spjd			    localtime_r(&time, &t) == NULL ||
1927168404Spjd			    strftime(propbuf, proplen, "%a %b %e %k:%M %Y",
1928168404Spjd			    &t) == 0)
1929168404Spjd				(void) snprintf(propbuf, proplen, "%llu", val);
1930168404Spjd		}
1931168404Spjd		break;
1932168404Spjd
1933168404Spjd	case ZFS_PROP_MOUNTPOINT:
1934168404Spjd		/*
1935168404Spjd		 * Getting the precise mountpoint can be tricky.
1936168404Spjd		 *
1937168404Spjd		 *  - for 'none' or 'legacy', return those values.
1938168404Spjd		 *  - for inherited mountpoints, we want to take everything
1939168404Spjd		 *    after our ancestor and append it to the inherited value.
1940168404Spjd		 *
1941168404Spjd		 * If the pool has an alternate root, we want to prepend that
1942168404Spjd		 * root to any values we return.
1943168404Spjd		 */
1944185029Spjd
1945168404Spjd		str = getprop_string(zhp, prop, &source);
1946168404Spjd
1947185029Spjd		if (str[0] == '/') {
1948185029Spjd			char buf[MAXPATHLEN];
1949185029Spjd			char *root = buf;
1950219089Spjd			const char *relpath;
1951168404Spjd
1952219089Spjd			/*
1953219089Spjd			 * If we inherit the mountpoint, even from a dataset
1954219089Spjd			 * with a received value, the source will be the path of
1955219089Spjd			 * the dataset we inherit from. If source is
1956219089Spjd			 * ZPROP_SOURCE_VAL_RECVD, the received value is not
1957219089Spjd			 * inherited.
1958219089Spjd			 */
1959219089Spjd			if (strcmp(source, ZPROP_SOURCE_VAL_RECVD) == 0) {
1960219089Spjd				relpath = "";
1961219089Spjd			} else {
1962219089Spjd				relpath = zhp->zfs_name + strlen(source);
1963219089Spjd				if (relpath[0] == '/')
1964219089Spjd					relpath++;
1965219089Spjd			}
1966185029Spjd
1967185029Spjd			if ((zpool_get_prop(zhp->zpool_hdl,
1968185029Spjd			    ZPOOL_PROP_ALTROOT, buf, MAXPATHLEN, NULL)) ||
1969185029Spjd			    (strcmp(root, "-") == 0))
1970185029Spjd				root[0] = '\0';
1971185029Spjd			/*
1972185029Spjd			 * Special case an alternate root of '/'. This will
1973185029Spjd			 * avoid having multiple leading slashes in the
1974185029Spjd			 * mountpoint path.
1975185029Spjd			 */
1976185029Spjd			if (strcmp(root, "/") == 0)
1977185029Spjd				root++;
1978185029Spjd
1979185029Spjd			/*
1980185029Spjd			 * If the mountpoint is '/' then skip over this
1981185029Spjd			 * if we are obtaining either an alternate root or
1982185029Spjd			 * an inherited mountpoint.
1983185029Spjd			 */
1984185029Spjd			if (str[1] == '\0' && (root[0] != '\0' ||
1985185029Spjd			    relpath[0] != '\0'))
1986168404Spjd				str++;
1987168404Spjd
1988168404Spjd			if (relpath[0] == '\0')
1989168404Spjd				(void) snprintf(propbuf, proplen, "%s%s",
1990168404Spjd				    root, str);
1991168404Spjd			else
1992168404Spjd				(void) snprintf(propbuf, proplen, "%s%s%s%s",
1993168404Spjd				    root, str, relpath[0] == '@' ? "" : "/",
1994168404Spjd				    relpath);
1995168404Spjd		} else {
1996168404Spjd			/* 'legacy' or 'none' */
1997168404Spjd			(void) strlcpy(propbuf, str, proplen);
1998168404Spjd		}
1999168404Spjd
2000168404Spjd		break;
2001168404Spjd
2002168404Spjd	case ZFS_PROP_ORIGIN:
2003168404Spjd		(void) strlcpy(propbuf, getprop_string(zhp, prop, &source),
2004168404Spjd		    proplen);
2005168404Spjd		/*
2006168404Spjd		 * If there is no parent at all, return failure to indicate that
2007168404Spjd		 * it doesn't apply to this dataset.
2008168404Spjd		 */
2009168404Spjd		if (propbuf[0] == '\0')
2010168404Spjd			return (-1);
2011168404Spjd		break;
2012168404Spjd
2013168404Spjd	case ZFS_PROP_QUOTA:
2014185029Spjd	case ZFS_PROP_REFQUOTA:
2015168404Spjd	case ZFS_PROP_RESERVATION:
2016185029Spjd	case ZFS_PROP_REFRESERVATION:
2017185029Spjd
2018168404Spjd		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2019168404Spjd			return (-1);
2020168404Spjd
2021168404Spjd		/*
2022168404Spjd		 * If quota or reservation is 0, we translate this into 'none'
2023168404Spjd		 * (unless literal is set), and indicate that it's the default
2024168404Spjd		 * value.  Otherwise, we print the number nicely and indicate
2025168404Spjd		 * that its set locally.
2026168404Spjd		 */
2027168404Spjd		if (val == 0) {
2028168404Spjd			if (literal)
2029168404Spjd				(void) strlcpy(propbuf, "0", proplen);
2030168404Spjd			else
2031168404Spjd				(void) strlcpy(propbuf, "none", proplen);
2032168404Spjd		} else {
2033168404Spjd			if (literal)
2034168404Spjd				(void) snprintf(propbuf, proplen, "%llu",
2035168404Spjd				    (u_longlong_t)val);
2036168404Spjd			else
2037168404Spjd				zfs_nicenum(val, propbuf, proplen);
2038168404Spjd		}
2039168404Spjd		break;
2040168404Spjd
2041168404Spjd	case ZFS_PROP_COMPRESSRATIO:
2042168404Spjd		if (get_numeric_property(zhp, prop, src, &source, &val) != 0)
2043168404Spjd			return (-1);
2044219089Spjd		(void) snprintf(propbuf, proplen, "%llu.%02llux",
2045219089Spjd		    (u_longlong_t)(val / 100),
2046219089Spjd		    (u_longlong_t)(val % 100));
2047168404Spjd		break;
2048168404Spjd
2049168404Spjd	case ZFS_PROP_TYPE:
2050168404Spjd		switch (zhp->zfs_type) {
2051168404Spjd		case ZFS_TYPE_FILESYSTEM:
2052168404Spjd			str = "filesystem";
2053168404Spjd			break;
2054168404Spjd		case ZFS_TYPE_VOLUME:
2055168404Spjd			str = "volume";
2056168404Spjd			break;
2057168404Spjd		case ZFS_TYPE_SNAPSHOT:
2058168404Spjd			str = "snapshot";
2059168404Spjd			break;
2060168404Spjd		default:
2061168404Spjd			abort();
2062168404Spjd		}
2063168404Spjd		(void) snprintf(propbuf, proplen, "%s", str);
2064168404Spjd		break;
2065168404Spjd
2066168404Spjd	case ZFS_PROP_MOUNTED:
2067168404Spjd		/*
2068168404Spjd		 * The 'mounted' property is a pseudo-property that described
2069168404Spjd		 * whether the filesystem is currently mounted.  Even though
2070168404Spjd		 * it's a boolean value, the typical values of "on" and "off"
2071168404Spjd		 * don't make sense, so we translate to "yes" and "no".
2072168404Spjd		 */
2073168404Spjd		if (get_numeric_property(zhp, ZFS_PROP_MOUNTED,
2074168404Spjd		    src, &source, &val) != 0)
2075168404Spjd			return (-1);
2076168404Spjd		if (val)
2077168404Spjd			(void) strlcpy(propbuf, "yes", proplen);
2078168404Spjd		else
2079168404Spjd			(void) strlcpy(propbuf, "no", proplen);
2080168404Spjd		break;
2081168404Spjd
2082168404Spjd	case ZFS_PROP_NAME:
2083168404Spjd		/*
2084168404Spjd		 * The 'name' property is a pseudo-property derived from the
2085168404Spjd		 * dataset name.  It is presented as a real property to simplify
2086168404Spjd		 * consumers.
2087168404Spjd		 */
2088168404Spjd		(void) strlcpy(propbuf, zhp->zfs_name, proplen);
2089168404Spjd		break;
2090168404Spjd
2091219089Spjd	case ZFS_PROP_MLSLABEL:
2092219089Spjd		{
2093219089Spjd#ifdef sun
2094219089Spjd			m_label_t *new_sl = NULL;
2095219089Spjd			char *ascii = NULL;	/* human readable label */
2096219089Spjd
2097219089Spjd			(void) strlcpy(propbuf,
2098219089Spjd			    getprop_string(zhp, prop, &source), proplen);
2099219089Spjd
2100219089Spjd			if (literal || (strcasecmp(propbuf,
2101219089Spjd			    ZFS_MLSLABEL_DEFAULT) == 0))
2102219089Spjd				break;
2103219089Spjd
2104219089Spjd			/*
2105219089Spjd			 * Try to translate the internal hex string to
2106219089Spjd			 * human-readable output.  If there are any
2107219089Spjd			 * problems just use the hex string.
2108219089Spjd			 */
2109219089Spjd
2110219089Spjd			if (str_to_label(propbuf, &new_sl, MAC_LABEL,
2111219089Spjd			    L_NO_CORRECTION, NULL) == -1) {
2112219089Spjd				m_label_free(new_sl);
2113219089Spjd				break;
2114219089Spjd			}
2115219089Spjd
2116219089Spjd			if (label_to_str(new_sl, &ascii, M_LABEL,
2117219089Spjd			    DEF_NAMES) != 0) {
2118219089Spjd				if (ascii)
2119219089Spjd					free(ascii);
2120219089Spjd				m_label_free(new_sl);
2121219089Spjd				break;
2122219089Spjd			}
2123219089Spjd			m_label_free(new_sl);
2124219089Spjd
2125219089Spjd			(void) strlcpy(propbuf, ascii, proplen);
2126219089Spjd			free(ascii);
2127219089Spjd#else	/* !sun */
2128219089Spjd			propbuf[0] = '\0';
2129219089Spjd#endif	/* !sun */
2130219089Spjd		}
2131219089Spjd		break;
2132219089Spjd
2133168404Spjd	default:
2134185029Spjd		switch (zfs_prop_get_type(prop)) {
2135185029Spjd		case PROP_TYPE_NUMBER:
2136185029Spjd			if (get_numeric_property(zhp, prop, src,
2137185029Spjd			    &source, &val) != 0)
2138185029Spjd				return (-1);
2139185029Spjd			if (literal)
2140185029Spjd				(void) snprintf(propbuf, proplen, "%llu",
2141185029Spjd				    (u_longlong_t)val);
2142185029Spjd			else
2143185029Spjd				zfs_nicenum(val, propbuf, proplen);
2144185029Spjd			break;
2145185029Spjd
2146185029Spjd		case PROP_TYPE_STRING:
2147185029Spjd			(void) strlcpy(propbuf,
2148185029Spjd			    getprop_string(zhp, prop, &source), proplen);
2149185029Spjd			break;
2150185029Spjd
2151185029Spjd		case PROP_TYPE_INDEX:
2152185029Spjd			if (get_numeric_property(zhp, prop, src,
2153185029Spjd			    &source, &val) != 0)
2154185029Spjd				return (-1);
2155185029Spjd			if (zfs_prop_index_to_string(prop, val, &strval) != 0)
2156185029Spjd				return (-1);
2157185029Spjd			(void) strlcpy(propbuf, strval, proplen);
2158185029Spjd			break;
2159185029Spjd
2160185029Spjd		default:
2161185029Spjd			abort();
2162185029Spjd		}
2163168404Spjd	}
2164168404Spjd
2165168404Spjd	get_source(zhp, src, source, statbuf, statlen);
2166168404Spjd
2167168404Spjd	return (0);
2168168404Spjd}
2169168404Spjd
2170168404Spjd/*
2171168404Spjd * Utility function to get the given numeric property.  Does no validation that
2172168404Spjd * the given property is the appropriate type; should only be used with
2173168404Spjd * hard-coded property types.
2174168404Spjd */
2175168404Spjduint64_t
2176168404Spjdzfs_prop_get_int(zfs_handle_t *zhp, zfs_prop_t prop)
2177168404Spjd{
2178168404Spjd	char *source;
2179168404Spjd	uint64_t val;
2180168404Spjd
2181185029Spjd	(void) get_numeric_property(zhp, prop, NULL, &source, &val);
2182168404Spjd
2183168404Spjd	return (val);
2184168404Spjd}
2185168404Spjd
2186185029Spjdint
2187185029Spjdzfs_prop_set_int(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t val)
2188185029Spjd{
2189185029Spjd	char buf[64];
2190185029Spjd
2191209962Smm	(void) snprintf(buf, sizeof (buf), "%llu", (longlong_t)val);
2192185029Spjd	return (zfs_prop_set(zhp, zfs_prop_to_name(prop), buf));
2193185029Spjd}
2194185029Spjd
2195168404Spjd/*
2196168404Spjd * Similar to zfs_prop_get(), but returns the value as an integer.
2197168404Spjd */
2198168404Spjdint
2199168404Spjdzfs_prop_get_numeric(zfs_handle_t *zhp, zfs_prop_t prop, uint64_t *value,
2200185029Spjd    zprop_source_t *src, char *statbuf, size_t statlen)
2201168404Spjd{
2202168404Spjd	char *source;
2203168404Spjd
2204168404Spjd	/*
2205168404Spjd	 * Check to see if this property applies to our object
2206168404Spjd	 */
2207185029Spjd	if (!zfs_prop_valid_for_type(prop, zhp->zfs_type)) {
2208168404Spjd		return (zfs_error_fmt(zhp->zfs_hdl, EZFS_PROPTYPE,
2209168404Spjd		    dgettext(TEXT_DOMAIN, "cannot get property '%s'"),
2210168404Spjd		    zfs_prop_to_name(prop)));
2211185029Spjd	}
2212168404Spjd
2213168404Spjd	if (src)
2214185029Spjd		*src = ZPROP_SRC_NONE;
2215168404Spjd
2216168404Spjd	if (get_numeric_property(zhp, prop, src, &source, value) != 0)
2217168404Spjd		return (-1);
2218168404Spjd
2219168404Spjd	get_source(zhp, src, source, statbuf, statlen);
2220168404Spjd
2221168404Spjd	return (0);
2222168404Spjd}
2223168404Spjd
2224209962Smmstatic int
2225209962Smmidmap_id_to_numeric_domain_rid(uid_t id, boolean_t isuser,
2226209962Smm    char **domainp, idmap_rid_t *ridp)
2227209962Smm{
2228209962Smm#ifdef sun
2229209962Smm	idmap_get_handle_t *get_hdl = NULL;
2230209962Smm	idmap_stat status;
2231209962Smm	int err = EINVAL;
2232209962Smm
2233219089Spjd	if (idmap_get_create(&get_hdl) != IDMAP_SUCCESS)
2234209962Smm		goto out;
2235209962Smm
2236209962Smm	if (isuser) {
2237209962Smm		err = idmap_get_sidbyuid(get_hdl, id,
2238209962Smm		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2239209962Smm	} else {
2240209962Smm		err = idmap_get_sidbygid(get_hdl, id,
2241209962Smm		    IDMAP_REQ_FLG_USE_CACHE, domainp, ridp, &status);
2242209962Smm	}
2243209962Smm	if (err == IDMAP_SUCCESS &&
2244209962Smm	    idmap_get_mappings(get_hdl) == IDMAP_SUCCESS &&
2245209962Smm	    status == IDMAP_SUCCESS)
2246209962Smm		err = 0;
2247209962Smm	else
2248209962Smm		err = EINVAL;
2249209962Smmout:
2250209962Smm	if (get_hdl)
2251209962Smm		idmap_get_destroy(get_hdl);
2252209962Smm	return (err);
2253209962Smm#else	/* !sun */
2254209962Smm	assert(!"invalid code path");
2255209962Smm#endif	/* !sun */
2256209962Smm}
2257209962Smm
2258168404Spjd/*
2259209962Smm * convert the propname into parameters needed by kernel
2260209962Smm * Eg: userquota@ahrens -> ZFS_PROP_USERQUOTA, "", 126829
2261209962Smm * Eg: userused@matt@domain -> ZFS_PROP_USERUSED, "S-1-123-456", 789
2262209962Smm */
2263209962Smmstatic int
2264209962Smmuserquota_propname_decode(const char *propname, boolean_t zoned,
2265209962Smm    zfs_userquota_prop_t *typep, char *domain, int domainlen, uint64_t *ridp)
2266209962Smm{
2267209962Smm	zfs_userquota_prop_t type;
2268209962Smm	char *cp, *end;
2269209962Smm	char *numericsid = NULL;
2270209962Smm	boolean_t isuser;
2271209962Smm
2272209962Smm	domain[0] = '\0';
2273209962Smm
2274209962Smm	/* Figure out the property type ({user|group}{quota|space}) */
2275209962Smm	for (type = 0; type < ZFS_NUM_USERQUOTA_PROPS; type++) {
2276209962Smm		if (strncmp(propname, zfs_userquota_prop_prefixes[type],
2277209962Smm		    strlen(zfs_userquota_prop_prefixes[type])) == 0)
2278209962Smm			break;
2279209962Smm	}
2280209962Smm	if (type == ZFS_NUM_USERQUOTA_PROPS)
2281209962Smm		return (EINVAL);
2282209962Smm	*typep = type;
2283209962Smm
2284209962Smm	isuser = (type == ZFS_PROP_USERQUOTA ||
2285209962Smm	    type == ZFS_PROP_USERUSED);
2286209962Smm
2287209962Smm	cp = strchr(propname, '@') + 1;
2288209962Smm
2289209962Smm	if (strchr(cp, '@')) {
2290209962Smm#ifdef sun
2291209962Smm		/*
2292209962Smm		 * It's a SID name (eg "user@domain") that needs to be
2293209962Smm		 * turned into S-1-domainID-RID.
2294209962Smm		 */
2295209962Smm		directory_error_t e;
2296209962Smm		if (zoned && getzoneid() == GLOBAL_ZONEID)
2297209962Smm			return (ENOENT);
2298209962Smm		if (isuser) {
2299209962Smm			e = directory_sid_from_user_name(NULL,
2300209962Smm			    cp, &numericsid);
2301209962Smm		} else {
2302209962Smm			e = directory_sid_from_group_name(NULL,
2303209962Smm			    cp, &numericsid);
2304209962Smm		}
2305209962Smm		if (e != NULL) {
2306209962Smm			directory_error_free(e);
2307209962Smm			return (ENOENT);
2308209962Smm		}
2309209962Smm		if (numericsid == NULL)
2310209962Smm			return (ENOENT);
2311209962Smm		cp = numericsid;
2312209962Smm		/* will be further decoded below */
2313209962Smm#else	/* !sun */
2314219089Spjd		return (ENOENT);
2315209962Smm#endif	/* !sun */
2316209962Smm	}
2317209962Smm
2318209962Smm	if (strncmp(cp, "S-1-", 4) == 0) {
2319209962Smm		/* It's a numeric SID (eg "S-1-234-567-89") */
2320209962Smm		(void) strlcpy(domain, cp, domainlen);
2321209962Smm		cp = strrchr(domain, '-');
2322209962Smm		*cp = '\0';
2323209962Smm		cp++;
2324209962Smm
2325209962Smm		errno = 0;
2326209962Smm		*ridp = strtoull(cp, &end, 10);
2327209962Smm		if (numericsid) {
2328209962Smm			free(numericsid);
2329209962Smm			numericsid = NULL;
2330209962Smm		}
2331209962Smm		if (errno != 0 || *end != '\0')
2332209962Smm			return (EINVAL);
2333209962Smm	} else if (!isdigit(*cp)) {
2334209962Smm		/*
2335209962Smm		 * It's a user/group name (eg "user") that needs to be
2336209962Smm		 * turned into a uid/gid
2337209962Smm		 */
2338209962Smm		if (zoned && getzoneid() == GLOBAL_ZONEID)
2339209962Smm			return (ENOENT);
2340209962Smm		if (isuser) {
2341209962Smm			struct passwd *pw;
2342209962Smm			pw = getpwnam(cp);
2343209962Smm			if (pw == NULL)
2344209962Smm				return (ENOENT);
2345209962Smm			*ridp = pw->pw_uid;
2346209962Smm		} else {
2347209962Smm			struct group *gr;
2348209962Smm			gr = getgrnam(cp);
2349209962Smm			if (gr == NULL)
2350209962Smm				return (ENOENT);
2351209962Smm			*ridp = gr->gr_gid;
2352209962Smm		}
2353209962Smm	} else {
2354209962Smm		/* It's a user/group ID (eg "12345"). */
2355209962Smm		uid_t id = strtoul(cp, &end, 10);
2356209962Smm		idmap_rid_t rid;
2357209962Smm		char *mapdomain;
2358209962Smm
2359209962Smm		if (*end != '\0')
2360209962Smm			return (EINVAL);
2361209962Smm		if (id > MAXUID) {
2362209962Smm			/* It's an ephemeral ID. */
2363209962Smm			if (idmap_id_to_numeric_domain_rid(id, isuser,
2364209962Smm			    &mapdomain, &rid) != 0)
2365209962Smm				return (ENOENT);
2366209962Smm			(void) strlcpy(domain, mapdomain, domainlen);
2367209962Smm			*ridp = rid;
2368209962Smm		} else {
2369209962Smm			*ridp = id;
2370209962Smm		}
2371209962Smm	}
2372209962Smm
2373209962Smm	ASSERT3P(numericsid, ==, NULL);
2374209962Smm	return (0);
2375209962Smm}
2376209962Smm
2377209962Smmstatic int
2378209962Smmzfs_prop_get_userquota_common(zfs_handle_t *zhp, const char *propname,
2379209962Smm    uint64_t *propvalue, zfs_userquota_prop_t *typep)
2380209962Smm{
2381209962Smm	int err;
2382209962Smm	zfs_cmd_t zc = { 0 };
2383209962Smm
2384209962Smm	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2385209962Smm
2386209962Smm	err = userquota_propname_decode(propname,
2387209962Smm	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED),
2388209962Smm	    typep, zc.zc_value, sizeof (zc.zc_value), &zc.zc_guid);
2389209962Smm	zc.zc_objset_type = *typep;
2390209962Smm	if (err)
2391209962Smm		return (err);
2392209962Smm
2393209962Smm	err = ioctl(zhp->zfs_hdl->libzfs_fd, ZFS_IOC_USERSPACE_ONE, &zc);
2394209962Smm	if (err)
2395209962Smm		return (err);
2396209962Smm
2397209962Smm	*propvalue = zc.zc_cookie;
2398209962Smm	return (0);
2399209962Smm}
2400209962Smm
2401209962Smmint
2402209962Smmzfs_prop_get_userquota_int(zfs_handle_t *zhp, const char *propname,
2403209962Smm    uint64_t *propvalue)
2404209962Smm{
2405209962Smm	zfs_userquota_prop_t type;
2406209962Smm
2407209962Smm	return (zfs_prop_get_userquota_common(zhp, propname, propvalue,
2408209962Smm	    &type));
2409209962Smm}
2410209962Smm
2411209962Smmint
2412209962Smmzfs_prop_get_userquota(zfs_handle_t *zhp, const char *propname,
2413209962Smm    char *propbuf, int proplen, boolean_t literal)
2414209962Smm{
2415209962Smm	int err;
2416209962Smm	uint64_t propvalue;
2417209962Smm	zfs_userquota_prop_t type;
2418209962Smm
2419209962Smm	err = zfs_prop_get_userquota_common(zhp, propname, &propvalue,
2420209962Smm	    &type);
2421209962Smm
2422209962Smm	if (err)
2423209962Smm		return (err);
2424209962Smm
2425209962Smm	if (literal) {
2426209962Smm		(void) snprintf(propbuf, proplen, "%llu", propvalue);
2427209962Smm	} else if (propvalue == 0 &&
2428209962Smm	    (type == ZFS_PROP_USERQUOTA || type == ZFS_PROP_GROUPQUOTA)) {
2429209962Smm		(void) strlcpy(propbuf, "none", proplen);
2430209962Smm	} else {
2431209962Smm		zfs_nicenum(propvalue, propbuf, proplen);
2432209962Smm	}
2433209962Smm	return (0);
2434209962Smm}
2435209962Smm
2436209962Smm/*
2437168404Spjd * Returns the name of the given zfs handle.
2438168404Spjd */
2439168404Spjdconst char *
2440168404Spjdzfs_get_name(const zfs_handle_t *zhp)
2441168404Spjd{
2442168404Spjd	return (zhp->zfs_name);
2443168404Spjd}
2444168404Spjd
2445168404Spjd/*
2446168404Spjd * Returns the type of the given zfs handle.
2447168404Spjd */
2448168404Spjdzfs_type_t
2449168404Spjdzfs_get_type(const zfs_handle_t *zhp)
2450168404Spjd{
2451168404Spjd	return (zhp->zfs_type);
2452168404Spjd}
2453168404Spjd
2454209962Smmstatic int
2455209962Smmzfs_do_list_ioctl(zfs_handle_t *zhp, unsigned long arg, zfs_cmd_t *zc)
2456209962Smm{
2457209962Smm	int rc;
2458209962Smm	uint64_t	orig_cookie;
2459209962Smm
2460209962Smm	orig_cookie = zc->zc_cookie;
2461209962Smmtop:
2462209962Smm	(void) strlcpy(zc->zc_name, zhp->zfs_name, sizeof (zc->zc_name));
2463209962Smm	rc = ioctl(zhp->zfs_hdl->libzfs_fd, arg, zc);
2464209962Smm
2465209962Smm	if (rc == -1) {
2466209962Smm		switch (errno) {
2467209962Smm		case ENOMEM:
2468209962Smm			/* expand nvlist memory and try again */
2469209962Smm			if (zcmd_expand_dst_nvlist(zhp->zfs_hdl, zc) != 0) {
2470209962Smm				zcmd_free_nvlists(zc);
2471209962Smm				return (-1);
2472209962Smm			}
2473209962Smm			zc->zc_cookie = orig_cookie;
2474209962Smm			goto top;
2475209962Smm		/*
2476209962Smm		 * An errno value of ESRCH indicates normal completion.
2477209962Smm		 * If ENOENT is returned, then the underlying dataset
2478209962Smm		 * has been removed since we obtained the handle.
2479209962Smm		 */
2480209962Smm		case ESRCH:
2481209962Smm		case ENOENT:
2482209962Smm			rc = 1;
2483209962Smm			break;
2484209962Smm		default:
2485209962Smm			rc = zfs_standard_error(zhp->zfs_hdl, errno,
2486209962Smm			    dgettext(TEXT_DOMAIN,
2487209962Smm			    "cannot iterate filesystems"));
2488209962Smm			break;
2489209962Smm		}
2490209962Smm	}
2491209962Smm	return (rc);
2492209962Smm}
2493209962Smm
2494168404Spjd/*
2495168404Spjd * Iterate over all child filesystems
2496168404Spjd */
2497168404Spjdint
2498168404Spjdzfs_iter_filesystems(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2499168404Spjd{
2500168404Spjd	zfs_cmd_t zc = { 0 };
2501168404Spjd	zfs_handle_t *nzhp;
2502168404Spjd	int ret;
2503168404Spjd
2504185029Spjd	if (zhp->zfs_type != ZFS_TYPE_FILESYSTEM)
2505185029Spjd		return (0);
2506185029Spjd
2507209962Smm	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2508209962Smm		return (-1);
2509209962Smm
2510209962Smm	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_DATASET_LIST_NEXT,
2511209962Smm	    &zc)) == 0) {
2512168404Spjd		/*
2513168404Spjd		 * Silently ignore errors, as the only plausible explanation is
2514168404Spjd		 * that the pool has since been removed.
2515168404Spjd		 */
2516209962Smm		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2517209962Smm		    &zc)) == NULL) {
2518168404Spjd			continue;
2519209962Smm		}
2520168404Spjd
2521209962Smm		if ((ret = func(nzhp, data)) != 0) {
2522209962Smm			zcmd_free_nvlists(&zc);
2523168404Spjd			return (ret);
2524209962Smm		}
2525168404Spjd	}
2526209962Smm	zcmd_free_nvlists(&zc);
2527209962Smm	return ((ret < 0) ? ret : 0);
2528168404Spjd}
2529168404Spjd
2530168404Spjd/*
2531168404Spjd * Iterate over all snapshots
2532168404Spjd */
2533168404Spjdint
2534168404Spjdzfs_iter_snapshots(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2535168404Spjd{
2536168404Spjd	zfs_cmd_t zc = { 0 };
2537168404Spjd	zfs_handle_t *nzhp;
2538168404Spjd	int ret;
2539168404Spjd
2540185029Spjd	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT)
2541185029Spjd		return (0);
2542185029Spjd
2543209962Smm	if (zcmd_alloc_dst_nvlist(zhp->zfs_hdl, &zc, 0) != 0)
2544209962Smm		return (-1);
2545209962Smm	while ((ret = zfs_do_list_ioctl(zhp, ZFS_IOC_SNAPSHOT_LIST_NEXT,
2546209962Smm	    &zc)) == 0) {
2547168404Spjd
2548209962Smm		if ((nzhp = make_dataset_handle_zc(zhp->zfs_hdl,
2549209962Smm		    &zc)) == NULL) {
2550209962Smm			continue;
2551209962Smm		}
2552209962Smm
2553209962Smm		if ((ret = func(nzhp, data)) != 0) {
2554209962Smm			zcmd_free_nvlists(&zc);
2555168404Spjd			return (ret);
2556209962Smm		}
2557168404Spjd	}
2558209962Smm	zcmd_free_nvlists(&zc);
2559209962Smm	return ((ret < 0) ? ret : 0);
2560168404Spjd}
2561168404Spjd
2562168404Spjd/*
2563168404Spjd * Iterate over all children, snapshots and filesystems
2564168404Spjd */
2565168404Spjdint
2566168404Spjdzfs_iter_children(zfs_handle_t *zhp, zfs_iter_f func, void *data)
2567168404Spjd{
2568168404Spjd	int ret;
2569168404Spjd
2570168404Spjd	if ((ret = zfs_iter_filesystems(zhp, func, data)) != 0)
2571168404Spjd		return (ret);
2572168404Spjd
2573168404Spjd	return (zfs_iter_snapshots(zhp, func, data));
2574168404Spjd}
2575168404Spjd
2576168404Spjd/*
2577219089Spjd * Is one dataset name a child dataset of another?
2578219089Spjd *
2579219089Spjd * Needs to handle these cases:
2580219089Spjd * Dataset 1	"a/foo"		"a/foo"		"a/foo"		"a/foo"
2581219089Spjd * Dataset 2	"a/fo"		"a/foobar"	"a/bar/baz"	"a/foo/bar"
2582219089Spjd * Descendant?	No.		No.		No.		Yes.
2583219089Spjd */
2584219089Spjdstatic boolean_t
2585219089Spjdis_descendant(const char *ds1, const char *ds2)
2586219089Spjd{
2587219089Spjd	size_t d1len = strlen(ds1);
2588219089Spjd
2589219089Spjd	/* ds2 can't be a descendant if it's smaller */
2590219089Spjd	if (strlen(ds2) < d1len)
2591219089Spjd		return (B_FALSE);
2592219089Spjd
2593219089Spjd	/* otherwise, compare strings and verify that there's a '/' char */
2594219089Spjd	return (ds2[d1len] == '/' && (strncmp(ds1, ds2, d1len) == 0));
2595219089Spjd}
2596219089Spjd
2597219089Spjd/*
2598168404Spjd * Given a complete name, return just the portion that refers to the parent.
2599168404Spjd * Can return NULL if this is a pool.
2600168404Spjd */
2601168404Spjdstatic int
2602168404Spjdparent_name(const char *path, char *buf, size_t buflen)
2603168404Spjd{
2604168404Spjd	char *loc;
2605168404Spjd
2606168404Spjd	if ((loc = strrchr(path, '/')) == NULL)
2607168404Spjd		return (-1);
2608168404Spjd
2609168404Spjd	(void) strncpy(buf, path, MIN(buflen, loc - path));
2610168404Spjd	buf[loc - path] = '\0';
2611168404Spjd
2612168404Spjd	return (0);
2613168404Spjd}
2614168404Spjd
2615168404Spjd/*
2616185029Spjd * If accept_ancestor is false, then check to make sure that the given path has
2617185029Spjd * a parent, and that it exists.  If accept_ancestor is true, then find the
2618185029Spjd * closest existing ancestor for the given path.  In prefixlen return the
2619185029Spjd * length of already existing prefix of the given path.  We also fetch the
2620185029Spjd * 'zoned' property, which is used to validate property settings when creating
2621185029Spjd * new datasets.
2622168404Spjd */
2623168404Spjdstatic int
2624185029Spjdcheck_parents(libzfs_handle_t *hdl, const char *path, uint64_t *zoned,
2625185029Spjd    boolean_t accept_ancestor, int *prefixlen)
2626168404Spjd{
2627168404Spjd	zfs_cmd_t zc = { 0 };
2628168404Spjd	char parent[ZFS_MAXNAMELEN];
2629168404Spjd	char *slash;
2630168404Spjd	zfs_handle_t *zhp;
2631168404Spjd	char errbuf[1024];
2632219089Spjd	uint64_t is_zoned;
2633168404Spjd
2634209962Smm	(void) snprintf(errbuf, sizeof (errbuf),
2635209962Smm	    dgettext(TEXT_DOMAIN, "cannot create '%s'"), path);
2636168404Spjd
2637168404Spjd	/* get parent, and check to see if this is just a pool */
2638168404Spjd	if (parent_name(path, parent, sizeof (parent)) != 0) {
2639168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2640168404Spjd		    "missing dataset name"));
2641168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2642168404Spjd	}
2643168404Spjd
2644168404Spjd	/* check to see if the pool exists */
2645168404Spjd	if ((slash = strchr(parent, '/')) == NULL)
2646168404Spjd		slash = parent + strlen(parent);
2647168404Spjd	(void) strncpy(zc.zc_name, parent, slash - parent);
2648168404Spjd	zc.zc_name[slash - parent] = '\0';
2649168404Spjd	if (ioctl(hdl->libzfs_fd, ZFS_IOC_OBJSET_STATS, &zc) != 0 &&
2650168404Spjd	    errno == ENOENT) {
2651168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2652168404Spjd		    "no such pool '%s'"), zc.zc_name);
2653168404Spjd		return (zfs_error(hdl, EZFS_NOENT, errbuf));
2654168404Spjd	}
2655168404Spjd
2656168404Spjd	/* check to see if the parent dataset exists */
2657185029Spjd	while ((zhp = make_dataset_handle(hdl, parent)) == NULL) {
2658185029Spjd		if (errno == ENOENT && accept_ancestor) {
2659185029Spjd			/*
2660185029Spjd			 * Go deeper to find an ancestor, give up on top level.
2661185029Spjd			 */
2662185029Spjd			if (parent_name(parent, parent, sizeof (parent)) != 0) {
2663185029Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2664185029Spjd				    "no such pool '%s'"), zc.zc_name);
2665185029Spjd				return (zfs_error(hdl, EZFS_NOENT, errbuf));
2666185029Spjd			}
2667185029Spjd		} else if (errno == ENOENT) {
2668168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2669168404Spjd			    "parent does not exist"));
2670168404Spjd			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2671185029Spjd		} else
2672168404Spjd			return (zfs_standard_error(hdl, errno, errbuf));
2673168404Spjd	}
2674168404Spjd
2675219089Spjd	is_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
2676219089Spjd	if (zoned != NULL)
2677219089Spjd		*zoned = is_zoned;
2678219089Spjd
2679168404Spjd	/* we are in a non-global zone, but parent is in the global zone */
2680219089Spjd	if (getzoneid() != GLOBAL_ZONEID && !is_zoned) {
2681168404Spjd		(void) zfs_standard_error(hdl, EPERM, errbuf);
2682168404Spjd		zfs_close(zhp);
2683168404Spjd		return (-1);
2684168404Spjd	}
2685168404Spjd
2686168404Spjd	/* make sure parent is a filesystem */
2687168404Spjd	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
2688168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2689168404Spjd		    "parent is not a filesystem"));
2690168404Spjd		(void) zfs_error(hdl, EZFS_BADTYPE, errbuf);
2691168404Spjd		zfs_close(zhp);
2692168404Spjd		return (-1);
2693168404Spjd	}
2694168404Spjd
2695168404Spjd	zfs_close(zhp);
2696185029Spjd	if (prefixlen != NULL)
2697185029Spjd		*prefixlen = strlen(parent);
2698168404Spjd	return (0);
2699168404Spjd}
2700168404Spjd
2701168404Spjd/*
2702185029Spjd * Finds whether the dataset of the given type(s) exists.
2703185029Spjd */
2704185029Spjdboolean_t
2705185029Spjdzfs_dataset_exists(libzfs_handle_t *hdl, const char *path, zfs_type_t types)
2706185029Spjd{
2707185029Spjd	zfs_handle_t *zhp;
2708185029Spjd
2709185029Spjd	if (!zfs_validate_name(hdl, path, types, B_FALSE))
2710185029Spjd		return (B_FALSE);
2711185029Spjd
2712185029Spjd	/*
2713185029Spjd	 * Try to get stats for the dataset, which will tell us if it exists.
2714185029Spjd	 */
2715185029Spjd	if ((zhp = make_dataset_handle(hdl, path)) != NULL) {
2716185029Spjd		int ds_type = zhp->zfs_type;
2717185029Spjd
2718185029Spjd		zfs_close(zhp);
2719185029Spjd		if (types & ds_type)
2720185029Spjd			return (B_TRUE);
2721185029Spjd	}
2722185029Spjd	return (B_FALSE);
2723185029Spjd}
2724185029Spjd
2725185029Spjd/*
2726185029Spjd * Given a path to 'target', create all the ancestors between
2727185029Spjd * the prefixlen portion of the path, and the target itself.
2728185029Spjd * Fail if the initial prefixlen-ancestor does not already exist.
2729185029Spjd */
2730185029Spjdint
2731185029Spjdcreate_parents(libzfs_handle_t *hdl, char *target, int prefixlen)
2732185029Spjd{
2733185029Spjd	zfs_handle_t *h;
2734185029Spjd	char *cp;
2735185029Spjd	const char *opname;
2736185029Spjd
2737185029Spjd	/* make sure prefix exists */
2738185029Spjd	cp = target + prefixlen;
2739185029Spjd	if (*cp != '/') {
2740185029Spjd		assert(strchr(cp, '/') == NULL);
2741185029Spjd		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2742185029Spjd	} else {
2743185029Spjd		*cp = '\0';
2744185029Spjd		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2745185029Spjd		*cp = '/';
2746185029Spjd	}
2747185029Spjd	if (h == NULL)
2748185029Spjd		return (-1);
2749185029Spjd	zfs_close(h);
2750185029Spjd
2751185029Spjd	/*
2752185029Spjd	 * Attempt to create, mount, and share any ancestor filesystems,
2753185029Spjd	 * up to the prefixlen-long one.
2754185029Spjd	 */
2755185029Spjd	for (cp = target + prefixlen + 1;
2756185029Spjd	    cp = strchr(cp, '/'); *cp = '/', cp++) {
2757185029Spjd		char *logstr;
2758185029Spjd
2759185029Spjd		*cp = '\0';
2760185029Spjd
2761185029Spjd		h = make_dataset_handle(hdl, target);
2762185029Spjd		if (h) {
2763185029Spjd			/* it already exists, nothing to do here */
2764185029Spjd			zfs_close(h);
2765185029Spjd			continue;
2766185029Spjd		}
2767185029Spjd
2768185029Spjd		logstr = hdl->libzfs_log_str;
2769185029Spjd		hdl->libzfs_log_str = NULL;
2770185029Spjd		if (zfs_create(hdl, target, ZFS_TYPE_FILESYSTEM,
2771185029Spjd		    NULL) != 0) {
2772185029Spjd			hdl->libzfs_log_str = logstr;
2773185029Spjd			opname = dgettext(TEXT_DOMAIN, "create");
2774185029Spjd			goto ancestorerr;
2775185029Spjd		}
2776185029Spjd
2777185029Spjd		hdl->libzfs_log_str = logstr;
2778185029Spjd		h = zfs_open(hdl, target, ZFS_TYPE_FILESYSTEM);
2779185029Spjd		if (h == NULL) {
2780185029Spjd			opname = dgettext(TEXT_DOMAIN, "open");
2781185029Spjd			goto ancestorerr;
2782185029Spjd		}
2783185029Spjd
2784185029Spjd		if (zfs_mount(h, NULL, 0) != 0) {
2785185029Spjd			opname = dgettext(TEXT_DOMAIN, "mount");
2786185029Spjd			goto ancestorerr;
2787185029Spjd		}
2788185029Spjd
2789185029Spjd		if (zfs_share(h) != 0) {
2790185029Spjd			opname = dgettext(TEXT_DOMAIN, "share");
2791185029Spjd			goto ancestorerr;
2792185029Spjd		}
2793185029Spjd
2794185029Spjd		zfs_close(h);
2795185029Spjd	}
2796185029Spjd
2797185029Spjd	return (0);
2798185029Spjd
2799185029Spjdancestorerr:
2800185029Spjd	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2801185029Spjd	    "failed to %s ancestor '%s'"), opname, target);
2802185029Spjd	return (-1);
2803185029Spjd}
2804185029Spjd
2805185029Spjd/*
2806185029Spjd * Creates non-existing ancestors of the given path.
2807185029Spjd */
2808185029Spjdint
2809185029Spjdzfs_create_ancestors(libzfs_handle_t *hdl, const char *path)
2810185029Spjd{
2811185029Spjd	int prefix;
2812185029Spjd	char *path_copy;
2813185029Spjd	int rc;
2814185029Spjd
2815219089Spjd	if (check_parents(hdl, path, NULL, B_TRUE, &prefix) != 0)
2816185029Spjd		return (-1);
2817185029Spjd
2818185029Spjd	if ((path_copy = strdup(path)) != NULL) {
2819185029Spjd		rc = create_parents(hdl, path_copy, prefix);
2820185029Spjd		free(path_copy);
2821185029Spjd	}
2822185029Spjd	if (path_copy == NULL || rc != 0)
2823185029Spjd		return (-1);
2824185029Spjd
2825185029Spjd	return (0);
2826185029Spjd}
2827185029Spjd
2828185029Spjd/*
2829168404Spjd * Create a new filesystem or volume.
2830168404Spjd */
2831168404Spjdint
2832168404Spjdzfs_create(libzfs_handle_t *hdl, const char *path, zfs_type_t type,
2833168404Spjd    nvlist_t *props)
2834168404Spjd{
2835168404Spjd	zfs_cmd_t zc = { 0 };
2836168404Spjd	int ret;
2837168404Spjd	uint64_t size = 0;
2838168404Spjd	uint64_t blocksize = zfs_prop_default_numeric(ZFS_PROP_VOLBLOCKSIZE);
2839168404Spjd	char errbuf[1024];
2840168404Spjd	uint64_t zoned;
2841168404Spjd
2842168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
2843168404Spjd	    "cannot create '%s'"), path);
2844168404Spjd
2845168404Spjd	/* validate the path, taking care to note the extended error message */
2846185029Spjd	if (!zfs_validate_name(hdl, path, type, B_TRUE))
2847168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
2848168404Spjd
2849168404Spjd	/* validate parents exist */
2850185029Spjd	if (check_parents(hdl, path, &zoned, B_FALSE, NULL) != 0)
2851168404Spjd		return (-1);
2852168404Spjd
2853168404Spjd	/*
2854168404Spjd	 * The failure modes when creating a dataset of a different type over
2855168404Spjd	 * one that already exists is a little strange.  In particular, if you
2856168404Spjd	 * try to create a dataset on top of an existing dataset, the ioctl()
2857168404Spjd	 * will return ENOENT, not EEXIST.  To prevent this from happening, we
2858168404Spjd	 * first try to see if the dataset exists.
2859168404Spjd	 */
2860168404Spjd	(void) strlcpy(zc.zc_name, path, sizeof (zc.zc_name));
2861185029Spjd	if (zfs_dataset_exists(hdl, zc.zc_name, ZFS_TYPE_DATASET)) {
2862168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2863168404Spjd		    "dataset already exists"));
2864168404Spjd		return (zfs_error(hdl, EZFS_EXISTS, errbuf));
2865168404Spjd	}
2866168404Spjd
2867168404Spjd	if (type == ZFS_TYPE_VOLUME)
2868168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
2869168404Spjd	else
2870168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
2871168404Spjd
2872185029Spjd	if (props && (props = zfs_valid_proplist(hdl, type, props,
2873168404Spjd	    zoned, NULL, errbuf)) == 0)
2874168404Spjd		return (-1);
2875168404Spjd
2876168404Spjd	if (type == ZFS_TYPE_VOLUME) {
2877168404Spjd		/*
2878168404Spjd		 * If we are creating a volume, the size and block size must
2879168404Spjd		 * satisfy a few restraints.  First, the blocksize must be a
2880168404Spjd		 * valid block size between SPA_{MIN,MAX}BLOCKSIZE.  Second, the
2881168404Spjd		 * volsize must be a multiple of the block size, and cannot be
2882168404Spjd		 * zero.
2883168404Spjd		 */
2884168404Spjd		if (props == NULL || nvlist_lookup_uint64(props,
2885168404Spjd		    zfs_prop_to_name(ZFS_PROP_VOLSIZE), &size) != 0) {
2886168404Spjd			nvlist_free(props);
2887168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2888168404Spjd			    "missing volume size"));
2889168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2890168404Spjd		}
2891168404Spjd
2892168404Spjd		if ((ret = nvlist_lookup_uint64(props,
2893168404Spjd		    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
2894168404Spjd		    &blocksize)) != 0) {
2895168404Spjd			if (ret == ENOENT) {
2896168404Spjd				blocksize = zfs_prop_default_numeric(
2897168404Spjd				    ZFS_PROP_VOLBLOCKSIZE);
2898168404Spjd			} else {
2899168404Spjd				nvlist_free(props);
2900168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2901168404Spjd				    "missing volume block size"));
2902168404Spjd				return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2903168404Spjd			}
2904168404Spjd		}
2905168404Spjd
2906168404Spjd		if (size == 0) {
2907168404Spjd			nvlist_free(props);
2908168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2909168404Spjd			    "volume size cannot be zero"));
2910168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2911168404Spjd		}
2912168404Spjd
2913168404Spjd		if (size % blocksize != 0) {
2914168404Spjd			nvlist_free(props);
2915168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2916168404Spjd			    "volume size must be a multiple of volume block "
2917168404Spjd			    "size"));
2918168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2919168404Spjd		}
2920168404Spjd	}
2921168404Spjd
2922185029Spjd	if (props && zcmd_write_src_nvlist(hdl, &zc, props) != 0)
2923168404Spjd		return (-1);
2924168404Spjd	nvlist_free(props);
2925168404Spjd
2926168404Spjd	/* create the dataset */
2927185029Spjd	ret = zfs_ioctl(hdl, ZFS_IOC_CREATE, &zc);
2928168404Spjd
2929168404Spjd	zcmd_free_nvlists(&zc);
2930168404Spjd
2931168404Spjd	/* check for failure */
2932168404Spjd	if (ret != 0) {
2933168404Spjd		char parent[ZFS_MAXNAMELEN];
2934168404Spjd		(void) parent_name(path, parent, sizeof (parent));
2935168404Spjd
2936168404Spjd		switch (errno) {
2937168404Spjd		case ENOENT:
2938168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2939168404Spjd			    "no such parent '%s'"), parent);
2940168404Spjd			return (zfs_error(hdl, EZFS_NOENT, errbuf));
2941168404Spjd
2942168404Spjd		case EINVAL:
2943168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2944168404Spjd			    "parent '%s' is not a filesystem"), parent);
2945168404Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
2946168404Spjd
2947168404Spjd		case EDOM:
2948168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2949168404Spjd			    "volume block size must be power of 2 from "
2950168404Spjd			    "%u to %uk"),
2951168404Spjd			    (uint_t)SPA_MINBLOCKSIZE,
2952168404Spjd			    (uint_t)SPA_MAXBLOCKSIZE >> 10);
2953168404Spjd
2954168404Spjd			return (zfs_error(hdl, EZFS_BADPROP, errbuf));
2955168404Spjd
2956185029Spjd		case ENOTSUP:
2957185029Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
2958185029Spjd			    "pool must be upgraded to set this "
2959185029Spjd			    "property or value"));
2960185029Spjd			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
2961168404Spjd#ifdef _ILP32
2962168404Spjd		case EOVERFLOW:
2963168404Spjd			/*
2964168404Spjd			 * This platform can't address a volume this big.
2965168404Spjd			 */
2966168404Spjd			if (type == ZFS_TYPE_VOLUME)
2967168404Spjd				return (zfs_error(hdl, EZFS_VOLTOOBIG,
2968168404Spjd				    errbuf));
2969168404Spjd#endif
2970168404Spjd			/* FALLTHROUGH */
2971168404Spjd		default:
2972168404Spjd			return (zfs_standard_error(hdl, errno, errbuf));
2973168404Spjd		}
2974168404Spjd	}
2975168404Spjd
2976168404Spjd	return (0);
2977168404Spjd}
2978168404Spjd
2979168404Spjd/*
2980168404Spjd * Destroys the given dataset.  The caller must make sure that the filesystem
2981168404Spjd * isn't mounted, and that there are no active dependents.
2982168404Spjd */
2983168404Spjdint
2984219089Spjdzfs_destroy(zfs_handle_t *zhp, boolean_t defer)
2985168404Spjd{
2986168404Spjd	zfs_cmd_t zc = { 0 };
2987168404Spjd
2988168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
2989168404Spjd
2990168404Spjd	if (ZFS_IS_VOLUME(zhp)) {
2991168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
2992168404Spjd	} else {
2993168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
2994168404Spjd	}
2995168404Spjd
2996219089Spjd	zc.zc_defer_destroy = defer;
2997185029Spjd	if (zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY, &zc) != 0) {
2998168404Spjd		return (zfs_standard_error_fmt(zhp->zfs_hdl, errno,
2999168404Spjd		    dgettext(TEXT_DOMAIN, "cannot destroy '%s'"),
3000168404Spjd		    zhp->zfs_name));
3001168404Spjd	}
3002168404Spjd
3003168404Spjd	remove_mountpoint(zhp);
3004168404Spjd
3005168404Spjd	return (0);
3006168404Spjd}
3007168404Spjd
3008168404Spjdstruct destroydata {
3009168404Spjd	char *snapname;
3010168404Spjd	boolean_t gotone;
3011168404Spjd	boolean_t closezhp;
3012168404Spjd};
3013168404Spjd
3014168404Spjdstatic int
3015219089Spjdzfs_check_snap_cb(zfs_handle_t *zhp, void *arg)
3016168404Spjd{
3017168404Spjd	struct destroydata *dd = arg;
3018168404Spjd	zfs_handle_t *szhp;
3019168404Spjd	char name[ZFS_MAXNAMELEN];
3020168404Spjd	boolean_t closezhp = dd->closezhp;
3021219089Spjd	int rv = 0;
3022168404Spjd
3023168404Spjd	(void) strlcpy(name, zhp->zfs_name, sizeof (name));
3024168404Spjd	(void) strlcat(name, "@", sizeof (name));
3025168404Spjd	(void) strlcat(name, dd->snapname, sizeof (name));
3026168404Spjd
3027168404Spjd	szhp = make_dataset_handle(zhp->zfs_hdl, name);
3028168404Spjd	if (szhp) {
3029168404Spjd		dd->gotone = B_TRUE;
3030168404Spjd		zfs_close(szhp);
3031168404Spjd	}
3032168404Spjd
3033168404Spjd	dd->closezhp = B_TRUE;
3034219089Spjd	if (!dd->gotone)
3035219089Spjd		rv = zfs_iter_filesystems(zhp, zfs_check_snap_cb, arg);
3036168404Spjd	if (closezhp)
3037168404Spjd		zfs_close(zhp);
3038168404Spjd	return (rv);
3039168404Spjd}
3040168404Spjd
3041168404Spjd/*
3042168404Spjd * Destroys all snapshots with the given name in zhp & descendants.
3043168404Spjd */
3044168404Spjdint
3045219089Spjdzfs_destroy_snaps(zfs_handle_t *zhp, char *snapname, boolean_t defer)
3046168404Spjd{
3047168404Spjd	zfs_cmd_t zc = { 0 };
3048168404Spjd	int ret;
3049168404Spjd	struct destroydata dd = { 0 };
3050168404Spjd
3051168404Spjd	dd.snapname = snapname;
3052219089Spjd	(void) zfs_check_snap_cb(zhp, &dd);
3053168404Spjd
3054168404Spjd	if (!dd.gotone) {
3055168404Spjd		return (zfs_standard_error_fmt(zhp->zfs_hdl, ENOENT,
3056168404Spjd		    dgettext(TEXT_DOMAIN, "cannot destroy '%s@%s'"),
3057168404Spjd		    zhp->zfs_name, snapname));
3058168404Spjd	}
3059168404Spjd
3060168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3061168404Spjd	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
3062219089Spjd	zc.zc_defer_destroy = defer;
3063168404Spjd
3064185029Spjd	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_DESTROY_SNAPS, &zc);
3065168404Spjd	if (ret != 0) {
3066168404Spjd		char errbuf[1024];
3067168404Spjd
3068168404Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3069168404Spjd		    "cannot destroy '%s@%s'"), zc.zc_name, snapname);
3070168404Spjd
3071168404Spjd		switch (errno) {
3072168404Spjd		case EEXIST:
3073168404Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3074168404Spjd			    "snapshot is cloned"));
3075168404Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_EXISTS, errbuf));
3076168404Spjd
3077168404Spjd		default:
3078168404Spjd			return (zfs_standard_error(zhp->zfs_hdl, errno,
3079168404Spjd			    errbuf));
3080168404Spjd		}
3081168404Spjd	}
3082168404Spjd
3083168404Spjd	return (0);
3084168404Spjd}
3085168404Spjd
3086168404Spjd/*
3087168404Spjd * Clones the given dataset.  The target must be of the same type as the source.
3088168404Spjd */
3089168404Spjdint
3090168404Spjdzfs_clone(zfs_handle_t *zhp, const char *target, nvlist_t *props)
3091168404Spjd{
3092168404Spjd	zfs_cmd_t zc = { 0 };
3093168404Spjd	char parent[ZFS_MAXNAMELEN];
3094168404Spjd	int ret;
3095168404Spjd	char errbuf[1024];
3096168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3097168404Spjd	zfs_type_t type;
3098168404Spjd	uint64_t zoned;
3099168404Spjd
3100168404Spjd	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
3101168404Spjd
3102168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3103168404Spjd	    "cannot create '%s'"), target);
3104168404Spjd
3105168404Spjd	/* validate the target name */
3106185029Spjd	if (!zfs_validate_name(hdl, target, ZFS_TYPE_FILESYSTEM, B_TRUE))
3107168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3108168404Spjd
3109168404Spjd	/* validate parents exist */
3110185029Spjd	if (check_parents(hdl, target, &zoned, B_FALSE, NULL) != 0)
3111168404Spjd		return (-1);
3112168404Spjd
3113168404Spjd	(void) parent_name(target, parent, sizeof (parent));
3114168404Spjd
3115168404Spjd	/* do the clone */
3116168404Spjd	if (ZFS_IS_VOLUME(zhp)) {
3117168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3118168404Spjd		type = ZFS_TYPE_VOLUME;
3119168404Spjd	} else {
3120168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3121168404Spjd		type = ZFS_TYPE_FILESYSTEM;
3122168404Spjd	}
3123168404Spjd
3124168404Spjd	if (props) {
3125185029Spjd		if ((props = zfs_valid_proplist(hdl, type, props, zoned,
3126185029Spjd		    zhp, errbuf)) == NULL)
3127168404Spjd			return (-1);
3128168404Spjd
3129185029Spjd		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3130168404Spjd			nvlist_free(props);
3131168404Spjd			return (-1);
3132168404Spjd		}
3133168404Spjd
3134168404Spjd		nvlist_free(props);
3135168404Spjd	}
3136168404Spjd
3137168404Spjd	(void) strlcpy(zc.zc_name, target, sizeof (zc.zc_name));
3138168404Spjd	(void) strlcpy(zc.zc_value, zhp->zfs_name, sizeof (zc.zc_value));
3139185029Spjd	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_CREATE, &zc);
3140168404Spjd
3141168404Spjd	zcmd_free_nvlists(&zc);
3142168404Spjd
3143168404Spjd	if (ret != 0) {
3144168404Spjd		switch (errno) {
3145168404Spjd
3146168404Spjd		case ENOENT:
3147168404Spjd			/*
3148168404Spjd			 * The parent doesn't exist.  We should have caught this
3149168404Spjd			 * above, but there may a race condition that has since
3150168404Spjd			 * destroyed the parent.
3151168404Spjd			 *
3152168404Spjd			 * At this point, we don't know whether it's the source
3153168404Spjd			 * that doesn't exist anymore, or whether the target
3154168404Spjd			 * dataset doesn't exist.
3155168404Spjd			 */
3156168404Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3157168404Spjd			    "no such parent '%s'"), parent);
3158168404Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_NOENT, errbuf));
3159168404Spjd
3160168404Spjd		case EXDEV:
3161168404Spjd			zfs_error_aux(zhp->zfs_hdl, dgettext(TEXT_DOMAIN,
3162168404Spjd			    "source and target pools differ"));
3163168404Spjd			return (zfs_error(zhp->zfs_hdl, EZFS_CROSSTARGET,
3164168404Spjd			    errbuf));
3165168404Spjd
3166168404Spjd		default:
3167168404Spjd			return (zfs_standard_error(zhp->zfs_hdl, errno,
3168168404Spjd			    errbuf));
3169168404Spjd		}
3170168404Spjd	}
3171168404Spjd
3172168404Spjd	return (ret);
3173168404Spjd}
3174168404Spjd
3175168404Spjd/*
3176168404Spjd * Promotes the given clone fs to be the clone parent.
3177168404Spjd */
3178168404Spjdint
3179168404Spjdzfs_promote(zfs_handle_t *zhp)
3180168404Spjd{
3181168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3182168404Spjd	zfs_cmd_t zc = { 0 };
3183168404Spjd	char parent[MAXPATHLEN];
3184168404Spjd	int ret;
3185168404Spjd	char errbuf[1024];
3186168404Spjd
3187168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3188168404Spjd	    "cannot promote '%s'"), zhp->zfs_name);
3189168404Spjd
3190168404Spjd	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
3191168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3192168404Spjd		    "snapshots can not be promoted"));
3193168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3194168404Spjd	}
3195168404Spjd
3196185029Spjd	(void) strlcpy(parent, zhp->zfs_dmustats.dds_origin, sizeof (parent));
3197168404Spjd	if (parent[0] == '\0') {
3198168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3199168404Spjd		    "not a cloned filesystem"));
3200168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3201168404Spjd	}
3202168404Spjd
3203185029Spjd	(void) strlcpy(zc.zc_value, zhp->zfs_dmustats.dds_origin,
3204168404Spjd	    sizeof (zc.zc_value));
3205168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3206185029Spjd	ret = zfs_ioctl(hdl, ZFS_IOC_PROMOTE, &zc);
3207168404Spjd
3208168404Spjd	if (ret != 0) {
3209168404Spjd		int save_errno = errno;
3210168404Spjd
3211168404Spjd		switch (save_errno) {
3212168404Spjd		case EEXIST:
3213219089Spjd			/* There is a conflicting snapshot name. */
3214168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3215219089Spjd			    "conflicting snapshot '%s' from parent '%s'"),
3216219089Spjd			    zc.zc_string, parent);
3217168404Spjd			return (zfs_error(hdl, EZFS_EXISTS, errbuf));
3218168404Spjd
3219168404Spjd		default:
3220168404Spjd			return (zfs_standard_error(hdl, save_errno, errbuf));
3221168404Spjd		}
3222168404Spjd	}
3223168404Spjd	return (ret);
3224168404Spjd}
3225168404Spjd
3226168404Spjd/*
3227168404Spjd * Takes a snapshot of the given dataset.
3228168404Spjd */
3229168404Spjdint
3230185029Spjdzfs_snapshot(libzfs_handle_t *hdl, const char *path, boolean_t recursive,
3231185029Spjd    nvlist_t *props)
3232168404Spjd{
3233168404Spjd	const char *delim;
3234185029Spjd	char parent[ZFS_MAXNAMELEN];
3235168404Spjd	zfs_handle_t *zhp;
3236168404Spjd	zfs_cmd_t zc = { 0 };
3237168404Spjd	int ret;
3238168404Spjd	char errbuf[1024];
3239168404Spjd
3240168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3241168404Spjd	    "cannot snapshot '%s'"), path);
3242168404Spjd
3243168404Spjd	/* validate the target name */
3244185029Spjd	if (!zfs_validate_name(hdl, path, ZFS_TYPE_SNAPSHOT, B_TRUE))
3245168404Spjd		return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3246168404Spjd
3247185029Spjd	if (props) {
3248185029Spjd		if ((props = zfs_valid_proplist(hdl, ZFS_TYPE_SNAPSHOT,
3249185029Spjd		    props, B_FALSE, NULL, errbuf)) == NULL)
3250185029Spjd			return (-1);
3251185029Spjd
3252185029Spjd		if (zcmd_write_src_nvlist(hdl, &zc, props) != 0) {
3253185029Spjd			nvlist_free(props);
3254185029Spjd			return (-1);
3255185029Spjd		}
3256185029Spjd
3257185029Spjd		nvlist_free(props);
3258185029Spjd	}
3259185029Spjd
3260168404Spjd	/* make sure the parent exists and is of the appropriate type */
3261168404Spjd	delim = strchr(path, '@');
3262168404Spjd	(void) strncpy(parent, path, delim - path);
3263168404Spjd	parent[delim - path] = '\0';
3264168404Spjd
3265168404Spjd	if ((zhp = zfs_open(hdl, parent, ZFS_TYPE_FILESYSTEM |
3266168404Spjd	    ZFS_TYPE_VOLUME)) == NULL) {
3267185029Spjd		zcmd_free_nvlists(&zc);
3268168404Spjd		return (-1);
3269168404Spjd	}
3270168404Spjd
3271168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3272168404Spjd	(void) strlcpy(zc.zc_value, delim+1, sizeof (zc.zc_value));
3273185029Spjd	if (ZFS_IS_VOLUME(zhp))
3274185029Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3275185029Spjd	else
3276185029Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3277168404Spjd	zc.zc_cookie = recursive;
3278185029Spjd	ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_SNAPSHOT, &zc);
3279168404Spjd
3280185029Spjd	zcmd_free_nvlists(&zc);
3281185029Spjd
3282168404Spjd	/*
3283168404Spjd	 * if it was recursive, the one that actually failed will be in
3284168404Spjd	 * zc.zc_name.
3285168404Spjd	 */
3286219089Spjd	if (ret != 0) {
3287185029Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3288185029Spjd		    "cannot create snapshot '%s@%s'"), zc.zc_name, zc.zc_value);
3289219089Spjd		(void) zfs_standard_error(hdl, errno, errbuf);
3290168404Spjd	}
3291168404Spjd
3292168404Spjd	zfs_close(zhp);
3293168404Spjd
3294168404Spjd	return (ret);
3295168404Spjd}
3296168404Spjd
3297168404Spjd/*
3298168404Spjd * Destroy any more recent snapshots.  We invoke this callback on any dependents
3299168404Spjd * of the snapshot first.  If the 'cb_dependent' member is non-zero, then this
3300168404Spjd * is a dependent and we should just destroy it without checking the transaction
3301168404Spjd * group.
3302168404Spjd */
3303168404Spjdtypedef struct rollback_data {
3304168404Spjd	const char	*cb_target;		/* the snapshot */
3305168404Spjd	uint64_t	cb_create;		/* creation time reference */
3306185029Spjd	boolean_t	cb_error;
3307168404Spjd	boolean_t	cb_dependent;
3308185029Spjd	boolean_t	cb_force;
3309168404Spjd} rollback_data_t;
3310168404Spjd
3311168404Spjdstatic int
3312168404Spjdrollback_destroy(zfs_handle_t *zhp, void *data)
3313168404Spjd{
3314168404Spjd	rollback_data_t *cbp = data;
3315168404Spjd
3316168404Spjd	if (!cbp->cb_dependent) {
3317168404Spjd		if (strcmp(zhp->zfs_name, cbp->cb_target) != 0 &&
3318168404Spjd		    zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT &&
3319168404Spjd		    zfs_prop_get_int(zhp, ZFS_PROP_CREATETXG) >
3320168404Spjd		    cbp->cb_create) {
3321185029Spjd			char *logstr;
3322168404Spjd
3323168404Spjd			cbp->cb_dependent = B_TRUE;
3324185029Spjd			cbp->cb_error |= zfs_iter_dependents(zhp, B_FALSE,
3325185029Spjd			    rollback_destroy, cbp);
3326168404Spjd			cbp->cb_dependent = B_FALSE;
3327168404Spjd
3328185029Spjd			logstr = zhp->zfs_hdl->libzfs_log_str;
3329185029Spjd			zhp->zfs_hdl->libzfs_log_str = NULL;
3330219089Spjd			cbp->cb_error |= zfs_destroy(zhp, B_FALSE);
3331185029Spjd			zhp->zfs_hdl->libzfs_log_str = logstr;
3332168404Spjd		}
3333168404Spjd	} else {
3334185029Spjd		/* We must destroy this clone; first unmount it */
3335185029Spjd		prop_changelist_t *clp;
3336185029Spjd
3337185029Spjd		clp = changelist_gather(zhp, ZFS_PROP_NAME, 0,
3338185029Spjd		    cbp->cb_force ? MS_FORCE: 0);
3339185029Spjd		if (clp == NULL || changelist_prefix(clp) != 0) {
3340185029Spjd			cbp->cb_error = B_TRUE;
3341185029Spjd			zfs_close(zhp);
3342185029Spjd			return (0);
3343185029Spjd		}
3344219089Spjd		if (zfs_destroy(zhp, B_FALSE) != 0)
3345185029Spjd			cbp->cb_error = B_TRUE;
3346168404Spjd		else
3347185029Spjd			changelist_remove(clp, zhp->zfs_name);
3348185029Spjd		(void) changelist_postfix(clp);
3349185029Spjd		changelist_free(clp);
3350168404Spjd	}
3351168404Spjd
3352168404Spjd	zfs_close(zhp);
3353168404Spjd	return (0);
3354168404Spjd}
3355168404Spjd
3356168404Spjd/*
3357168404Spjd * Given a dataset, rollback to a specific snapshot, discarding any
3358168404Spjd * data changes since then and making it the active dataset.
3359168404Spjd *
3360168404Spjd * Any snapshots more recent than the target are destroyed, along with
3361168404Spjd * their dependents.
3362168404Spjd */
3363168404Spjdint
3364185029Spjdzfs_rollback(zfs_handle_t *zhp, zfs_handle_t *snap, boolean_t force)
3365168404Spjd{
3366168404Spjd	rollback_data_t cb = { 0 };
3367185029Spjd	int err;
3368185029Spjd	zfs_cmd_t zc = { 0 };
3369185029Spjd	boolean_t restore_resv = 0;
3370185029Spjd	uint64_t old_volsize, new_volsize;
3371185029Spjd	zfs_prop_t resv_prop;
3372168404Spjd
3373185029Spjd	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM ||
3374185029Spjd	    zhp->zfs_type == ZFS_TYPE_VOLUME);
3375168404Spjd
3376168404Spjd	/*
3377168404Spjd	 * Destroy all recent snapshots and its dependends.
3378168404Spjd	 */
3379185029Spjd	cb.cb_force = force;
3380168404Spjd	cb.cb_target = snap->zfs_name;
3381168404Spjd	cb.cb_create = zfs_prop_get_int(snap, ZFS_PROP_CREATETXG);
3382168404Spjd	(void) zfs_iter_children(zhp, rollback_destroy, &cb);
3383168404Spjd
3384185029Spjd	if (cb.cb_error)
3385185029Spjd		return (-1);
3386168404Spjd
3387168404Spjd	/*
3388168404Spjd	 * Now that we have verified that the snapshot is the latest,
3389168404Spjd	 * rollback to the given snapshot.
3390168404Spjd	 */
3391168404Spjd
3392185029Spjd	if (zhp->zfs_type == ZFS_TYPE_VOLUME) {
3393185029Spjd		if (zfs_which_resv_prop(zhp, &resv_prop) < 0)
3394185029Spjd			return (-1);
3395185029Spjd		old_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
3396185029Spjd		restore_resv =
3397185029Spjd		    (old_volsize == zfs_prop_get_int(zhp, resv_prop));
3398168404Spjd	}
3399168404Spjd
3400185029Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3401185029Spjd
3402185029Spjd	if (ZFS_IS_VOLUME(zhp))
3403185029Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3404185029Spjd	else
3405185029Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3406185029Spjd
3407168404Spjd	/*
3408185029Spjd	 * We rely on zfs_iter_children() to verify that there are no
3409185029Spjd	 * newer snapshots for the given dataset.  Therefore, we can
3410185029Spjd	 * simply pass the name on to the ioctl() call.  There is still
3411185029Spjd	 * an unlikely race condition where the user has taken a
3412185029Spjd	 * snapshot since we verified that this was the most recent.
3413185029Spjd	 *
3414168404Spjd	 */
3415185029Spjd	if ((err = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_ROLLBACK, &zc)) != 0) {
3416185029Spjd		(void) zfs_standard_error_fmt(zhp->zfs_hdl, errno,
3417185029Spjd		    dgettext(TEXT_DOMAIN, "cannot rollback '%s'"),
3418185029Spjd		    zhp->zfs_name);
3419185029Spjd		return (err);
3420185029Spjd	}
3421168404Spjd
3422185029Spjd	/*
3423185029Spjd	 * For volumes, if the pre-rollback volsize matched the pre-
3424185029Spjd	 * rollback reservation and the volsize has changed then set
3425185029Spjd	 * the reservation property to the post-rollback volsize.
3426185029Spjd	 * Make a new handle since the rollback closed the dataset.
3427185029Spjd	 */
3428185029Spjd	if ((zhp->zfs_type == ZFS_TYPE_VOLUME) &&
3429185029Spjd	    (zhp = make_dataset_handle(zhp->zfs_hdl, zhp->zfs_name))) {
3430185029Spjd		if (restore_resv) {
3431185029Spjd			new_volsize = zfs_prop_get_int(zhp, ZFS_PROP_VOLSIZE);
3432185029Spjd			if (old_volsize != new_volsize)
3433185029Spjd				err = zfs_prop_set_int(zhp, resv_prop,
3434185029Spjd				    new_volsize);
3435185029Spjd		}
3436185029Spjd		zfs_close(zhp);
3437185029Spjd	}
3438185029Spjd	return (err);
3439168404Spjd}
3440168404Spjd
3441168404Spjd/*
3442168404Spjd * Iterate over all dependents for a given dataset.  This includes both
3443168404Spjd * hierarchical dependents (children) and data dependents (snapshots and
3444168404Spjd * clones).  The bulk of the processing occurs in get_dependents() in
3445168404Spjd * libzfs_graph.c.
3446168404Spjd */
3447168404Spjdint
3448168404Spjdzfs_iter_dependents(zfs_handle_t *zhp, boolean_t allowrecursion,
3449168404Spjd    zfs_iter_f func, void *data)
3450168404Spjd{
3451168404Spjd	char **dependents;
3452168404Spjd	size_t count;
3453168404Spjd	int i;
3454168404Spjd	zfs_handle_t *child;
3455168404Spjd	int ret = 0;
3456168404Spjd
3457168404Spjd	if (get_dependents(zhp->zfs_hdl, allowrecursion, zhp->zfs_name,
3458168404Spjd	    &dependents, &count) != 0)
3459168404Spjd		return (-1);
3460168404Spjd
3461168404Spjd	for (i = 0; i < count; i++) {
3462168404Spjd		if ((child = make_dataset_handle(zhp->zfs_hdl,
3463168404Spjd		    dependents[i])) == NULL)
3464168404Spjd			continue;
3465168404Spjd
3466168404Spjd		if ((ret = func(child, data)) != 0)
3467168404Spjd			break;
3468168404Spjd	}
3469168404Spjd
3470168404Spjd	for (i = 0; i < count; i++)
3471168404Spjd		free(dependents[i]);
3472168404Spjd	free(dependents);
3473168404Spjd
3474168404Spjd	return (ret);
3475168404Spjd}
3476168404Spjd
3477168404Spjd/*
3478168404Spjd * Renames the given dataset.
3479168404Spjd */
3480168404Spjdint
3481185029Spjdzfs_rename(zfs_handle_t *zhp, const char *target, boolean_t recursive)
3482168404Spjd{
3483168404Spjd	int ret;
3484168404Spjd	zfs_cmd_t zc = { 0 };
3485168404Spjd	char *delim;
3486168676Spjd	prop_changelist_t *cl = NULL;
3487168676Spjd	zfs_handle_t *zhrp = NULL;
3488168676Spjd	char *parentname = NULL;
3489168404Spjd	char parent[ZFS_MAXNAMELEN];
3490168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3491168404Spjd	char errbuf[1024];
3492168404Spjd
3493168404Spjd	/* if we have the same exact name, just return success */
3494168404Spjd	if (strcmp(zhp->zfs_name, target) == 0)
3495168404Spjd		return (0);
3496168404Spjd
3497168404Spjd	(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3498168404Spjd	    "cannot rename to '%s'"), target);
3499168404Spjd
3500168404Spjd	/*
3501168404Spjd	 * Make sure the target name is valid
3502168404Spjd	 */
3503168404Spjd	if (zhp->zfs_type == ZFS_TYPE_SNAPSHOT) {
3504168404Spjd		if ((strchr(target, '@') == NULL) ||
3505168404Spjd		    *target == '@') {
3506168404Spjd			/*
3507168404Spjd			 * Snapshot target name is abbreviated,
3508168404Spjd			 * reconstruct full dataset name
3509168404Spjd			 */
3510168404Spjd			(void) strlcpy(parent, zhp->zfs_name,
3511168404Spjd			    sizeof (parent));
3512168404Spjd			delim = strchr(parent, '@');
3513168404Spjd			if (strchr(target, '@') == NULL)
3514168404Spjd				*(++delim) = '\0';
3515168404Spjd			else
3516168404Spjd				*delim = '\0';
3517168404Spjd			(void) strlcat(parent, target, sizeof (parent));
3518168404Spjd			target = parent;
3519168404Spjd		} else {
3520168404Spjd			/*
3521168404Spjd			 * Make sure we're renaming within the same dataset.
3522168404Spjd			 */
3523168404Spjd			delim = strchr(target, '@');
3524168404Spjd			if (strncmp(zhp->zfs_name, target, delim - target)
3525168404Spjd			    != 0 || zhp->zfs_name[delim - target] != '@') {
3526168404Spjd				zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3527168404Spjd				    "snapshots must be part of same "
3528168404Spjd				    "dataset"));
3529168404Spjd				return (zfs_error(hdl, EZFS_CROSSTARGET,
3530168404Spjd				    errbuf));
3531168404Spjd			}
3532168404Spjd		}
3533185029Spjd		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
3534168404Spjd			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3535168404Spjd	} else {
3536168676Spjd		if (recursive) {
3537168676Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3538168676Spjd			    "recursive rename must be a snapshot"));
3539168676Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
3540168676Spjd		}
3541168676Spjd
3542185029Spjd		if (!zfs_validate_name(hdl, target, zhp->zfs_type, B_TRUE))
3543168404Spjd			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3544168404Spjd
3545168404Spjd		/* validate parents */
3546219089Spjd		if (check_parents(hdl, target, NULL, B_FALSE, NULL) != 0)
3547168404Spjd			return (-1);
3548168404Spjd
3549168404Spjd		/* make sure we're in the same pool */
3550168404Spjd		verify((delim = strchr(target, '/')) != NULL);
3551168404Spjd		if (strncmp(zhp->zfs_name, target, delim - target) != 0 ||
3552168404Spjd		    zhp->zfs_name[delim - target] != '/') {
3553168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3554168404Spjd			    "datasets must be within same pool"));
3555168404Spjd			return (zfs_error(hdl, EZFS_CROSSTARGET, errbuf));
3556168404Spjd		}
3557168404Spjd
3558168404Spjd		/* new name cannot be a child of the current dataset name */
3559219089Spjd		if (is_descendant(zhp->zfs_name, target)) {
3560168404Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3561219089Spjd			    "New dataset name cannot be a descendant of "
3562168404Spjd			    "current dataset name"));
3563168404Spjd			return (zfs_error(hdl, EZFS_INVALIDNAME, errbuf));
3564168404Spjd		}
3565168404Spjd	}
3566168404Spjd
3567168404Spjd	(void) snprintf(errbuf, sizeof (errbuf),
3568168404Spjd	    dgettext(TEXT_DOMAIN, "cannot rename '%s'"), zhp->zfs_name);
3569168404Spjd
3570168404Spjd	if (getzoneid() == GLOBAL_ZONEID &&
3571168404Spjd	    zfs_prop_get_int(zhp, ZFS_PROP_ZONED)) {
3572168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3573168404Spjd		    "dataset is used in a non-global zone"));
3574168404Spjd		return (zfs_error(hdl, EZFS_ZONED, errbuf));
3575168404Spjd	}
3576168404Spjd
3577168676Spjd	if (recursive) {
3578168404Spjd
3579185029Spjd		parentname = zfs_strdup(zhp->zfs_hdl, zhp->zfs_name);
3580185029Spjd		if (parentname == NULL) {
3581185029Spjd			ret = -1;
3582185029Spjd			goto error;
3583185029Spjd		}
3584168676Spjd		delim = strchr(parentname, '@');
3585168676Spjd		*delim = '\0';
3586185029Spjd		zhrp = zfs_open(zhp->zfs_hdl, parentname, ZFS_TYPE_DATASET);
3587168676Spjd		if (zhrp == NULL) {
3588185029Spjd			ret = -1;
3589185029Spjd			goto error;
3590168676Spjd		}
3591168676Spjd
3592168676Spjd	} else {
3593185029Spjd		if ((cl = changelist_gather(zhp, ZFS_PROP_NAME, 0, 0)) == NULL)
3594168676Spjd			return (-1);
3595168676Spjd
3596168676Spjd		if (changelist_haszonedchild(cl)) {
3597168676Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3598168676Spjd			    "child dataset with inherited mountpoint is used "
3599168676Spjd			    "in a non-global zone"));
3600168676Spjd			(void) zfs_error(hdl, EZFS_ZONED, errbuf);
3601168676Spjd			goto error;
3602168676Spjd		}
3603168676Spjd
3604168676Spjd		if ((ret = changelist_prefix(cl)) != 0)
3605168676Spjd			goto error;
3606168404Spjd	}
3607168404Spjd
3608168404Spjd	if (ZFS_IS_VOLUME(zhp))
3609168404Spjd		zc.zc_objset_type = DMU_OST_ZVOL;
3610168404Spjd	else
3611168404Spjd		zc.zc_objset_type = DMU_OST_ZFS;
3612168404Spjd
3613168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3614168404Spjd	(void) strlcpy(zc.zc_value, target, sizeof (zc.zc_value));
3615168404Spjd
3616168676Spjd	zc.zc_cookie = recursive;
3617168676Spjd
3618185029Spjd	if ((ret = zfs_ioctl(zhp->zfs_hdl, ZFS_IOC_RENAME, &zc)) != 0) {
3619168676Spjd		/*
3620168676Spjd		 * if it was recursive, the one that actually failed will
3621168676Spjd		 * be in zc.zc_name
3622168676Spjd		 */
3623168676Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3624185029Spjd		    "cannot rename '%s'"), zc.zc_name);
3625168404Spjd
3626168676Spjd		if (recursive && errno == EEXIST) {
3627168676Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3628168676Spjd			    "a child dataset already has a snapshot "
3629168676Spjd			    "with the new name"));
3630185029Spjd			(void) zfs_error(hdl, EZFS_EXISTS, errbuf);
3631168676Spjd		} else {
3632168676Spjd			(void) zfs_standard_error(zhp->zfs_hdl, errno, errbuf);
3633168676Spjd		}
3634168676Spjd
3635168404Spjd		/*
3636168404Spjd		 * On failure, we still want to remount any filesystems that
3637168404Spjd		 * were previously mounted, so we don't alter the system state.
3638168404Spjd		 */
3639219089Spjd		if (!recursive)
3640168676Spjd			(void) changelist_postfix(cl);
3641168404Spjd	} else {
3642219089Spjd		if (!recursive) {
3643168676Spjd			changelist_rename(cl, zfs_get_name(zhp), target);
3644168676Spjd			ret = changelist_postfix(cl);
3645168676Spjd		}
3646168404Spjd	}
3647168404Spjd
3648168404Spjderror:
3649168676Spjd	if (parentname) {
3650168676Spjd		free(parentname);
3651168676Spjd	}
3652168676Spjd	if (zhrp) {
3653168676Spjd		zfs_close(zhrp);
3654168676Spjd	}
3655168676Spjd	if (cl) {
3656168676Spjd		changelist_free(cl);
3657168676Spjd	}
3658168404Spjd	return (ret);
3659168404Spjd}
3660168404Spjd
3661219089Spjdnvlist_t *
3662219089Spjdzfs_get_user_props(zfs_handle_t *zhp)
3663168404Spjd{
3664219089Spjd	return (zhp->zfs_user_props);
3665168676Spjd}
3666168676Spjd
3667168404Spjdnvlist_t *
3668219089Spjdzfs_get_recvd_props(zfs_handle_t *zhp)
3669168404Spjd{
3670219089Spjd	if (zhp->zfs_recvd_props == NULL)
3671219089Spjd		if (get_recvd_props_ioctl(zhp) != 0)
3672219089Spjd			return (NULL);
3673219089Spjd	return (zhp->zfs_recvd_props);
3674168404Spjd}
3675168404Spjd
3676168404Spjd/*
3677168404Spjd * This function is used by 'zfs list' to determine the exact set of columns to
3678168404Spjd * display, and their maximum widths.  This does two main things:
3679168404Spjd *
3680168404Spjd *      - If this is a list of all properties, then expand the list to include
3681168404Spjd *        all native properties, and set a flag so that for each dataset we look
3682168404Spjd *        for new unique user properties and add them to the list.
3683168404Spjd *
3684168404Spjd *      - For non fixed-width properties, keep track of the maximum width seen
3685219089Spjd *        so that we can size the column appropriately. If the user has
3686219089Spjd *        requested received property values, we also need to compute the width
3687219089Spjd *        of the RECEIVED column.
3688168404Spjd */
3689168404Spjdint
3690219089Spjdzfs_expand_proplist(zfs_handle_t *zhp, zprop_list_t **plp, boolean_t received)
3691168404Spjd{
3692168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3693185029Spjd	zprop_list_t *entry;
3694185029Spjd	zprop_list_t **last, **start;
3695168404Spjd	nvlist_t *userprops, *propval;
3696168404Spjd	nvpair_t *elem;
3697168404Spjd	char *strval;
3698168404Spjd	char buf[ZFS_MAXPROPLEN];
3699168404Spjd
3700185029Spjd	if (zprop_expand_list(hdl, plp, ZFS_TYPE_DATASET) != 0)
3701168404Spjd		return (-1);
3702168404Spjd
3703168404Spjd	userprops = zfs_get_user_props(zhp);
3704168404Spjd
3705168404Spjd	entry = *plp;
3706168404Spjd	if (entry->pl_all && nvlist_next_nvpair(userprops, NULL) != NULL) {
3707168404Spjd		/*
3708168404Spjd		 * Go through and add any user properties as necessary.  We
3709168404Spjd		 * start by incrementing our list pointer to the first
3710168404Spjd		 * non-native property.
3711168404Spjd		 */
3712168404Spjd		start = plp;
3713168404Spjd		while (*start != NULL) {
3714185029Spjd			if ((*start)->pl_prop == ZPROP_INVAL)
3715168404Spjd				break;
3716168404Spjd			start = &(*start)->pl_next;
3717168404Spjd		}
3718168404Spjd
3719168404Spjd		elem = NULL;
3720168404Spjd		while ((elem = nvlist_next_nvpair(userprops, elem)) != NULL) {
3721168404Spjd			/*
3722168404Spjd			 * See if we've already found this property in our list.
3723168404Spjd			 */
3724168404Spjd			for (last = start; *last != NULL;
3725168404Spjd			    last = &(*last)->pl_next) {
3726168404Spjd				if (strcmp((*last)->pl_user_prop,
3727168404Spjd				    nvpair_name(elem)) == 0)
3728168404Spjd					break;
3729168404Spjd			}
3730168404Spjd
3731168404Spjd			if (*last == NULL) {
3732168404Spjd				if ((entry = zfs_alloc(hdl,
3733185029Spjd				    sizeof (zprop_list_t))) == NULL ||
3734168404Spjd				    ((entry->pl_user_prop = zfs_strdup(hdl,
3735168404Spjd				    nvpair_name(elem)))) == NULL) {
3736168404Spjd					free(entry);
3737168404Spjd					return (-1);
3738168404Spjd				}
3739168404Spjd
3740185029Spjd				entry->pl_prop = ZPROP_INVAL;
3741168404Spjd				entry->pl_width = strlen(nvpair_name(elem));
3742168404Spjd				entry->pl_all = B_TRUE;
3743168404Spjd				*last = entry;
3744168404Spjd			}
3745168404Spjd		}
3746168404Spjd	}
3747168404Spjd
3748168404Spjd	/*
3749168404Spjd	 * Now go through and check the width of any non-fixed columns
3750168404Spjd	 */
3751168404Spjd	for (entry = *plp; entry != NULL; entry = entry->pl_next) {
3752168404Spjd		if (entry->pl_fixed)
3753168404Spjd			continue;
3754168404Spjd
3755185029Spjd		if (entry->pl_prop != ZPROP_INVAL) {
3756168404Spjd			if (zfs_prop_get(zhp, entry->pl_prop,
3757168404Spjd			    buf, sizeof (buf), NULL, NULL, 0, B_FALSE) == 0) {
3758168404Spjd				if (strlen(buf) > entry->pl_width)
3759168404Spjd					entry->pl_width = strlen(buf);
3760168404Spjd			}
3761219089Spjd			if (received && zfs_prop_get_recvd(zhp,
3762219089Spjd			    zfs_prop_to_name(entry->pl_prop),
3763219089Spjd			    buf, sizeof (buf), B_FALSE) == 0)
3764219089Spjd				if (strlen(buf) > entry->pl_recvd_width)
3765219089Spjd					entry->pl_recvd_width = strlen(buf);
3766219089Spjd		} else {
3767219089Spjd			if (nvlist_lookup_nvlist(userprops, entry->pl_user_prop,
3768219089Spjd			    &propval) == 0) {
3769219089Spjd				verify(nvlist_lookup_string(propval,
3770219089Spjd				    ZPROP_VALUE, &strval) == 0);
3771219089Spjd				if (strlen(strval) > entry->pl_width)
3772219089Spjd					entry->pl_width = strlen(strval);
3773219089Spjd			}
3774219089Spjd			if (received && zfs_prop_get_recvd(zhp,
3775219089Spjd			    entry->pl_user_prop,
3776219089Spjd			    buf, sizeof (buf), B_FALSE) == 0)
3777219089Spjd				if (strlen(buf) > entry->pl_recvd_width)
3778219089Spjd					entry->pl_recvd_width = strlen(buf);
3779168404Spjd		}
3780168404Spjd	}
3781168404Spjd
3782168404Spjd	return (0);
3783168404Spjd}
3784168404Spjd
3785185029Spjdint
3786185029Spjdzfs_deleg_share_nfs(libzfs_handle_t *hdl, char *dataset, char *path,
3787209962Smm    char *resource, void *export, void *sharetab,
3788209962Smm    int sharemax, zfs_share_op_t operation)
3789185029Spjd{
3790185029Spjd	zfs_cmd_t zc = { 0 };
3791185029Spjd	int error;
3792185029Spjd
3793185029Spjd	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3794185029Spjd	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3795209962Smm	if (resource)
3796209962Smm		(void) strlcpy(zc.zc_string, resource, sizeof (zc.zc_string));
3797185029Spjd	zc.zc_share.z_sharedata = (uint64_t)(uintptr_t)sharetab;
3798185029Spjd	zc.zc_share.z_exportdata = (uint64_t)(uintptr_t)export;
3799185029Spjd	zc.zc_share.z_sharetype = operation;
3800185029Spjd	zc.zc_share.z_sharemax = sharemax;
3801185029Spjd	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SHARE, &zc);
3802185029Spjd	return (error);
3803185029Spjd}
3804185029Spjd
3805205198Sdelphijvoid
3806205198Sdelphijzfs_prune_proplist(zfs_handle_t *zhp, uint8_t *props)
3807205198Sdelphij{
3808205198Sdelphij	nvpair_t *curr;
3809205198Sdelphij
3810205198Sdelphij	/*
3811205198Sdelphij	 * Keep a reference to the props-table against which we prune the
3812205198Sdelphij	 * properties.
3813205198Sdelphij	 */
3814205198Sdelphij	zhp->zfs_props_table = props;
3815205198Sdelphij
3816205198Sdelphij	curr = nvlist_next_nvpair(zhp->zfs_props, NULL);
3817205198Sdelphij
3818205198Sdelphij	while (curr) {
3819205198Sdelphij		zfs_prop_t zfs_prop = zfs_name_to_prop(nvpair_name(curr));
3820205198Sdelphij		nvpair_t *next = nvlist_next_nvpair(zhp->zfs_props, curr);
3821205198Sdelphij
3822206199Sdelphij		/*
3823219089Spjd		 * User properties will result in ZPROP_INVAL, and since we
3824219089Spjd		 * only know how to prune standard ZFS properties, we always
3825219089Spjd		 * leave these in the list.  This can also happen if we
3826219089Spjd		 * encounter an unknown DSL property (when running older
3827219089Spjd		 * software, for example).
3828206199Sdelphij		 */
3829206199Sdelphij		if (zfs_prop != ZPROP_INVAL && props[zfs_prop] == B_FALSE)
3830205198Sdelphij			(void) nvlist_remove(zhp->zfs_props,
3831205198Sdelphij			    nvpair_name(curr), nvpair_type(curr));
3832205198Sdelphij		curr = next;
3833205198Sdelphij	}
3834205198Sdelphij}
3835205198Sdelphij
3836209962Smm#ifdef sun
3837209962Smmstatic int
3838209962Smmzfs_smb_acl_mgmt(libzfs_handle_t *hdl, char *dataset, char *path,
3839209962Smm    zfs_smb_acl_op_t cmd, char *resource1, char *resource2)
3840209962Smm{
3841209962Smm	zfs_cmd_t zc = { 0 };
3842209962Smm	nvlist_t *nvlist = NULL;
3843209962Smm	int error;
3844209962Smm
3845209962Smm	(void) strlcpy(zc.zc_name, dataset, sizeof (zc.zc_name));
3846209962Smm	(void) strlcpy(zc.zc_value, path, sizeof (zc.zc_value));
3847209962Smm	zc.zc_cookie = (uint64_t)cmd;
3848209962Smm
3849209962Smm	if (cmd == ZFS_SMB_ACL_RENAME) {
3850209962Smm		if (nvlist_alloc(&nvlist, NV_UNIQUE_NAME, 0) != 0) {
3851209962Smm			(void) no_memory(hdl);
3852209962Smm			return (NULL);
3853209962Smm		}
3854209962Smm	}
3855209962Smm
3856209962Smm	switch (cmd) {
3857209962Smm	case ZFS_SMB_ACL_ADD:
3858209962Smm	case ZFS_SMB_ACL_REMOVE:
3859209962Smm		(void) strlcpy(zc.zc_string, resource1, sizeof (zc.zc_string));
3860209962Smm		break;
3861209962Smm	case ZFS_SMB_ACL_RENAME:
3862209962Smm		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_SRC,
3863209962Smm		    resource1) != 0) {
3864209962Smm				(void) no_memory(hdl);
3865209962Smm				return (-1);
3866209962Smm		}
3867209962Smm		if (nvlist_add_string(nvlist, ZFS_SMB_ACL_TARGET,
3868209962Smm		    resource2) != 0) {
3869209962Smm				(void) no_memory(hdl);
3870209962Smm				return (-1);
3871209962Smm		}
3872209962Smm		if (zcmd_write_src_nvlist(hdl, &zc, nvlist) != 0) {
3873209962Smm			nvlist_free(nvlist);
3874209962Smm			return (-1);
3875209962Smm		}
3876209962Smm		break;
3877209962Smm	case ZFS_SMB_ACL_PURGE:
3878209962Smm		break;
3879209962Smm	default:
3880209962Smm		return (-1);
3881209962Smm	}
3882209962Smm	error = ioctl(hdl->libzfs_fd, ZFS_IOC_SMB_ACL, &zc);
3883209962Smm	if (nvlist)
3884209962Smm		nvlist_free(nvlist);
3885209962Smm	return (error);
3886209962Smm}
3887209962Smm
3888209962Smmint
3889209962Smmzfs_smb_acl_add(libzfs_handle_t *hdl, char *dataset,
3890209962Smm    char *path, char *resource)
3891209962Smm{
3892209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_ADD,
3893209962Smm	    resource, NULL));
3894209962Smm}
3895209962Smm
3896209962Smmint
3897209962Smmzfs_smb_acl_remove(libzfs_handle_t *hdl, char *dataset,
3898209962Smm    char *path, char *resource)
3899209962Smm{
3900209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_REMOVE,
3901209962Smm	    resource, NULL));
3902209962Smm}
3903209962Smm
3904209962Smmint
3905209962Smmzfs_smb_acl_purge(libzfs_handle_t *hdl, char *dataset, char *path)
3906209962Smm{
3907209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_PURGE,
3908209962Smm	    NULL, NULL));
3909209962Smm}
3910209962Smm
3911209962Smmint
3912209962Smmzfs_smb_acl_rename(libzfs_handle_t *hdl, char *dataset, char *path,
3913209962Smm    char *oldname, char *newname)
3914209962Smm{
3915209962Smm	return (zfs_smb_acl_mgmt(hdl, dataset, path, ZFS_SMB_ACL_RENAME,
3916209962Smm	    oldname, newname));
3917209962Smm}
3918209962Smm#endif	/* sun */
3919209962Smm
3920209962Smmint
3921209962Smmzfs_userspace(zfs_handle_t *zhp, zfs_userquota_prop_t type,
3922209962Smm    zfs_userspace_cb_t func, void *arg)
3923209962Smm{
3924209962Smm	zfs_cmd_t zc = { 0 };
3925209962Smm	int error;
3926209962Smm	zfs_useracct_t buf[100];
3927209962Smm
3928209962Smm	(void) strncpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3929209962Smm
3930209962Smm	zc.zc_objset_type = type;
3931209962Smm	zc.zc_nvlist_dst = (uintptr_t)buf;
3932209962Smm
3933209962Smm	/* CONSTCOND */
3934209962Smm	while (1) {
3935209962Smm		zfs_useracct_t *zua = buf;
3936209962Smm
3937209962Smm		zc.zc_nvlist_dst_size = sizeof (buf);
3938209962Smm		error = ioctl(zhp->zfs_hdl->libzfs_fd,
3939209962Smm		    ZFS_IOC_USERSPACE_MANY, &zc);
3940209962Smm		if (error || zc.zc_nvlist_dst_size == 0)
3941209962Smm			break;
3942209962Smm
3943209962Smm		while (zc.zc_nvlist_dst_size > 0) {
3944209962Smm			error = func(arg, zua->zu_domain, zua->zu_rid,
3945209962Smm			    zua->zu_space);
3946209962Smm			if (error != 0)
3947209962Smm				return (error);
3948209962Smm			zua++;
3949209962Smm			zc.zc_nvlist_dst_size -= sizeof (zfs_useracct_t);
3950209962Smm		}
3951209962Smm	}
3952209962Smm
3953209962Smm	return (error);
3954209962Smm}
3955209962Smm
3956219089Spjdint
3957219089Spjdzfs_hold(zfs_handle_t *zhp, const char *snapname, const char *tag,
3958219089Spjd    boolean_t recursive, boolean_t temphold, boolean_t enoent_ok,
3959219089Spjd    int cleanup_fd, uint64_t dsobj, uint64_t createtxg)
3960219089Spjd{
3961219089Spjd	zfs_cmd_t zc = { 0 };
3962219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
3963219089Spjd
3964219089Spjd	ASSERT(!recursive || dsobj == 0);
3965219089Spjd
3966219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
3967219089Spjd	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
3968219089Spjd	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
3969219089Spjd	    >= sizeof (zc.zc_string))
3970219089Spjd		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
3971219089Spjd	zc.zc_cookie = recursive;
3972219089Spjd	zc.zc_temphold = temphold;
3973219089Spjd	zc.zc_cleanup_fd = cleanup_fd;
3974219089Spjd	zc.zc_sendobj = dsobj;
3975219089Spjd	zc.zc_createtxg = createtxg;
3976219089Spjd
3977219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_HOLD, &zc) != 0) {
3978219089Spjd		char errbuf[ZFS_MAXNAMELEN+32];
3979219089Spjd
3980219089Spjd		/*
3981219089Spjd		 * if it was recursive, the one that actually failed will be in
3982219089Spjd		 * zc.zc_name.
3983219089Spjd		 */
3984219089Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
3985219089Spjd		    "cannot hold '%s@%s'"), zc.zc_name, snapname);
3986219089Spjd		switch (errno) {
3987219089Spjd		case E2BIG:
3988219089Spjd			/*
3989219089Spjd			 * Temporary tags wind up having the ds object id
3990219089Spjd			 * prepended. So even if we passed the length check
3991219089Spjd			 * above, it's still possible for the tag to wind
3992219089Spjd			 * up being slightly too long.
3993219089Spjd			 */
3994219089Spjd			return (zfs_error(hdl, EZFS_TAGTOOLONG, errbuf));
3995219089Spjd		case ENOTSUP:
3996219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
3997219089Spjd			    "pool must be upgraded"));
3998219089Spjd			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
3999219089Spjd		case EINVAL:
4000219089Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4001219089Spjd		case EEXIST:
4002219089Spjd			return (zfs_error(hdl, EZFS_REFTAG_HOLD, errbuf));
4003219089Spjd		case ENOENT:
4004219089Spjd			if (enoent_ok)
4005219089Spjd				return (ENOENT);
4006219089Spjd			/* FALLTHROUGH */
4007219089Spjd		default:
4008219089Spjd			return (zfs_standard_error_fmt(hdl, errno, errbuf));
4009219089Spjd		}
4010219089Spjd	}
4011219089Spjd
4012219089Spjd	return (0);
4013219089Spjd}
4014219089Spjd
4015219089Spjdint
4016219089Spjdzfs_release(zfs_handle_t *zhp, const char *snapname, const char *tag,
4017219089Spjd    boolean_t recursive)
4018219089Spjd{
4019219089Spjd	zfs_cmd_t zc = { 0 };
4020219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4021219089Spjd
4022219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4023219089Spjd	(void) strlcpy(zc.zc_value, snapname, sizeof (zc.zc_value));
4024219089Spjd	if (strlcpy(zc.zc_string, tag, sizeof (zc.zc_string))
4025219089Spjd	    >= sizeof (zc.zc_string))
4026219089Spjd		return (zfs_error(hdl, EZFS_TAGTOOLONG, tag));
4027219089Spjd	zc.zc_cookie = recursive;
4028219089Spjd
4029219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_RELEASE, &zc) != 0) {
4030219089Spjd		char errbuf[ZFS_MAXNAMELEN+32];
4031219089Spjd
4032219089Spjd		/*
4033219089Spjd		 * if it was recursive, the one that actually failed will be in
4034219089Spjd		 * zc.zc_name.
4035219089Spjd		 */
4036219089Spjd		(void) snprintf(errbuf, sizeof (errbuf), dgettext(TEXT_DOMAIN,
4037219089Spjd		    "cannot release '%s' from '%s@%s'"), tag, zc.zc_name,
4038219089Spjd		    snapname);
4039219089Spjd		switch (errno) {
4040219089Spjd		case ESRCH:
4041219089Spjd			return (zfs_error(hdl, EZFS_REFTAG_RELE, errbuf));
4042219089Spjd		case ENOTSUP:
4043219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4044219089Spjd			    "pool must be upgraded"));
4045219089Spjd			return (zfs_error(hdl, EZFS_BADVERSION, errbuf));
4046219089Spjd		case EINVAL:
4047219089Spjd			return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4048219089Spjd		default:
4049219089Spjd			return (zfs_standard_error_fmt(hdl, errno, errbuf));
4050219089Spjd		}
4051219089Spjd	}
4052219089Spjd
4053219089Spjd	return (0);
4054219089Spjd}
4055219089Spjd
4056219089Spjdint
4057219089Spjdzfs_get_fsacl(zfs_handle_t *zhp, nvlist_t **nvl)
4058219089Spjd{
4059219089Spjd	zfs_cmd_t zc = { 0 };
4060219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4061219089Spjd	int nvsz = 2048;
4062219089Spjd	void *nvbuf;
4063219089Spjd	int err = 0;
4064219089Spjd	char errbuf[ZFS_MAXNAMELEN+32];
4065219089Spjd
4066219089Spjd	assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
4067219089Spjd	    zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4068219089Spjd
4069219089Spjdtryagain:
4070219089Spjd
4071219089Spjd	nvbuf = malloc(nvsz);
4072219089Spjd	if (nvbuf == NULL) {
4073219089Spjd		err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
4074219089Spjd		goto out;
4075219089Spjd	}
4076219089Spjd
4077219089Spjd	zc.zc_nvlist_dst_size = nvsz;
4078219089Spjd	zc.zc_nvlist_dst = (uintptr_t)nvbuf;
4079219089Spjd
4080219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
4081219089Spjd
4082219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_GET_FSACL, &zc) != 0) {
4083219089Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4084219089Spjd		    dgettext(TEXT_DOMAIN, "cannot get permissions on '%s'"),
4085219089Spjd		    zc.zc_name);
4086219089Spjd		switch (errno) {
4087219089Spjd		case ENOMEM:
4088219089Spjd			free(nvbuf);
4089219089Spjd			nvsz = zc.zc_nvlist_dst_size;
4090219089Spjd			goto tryagain;
4091219089Spjd
4092219089Spjd		case ENOTSUP:
4093219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4094219089Spjd			    "pool must be upgraded"));
4095219089Spjd			err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4096219089Spjd			break;
4097219089Spjd		case EINVAL:
4098219089Spjd			err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4099219089Spjd			break;
4100219089Spjd		case ENOENT:
4101219089Spjd			err = zfs_error(hdl, EZFS_NOENT, errbuf);
4102219089Spjd			break;
4103219089Spjd		default:
4104219089Spjd			err = zfs_standard_error_fmt(hdl, errno, errbuf);
4105219089Spjd			break;
4106219089Spjd		}
4107219089Spjd	} else {
4108219089Spjd		/* success */
4109219089Spjd		int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
4110219089Spjd		if (rc) {
4111219089Spjd			(void) snprintf(errbuf, sizeof (errbuf), dgettext(
4112219089Spjd			    TEXT_DOMAIN, "cannot get permissions on '%s'"),
4113219089Spjd			    zc.zc_name);
4114219089Spjd			err = zfs_standard_error_fmt(hdl, rc, errbuf);
4115219089Spjd		}
4116219089Spjd	}
4117219089Spjd
4118219089Spjd	free(nvbuf);
4119219089Spjdout:
4120219089Spjd	return (err);
4121219089Spjd}
4122219089Spjd
4123219089Spjdint
4124219089Spjdzfs_set_fsacl(zfs_handle_t *zhp, boolean_t un, nvlist_t *nvl)
4125219089Spjd{
4126219089Spjd	zfs_cmd_t zc = { 0 };
4127219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4128219089Spjd	char *nvbuf;
4129219089Spjd	char errbuf[ZFS_MAXNAMELEN+32];
4130219089Spjd	size_t nvsz;
4131219089Spjd	int err;
4132219089Spjd
4133219089Spjd	assert(zhp->zfs_type == ZFS_TYPE_VOLUME ||
4134219089Spjd	    zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4135219089Spjd
4136219089Spjd	err = nvlist_size(nvl, &nvsz, NV_ENCODE_NATIVE);
4137219089Spjd	assert(err == 0);
4138219089Spjd
4139219089Spjd	nvbuf = malloc(nvsz);
4140219089Spjd
4141219089Spjd	err = nvlist_pack(nvl, &nvbuf, &nvsz, NV_ENCODE_NATIVE, 0);
4142219089Spjd	assert(err == 0);
4143219089Spjd
4144219089Spjd	zc.zc_nvlist_src_size = nvsz;
4145219089Spjd	zc.zc_nvlist_src = (uintptr_t)nvbuf;
4146219089Spjd	zc.zc_perm_action = un;
4147219089Spjd
4148219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4149219089Spjd
4150219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_SET_FSACL, &zc) != 0) {
4151219089Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4152219089Spjd		    dgettext(TEXT_DOMAIN, "cannot set permissions on '%s'"),
4153219089Spjd		    zc.zc_name);
4154219089Spjd		switch (errno) {
4155219089Spjd		case ENOTSUP:
4156219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4157219089Spjd			    "pool must be upgraded"));
4158219089Spjd			err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4159219089Spjd			break;
4160219089Spjd		case EINVAL:
4161219089Spjd			err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4162219089Spjd			break;
4163219089Spjd		case ENOENT:
4164219089Spjd			err = zfs_error(hdl, EZFS_NOENT, errbuf);
4165219089Spjd			break;
4166219089Spjd		default:
4167219089Spjd			err = zfs_standard_error_fmt(hdl, errno, errbuf);
4168219089Spjd			break;
4169219089Spjd		}
4170219089Spjd	}
4171219089Spjd
4172219089Spjd	free(nvbuf);
4173219089Spjd
4174219089Spjd	return (err);
4175219089Spjd}
4176219089Spjd
4177219089Spjdint
4178219089Spjdzfs_get_holds(zfs_handle_t *zhp, nvlist_t **nvl)
4179219089Spjd{
4180219089Spjd	zfs_cmd_t zc = { 0 };
4181219089Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4182219089Spjd	int nvsz = 2048;
4183219089Spjd	void *nvbuf;
4184219089Spjd	int err = 0;
4185219089Spjd	char errbuf[ZFS_MAXNAMELEN+32];
4186219089Spjd
4187219089Spjd	assert(zhp->zfs_type == ZFS_TYPE_SNAPSHOT);
4188219089Spjd
4189219089Spjdtryagain:
4190219089Spjd
4191219089Spjd	nvbuf = malloc(nvsz);
4192219089Spjd	if (nvbuf == NULL) {
4193219089Spjd		err = (zfs_error(hdl, EZFS_NOMEM, strerror(errno)));
4194219089Spjd		goto out;
4195219089Spjd	}
4196219089Spjd
4197219089Spjd	zc.zc_nvlist_dst_size = nvsz;
4198219089Spjd	zc.zc_nvlist_dst = (uintptr_t)nvbuf;
4199219089Spjd
4200219089Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, ZFS_MAXNAMELEN);
4201219089Spjd
4202219089Spjd	if (zfs_ioctl(hdl, ZFS_IOC_GET_HOLDS, &zc) != 0) {
4203219089Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4204219089Spjd		    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
4205219089Spjd		    zc.zc_name);
4206219089Spjd		switch (errno) {
4207219089Spjd		case ENOMEM:
4208219089Spjd			free(nvbuf);
4209219089Spjd			nvsz = zc.zc_nvlist_dst_size;
4210219089Spjd			goto tryagain;
4211219089Spjd
4212219089Spjd		case ENOTSUP:
4213219089Spjd			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4214219089Spjd			    "pool must be upgraded"));
4215219089Spjd			err = zfs_error(hdl, EZFS_BADVERSION, errbuf);
4216219089Spjd			break;
4217219089Spjd		case EINVAL:
4218219089Spjd			err = zfs_error(hdl, EZFS_BADTYPE, errbuf);
4219219089Spjd			break;
4220219089Spjd		case ENOENT:
4221219089Spjd			err = zfs_error(hdl, EZFS_NOENT, errbuf);
4222219089Spjd			break;
4223219089Spjd		default:
4224219089Spjd			err = zfs_standard_error_fmt(hdl, errno, errbuf);
4225219089Spjd			break;
4226219089Spjd		}
4227219089Spjd	} else {
4228219089Spjd		/* success */
4229219089Spjd		int rc = nvlist_unpack(nvbuf, zc.zc_nvlist_dst_size, nvl, 0);
4230219089Spjd		if (rc) {
4231219089Spjd			(void) snprintf(errbuf, sizeof (errbuf),
4232219089Spjd			    dgettext(TEXT_DOMAIN, "cannot get holds for '%s'"),
4233219089Spjd			    zc.zc_name);
4234219089Spjd			err = zfs_standard_error_fmt(hdl, rc, errbuf);
4235219089Spjd		}
4236219089Spjd	}
4237219089Spjd
4238219089Spjd	free(nvbuf);
4239219089Spjdout:
4240219089Spjd	return (err);
4241219089Spjd}
4242219089Spjd
4243219089Spjduint64_t
4244219089Spjdzvol_volsize_to_reservation(uint64_t volsize, nvlist_t *props)
4245219089Spjd{
4246219089Spjd	uint64_t numdb;
4247219089Spjd	uint64_t nblocks, volblocksize;
4248219089Spjd	int ncopies;
4249219089Spjd	char *strval;
4250219089Spjd
4251219089Spjd	if (nvlist_lookup_string(props,
4252219089Spjd	    zfs_prop_to_name(ZFS_PROP_COPIES), &strval) == 0)
4253219089Spjd		ncopies = atoi(strval);
4254219089Spjd	else
4255219089Spjd		ncopies = 1;
4256219089Spjd	if (nvlist_lookup_uint64(props,
4257219089Spjd	    zfs_prop_to_name(ZFS_PROP_VOLBLOCKSIZE),
4258219089Spjd	    &volblocksize) != 0)
4259219089Spjd		volblocksize = ZVOL_DEFAULT_BLOCKSIZE;
4260219089Spjd	nblocks = volsize/volblocksize;
4261219089Spjd	/* start with metadnode L0-L6 */
4262219089Spjd	numdb = 7;
4263219089Spjd	/* calculate number of indirects */
4264219089Spjd	while (nblocks > 1) {
4265219089Spjd		nblocks += DNODES_PER_LEVEL - 1;
4266219089Spjd		nblocks /= DNODES_PER_LEVEL;
4267219089Spjd		numdb += nblocks;
4268219089Spjd	}
4269219089Spjd	numdb *= MIN(SPA_DVAS_PER_BP, ncopies + 1);
4270219089Spjd	volsize *= ncopies;
4271219089Spjd	/*
4272219089Spjd	 * this is exactly DN_MAX_INDBLKSHIFT when metadata isn't
4273219089Spjd	 * compressed, but in practice they compress down to about
4274219089Spjd	 * 1100 bytes
4275219089Spjd	 */
4276219089Spjd	numdb *= 1ULL << DN_MAX_INDBLKSHIFT;
4277219089Spjd	volsize += numdb;
4278219089Spjd	return (volsize);
4279219089Spjd}
4280219089Spjd
4281168404Spjd/*
4282168404Spjd * Attach/detach the given filesystem to/from the given jail.
4283168404Spjd */
4284168404Spjdint
4285168404Spjdzfs_jail(zfs_handle_t *zhp, int jailid, int attach)
4286168404Spjd{
4287168404Spjd	libzfs_handle_t *hdl = zhp->zfs_hdl;
4288168404Spjd	zfs_cmd_t zc = { 0 };
4289168404Spjd	char errbuf[1024];
4290168404Spjd	int cmd, ret;
4291168404Spjd
4292168404Spjd	if (attach) {
4293168404Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4294168404Spjd		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
4295168404Spjd	} else {
4296168404Spjd		(void) snprintf(errbuf, sizeof (errbuf),
4297168404Spjd		    dgettext(TEXT_DOMAIN, "cannot jail '%s'"), zhp->zfs_name);
4298168404Spjd	}
4299168404Spjd
4300168404Spjd	switch (zhp->zfs_type) {
4301168404Spjd	case ZFS_TYPE_VOLUME:
4302168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4303168404Spjd		    "volumes can not be jailed"));
4304168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4305168404Spjd	case ZFS_TYPE_SNAPSHOT:
4306168404Spjd		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
4307168404Spjd		    "snapshots can not be jailed"));
4308168404Spjd		return (zfs_error(hdl, EZFS_BADTYPE, errbuf));
4309168404Spjd	}
4310168404Spjd	assert(zhp->zfs_type == ZFS_TYPE_FILESYSTEM);
4311168404Spjd
4312168404Spjd	(void) strlcpy(zc.zc_name, zhp->zfs_name, sizeof (zc.zc_name));
4313168404Spjd	zc.zc_objset_type = DMU_OST_ZFS;
4314168404Spjd	zc.zc_jailid = jailid;
4315168404Spjd
4316168404Spjd	cmd = attach ? ZFS_IOC_JAIL : ZFS_IOC_UNJAIL;
4317168404Spjd	if ((ret = ioctl(hdl->libzfs_fd, cmd, &zc)) != 0)
4318168404Spjd		zfs_standard_error(hdl, errno, errbuf);
4319168404Spjd
4320168404Spjd	return (ret);
4321168404Spjd}
4322