libzfs_changelist.c revision 185029
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/* 23185029Spjd * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24168404Spjd * Use is subject to license terms. 25185029Spjd * 26185029Spjd * Portions Copyright 2007 Ramprakash Jelari 27168404Spjd */ 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; 68185029Spjd boolean_t cn_needpost; /* is postfix() needed? */ 69168404Spjd uu_list_node_t cn_listnode; 70168404Spjd} prop_changenode_t; 71168404Spjd 72168404Spjdstruct prop_changelist { 73168404Spjd zfs_prop_t cl_prop; 74168404Spjd zfs_prop_t cl_realprop; 75185029Spjd zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */ 76168404Spjd uu_list_pool_t *cl_pool; 77168404Spjd uu_list_t *cl_list; 78168404Spjd boolean_t cl_waslegacy; 79168404Spjd boolean_t cl_allchildren; 80168404Spjd boolean_t cl_alldependents; 81185029Spjd int cl_mflags; /* Mount flags */ 82185029Spjd int cl_gflags; /* Gather request flags */ 83168404Spjd boolean_t cl_haszonedchild; 84168404Spjd boolean_t cl_sorted; 85168404Spjd}; 86168404Spjd 87168404Spjd/* 88168404Spjd * If the property is 'mountpoint', go through and unmount filesystems as 89168404Spjd * necessary. We don't do the same for 'sharenfs', because we can just re-share 90185029Spjd * with different options without interrupting service. We do handle 'sharesmb' 91185029Spjd * since there may be old resource names that need to be removed. 92168404Spjd */ 93168404Spjdint 94168404Spjdchangelist_prefix(prop_changelist_t *clp) 95168404Spjd{ 96168404Spjd prop_changenode_t *cn; 97168404Spjd int ret = 0; 98168404Spjd 99185029Spjd if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 100185029Spjd clp->cl_prop != ZFS_PROP_SHARESMB) 101168404Spjd return (0); 102168404Spjd 103168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 104168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 105185029Spjd 106185029Spjd /* if a previous loop failed, set the remaining to false */ 107185029Spjd if (ret == -1) { 108185029Spjd cn->cn_needpost = B_FALSE; 109185029Spjd continue; 110185029Spjd } 111185029Spjd 112168404Spjd /* 113168404Spjd * If we are in the global zone, but this dataset is exported 114168404Spjd * to a local zone, do nothing. 115168404Spjd */ 116168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 117168404Spjd continue; 118168404Spjd 119168404Spjd if (ZFS_IS_VOLUME(cn->cn_handle)) { 120168404Spjd switch (clp->cl_realprop) { 121168404Spjd case ZFS_PROP_NAME: 122168404Spjd /* 123168404Spjd * If this was a rename, unshare the zvol, and 124168404Spjd * remove the /dev/zvol links. 125168404Spjd */ 126168404Spjd (void) zfs_unshare_iscsi(cn->cn_handle); 127168404Spjd 128168404Spjd if (zvol_remove_link(cn->cn_handle->zfs_hdl, 129185029Spjd cn->cn_handle->zfs_name) != 0) { 130168404Spjd ret = -1; 131185029Spjd cn->cn_needpost = B_FALSE; 132185029Spjd (void) zfs_share_iscsi(cn->cn_handle); 133185029Spjd } 134168404Spjd break; 135168404Spjd 136168404Spjd case ZFS_PROP_VOLSIZE: 137168404Spjd /* 138168404Spjd * If this was a change to the volume size, we 139168404Spjd * need to unshare and reshare the volume. 140168404Spjd */ 141168404Spjd (void) zfs_unshare_iscsi(cn->cn_handle); 142168404Spjd break; 143168404Spjd } 144185029Spjd } else { 145185029Spjd /* 146185029Spjd * Do the property specific processing. 147185029Spjd */ 148185029Spjd switch (clp->cl_prop) { 149185029Spjd case ZFS_PROP_MOUNTPOINT: 150185029Spjd if (zfs_unmount(cn->cn_handle, NULL, 151185029Spjd clp->cl_mflags) != 0) { 152185029Spjd ret = -1; 153185029Spjd cn->cn_needpost = B_FALSE; 154185029Spjd } 155185029Spjd break; 156185029Spjd case ZFS_PROP_SHARESMB: 157185029Spjd (void) zfs_unshare_smb(cn->cn_handle, NULL); 158185029Spjd break; 159185029Spjd } 160185029Spjd } 161168404Spjd } 162168404Spjd 163185029Spjd if (ret == -1) 164185029Spjd (void) changelist_postfix(clp); 165185029Spjd 166168404Spjd return (ret); 167168404Spjd} 168168404Spjd 169168404Spjd/* 170168404Spjd * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or 171168404Spjd * reshare the filesystems as necessary. In changelist_gather() we recorded 172168404Spjd * whether the filesystem was previously shared or mounted. The action we take 173168404Spjd * depends on the previous state, and whether the value was previously 'legacy'. 174168404Spjd * For non-legacy properties, we only remount/reshare the filesystem if it was 175168404Spjd * previously mounted/shared. Otherwise, we always remount/reshare the 176168404Spjd * filesystem. 177168404Spjd */ 178168404Spjdint 179168404Spjdchangelist_postfix(prop_changelist_t *clp) 180168404Spjd{ 181168404Spjd prop_changenode_t *cn; 182168404Spjd char shareopts[ZFS_MAXPROPLEN]; 183185029Spjd int errors = 0; 184185029Spjd libzfs_handle_t *hdl; 185168404Spjd 186168404Spjd /* 187168404Spjd * If we're changing the mountpoint, attempt to destroy the underlying 188168404Spjd * mountpoint. All other datasets will have inherited from this dataset 189168404Spjd * (in which case their mountpoints exist in the filesystem in the new 190168404Spjd * location), or have explicit mountpoints set (in which case they won't 191168404Spjd * be in the changelist). 192168404Spjd */ 193168404Spjd if ((cn = uu_list_last(clp->cl_list)) == NULL) 194168404Spjd return (0); 195168404Spjd 196168404Spjd if (clp->cl_prop == ZFS_PROP_MOUNTPOINT) 197168404Spjd remove_mountpoint(cn->cn_handle); 198168404Spjd 199168404Spjd /* 200185029Spjd * It is possible that the changelist_prefix() used libshare 201185029Spjd * to unshare some entries. Since libshare caches data, an 202185029Spjd * attempt to reshare during postfix can fail unless libshare 203185029Spjd * is uninitialized here so that it will reinitialize later. 204185029Spjd */ 205185029Spjd if (cn->cn_handle != NULL) { 206185029Spjd hdl = cn->cn_handle->zfs_hdl; 207185029Spjd assert(hdl != NULL); 208185029Spjd zfs_uninit_libshare(hdl); 209185029Spjd } 210185029Spjd 211185029Spjd /* 212168404Spjd * We walk the datasets in reverse, because we want to mount any parent 213185029Spjd * datasets before mounting the children. We walk all datasets even if 214185029Spjd * there are errors. 215168404Spjd */ 216168404Spjd for (cn = uu_list_last(clp->cl_list); cn != NULL; 217168404Spjd cn = uu_list_prev(clp->cl_list, cn)) { 218185029Spjd 219185029Spjd boolean_t sharenfs; 220185029Spjd boolean_t sharesmb; 221185029Spjd 222168404Spjd /* 223168404Spjd * If we are in the global zone, but this dataset is exported 224168404Spjd * to a local zone, do nothing. 225168404Spjd */ 226168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 227168404Spjd continue; 228168404Spjd 229185029Spjd /* Only do post-processing if it's required */ 230185029Spjd if (!cn->cn_needpost) 231185029Spjd continue; 232185029Spjd cn->cn_needpost = B_FALSE; 233185029Spjd 234168404Spjd zfs_refresh_properties(cn->cn_handle); 235168404Spjd 236168404Spjd if (ZFS_IS_VOLUME(cn->cn_handle)) { 237168404Spjd /* 238168404Spjd * If we're doing a rename, recreate the /dev/zvol 239168404Spjd * links. 240168404Spjd */ 241168404Spjd if (clp->cl_realprop == ZFS_PROP_NAME && 242168404Spjd zvol_create_link(cn->cn_handle->zfs_hdl, 243168404Spjd cn->cn_handle->zfs_name) != 0) { 244185029Spjd errors++; 245168404Spjd } else if (cn->cn_shared || 246168404Spjd clp->cl_prop == ZFS_PROP_SHAREISCSI) { 247168404Spjd if (zfs_prop_get(cn->cn_handle, 248168404Spjd ZFS_PROP_SHAREISCSI, shareopts, 249168404Spjd sizeof (shareopts), NULL, NULL, 0, 250168404Spjd B_FALSE) == 0 && 251168404Spjd strcmp(shareopts, "off") == 0) { 252185029Spjd errors += 253185029Spjd zfs_unshare_iscsi(cn->cn_handle); 254168404Spjd } else { 255185029Spjd errors += 256185029Spjd zfs_share_iscsi(cn->cn_handle); 257168404Spjd } 258168404Spjd } 259168404Spjd 260168404Spjd continue; 261168404Spjd } 262168404Spjd 263185029Spjd /* 264185029Spjd * Remount if previously mounted or mountpoint was legacy, 265185029Spjd * or sharenfs or sharesmb property is set. 266185029Spjd */ 267185029Spjd sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, 268185029Spjd shareopts, sizeof (shareopts), NULL, NULL, 0, 269185029Spjd B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); 270185029Spjd 271185029Spjd sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB, 272185029Spjd shareopts, sizeof (shareopts), NULL, NULL, 0, 273185029Spjd B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); 274185029Spjd 275185029Spjd if ((cn->cn_mounted || clp->cl_waslegacy || sharenfs || 276185029Spjd sharesmb) && !zfs_is_mounted(cn->cn_handle, NULL) && 277168404Spjd zfs_mount(cn->cn_handle, NULL, 0) != 0) 278185029Spjd errors++; 279168404Spjd 280168404Spjd /* 281168404Spjd * We always re-share even if the filesystem is currently 282168404Spjd * shared, so that we can adopt any new options. 283168404Spjd */ 284185029Spjd if (sharenfs) 285185029Spjd errors += zfs_share_nfs(cn->cn_handle); 286185029Spjd else if (cn->cn_shared || clp->cl_waslegacy) 287185029Spjd errors += zfs_unshare_nfs(cn->cn_handle, NULL); 288185029Spjd if (sharesmb) 289185029Spjd errors += zfs_share_smb(cn->cn_handle); 290185029Spjd else if (cn->cn_shared || clp->cl_waslegacy) 291185029Spjd errors += zfs_unshare_smb(cn->cn_handle, NULL); 292168404Spjd } 293168404Spjd 294185029Spjd return (errors ? -1 : 0); 295168404Spjd} 296168404Spjd 297168404Spjd/* 298168404Spjd * Is this "dataset" a child of "parent"? 299168404Spjd */ 300185029Spjdboolean_t 301168404Spjdisa_child_of(const char *dataset, const char *parent) 302168404Spjd{ 303168404Spjd int len; 304168404Spjd 305168404Spjd len = strlen(parent); 306168404Spjd 307168404Spjd if (strncmp(dataset, parent, len) == 0 && 308168404Spjd (dataset[len] == '@' || dataset[len] == '/' || 309168404Spjd dataset[len] == '\0')) 310168404Spjd return (B_TRUE); 311168404Spjd else 312168404Spjd return (B_FALSE); 313168404Spjd 314168404Spjd} 315168404Spjd 316168404Spjd/* 317168404Spjd * If we rename a filesystem, child filesystem handles are no longer valid 318168404Spjd * since we identify each dataset by its name in the ZFS namespace. As a 319168404Spjd * result, we have to go through and fix up all the names appropriately. We 320168404Spjd * could do this automatically if libzfs kept track of all open handles, but 321168404Spjd * this is a lot less work. 322168404Spjd */ 323168404Spjdvoid 324168404Spjdchangelist_rename(prop_changelist_t *clp, const char *src, const char *dst) 325168404Spjd{ 326168404Spjd prop_changenode_t *cn; 327168404Spjd char newname[ZFS_MAXNAMELEN]; 328168404Spjd 329168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 330168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 331168404Spjd /* 332168404Spjd * Do not rename a clone that's not in the source hierarchy. 333168404Spjd */ 334168404Spjd if (!isa_child_of(cn->cn_handle->zfs_name, src)) 335168404Spjd continue; 336168404Spjd 337168404Spjd /* 338168404Spjd * Destroy the previous mountpoint if needed. 339168404Spjd */ 340168404Spjd remove_mountpoint(cn->cn_handle); 341168404Spjd 342168404Spjd (void) strlcpy(newname, dst, sizeof (newname)); 343168404Spjd (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src)); 344168404Spjd 345168404Spjd (void) strlcpy(cn->cn_handle->zfs_name, newname, 346168404Spjd sizeof (cn->cn_handle->zfs_name)); 347168404Spjd } 348168404Spjd} 349168404Spjd 350168404Spjd/* 351185029Spjd * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property, 352185029Spjd * unshare all the datasets in the list. 353168404Spjd */ 354168404Spjdint 355185029Spjdchangelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto) 356168404Spjd{ 357168404Spjd prop_changenode_t *cn; 358168404Spjd int ret = 0; 359168404Spjd 360185029Spjd if (clp->cl_prop != ZFS_PROP_SHARENFS && 361185029Spjd clp->cl_prop != ZFS_PROP_SHARESMB) 362168404Spjd return (0); 363168404Spjd 364168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 365168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 366185029Spjd if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0) 367168404Spjd ret = -1; 368168404Spjd } 369168404Spjd 370168404Spjd return (ret); 371168404Spjd} 372168404Spjd 373168404Spjd/* 374168404Spjd * Check if there is any child exported to a local zone in a given changelist. 375168404Spjd * This information has already been recorded while gathering the changelist 376168404Spjd * via changelist_gather(). 377168404Spjd */ 378168404Spjdint 379168404Spjdchangelist_haszonedchild(prop_changelist_t *clp) 380168404Spjd{ 381168404Spjd return (clp->cl_haszonedchild); 382168404Spjd} 383168404Spjd 384168404Spjd/* 385168404Spjd * Remove a node from a gathered list. 386168404Spjd */ 387168404Spjdvoid 388185029Spjdchangelist_remove(prop_changelist_t *clp, const char *name) 389168404Spjd{ 390168404Spjd prop_changenode_t *cn; 391168404Spjd 392168404Spjd for (cn = uu_list_first(clp->cl_list); cn != NULL; 393168404Spjd cn = uu_list_next(clp->cl_list, cn)) { 394168404Spjd 395185029Spjd if (strcmp(cn->cn_handle->zfs_name, name) == 0) { 396168404Spjd uu_list_remove(clp->cl_list, cn); 397168404Spjd zfs_close(cn->cn_handle); 398168404Spjd free(cn); 399168404Spjd return; 400168404Spjd } 401168404Spjd } 402168404Spjd} 403168404Spjd 404168404Spjd/* 405168404Spjd * Release any memory associated with a changelist. 406168404Spjd */ 407168404Spjdvoid 408168404Spjdchangelist_free(prop_changelist_t *clp) 409168404Spjd{ 410168404Spjd prop_changenode_t *cn; 411168926Spjd void *cookie; 412168404Spjd 413168404Spjd if (clp->cl_list) { 414168926Spjd cookie = NULL; 415168926Spjd while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) { 416168404Spjd zfs_close(cn->cn_handle); 417168404Spjd free(cn); 418168404Spjd } 419168404Spjd 420168404Spjd uu_list_destroy(clp->cl_list); 421168404Spjd } 422168404Spjd if (clp->cl_pool) 423168404Spjd uu_list_pool_destroy(clp->cl_pool); 424168404Spjd 425168404Spjd free(clp); 426168404Spjd} 427168404Spjd 428168404Spjdstatic int 429168404Spjdchange_one(zfs_handle_t *zhp, void *data) 430168404Spjd{ 431168404Spjd prop_changelist_t *clp = data; 432168404Spjd char property[ZFS_MAXPROPLEN]; 433168404Spjd char where[64]; 434168404Spjd prop_changenode_t *cn; 435185029Spjd zprop_source_t sourcetype; 436185029Spjd zprop_source_t share_sourcetype; 437168404Spjd 438168404Spjd /* 439168404Spjd * We only want to unmount/unshare those filesystems that may inherit 440168404Spjd * from the target filesystem. If we find any filesystem with a 441168404Spjd * locally set mountpoint, we ignore any children since changing the 442168404Spjd * property will not affect them. If this is a rename, we iterate 443168404Spjd * over all children regardless, since we need them unmounted in 444168404Spjd * order to do the rename. Also, if this is a volume and we're doing 445168404Spjd * a rename, then always add it to the changelist. 446168404Spjd */ 447168404Spjd 448168404Spjd if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) && 449168404Spjd zfs_prop_get(zhp, clp->cl_prop, property, 450168404Spjd sizeof (property), &sourcetype, where, sizeof (where), 451168404Spjd B_FALSE) != 0) { 452168404Spjd zfs_close(zhp); 453168404Spjd return (0); 454168404Spjd } 455168404Spjd 456185029Spjd /* 457185029Spjd * If we are "watching" sharenfs or sharesmb 458185029Spjd * then check out the companion property which is tracked 459185029Spjd * in cl_shareprop 460185029Spjd */ 461185029Spjd if (clp->cl_shareprop != ZPROP_INVAL && 462185029Spjd zfs_prop_get(zhp, clp->cl_shareprop, property, 463185029Spjd sizeof (property), &share_sourcetype, where, sizeof (where), 464185029Spjd B_FALSE) != 0) { 465185029Spjd zfs_close(zhp); 466185029Spjd return (0); 467185029Spjd } 468185029Spjd 469168404Spjd if (clp->cl_alldependents || clp->cl_allchildren || 470185029Spjd sourcetype == ZPROP_SRC_DEFAULT || 471185029Spjd sourcetype == ZPROP_SRC_INHERITED || 472185029Spjd (clp->cl_shareprop != ZPROP_INVAL && 473185029Spjd (share_sourcetype == ZPROP_SRC_DEFAULT || 474185029Spjd share_sourcetype == ZPROP_SRC_INHERITED))) { 475168404Spjd if ((cn = zfs_alloc(zfs_get_handle(zhp), 476168404Spjd sizeof (prop_changenode_t))) == NULL) { 477168404Spjd zfs_close(zhp); 478168404Spjd return (-1); 479168404Spjd } 480168404Spjd 481168404Spjd cn->cn_handle = zhp; 482185029Spjd cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || 483185029Spjd zfs_is_mounted(zhp, NULL); 484168404Spjd cn->cn_shared = zfs_is_shared(zhp); 485168404Spjd cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 486185029Spjd cn->cn_needpost = B_TRUE; 487168404Spjd 488168404Spjd /* Indicate if any child is exported to a local zone. */ 489168404Spjd if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 490168404Spjd clp->cl_haszonedchild = B_TRUE; 491168404Spjd 492168404Spjd uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 493168404Spjd 494168404Spjd if (clp->cl_sorted) { 495168404Spjd uu_list_index_t idx; 496168404Spjd 497168404Spjd (void) uu_list_find(clp->cl_list, cn, NULL, 498168404Spjd &idx); 499168404Spjd uu_list_insert(clp->cl_list, cn, idx); 500168404Spjd } else { 501168404Spjd ASSERT(!clp->cl_alldependents); 502168404Spjd verify(uu_list_insert_before(clp->cl_list, 503168926Spjd uu_list_first(clp->cl_list), cn) == 0); 504168404Spjd } 505168404Spjd 506168404Spjd if (!clp->cl_alldependents) 507168404Spjd return (zfs_iter_children(zhp, change_one, data)); 508168404Spjd } else { 509168404Spjd zfs_close(zhp); 510168404Spjd } 511168404Spjd 512168404Spjd return (0); 513168404Spjd} 514168404Spjd 515168404Spjd/*ARGSUSED*/ 516168404Spjdstatic int 517168404Spjdcompare_mountpoints(const void *a, const void *b, void *unused) 518168404Spjd{ 519168404Spjd const prop_changenode_t *ca = a; 520168404Spjd const prop_changenode_t *cb = b; 521168404Spjd 522168404Spjd char mounta[MAXPATHLEN]; 523168404Spjd char mountb[MAXPATHLEN]; 524168404Spjd 525168404Spjd boolean_t hasmounta, hasmountb; 526168404Spjd 527168404Spjd /* 528168404Spjd * When unsharing or unmounting filesystems, we need to do it in 529168404Spjd * mountpoint order. This allows the user to have a mountpoint 530168404Spjd * hierarchy that is different from the dataset hierarchy, and still 531168404Spjd * allow it to be changed. However, if either dataset doesn't have a 532168404Spjd * mountpoint (because it is a volume or a snapshot), we place it at the 533168404Spjd * end of the list, because it doesn't affect our change at all. 534168404Spjd */ 535168404Spjd hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta, 536168404Spjd sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 537168404Spjd hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb, 538168404Spjd sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 539168404Spjd 540168404Spjd if (!hasmounta && hasmountb) 541168404Spjd return (-1); 542168404Spjd else if (hasmounta && !hasmountb) 543168404Spjd return (1); 544168404Spjd else if (!hasmounta && !hasmountb) 545168404Spjd return (0); 546168404Spjd else 547168404Spjd return (strcmp(mountb, mounta)); 548168404Spjd} 549168404Spjd 550168404Spjd/* 551168404Spjd * Given a ZFS handle and a property, construct a complete list of datasets 552168404Spjd * that need to be modified as part of this process. For anything but the 553168404Spjd * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. 554168404Spjd * Otherwise, we iterate over all children and look for any datasets that 555168404Spjd * inherit the property. For each such dataset, we add it to the list and 556168404Spjd * mark whether it was shared beforehand. 557168404Spjd */ 558168404Spjdprop_changelist_t * 559185029Spjdchangelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags, 560185029Spjd int mnt_flags) 561168404Spjd{ 562168404Spjd prop_changelist_t *clp; 563168404Spjd prop_changenode_t *cn; 564168404Spjd zfs_handle_t *temp; 565168404Spjd char property[ZFS_MAXPROPLEN]; 566168404Spjd uu_compare_fn_t *compare = NULL; 567168404Spjd 568168404Spjd if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) 569168404Spjd return (NULL); 570168404Spjd 571168404Spjd /* 572168404Spjd * For mountpoint-related tasks, we want to sort everything by 573168404Spjd * mountpoint, so that we mount and unmount them in the appropriate 574168404Spjd * order, regardless of their position in the hierarchy. 575168404Spjd */ 576168404Spjd if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || 577185029Spjd prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS || 578185029Spjd prop == ZFS_PROP_SHARESMB) { 579168404Spjd compare = compare_mountpoints; 580168404Spjd clp->cl_sorted = B_TRUE; 581168404Spjd } 582168404Spjd 583168404Spjd clp->cl_pool = uu_list_pool_create("changelist_pool", 584168404Spjd sizeof (prop_changenode_t), 585168404Spjd offsetof(prop_changenode_t, cn_listnode), 586168404Spjd compare, 0); 587168404Spjd if (clp->cl_pool == 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 clp->cl_list = uu_list_create(clp->cl_pool, NULL, 595168404Spjd clp->cl_sorted ? UU_LIST_SORTED : 0); 596185029Spjd clp->cl_gflags = gather_flags; 597185029Spjd clp->cl_mflags = mnt_flags; 598168404Spjd 599168404Spjd if (clp->cl_list == NULL) { 600168404Spjd assert(uu_error() == UU_ERROR_NO_MEMORY); 601168404Spjd (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 602168404Spjd changelist_free(clp); 603168404Spjd return (NULL); 604168404Spjd } 605168404Spjd 606168404Spjd /* 607168404Spjd * If this is a rename or the 'zoned' property, we pretend we're 608168404Spjd * changing the mountpoint and flag it so we can catch all children in 609168404Spjd * change_one(). 610168404Spjd * 611168404Spjd * Flag cl_alldependents to catch all children plus the dependents 612168404Spjd * (clones) that are not in the hierarchy. 613168404Spjd */ 614168404Spjd if (prop == ZFS_PROP_NAME) { 615168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 616168404Spjd clp->cl_alldependents = B_TRUE; 617168404Spjd } else if (prop == ZFS_PROP_ZONED) { 618168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 619168404Spjd clp->cl_allchildren = B_TRUE; 620168404Spjd } else if (prop == ZFS_PROP_CANMOUNT) { 621168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 622168404Spjd } else if (prop == ZFS_PROP_VOLSIZE) { 623168404Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 624185029Spjd } else if (prop == ZFS_PROP_VERSION) { 625185029Spjd clp->cl_prop = ZFS_PROP_MOUNTPOINT; 626168404Spjd } else { 627168404Spjd clp->cl_prop = prop; 628168404Spjd } 629168404Spjd clp->cl_realprop = prop; 630168404Spjd 631168404Spjd if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 632168404Spjd clp->cl_prop != ZFS_PROP_SHARENFS && 633185029Spjd clp->cl_prop != ZFS_PROP_SHARESMB && 634168404Spjd clp->cl_prop != ZFS_PROP_SHAREISCSI) 635168404Spjd return (clp); 636168404Spjd 637185029Spjd /* 638185029Spjd * If watching SHARENFS or SHARESMB then 639185029Spjd * also watch its companion property. 640185029Spjd */ 641185029Spjd if (clp->cl_prop == ZFS_PROP_SHARENFS) 642185029Spjd clp->cl_shareprop = ZFS_PROP_SHARESMB; 643185029Spjd else if (clp->cl_prop == ZFS_PROP_SHARESMB) 644185029Spjd clp->cl_shareprop = ZFS_PROP_SHARENFS; 645185029Spjd 646168404Spjd if (clp->cl_alldependents) { 647168404Spjd if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { 648168404Spjd changelist_free(clp); 649168404Spjd return (NULL); 650168404Spjd } 651168404Spjd } else if (zfs_iter_children(zhp, change_one, clp) != 0) { 652168404Spjd changelist_free(clp); 653168404Spjd return (NULL); 654168404Spjd } 655168404Spjd 656168404Spjd /* 657168404Spjd * We have to re-open ourselves because we auto-close all the handles 658168404Spjd * and can't tell the difference. 659168404Spjd */ 660168404Spjd if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), 661185029Spjd ZFS_TYPE_DATASET)) == NULL) { 662168404Spjd changelist_free(clp); 663168404Spjd return (NULL); 664168404Spjd } 665168404Spjd 666168404Spjd /* 667168404Spjd * Always add ourself to the list. We add ourselves to the end so that 668168404Spjd * we're the last to be unmounted. 669168404Spjd */ 670168404Spjd if ((cn = zfs_alloc(zhp->zfs_hdl, 671168404Spjd sizeof (prop_changenode_t))) == NULL) { 672168404Spjd zfs_close(temp); 673168404Spjd changelist_free(clp); 674168404Spjd return (NULL); 675168404Spjd } 676168404Spjd 677168404Spjd cn->cn_handle = temp; 678185029Spjd cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || 679185029Spjd zfs_is_mounted(temp, NULL); 680168404Spjd cn->cn_shared = zfs_is_shared(temp); 681168404Spjd cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 682185029Spjd cn->cn_needpost = B_TRUE; 683168404Spjd 684168404Spjd uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 685168404Spjd if (clp->cl_sorted) { 686168404Spjd uu_list_index_t idx; 687168404Spjd (void) uu_list_find(clp->cl_list, cn, NULL, &idx); 688168404Spjd uu_list_insert(clp->cl_list, cn, idx); 689168404Spjd } else { 690168404Spjd verify(uu_list_insert_after(clp->cl_list, 691168404Spjd uu_list_last(clp->cl_list), cn) == 0); 692168404Spjd } 693168404Spjd 694168404Spjd /* 695185029Spjd * If the mountpoint property was previously 'legacy', or 'none', 696185029Spjd * record it as the behavior of changelist_postfix() will be different. 697168404Spjd */ 698185029Spjd if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && 699185029Spjd (zfs_prop_get(zhp, prop, property, sizeof (property), 700168404Spjd NULL, NULL, 0, B_FALSE) == 0 && 701185029Spjd (strcmp(property, "legacy") == 0 || 702185029Spjd strcmp(property, "none") == 0))) { 703185029Spjd /* 704185029Spjd * do not automatically mount ex-legacy datasets if 705185029Spjd * we specifically set canmount to noauto 706185029Spjd */ 707185029Spjd if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) != 708185029Spjd ZFS_CANMOUNT_NOAUTO) 709185029Spjd clp->cl_waslegacy = B_TRUE; 710185029Spjd } 711168404Spjd 712168404Spjd return (clp); 713168404Spjd} 714