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]
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.
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:
55 * changelist_postfix()
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
66 typedef struct prop_changenode
{
67 zfs_handle_t
*cn_handle
;
71 boolean_t cn_needpost
; /* is postfix() needed? */
72 uu_avl_node_t cn_treenode
;
75 struct prop_changelist
{
77 zfs_prop_t cl_realprop
;
78 zfs_prop_t cl_shareprop
; /* used with sharenfs/sharesmb */
79 uu_avl_pool_t
*cl_pool
;
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.
96 changelist_prefix(prop_changelist_t
*clp
)
98 prop_changenode_t
*cn
;
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
)
108 if ((walk
= uu_avl_walk_start(clp
->cl_tree
, UU_WALK_ROBUST
)) == NULL
)
111 while ((cn
= uu_avl_walk_next(walk
)) != NULL
) {
113 /* if a previous loop failed, set the remaining to false */
115 cn
->cn_needpost
= B_FALSE
;
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
)
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
)
134 if (zfs_unmount(cn
->cn_handle
, NULL
,
135 clp
->cl_mflags
) != 0) {
137 cn
->cn_needpost
= B_FALSE
;
140 case ZFS_PROP_SHARESMB
:
141 (void) zfs_unshare(cn
->cn_handle
, NULL
,
143 commit_smb_shares
= B_TRUE
;
152 if (commit_smb_shares
)
153 zfs_commit_shares(smb
);
154 uu_avl_walk_end(walk
);
157 (void) changelist_postfix(clp
);
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
172 changelist_postfix(prop_changelist_t
*clp
)
174 prop_changenode_t
*cn
;
176 char shareopts
[ZFS_MAXPROPLEN
];
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
)
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
200 if ((walk
= uu_avl_walk_start(clp
->cl_tree
,
201 UU_WALK_REVERSE
| UU_WALK_ROBUST
)) == NULL
)
204 while ((cn
= uu_avl_walk_next(walk
)) != NULL
) {
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
)
218 /* Only do post-processing if it's required */
219 if (!cn
->cn_needpost
)
221 cn
->cn_needpost
= B_FALSE
;
223 zfs_refresh_properties(cn
->cn_handle
);
225 if (ZFS_IS_VOLUME(cn
->cn_handle
))
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)
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"?
298 isa_child_of(const char *dataset
, const char *parent
)
302 len
= strlen(parent
);
304 if (strncmp(dataset
, parent
, len
) == 0 &&
305 (dataset
[len
] == '@' || dataset
[len
] == '/' ||
306 dataset
[len
] == '\0'))
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.
321 changelist_rename(prop_changelist_t
*clp
, const char *src
, const char *dst
)
323 prop_changenode_t
*cn
;
325 char newname
[ZFS_MAX_DATASET_NAME_LEN
];
327 if ((walk
= uu_avl_walk_start(clp
->cl_tree
, UU_WALK_ROBUST
)) == NULL
)
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
))
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
),
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
;
364 if (clp
->cl_prop
!= ZFS_PROP_SHARENFS
&&
365 clp
->cl_prop
!= ZFS_PROP_SHARESMB
)
368 if ((walk
= uu_avl_walk_start(clp
->cl_tree
, UU_WALK_ROBUST
)) == NULL
)
371 while ((cn
= uu_avl_walk_next(walk
)) != NULL
) {
372 if (zfs_unshare(cn
->cn_handle
, NULL
, proto
) != 0)
376 for (const enum sa_protocol
*p
= proto
; *p
!= SA_NO_PROTOCOL
; ++p
)
377 sa_commit_shares(*p
);
378 uu_avl_walk_end(walk
);
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.
398 changelist_remove(prop_changelist_t
*clp
, const char *name
)
400 prop_changenode_t
*cn
;
403 if ((walk
= uu_avl_walk_start(clp
->cl_tree
, UU_WALK_ROBUST
)) == NULL
)
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
);
411 uu_avl_walk_end(walk
);
416 uu_avl_walk_end(walk
);
420 * Release any memory associated with a changelist.
423 changelist_free(prop_changelist_t
*clp
)
425 prop_changenode_t
*cn
;
430 if ((walk
= uu_avl_walk_start(clp
->cl_tree
,
431 UU_WALK_ROBUST
)) == NULL
)
434 while ((cn
= uu_avl_walk_next(walk
)) != NULL
) {
435 uu_avl_remove(clp
->cl_tree
, cn
);
436 zfs_close(cn
->cn_handle
);
440 uu_avl_walk_end(walk
);
441 uu_avl_destroy(clp
->cl_tree
);
444 uu_avl_pool_destroy(clp
->cl_pool
);
450 * Add one dataset to changelist
453 changelist_add_mounted(zfs_handle_t
*zhp
, void *data
)
455 prop_changelist_t
*clp
= data
;
456 prop_changenode_t
*cn
;
459 ASSERT3U(clp
->cl_prop
, ==, ZFS_PROP_MOUNTPOINT
);
461 cn
= zfs_alloc(zfs_get_handle(zhp
), sizeof (prop_changenode_t
));
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
);
486 change_one(zfs_handle_t
*zhp
, void *data
)
488 prop_changelist_t
*clp
= data
;
489 char property
[ZFS_MAXPROPLEN
];
491 prop_changenode_t
*cn
= NULL
;
492 zprop_source_t sourcetype
= ZPROP_SRC_NONE
;
493 zprop_source_t share_sourcetype
= ZPROP_SRC_NONE
;
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
),
514 * If we are "watching" sharenfs or sharesmb
515 * then check out the companion property which is tracked
518 if (clp
->cl_shareprop
!= ZPROP_INVAL
&&
519 zfs_prop_get(zhp
, clp
->cl_shareprop
, property
,
520 sizeof (property
), &share_sourcetype
, where
, sizeof (where
),
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
));
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
);
547 if (uu_avl_find(clp
->cl_tree
, cn
, NULL
, &idx
) == NULL
) {
548 uu_avl_insert(clp
->cl_tree
, cn
, idx
);
554 if (!clp
->cl_alldependents
)
555 ret
= zfs_iter_children(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.
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
)
588 else if (haspropa
&& !haspropb
)
590 else if (!haspropa
&& !haspropb
)
593 return (strcmp(propb
, propa
));
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.
606 return (compare_props(a
, b
, ZFS_PROP_MOUNTPOINT
));
610 compare_dataset_names(const void *a
, const void *b
, 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.
625 changelist_gather(zfs_handle_t
*zhp
, zfs_prop_t prop
, int gather_flags
,
628 prop_changelist_t
*clp
;
629 prop_changenode_t
*cn
;
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)) {
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
);
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
);
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
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
;
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
)
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
);
723 } else if (clp
->cl_alldependents
) {
724 if (zfs_iter_dependents(zhp
, 0, B_TRUE
, change_one
, clp
) != 0) {
725 changelist_free(clp
);
728 } else if (zfs_iter_children(zhp
, 0, change_one
, clp
) != 0) {
729 changelist_free(clp
);
734 * We have to re-open ourselves because we auto-close all the handles
735 * and can't tell the difference.
737 if ((temp
= zfs_open(zhp
->zfs_hdl
, zfs_get_name(zhp
),
738 ZFS_TYPE_DATASET
)) == NULL
) {
739 changelist_free(clp
);
744 * Always add ourself to the list. We add ourselves to the end so that
745 * we're the last to be unmounted.
747 cn
= zfs_alloc(zhp
->zfs_hdl
, sizeof (prop_changenode_t
));
748 cn
->cn_handle
= temp
;
749 cn
->cn_mounted
= (clp
->cl_gflags
& CL_GATHER_MOUNT_ALWAYS
) ||
750 zfs_is_mounted(temp
, NULL
);
751 cn
->cn_shared
= zfs_is_shared(temp
, NULL
, NULL
);
752 cn
->cn_zoned
= zfs_prop_get_int(zhp
, ZFS_PROP_ZONED
);
753 cn
->cn_needpost
= B_TRUE
;
755 uu_avl_node_init(cn
, &cn
->cn_treenode
, clp
->cl_pool
);
757 if (uu_avl_find(clp
->cl_tree
, cn
, NULL
, &idx
) == NULL
) {
758 uu_avl_insert(clp
->cl_tree
, cn
, idx
);
765 * If the mountpoint property was previously 'legacy', or 'none',
766 * record it as the behavior of changelist_postfix() will be different.
768 if ((clp
->cl_prop
== ZFS_PROP_MOUNTPOINT
) && legacy
) {
770 * do not automatically mount ex-legacy datasets if
771 * we specifically set canmount to noauto
773 if (zfs_prop_get_int(zhp
, ZFS_PROP_CANMOUNT
) !=
775 clp
->cl_waslegacy
= B_TRUE
;