libzfs_changelist.c revision 168926
1168404Spjd/* 2168404Spjd * CDDL HEADER START 3168404Spjd * 4168404Spjd * The contents of this file are subject to the terms of the 5168404Spjd * Common Development and Distribution License (the "License"). 6168404Spjd * You may not use this file except in compliance with the License. 7168404Spjd * 8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9168404Spjd * or http://www.opensolaris.org/os/licensing. 10168404Spjd * See the License for the specific language governing permissions 11168404Spjd * and limitations under the License. 12168404Spjd * 13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each 14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15168404Spjd * If applicable, add the following below this CDDL HEADER, with the 16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18168404Spjd * 19168404Spjd * CDDL HEADER END 20168404Spjd */ 21168404Spjd 22168404Spjd/* 23168404Spjd * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24168404Spjd * Use is subject to license terms. 25168404Spjd */ 26168404Spjd 27168404Spjd#pragma ident "%Z%%M% %I% %E% SMI" 28168404Spjd 29168404Spjd#include <libintl.h> 30168404Spjd#include <libuutil.h> 31168404Spjd#include <stddef.h> 32168404Spjd#include <stdlib.h> 33168404Spjd#include <string.h> 34168404Spjd#include <unistd.h> 35168404Spjd#include <zone.h> 36168404Spjd 37168404Spjd#include <libzfs.h> 38168404Spjd 39168404Spjd#include "libzfs_impl.h" 40168404Spjd 41168404Spjd/* 42168404Spjd * Structure to keep track of dataset state. Before changing the 'sharenfs' or 43168404Spjd * 'mountpoint' property, we record whether the filesystem was previously 44168404Spjd * mounted/shared. This prior state dictates whether we remount/reshare the 45168404Spjd * dataset after the property has been changed. 46168404Spjd * 47168404Spjd * The interface consists of the following sequence of functions: 48168404Spjd * 49168404Spjd * changelist_gather() 50168404Spjd * changelist_prefix() 51168404Spjd * < change property > 52168404Spjd * changelist_postfix() 53168404Spjd * changelist_free() 54168404Spjd * 55168404Spjd * Other interfaces: 56168404Spjd * 57168404Spjd * changelist_remove() - remove a node from a gathered list 58168404Spjd * changelist_rename() - renames all datasets appropriately when doing a rename 59168404Spjd * changelist_unshare() - unshares all the nodes in a given changelist 60168404Spjd * changelist_haszonedchild() - check if there is any child exported to 61168404Spjd * a local zone 62168404Spjd */ 63168404Spjdtypedef struct prop_changenode { 64168404Spjd zfs_handle_t *cn_handle; 65168404Spjd int cn_shared; 66168404Spjd int cn_mounted; 67168404Spjd int cn_zoned; 68168404Spjd uu_list_node_t cn_listnode; 69168404Spjd} prop_changenode_t; 70168404Spjd 71168404Spjdstruct prop_changelist { 72168404Spjd zfs_prop_t cl_prop; 73168404Spjd zfs_prop_t cl_realprop; 74168404Spjd uu_list_pool_t *cl_pool; 75168404Spjd uu_list_t *cl_list; 76168404Spjd boolean_t cl_waslegacy; 77168404Spjd boolean_t cl_allchildren; 78168404Spjd boolean_t cl_alldependents; 79168404Spjd int cl_flags; 80168404Spjd boolean_t cl_haszonedchild; 81168404Spjd boolean_t cl_sorted; 82168404Spjd}; 83168404Spjd 84168404Spjd/* 85168404Spjd * If the property is 'mountpoint', go through and unmount filesystems as 86168404Spjd * necessary. We don't do the same for 'sharenfs', because we can just re-share 87168404Spjd * with different options without interrupting service. 88168404Spjd */ 89168404Spjdint 90168404Spjdchangelist_prefix(prop_changelist_t *clp) 91168404Spjd{ 92168404Spjd prop_changenode_t *cn; 93168404Spjd int ret = 0; 94168404Spjd 95168404Spjd if (clp->cl_prop != ZFS_PROP_MOUNTPOINT) 96168404Spjd return (0); 97168404Spjd 98168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 99168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 100168404Spjd /* 101168404Spjd * If we are in the global zone, but this dataset is exported 102168404Spjd * to a local zone, do nothing. 103168404Spjd */ 104168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 105168404Spjd continue; 106168404Spjd 107168404Spjd if (ZFS_IS_VOLUME(cn->cn_handle)) { 108168404Spjd switch (clp->cl_realprop) { 109168404Spjd case ZFS_PROP_NAME: 110168404Spjd /* 111168404Spjd * If this was a rename, unshare the zvol, and 112168404Spjd * remove the /dev/zvol links. 113168404Spjd */ 114168404Spjd (void) zfs_unshare_iscsi(cn->cn_handle); 115168404Spjd 116168404Spjd if (zvol_remove_link(cn->cn_handle->zfs_hdl, 117168404Spjd cn->cn_handle->zfs_name) != 0) 118168404Spjd ret = -1; 119168404Spjd break; 120168404Spjd 121168404Spjd case ZFS_PROP_VOLSIZE: 122168404Spjd /* 123168404Spjd * If this was a change to the volume size, we 124168404Spjd * need to unshare and reshare the volume. 125168404Spjd */ 126168404Spjd (void) zfs_unshare_iscsi(cn->cn_handle); 127168404Spjd break; 128168404Spjd } 129168404Spjd } else if (zfs_unmount(cn->cn_handle, NULL, clp->cl_flags) != 0) 130168404Spjd ret = -1; 131168404Spjd } 132168404Spjd 133168404Spjd return (ret); 134168404Spjd} 135168404Spjd 136168404Spjd/* 137168404Spjd * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or 138168404Spjd * reshare the filesystems as necessary. In changelist_gather() we recorded 139168404Spjd * whether the filesystem was previously shared or mounted. The action we take 140168404Spjd * depends on the previous state, and whether the value was previously 'legacy'. 141168404Spjd * For non-legacy properties, we only remount/reshare the filesystem if it was 142168404Spjd * previously mounted/shared. Otherwise, we always remount/reshare the 143168404Spjd * filesystem. 144168404Spjd */ 145168404Spjdint 146168404Spjdchangelist_postfix(prop_changelist_t *clp) 147168404Spjd{ 148168404Spjd prop_changenode_t *cn; 149168404Spjd char shareopts[ZFS_MAXPROPLEN]; 150168404Spjd int ret = 0; 151168404Spjd 152168404Spjd /* 153168404Spjd * If we're changing the mountpoint, attempt to destroy the underlying 154168404Spjd * mountpoint. All other datasets will have inherited from this dataset 155168404Spjd * (in which case their mountpoints exist in the filesystem in the new 156168404Spjd * location), or have explicit mountpoints set (in which case they won't 157168404Spjd * be in the changelist). 158168404Spjd */ 159168404Spjd if ((cn = uu_list_last(clp->cl_list)) == NULL) 160168404Spjd return (0); 161168404Spjd 162168404Spjd if (clp->cl_prop == ZFS_PROP_MOUNTPOINT) 163168404Spjd remove_mountpoint(cn->cn_handle); 164168404Spjd 165168404Spjd /* 166168404Spjd * We walk the datasets in reverse, because we want to mount any parent 167168404Spjd * datasets before mounting the children. 168168404Spjd */ 169168404Spjd for (cn = uu_list_last(clp->cl_list); cn != NULL; 170168404Spjd cn = uu_list_prev(clp->cl_list, cn)) { 171168404Spjd /* 172168404Spjd * If we are in the global zone, but this dataset is exported 173168404Spjd * to a local zone, do nothing. 174168404Spjd */ 175168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 176168404Spjd continue; 177168404Spjd 178168404Spjd zfs_refresh_properties(cn->cn_handle); 179168404Spjd 180168404Spjd if (ZFS_IS_VOLUME(cn->cn_handle)) { 181168404Spjd /* 182168404Spjd * If we're doing a rename, recreate the /dev/zvol 183168404Spjd * links. 184168404Spjd */ 185168404Spjd if (clp->cl_realprop == ZFS_PROP_NAME && 186168404Spjd zvol_create_link(cn->cn_handle->zfs_hdl, 187168404Spjd cn->cn_handle->zfs_name) != 0) { 188168404Spjd ret = -1; 189168404Spjd } else if (cn->cn_shared || 190168404Spjd clp->cl_prop == ZFS_PROP_SHAREISCSI) { 191168404Spjd if (zfs_prop_get(cn->cn_handle, 192168404Spjd ZFS_PROP_SHAREISCSI, shareopts, 193168404Spjd sizeof (shareopts), NULL, NULL, 0, 194168404Spjd B_FALSE) == 0 && 195168404Spjd strcmp(shareopts, "off") == 0) { 196168404Spjd ret = zfs_unshare_iscsi(cn->cn_handle); 197168404Spjd } else { 198168404Spjd ret = zfs_share_iscsi(cn->cn_handle); 199168404Spjd } 200168404Spjd } 201168404Spjd 202168404Spjd continue; 203168404Spjd } 204168404Spjd 205168404Spjd if ((clp->cl_waslegacy || cn->cn_mounted) && 206168404Spjd !zfs_is_mounted(cn->cn_handle, NULL) && 207168404Spjd zfs_mount(cn->cn_handle, NULL, 0) != 0) 208168404Spjd ret = -1; 209168404Spjd 210168404Spjd /* 211168404Spjd * We always re-share even if the filesystem is currently 212168404Spjd * shared, so that we can adopt any new options. 213168404Spjd */ 214168404Spjd if (cn->cn_shared || 215168404Spjd (clp->cl_prop == ZFS_PROP_SHARENFS && clp->cl_waslegacy)) { 216168404Spjd if (zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, 217168404Spjd shareopts, sizeof (shareopts), NULL, NULL, 0, 218168404Spjd B_FALSE) == 0 && strcmp(shareopts, "off") == 0) { 219168404Spjd ret = zfs_unshare_nfs(cn->cn_handle, NULL); 220168404Spjd } else { 221168404Spjd ret = zfs_share_nfs(cn->cn_handle); 222168404Spjd } 223168404Spjd } 224168404Spjd } 225168404Spjd 226168404Spjd return (ret); 227168404Spjd} 228168404Spjd 229168404Spjd/* 230168404Spjd * Is this "dataset" a child of "parent"? 231168404Spjd */ 232168404Spjdstatic boolean_t 233168404Spjdisa_child_of(const char *dataset, const char *parent) 234168404Spjd{ 235168404Spjd int len; 236168404Spjd 237168404Spjd len = strlen(parent); 238168404Spjd 239168404Spjd if (strncmp(dataset, parent, len) == 0 && 240168404Spjd (dataset[len] == '@' || dataset[len] == '/' || 241168404Spjd dataset[len] == '\0')) 242168404Spjd return (B_TRUE); 243168404Spjd else 244168404Spjd return (B_FALSE); 245168404Spjd 246168404Spjd} 247168404Spjd 248168404Spjd/* 249168404Spjd * If we rename a filesystem, child filesystem handles are no longer valid 250168404Spjd * since we identify each dataset by its name in the ZFS namespace. As a 251168404Spjd * result, we have to go through and fix up all the names appropriately. We 252168404Spjd * could do this automatically if libzfs kept track of all open handles, but 253168404Spjd * this is a lot less work. 254168404Spjd */ 255168404Spjdvoid 256168404Spjdchangelist_rename(prop_changelist_t *clp, const char *src, const char *dst) 257168404Spjd{ 258168404Spjd prop_changenode_t *cn; 259168404Spjd char newname[ZFS_MAXNAMELEN]; 260168404Spjd 261168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 262168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 263168404Spjd /* 264168404Spjd * Do not rename a clone that's not in the source hierarchy. 265168404Spjd */ 266168404Spjd if (!isa_child_of(cn->cn_handle->zfs_name, src)) 267168404Spjd continue; 268168404Spjd 269168404Spjd /* 270168404Spjd * Destroy the previous mountpoint if needed. 271168404Spjd */ 272168404Spjd remove_mountpoint(cn->cn_handle); 273168404Spjd 274168404Spjd (void) strlcpy(newname, dst, sizeof (newname)); 275168404Spjd (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src)); 276168404Spjd 277168404Spjd (void) strlcpy(cn->cn_handle->zfs_name, newname, 278168404Spjd sizeof (cn->cn_handle->zfs_name)); 279168404Spjd } 280168404Spjd} 281168404Spjd 282168404Spjd/* 283168404Spjd * Given a gathered changelist for the 'sharenfs' property, unshare all the 284168404Spjd * datasets in the list. 285168404Spjd */ 286168404Spjdint 287168404Spjdchangelist_unshare(prop_changelist_t *clp) 288168404Spjd{ 289168404Spjd prop_changenode_t *cn; 290168404Spjd int ret = 0; 291168404Spjd 292168404Spjd if (clp->cl_prop != ZFS_PROP_SHARENFS) 293168404Spjd return (0); 294168404Spjd 295168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 296168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 297168404Spjd if (zfs_unshare_nfs(cn->cn_handle, NULL) != 0) 298168404Spjd ret = -1; 299168404Spjd } 300168404Spjd 301168404Spjd return (ret); 302168404Spjd} 303168404Spjd 304168404Spjd/* 305168404Spjd * Check if there is any child exported to a local zone in a given changelist. 306168404Spjd * This information has already been recorded while gathering the changelist 307168404Spjd * via changelist_gather(). 308168404Spjd */ 309168404Spjdint 310168404Spjdchangelist_haszonedchild(prop_changelist_t *clp) 311168404Spjd{ 312168404Spjd return (clp->cl_haszonedchild); 313168404Spjd} 314168404Spjd 315168404Spjd/* 316168404Spjd * Remove a node from a gathered list. 317168404Spjd */ 318168404Spjdvoid 319168404Spjdchangelist_remove(zfs_handle_t *zhp, prop_changelist_t *clp) 320168404Spjd{ 321168404Spjd prop_changenode_t *cn; 322168404Spjd 323168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 324168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 325168404Spjd 326168404Spjd if (strcmp(cn->cn_handle->zfs_name, zhp->zfs_name) == 0) { 327168404Spjd uu_list_remove(clp->cl_list, cn); 328168404Spjd zfs_close(cn->cn_handle); 329168404Spjd free(cn); 330168404Spjd return; 331168404Spjd } 332168404Spjd } 333168404Spjd} 334168404Spjd 335168404Spjd/* 336168404Spjd * Release any memory associated with a changelist. 337168404Spjd */ 338168404Spjdvoid 339168404Spjdchangelist_free(prop_changelist_t *clp) 340168404Spjd{ 341168404Spjd prop_changenode_t *cn; 342168926Spjd void *cookie; 343168404Spjd 344168404Spjd if (clp->cl_list) { 345168926Spjd cookie = NULL; 346168926Spjd while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) { 347168404Spjd zfs_close(cn->cn_handle); 348168404Spjd free(cn); 349168404Spjd } 350168404Spjd 351168404Spjd uu_list_destroy(clp->cl_list); 352168404Spjd } 353168404Spjd if (clp->cl_pool) 354168404Spjd uu_list_pool_destroy(clp->cl_pool); 355168404Spjd 356168404Spjd free(clp); 357168404Spjd} 358168404Spjd 359168404Spjdstatic int 360168404Spjdchange_one(zfs_handle_t *zhp, void *data) 361168404Spjd{ 362168404Spjd prop_changelist_t *clp = data; 363168404Spjd char property[ZFS_MAXPROPLEN]; 364168404Spjd char where[64]; 365168404Spjd prop_changenode_t *cn; 366168404Spjd zfs_source_t sourcetype; 367168404Spjd 368168404Spjd /* 369168404Spjd * We only want to unmount/unshare those filesystems that may inherit 370168404Spjd * from the target filesystem. If we find any filesystem with a 371168404Spjd * locally set mountpoint, we ignore any children since changing the 372168404Spjd * property will not affect them. If this is a rename, we iterate 373168404Spjd * over all children regardless, since we need them unmounted in 374168404Spjd * order to do the rename. Also, if this is a volume and we're doing 375168404Spjd * a rename, then always add it to the changelist. 376168404Spjd */ 377168404Spjd 378168404Spjd if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) && 379168404Spjd zfs_prop_get(zhp, clp->cl_prop, property, 380168404Spjd sizeof (property), &sourcetype, where, sizeof (where), 381168404Spjd B_FALSE) != 0) { 382168404Spjd zfs_close(zhp); 383168404Spjd return (0); 384168404Spjd } 385168404Spjd 386168404Spjd if (clp->cl_alldependents || clp->cl_allchildren || 387168404Spjd sourcetype == ZFS_SRC_DEFAULT || sourcetype == ZFS_SRC_INHERITED) { 388168404Spjd if ((cn = zfs_alloc(zfs_get_handle(zhp), 389168404Spjd sizeof (prop_changenode_t))) == NULL) { 390168404Spjd zfs_close(zhp); 391168404Spjd return (-1); 392168404Spjd } 393168404Spjd 394168404Spjd cn->cn_handle = zhp; 395168404Spjd cn->cn_mounted = zfs_is_mounted(zhp, NULL); 396168404Spjd cn->cn_shared = zfs_is_shared(zhp); 397168404Spjd cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 398168404Spjd 399168404Spjd /* Indicate if any child is exported to a local zone. */ 400168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 401168404Spjd clp->cl_haszonedchild = B_TRUE; 402168404Spjd 403168404Spjd uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 404168404Spjd 405168404Spjd if (clp->cl_sorted) { 406168404Spjd uu_list_index_t idx; 407168404Spjd 408168404Spjd (void) uu_list_find(clp->cl_list, cn, NULL, 409168404Spjd &idx); 410168404Spjd uu_list_insert(clp->cl_list, cn, idx); 411168404Spjd } else { 412168404Spjd ASSERT(!clp->cl_alldependents); 413168404Spjd verify(uu_list_insert_before(clp->cl_list, 414168926Spjd uu_list_first(clp->cl_list), cn) == 0); 415168404Spjd } 416168404Spjd 417168404Spjd if (!clp->cl_alldependents) 418168404Spjd return (zfs_iter_children(zhp, change_one, data)); 419168404Spjd } else { 420168404Spjd zfs_close(zhp); 421168404Spjd } 422168404Spjd 423168404Spjd return (0); 424168404Spjd} 425168404Spjd 426168404Spjd/*ARGSUSED*/ 427168404Spjdstatic int 428168404Spjdcompare_mountpoints(const void *a, const void *b, void *unused) 429168404Spjd{ 430168404Spjd const prop_changenode_t *ca = a; 431168404Spjd const prop_changenode_t *cb = b; 432168404Spjd 433168404Spjd char mounta[MAXPATHLEN]; 434168404Spjd char mountb[MAXPATHLEN]; 435168404Spjd 436168404Spjd boolean_t hasmounta, hasmountb; 437168404Spjd 438168404Spjd /* 439168404Spjd * When unsharing or unmounting filesystems, we need to do it in 440168404Spjd * mountpoint order. This allows the user to have a mountpoint 441168404Spjd * hierarchy that is different from the dataset hierarchy, and still 442168404Spjd * allow it to be changed. However, if either dataset doesn't have a 443168404Spjd * mountpoint (because it is a volume or a snapshot), we place it at the 444168404Spjd * end of the list, because it doesn't affect our change at all. 445168404Spjd */ 446168404Spjd hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta, 447168404Spjd sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 448168404Spjd hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb, 449168404Spjd sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 450168404Spjd 451168404Spjd if (!hasmounta && hasmountb) 452168404Spjd return (-1); 453168404Spjd else if (hasmounta && !hasmountb) 454168404Spjd return (1); 455168404Spjd else if (!hasmounta && !hasmountb) 456168404Spjd return (0); 457168404Spjd else 458168404Spjd return (strcmp(mountb, mounta)); 459168404Spjd} 460168404Spjd 461168404Spjd/* 462168404Spjd * Given a ZFS handle and a property, construct a complete list of datasets 463168404Spjd * that need to be modified as part of this process. For anything but the 464168404Spjd * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. 465168404Spjd * Otherwise, we iterate over all children and look for any datasets that 466168404Spjd * inherit the property. For each such dataset, we add it to the list and 467168404Spjd * mark whether it was shared beforehand. 468168404Spjd */ 469168404Spjdprop_changelist_t * 470168404Spjdchangelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags) 471168404Spjd{ 472168404Spjd prop_changelist_t *clp; 473168404Spjd prop_changenode_t *cn; 474168404Spjd zfs_handle_t *temp; 475168404Spjd char property[ZFS_MAXPROPLEN]; 476168404Spjd uu_compare_fn_t *compare = NULL; 477168404Spjd 478168404Spjd if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) 479168404Spjd return (NULL); 480168404Spjd 481168404Spjd /* 482168404Spjd * For mountpoint-related tasks, we want to sort everything by 483168404Spjd * mountpoint, so that we mount and unmount them in the appropriate 484168404Spjd * order, regardless of their position in the hierarchy. 485168404Spjd */ 486168404Spjd if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || 487168404Spjd prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) { 488168404Spjd compare = compare_mountpoints; 489168404Spjd clp->cl_sorted = B_TRUE; 490168404Spjd } 491168404Spjd 492168404Spjd clp->cl_pool = uu_list_pool_create("changelist_pool", 493168404Spjd sizeof (prop_changenode_t), 494168404Spjd offsetof(prop_changenode_t, cn_listnode), 495168404Spjd compare, 0); 496168404Spjd if (clp->cl_pool == NULL) { 497168404Spjd assert(uu_error() == UU_ERROR_NO_MEMORY); 498168404Spjd (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 499168404Spjd changelist_free(clp); 500168404Spjd return (NULL); 501168404Spjd } 502168404Spjd 503168404Spjd clp->cl_list = uu_list_create(clp->cl_pool, NULL, 504168404Spjd clp->cl_sorted ? UU_LIST_SORTED : 0); 505168404Spjd clp->cl_flags = flags; 506168404Spjd 507168404Spjd if (clp->cl_list == NULL) { 508168404Spjd assert(uu_error() == UU_ERROR_NO_MEMORY); 509168404Spjd (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 510168404Spjd changelist_free(clp); 511168404Spjd return (NULL); 512168404Spjd } 513168404Spjd 514168404Spjd /* 515168404Spjd * If this is a rename or the 'zoned' property, we pretend we're 516168404Spjd * changing the mountpoint and flag it so we can catch all children in 517168404Spjd * change_one(). 518168404Spjd * 519168404Spjd * Flag cl_alldependents to catch all children plus the dependents 520168404Spjd * (clones) that are not in the hierarchy. 521168404Spjd */ 522168404Spjd if (prop == ZFS_PROP_NAME) { 523168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 524168404Spjd clp->cl_alldependents = B_TRUE; 525168404Spjd } else if (prop == ZFS_PROP_ZONED) { 526168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 527168404Spjd clp->cl_allchildren = B_TRUE; 528168404Spjd } else if (prop == ZFS_PROP_CANMOUNT) { 529168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 530168404Spjd } else if (prop == ZFS_PROP_VOLSIZE) { 531168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 532168404Spjd } else { 533168404Spjd clp->cl_prop = prop; 534168404Spjd } 535168404Spjd clp->cl_realprop = prop; 536168404Spjd 537168404Spjd if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 538168404Spjd clp->cl_prop != ZFS_PROP_SHARENFS && 539168404Spjd clp->cl_prop != ZFS_PROP_SHAREISCSI) 540168404Spjd return (clp); 541168404Spjd 542168404Spjd if (clp->cl_alldependents) { 543168404Spjd if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { 544168404Spjd changelist_free(clp); 545168404Spjd return (NULL); 546168404Spjd } 547168404Spjd } else if (zfs_iter_children(zhp, change_one, clp) != 0) { 548168404Spjd changelist_free(clp); 549168404Spjd return (NULL); 550168404Spjd } 551168404Spjd 552168404Spjd /* 553168404Spjd * We have to re-open ourselves because we auto-close all the handles 554168404Spjd * and can't tell the difference. 555168404Spjd */ 556168404Spjd if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), 557168404Spjd ZFS_TYPE_ANY)) == NULL) { 558168404Spjd changelist_free(clp); 559168404Spjd return (NULL); 560168404Spjd } 561168404Spjd 562168404Spjd /* 563168404Spjd * Always add ourself to the list. We add ourselves to the end so that 564168404Spjd * we're the last to be unmounted. 565168404Spjd */ 566168404Spjd if ((cn = zfs_alloc(zhp->zfs_hdl, 567168404Spjd sizeof (prop_changenode_t))) == NULL) { 568168404Spjd zfs_close(temp); 569168404Spjd changelist_free(clp); 570168404Spjd return (NULL); 571168404Spjd } 572168404Spjd 573168404Spjd cn->cn_handle = temp; 574168404Spjd cn->cn_mounted = zfs_is_mounted(temp, NULL); 575168404Spjd cn->cn_shared = zfs_is_shared(temp); 576168404Spjd cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 577168404Spjd 578168404Spjd uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 579168404Spjd if (clp->cl_sorted) { 580168404Spjd uu_list_index_t idx; 581168404Spjd (void) uu_list_find(clp->cl_list, cn, NULL, &idx); 582168404Spjd uu_list_insert(clp->cl_list, cn, idx); 583168404Spjd } else { 584168404Spjd verify(uu_list_insert_after(clp->cl_list, 585168404Spjd uu_list_last(clp->cl_list), cn) == 0); 586168404Spjd } 587168404Spjd 588168404Spjd /* 589168404Spjd * If the property was previously 'legacy' or 'none', record this fact, 590168404Spjd * as the behavior of changelist_postfix() will be different. 591168404Spjd */ 592168404Spjd if (zfs_prop_get(zhp, prop, property, sizeof (property), 593168404Spjd NULL, NULL, 0, B_FALSE) == 0 && 594168404Spjd (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0 || 595168404Spjd strcmp(property, "off") == 0)) 596168404Spjd clp->cl_waslegacy = B_TRUE; 597168404Spjd 598168404Spjd return (clp); 599168404Spjd} 600