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