xref: /freebsd-src/sys/contrib/openzfs/lib/libzfs/libzfs_changelist.c (revision dd21556857e8d40f66bf5ad54754d9d52669ebf7)
1eda14cbcSMatt Macy /*
2eda14cbcSMatt Macy  * CDDL HEADER START
3eda14cbcSMatt Macy  *
4eda14cbcSMatt Macy  * The contents of this file are subject to the terms of the
5eda14cbcSMatt Macy  * Common Development and Distribution License (the "License").
6eda14cbcSMatt Macy  * You may not use this file except in compliance with the License.
7eda14cbcSMatt Macy  *
8eda14cbcSMatt Macy  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9271171e0SMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
10eda14cbcSMatt Macy  * See the License for the specific language governing permissions
11eda14cbcSMatt Macy  * and limitations under the License.
12eda14cbcSMatt Macy  *
13eda14cbcSMatt Macy  * When distributing Covered Code, include this CDDL HEADER in each
14eda14cbcSMatt Macy  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15eda14cbcSMatt Macy  * If applicable, add the following below this CDDL HEADER, with the
16eda14cbcSMatt Macy  * fields enclosed by brackets "[]" replaced with your own identifying
17eda14cbcSMatt Macy  * information: Portions Copyright [yyyy] [name of copyright owner]
18eda14cbcSMatt Macy  *
19eda14cbcSMatt Macy  * CDDL HEADER END
20eda14cbcSMatt Macy  */
21eda14cbcSMatt Macy 
22eda14cbcSMatt Macy /*
23eda14cbcSMatt Macy  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24eda14cbcSMatt Macy  * Use is subject to license terms.
25eda14cbcSMatt Macy  *
26eda14cbcSMatt Macy  * Portions Copyright 2007 Ramprakash Jelari
27eda14cbcSMatt Macy  * Copyright (c) 2014, 2020 by Delphix. All rights reserved.
28eda14cbcSMatt Macy  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
29eda14cbcSMatt Macy  * Copyright (c) 2018 Datto Inc.
30eda14cbcSMatt Macy  */
31eda14cbcSMatt Macy 
32eda14cbcSMatt Macy #include <libintl.h>
33eda14cbcSMatt Macy #include <libuutil.h>
34eda14cbcSMatt Macy #include <stddef.h>
35eda14cbcSMatt Macy #include <stdlib.h>
36eda14cbcSMatt Macy #include <string.h>
37eda14cbcSMatt Macy #include <unistd.h>
38eda14cbcSMatt Macy #include <zone.h>
39eda14cbcSMatt Macy 
40eda14cbcSMatt Macy #include <libzfs.h>
41eda14cbcSMatt Macy 
42eda14cbcSMatt Macy #include "libzfs_impl.h"
43eda14cbcSMatt Macy 
44eda14cbcSMatt Macy /*
45eda14cbcSMatt Macy  * Structure to keep track of dataset state.  Before changing the 'sharenfs' or
46eda14cbcSMatt Macy  * 'mountpoint' property, we record whether the filesystem was previously
47eda14cbcSMatt Macy  * mounted/shared.  This prior state dictates whether we remount/reshare the
48eda14cbcSMatt Macy  * dataset after the property has been changed.
49eda14cbcSMatt Macy  *
50eda14cbcSMatt Macy  * The interface consists of the following sequence of functions:
51eda14cbcSMatt Macy  *
52eda14cbcSMatt Macy  * 	changelist_gather()
53eda14cbcSMatt Macy  * 	changelist_prefix()
54eda14cbcSMatt Macy  * 	< change property >
55eda14cbcSMatt Macy  * 	changelist_postfix()
56eda14cbcSMatt Macy  * 	changelist_free()
57eda14cbcSMatt Macy  *
58eda14cbcSMatt Macy  * Other interfaces:
59eda14cbcSMatt Macy  *
60eda14cbcSMatt Macy  * changelist_remove() - remove a node from a gathered list
61eda14cbcSMatt Macy  * changelist_rename() - renames all datasets appropriately when doing a rename
62eda14cbcSMatt Macy  * changelist_unshare() - unshares all the nodes in a given changelist
63eda14cbcSMatt Macy  * changelist_haszonedchild() - check if there is any child exported to
64eda14cbcSMatt Macy  *				a local zone
65eda14cbcSMatt Macy  */
66eda14cbcSMatt Macy typedef struct prop_changenode {
67eda14cbcSMatt Macy 	zfs_handle_t		*cn_handle;
68eda14cbcSMatt Macy 	int			cn_shared;
69eda14cbcSMatt Macy 	int			cn_mounted;
70eda14cbcSMatt Macy 	int			cn_zoned;
71eda14cbcSMatt Macy 	boolean_t		cn_needpost;	/* is postfix() needed? */
72eda14cbcSMatt Macy 	uu_avl_node_t		cn_treenode;
73eda14cbcSMatt Macy } prop_changenode_t;
74eda14cbcSMatt Macy 
75eda14cbcSMatt Macy struct prop_changelist {
76eda14cbcSMatt Macy 	zfs_prop_t		cl_prop;
77eda14cbcSMatt Macy 	zfs_prop_t		cl_realprop;
78eda14cbcSMatt Macy 	zfs_prop_t		cl_shareprop;  /* used with sharenfs/sharesmb */
79eda14cbcSMatt Macy 	uu_avl_pool_t		*cl_pool;
80eda14cbcSMatt Macy 	uu_avl_t		*cl_tree;
81eda14cbcSMatt Macy 	boolean_t		cl_waslegacy;
82eda14cbcSMatt Macy 	boolean_t		cl_allchildren;
83eda14cbcSMatt Macy 	boolean_t		cl_alldependents;
84eda14cbcSMatt Macy 	int			cl_mflags;	/* Mount flags */
85eda14cbcSMatt Macy 	int			cl_gflags;	/* Gather request flags */
86eda14cbcSMatt Macy 	boolean_t		cl_haszonedchild;
87eda14cbcSMatt Macy };
88eda14cbcSMatt Macy 
89eda14cbcSMatt Macy /*
90eda14cbcSMatt Macy  * If the property is 'mountpoint', go through and unmount filesystems as
91eda14cbcSMatt Macy  * necessary.  We don't do the same for 'sharenfs', because we can just re-share
92eda14cbcSMatt Macy  * with different options without interrupting service. We do handle 'sharesmb'
93eda14cbcSMatt Macy  * since there may be old resource names that need to be removed.
94eda14cbcSMatt Macy  */
95eda14cbcSMatt Macy int
96eda14cbcSMatt Macy changelist_prefix(prop_changelist_t *clp)
97eda14cbcSMatt Macy {
98eda14cbcSMatt Macy 	prop_changenode_t *cn;
99eda14cbcSMatt Macy 	uu_avl_walk_t *walk;
100eda14cbcSMatt Macy 	int ret = 0;
101716fd348SMartin Matuska 	const enum sa_protocol smb[] = {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
102eda14cbcSMatt Macy 	boolean_t commit_smb_shares = B_FALSE;
103eda14cbcSMatt Macy 
104eda14cbcSMatt Macy 	if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
105eda14cbcSMatt Macy 	    clp->cl_prop != ZFS_PROP_SHARESMB)
106eda14cbcSMatt Macy 		return (0);
107eda14cbcSMatt Macy 
108f7f4bd06SMartin Matuska 	/*
109f7f4bd06SMartin Matuska 	 * If CL_GATHER_DONT_UNMOUNT is set, don't want to unmount/unshare and
110f7f4bd06SMartin Matuska 	 * later (re)mount/(re)share the filesystem in postfix phase, so we
111f7f4bd06SMartin Matuska 	 * return from here. If filesystem is mounted or unmounted, leave it
112f7f4bd06SMartin Matuska 	 * as it is.
113f7f4bd06SMartin Matuska 	 */
114f7f4bd06SMartin Matuska 	if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
115f7f4bd06SMartin Matuska 		return (0);
116f7f4bd06SMartin Matuska 
117eda14cbcSMatt Macy 	if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
118eda14cbcSMatt Macy 		return (-1);
119eda14cbcSMatt Macy 
120eda14cbcSMatt Macy 	while ((cn = uu_avl_walk_next(walk)) != NULL) {
121eda14cbcSMatt Macy 
122eda14cbcSMatt Macy 		/* if a previous loop failed, set the remaining to false */
123eda14cbcSMatt Macy 		if (ret == -1) {
124eda14cbcSMatt Macy 			cn->cn_needpost = B_FALSE;
125eda14cbcSMatt Macy 			continue;
126eda14cbcSMatt Macy 		}
127eda14cbcSMatt Macy 
128eda14cbcSMatt Macy 		/*
129eda14cbcSMatt Macy 		 * If we are in the global zone, but this dataset is exported
130eda14cbcSMatt Macy 		 * to a local zone, do nothing.
131eda14cbcSMatt Macy 		 */
132eda14cbcSMatt Macy 		if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
133eda14cbcSMatt Macy 			continue;
134eda14cbcSMatt Macy 
135eda14cbcSMatt Macy 		if (!ZFS_IS_VOLUME(cn->cn_handle)) {
136eda14cbcSMatt Macy 			/*
137eda14cbcSMatt Macy 			 * Do the property specific processing.
138eda14cbcSMatt Macy 			 */
139eda14cbcSMatt Macy 			switch (clp->cl_prop) {
140eda14cbcSMatt Macy 			case ZFS_PROP_MOUNTPOINT:
141eda14cbcSMatt Macy 				if (zfs_unmount(cn->cn_handle, NULL,
142eda14cbcSMatt Macy 				    clp->cl_mflags) != 0) {
143eda14cbcSMatt Macy 					ret = -1;
144eda14cbcSMatt Macy 					cn->cn_needpost = B_FALSE;
145eda14cbcSMatt Macy 				}
146eda14cbcSMatt Macy 				break;
147eda14cbcSMatt Macy 			case ZFS_PROP_SHARESMB:
148716fd348SMartin Matuska 				(void) zfs_unshare(cn->cn_handle, NULL,
149716fd348SMartin Matuska 				    smb);
150eda14cbcSMatt Macy 				commit_smb_shares = B_TRUE;
151eda14cbcSMatt Macy 				break;
152eda14cbcSMatt Macy 
153eda14cbcSMatt Macy 			default:
154eda14cbcSMatt Macy 				break;
155eda14cbcSMatt Macy 			}
156eda14cbcSMatt Macy 		}
157eda14cbcSMatt Macy 	}
158eda14cbcSMatt Macy 
159eda14cbcSMatt Macy 	if (commit_smb_shares)
160716fd348SMartin Matuska 		zfs_commit_shares(smb);
161eda14cbcSMatt Macy 	uu_avl_walk_end(walk);
162eda14cbcSMatt Macy 
163eda14cbcSMatt Macy 	if (ret == -1)
164eda14cbcSMatt Macy 		(void) changelist_postfix(clp);
165eda14cbcSMatt Macy 
166eda14cbcSMatt Macy 	return (ret);
167eda14cbcSMatt Macy }
168eda14cbcSMatt Macy 
169eda14cbcSMatt Macy /*
170eda14cbcSMatt Macy  * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
171eda14cbcSMatt Macy  * reshare the filesystems as necessary.  In changelist_gather() we recorded
172eda14cbcSMatt Macy  * whether the filesystem was previously shared or mounted.  The action we take
173eda14cbcSMatt Macy  * depends on the previous state, and whether the value was previously 'legacy'.
174f7f4bd06SMartin Matuska  * For non-legacy properties, we always remount/reshare the filesystem,
175f7f4bd06SMartin Matuska  * if CL_GATHER_DONT_UNMOUNT is not set.
176eda14cbcSMatt Macy  */
177eda14cbcSMatt Macy int
178eda14cbcSMatt Macy changelist_postfix(prop_changelist_t *clp)
179eda14cbcSMatt Macy {
180eda14cbcSMatt Macy 	prop_changenode_t *cn;
181eda14cbcSMatt Macy 	uu_avl_walk_t *walk;
182eda14cbcSMatt Macy 	char shareopts[ZFS_MAXPROPLEN];
183eda14cbcSMatt Macy 	boolean_t commit_smb_shares = B_FALSE;
184eda14cbcSMatt Macy 	boolean_t commit_nfs_shares = B_FALSE;
185eda14cbcSMatt Macy 
186eda14cbcSMatt Macy 	/*
187f7f4bd06SMartin Matuska 	 * If CL_GATHER_DONT_UNMOUNT is set, it means we don't want to (un)mount
188f7f4bd06SMartin Matuska 	 * or (re/un)share the filesystem, so we return from here. If filesystem
189f7f4bd06SMartin Matuska 	 * is mounted or unmounted, leave it as it is.
190f7f4bd06SMartin Matuska 	 */
191f7f4bd06SMartin Matuska 	if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
192f7f4bd06SMartin Matuska 		return (0);
193f7f4bd06SMartin Matuska 
194f7f4bd06SMartin Matuska 	/*
195eda14cbcSMatt Macy 	 * If we're changing the mountpoint, attempt to destroy the underlying
196eda14cbcSMatt Macy 	 * mountpoint.  All other datasets will have inherited from this dataset
197eda14cbcSMatt Macy 	 * (in which case their mountpoints exist in the filesystem in the new
198eda14cbcSMatt Macy 	 * location), or have explicit mountpoints set (in which case they won't
199eda14cbcSMatt Macy 	 * be in the changelist).
200eda14cbcSMatt Macy 	 */
201eda14cbcSMatt Macy 	if ((cn = uu_avl_last(clp->cl_tree)) == NULL)
202eda14cbcSMatt Macy 		return (0);
203eda14cbcSMatt Macy 
204eac7052fSMatt Macy 	if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
205eac7052fSMatt Macy 	    !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT))
206eda14cbcSMatt Macy 		remove_mountpoint(cn->cn_handle);
207eda14cbcSMatt Macy 
208eda14cbcSMatt Macy 	/*
209eda14cbcSMatt Macy 	 * We walk the datasets in reverse, because we want to mount any parent
210eda14cbcSMatt Macy 	 * datasets before mounting the children.  We walk all datasets even if
211eda14cbcSMatt Macy 	 * there are errors.
212eda14cbcSMatt Macy 	 */
213eda14cbcSMatt Macy 	if ((walk = uu_avl_walk_start(clp->cl_tree,
214eda14cbcSMatt Macy 	    UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
215eda14cbcSMatt Macy 		return (-1);
216eda14cbcSMatt Macy 
217eda14cbcSMatt Macy 	while ((cn = uu_avl_walk_next(walk)) != NULL) {
218eda14cbcSMatt Macy 
219eda14cbcSMatt Macy 		boolean_t sharenfs;
220eda14cbcSMatt Macy 		boolean_t sharesmb;
221eda14cbcSMatt Macy 		boolean_t mounted;
222eda14cbcSMatt Macy 		boolean_t needs_key;
223eda14cbcSMatt Macy 
224eda14cbcSMatt Macy 		/*
225eda14cbcSMatt Macy 		 * If we are in the global zone, but this dataset is exported
226eda14cbcSMatt Macy 		 * to a local zone, do nothing.
227eda14cbcSMatt Macy 		 */
228eda14cbcSMatt Macy 		if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
229eda14cbcSMatt Macy 			continue;
230eda14cbcSMatt Macy 
231eda14cbcSMatt Macy 		/* Only do post-processing if it's required */
232eda14cbcSMatt Macy 		if (!cn->cn_needpost)
233eda14cbcSMatt Macy 			continue;
234eda14cbcSMatt Macy 		cn->cn_needpost = B_FALSE;
235eda14cbcSMatt Macy 
236eda14cbcSMatt Macy 		zfs_refresh_properties(cn->cn_handle);
237eda14cbcSMatt Macy 
238eda14cbcSMatt Macy 		if (ZFS_IS_VOLUME(cn->cn_handle))
239eda14cbcSMatt Macy 			continue;
240eda14cbcSMatt Macy 
241eda14cbcSMatt Macy 		/*
242eda14cbcSMatt Macy 		 * Remount if previously mounted or mountpoint was legacy,
243eda14cbcSMatt Macy 		 * or sharenfs or sharesmb  property is set.
244eda14cbcSMatt Macy 		 */
245eda14cbcSMatt Macy 		sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
246eda14cbcSMatt Macy 		    shareopts, sizeof (shareopts), NULL, NULL, 0,
247eda14cbcSMatt Macy 		    B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
248eda14cbcSMatt Macy 
249eda14cbcSMatt Macy 		sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
250eda14cbcSMatt Macy 		    shareopts, sizeof (shareopts), NULL, NULL, 0,
251eda14cbcSMatt Macy 		    B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
252eda14cbcSMatt Macy 
253eda14cbcSMatt Macy 		needs_key = (zfs_prop_get_int(cn->cn_handle,
254eda14cbcSMatt Macy 		    ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
255eda14cbcSMatt Macy 
256f7f4bd06SMartin Matuska 		mounted = zfs_is_mounted(cn->cn_handle, NULL);
257eda14cbcSMatt Macy 
258eda14cbcSMatt Macy 		if (!mounted && !needs_key && (cn->cn_mounted ||
259cbfe9975SMartin Matuska 		    (((clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
260cbfe9975SMartin Matuska 		    clp->cl_prop == clp->cl_realprop) ||
261cbfe9975SMartin Matuska 		    sharenfs || sharesmb || clp->cl_waslegacy) &&
262eda14cbcSMatt Macy 		    (zfs_prop_get_int(cn->cn_handle,
263eda14cbcSMatt Macy 		    ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
264eda14cbcSMatt Macy 
265cbfe9975SMartin Matuska 			if (zfs_mount(cn->cn_handle, NULL, 0) == 0)
266eda14cbcSMatt Macy 				mounted = TRUE;
267eda14cbcSMatt Macy 		}
268eda14cbcSMatt Macy 
269eda14cbcSMatt Macy 		/*
270eda14cbcSMatt Macy 		 * If the file system is mounted we always re-share even
271eda14cbcSMatt Macy 		 * if the filesystem is currently shared, so that we can
272eda14cbcSMatt Macy 		 * adopt any new options.
273eda14cbcSMatt Macy 		 */
274716fd348SMartin Matuska 		const enum sa_protocol nfs[] =
275716fd348SMartin Matuska 		    {SA_PROTOCOL_NFS, SA_NO_PROTOCOL};
276eda14cbcSMatt Macy 		if (sharenfs && mounted) {
277cbfe9975SMartin Matuska 			zfs_share(cn->cn_handle, nfs);
278eda14cbcSMatt Macy 			commit_nfs_shares = B_TRUE;
279eda14cbcSMatt Macy 		} else if (cn->cn_shared || clp->cl_waslegacy) {
280cbfe9975SMartin Matuska 			zfs_unshare(cn->cn_handle, NULL, nfs);
281eda14cbcSMatt Macy 			commit_nfs_shares = B_TRUE;
282eda14cbcSMatt Macy 		}
283716fd348SMartin Matuska 		const enum sa_protocol smb[] =
284716fd348SMartin Matuska 		    {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
285eda14cbcSMatt Macy 		if (sharesmb && mounted) {
286cbfe9975SMartin Matuska 			zfs_share(cn->cn_handle, smb);
287eda14cbcSMatt Macy 			commit_smb_shares = B_TRUE;
288eda14cbcSMatt Macy 		} else if (cn->cn_shared || clp->cl_waslegacy) {
289cbfe9975SMartin Matuska 			zfs_unshare(cn->cn_handle, NULL, smb);
290eda14cbcSMatt Macy 			commit_smb_shares = B_TRUE;
291eda14cbcSMatt Macy 		}
292eda14cbcSMatt Macy 	}
293716fd348SMartin Matuska 
294716fd348SMartin Matuska 	enum sa_protocol proto[SA_PROTOCOL_COUNT + 1], *p = proto;
295eda14cbcSMatt Macy 	if (commit_nfs_shares)
296716fd348SMartin Matuska 		*p++ = SA_PROTOCOL_NFS;
297eda14cbcSMatt Macy 	if (commit_smb_shares)
298716fd348SMartin Matuska 		*p++ = SA_PROTOCOL_SMB;
299716fd348SMartin Matuska 	*p++ = SA_NO_PROTOCOL;
300716fd348SMartin Matuska 	zfs_commit_shares(proto);
301eda14cbcSMatt Macy 	uu_avl_walk_end(walk);
302eda14cbcSMatt Macy 
303cbfe9975SMartin Matuska 	return (0);
304eda14cbcSMatt Macy }
305eda14cbcSMatt Macy 
306eda14cbcSMatt Macy /*
307eda14cbcSMatt Macy  * Is this "dataset" a child of "parent"?
308eda14cbcSMatt Macy  */
30916038816SMartin Matuska static boolean_t
310eda14cbcSMatt Macy isa_child_of(const char *dataset, const char *parent)
311eda14cbcSMatt Macy {
312eda14cbcSMatt Macy 	int len;
313eda14cbcSMatt Macy 
314eda14cbcSMatt Macy 	len = strlen(parent);
315eda14cbcSMatt Macy 
316eda14cbcSMatt Macy 	if (strncmp(dataset, parent, len) == 0 &&
317eda14cbcSMatt Macy 	    (dataset[len] == '@' || dataset[len] == '/' ||
318eda14cbcSMatt Macy 	    dataset[len] == '\0'))
319eda14cbcSMatt Macy 		return (B_TRUE);
320eda14cbcSMatt Macy 	else
321eda14cbcSMatt Macy 		return (B_FALSE);
322eda14cbcSMatt Macy 
323eda14cbcSMatt Macy }
324eda14cbcSMatt Macy 
325eda14cbcSMatt Macy /*
326eda14cbcSMatt Macy  * If we rename a filesystem, child filesystem handles are no longer valid
327eda14cbcSMatt Macy  * since we identify each dataset by its name in the ZFS namespace.  As a
328eda14cbcSMatt Macy  * result, we have to go through and fix up all the names appropriately.  We
329eda14cbcSMatt Macy  * could do this automatically if libzfs kept track of all open handles, but
330eda14cbcSMatt Macy  * this is a lot less work.
331eda14cbcSMatt Macy  */
332eda14cbcSMatt Macy void
333eda14cbcSMatt Macy changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
334eda14cbcSMatt Macy {
335eda14cbcSMatt Macy 	prop_changenode_t *cn;
336eda14cbcSMatt Macy 	uu_avl_walk_t *walk;
337eda14cbcSMatt Macy 	char newname[ZFS_MAX_DATASET_NAME_LEN];
338eda14cbcSMatt Macy 
339eda14cbcSMatt Macy 	if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
340eda14cbcSMatt Macy 		return;
341eda14cbcSMatt Macy 
342eda14cbcSMatt Macy 	while ((cn = uu_avl_walk_next(walk)) != NULL) {
343eda14cbcSMatt Macy 		/*
344eda14cbcSMatt Macy 		 * Do not rename a clone that's not in the source hierarchy.
345eda14cbcSMatt Macy 		 */
346eda14cbcSMatt Macy 		if (!isa_child_of(cn->cn_handle->zfs_name, src))
347eda14cbcSMatt Macy 			continue;
348eda14cbcSMatt Macy 
349eda14cbcSMatt Macy 		/*
350eda14cbcSMatt Macy 		 * Destroy the previous mountpoint if needed.
351eda14cbcSMatt Macy 		 */
352eda14cbcSMatt Macy 		remove_mountpoint(cn->cn_handle);
353eda14cbcSMatt Macy 
354eda14cbcSMatt Macy 		(void) strlcpy(newname, dst, sizeof (newname));
355eda14cbcSMatt Macy 		(void) strlcat(newname, cn->cn_handle->zfs_name + strlen(src),
356eda14cbcSMatt Macy 		    sizeof (newname));
357eda14cbcSMatt Macy 
358eda14cbcSMatt Macy 		(void) strlcpy(cn->cn_handle->zfs_name, newname,
359eda14cbcSMatt Macy 		    sizeof (cn->cn_handle->zfs_name));
360eda14cbcSMatt Macy 	}
361eda14cbcSMatt Macy 
362eda14cbcSMatt Macy 	uu_avl_walk_end(walk);
363eda14cbcSMatt Macy }
364eda14cbcSMatt Macy 
365eda14cbcSMatt Macy /*
366eda14cbcSMatt Macy  * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
367eda14cbcSMatt Macy  * unshare all the datasets in the list.
368eda14cbcSMatt Macy  */
369eda14cbcSMatt Macy int
370716fd348SMartin Matuska changelist_unshare(prop_changelist_t *clp, const enum sa_protocol *proto)
371eda14cbcSMatt Macy {
372eda14cbcSMatt Macy 	prop_changenode_t *cn;
373eda14cbcSMatt Macy 	uu_avl_walk_t *walk;
374eda14cbcSMatt Macy 	int ret = 0;
375eda14cbcSMatt Macy 
376eda14cbcSMatt Macy 	if (clp->cl_prop != ZFS_PROP_SHARENFS &&
377eda14cbcSMatt Macy 	    clp->cl_prop != ZFS_PROP_SHARESMB)
378eda14cbcSMatt Macy 		return (0);
379eda14cbcSMatt Macy 
380eda14cbcSMatt Macy 	if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
381eda14cbcSMatt Macy 		return (-1);
382eda14cbcSMatt Macy 
383eda14cbcSMatt Macy 	while ((cn = uu_avl_walk_next(walk)) != NULL) {
384716fd348SMartin Matuska 		if (zfs_unshare(cn->cn_handle, NULL, proto) != 0)
385eda14cbcSMatt Macy 			ret = -1;
386eda14cbcSMatt Macy 	}
387eda14cbcSMatt Macy 
388716fd348SMartin Matuska 	for (const enum sa_protocol *p = proto; *p != SA_NO_PROTOCOL; ++p)
389716fd348SMartin Matuska 		sa_commit_shares(*p);
390eda14cbcSMatt Macy 	uu_avl_walk_end(walk);
391eda14cbcSMatt Macy 
392eda14cbcSMatt Macy 	return (ret);
393eda14cbcSMatt Macy }
394eda14cbcSMatt Macy 
395eda14cbcSMatt Macy /*
396eda14cbcSMatt Macy  * Check if there is any child exported to a local zone in a given changelist.
397eda14cbcSMatt Macy  * This information has already been recorded while gathering the changelist
398eda14cbcSMatt Macy  * via changelist_gather().
399eda14cbcSMatt Macy  */
400eda14cbcSMatt Macy int
401eda14cbcSMatt Macy changelist_haszonedchild(prop_changelist_t *clp)
402eda14cbcSMatt Macy {
403eda14cbcSMatt Macy 	return (clp->cl_haszonedchild);
404eda14cbcSMatt Macy }
405eda14cbcSMatt Macy 
406eda14cbcSMatt Macy /*
407eda14cbcSMatt Macy  * Remove a node from a gathered list.
408eda14cbcSMatt Macy  */
409eda14cbcSMatt Macy void
410eda14cbcSMatt Macy changelist_remove(prop_changelist_t *clp, const char *name)
411eda14cbcSMatt Macy {
412eda14cbcSMatt Macy 	prop_changenode_t *cn;
413eda14cbcSMatt Macy 	uu_avl_walk_t *walk;
414eda14cbcSMatt Macy 
415eda14cbcSMatt Macy 	if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
416eda14cbcSMatt Macy 		return;
417eda14cbcSMatt Macy 
418eda14cbcSMatt Macy 	while ((cn = uu_avl_walk_next(walk)) != NULL) {
419eda14cbcSMatt Macy 		if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
420eda14cbcSMatt Macy 			uu_avl_remove(clp->cl_tree, cn);
421eda14cbcSMatt Macy 			zfs_close(cn->cn_handle);
422eda14cbcSMatt Macy 			free(cn);
423eda14cbcSMatt Macy 			uu_avl_walk_end(walk);
424eda14cbcSMatt Macy 			return;
425eda14cbcSMatt Macy 		}
426eda14cbcSMatt Macy 	}
427eda14cbcSMatt Macy 
428eda14cbcSMatt Macy 	uu_avl_walk_end(walk);
429eda14cbcSMatt Macy }
430eda14cbcSMatt Macy 
431eda14cbcSMatt Macy /*
432eda14cbcSMatt Macy  * Release any memory associated with a changelist.
433eda14cbcSMatt Macy  */
434eda14cbcSMatt Macy void
435eda14cbcSMatt Macy changelist_free(prop_changelist_t *clp)
436eda14cbcSMatt Macy {
437eda14cbcSMatt Macy 	prop_changenode_t *cn;
438eda14cbcSMatt Macy 
439eda14cbcSMatt Macy 	if (clp->cl_tree) {
440eda14cbcSMatt Macy 		uu_avl_walk_t *walk;
441eda14cbcSMatt Macy 
442eda14cbcSMatt Macy 		if ((walk = uu_avl_walk_start(clp->cl_tree,
443eda14cbcSMatt Macy 		    UU_WALK_ROBUST)) == NULL)
444eda14cbcSMatt Macy 			return;
445eda14cbcSMatt Macy 
446eda14cbcSMatt Macy 		while ((cn = uu_avl_walk_next(walk)) != NULL) {
447eda14cbcSMatt Macy 			uu_avl_remove(clp->cl_tree, cn);
448eda14cbcSMatt Macy 			zfs_close(cn->cn_handle);
449eda14cbcSMatt Macy 			free(cn);
450eda14cbcSMatt Macy 		}
451eda14cbcSMatt Macy 
452eda14cbcSMatt Macy 		uu_avl_walk_end(walk);
453eda14cbcSMatt Macy 		uu_avl_destroy(clp->cl_tree);
454eda14cbcSMatt Macy 	}
455eda14cbcSMatt Macy 	if (clp->cl_pool)
456eda14cbcSMatt Macy 		uu_avl_pool_destroy(clp->cl_pool);
457eda14cbcSMatt Macy 
458eda14cbcSMatt Macy 	free(clp);
459eda14cbcSMatt Macy }
460eda14cbcSMatt Macy 
461eda14cbcSMatt Macy /*
462eda14cbcSMatt Macy  * Add one dataset to changelist
463eda14cbcSMatt Macy  */
464eda14cbcSMatt Macy static int
465eda14cbcSMatt Macy changelist_add_mounted(zfs_handle_t *zhp, void *data)
466eda14cbcSMatt Macy {
467eda14cbcSMatt Macy 	prop_changelist_t *clp = data;
468eda14cbcSMatt Macy 	prop_changenode_t *cn;
469eda14cbcSMatt Macy 	uu_avl_index_t idx;
470eda14cbcSMatt Macy 
471eda14cbcSMatt Macy 	ASSERT3U(clp->cl_prop, ==, ZFS_PROP_MOUNTPOINT);
472eda14cbcSMatt Macy 
473716fd348SMartin Matuska 	cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
474eda14cbcSMatt Macy 	cn->cn_handle = zhp;
475eda14cbcSMatt Macy 	cn->cn_mounted = zfs_is_mounted(zhp, NULL);
476eda14cbcSMatt Macy 	ASSERT3U(cn->cn_mounted, ==, B_TRUE);
477716fd348SMartin Matuska 	cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
478eda14cbcSMatt Macy 	cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
479eda14cbcSMatt Macy 	cn->cn_needpost = B_TRUE;
480eda14cbcSMatt Macy 
481eda14cbcSMatt Macy 	/* Indicate if any child is exported to a local zone. */
482eda14cbcSMatt Macy 	if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
483eda14cbcSMatt Macy 		clp->cl_haszonedchild = B_TRUE;
484eda14cbcSMatt Macy 
485eda14cbcSMatt Macy 	uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
486eda14cbcSMatt Macy 
487eda14cbcSMatt Macy 	if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
488eda14cbcSMatt Macy 		uu_avl_insert(clp->cl_tree, cn, idx);
489eda14cbcSMatt Macy 	} else {
490eda14cbcSMatt Macy 		free(cn);
491eda14cbcSMatt Macy 		zfs_close(zhp);
492eda14cbcSMatt Macy 	}
493eda14cbcSMatt Macy 
494eda14cbcSMatt Macy 	return (0);
495eda14cbcSMatt Macy }
496eda14cbcSMatt Macy 
497eda14cbcSMatt Macy static int
498eda14cbcSMatt Macy change_one(zfs_handle_t *zhp, void *data)
499eda14cbcSMatt Macy {
500eda14cbcSMatt Macy 	prop_changelist_t *clp = data;
501eda14cbcSMatt Macy 	char property[ZFS_MAXPROPLEN];
502eda14cbcSMatt Macy 	char where[64];
503eda14cbcSMatt Macy 	prop_changenode_t *cn = NULL;
504eda14cbcSMatt Macy 	zprop_source_t sourcetype = ZPROP_SRC_NONE;
505eda14cbcSMatt Macy 	zprop_source_t share_sourcetype = ZPROP_SRC_NONE;
506eda14cbcSMatt Macy 	int ret = 0;
507eda14cbcSMatt Macy 
508eda14cbcSMatt Macy 	/*
509eda14cbcSMatt Macy 	 * We only want to unmount/unshare those filesystems that may inherit
510eda14cbcSMatt Macy 	 * from the target filesystem.  If we find any filesystem with a
511eda14cbcSMatt Macy 	 * locally set mountpoint, we ignore any children since changing the
512eda14cbcSMatt Macy 	 * property will not affect them.  If this is a rename, we iterate
513eda14cbcSMatt Macy 	 * over all children regardless, since we need them unmounted in
514eda14cbcSMatt Macy 	 * order to do the rename.  Also, if this is a volume and we're doing
515eda14cbcSMatt Macy 	 * a rename, then always add it to the changelist.
516eda14cbcSMatt Macy 	 */
517eda14cbcSMatt Macy 
518eda14cbcSMatt Macy 	if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
519eda14cbcSMatt Macy 	    zfs_prop_get(zhp, clp->cl_prop, property,
520eda14cbcSMatt Macy 	    sizeof (property), &sourcetype, where, sizeof (where),
521eda14cbcSMatt Macy 	    B_FALSE) != 0) {
522eda14cbcSMatt Macy 		goto out;
523eda14cbcSMatt Macy 	}
524eda14cbcSMatt Macy 
525eda14cbcSMatt Macy 	/*
526eda14cbcSMatt Macy 	 * If we are "watching" sharenfs or sharesmb
527eda14cbcSMatt Macy 	 * then check out the companion property which is tracked
528eda14cbcSMatt Macy 	 * in cl_shareprop
529eda14cbcSMatt Macy 	 */
530eda14cbcSMatt Macy 	if (clp->cl_shareprop != ZPROP_INVAL &&
531eda14cbcSMatt Macy 	    zfs_prop_get(zhp, clp->cl_shareprop, property,
532eda14cbcSMatt Macy 	    sizeof (property), &share_sourcetype, where, sizeof (where),
533eda14cbcSMatt Macy 	    B_FALSE) != 0) {
534eda14cbcSMatt Macy 		goto out;
535eda14cbcSMatt Macy 	}
536eda14cbcSMatt Macy 
537eda14cbcSMatt Macy 	if (clp->cl_alldependents || clp->cl_allchildren ||
538eda14cbcSMatt Macy 	    sourcetype == ZPROP_SRC_DEFAULT ||
539eda14cbcSMatt Macy 	    sourcetype == ZPROP_SRC_INHERITED ||
540eda14cbcSMatt Macy 	    (clp->cl_shareprop != ZPROP_INVAL &&
541eda14cbcSMatt Macy 	    (share_sourcetype == ZPROP_SRC_DEFAULT ||
542eda14cbcSMatt Macy 	    share_sourcetype == ZPROP_SRC_INHERITED))) {
543716fd348SMartin Matuska 		cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
544eda14cbcSMatt Macy 		cn->cn_handle = zhp;
545eda14cbcSMatt Macy 		cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
546eda14cbcSMatt Macy 		    zfs_is_mounted(zhp, NULL);
547716fd348SMartin Matuska 		cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
548eda14cbcSMatt Macy 		cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
549eda14cbcSMatt Macy 		cn->cn_needpost = B_TRUE;
550eda14cbcSMatt Macy 
551eda14cbcSMatt Macy 		/* Indicate if any child is exported to a local zone. */
552eda14cbcSMatt Macy 		if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
553eda14cbcSMatt Macy 			clp->cl_haszonedchild = B_TRUE;
554eda14cbcSMatt Macy 
555eda14cbcSMatt Macy 		uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
556eda14cbcSMatt Macy 
557eda14cbcSMatt Macy 		uu_avl_index_t idx;
558eda14cbcSMatt Macy 
559eda14cbcSMatt Macy 		if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
560eda14cbcSMatt Macy 			uu_avl_insert(clp->cl_tree, cn, idx);
561eda14cbcSMatt Macy 		} else {
562eda14cbcSMatt Macy 			free(cn);
563eda14cbcSMatt Macy 			cn = NULL;
564eda14cbcSMatt Macy 		}
565eda14cbcSMatt Macy 
566*dd215568SMartin Matuska 		if (!clp->cl_alldependents) {
567*dd215568SMartin Matuska 			if (clp->cl_prop != ZFS_PROP_MOUNTPOINT) {
568*dd215568SMartin Matuska 				ret = zfs_iter_filesystems_v2(zhp, 0,
569*dd215568SMartin Matuska 				    change_one, data);
570*dd215568SMartin Matuska 			} else {
571*dd215568SMartin Matuska 				ret = zfs_iter_children_v2(zhp, 0, change_one,
572*dd215568SMartin Matuska 				    data);
573*dd215568SMartin Matuska 			}
574*dd215568SMartin Matuska 		}
575eda14cbcSMatt Macy 
576eda14cbcSMatt Macy 		/*
577eda14cbcSMatt Macy 		 * If we added the handle to the changelist, we will re-use it
578eda14cbcSMatt Macy 		 * later so return without closing it.
579eda14cbcSMatt Macy 		 */
580eda14cbcSMatt Macy 		if (cn != NULL)
581eda14cbcSMatt Macy 			return (ret);
582eda14cbcSMatt Macy 	}
583eda14cbcSMatt Macy 
584eda14cbcSMatt Macy out:
585eda14cbcSMatt Macy 	zfs_close(zhp);
586eda14cbcSMatt Macy 	return (ret);
587eda14cbcSMatt Macy }
588eda14cbcSMatt Macy 
589eda14cbcSMatt Macy static int
590eda14cbcSMatt Macy compare_props(const void *a, const void *b, zfs_prop_t prop)
591eda14cbcSMatt Macy {
592eda14cbcSMatt Macy 	const prop_changenode_t *ca = a;
593eda14cbcSMatt Macy 	const prop_changenode_t *cb = b;
594eda14cbcSMatt Macy 
595eda14cbcSMatt Macy 	char propa[MAXPATHLEN];
596eda14cbcSMatt Macy 	char propb[MAXPATHLEN];
597eda14cbcSMatt Macy 
598eda14cbcSMatt Macy 	boolean_t haspropa, haspropb;
599eda14cbcSMatt Macy 
600eda14cbcSMatt Macy 	haspropa = (zfs_prop_get(ca->cn_handle, prop, propa, sizeof (propa),
601eda14cbcSMatt Macy 	    NULL, NULL, 0, B_FALSE) == 0);
602eda14cbcSMatt Macy 	haspropb = (zfs_prop_get(cb->cn_handle, prop, propb, sizeof (propb),
603eda14cbcSMatt Macy 	    NULL, NULL, 0, B_FALSE) == 0);
604eda14cbcSMatt Macy 
605eda14cbcSMatt Macy 	if (!haspropa && haspropb)
606eda14cbcSMatt Macy 		return (-1);
607eda14cbcSMatt Macy 	else if (haspropa && !haspropb)
608eda14cbcSMatt Macy 		return (1);
609eda14cbcSMatt Macy 	else if (!haspropa && !haspropb)
610eda14cbcSMatt Macy 		return (0);
611eda14cbcSMatt Macy 	else
612eda14cbcSMatt Macy 		return (strcmp(propb, propa));
613eda14cbcSMatt Macy }
614eda14cbcSMatt Macy 
615eda14cbcSMatt Macy static int
616eda14cbcSMatt Macy compare_mountpoints(const void *a, const void *b, void *unused)
617eda14cbcSMatt Macy {
618eda14cbcSMatt Macy 	/*
619eda14cbcSMatt Macy 	 * When unsharing or unmounting filesystems, we need to do it in
620eda14cbcSMatt Macy 	 * mountpoint order.  This allows the user to have a mountpoint
621eda14cbcSMatt Macy 	 * hierarchy that is different from the dataset hierarchy, and still
622eda14cbcSMatt Macy 	 * allow it to be changed.
623eda14cbcSMatt Macy 	 */
624e92ffd9bSMartin Matuska 	(void) unused;
625eda14cbcSMatt Macy 	return (compare_props(a, b, ZFS_PROP_MOUNTPOINT));
626eda14cbcSMatt Macy }
627eda14cbcSMatt Macy 
628eda14cbcSMatt Macy static int
629eda14cbcSMatt Macy compare_dataset_names(const void *a, const void *b, void *unused)
630eda14cbcSMatt Macy {
631e92ffd9bSMartin Matuska 	(void) unused;
632eda14cbcSMatt Macy 	return (compare_props(a, b, ZFS_PROP_NAME));
633eda14cbcSMatt Macy }
634eda14cbcSMatt Macy 
635eda14cbcSMatt Macy /*
636eda14cbcSMatt Macy  * Given a ZFS handle and a property, construct a complete list of datasets
637eda14cbcSMatt Macy  * that need to be modified as part of this process.  For anything but the
638eda14cbcSMatt Macy  * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
639eda14cbcSMatt Macy  * Otherwise, we iterate over all children and look for any datasets that
640eda14cbcSMatt Macy  * inherit the property.  For each such dataset, we add it to the list and
641eda14cbcSMatt Macy  * mark whether it was shared beforehand.
642eda14cbcSMatt Macy  */
643eda14cbcSMatt Macy prop_changelist_t *
644eda14cbcSMatt Macy changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
645eda14cbcSMatt Macy     int mnt_flags)
646eda14cbcSMatt Macy {
647eda14cbcSMatt Macy 	prop_changelist_t *clp;
648eda14cbcSMatt Macy 	prop_changenode_t *cn;
649eda14cbcSMatt Macy 	zfs_handle_t *temp;
650eda14cbcSMatt Macy 	char property[ZFS_MAXPROPLEN];
651eda14cbcSMatt Macy 	boolean_t legacy = B_FALSE;
652eda14cbcSMatt Macy 
653716fd348SMartin Matuska 	clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t));
654eda14cbcSMatt Macy 
655eda14cbcSMatt Macy 	/*
656eda14cbcSMatt Macy 	 * For mountpoint-related tasks, we want to sort everything by
657eda14cbcSMatt Macy 	 * mountpoint, so that we mount and unmount them in the appropriate
658eda14cbcSMatt Macy 	 * order, regardless of their position in the hierarchy.
659eda14cbcSMatt Macy 	 */
660eda14cbcSMatt Macy 	if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
661eda14cbcSMatt Macy 	    prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
662eda14cbcSMatt Macy 	    prop == ZFS_PROP_SHARESMB) {
663eda14cbcSMatt Macy 
664eda14cbcSMatt Macy 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
665eda14cbcSMatt Macy 		    property, sizeof (property),
666eda14cbcSMatt Macy 		    NULL, NULL, 0, B_FALSE) == 0 &&
667eda14cbcSMatt Macy 		    (strcmp(property, "legacy") == 0 ||
668eda14cbcSMatt Macy 		    strcmp(property, "none") == 0)) {
669eda14cbcSMatt Macy 			legacy = B_TRUE;
670eda14cbcSMatt Macy 		}
671eda14cbcSMatt Macy 	}
672eda14cbcSMatt Macy 
673eda14cbcSMatt Macy 	clp->cl_pool = uu_avl_pool_create("changelist_pool",
674eda14cbcSMatt Macy 	    sizeof (prop_changenode_t),
675eda14cbcSMatt Macy 	    offsetof(prop_changenode_t, cn_treenode),
676eda14cbcSMatt Macy 	    legacy ? compare_dataset_names : compare_mountpoints, 0);
677eda14cbcSMatt Macy 	if (clp->cl_pool == NULL) {
678eda14cbcSMatt Macy 		assert(uu_error() == UU_ERROR_NO_MEMORY);
679eda14cbcSMatt Macy 		(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
680eda14cbcSMatt Macy 		changelist_free(clp);
681eda14cbcSMatt Macy 		return (NULL);
682eda14cbcSMatt Macy 	}
683eda14cbcSMatt Macy 
684eda14cbcSMatt Macy 	clp->cl_tree = uu_avl_create(clp->cl_pool, NULL, UU_DEFAULT);
685eda14cbcSMatt Macy 	clp->cl_gflags = gather_flags;
686eda14cbcSMatt Macy 	clp->cl_mflags = mnt_flags;
687eda14cbcSMatt Macy 
688eda14cbcSMatt Macy 	if (clp->cl_tree == NULL) {
689eda14cbcSMatt Macy 		assert(uu_error() == UU_ERROR_NO_MEMORY);
690eda14cbcSMatt Macy 		(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
691eda14cbcSMatt Macy 		changelist_free(clp);
692eda14cbcSMatt Macy 		return (NULL);
693eda14cbcSMatt Macy 	}
694eda14cbcSMatt Macy 
695eda14cbcSMatt Macy 	/*
696eda14cbcSMatt Macy 	 * If this is a rename or the 'zoned' property, we pretend we're
697eda14cbcSMatt Macy 	 * changing the mountpoint and flag it so we can catch all children in
698eda14cbcSMatt Macy 	 * change_one().
699eda14cbcSMatt Macy 	 *
700eda14cbcSMatt Macy 	 * Flag cl_alldependents to catch all children plus the dependents
701eda14cbcSMatt Macy 	 * (clones) that are not in the hierarchy.
702eda14cbcSMatt Macy 	 */
703eda14cbcSMatt Macy 	if (prop == ZFS_PROP_NAME) {
704eda14cbcSMatt Macy 		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
705eda14cbcSMatt Macy 		clp->cl_alldependents = B_TRUE;
706eda14cbcSMatt Macy 	} else if (prop == ZFS_PROP_ZONED) {
707eda14cbcSMatt Macy 		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
708eda14cbcSMatt Macy 		clp->cl_allchildren = B_TRUE;
709eda14cbcSMatt Macy 	} else if (prop == ZFS_PROP_CANMOUNT) {
710eda14cbcSMatt Macy 		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
711eda14cbcSMatt Macy 	} else if (prop == ZFS_PROP_VOLSIZE) {
712eda14cbcSMatt Macy 		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
713eda14cbcSMatt Macy 	} else {
714eda14cbcSMatt Macy 		clp->cl_prop = prop;
715eda14cbcSMatt Macy 	}
716eda14cbcSMatt Macy 	clp->cl_realprop = prop;
717eda14cbcSMatt Macy 
718eda14cbcSMatt Macy 	if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
719eda14cbcSMatt Macy 	    clp->cl_prop != ZFS_PROP_SHARENFS &&
720eda14cbcSMatt Macy 	    clp->cl_prop != ZFS_PROP_SHARESMB)
721eda14cbcSMatt Macy 		return (clp);
722eda14cbcSMatt Macy 
723eda14cbcSMatt Macy 	/*
724eda14cbcSMatt Macy 	 * If watching SHARENFS or SHARESMB then
725eda14cbcSMatt Macy 	 * also watch its companion property.
726eda14cbcSMatt Macy 	 */
727eda14cbcSMatt Macy 	if (clp->cl_prop == ZFS_PROP_SHARENFS)
728eda14cbcSMatt Macy 		clp->cl_shareprop = ZFS_PROP_SHARESMB;
729eda14cbcSMatt Macy 	else if (clp->cl_prop == ZFS_PROP_SHARESMB)
730eda14cbcSMatt Macy 		clp->cl_shareprop = ZFS_PROP_SHARENFS;
731eda14cbcSMatt Macy 
732eda14cbcSMatt Macy 	if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
733eda14cbcSMatt Macy 	    (clp->cl_gflags & CL_GATHER_ITER_MOUNTED)) {
734eda14cbcSMatt Macy 		/*
735eda14cbcSMatt Macy 		 * Instead of iterating through all of the dataset children we
736eda14cbcSMatt Macy 		 * gather mounted dataset children from MNTTAB
737eda14cbcSMatt Macy 		 */
738eda14cbcSMatt Macy 		if (zfs_iter_mounted(zhp, changelist_add_mounted, clp) != 0) {
739eda14cbcSMatt Macy 			changelist_free(clp);
740eda14cbcSMatt Macy 			return (NULL);
741eda14cbcSMatt Macy 		}
742eda14cbcSMatt Macy 	} else if (clp->cl_alldependents) {
743d411c1d6SMartin Matuska 		if (zfs_iter_dependents_v2(zhp, 0, B_TRUE, change_one,
744d411c1d6SMartin Matuska 		    clp) != 0) {
745eda14cbcSMatt Macy 			changelist_free(clp);
746eda14cbcSMatt Macy 			return (NULL);
747eda14cbcSMatt Macy 		}
748*dd215568SMartin Matuska 	} else if (clp->cl_prop != ZFS_PROP_MOUNTPOINT) {
749*dd215568SMartin Matuska 		if (zfs_iter_filesystems_v2(zhp, 0, change_one, clp) != 0) {
750*dd215568SMartin Matuska 			changelist_free(clp);
751*dd215568SMartin Matuska 			return (NULL);
752*dd215568SMartin Matuska 		}
753d411c1d6SMartin Matuska 	} else if (zfs_iter_children_v2(zhp, 0, change_one, clp) != 0) {
754eda14cbcSMatt Macy 		changelist_free(clp);
755eda14cbcSMatt Macy 		return (NULL);
756eda14cbcSMatt Macy 	}
757eda14cbcSMatt Macy 
758eda14cbcSMatt Macy 	/*
759eda14cbcSMatt Macy 	 * We have to re-open ourselves because we auto-close all the handles
760eda14cbcSMatt Macy 	 * and can't tell the difference.
761eda14cbcSMatt Macy 	 */
762eda14cbcSMatt Macy 	if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
763eda14cbcSMatt Macy 	    ZFS_TYPE_DATASET)) == NULL) {
764eda14cbcSMatt Macy 		changelist_free(clp);
765eda14cbcSMatt Macy 		return (NULL);
766eda14cbcSMatt Macy 	}
767eda14cbcSMatt Macy 
768eda14cbcSMatt Macy 	/*
769eda14cbcSMatt Macy 	 * Always add ourself to the list.  We add ourselves to the end so that
770eda14cbcSMatt Macy 	 * we're the last to be unmounted.
771eda14cbcSMatt Macy 	 */
772716fd348SMartin Matuska 	cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t));
773eda14cbcSMatt Macy 	cn->cn_handle = temp;
774eda14cbcSMatt Macy 	cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
775eda14cbcSMatt Macy 	    zfs_is_mounted(temp, NULL);
776716fd348SMartin Matuska 	cn->cn_shared = zfs_is_shared(temp, NULL, NULL);
777eda14cbcSMatt Macy 	cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
778eda14cbcSMatt Macy 	cn->cn_needpost = B_TRUE;
779eda14cbcSMatt Macy 
780eda14cbcSMatt Macy 	uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
781eda14cbcSMatt Macy 	uu_avl_index_t idx;
782eda14cbcSMatt Macy 	if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
783eda14cbcSMatt Macy 		uu_avl_insert(clp->cl_tree, cn, idx);
784eda14cbcSMatt Macy 	} else {
785eda14cbcSMatt Macy 		free(cn);
786eda14cbcSMatt Macy 		zfs_close(temp);
787eda14cbcSMatt Macy 	}
788eda14cbcSMatt Macy 
789eda14cbcSMatt Macy 	/*
790eda14cbcSMatt Macy 	 * If the mountpoint property was previously 'legacy', or 'none',
791eda14cbcSMatt Macy 	 * record it as the behavior of changelist_postfix() will be different.
792eda14cbcSMatt Macy 	 */
793eda14cbcSMatt Macy 	if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
794eda14cbcSMatt Macy 		/*
795eda14cbcSMatt Macy 		 * do not automatically mount ex-legacy datasets if
796eda14cbcSMatt Macy 		 * we specifically set canmount to noauto
797eda14cbcSMatt Macy 		 */
798eda14cbcSMatt Macy 		if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
799eda14cbcSMatt Macy 		    ZFS_CANMOUNT_NOAUTO)
800eda14cbcSMatt Macy 			clp->cl_waslegacy = B_TRUE;
801eda14cbcSMatt Macy 	}
802eda14cbcSMatt Macy 
803eda14cbcSMatt Macy 	return (clp);
804eda14cbcSMatt Macy }
805