1 789 ahrens /* 2 789 ahrens * CDDL HEADER START 3 789 ahrens * 4 789 ahrens * The contents of this file are subject to the terms of the 5 2082 eschrock * Common Development and Distribution License (the "License"). 6 2082 eschrock * You may not use this file except in compliance with the License. 7 789 ahrens * 8 789 ahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 789 ahrens * or http://www.opensolaris.org/os/licensing. 10 789 ahrens * See the License for the specific language governing permissions 11 789 ahrens * and limitations under the License. 12 789 ahrens * 13 789 ahrens * When distributing Covered Code, include this CDDL HEADER in each 14 789 ahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 789 ahrens * If applicable, add the following below this CDDL HEADER, with the 16 789 ahrens * fields enclosed by brackets "[]" replaced with your own identifying 17 789 ahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18 789 ahrens * 19 789 ahrens * CDDL HEADER END 20 789 ahrens */ 21 3126 ahl 22 789 ahrens /* 23 9396 Matthew * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 789 ahrens * Use is subject to license terms. 25 5542 mmusante * 26 5542 mmusante * Portions Copyright 2007 Ramprakash Jelari 27 789 ahrens */ 28 789 ahrens 29 789 ahrens #include <libintl.h> 30 789 ahrens #include <libuutil.h> 31 789 ahrens #include <stddef.h> 32 789 ahrens #include <stdlib.h> 33 789 ahrens #include <string.h> 34 789 ahrens #include <unistd.h> 35 789 ahrens #include <zone.h> 36 789 ahrens 37 789 ahrens #include <libzfs.h> 38 789 ahrens 39 789 ahrens #include "libzfs_impl.h" 40 789 ahrens 41 789 ahrens /* 42 789 ahrens * Structure to keep track of dataset state. Before changing the 'sharenfs' or 43 789 ahrens * 'mountpoint' property, we record whether the filesystem was previously 44 789 ahrens * mounted/shared. This prior state dictates whether we remount/reshare the 45 789 ahrens * dataset after the property has been changed. 46 789 ahrens * 47 789 ahrens * The interface consists of the following sequence of functions: 48 789 ahrens * 49 789 ahrens * changelist_gather() 50 789 ahrens * changelist_prefix() 51 789 ahrens * < change property > 52 789 ahrens * changelist_postfix() 53 789 ahrens * changelist_free() 54 789 ahrens * 55 789 ahrens * Other interfaces: 56 789 ahrens * 57 1294 lling * changelist_remove() - remove a node from a gathered list 58 789 ahrens * changelist_rename() - renames all datasets appropriately when doing a rename 59 789 ahrens * changelist_unshare() - unshares all the nodes in a given changelist 60 789 ahrens * changelist_haszonedchild() - check if there is any child exported to 61 789 ahrens * a local zone 62 789 ahrens */ 63 789 ahrens typedef struct prop_changenode { 64 789 ahrens zfs_handle_t *cn_handle; 65 789 ahrens int cn_shared; 66 789 ahrens int cn_mounted; 67 789 ahrens int cn_zoned; 68 5542 mmusante boolean_t cn_needpost; /* is postfix() needed? */ 69 789 ahrens uu_list_node_t cn_listnode; 70 789 ahrens } prop_changenode_t; 71 789 ahrens 72 789 ahrens struct prop_changelist { 73 789 ahrens zfs_prop_t cl_prop; 74 789 ahrens zfs_prop_t cl_realprop; 75 5331 amw zfs_prop_t cl_shareprop; /* used with sharenfs/sharesmb */ 76 789 ahrens uu_list_pool_t *cl_pool; 77 789 ahrens uu_list_t *cl_list; 78 2082 eschrock boolean_t cl_waslegacy; 79 2082 eschrock boolean_t cl_allchildren; 80 2082 eschrock boolean_t cl_alldependents; 81 7366 Tim int cl_mflags; /* Mount flags */ 82 7366 Tim int cl_gflags; /* Gather request flags */ 83 2082 eschrock boolean_t cl_haszonedchild; 84 2474 eschrock boolean_t cl_sorted; 85 789 ahrens }; 86 789 ahrens 87 789 ahrens /* 88 789 ahrens * If the property is 'mountpoint', go through and unmount filesystems as 89 789 ahrens * necessary. We don't do the same for 'sharenfs', because we can just re-share 90 6113 dougm * with different options without interrupting service. We do handle 'sharesmb' 91 6113 dougm * since there may be old resource names that need to be removed. 92 789 ahrens */ 93 789 ahrens int 94 789 ahrens changelist_prefix(prop_changelist_t *clp) 95 789 ahrens { 96 789 ahrens prop_changenode_t *cn; 97 789 ahrens int ret = 0; 98 789 ahrens 99 6113 dougm if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 100 6113 dougm clp->cl_prop != ZFS_PROP_SHARESMB) 101 789 ahrens return (0); 102 789 ahrens 103 789 ahrens for (cn = uu_list_first(clp->cl_list); cn != NULL; 104 789 ahrens cn = uu_list_next(clp->cl_list, cn)) { 105 5610 mmusante 106 5610 mmusante /* if a previous loop failed, set the remaining to false */ 107 5610 mmusante if (ret == -1) { 108 5610 mmusante cn->cn_needpost = B_FALSE; 109 5610 mmusante continue; 110 5610 mmusante } 111 5610 mmusante 112 789 ahrens /* 113 3126 ahl * If we are in the global zone, but this dataset is exported 114 3126 ahl * to a local zone, do nothing. 115 789 ahrens */ 116 3126 ahl if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 117 789 ahrens continue; 118 789 ahrens 119 3126 ahl if (ZFS_IS_VOLUME(cn->cn_handle)) { 120 3126 ahl switch (clp->cl_realprop) { 121 3126 ahl case ZFS_PROP_NAME: 122 10588 Eric /* If this was a rename, unshare the zvol */ 123 3126 ahl (void) zfs_unshare_iscsi(cn->cn_handle); 124 3126 ahl break; 125 3126 ahl 126 3126 ahl case ZFS_PROP_VOLSIZE: 127 3126 ahl /* 128 3126 ahl * If this was a change to the volume size, we 129 3126 ahl * need to unshare and reshare the volume. 130 3126 ahl */ 131 3126 ahl (void) zfs_unshare_iscsi(cn->cn_handle); 132 3126 ahl break; 133 3126 ahl } 134 6113 dougm } else { 135 6113 dougm /* 136 6113 dougm * Do the property specific processing. 137 6113 dougm */ 138 6113 dougm switch (clp->cl_prop) { 139 6113 dougm case ZFS_PROP_MOUNTPOINT: 140 6113 dougm if (zfs_unmount(cn->cn_handle, NULL, 141 7366 Tim clp->cl_mflags) != 0) { 142 6113 dougm ret = -1; 143 6113 dougm cn->cn_needpost = B_FALSE; 144 6113 dougm } 145 6113 dougm break; 146 6113 dougm case ZFS_PROP_SHARESMB: 147 6113 dougm (void) zfs_unshare_smb(cn->cn_handle, NULL); 148 6113 dougm break; 149 6113 dougm } 150 4577 ahrens } 151 789 ahrens } 152 5542 mmusante 153 5542 mmusante if (ret == -1) 154 5542 mmusante (void) changelist_postfix(clp); 155 789 ahrens 156 789 ahrens return (ret); 157 789 ahrens } 158 789 ahrens 159 789 ahrens /* 160 3126 ahl * If the property is 'mountpoint' or 'sharenfs', go through and remount and/or 161 789 ahrens * reshare the filesystems as necessary. In changelist_gather() we recorded 162 789 ahrens * whether the filesystem was previously shared or mounted. The action we take 163 789 ahrens * depends on the previous state, and whether the value was previously 'legacy'. 164 789 ahrens * For non-legacy properties, we only remount/reshare the filesystem if it was 165 789 ahrens * previously mounted/shared. Otherwise, we always remount/reshare the 166 789 ahrens * filesystem. 167 789 ahrens */ 168 789 ahrens int 169 789 ahrens changelist_postfix(prop_changelist_t *clp) 170 789 ahrens { 171 789 ahrens prop_changenode_t *cn; 172 3126 ahl char shareopts[ZFS_MAXPROPLEN]; 173 5473 rm160521 int errors = 0; 174 4180 dougm libzfs_handle_t *hdl; 175 789 ahrens 176 789 ahrens /* 177 789 ahrens * If we're changing the mountpoint, attempt to destroy the underlying 178 789 ahrens * mountpoint. All other datasets will have inherited from this dataset 179 789 ahrens * (in which case their mountpoints exist in the filesystem in the new 180 789 ahrens * location), or have explicit mountpoints set (in which case they won't 181 789 ahrens * be in the changelist). 182 789 ahrens */ 183 789 ahrens if ((cn = uu_list_last(clp->cl_list)) == NULL) 184 789 ahrens return (0); 185 789 ahrens 186 789 ahrens if (clp->cl_prop == ZFS_PROP_MOUNTPOINT) 187 789 ahrens remove_mountpoint(cn->cn_handle); 188 4180 dougm 189 4180 dougm /* 190 4180 dougm * It is possible that the changelist_prefix() used libshare 191 4180 dougm * to unshare some entries. Since libshare caches data, an 192 4180 dougm * attempt to reshare during postfix can fail unless libshare 193 4180 dougm * is uninitialized here so that it will reinitialize later. 194 4180 dougm */ 195 4180 dougm if (cn->cn_handle != NULL) { 196 4302 dougm hdl = cn->cn_handle->zfs_hdl; 197 4302 dougm assert(hdl != NULL); 198 4302 dougm zfs_uninit_libshare(hdl); 199 4180 dougm } 200 789 ahrens 201 789 ahrens /* 202 789 ahrens * We walk the datasets in reverse, because we want to mount any parent 203 5473 rm160521 * datasets before mounting the children. We walk all datasets even if 204 5473 rm160521 * there are errors. 205 789 ahrens */ 206 789 ahrens for (cn = uu_list_last(clp->cl_list); cn != NULL; 207 789 ahrens cn = uu_list_prev(clp->cl_list, cn)) { 208 4840 rm160521 209 4840 rm160521 boolean_t sharenfs; 210 5331 amw boolean_t sharesmb; 211 9404 william boolean_t mounted; 212 4840 rm160521 213 789 ahrens /* 214 3126 ahl * If we are in the global zone, but this dataset is exported 215 3126 ahl * to a local zone, do nothing. 216 789 ahrens */ 217 3126 ahl if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 218 789 ahrens continue; 219 5542 mmusante 220 5610 mmusante /* Only do post-processing if it's required */ 221 5542 mmusante if (!cn->cn_needpost) 222 5542 mmusante continue; 223 5542 mmusante cn->cn_needpost = B_FALSE; 224 789 ahrens 225 789 ahrens zfs_refresh_properties(cn->cn_handle); 226 789 ahrens 227 3126 ahl if (ZFS_IS_VOLUME(cn->cn_handle)) { 228 10588 Eric if (cn->cn_shared || 229 3126 ahl clp->cl_prop == ZFS_PROP_SHAREISCSI) { 230 3126 ahl if (zfs_prop_get(cn->cn_handle, 231 3126 ahl ZFS_PROP_SHAREISCSI, shareopts, 232 3126 ahl sizeof (shareopts), NULL, NULL, 0, 233 3126 ahl B_FALSE) == 0 && 234 3126 ahl strcmp(shareopts, "off") == 0) { 235 5473 rm160521 errors += 236 5473 rm160521 zfs_unshare_iscsi(cn->cn_handle); 237 3126 ahl } else { 238 5473 rm160521 errors += 239 5473 rm160521 zfs_share_iscsi(cn->cn_handle); 240 3126 ahl } 241 3126 ahl } 242 3126 ahl 243 789 ahrens continue; 244 789 ahrens } 245 789 ahrens 246 4840 rm160521 /* 247 4840 rm160521 * Remount if previously mounted or mountpoint was legacy, 248 5331 amw * or sharenfs or sharesmb property is set. 249 4840 rm160521 */ 250 4840 rm160521 sharenfs = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARENFS, 251 4840 rm160521 shareopts, sizeof (shareopts), NULL, NULL, 0, 252 4840 rm160521 B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); 253 4840 rm160521 254 5331 amw sharesmb = ((zfs_prop_get(cn->cn_handle, ZFS_PROP_SHARESMB, 255 5331 amw shareopts, sizeof (shareopts), NULL, NULL, 0, 256 5331 amw B_FALSE) == 0) && (strcmp(shareopts, "off") != 0)); 257 5331 amw 258 9404 william mounted = zfs_is_mounted(cn->cn_handle, NULL); 259 9404 william 260 9404 william if (!mounted && (cn->cn_mounted || 261 9404 william ((sharenfs || sharesmb || clp->cl_waslegacy) && 262 9404 william (zfs_prop_get_int(cn->cn_handle, 263 9404 william ZFS_PROP_CANMOUNT) == ZFS_CANMOUNT_ON)))) { 264 9404 william 265 9404 william if (zfs_mount(cn->cn_handle, NULL, 0) != 0) 266 9404 william errors++; 267 9404 william else 268 9404 william mounted = TRUE; 269 9404 william } 270 789 ahrens 271 789 ahrens /* 272 9404 william * If the file system is mounted we always re-share even 273 9404 william * if the filesystem is currently shared, so that we can 274 9404 william * adopt any new options. 275 789 ahrens */ 276 9404 william if (sharenfs && mounted) 277 5473 rm160521 errors += zfs_share_nfs(cn->cn_handle); 278 5473 rm160521 else if (cn->cn_shared || clp->cl_waslegacy) 279 5473 rm160521 errors += zfs_unshare_nfs(cn->cn_handle, NULL); 280 9404 william if (sharesmb && mounted) 281 5473 rm160521 errors += zfs_share_smb(cn->cn_handle); 282 5473 rm160521 else if (cn->cn_shared || clp->cl_waslegacy) 283 5473 rm160521 errors += zfs_unshare_smb(cn->cn_handle, NULL); 284 789 ahrens } 285 789 ahrens 286 5473 rm160521 return (errors ? -1 : 0); 287 789 ahrens } 288 789 ahrens 289 789 ahrens /* 290 1294 lling * Is this "dataset" a child of "parent"? 291 1294 lling */ 292 6027 rm160521 boolean_t 293 3841 mmusante isa_child_of(const char *dataset, const char *parent) 294 1294 lling { 295 1294 lling int len; 296 1294 lling 297 1294 lling len = strlen(parent); 298 1294 lling 299 1294 lling if (strncmp(dataset, parent, len) == 0 && 300 3841 mmusante (dataset[len] == '@' || dataset[len] == '/' || 301 3841 mmusante dataset[len] == '\0')) 302 2082 eschrock return (B_TRUE); 303 1294 lling else 304 2082 eschrock return (B_FALSE); 305 1294 lling 306 1294 lling } 307 1294 lling 308 1294 lling /* 309 3126 ahl * If we rename a filesystem, child filesystem handles are no longer valid 310 3126 ahl * since we identify each dataset by its name in the ZFS namespace. As a 311 3126 ahl * result, we have to go through and fix up all the names appropriately. We 312 3126 ahl * could do this automatically if libzfs kept track of all open handles, but 313 3126 ahl * this is a lot less work. 314 789 ahrens */ 315 789 ahrens void 316 789 ahrens changelist_rename(prop_changelist_t *clp, const char *src, const char *dst) 317 789 ahrens { 318 789 ahrens prop_changenode_t *cn; 319 789 ahrens char newname[ZFS_MAXNAMELEN]; 320 789 ahrens 321 789 ahrens for (cn = uu_list_first(clp->cl_list); cn != NULL; 322 789 ahrens cn = uu_list_next(clp->cl_list, cn)) { 323 1294 lling /* 324 1294 lling * Do not rename a clone that's not in the source hierarchy. 325 1294 lling */ 326 1294 lling if (!isa_child_of(cn->cn_handle->zfs_name, src)) 327 1294 lling continue; 328 1294 lling 329 789 ahrens /* 330 789 ahrens * Destroy the previous mountpoint if needed. 331 789 ahrens */ 332 789 ahrens remove_mountpoint(cn->cn_handle); 333 789 ahrens 334 789 ahrens (void) strlcpy(newname, dst, sizeof (newname)); 335 789 ahrens (void) strcat(newname, cn->cn_handle->zfs_name + strlen(src)); 336 789 ahrens 337 789 ahrens (void) strlcpy(cn->cn_handle->zfs_name, newname, 338 789 ahrens sizeof (cn->cn_handle->zfs_name)); 339 789 ahrens } 340 789 ahrens } 341 789 ahrens 342 789 ahrens /* 343 5331 amw * Given a gathered changelist for the 'sharenfs' or 'sharesmb' property, 344 5331 amw * unshare all the datasets in the list. 345 789 ahrens */ 346 789 ahrens int 347 5331 amw changelist_unshare(prop_changelist_t *clp, zfs_share_proto_t *proto) 348 789 ahrens { 349 789 ahrens prop_changenode_t *cn; 350 789 ahrens int ret = 0; 351 789 ahrens 352 5331 amw if (clp->cl_prop != ZFS_PROP_SHARENFS && 353 5331 amw clp->cl_prop != ZFS_PROP_SHARESMB) 354 789 ahrens return (0); 355 789 ahrens 356 789 ahrens for (cn = uu_list_first(clp->cl_list); cn != NULL; 357 789 ahrens cn = uu_list_next(clp->cl_list, cn)) { 358 5331 amw if (zfs_unshare_proto(cn->cn_handle, NULL, proto) != 0) 359 789 ahrens ret = -1; 360 789 ahrens } 361 789 ahrens 362 789 ahrens return (ret); 363 789 ahrens } 364 789 ahrens 365 789 ahrens /* 366 3126 ahl * Check if there is any child exported to a local zone in a given changelist. 367 3126 ahl * This information has already been recorded while gathering the changelist 368 3126 ahl * via changelist_gather(). 369 789 ahrens */ 370 789 ahrens int 371 789 ahrens changelist_haszonedchild(prop_changelist_t *clp) 372 789 ahrens { 373 789 ahrens return (clp->cl_haszonedchild); 374 1294 lling } 375 1294 lling 376 1294 lling /* 377 1294 lling * Remove a node from a gathered list. 378 1294 lling */ 379 1294 lling void 380 5367 ahrens changelist_remove(prop_changelist_t *clp, const char *name) 381 1294 lling { 382 1294 lling prop_changenode_t *cn; 383 1294 lling 384 1294 lling for (cn = uu_list_first(clp->cl_list); cn != NULL; 385 1294 lling cn = uu_list_next(clp->cl_list, cn)) { 386 1294 lling 387 5367 ahrens if (strcmp(cn->cn_handle->zfs_name, name) == 0) { 388 1294 lling uu_list_remove(clp->cl_list, cn); 389 1294 lling zfs_close(cn->cn_handle); 390 1294 lling free(cn); 391 1294 lling return; 392 1294 lling } 393 1294 lling } 394 789 ahrens } 395 789 ahrens 396 789 ahrens /* 397 789 ahrens * Release any memory associated with a changelist. 398 789 ahrens */ 399 789 ahrens void 400 789 ahrens changelist_free(prop_changelist_t *clp) 401 789 ahrens { 402 789 ahrens prop_changenode_t *cn; 403 4074 eschrock void *cookie; 404 789 ahrens 405 2142 eschrock if (clp->cl_list) { 406 4074 eschrock cookie = NULL; 407 4074 eschrock while ((cn = uu_list_teardown(clp->cl_list, &cookie)) != NULL) { 408 2142 eschrock zfs_close(cn->cn_handle); 409 2142 eschrock free(cn); 410 2142 eschrock } 411 2142 eschrock 412 2142 eschrock uu_list_destroy(clp->cl_list); 413 789 ahrens } 414 2142 eschrock if (clp->cl_pool) 415 2142 eschrock uu_list_pool_destroy(clp->cl_pool); 416 789 ahrens 417 789 ahrens free(clp); 418 789 ahrens } 419 789 ahrens 420 789 ahrens static int 421 789 ahrens change_one(zfs_handle_t *zhp, void *data) 422 789 ahrens { 423 789 ahrens prop_changelist_t *clp = data; 424 789 ahrens char property[ZFS_MAXPROPLEN]; 425 789 ahrens char where[64]; 426 789 ahrens prop_changenode_t *cn; 427 5094 lling zprop_source_t sourcetype; 428 5331 amw zprop_source_t share_sourcetype; 429 789 ahrens 430 789 ahrens /* 431 3126 ahl * We only want to unmount/unshare those filesystems that may inherit 432 3126 ahl * from the target filesystem. If we find any filesystem with a 433 3126 ahl * locally set mountpoint, we ignore any children since changing the 434 3126 ahl * property will not affect them. If this is a rename, we iterate 435 3126 ahl * over all children regardless, since we need them unmounted in 436 3126 ahl * order to do the rename. Also, if this is a volume and we're doing 437 3126 ahl * a rename, then always add it to the changelist. 438 789 ahrens */ 439 789 ahrens 440 2676 eschrock if (!(ZFS_IS_VOLUME(zhp) && clp->cl_realprop == ZFS_PROP_NAME) && 441 789 ahrens zfs_prop_get(zhp, clp->cl_prop, property, 442 789 ahrens sizeof (property), &sourcetype, where, sizeof (where), 443 2082 eschrock B_FALSE) != 0) { 444 2082 eschrock zfs_close(zhp); 445 789 ahrens return (0); 446 2082 eschrock } 447 789 ahrens 448 5331 amw /* 449 5331 amw * If we are "watching" sharenfs or sharesmb 450 5331 amw * then check out the companion property which is tracked 451 5331 amw * in cl_shareprop 452 5331 amw */ 453 5331 amw if (clp->cl_shareprop != ZPROP_INVAL && 454 5331 amw zfs_prop_get(zhp, clp->cl_shareprop, property, 455 5331 amw sizeof (property), &share_sourcetype, where, sizeof (where), 456 5331 amw B_FALSE) != 0) { 457 5331 amw zfs_close(zhp); 458 5331 amw return (0); 459 5331 amw } 460 5331 amw 461 1294 lling if (clp->cl_alldependents || clp->cl_allchildren || 462 5094 lling sourcetype == ZPROP_SRC_DEFAULT || 463 5331 amw sourcetype == ZPROP_SRC_INHERITED || 464 5331 amw (clp->cl_shareprop != ZPROP_INVAL && 465 5331 amw (share_sourcetype == ZPROP_SRC_DEFAULT || 466 5331 amw share_sourcetype == ZPROP_SRC_INHERITED))) { 467 2082 eschrock if ((cn = zfs_alloc(zfs_get_handle(zhp), 468 2082 eschrock sizeof (prop_changenode_t))) == NULL) { 469 2082 eschrock zfs_close(zhp); 470 2082 eschrock return (-1); 471 2082 eschrock } 472 789 ahrens 473 789 ahrens cn->cn_handle = zhp; 474 7366 Tim cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || 475 7366 Tim zfs_is_mounted(zhp, NULL); 476 3126 ahl cn->cn_shared = zfs_is_shared(zhp); 477 789 ahrens cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 478 5610 mmusante cn->cn_needpost = B_TRUE; 479 789 ahrens 480 3126 ahl /* Indicate if any child is exported to a local zone. */ 481 3126 ahl if (getzoneid() == GLOBAL_ZONEID && cn->cn_zoned) 482 2082 eschrock clp->cl_haszonedchild = B_TRUE; 483 789 ahrens 484 789 ahrens uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 485 789 ahrens 486 2474 eschrock if (clp->cl_sorted) { 487 2474 eschrock uu_list_index_t idx; 488 2474 eschrock 489 2474 eschrock (void) uu_list_find(clp->cl_list, cn, NULL, 490 2474 eschrock &idx); 491 2474 eschrock uu_list_insert(clp->cl_list, cn, idx); 492 1294 lling } else { 493 10196 william /* 494 10196 william * Add this child to beginning of the list. Children 495 10196 william * below this one in the hierarchy will get added above 496 10196 william * this one in the list. This produces a list in 497 10196 william * reverse dataset name order. 498 10196 william * This is necessary when the original mountpoint 499 10196 william * is legacy or none. 500 10196 william */ 501 2474 eschrock ASSERT(!clp->cl_alldependents); 502 1294 lling verify(uu_list_insert_before(clp->cl_list, 503 4074 eschrock uu_list_first(clp->cl_list), cn) == 0); 504 2474 eschrock } 505 2474 eschrock 506 2474 eschrock if (!clp->cl_alldependents) 507 1294 lling return (zfs_iter_children(zhp, change_one, data)); 508 789 ahrens } else { 509 789 ahrens zfs_close(zhp); 510 789 ahrens } 511 789 ahrens 512 789 ahrens return (0); 513 789 ahrens } 514 789 ahrens 515 2474 eschrock /*ARGSUSED*/ 516 2474 eschrock static int 517 2474 eschrock compare_mountpoints(const void *a, const void *b, void *unused) 518 2474 eschrock { 519 2474 eschrock const prop_changenode_t *ca = a; 520 2474 eschrock const prop_changenode_t *cb = b; 521 2474 eschrock 522 2474 eschrock char mounta[MAXPATHLEN]; 523 2474 eschrock char mountb[MAXPATHLEN]; 524 2474 eschrock 525 2474 eschrock boolean_t hasmounta, hasmountb; 526 2474 eschrock 527 2474 eschrock /* 528 2474 eschrock * When unsharing or unmounting filesystems, we need to do it in 529 2474 eschrock * mountpoint order. This allows the user to have a mountpoint 530 2474 eschrock * hierarchy that is different from the dataset hierarchy, and still 531 2474 eschrock * allow it to be changed. However, if either dataset doesn't have a 532 2474 eschrock * mountpoint (because it is a volume or a snapshot), we place it at the 533 2474 eschrock * end of the list, because it doesn't affect our change at all. 534 2474 eschrock */ 535 2474 eschrock hasmounta = (zfs_prop_get(ca->cn_handle, ZFS_PROP_MOUNTPOINT, mounta, 536 2474 eschrock sizeof (mounta), NULL, NULL, 0, B_FALSE) == 0); 537 2474 eschrock hasmountb = (zfs_prop_get(cb->cn_handle, ZFS_PROP_MOUNTPOINT, mountb, 538 2474 eschrock sizeof (mountb), NULL, NULL, 0, B_FALSE) == 0); 539 2474 eschrock 540 2474 eschrock if (!hasmounta && hasmountb) 541 2474 eschrock return (-1); 542 2474 eschrock else if (hasmounta && !hasmountb) 543 2474 eschrock return (1); 544 2474 eschrock else if (!hasmounta && !hasmountb) 545 2474 eschrock return (0); 546 2474 eschrock else 547 2474 eschrock return (strcmp(mountb, mounta)); 548 2474 eschrock } 549 789 ahrens 550 789 ahrens /* 551 3126 ahl * Given a ZFS handle and a property, construct a complete list of datasets 552 3126 ahl * that need to be modified as part of this process. For anything but the 553 789 ahrens * 'mountpoint' and 'sharenfs' properties, this just returns an empty list. 554 3126 ahl * Otherwise, we iterate over all children and look for any datasets that 555 3126 ahl * inherit the property. For each such dataset, we add it to the list and 556 3126 ahl * mark whether it was shared beforehand. 557 789 ahrens */ 558 789 ahrens prop_changelist_t * 559 7366 Tim changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags, 560 7366 Tim int mnt_flags) 561 789 ahrens { 562 2082 eschrock prop_changelist_t *clp; 563 789 ahrens prop_changenode_t *cn; 564 789 ahrens zfs_handle_t *temp; 565 789 ahrens char property[ZFS_MAXPROPLEN]; 566 2474 eschrock uu_compare_fn_t *compare = NULL; 567 10196 william boolean_t legacy = B_FALSE; 568 2082 eschrock 569 2082 eschrock if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL) 570 2082 eschrock return (NULL); 571 789 ahrens 572 2474 eschrock /* 573 2474 eschrock * For mountpoint-related tasks, we want to sort everything by 574 2474 eschrock * mountpoint, so that we mount and unmount them in the appropriate 575 2474 eschrock * order, regardless of their position in the hierarchy. 576 2474 eschrock */ 577 2474 eschrock if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED || 578 5331 amw prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS || 579 5331 amw prop == ZFS_PROP_SHARESMB) { 580 10196 william 581 10196 william if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 582 10196 william property, sizeof (property), 583 10196 william NULL, NULL, 0, B_FALSE) == 0 && 584 10196 william (strcmp(property, "legacy") == 0 || 585 10196 william strcmp(property, "none") == 0)) { 586 10196 william 587 10196 william legacy = B_TRUE; 588 10196 william } 589 10196 william if (!legacy) { 590 10196 william compare = compare_mountpoints; 591 10196 william clp->cl_sorted = B_TRUE; 592 10196 william } 593 2474 eschrock } 594 2474 eschrock 595 789 ahrens clp->cl_pool = uu_list_pool_create("changelist_pool", 596 789 ahrens sizeof (prop_changenode_t), 597 789 ahrens offsetof(prop_changenode_t, cn_listnode), 598 2474 eschrock compare, 0); 599 2142 eschrock if (clp->cl_pool == NULL) { 600 2142 eschrock assert(uu_error() == UU_ERROR_NO_MEMORY); 601 2142 eschrock (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 602 2142 eschrock changelist_free(clp); 603 2142 eschrock return (NULL); 604 2142 eschrock } 605 789 ahrens 606 2474 eschrock clp->cl_list = uu_list_create(clp->cl_pool, NULL, 607 2474 eschrock clp->cl_sorted ? UU_LIST_SORTED : 0); 608 7366 Tim clp->cl_gflags = gather_flags; 609 7366 Tim clp->cl_mflags = mnt_flags; 610 2142 eschrock 611 2142 eschrock if (clp->cl_list == NULL) { 612 2142 eschrock assert(uu_error() == UU_ERROR_NO_MEMORY); 613 2142 eschrock (void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error"); 614 2142 eschrock changelist_free(clp); 615 2142 eschrock return (NULL); 616 2142 eschrock } 617 789 ahrens 618 789 ahrens /* 619 789 ahrens * If this is a rename or the 'zoned' property, we pretend we're 620 789 ahrens * changing the mountpoint and flag it so we can catch all children in 621 789 ahrens * change_one(). 622 1294 lling * 623 3126 ahl * Flag cl_alldependents to catch all children plus the dependents 624 3126 ahl * (clones) that are not in the hierarchy. 625 789 ahrens */ 626 1294 lling if (prop == ZFS_PROP_NAME) { 627 1294 lling clp->cl_prop = ZFS_PROP_MOUNTPOINT; 628 2082 eschrock clp->cl_alldependents = B_TRUE; 629 1294 lling } else if (prop == ZFS_PROP_ZONED) { 630 789 ahrens clp->cl_prop = ZFS_PROP_MOUNTPOINT; 631 2082 eschrock clp->cl_allchildren = B_TRUE; 632 2676 eschrock } else if (prop == ZFS_PROP_CANMOUNT) { 633 2676 eschrock clp->cl_prop = ZFS_PROP_MOUNTPOINT; 634 3126 ahl } else if (prop == ZFS_PROP_VOLSIZE) { 635 3126 ahl clp->cl_prop = ZFS_PROP_MOUNTPOINT; 636 789 ahrens } else { 637 789 ahrens clp->cl_prop = prop; 638 789 ahrens } 639 789 ahrens clp->cl_realprop = prop; 640 789 ahrens 641 789 ahrens if (clp->cl_prop != ZFS_PROP_MOUNTPOINT && 642 3126 ahl clp->cl_prop != ZFS_PROP_SHARENFS && 643 5331 amw clp->cl_prop != ZFS_PROP_SHARESMB && 644 3126 ahl clp->cl_prop != ZFS_PROP_SHAREISCSI) 645 789 ahrens return (clp); 646 5331 amw 647 5331 amw /* 648 5331 amw * If watching SHARENFS or SHARESMB then 649 5331 amw * also watch its companion property. 650 5331 amw */ 651 5331 amw if (clp->cl_prop == ZFS_PROP_SHARENFS) 652 5331 amw clp->cl_shareprop = ZFS_PROP_SHARESMB; 653 5331 amw else if (clp->cl_prop == ZFS_PROP_SHARESMB) 654 5331 amw clp->cl_shareprop = ZFS_PROP_SHARENFS; 655 789 ahrens 656 1294 lling if (clp->cl_alldependents) { 657 2474 eschrock if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) { 658 1294 lling changelist_free(clp); 659 1294 lling return (NULL); 660 1294 lling } 661 1294 lling } else if (zfs_iter_children(zhp, change_one, clp) != 0) { 662 789 ahrens changelist_free(clp); 663 789 ahrens return (NULL); 664 789 ahrens } 665 789 ahrens 666 789 ahrens /* 667 789 ahrens * We have to re-open ourselves because we auto-close all the handles 668 789 ahrens * and can't tell the difference. 669 789 ahrens */ 670 2082 eschrock if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp), 671 5094 lling ZFS_TYPE_DATASET)) == NULL) { 672 2082 eschrock changelist_free(clp); 673 789 ahrens return (NULL); 674 789 ahrens } 675 789 ahrens 676 789 ahrens /* 677 789 ahrens * Always add ourself to the list. We add ourselves to the end so that 678 789 ahrens * we're the last to be unmounted. 679 789 ahrens */ 680 2082 eschrock if ((cn = zfs_alloc(zhp->zfs_hdl, 681 2082 eschrock sizeof (prop_changenode_t))) == NULL) { 682 2082 eschrock zfs_close(temp); 683 2082 eschrock changelist_free(clp); 684 2082 eschrock return (NULL); 685 2082 eschrock } 686 2082 eschrock 687 789 ahrens cn->cn_handle = temp; 688 7366 Tim cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) || 689 7366 Tim zfs_is_mounted(temp, NULL); 690 3126 ahl cn->cn_shared = zfs_is_shared(temp); 691 789 ahrens cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED); 692 5610 mmusante cn->cn_needpost = B_TRUE; 693 789 ahrens 694 789 ahrens uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool); 695 2474 eschrock if (clp->cl_sorted) { 696 2474 eschrock uu_list_index_t idx; 697 2474 eschrock (void) uu_list_find(clp->cl_list, cn, NULL, &idx); 698 2474 eschrock uu_list_insert(clp->cl_list, cn, idx); 699 2474 eschrock } else { 700 10196 william /* 701 10196 william * Add the target dataset to the end of the list. 702 10196 william * The list is not really unsorted. The list will be 703 10196 william * in reverse dataset name order. This is necessary 704 10196 william * when the original mountpoint is legacy or none. 705 10196 william */ 706 2474 eschrock verify(uu_list_insert_after(clp->cl_list, 707 2474 eschrock uu_list_last(clp->cl_list), cn) == 0); 708 2474 eschrock } 709 789 ahrens 710 789 ahrens /* 711 4840 rm160521 * If the mountpoint property was previously 'legacy', or 'none', 712 4840 rm160521 * record it as the behavior of changelist_postfix() will be different. 713 789 ahrens */ 714 10196 william if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) { 715 6168 hs24103 /* 716 6168 hs24103 * do not automatically mount ex-legacy datasets if 717 6168 hs24103 * we specifically set canmount to noauto 718 6168 hs24103 */ 719 6168 hs24103 if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) != 720 6168 hs24103 ZFS_CANMOUNT_NOAUTO) 721 6168 hs24103 clp->cl_waslegacy = B_TRUE; 722 6168 hs24103 } 723 789 ahrens 724 789 ahrens return (clp); 725 789 ahrens } 726