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/* 23219089Spjd * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 24168404Spjd * Use is subject to license terms. 25185029Spjd * 26185029Spjd * Portions Copyright 2007 Ramprakash Jelari 27226706Spjd * 28226706Spjd * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>. 29226706Spjd * All rights reserved. 30168404Spjd */ 31168404Spjd 32168404Spjd#include <libintl.h> 33168404Spjd#include <libuutil.h> 34168404Spjd#include <stddef.h> 35168404Spjd#include <stdlib.h> 36168404Spjd#include <string.h> 37168404Spjd#include <unistd.h> 38168404Spjd#include <zone.h> 39168404Spjd 40168404Spjd#include <libzfs.h> 41168404Spjd 42168404Spjd#include "libzfs_impl.h" 43168404Spjd 44168404Spjd/* 45168404Spjd * Structure to keep track of dataset state. Before changing the 'sharenfs' or 46168404Spjd * 'mountpoint' property, we record whether the filesystem was previously 47168404Spjd * mounted/shared. This prior state dictates whether we remount/reshare the 48168404Spjd * dataset after the property has been changed. 49168404Spjd * 50168404Spjd * The interface consists of the following sequence of functions: 51168404Spjd * 52168404Spjd * changelist_gather() 53168404Spjd * changelist_prefix() 54168404Spjd * < change property > 55168404Spjd * changelist_postfix() 56168404Spjd * changelist_free() 57168404Spjd * 58168404Spjd * Other interfaces: 59168404Spjd * 60168404Spjd * changelist_remove() - remove a node from a gathered list 61168404Spjd * changelist_rename() - renames all datasets appropriately when doing a rename 62168404Spjd * changelist_unshare() - unshares all the nodes in a given changelist 63168404Spjd * changelist_haszonedchild() - check if there is any child exported to 64168404Spjd * a local zone 65168404Spjd */ 66168404Spjdtypedef struct prop_changenode { 67168404Spjd zfs_handle_t *cn_handle; 68168404Spjd int cn_shared; 69168404Spjd int cn_mounted; 70168404Spjd int cn_zoned; 71185029Spjd boolean_t cn_needpost; /* is postfix() needed? */ 72168404Spjd uu_list_node_t cn_listnode; 73168404Spjd} prop_changenode_t; 74168404Spjd 75168404Spjdstruct prop_changelist { 76168404Spjd zfs_prop_t cl_prop; 77168404Spjd zfs_prop_t cl_realprop; 78185029Spjd zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */ 79168404Spjd uu_list_pool_t *cl_pool; 80168404Spjd uu_list_t *cl_list; 81168404Spjd boolean_t cl_waslegacy; 82168404Spjd boolean_t cl_allchildren; 83168404Spjd boolean_t cl_alldependents; 84185029Spjd int cl_mflags; /* Mount flags */ 85185029Spjd int cl_gflags; /* Gather request flags */ 86168404Spjd boolean_t cl_haszonedchild; 87168404Spjd boolean_t cl_sorted; 88168404Spjd}; 89168404Spjd 90168404Spjd/* 91168404Spjd * If the property is 'mountpoint', go through and unmount filesystems as 92168404Spjd * necessary. We don't do the same for 'sharenfs', because we can just re-share 93185029Spjd * with different options without interrupting service. We do handle 'sharesmb' 94185029Spjd * since there may be old resource names that need to be removed. 95168404Spjd */ 96168404Spjdint 97168404Spjdchangelist_prefix(prop_changelist_t *clp) 98168404Spjd{ 99168404Spjd prop_changenode_t *cn; 100168404Spjd int ret = 0; 101168404Spjd 102185029Spjd if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 103185029Spjd clp->cl_prop != ZFS_PROP_SHARESMB) 104168404Spjd return (0); 105168404Spjd 106168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 107168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 108185029Spjd 109185029Spjd /* if a previous loop failed, set the remaining to false */ 110185029Spjd if (ret == -1) { 111185029Spjd cn->cn_needpost = B_FALSE; 112185029Spjd continue; 113185029Spjd } 114185029Spjd 115168404Spjd /* 116168404Spjd * If we are in the global zone, but this dataset is exported 117168404Spjd * to a local zone, do nothing. 118168404Spjd */ 119168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 120168404Spjd continue; 121168404Spjd 122219089Spjd if (!ZFS_IS_VOLUME(cn->cn_handle)) { 123185029Spjd /* 124185029Spjd * Do the property specific processing. 125185029Spjd */ 126185029Spjd switch (clp->cl_prop) { 127185029Spjd case ZFS_PROP_MOUNTPOINT: 128226705Spjd if (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) 129226676Spjd break; 130185029Spjd if (zfs_unmount(cn->cn_handle, NULL, 131185029Spjd clp->cl_mflags) != 0) { 132185029Spjd ret = -1; 133185029Spjd cn->cn_needpost = B_FALSE; 134185029Spjd } 135185029Spjd break; 136185029Spjd case ZFS_PROP_SHARESMB: 137185029Spjd (void) zfs_unshare_smb(cn->cn_handle, NULL); 138185029Spjd break; 139185029Spjd } 140185029Spjd } 141168404Spjd } 142168404Spjd 143185029Spjd if (ret == -1) 144185029Spjd (void) changelist_postfix(clp); 145185029Spjd 146168404Spjd return (ret); 147168404Spjd} 148168404Spjd 149168404Spjd/* 150168404Spjd * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or 151168404Spjd * reshare the filesystems as necessary. In changelist_gather() we recorded 152168404Spjd * whether the filesystem was previously shared or mounted. The action we take 153168404Spjd * depends on the previous state, and whether the value was previously 'legacy'. 154168404Spjd * For non-legacy properties, we only remount/reshare the filesystem if it was 155168404Spjd * previously mounted/shared. Otherwise, we always remount/reshare the 156168404Spjd * filesystem. 157168404Spjd */ 158168404Spjdint 159168404Spjdchangelist_postfix(prop_changelist_t *clp) 160168404Spjd{ 161168404Spjd prop_changenode_t *cn; 162168404Spjd char shareopts[ZFS_MAXPROPLEN]; 163185029Spjd int errors = 0; 164185029Spjd libzfs_handle_t *hdl; 165168404Spjd 166168404Spjd /* 167168404Spjd * If we're changing the mountpoint, attempt to destroy the underlying 168168404Spjd * mountpoint. All other datasets will have inherited from this dataset 169168404Spjd * (in which case their mountpoints exist in the filesystem in the new 170168404Spjd * location), or have explicit mountpoints set (in which case they won't 171168404Spjd * be in the changelist). 172168404Spjd */ 173168404Spjd if ((cn = uu_list_last(clp->cl_list)) == NULL) 174168404Spjd return (0); 175168404Spjd 176226705Spjd if (clp->cl_prop == ZFS_PROP_MOUNTPOINT && 177226705Spjd !(clp->cl_gflags & CL_GATHER_DONT_UNMOUNT)) { 178168404Spjd remove_mountpoint(cn->cn_handle); 179226705Spjd } 180168404Spjd 181168404Spjd /* 182185029Spjd * It is possible that the changelist_prefix() used libshare 183185029Spjd * to unshare some entries. Since libshare caches data, an 184185029Spjd * attempt to reshare during postfix can fail unless libshare 185185029Spjd * is uninitialized here so that it will reinitialize later. 186185029Spjd */ 187185029Spjd if (cn->cn_handle != NULL) { 188185029Spjd hdl = cn->cn_handle->zfs_hdl; 189185029Spjd assert(hdl != NULL); 190185029Spjd zfs_uninit_libshare(hdl); 191185029Spjd } 192185029Spjd 193185029Spjd /* 194168404Spjd * We walk the datasets in reverse, because we want to mount any parent 195185029Spjd * datasets before mounting the children. We walk all datasets even if 196185029Spjd * there are errors. 197168404Spjd */ 198168404Spjd for (cn = uu_list_last(clp->cl_list); cn != NULL; 199168404Spjd cn = uu_list_prev(clp->cl_list, cn)) { 200185029Spjd 201185029Spjd boolean_t sharenfs; 202185029Spjd boolean_t sharesmb; 203209962Smm boolean_t mounted; 204185029Spjd 205168404Spjd /* 206168404Spjd * If we are in the global zone, but this dataset is exported 207168404Spjd * to a local zone, do nothing. 208168404Spjd */ 209168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 210168404Spjd continue; 211168404Spjd 212185029Spjd /* Only do post-processing if it's required */ 213185029Spjd if (!cn->cn_needpost) 214185029Spjd continue; 215185029Spjd cn->cn_needpost = B_FALSE; 216185029Spjd 217168404Spjd zfs_refresh_properties(cn->cn_handle); 218168404Spjd 219219089Spjd if (ZFS_IS_VOLUME(cn->cn_handle)) 220168404Spjd continue; 221168404Spjd 222185029Spjd /* 223185029Spjd * Remount if previously mounted or mountpoint was legacy, 224185029Spjd * or sharenfs or sharesmb property is set. 225185029Spjd */ 226185029Spjd sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, 227185029Spjd shareopts, sizeof (shareopts), NULL, NULL, 0, 228185029Spjd B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); 229185029Spjd 230185029Spjd sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB, 231185029Spjd shareopts, sizeof (shareopts), NULL, NULL, 0, 232185029Spjd B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); 233185029Spjd 234226705Spjd mounted = (clp->cl_gflags & CL_GATHER_DONT_UNMOUNT) || 235226705Spjd zfs_is_mounted(cn->cn_handle, NULL); 236168404Spjd 237209962Smm if (!mounted && (cn->cn_mounted || 238209962Smm ((sharenfs || sharesmb || clp->cl_waslegacy) && 239209962Smm (zfs_prop_get_int(cn->cn_handle, 240209962Smm ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) { 241209962Smm 242209962Smm if (zfs_mount(cn->cn_handle, NULL, 0) != 0) 243209962Smm errors++; 244209962Smm else 245209962Smm mounted = TRUE; 246209962Smm } 247209962Smm 248168404Spjd /* 249209962Smm * If the file system is mounted we always re-share even 250209962Smm * if the filesystem is currently shared, so that we can 251209962Smm * adopt any new options. 252168404Spjd */ 253209962Smm if (sharenfs && mounted) 254185029Spjd errors += zfs_share_nfs(cn->cn_handle); 255185029Spjd else if (cn->cn_shared || clp->cl_waslegacy) 256185029Spjd errors += zfs_unshare_nfs(cn->cn_handle, NULL); 257209962Smm if (sharesmb && mounted) 258185029Spjd errors += zfs_share_smb(cn->cn_handle); 259185029Spjd else if (cn->cn_shared || clp->cl_waslegacy) 260185029Spjd errors += zfs_unshare_smb(cn->cn_handle, NULL); 261168404Spjd } 262168404Spjd 263185029Spjd return (errors ? -1 : 0); 264168404Spjd} 265168404Spjd 266168404Spjd/* 267168404Spjd * Is this "dataset" a child of "parent"? 268168404Spjd */ 269185029Spjdboolean_t 270168404Spjdisa_child_of(const char *dataset, const char *parent) 271168404Spjd{ 272168404Spjd int len; 273168404Spjd 274168404Spjd len = strlen(parent); 275168404Spjd 276168404Spjd if (strncmp(dataset, parent, len) == 0 && 277168404Spjd (dataset[len] == '@' || dataset[len] == '/' || 278168404Spjd dataset[len] == '\0')) 279168404Spjd return (B_TRUE); 280168404Spjd else 281168404Spjd return (B_FALSE); 282168404Spjd 283168404Spjd} 284168404Spjd 285168404Spjd/* 286168404Spjd * If we rename a filesystem, child filesystem handles are no longer valid 287168404Spjd * since we identify each dataset by its name in the ZFS namespace. As a 288168404Spjd * result, we have to go through and fix up all the names appropriately. We 289168404Spjd * could do this automatically if libzfs kept track of all open handles, but 290168404Spjd * this is a lot less work. 291168404Spjd */ 292168404Spjdvoid 293168404Spjdchangelist_rename(prop_changelist_t *clp, const char *src, const char *dst) 294168404Spjd{ 295168404Spjd prop_changenode_t *cn; 296168404Spjd char newname[ZFS_MAXNAMELEN]; 297168404Spjd 298168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 299168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 300168404Spjd /* 301168404Spjd * Do not rename a clone that's not in the source hierarchy. 302168404Spjd */ 303168404Spjd if (!isa_child_of(cn->cn_handle->zfs_name, src)) 304168404Spjd continue; 305168404Spjd 306168404Spjd /* 307168404Spjd * Destroy the previous mountpoint if needed. 308168404Spjd */ 309168404Spjd remove_mountpoint(cn->cn_handle); 310168404Spjd 311168404Spjd (void) strlcpy(newname, dst, sizeof (newname)); 312168404Spjd (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src)); 313168404Spjd 314168404Spjd (void) strlcpy(cn->cn_handle->zfs_name, newname, 315168404Spjd sizeof (cn->cn_handle->zfs_name)); 316168404Spjd } 317168404Spjd} 318168404Spjd 319168404Spjd/* 320185029Spjd * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property, 321185029Spjd * unshare all the datasets in the list. 322168404Spjd */ 323168404Spjdint 324185029Spjdchangelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto) 325168404Spjd{ 326168404Spjd prop_changenode_t *cn; 327168404Spjd int ret = 0; 328168404Spjd 329185029Spjd if (clp->cl_prop != ZFS_PROP_SHARENFS && 330185029Spjd clp->cl_prop != ZFS_PROP_SHARESMB) 331168404Spjd return (0); 332168404Spjd 333168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 334168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 335185029Spjd if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0) 336168404Spjd ret = -1; 337168404Spjd } 338168404Spjd 339168404Spjd return (ret); 340168404Spjd} 341168404Spjd 342168404Spjd/* 343168404Spjd * Check if there is any child exported to a local zone in a given changelist. 344168404Spjd * This information has already been recorded while gathering the changelist 345168404Spjd * via changelist_gather(). 346168404Spjd */ 347168404Spjdint 348168404Spjdchangelist_haszonedchild(prop_changelist_t *clp) 349168404Spjd{ 350168404Spjd return (clp->cl_haszonedchild); 351168404Spjd} 352168404Spjd 353168404Spjd/* 354168404Spjd * Remove a node from a gathered list. 355168404Spjd */ 356168404Spjdvoid 357185029Spjdchangelist_remove(prop_changelist_t *clp, const char *name) 358168404Spjd{ 359168404Spjd prop_changenode_t *cn; 360168404Spjd 361168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 362168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 363168404Spjd 364185029Spjd if (strcmp(cn->cn_handle->zfs_name, name) == 0) { 365168404Spjd uu_list_remove(clp->cl_list, cn); 366168404Spjd zfs_close(cn->cn_handle); 367168404Spjd free(cn); 368168404Spjd return; 369168404Spjd } 370168404Spjd } 371168404Spjd} 372168404Spjd 373168404Spjd/* 374168404Spjd * Release any memory associated with a changelist. 375168404Spjd */ 376168404Spjdvoid 377168404Spjdchangelist_free(prop_changelist_t *clp) 378168404Spjd{ 379168404Spjd prop_changenode_t *cn; 380168926Spjd void *cookie; 381168404Spjd 382168404Spjd if (clp->cl_list) { 383168926Spjd cookie = NULL; 384168926Spjd while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) { 385168404Spjd zfs_close(cn->cn_handle); 386168404Spjd free(cn); 387168404Spjd } 388168404Spjd 389168404Spjd uu_list_destroy(clp->cl_list); 390168404Spjd } 391168404Spjd if (clp->cl_pool) 392168404Spjd uu_list_pool_destroy(clp->cl_pool); 393168404Spjd 394168404Spjd free(clp); 395168404Spjd} 396168404Spjd 397168404Spjdstatic int 398168404Spjdchange_one(zfs_handle_t *zhp, void *data) 399168404Spjd{ 400168404Spjd prop_changelist_t *clp = data; 401168404Spjd char property[ZFS_MAXPROPLEN]; 402168404Spjd char where[64]; 403168404Spjd prop_changenode_t *cn; 404185029Spjd zprop_source_t sourcetype; 405185029Spjd zprop_source_t share_sourcetype; 406168404Spjd 407168404Spjd /* 408168404Spjd * We only want to unmount/unshare those filesystems that may inherit 409168404Spjd * from the target filesystem. If we find any filesystem with a 410168404Spjd * locally set mountpoint, we ignore any children since changing the 411168404Spjd * property will not affect them. If this is a rename, we iterate 412168404Spjd * over all children regardless, since we need them unmounted in 413168404Spjd * order to do the rename. Also, if this is a volume and we're doing 414168404Spjd * a rename, then always add it to the changelist. 415168404Spjd */ 416168404Spjd 417168404Spjd if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) && 418168404Spjd zfs_prop_get(zhp, clp->cl_prop, property, 419168404Spjd sizeof (property), &sourcetype, where, sizeof (where), 420168404Spjd B_FALSE) != 0) { 421168404Spjd zfs_close(zhp); 422168404Spjd return (0); 423168404Spjd } 424168404Spjd 425185029Spjd /* 426185029Spjd * If we are "watching" sharenfs or sharesmb 427185029Spjd * then check out the companion property which is tracked 428185029Spjd * in cl_shareprop 429185029Spjd */ 430185029Spjd if (clp->cl_shareprop != ZPROP_INVAL && 431185029Spjd zfs_prop_get(zhp, clp->cl_shareprop, property, 432185029Spjd sizeof (property), &share_sourcetype, where, sizeof (where), 433185029Spjd B_FALSE) != 0) { 434185029Spjd zfs_close(zhp); 435185029Spjd return (0); 436185029Spjd } 437185029Spjd 438168404Spjd if (clp->cl_alldependents || clp->cl_allchildren || 439185029Spjd sourcetype == ZPROP_SRC_DEFAULT || 440185029Spjd sourcetype == ZPROP_SRC_INHERITED || 441185029Spjd (clp->cl_shareprop != ZPROP_INVAL && 442185029Spjd (share_sourcetype == ZPROP_SRC_DEFAULT || 443185029Spjd share_sourcetype == ZPROP_SRC_INHERITED))) { 444168404Spjd if ((cn = zfs_alloc(zfs_get_handle(zhp), 445168404Spjd sizeof (prop_changenode_t))) == NULL) { 446168404Spjd zfs_close(zhp); 447168404Spjd return (-1); 448168404Spjd } 449168404Spjd 450168404Spjd cn->cn_handle = zhp; 451185029Spjd cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || 452185029Spjd zfs_is_mounted(zhp, NULL); 453168404Spjd cn->cn_shared = zfs_is_shared(zhp); 454168404Spjd cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 455185029Spjd cn->cn_needpost = B_TRUE; 456168404Spjd 457168404Spjd /* Indicate if any child is exported to a local zone. */ 458168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 459168404Spjd clp->cl_haszonedchild = B_TRUE; 460168404Spjd 461168404Spjd uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 462168404Spjd 463168404Spjd if (clp->cl_sorted) { 464168404Spjd uu_list_index_t idx; 465168404Spjd 466168404Spjd (void) uu_list_find(clp->cl_list, cn, NULL, 467168404Spjd &idx); 468168404Spjd uu_list_insert(clp->cl_list, cn, idx); 469168404Spjd } else { 470219089Spjd /* 471219089Spjd * Add this child to beginning of the list. Children 472219089Spjd * below this one in the hierarchy will get added above 473219089Spjd * this one in the list. This produces a list in 474219089Spjd * reverse dataset name order. 475219089Spjd * This is necessary when the original mountpoint 476219089Spjd * is legacy or none. 477219089Spjd */ 478168404Spjd verify(uu_list_insert_before(clp->cl_list, 479168926Spjd uu_list_first(clp->cl_list), cn) == 0); 480168404Spjd } 481168404Spjd 482168404Spjd if (!clp->cl_alldependents) 483168404Spjd return (zfs_iter_children(zhp, change_one, data)); 484168404Spjd } else { 485168404Spjd zfs_close(zhp); 486168404Spjd } 487168404Spjd 488168404Spjd return (0); 489168404Spjd} 490168404Spjd 491168404Spjd/*ARGSUSED*/ 492168404Spjdstatic int 493168404Spjdcompare_mountpoints(const void *a, const void *b, void *unused) 494168404Spjd{ 495168404Spjd const prop_changenode_t *ca = a; 496168404Spjd const prop_changenode_t *cb = b; 497168404Spjd 498168404Spjd char mounta[MAXPATHLEN]; 499168404Spjd char mountb[MAXPATHLEN]; 500168404Spjd 501168404Spjd boolean_t hasmounta, hasmountb; 502168404Spjd 503168404Spjd /* 504168404Spjd * When unsharing or unmounting filesystems, we need to do it in 505168404Spjd * mountpoint order. This allows the user to have a mountpoint 506168404Spjd * hierarchy that is different from the dataset hierarchy, and still 507168404Spjd * allow it to be changed. However, if either dataset doesn't have a 508168404Spjd * mountpoint (because it is a volume or a snapshot), we place it at the 509168404Spjd * end of the list, because it doesn't affect our change at all. 510168404Spjd */ 511168404Spjd hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta, 512168404Spjd sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 513168404Spjd hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb, 514168404Spjd sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 515168404Spjd 516168404Spjd if (!hasmounta && hasmountb) 517168404Spjd return (-1); 518168404Spjd else if (hasmounta && !hasmountb) 519168404Spjd return (1); 520168404Spjd else if (!hasmounta && !hasmountb) 521168404Spjd return (0); 522168404Spjd else 523168404Spjd return (strcmp(mountb, mounta)); 524168404Spjd} 525168404Spjd 526168404Spjd/* 527168404Spjd * Given a ZFS handle and a property, construct a complete list of datasets 528168404Spjd * that need to be modified as part of this process. For anything but the 529168404Spjd * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. 530168404Spjd * Otherwise, we iterate over all children and look for any datasets that 531168404Spjd * inherit the property. For each such dataset, we add it to the list and 532168404Spjd * mark whether it was shared beforehand. 533168404Spjd */ 534168404Spjdprop_changelist_t * 535185029Spjdchangelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags, 536185029Spjd int mnt_flags) 537168404Spjd{ 538168404Spjd prop_changelist_t *clp; 539168404Spjd prop_changenode_t *cn; 540168404Spjd zfs_handle_t *temp; 541168404Spjd char property[ZFS_MAXPROPLEN]; 542168404Spjd uu_compare_fn_t *compare = NULL; 543219089Spjd boolean_t legacy = B_FALSE; 544168404Spjd 545168404Spjd if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) 546168404Spjd return (NULL); 547168404Spjd 548168404Spjd /* 549168404Spjd * For mountpoint-related tasks, we want to sort everything by 550168404Spjd * mountpoint, so that we mount and unmount them in the appropriate 551168404Spjd * order, regardless of their position in the hierarchy. 552168404Spjd */ 553168404Spjd if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || 554185029Spjd prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS || 555185029Spjd prop == ZFS_PROP_SHARESMB) { 556219089Spjd 557219089Spjd if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 558219089Spjd property, sizeof (property), 559219089Spjd NULL, NULL, 0, B_FALSE) == 0 && 560219089Spjd (strcmp(property, "legacy") == 0 || 561219089Spjd strcmp(property, "none") == 0)) { 562219089Spjd 563219089Spjd legacy = B_TRUE; 564219089Spjd } 565219089Spjd if (!legacy) { 566219089Spjd compare = compare_mountpoints; 567219089Spjd clp->cl_sorted = B_TRUE; 568219089Spjd } 569168404Spjd } 570168404Spjd 571168404Spjd clp->cl_pool = uu_list_pool_create("changelist_pool", 572168404Spjd sizeof (prop_changenode_t), 573168404Spjd offsetof(prop_changenode_t, cn_listnode), 574168404Spjd compare, 0); 575168404Spjd if (clp->cl_pool == NULL) { 576168404Spjd assert(uu_error() == UU_ERROR_NO_MEMORY); 577168404Spjd (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 578168404Spjd changelist_free(clp); 579168404Spjd return (NULL); 580168404Spjd } 581168404Spjd 582168404Spjd clp->cl_list = uu_list_create(clp->cl_pool, NULL, 583168404Spjd clp->cl_sorted ? UU_LIST_SORTED : 0); 584185029Spjd clp->cl_gflags = gather_flags; 585185029Spjd clp->cl_mflags = mnt_flags; 586168404Spjd 587168404Spjd if (clp->cl_list == NULL) { 588168404Spjd assert(uu_error() == UU_ERROR_NO_MEMORY); 589168404Spjd (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 590168404Spjd changelist_free(clp); 591168404Spjd return (NULL); 592168404Spjd } 593168404Spjd 594168404Spjd /* 595168404Spjd * If this is a rename or the 'zoned' property, we pretend we're 596168404Spjd * changing the mountpoint and flag it so we can catch all children in 597168404Spjd * change_one(). 598168404Spjd * 599168404Spjd * Flag cl_alldependents to catch all children plus the dependents 600168404Spjd * (clones) that are not in the hierarchy. 601168404Spjd */ 602168404Spjd if (prop == ZFS_PROP_NAME) { 603168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 604168404Spjd clp->cl_alldependents = B_TRUE; 605168404Spjd } else if (prop == ZFS_PROP_ZONED) { 606168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 607168404Spjd clp->cl_allchildren = B_TRUE; 608168404Spjd } else if (prop == ZFS_PROP_CANMOUNT) { 609168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 610168404Spjd } else if (prop == ZFS_PROP_VOLSIZE) { 611168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 612168404Spjd } else { 613168404Spjd clp->cl_prop = prop; 614168404Spjd } 615168404Spjd clp->cl_realprop = prop; 616168404Spjd 617168404Spjd if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 618168404Spjd clp->cl_prop != ZFS_PROP_SHARENFS && 619219089Spjd clp->cl_prop != ZFS_PROP_SHARESMB) 620168404Spjd return (clp); 621168404Spjd 622185029Spjd /* 623185029Spjd * If watching SHARENFS or SHARESMB then 624185029Spjd * also watch its companion property. 625185029Spjd */ 626185029Spjd if (clp->cl_prop == ZFS_PROP_SHARENFS) 627185029Spjd clp->cl_shareprop = ZFS_PROP_SHARESMB; 628185029Spjd else if (clp->cl_prop == ZFS_PROP_SHARESMB) 629185029Spjd clp->cl_shareprop = ZFS_PROP_SHARENFS; 630185029Spjd 631168404Spjd if (clp->cl_alldependents) { 632168404Spjd if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { 633168404Spjd changelist_free(clp); 634168404Spjd return (NULL); 635168404Spjd } 636168404Spjd } else if (zfs_iter_children(zhp, change_one, clp) != 0) { 637168404Spjd changelist_free(clp); 638168404Spjd return (NULL); 639168404Spjd } 640168404Spjd 641168404Spjd /* 642168404Spjd * We have to re-open ourselves because we auto-close all the handles 643168404Spjd * and can't tell the difference. 644168404Spjd */ 645168404Spjd if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), 646185029Spjd ZFS_TYPE_DATASET)) == NULL) { 647168404Spjd changelist_free(clp); 648168404Spjd return (NULL); 649168404Spjd } 650168404Spjd 651168404Spjd /* 652168404Spjd * Always add ourself to the list. We add ourselves to the end so that 653168404Spjd * we're the last to be unmounted. 654168404Spjd */ 655168404Spjd if ((cn = zfs_alloc(zhp->zfs_hdl, 656168404Spjd sizeof (prop_changenode_t))) == NULL) { 657168404Spjd zfs_close(temp); 658168404Spjd changelist_free(clp); 659168404Spjd return (NULL); 660168404Spjd } 661168404Spjd 662168404Spjd cn->cn_handle = temp; 663185029Spjd cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || 664185029Spjd zfs_is_mounted(temp, NULL); 665168404Spjd cn->cn_shared = zfs_is_shared(temp); 666168404Spjd cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 667185029Spjd cn->cn_needpost = B_TRUE; 668168404Spjd 669168404Spjd uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 670168404Spjd if (clp->cl_sorted) { 671168404Spjd uu_list_index_t idx; 672168404Spjd (void) uu_list_find(clp->cl_list, cn, NULL, &idx); 673168404Spjd uu_list_insert(clp->cl_list, cn, idx); 674168404Spjd } else { 675219089Spjd /* 676219089Spjd * Add the target dataset to the end of the list. 677219089Spjd * The list is not really unsorted. The list will be 678219089Spjd * in reverse dataset name order. This is necessary 679219089Spjd * when the original mountpoint is legacy or none. 680219089Spjd */ 681168404Spjd verify(uu_list_insert_after(clp->cl_list, 682168404Spjd uu_list_last(clp->cl_list), cn) == 0); 683168404Spjd } 684168404Spjd 685168404Spjd /* 686185029Spjd * If the mountpoint property was previously 'legacy', or 'none', 687185029Spjd * record it as the behavior of changelist_postfix() will be different. 688168404Spjd */ 689219089Spjd if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) { 690185029Spjd /* 691185029Spjd * do not automatically mount ex-legacy datasets if 692185029Spjd * we specifically set canmount to noauto 693185029Spjd */ 694185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) != 695185029Spjd ZFS_CANMOUNT_NOAUTO) 696185029Spjd clp->cl_waslegacy = B_TRUE; 697185029Spjd } 698168404Spjd 699168404Spjd return (clp); 700168404Spjd} 701