ZIL: Tune some assertions.
[zfs.git] / lib / libzfs / libzfs_changelist.c
blobdd14c570ec036a7e05448aa0113a587d6c67ffa0
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
26 * Portions Copyright 2007 Ramprakash Jelari
27 * Copyright (c) 2014, 2020 by Delphix. All rights reserved.
28 * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
29 * Copyright (c) 2018 Datto Inc.
32 #include <libintl.h>
33 #include <libuutil.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <zone.h>
40 #include <libzfs.h>
42 #include "libzfs_impl.h"
45 * Structure to keep track of dataset state. Before changing the 'sharenfs' or
46 * 'mountpoint' property, we record whether the filesystem was previously
47 * mounted/shared. This prior state dictates whether we remount/reshare the
48 * dataset after the property has been changed.
50 * The interface consists of the following sequence of functions:
52 * changelist_gather()
53 * changelist_prefix()
54 * < change property >
55 * changelist_postfix()
56 * changelist_free()
58 * Other interfaces:
60 * changelist_remove() - remove a node from a gathered list
61 * changelist_rename() - renames all datasets appropriately when doing a rename
62 * changelist_unshare() - unshares all the nodes in a given changelist
63 * changelist_haszonedchild() - check if there is any child exported to
64 * a local zone
66 typedef struct prop_changenode {
67 zfs_handle_t *cn_handle;
68 int cn_shared;
69 int cn_mounted;
70 int cn_zoned;
71 boolean_t cn_needpost; /* is postfix() needed? */
72 uu_avl_node_t cn_treenode;
73 } prop_changenode_t;
75 struct prop_changelist {
76 zfs_prop_t cl_prop;
77 zfs_prop_t cl_realprop;
78 zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */
79 uu_avl_pool_t *cl_pool;
80 uu_avl_t *cl_tree;
81 boolean_t cl_waslegacy;
82 boolean_t cl_allchildren;
83 boolean_t cl_alldependents;
84 int cl_mflags; /* Mount flags */
85 int cl_gflags; /* Gather request flags */
86 boolean_t cl_haszonedchild;
90 * If the property is 'mountpoint', go through and unmount filesystems as
91 * necessary. We don't do the same for 'sharenfs', because we can just re-share
92 * with different options without interrupting service. We do handle 'sharesmb'
93 * since there may be old resource names that need to be removed.
95 int
96 changelist_prefix(prop_changelist_t *clp)
98 prop_changenode_t *cn;
99 uu_avl_walk_t *walk;
100 int ret = 0;
101 const enum sa_protocol smb[] = {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
102 boolean_t commit_smb_shares = B_FALSE;
104 if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
105 clp->cl_prop != ZFS_PROP_SHARESMB)
106 return (0);
108 if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
109 return (-1);
111 while ((cn = uu_avl_walk_next(walk)) != NULL) {
113 /* if a previous loop failed, set the remaining to false */
114 if (ret == -1) {
115 cn->cn_needpost = B_FALSE;
116 continue;
120 * If we are in the global zone, but this dataset is exported
121 * to a local zone, do nothing.
123 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
124 continue;
126 if (!ZFS_IS_VOLUME(cn->cn_handle)) {
128 * Do the property specific processing.
130 switch (clp->cl_prop) {
131 case ZFS_PROP_MOUNTPOINT:
132 if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)
133 break;
134 if (zfs_unmount(cn->cn_handle, NULL,
135 clp->cl_mflags) != 0) {
136 ret = -1;
137 cn->cn_needpost = B_FALSE;
139 break;
140 case ZFS_PROP_SHARESMB:
141 (void) zfs_unshare(cn->cn_handle, NULL,
142 smb);
143 commit_smb_shares = B_TRUE;
144 break;
146 default:
147 break;
152 if (commit_smb_shares)
153 zfs_commit_shares(smb);
154 uu_avl_walk_end(walk);
156 if (ret == -1)
157 (void) changelist_postfix(clp);
159 return (ret);
163 * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or
164 * reshare the filesystems as necessary. In changelist_gather() we recorded
165 * whether the filesystem was previously shared or mounted. The action we take
166 * depends on the previous state, and whether the value was previously 'legacy'.
167 * For non-legacy properties, we only remount/reshare the filesystem if it was
168 * previously mounted/shared. Otherwise, we always remount/reshare the
169 * filesystem.
172 changelist_postfix(prop_changelist_t *clp)
174 prop_changenode_t *cn;
175 uu_avl_walk_t *walk;
176 char shareopts[ZFS_MAXPROPLEN];
177 int errors = 0;
178 boolean_t commit_smb_shares = B_FALSE;
179 boolean_t commit_nfs_shares = B_FALSE;
182 * If we're changing the mountpoint, attempt to destroy the underlying
183 * mountpoint. All other datasets will have inherited from this dataset
184 * (in which case their mountpoints exist in the filesystem in the new
185 * location), or have explicit mountpoints set (in which case they won't
186 * be in the changelist).
188 if ((cn = uu_avl_last(clp->cl_tree)) == NULL)
189 return (0);
191 if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
192 !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT))
193 remove_mountpoint(cn->cn_handle);
196 * We walk the datasets in reverse, because we want to mount any parent
197 * datasets before mounting the children. We walk all datasets even if
198 * there are errors.
200 if ((walk = uu_avl_walk_start(clp->cl_tree,
201 UU_WALK_REVERSE | UU_WALK_ROBUST)) == NULL)
202 return (-1);
204 while ((cn = uu_avl_walk_next(walk)) != NULL) {
206 boolean_t sharenfs;
207 boolean_t sharesmb;
208 boolean_t mounted;
209 boolean_t needs_key;
212 * If we are in the global zone, but this dataset is exported
213 * to a local zone, do nothing.
215 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
216 continue;
218 /* Only do post-processing if it's required */
219 if (!cn->cn_needpost)
220 continue;
221 cn->cn_needpost = B_FALSE;
223 zfs_refresh_properties(cn->cn_handle);
225 if (ZFS_IS_VOLUME(cn->cn_handle))
226 continue;
229 * Remount if previously mounted or mountpoint was legacy,
230 * or sharenfs or sharesmb property is set.
232 sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS,
233 shareopts, sizeof (shareopts), NULL, NULL, 0,
234 B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
236 sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB,
237 shareopts, sizeof (shareopts), NULL, NULL, 0,
238 B_FALSE) == 0) && (strcmp(shareopts, "off") != 0));
240 needs_key = (zfs_prop_get_int(cn->cn_handle,
241 ZFS_PROP_KEYSTATUS) == ZFS_KEYSTATUS_UNAVAILABLE);
243 mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) ||
244 zfs_is_mounted(cn->cn_handle, NULL);
246 if (!mounted && !needs_key && (cn->cn_mounted ||
247 ((sharenfs || sharesmb || clp->cl_waslegacy) &&
248 (zfs_prop_get_int(cn->cn_handle,
249 ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) {
251 if (zfs_mount(cn->cn_handle, NULL, 0) != 0)
252 errors++;
253 else
254 mounted = TRUE;
258 * If the file system is mounted we always re-share even
259 * if the filesystem is currently shared, so that we can
260 * adopt any new options.
262 const enum sa_protocol nfs[] =
263 {SA_PROTOCOL_NFS, SA_NO_PROTOCOL};
264 if (sharenfs && mounted) {
265 errors += zfs_share(cn->cn_handle, nfs);
266 commit_nfs_shares = B_TRUE;
267 } else if (cn->cn_shared || clp->cl_waslegacy) {
268 errors += zfs_unshare(cn->cn_handle, NULL, nfs);
269 commit_nfs_shares = B_TRUE;
271 const enum sa_protocol smb[] =
272 {SA_PROTOCOL_SMB, SA_NO_PROTOCOL};
273 if (sharesmb && mounted) {
274 errors += zfs_share(cn->cn_handle, smb);
275 commit_smb_shares = B_TRUE;
276 } else if (cn->cn_shared || clp->cl_waslegacy) {
277 errors += zfs_unshare(cn->cn_handle, NULL, smb);
278 commit_smb_shares = B_TRUE;
282 enum sa_protocol proto[SA_PROTOCOL_COUNT + 1], *p = proto;
283 if (commit_nfs_shares)
284 *p++ = SA_PROTOCOL_NFS;
285 if (commit_smb_shares)
286 *p++ = SA_PROTOCOL_SMB;
287 *p++ = SA_NO_PROTOCOL;
288 zfs_commit_shares(proto);
289 uu_avl_walk_end(walk);
291 return (errors ? -1 : 0);
295 * Is this "dataset" a child of "parent"?
297 static boolean_t
298 isa_child_of(const char *dataset, const char *parent)
300 int len;
302 len = strlen(parent);
304 if (strncmp(dataset, parent, len) == 0 &&
305 (dataset[len] == '@' || dataset[len] == '/' ||
306 dataset[len] == '\0'))
307 return (B_TRUE);
308 else
309 return (B_FALSE);
314 * If we rename a filesystem, child filesystem handles are no longer valid
315 * since we identify each dataset by its name in the ZFS namespace. As a
316 * result, we have to go through and fix up all the names appropriately. We
317 * could do this automatically if libzfs kept track of all open handles, but
318 * this is a lot less work.
320 void
321 changelist_rename(prop_changelist_t *clp, const char *src, const char *dst)
323 prop_changenode_t *cn;
324 uu_avl_walk_t *walk;
325 char newname[ZFS_MAX_DATASET_NAME_LEN];
327 if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
328 return;
330 while ((cn = uu_avl_walk_next(walk)) != NULL) {
332 * Do not rename a clone that's not in the source hierarchy.
334 if (!isa_child_of(cn->cn_handle->zfs_name, src))
335 continue;
338 * Destroy the previous mountpoint if needed.
340 remove_mountpoint(cn->cn_handle);
342 (void) strlcpy(newname, dst, sizeof (newname));
343 (void) strlcat(newname, cn->cn_handle->zfs_name + strlen(src),
344 sizeof (newname));
346 (void) strlcpy(cn->cn_handle->zfs_name, newname,
347 sizeof (cn->cn_handle->zfs_name));
350 uu_avl_walk_end(walk);
354 * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property,
355 * unshare all the datasets in the list.
358 changelist_unshare(prop_changelist_t *clp, const enum sa_protocol *proto)
360 prop_changenode_t *cn;
361 uu_avl_walk_t *walk;
362 int ret = 0;
364 if (clp->cl_prop != ZFS_PROP_SHARENFS &&
365 clp->cl_prop != ZFS_PROP_SHARESMB)
366 return (0);
368 if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
369 return (-1);
371 while ((cn = uu_avl_walk_next(walk)) != NULL) {
372 if (zfs_unshare(cn->cn_handle, NULL, proto) != 0)
373 ret = -1;
376 for (const enum sa_protocol *p = proto; *p != SA_NO_PROTOCOL; ++p)
377 sa_commit_shares(*p);
378 uu_avl_walk_end(walk);
380 return (ret);
384 * Check if there is any child exported to a local zone in a given changelist.
385 * This information has already been recorded while gathering the changelist
386 * via changelist_gather().
389 changelist_haszonedchild(prop_changelist_t *clp)
391 return (clp->cl_haszonedchild);
395 * Remove a node from a gathered list.
397 void
398 changelist_remove(prop_changelist_t *clp, const char *name)
400 prop_changenode_t *cn;
401 uu_avl_walk_t *walk;
403 if ((walk = uu_avl_walk_start(clp->cl_tree, UU_WALK_ROBUST)) == NULL)
404 return;
406 while ((cn = uu_avl_walk_next(walk)) != NULL) {
407 if (strcmp(cn->cn_handle->zfs_name, name) == 0) {
408 uu_avl_remove(clp->cl_tree, cn);
409 zfs_close(cn->cn_handle);
410 free(cn);
411 uu_avl_walk_end(walk);
412 return;
416 uu_avl_walk_end(walk);
420 * Release any memory associated with a changelist.
422 void
423 changelist_free(prop_changelist_t *clp)
425 prop_changenode_t *cn;
427 if (clp->cl_tree) {
428 uu_avl_walk_t *walk;
430 if ((walk = uu_avl_walk_start(clp->cl_tree,
431 UU_WALK_ROBUST)) == NULL)
432 return;
434 while ((cn = uu_avl_walk_next(walk)) != NULL) {
435 uu_avl_remove(clp->cl_tree, cn);
436 zfs_close(cn->cn_handle);
437 free(cn);
440 uu_avl_walk_end(walk);
441 uu_avl_destroy(clp->cl_tree);
443 if (clp->cl_pool)
444 uu_avl_pool_destroy(clp->cl_pool);
446 free(clp);
450 * Add one dataset to changelist
452 static int
453 changelist_add_mounted(zfs_handle_t *zhp, void *data)
455 prop_changelist_t *clp = data;
456 prop_changenode_t *cn;
457 uu_avl_index_t idx;
459 ASSERT3U(clp->cl_prop, ==, ZFS_PROP_MOUNTPOINT);
461 cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
462 cn->cn_handle = zhp;
463 cn->cn_mounted = zfs_is_mounted(zhp, NULL);
464 ASSERT3U(cn->cn_mounted, ==, B_TRUE);
465 cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
466 cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
467 cn->cn_needpost = B_TRUE;
469 /* Indicate if any child is exported to a local zone. */
470 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
471 clp->cl_haszonedchild = B_TRUE;
473 uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
475 if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
476 uu_avl_insert(clp->cl_tree, cn, idx);
477 } else {
478 free(cn);
479 zfs_close(zhp);
482 return (0);
485 static int
486 change_one(zfs_handle_t *zhp, void *data)
488 prop_changelist_t *clp = data;
489 char property[ZFS_MAXPROPLEN];
490 char where[64];
491 prop_changenode_t *cn = NULL;
492 zprop_source_t sourcetype = ZPROP_SRC_NONE;
493 zprop_source_t share_sourcetype = ZPROP_SRC_NONE;
494 int ret = 0;
497 * We only want to unmount/unshare those filesystems that may inherit
498 * from the target filesystem. If we find any filesystem with a
499 * locally set mountpoint, we ignore any children since changing the
500 * property will not affect them. If this is a rename, we iterate
501 * over all children regardless, since we need them unmounted in
502 * order to do the rename. Also, if this is a volume and we're doing
503 * a rename, then always add it to the changelist.
506 if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) &&
507 zfs_prop_get(zhp, clp->cl_prop, property,
508 sizeof (property), &sourcetype, where, sizeof (where),
509 B_FALSE) != 0) {
510 goto out;
514 * If we are "watching" sharenfs or sharesmb
515 * then check out the companion property which is tracked
516 * in cl_shareprop
518 if (clp->cl_shareprop != ZPROP_INVAL &&
519 zfs_prop_get(zhp, clp->cl_shareprop, property,
520 sizeof (property), &share_sourcetype, where, sizeof (where),
521 B_FALSE) != 0) {
522 goto out;
525 if (clp->cl_alldependents || clp->cl_allchildren ||
526 sourcetype == ZPROP_SRC_DEFAULT ||
527 sourcetype == ZPROP_SRC_INHERITED ||
528 (clp->cl_shareprop != ZPROP_INVAL &&
529 (share_sourcetype == ZPROP_SRC_DEFAULT ||
530 share_sourcetype == ZPROP_SRC_INHERITED))) {
531 cn = zfs_alloc(zfs_get_handle(zhp), sizeof (prop_changenode_t));
532 cn->cn_handle = zhp;
533 cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
534 zfs_is_mounted(zhp, NULL);
535 cn->cn_shared = zfs_is_shared(zhp, NULL, NULL);
536 cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
537 cn->cn_needpost = B_TRUE;
539 /* Indicate if any child is exported to a local zone. */
540 if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned)
541 clp->cl_haszonedchild = B_TRUE;
543 uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
545 uu_avl_index_t idx;
547 if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
548 uu_avl_insert(clp->cl_tree, cn, idx);
549 } else {
550 free(cn);
551 cn = NULL;
554 if (!clp->cl_alldependents)
555 ret = zfs_iter_children_v2(zhp, 0, change_one, data);
558 * If we added the handle to the changelist, we will re-use it
559 * later so return without closing it.
561 if (cn != NULL)
562 return (ret);
565 out:
566 zfs_close(zhp);
567 return (ret);
570 static int
571 compare_props(const void *a, const void *b, zfs_prop_t prop)
573 const prop_changenode_t *ca = a;
574 const prop_changenode_t *cb = b;
576 char propa[MAXPATHLEN];
577 char propb[MAXPATHLEN];
579 boolean_t haspropa, haspropb;
581 haspropa = (zfs_prop_get(ca->cn_handle, prop, propa, sizeof (propa),
582 NULL, NULL, 0, B_FALSE) == 0);
583 haspropb = (zfs_prop_get(cb->cn_handle, prop, propb, sizeof (propb),
584 NULL, NULL, 0, B_FALSE) == 0);
586 if (!haspropa && haspropb)
587 return (-1);
588 else if (haspropa && !haspropb)
589 return (1);
590 else if (!haspropa && !haspropb)
591 return (0);
592 else
593 return (strcmp(propb, propa));
596 static int
597 compare_mountpoints(const void *a, const void *b, void *unused)
600 * When unsharing or unmounting filesystems, we need to do it in
601 * mountpoint order. This allows the user to have a mountpoint
602 * hierarchy that is different from the dataset hierarchy, and still
603 * allow it to be changed.
605 (void) unused;
606 return (compare_props(a, b, ZFS_PROP_MOUNTPOINT));
609 static int
610 compare_dataset_names(const void *a, const void *b, void *unused)
612 (void) unused;
613 return (compare_props(a, b, ZFS_PROP_NAME));
617 * Given a ZFS handle and a property, construct a complete list of datasets
618 * that need to be modified as part of this process. For anything but the
619 * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
620 * Otherwise, we iterate over all children and look for any datasets that
621 * inherit the property. For each such dataset, we add it to the list and
622 * mark whether it was shared beforehand.
624 prop_changelist_t *
625 changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
626 int mnt_flags)
628 prop_changelist_t *clp;
629 prop_changenode_t *cn;
630 zfs_handle_t *temp;
631 char property[ZFS_MAXPROPLEN];
632 boolean_t legacy = B_FALSE;
634 clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t));
637 * For mountpoint-related tasks, we want to sort everything by
638 * mountpoint, so that we mount and unmount them in the appropriate
639 * order, regardless of their position in the hierarchy.
641 if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
642 prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
643 prop == ZFS_PROP_SHARESMB) {
645 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
646 property, sizeof (property),
647 NULL, NULL, 0, B_FALSE) == 0 &&
648 (strcmp(property, "legacy") == 0 ||
649 strcmp(property, "none") == 0)) {
650 legacy = B_TRUE;
654 clp->cl_pool = uu_avl_pool_create("changelist_pool",
655 sizeof (prop_changenode_t),
656 offsetof(prop_changenode_t, cn_treenode),
657 legacy ? compare_dataset_names : compare_mountpoints, 0);
658 if (clp->cl_pool == NULL) {
659 assert(uu_error() == UU_ERROR_NO_MEMORY);
660 (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
661 changelist_free(clp);
662 return (NULL);
665 clp->cl_tree = uu_avl_create(clp->cl_pool, NULL, UU_DEFAULT);
666 clp->cl_gflags = gather_flags;
667 clp->cl_mflags = mnt_flags;
669 if (clp->cl_tree == NULL) {
670 assert(uu_error() == UU_ERROR_NO_MEMORY);
671 (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
672 changelist_free(clp);
673 return (NULL);
677 * If this is a rename or the 'zoned' property, we pretend we're
678 * changing the mountpoint and flag it so we can catch all children in
679 * change_one().
681 * Flag cl_alldependents to catch all children plus the dependents
682 * (clones) that are not in the hierarchy.
684 if (prop == ZFS_PROP_NAME) {
685 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
686 clp->cl_alldependents = B_TRUE;
687 } else if (prop == ZFS_PROP_ZONED) {
688 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
689 clp->cl_allchildren = B_TRUE;
690 } else if (prop == ZFS_PROP_CANMOUNT) {
691 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
692 } else if (prop == ZFS_PROP_VOLSIZE) {
693 clp->cl_prop = ZFS_PROP_MOUNTPOINT;
694 } else {
695 clp->cl_prop = prop;
697 clp->cl_realprop = prop;
699 if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
700 clp->cl_prop != ZFS_PROP_SHARENFS &&
701 clp->cl_prop != ZFS_PROP_SHARESMB)
702 return (clp);
705 * If watching SHARENFS or SHARESMB then
706 * also watch its companion property.
708 if (clp->cl_prop == ZFS_PROP_SHARENFS)
709 clp->cl_shareprop = ZFS_PROP_SHARESMB;
710 else if (clp->cl_prop == ZFS_PROP_SHARESMB)
711 clp->cl_shareprop = ZFS_PROP_SHARENFS;
713 if (clp->cl_prop == ZFS_PROP_MOUNTPOINT &&
714 (clp->cl_gflags & CL_GATHER_ITER_MOUNTED)) {
716 * Instead of iterating through all of the dataset children we
717 * gather mounted dataset children from MNTTAB
719 if (zfs_iter_mounted(zhp, changelist_add_mounted, clp) != 0) {
720 changelist_free(clp);
721 return (NULL);
723 } else if (clp->cl_alldependents) {
724 if (zfs_iter_dependents_v2(zhp, 0, B_TRUE, change_one,
725 clp) != 0) {
726 changelist_free(clp);
727 return (NULL);
729 } else if (zfs_iter_children_v2(zhp, 0, change_one, clp) != 0) {
730 changelist_free(clp);
731 return (NULL);
735 * We have to re-open ourselves because we auto-close all the handles
736 * and can't tell the difference.
738 if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
739 ZFS_TYPE_DATASET)) == NULL) {
740 changelist_free(clp);
741 return (NULL);
745 * Always add ourself to the list. We add ourselves to the end so that
746 * we're the last to be unmounted.
748 cn = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changenode_t));
749 cn->cn_handle = temp;
750 cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
751 zfs_is_mounted(temp, NULL);
752 cn->cn_shared = zfs_is_shared(temp, NULL, NULL);
753 cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
754 cn->cn_needpost = B_TRUE;
756 uu_avl_node_init(cn, &cn->cn_treenode, clp->cl_pool);
757 uu_avl_index_t idx;
758 if (uu_avl_find(clp->cl_tree, cn, NULL, &idx) == NULL) {
759 uu_avl_insert(clp->cl_tree, cn, idx);
760 } else {
761 free(cn);
762 zfs_close(temp);
766 * If the mountpoint property was previously 'legacy', or 'none',
767 * record it as the behavior of changelist_postfix() will be different.
769 if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
771 * do not automatically mount ex-legacy datasets if
772 * we specifically set canmount to noauto
774 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
775 ZFS_CANMOUNT_NOAUTO)
776 clp->cl_waslegacy = B_TRUE;
779 return (clp);