1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * System includes 28 */ 29 #include <assert.h> 30 #include <errno.h> 31 #include <libintl.h> 32 #include <libnvpair.h> 33 #include <libzfs.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <sys/mntent.h> 38 #include <sys/mnttab.h> 39 #include <sys/mount.h> 40 #include <sys/stat.h> 41 #include <sys/types.h> 42 #include <sys/vfstab.h> 43 #include <sys/zone.h> 44 #include <sys/mkdev.h> 45 #include <unistd.h> 46 47 #include <libbe.h> 48 #include <libbe_priv.h> 49 50 #define BE_TMP_MNTPNT "/tmp/.be.XXXXXX" 51 52 typedef struct dir_data { 53 char *dir; 54 char *ds; 55 } dir_data_t; 56 57 /* Private function prototypes */ 58 static int be_mount_callback(zfs_handle_t *, void *); 59 static int be_unmount_callback(zfs_handle_t *, void *); 60 static int be_get_legacy_fs_callback(zfs_handle_t *, void *); 61 static int fix_mountpoint(zfs_handle_t *); 62 static int fix_mountpoint_callback(zfs_handle_t *, void *); 63 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t, 64 boolean_t); 65 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *); 66 static int loopback_mount_zonepath(const char *, be_mount_data_t *); 67 static int iter_shared_fs_callback(zfs_handle_t *, void *); 68 static int zpool_shared_fs_callback(zpool_handle_t *, void *); 69 static int unmount_shared_fs(be_unmount_data_t *); 70 static int add_to_fs_list(be_fs_list_data_t *, const char *); 71 static int be_mount_root(zfs_handle_t *, char *); 72 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *); 73 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *); 74 static int be_unmount_zones(be_unmount_data_t *); 75 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *, 76 char *); 77 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *); 78 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *); 79 80 81 /* ******************************************************************** */ 82 /* Public Functions */ 83 /* ******************************************************************** */ 84 85 /* 86 * Function: be_mount 87 * Description: Mounts a BE and its subordinate datasets at a given mountpoint. 88 * Parameters: 89 * be_attrs - pointer to nvlist_t of attributes being passed in. 90 * The following attributes are used by this function: 91 * 92 * BE_ATTR_ORIG_BE_NAME *required 93 * BE_ATTR_MOUNTPOINT *required 94 * BE_ATTR_MOUNT_FLAGS *optional 95 * Return: 96 * BE_SUCCESS - Success 97 * be_errno_t - Failure 98 * Scope: 99 * Public 100 */ 101 int 102 be_mount(nvlist_t *be_attrs) 103 { 104 char *be_name = NULL; 105 char *mountpoint = NULL; 106 uint16_t flags = 0; 107 int ret = BE_SUCCESS; 108 109 /* Initialize libzfs handle */ 110 if (!be_zfs_init()) 111 return (BE_ERR_INIT); 112 113 /* Get original BE name */ 114 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name) 115 != 0) { 116 be_print_err(gettext("be_mount: failed to lookup " 117 "BE_ATTR_ORIG_BE_NAME attribute\n")); 118 return (BE_ERR_INVAL); 119 } 120 121 /* Validate original BE name */ 122 if (!be_valid_be_name(be_name)) { 123 be_print_err(gettext("be_mount: invalid BE name %s\n"), 124 be_name); 125 return (BE_ERR_INVAL); 126 } 127 128 /* Get mountpoint */ 129 if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint) 130 != 0) { 131 be_print_err(gettext("be_mount: failed to lookup " 132 "BE_ATTR_MOUNTPOINT attribute\n")); 133 return (BE_ERR_INVAL); 134 } 135 136 /* Get flags */ 137 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 138 BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) { 139 be_print_err(gettext("be_mount: failed to lookup " 140 "BE_ATTR_MOUNT_FLAGS attribute\n")); 141 return (BE_ERR_INVAL); 142 } 143 144 ret = _be_mount(be_name, &mountpoint, flags); 145 146 be_zfs_fini(); 147 148 return (ret); 149 } 150 151 /* 152 * Function: be_unmount 153 * Description: Unmounts a BE and its subordinate datasets. 154 * Parameters: 155 * be_attrs - pointer to nvlist_t of attributes being passed in. 156 * The following attributes are used by this function: 157 * 158 * BE_ATTR_ORIG_BE_NAME *required 159 * BE_ATTR_UNMOUNT_FLAGS *optional 160 * Return: 161 * BE_SUCCESS - Success 162 * be_errno_t - Failure 163 * Scope: 164 * Public 165 */ 166 int 167 be_unmount(nvlist_t *be_attrs) 168 { 169 char *be_name = NULL; 170 uint16_t flags = 0; 171 int ret = BE_SUCCESS; 172 173 /* Initialize libzfs handle */ 174 if (!be_zfs_init()) 175 return (BE_ERR_INIT); 176 177 /* Get original BE name */ 178 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name) 179 != 0) { 180 be_print_err(gettext("be_unmount: failed to lookup " 181 "BE_ATTR_ORIG_BE_NAME attribute\n")); 182 return (BE_ERR_INVAL); 183 } 184 185 /* Validate original BE name */ 186 if (!be_valid_be_name(be_name)) { 187 be_print_err(gettext("be_unmount: invalid BE name %s\n"), 188 be_name); 189 return (BE_ERR_INVAL); 190 } 191 192 /* Get unmount flags */ 193 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 194 BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) { 195 be_print_err(gettext("be_unmount: failed to loookup " 196 "BE_ATTR_UNMOUNT_FLAGS attribute\n")); 197 return (BE_ERR_INVAL); 198 } 199 200 ret = _be_unmount(be_name, flags); 201 202 be_zfs_fini(); 203 204 return (ret); 205 } 206 207 /* ******************************************************************** */ 208 /* Semi-Private Functions */ 209 /* ******************************************************************** */ 210 211 /* 212 * Function: _be_mount 213 * Description: Mounts a BE. If the altroot is not provided, this function 214 * will generate a temporary mountpoint to mount the BE at. It 215 * will return this temporary mountpoint to the caller via the 216 * altroot reference pointer passed in. This returned value is 217 * allocated on heap storage and is the repsonsibility of the 218 * caller to free. 219 * Parameters: 220 * be_name - pointer to name of BE to mount. 221 * altroot - reference pointer to altroot of where to mount BE. 222 * flags - flag indicating special handling for mounting the BE 223 * Return: 224 * BE_SUCCESS - Success 225 * be_errno_t - Failure 226 * Scope: 227 * Semi-private (library wide use only) 228 */ 229 int 230 _be_mount(char *be_name, char **altroot, int flags) 231 { 232 be_transaction_data_t bt = { 0 }; 233 be_mount_data_t md = { 0 }; 234 zfs_handle_t *zhp; 235 char obe_root_ds[MAXPATHLEN]; 236 char *mp = NULL; 237 char *tmp_altroot = NULL; 238 int ret = BE_SUCCESS, err = 0; 239 uuid_t uu = { 0 }; 240 boolean_t gen_tmp_altroot = B_FALSE; 241 242 if (be_name == NULL || altroot == NULL) 243 return (BE_ERR_INVAL); 244 245 /* Set be_name as obe_name in bt structure */ 246 bt.obe_name = be_name; 247 248 /* Find which zpool obe_name lives in */ 249 if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 250 be_print_err(gettext("be_mount: failed to " 251 "find zpool for BE (%s)\n"), bt.obe_name); 252 return (BE_ERR_BE_NOENT); 253 } else if (err < 0) { 254 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"), 255 libzfs_error_description(g_zfs)); 256 return (zfs_err_to_be_err(g_zfs)); 257 } 258 259 /* Generate string for obe_name's root dataset */ 260 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 261 sizeof (obe_root_ds)); 262 bt.obe_root_ds = obe_root_ds; 263 264 /* Get handle to BE's root dataset */ 265 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 266 NULL) { 267 be_print_err(gettext("be_mount: failed to " 268 "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 269 libzfs_error_description(g_zfs)); 270 return (zfs_err_to_be_err(g_zfs)); 271 } 272 273 /* Make sure BE's root dataset isn't already mounted somewhere */ 274 if (zfs_is_mounted(zhp, &mp)) { 275 ZFS_CLOSE(zhp); 276 be_print_err(gettext("be_mount: %s is already mounted " 277 "at %s\n"), bt.obe_name, mp != NULL ? mp : ""); 278 free(mp); 279 return (BE_ERR_MOUNTED); 280 } 281 282 /* 283 * Fix this BE's mountpoint if its root dataset isn't set to 284 * either 'legacy' or '/'. 285 */ 286 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) { 287 be_print_err(gettext("be_mount: mountpoint check " 288 "failed for %s\n"), bt.obe_root_ds); 289 ZFS_CLOSE(zhp); 290 return (ret); 291 } 292 293 /* 294 * If altroot not provided, create a temporary alternate root 295 * to mount on 296 */ 297 if (*altroot == NULL) { 298 if ((ret = be_make_tmp_mountpoint(&tmp_altroot)) 299 != BE_SUCCESS) { 300 be_print_err(gettext("be_mount: failed to " 301 "make temporary mountpoint\n")); 302 ZFS_CLOSE(zhp); 303 return (ret); 304 } 305 gen_tmp_altroot = B_TRUE; 306 } else { 307 tmp_altroot = *altroot; 308 } 309 310 /* Mount the BE's root file system */ 311 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) { 312 be_print_err(gettext("be_mount: failed to " 313 "mount BE root file system\n")); 314 if (gen_tmp_altroot) 315 free(tmp_altroot); 316 ZFS_CLOSE(zhp); 317 return (ret); 318 } 319 320 /* Iterate through BE's children filesystems */ 321 if ((err = zfs_iter_filesystems(zhp, be_mount_callback, 322 tmp_altroot)) != 0) { 323 be_print_err(gettext("be_mount: failed to " 324 "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot); 325 if (gen_tmp_altroot) 326 free(tmp_altroot); 327 ZFS_CLOSE(zhp); 328 return (err); 329 } 330 331 md.altroot = tmp_altroot; 332 md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS; 333 md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW; 334 335 /* 336 * Mount shared file systems if mount flag says so. 337 */ 338 if (md.shared_fs) { 339 /* 340 * Mount all ZFS file systems not under the BE's root dataset 341 */ 342 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md); 343 344 /* TODO: Mount all non-ZFS file systems - Not supported yet */ 345 } 346 347 /* 348 * If we're in the global zone and the global zone has a valid uuid, 349 * mount all supported non-global zones. 350 */ 351 if (getzoneid() == GLOBAL_ZONEID && 352 !(flags & BE_MOUNT_FLAG_NO_ZONES) && 353 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) { 354 if ((ret = be_mount_zones(zhp, &md)) != BE_SUCCESS) { 355 (void) _be_unmount(bt.obe_name, 0); 356 if (gen_tmp_altroot) 357 free(tmp_altroot); 358 ZFS_CLOSE(zhp); 359 return (ret); 360 } 361 } 362 363 ZFS_CLOSE(zhp); 364 365 /* 366 * If a NULL altroot was passed in, pass the generated altroot 367 * back to the caller in altroot. 368 */ 369 if (gen_tmp_altroot) 370 *altroot = tmp_altroot; 371 372 return (BE_SUCCESS); 373 } 374 375 /* 376 * Function: _be_unmount 377 * Description: Unmount a BE. 378 * Parameters: 379 * be_name - pointer to name of BE to unmount. 380 * flags - flags for unmounting the BE. 381 * Returns: 382 * BE_SUCCESS - Success 383 * be_errno_t - Failure 384 * Scope: 385 * Semi-private (library wide use only) 386 */ 387 int 388 _be_unmount(char *be_name, int flags) 389 { 390 be_transaction_data_t bt = { 0 }; 391 be_unmount_data_t ud = { 0 }; 392 zfs_handle_t *zhp; 393 uuid_t uu = { 0 }; 394 char obe_root_ds[MAXPATHLEN]; 395 char mountpoint[MAXPATHLEN]; 396 char *mp = NULL; 397 int ret = BE_SUCCESS; 398 int zret = 0; 399 400 if (be_name == NULL) 401 return (BE_ERR_INVAL); 402 403 /* Set be_name as obe_name in bt structure */ 404 bt.obe_name = be_name; 405 406 /* Find which zpool obe_name lives in */ 407 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 408 be_print_err(gettext("be_unmount: failed to " 409 "find zpool for BE (%s)\n"), bt.obe_name); 410 return (BE_ERR_BE_NOENT); 411 } else if (zret < 0) { 412 be_print_err(gettext("be_unmount: " 413 "zpool_iter failed: %s\n"), 414 libzfs_error_description(g_zfs)); 415 ret = zfs_err_to_be_err(g_zfs); 416 return (ret); 417 } 418 419 /* Generate string for obe_name's root dataset */ 420 be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 421 sizeof (obe_root_ds)); 422 bt.obe_root_ds = obe_root_ds; 423 424 /* Get handle to BE's root dataset */ 425 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 426 NULL) { 427 be_print_err(gettext("be_unmount: failed to " 428 "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 429 libzfs_error_description(g_zfs)); 430 ret = zfs_err_to_be_err(g_zfs); 431 return (ret); 432 } 433 434 /* Make sure BE's root dataset is mounted somewhere */ 435 if (!zfs_is_mounted(zhp, &mp)) { 436 437 be_print_err(gettext("be_unmount: " 438 "(%s) not mounted\n"), bt.obe_name); 439 440 /* 441 * BE is not mounted, fix this BE's mountpoint if its root 442 * dataset isn't set to either 'legacy' or '/'. 443 */ 444 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) { 445 be_print_err(gettext("be_unmount: mountpoint check " 446 "failed for %s\n"), bt.obe_root_ds); 447 ZFS_CLOSE(zhp); 448 return (ret); 449 } 450 451 ZFS_CLOSE(zhp); 452 return (BE_SUCCESS); 453 } 454 455 /* 456 * If we didn't get a mountpoint from the zfs_is_mounted call, 457 * try and get it from its property. 458 */ 459 if (mp == NULL) { 460 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 461 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 462 be_print_err(gettext("be_unmount: failed to " 463 "get mountpoint of (%s)\n"), bt.obe_name); 464 ZFS_CLOSE(zhp); 465 return (BE_ERR_ZFS); 466 } 467 } else { 468 (void) strlcpy(mountpoint, mp, sizeof (mountpoint)); 469 free(mp); 470 } 471 472 /* If BE mounted as current root, fail */ 473 if (strcmp(mountpoint, "/") == 0) { 474 be_print_err(gettext("be_unmount: " 475 "cannot unmount currently running BE\n")); 476 ZFS_CLOSE(zhp); 477 return (BE_ERR_UMOUNT_CURR_BE); 478 } 479 480 ud.altroot = mountpoint; 481 ud.force = flags & BE_UNMOUNT_FLAG_FORCE; 482 483 /* Unmount all supported non-global zones if we're in the global zone */ 484 if (getzoneid() == GLOBAL_ZONEID && 485 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) { 486 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) { 487 ZFS_CLOSE(zhp); 488 return (ret); 489 } 490 } 491 492 /* TODO: Unmount all non-ZFS file systems - Not supported yet */ 493 494 /* Unmount all ZFS file systems not under the BE root dataset */ 495 if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) { 496 be_print_err(gettext("be_unmount: failed to " 497 "unmount shared file systems\n")); 498 ZFS_CLOSE(zhp); 499 return (ret); 500 } 501 502 /* Unmount all children datasets under the BE's root dataset */ 503 if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback, 504 &ud)) != 0) { 505 be_print_err(gettext("be_unmount: failed to " 506 "unmount BE (%s)\n"), bt.obe_name); 507 ZFS_CLOSE(zhp); 508 return (zret); 509 } 510 511 /* Unmount this BE's root filesystem */ 512 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) { 513 ZFS_CLOSE(zhp); 514 return (ret); 515 } 516 517 ZFS_CLOSE(zhp); 518 519 return (BE_SUCCESS); 520 } 521 522 /* 523 * Function: be_mount_zone_root 524 * Description: Mounts the zone root dataset for a zone. 525 * Parameters: 526 * zfs - zfs_handle_t pointer to zone root dataset 527 * md - be_mount_data_t pointer to data for zone to be mounted 528 * Returns: 529 * BE_SUCCESS - Success 530 * be_errno_t - Failure 531 * Scope: 532 * Semi-private (library wide use only) 533 */ 534 int 535 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md) 536 { 537 char mountpoint[MAXPATHLEN]; 538 int err = 0; 539 540 /* Get mountpoint property of dataset */ 541 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 542 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 543 be_print_err(gettext("be_mount_zone_root: failed to " 544 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp), 545 libzfs_error_description(g_zfs)); 546 return (zfs_err_to_be_err(g_zfs)); 547 } 548 549 /* 550 * Make sure zone's root dataset is set to 'legacy'. This is 551 * currently a requirement in this implementation of zones 552 * support. 553 */ 554 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 555 be_print_err(gettext("be_mount_zone_root: " 556 "zone root dataset mountpoint is not 'legacy'\n")); 557 return (BE_ERR_ZONE_ROOT_NOT_LEGACY); 558 } 559 560 /* 561 * Legacy mount the zone root dataset. 562 * 563 * As a workaround for 6176743, we mount the zone's root with the 564 * MS_OVERLAY option in case an alternate BE is mounted, and we're 565 * mounting the root for the zone from the current BE here. When an 566 * alternate BE is mounted, it ties up the zone's zoneroot directory 567 * for the current BE since the zone's zonepath is loopback mounted 568 * from the current BE. 569 * 570 * TODO: The MS_OVERLAY option needs to be removed when 6176743 571 * is fixed. 572 */ 573 if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS, 574 NULL, 0, NULL, 0) != 0) { 575 err = errno; 576 be_print_err(gettext("be_mount_zone_root: failed to " 577 "legacy mount zone root dataset (%s) at %s\n"), 578 zfs_get_name(zhp), md->altroot); 579 return (errno_to_be_err(err)); 580 } 581 582 return (BE_SUCCESS); 583 } 584 585 /* 586 * Function: be_unmount_zone_root 587 * Description: Unmounts the zone root dataset for a zone. 588 * Parameters: 589 * zhp - zfs_handle_t pointer to zone root dataset 590 * ud - be_unmount_data_t pointer to data for zone to be unmounted 591 * Returns: 592 * BE_SUCCESS - Success 593 * be_errno_t - Failure 594 * Scope: 595 * Semi-private (library wise use only) 596 */ 597 int 598 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud) 599 { 600 char mountpoint[MAXPATHLEN]; 601 602 /* Unmount the dataset */ 603 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) { 604 be_print_err(gettext("be_unmount_zone_root: failed to " 605 "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp), 606 libzfs_error_description(g_zfs)); 607 return (zfs_err_to_be_err(g_zfs)); 608 } 609 610 /* Get the current mountpoint property for the zone root dataset */ 611 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 612 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 613 be_print_err(gettext("be_unmount_zone_root: failed to " 614 "get mountpoint property for zone root dataset (%s): %s\n"), 615 zfs_get_name(zhp), libzfs_error_description(g_zfs)); 616 return (zfs_err_to_be_err(g_zfs)); 617 } 618 619 /* If mountpoint not already set to 'legacy', set it to 'legacy' */ 620 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 621 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 622 ZFS_MOUNTPOINT_LEGACY) != 0) { 623 be_print_err(gettext("be_unmount_zone_root: " 624 "failed to set mountpoint of zone root dataset " 625 "%s to 'legacy': %s\n"), zfs_get_name(zhp), 626 libzfs_error_description(g_zfs)); 627 return (zfs_err_to_be_err(g_zfs)); 628 } 629 } 630 631 return (BE_SUCCESS); 632 } 633 634 /* 635 * Function: be_get_legacy_fs 636 * Description: This function iterates through all non-shared file systems 637 * of a BE and finds the ones with a legacy mountpoint. For 638 * those file systems, it reads the BE's vfstab to get the 639 * mountpoint. If found, it adds that file system to the 640 * be_fs_list_data_t passed in. 641 * 642 * This function can be used to gather legacy mounted file systems 643 * for both global BEs and non-global zone BEs. To get data for 644 * a non-global zone BE, the zoneroot_ds and zoneroot parameters 645 * will be specified, otherwise they should be set to NULL. 646 * Parameters: 647 * be_name - global BE name from which to get legacy file 648 * system list. 649 * be_root_ds - root dataset of global BE. 650 * zoneroot_ds - root dataset of zone. 651 * zoneroot - zoneroot path of zone. 652 * fld - be_fs_list_data_t pointer. 653 * Returns: 654 * BE_SUCCESS - Success 655 * be_errno_t - Failure 656 * Scope: 657 * Semi-private (library wide use only) 658 */ 659 int 660 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds, 661 char *zoneroot, be_fs_list_data_t *fld) 662 { 663 zfs_handle_t *zhp = NULL; 664 char mountpoint[MAXPATHLEN]; 665 boolean_t mounted_here = B_FALSE; 666 boolean_t zone_mounted_here = B_FALSE; 667 int ret = BE_SUCCESS, err = 0; 668 669 if (be_name == NULL || be_root_ds == NULL || fld == NULL) 670 return (BE_ERR_INVAL); 671 672 /* Get handle to BE's root dataset */ 673 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) 674 == NULL) { 675 be_print_err(gettext("be_get_legacy_fs: failed to " 676 "open BE root dataset (%s): %s\n"), be_root_ds, 677 libzfs_error_description(g_zfs)); 678 ret = zfs_err_to_be_err(g_zfs); 679 return (ret); 680 } 681 682 /* If BE is not already mounted, mount it. */ 683 if (!zfs_is_mounted(zhp, &fld->altroot)) { 684 if ((ret = _be_mount(be_name, &fld->altroot, 685 zoneroot_ds ? BE_MOUNT_FLAG_NULL : 686 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 687 be_print_err(gettext("be_get_legacy_fs: " 688 "failed to mount BE %s\n"), be_name); 689 goto cleanup; 690 } 691 692 mounted_here = B_TRUE; 693 } else if (fld->altroot == NULL) { 694 be_print_err(gettext("be_get_legacy_fs: failed to " 695 "get altroot of mounted BE %s: %s\n"), 696 be_name, libzfs_error_description(g_zfs)); 697 ret = zfs_err_to_be_err(g_zfs); 698 goto cleanup; 699 } 700 701 /* 702 * If a zone root dataset was passed in, we're wanting to get 703 * legacy mounted file systems for that zone, not the global 704 * BE. 705 */ 706 if (zoneroot_ds != NULL) { 707 be_mount_data_t zone_md = { 0 }; 708 709 /* Close off handle to global BE's root dataset */ 710 ZFS_CLOSE(zhp); 711 712 /* Get handle to zone's root dataset */ 713 if ((zhp = zfs_open(g_zfs, zoneroot_ds, 714 ZFS_TYPE_FILESYSTEM)) == NULL) { 715 be_print_err(gettext("be_get_legacy_fs: failed to " 716 "open zone BE root dataset (%s): %s\n"), 717 zoneroot_ds, libzfs_error_description(g_zfs)); 718 ret = zfs_err_to_be_err(g_zfs); 719 goto cleanup; 720 } 721 722 /* Make sure the zone we're looking for is mounted */ 723 if (!zfs_is_mounted(zhp, &zone_md.altroot)) { 724 char zone_altroot[MAXPATHLEN]; 725 726 /* Generate alternate root path for zone */ 727 (void) snprintf(zone_altroot, sizeof (zone_altroot), 728 "%s%s", fld->altroot, zoneroot); 729 if ((zone_md.altroot = strdup(zone_altroot)) == NULL) { 730 be_print_err(gettext("be_get_legacy_fs: " 731 "memory allocation failed\n")); 732 ret = BE_ERR_NOMEM; 733 goto cleanup; 734 } 735 736 if ((ret = be_mount_zone_root(zhp, &zone_md)) 737 != BE_SUCCESS) { 738 be_print_err(gettext("be_get_legacy_fs: " 739 "failed to mount zone root %s\n"), 740 zoneroot_ds); 741 free(zone_md.altroot); 742 zone_md.altroot = NULL; 743 goto cleanup; 744 } 745 zone_mounted_here = B_TRUE; 746 } 747 748 free(fld->altroot); 749 fld->altroot = zone_md.altroot; 750 } 751 752 /* 753 * If the root dataset is in the vfstab with a mountpoint of "/", 754 * add it to the list 755 */ 756 if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp), 757 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) { 758 if (strcmp(mountpoint, "/") == 0) { 759 if (add_to_fs_list(fld, zfs_get_name(zhp)) 760 != BE_SUCCESS) { 761 be_print_err(gettext("be_get_legacy_fs: " 762 "failed to add %s to fs list\n"), 763 zfs_get_name(zhp)); 764 ret = BE_ERR_INVAL; 765 goto cleanup; 766 } 767 } 768 } 769 770 /* Iterate subordinate file systems looking for legacy mounts */ 771 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback, 772 fld)) != 0) { 773 be_print_err(gettext("be_get_legacy_fs: " 774 "failed to iterate %s to get legacy mounts\n"), 775 zfs_get_name(zhp)); 776 } 777 778 cleanup: 779 /* If we mounted the zone BE, unmount it */ 780 if (zone_mounted_here) { 781 be_unmount_data_t zone_ud = { 0 }; 782 783 zone_ud.altroot = fld->altroot; 784 zone_ud.force = B_TRUE; 785 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) { 786 be_print_err(gettext("be_get_legacy_fs: " 787 "failed to unmount zone root %s\n"), 788 zoneroot_ds); 789 if (ret == BE_SUCCESS) 790 ret = err; 791 } 792 } 793 794 /* If we mounted this BE, unmount it */ 795 if (mounted_here) { 796 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) { 797 be_print_err(gettext("be_get_legacy_fs: " 798 "failed to unmount %s\n"), be_name); 799 if (ret == BE_SUCCESS) 800 ret = err; 801 } 802 } 803 804 ZFS_CLOSE(zhp); 805 806 free(fld->altroot); 807 fld->altroot = NULL; 808 809 return (ret); 810 } 811 812 /* 813 * Function: be_free_fs_list 814 * Description: Function used to free the members of a be_fs_list_data_t 815 * structure. 816 * Parameters: 817 * fld - be_fs_list_data_t pointer to free. 818 * Returns: 819 * None 820 * Scope: 821 * Semi-private (library wide use only) 822 */ 823 void 824 be_free_fs_list(be_fs_list_data_t *fld) 825 { 826 int i; 827 828 if (fld == NULL) 829 return; 830 831 free(fld->altroot); 832 833 if (fld->fs_list == NULL) 834 return; 835 836 for (i = 0; i < fld->fs_num; i++) 837 free(fld->fs_list[i]); 838 839 free(fld->fs_list); 840 } 841 842 /* 843 * Function: be_get_ds_from_dir(char *dir) 844 * Description: Given a directory path, find the underlying dataset mounted 845 * at that directory path if there is one. The returned name 846 * is allocated in heap storage, so the caller is responsible 847 * for freeing it. 848 * Parameters: 849 * dir - char pointer of directory to find. 850 * Returns: 851 * NULL - if directory is not mounted from a dataset. 852 * name of dataset mounted at dir. 853 * Scope: 854 * Semi-private (library wide use only) 855 */ 856 char * 857 be_get_ds_from_dir(char *dir) 858 { 859 dir_data_t dd = { 0 }; 860 char resolved_dir[MAXPATHLEN]; 861 862 /* Make sure length of dir is within the max length */ 863 if (dir == NULL || strlen(dir) >= MAXPATHLEN) 864 return (NULL); 865 866 /* Resolve dir in case its lofs mounted */ 867 (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir)); 868 z_resolve_lofs(resolved_dir, sizeof (resolved_dir)); 869 870 dd.dir = resolved_dir; 871 872 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd); 873 874 return (dd.ds); 875 } 876 877 /* 878 * Function: be_make_tmp_mountpoint 879 * Description: This function generates a random temporary mountpoint 880 * and creates that mountpoint directory. It returns the 881 * mountpoint in heap storage, so the caller is responsible 882 * for freeing it. 883 * Parameters: 884 * tmp_mp - reference to pointer of where to store generated 885 * temporary mountpoint. 886 * Returns: 887 * BE_SUCCESS - Success 888 * be_errno_t - Failure 889 * Scope: 890 * Semi-private (library wide use only) 891 */ 892 int 893 be_make_tmp_mountpoint(char **tmp_mp) 894 { 895 int err = 0; 896 897 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) { 898 be_print_err(gettext("be_make_tmp_mountpoint: " 899 "malloc failed\n")); 900 return (BE_ERR_NOMEM); 901 } 902 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1); 903 if (mkdtemp(*tmp_mp) == NULL) { 904 err = errno; 905 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed " 906 "for %s: %s\n"), *tmp_mp, strerror(err)); 907 free(*tmp_mp); 908 *tmp_mp = NULL; 909 return (errno_to_be_err(err)); 910 } 911 912 return (BE_SUCCESS); 913 } 914 915 /* 916 * Function: be_mount_pool 917 * Description: This function determines if the pool's datase is mounted 918 * and if not it is used to mount the pool's dataset. The 919 * function returns the current mountpoint if we are able 920 * to mount the dataset. 921 * Parameters: 922 * zhp - handle to the pool's dataset 923 * tmp_mntpnt - The temporary mountpoint that the pool's 924 * dataset is mounted on. This is set only 925 * if the attempt to mount the dataset at it's 926 * set mountpoint fails, and we've used a 927 * temporary mount point for this dataset. It 928 * is expected that the caller will free this 929 * memory. 930 * orig_mntpnt - The original mountpoint for the pool. If a 931 * temporary mount point was needed this will 932 * be used to reset the mountpoint property to 933 * it's original mountpoint. It is expected that 934 * the caller will free this memory. 935 * pool_mounted - This flag indicates that the pool was mounted 936 * in this function. 937 * Returns: 938 * BE_SUCCESS - Success 939 * be_errno_t - Failure 940 * Scope: 941 * Semi-private (library wide use only) 942 */ 943 int 944 be_mount_pool( 945 zfs_handle_t *zhp, 946 char **tmp_mntpnt, 947 char **orig_mntpnt, 948 boolean_t *pool_mounted) 949 { 950 951 char mountpoint[MAXPATHLEN]; 952 int ret = 0; 953 954 *tmp_mntpnt = NULL; 955 *orig_mntpnt = NULL; 956 *pool_mounted = B_FALSE; 957 958 if (!zfs_is_mounted(zhp, NULL)) { 959 if (zfs_mount(zhp, NULL, 0) != 0) { 960 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 961 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 962 be_print_err(gettext("be_mount_pool: failed to " 963 "get mountpoint of (%s): %s\n"), 964 zfs_get_name(zhp), 965 libzfs_error_description(g_zfs)); 966 return (zfs_err_to_be_err(g_zfs)); 967 } 968 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) { 969 be_print_err(gettext("be_mount_pool: memory " 970 "allocation failed\n")); 971 return (BE_ERR_NOMEM); 972 } 973 /* 974 * attempt to mount on a temp mountpoint 975 */ 976 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt)) 977 != BE_SUCCESS) { 978 be_print_err(gettext("be_mount_pool: failed " 979 "to make temporary mountpoint\n")); 980 free(*orig_mntpnt); 981 *orig_mntpnt = NULL; 982 return (ret); 983 } 984 985 if (zfs_prop_set(zhp, 986 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 987 *tmp_mntpnt) != 0) { 988 be_print_err(gettext("be_mount_pool: failed " 989 "to set mountpoint of pool dataset %s to " 990 "%s: %s\n"), zfs_get_name(zhp), 991 *orig_mntpnt, 992 libzfs_error_description(g_zfs)); 993 free(*tmp_mntpnt); 994 free(*orig_mntpnt); 995 *orig_mntpnt = NULL; 996 *tmp_mntpnt = NULL; 997 return (zfs_err_to_be_err(g_zfs)); 998 } 999 1000 if (zfs_mount(zhp, NULL, 0) != 0) { 1001 be_print_err(gettext("be_mount_pool: failed " 1002 "to mount dataset %s at %s: %s\n"), 1003 zfs_get_name(zhp), *tmp_mntpnt, 1004 libzfs_error_description(g_zfs)); 1005 ret = zfs_err_to_be_err(g_zfs); 1006 if (zfs_prop_set(zhp, 1007 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1008 mountpoint) != 0) { 1009 be_print_err(gettext("be_mount_pool: " 1010 "failed to set mountpoint of pool " 1011 "dataset %s to %s: %s\n"), 1012 zfs_get_name(zhp), *tmp_mntpnt, 1013 libzfs_error_description(g_zfs)); 1014 } 1015 free(*tmp_mntpnt); 1016 free(*orig_mntpnt); 1017 *orig_mntpnt = NULL; 1018 *tmp_mntpnt = NULL; 1019 return (ret); 1020 } 1021 } 1022 *pool_mounted = B_TRUE; 1023 } 1024 1025 return (BE_SUCCESS); 1026 } 1027 1028 /* 1029 * Function: be_unmount_pool 1030 * Description: This function is used to unmount the pool's dataset if we 1031 * mounted it previously using be_mount_pool(). 1032 * Parameters: 1033 * zhp - handle to the pool's dataset 1034 * tmp_mntpnt - If a temprary mount point was used this will 1035 * be set. Since this was created in be_mount_pool 1036 * we will need to clean it up here. 1037 * orig_mntpnt - The original mountpoint for the pool. This is 1038 * used to set the dataset mountpoint property 1039 * back to it's original value in the case where a 1040 * temporary mountpoint was used. 1041 * Returns: 1042 * BE_SUCCESS - Success 1043 * be_errno_t - Failure 1044 * Scope: 1045 * Semi-private (library wide use only) 1046 */ 1047 int 1048 be_unmount_pool( 1049 zfs_handle_t *zhp, 1050 char *tmp_mntpnt, 1051 char *orig_mntpnt) 1052 { 1053 if (zfs_unmount(zhp, NULL, 0) != 0) { 1054 be_print_err(gettext("be_unmount_pool: failed to " 1055 "unmount pool (%s): %s\n"), zfs_get_name(zhp), 1056 libzfs_error_description(g_zfs)); 1057 return (zfs_err_to_be_err(g_zfs)); 1058 } 1059 if (orig_mntpnt != NULL) { 1060 if (tmp_mntpnt != NULL && 1061 strcmp(orig_mntpnt, tmp_mntpnt) != 0) { 1062 (void) rmdir(tmp_mntpnt); 1063 } 1064 if (zfs_prop_set(zhp, 1065 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1066 orig_mntpnt) != 0) { 1067 be_print_err(gettext("be_unmount_pool: failed " 1068 "to set the mountpoint for dataset (%s) to " 1069 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt, 1070 libzfs_error_description(g_zfs)); 1071 return (zfs_err_to_be_err(g_zfs)); 1072 } 1073 } 1074 1075 return (BE_SUCCESS); 1076 } 1077 1078 /* ******************************************************************** */ 1079 /* Private Functions */ 1080 /* ******************************************************************** */ 1081 1082 /* 1083 * Function: be_mount_callback 1084 * Description: Callback function used to iterate through all of a BE's 1085 * subordinate file systems and to mount them accordingly. 1086 * Parameters: 1087 * zhp - zfs_handle_t pointer to current file system being 1088 * processed. 1089 * data - pointer to the altroot of where to mount BE. 1090 * Returns: 1091 * 0 - Success 1092 * be_errno_t - Failure 1093 * Scope: 1094 * Private 1095 */ 1096 static int 1097 be_mount_callback(zfs_handle_t *zhp, void *data) 1098 { 1099 zprop_source_t sourcetype; 1100 const char *fs_name = zfs_get_name(zhp); 1101 char source[ZFS_MAXNAMELEN]; 1102 char *altroot = data; 1103 char zhp_mountpoint[MAXPATHLEN]; 1104 char mountpoint[MAXPATHLEN]; 1105 int ret = 0; 1106 1107 /* Get dataset's mountpoint and source values */ 1108 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint, 1109 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source), 1110 B_FALSE) != 0) { 1111 be_print_err(gettext("be_mount_callback: failed to " 1112 "get mountpoint and sourcetype for %s\n"), 1113 fs_name); 1114 ZFS_CLOSE(zhp); 1115 return (BE_ERR_ZFS); 1116 } 1117 1118 /* 1119 * Set this filesystem's 'canmount' property to 'noauto' just incase 1120 * it's been set 'on'. We do this so that when we change its 1121 * mountpoint zfs won't immediately try to mount it. 1122 */ 1123 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) { 1124 be_print_err(gettext("be_mount_callback: failed to " 1125 "set canmount to 'noauto' (%s)\n"), fs_name); 1126 ZFS_CLOSE(zhp); 1127 return (BE_ERR_ZFS); 1128 } 1129 1130 /* 1131 * If the mountpoint is none, there's nothing to do, goto next. 1132 * If the mountpoint is legacy, legacy mount it with mount(2). 1133 * If the mountpoint is inherited, its mountpoint should 1134 * already be set. If it's not, then explicitly fix-up 1135 * the mountpoint now by appending its explicitly set 1136 * mountpoint value to the BE mountpoint. 1137 */ 1138 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) { 1139 goto next; 1140 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1141 /* 1142 * If the mountpoint is set to 'legacy', we need to 1143 * dig into this BE's vfstab to figure out where to 1144 * mount it, and just mount it via mount(2). 1145 */ 1146 if (get_mountpoint_from_vfstab(altroot, fs_name, 1147 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) { 1148 1149 /* Legacy mount the file system */ 1150 if (mount(fs_name, mountpoint, MS_DATA, 1151 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) { 1152 be_print_err( 1153 gettext("be_mount_callback: " 1154 "failed to mount %s on %s\n"), 1155 fs_name, mountpoint); 1156 } 1157 } else { 1158 be_print_err( 1159 gettext("be_mount_callback: " 1160 "no entry for %s in vfstab, " 1161 "skipping ...\n"), fs_name); 1162 } 1163 1164 goto next; 1165 1166 } else if (sourcetype & ZPROP_SRC_INHERITED) { 1167 /* 1168 * If the mountpoint is inherited, its parent should have 1169 * already been processed so its current mountpoint value 1170 * is what its mountpoint ought to be. 1171 */ 1172 (void) strlcpy(mountpoint, zhp_mountpoint, sizeof (mountpoint)); 1173 } else if (sourcetype & ZPROP_SRC_LOCAL) { 1174 /* 1175 * Else process dataset with explicitly set mountpoint. 1176 */ 1177 (void) snprintf(mountpoint, sizeof (mountpoint), 1178 "%s%s", altroot, zhp_mountpoint); 1179 1180 /* Set the new mountpoint for the dataset */ 1181 if (zfs_prop_set(zhp, 1182 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1183 mountpoint)) { 1184 be_print_err(gettext("be_mount_callback: " 1185 "failed to set mountpoint for %s to " 1186 "%s\n"), fs_name, mountpoint); 1187 ZFS_CLOSE(zhp); 1188 return (BE_ERR_ZFS); 1189 } 1190 } else { 1191 be_print_err(gettext("be_mount_callback: " 1192 "mountpoint sourcetype of %s is %d, skipping ...\n"), 1193 fs_name, sourcetype); 1194 1195 goto next; 1196 } 1197 1198 /* Mount this filesystem */ 1199 if (zfs_mount(zhp, NULL, 0) != 0) { 1200 be_print_err(gettext("be_mount_callback: failed to " 1201 "mount dataset %s at %s: %s\n"), fs_name, mountpoint, 1202 libzfs_error_description(g_zfs)); 1203 /* 1204 * Set this filesystem's 'mountpoint' property back to what 1205 * it was 1206 */ 1207 if (sourcetype & ZPROP_SRC_LOCAL && 1208 strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 1209 (void) zfs_prop_set(zhp, 1210 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1211 zhp_mountpoint); 1212 } 1213 1214 ZFS_CLOSE(zhp); 1215 return (BE_ERR_MOUNT); 1216 } 1217 1218 next: 1219 /* Iterate through this dataset's children and mount them */ 1220 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback, 1221 altroot)) != 0) { 1222 ZFS_CLOSE(zhp); 1223 return (ret); 1224 } 1225 1226 1227 ZFS_CLOSE(zhp); 1228 return (0); 1229 } 1230 1231 /* 1232 * Function: be_unmount_callback 1233 * Description: Callback function used to iterate through all of a BE's 1234 * subordinate file systems and to unmount them. 1235 * Parameters: 1236 * zhp - zfs_handle_t pointer to current file system being 1237 * processed. 1238 * data - pointer to the mountpoint of where BE is mounted. 1239 * Returns: 1240 * 0 - Success 1241 * be_errno_t - Failure 1242 * Scope: 1243 * Private 1244 */ 1245 static int 1246 be_unmount_callback(zfs_handle_t *zhp, void *data) 1247 { 1248 be_unmount_data_t *ud = data; 1249 zprop_source_t sourcetype; 1250 const char *fs_name = zfs_get_name(zhp); 1251 char source[ZFS_MAXNAMELEN]; 1252 char mountpoint[MAXPATHLEN]; 1253 char *zhp_mountpoint; 1254 int ret = 0; 1255 1256 /* Iterate down this dataset's children first */ 1257 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) { 1258 ret = BE_ERR_UMOUNT; 1259 goto done; 1260 } 1261 1262 /* Is dataset even mounted ? */ 1263 if (!zfs_is_mounted(zhp, NULL)) 1264 goto done; 1265 1266 /* Unmount this file system */ 1267 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) { 1268 be_print_err(gettext("be_unmount_callback: " 1269 "failed to unmount %s: %s\n"), fs_name, 1270 libzfs_error_description(g_zfs)); 1271 ret = zfs_err_to_be_err(g_zfs); 1272 goto done; 1273 } 1274 1275 /* Get dataset's current mountpoint and source value */ 1276 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 1277 sizeof (mountpoint), &sourcetype, source, sizeof (source), 1278 B_FALSE) != 0) { 1279 be_print_err(gettext("be_unmount_callback: " 1280 "failed to get mountpoint and sourcetype for %s: %s\n"), 1281 fs_name, libzfs_error_description(g_zfs)); 1282 ret = zfs_err_to_be_err(g_zfs); 1283 goto done; 1284 } 1285 1286 if (sourcetype & ZPROP_SRC_INHERITED) { 1287 /* 1288 * If the mountpoint is inherited we don't need to 1289 * do anything. When its parent gets processed 1290 * its mountpoint will be set accordingly. 1291 */ 1292 goto done; 1293 } else if (sourcetype & ZPROP_SRC_LOCAL) { 1294 1295 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1296 /* 1297 * If the mountpoint is set to 'legacy', its already 1298 * been unmounted (from above call to zfs_unmount), and 1299 * we don't need to do anything else with it. 1300 */ 1301 goto done; 1302 1303 } else { 1304 /* 1305 * Else process dataset with explicitly set mountpoint. 1306 */ 1307 1308 /* 1309 * Get this dataset's mountpoint relative to 1310 * the BE's mountpoint. 1311 */ 1312 if ((strncmp(mountpoint, ud->altroot, 1313 strlen(ud->altroot)) == 0) && 1314 (mountpoint[strlen(ud->altroot)] == '/')) { 1315 1316 zhp_mountpoint = mountpoint + 1317 strlen(ud->altroot); 1318 1319 /* Set this dataset's mountpoint value */ 1320 if (zfs_prop_set(zhp, 1321 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1322 zhp_mountpoint)) { 1323 be_print_err( 1324 gettext("be_unmount_callback: " 1325 "failed to set mountpoint for " 1326 "%s to %s: %s\n"), fs_name, 1327 zhp_mountpoint, 1328 libzfs_error_description(g_zfs)); 1329 ret = zfs_err_to_be_err(g_zfs); 1330 } 1331 } else { 1332 be_print_err( 1333 gettext("be_unmount_callback: " 1334 "%s not mounted under BE's altroot %s, " 1335 "skipping ...\n"), fs_name, ud->altroot); 1336 /* 1337 * fs_name is mounted but not under the 1338 * root for this BE. 1339 */ 1340 ret = BE_ERR_INVALMOUNTPOINT; 1341 } 1342 } 1343 } else { 1344 be_print_err(gettext("be_unmount_callback: " 1345 "mountpoint sourcetype of %s is %d, skipping ...\n"), 1346 fs_name, sourcetype); 1347 ret = BE_ERR_ZFS; 1348 } 1349 1350 done: 1351 /* Set this filesystem's 'canmount' property to 'noauto' */ 1352 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) { 1353 be_print_err(gettext("be_unmount_callback: " 1354 "failed to set canmount to 'noauto' (%s)\n"), fs_name); 1355 if (ret == 0) 1356 ret = BE_ERR_ZFS; 1357 } 1358 1359 ZFS_CLOSE(zhp); 1360 return (ret); 1361 } 1362 1363 /* 1364 * Function: be_get_legacy_fs_callback 1365 * Description: The callback function is used to iterate through all 1366 * non-shared file systems of a BE, finding ones that have 1367 * a legacy mountpoint and an entry in the BE's vfstab. 1368 * It adds these file systems to the callback data. 1369 * Parameters: 1370 * zhp - zfs_handle_t pointer to current file system being 1371 * processed. 1372 * data - be_fs_list_data_t pointer 1373 * Returns: 1374 * 0 - Success 1375 * be_errno_t - Failure 1376 * Scope: 1377 * Private 1378 */ 1379 static int 1380 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data) 1381 { 1382 be_fs_list_data_t *fld = data; 1383 const char *fs_name = zfs_get_name(zhp); 1384 char zhp_mountpoint[MAXPATHLEN]; 1385 char mountpoint[MAXPATHLEN]; 1386 int ret = 0; 1387 1388 /* Get this dataset's mountpoint property */ 1389 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint, 1390 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 1391 be_print_err(gettext("be_get_legacy_fs_callback: " 1392 "failed to get mountpoint for %s: %s\n"), 1393 fs_name, libzfs_error_description(g_zfs)); 1394 ret = zfs_err_to_be_err(g_zfs); 1395 ZFS_CLOSE(zhp); 1396 return (ret); 1397 } 1398 1399 /* 1400 * If mountpoint is legacy, try to get its mountpoint from this BE's 1401 * vfstab. If it exists in the vfstab, add this file system to the 1402 * callback data. 1403 */ 1404 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1405 if (get_mountpoint_from_vfstab(fld->altroot, fs_name, 1406 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) { 1407 be_print_err(gettext("be_get_legacy_fs_callback: " 1408 "no entry for %s in vfstab, " 1409 "skipping ...\n"), fs_name); 1410 1411 goto next; 1412 } 1413 1414 /* Record file system into the callback data. */ 1415 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) { 1416 be_print_err(gettext("be_get_legacy_fs_callback: " 1417 "failed to add %s to fs list\n"), mountpoint); 1418 ZFS_CLOSE(zhp); 1419 return (BE_ERR_NOMEM); 1420 } 1421 } 1422 1423 next: 1424 /* Iterate through this dataset's children file systems */ 1425 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback, 1426 fld)) != 0) { 1427 ZFS_CLOSE(zhp); 1428 return (ret); 1429 } 1430 ZFS_CLOSE(zhp); 1431 return (0); 1432 } 1433 1434 /* 1435 * Function: add_to_fs_list 1436 * Description: Function used to add a file system to the fs_list array in 1437 * a be_fs_list_data_t structure. 1438 * Parameters: 1439 * fld - be_fs_list_data_t pointer 1440 * fs - file system to add 1441 * Returns: 1442 * BE_SUCCESS - Success 1443 * 1 - Failure 1444 * Scope: 1445 * Private 1446 */ 1447 static int 1448 add_to_fs_list(be_fs_list_data_t *fld, const char *fs) 1449 { 1450 if (fld == NULL || fs == NULL) 1451 return (1); 1452 1453 if ((fld->fs_list = (char **)realloc(fld->fs_list, 1454 sizeof (char *)*(fld->fs_num + 1))) == NULL) { 1455 be_print_err(gettext("add_to_fs_list: " 1456 "memory allocation failed\n")); 1457 return (1); 1458 } 1459 1460 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) { 1461 be_print_err(gettext("add_to_fs_list: " 1462 "memory allocation failed\n")); 1463 return (1); 1464 } 1465 1466 return (BE_SUCCESS); 1467 } 1468 1469 /* 1470 * Function: zpool_shared_fs_callback 1471 * Description: Callback function used to iterate through all existing pools 1472 * to find and mount all shared filesystems. This function 1473 * processes the pool's "pool data" dataset, then uses 1474 * iter_shared_fs_callback to iterate through the pool's 1475 * datasets. 1476 * Parameters: 1477 * zlp - zpool_handle_t pointer to the current pool being 1478 * looked at. 1479 * data - be_mount_data_t pointer 1480 * Returns: 1481 * 0 - Success 1482 * be_errno_t - Failure 1483 * Scope: 1484 * Private 1485 */ 1486 static int 1487 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data) 1488 { 1489 be_mount_data_t *md = data; 1490 zfs_handle_t *zhp = NULL; 1491 const char *zpool = zpool_get_name(zlp); 1492 int ret = 0; 1493 1494 /* 1495 * Get handle to pool's "pool data" dataset 1496 */ 1497 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) { 1498 be_print_err(gettext("zpool_shared_fs: " 1499 "failed to open pool dataset %s: %s\n"), zpool, 1500 libzfs_error_description(g_zfs)); 1501 ret = zfs_err_to_be_err(g_zfs); 1502 zpool_close(zlp); 1503 return (ret); 1504 } 1505 1506 /* Process this pool's "pool data" dataset */ 1507 (void) loopback_mount_shared_fs(zhp, md); 1508 1509 /* Interate through this pool's children */ 1510 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md); 1511 1512 ZFS_CLOSE(zhp); 1513 zpool_close(zlp); 1514 1515 return (0); 1516 } 1517 1518 /* 1519 * Function: iter_shared_fs_callback 1520 * Description: Callback function used to iterate through a pool's datasets 1521 * to find and mount all shared filesystems. It makes sure to 1522 * find the BE container dataset of the pool, if it exists, and 1523 * does not process and iterate down that path. 1524 * 1525 * Note - This function iterates linearly down the 1526 * hierarchical dataset paths and mounts things as it goes 1527 * along. It does not make sure that something deeper down 1528 * a dataset path has an interim mountpoint for something 1529 * processed earlier. 1530 * 1531 * Parameters: 1532 * zhp - zfs_handle_t pointer to the current dataset being 1533 * processed. 1534 * data - be_mount_data_t pointer 1535 * Returns: 1536 * 0 - Success 1537 * be_errno_t - Failure 1538 * Scope: 1539 * Private 1540 */ 1541 static int 1542 iter_shared_fs_callback(zfs_handle_t *zhp, void *data) 1543 { 1544 be_mount_data_t *md = data; 1545 const char *name = zfs_get_name(zhp); 1546 char container_ds[MAXPATHLEN]; 1547 char tmp_name[MAXPATHLEN]; 1548 char *pool; 1549 1550 /* Get the pool's name */ 1551 (void) strlcpy(tmp_name, name, sizeof (tmp_name)); 1552 pool = strtok(tmp_name, "/"); 1553 1554 if (pool) { 1555 /* Get the name of this pool's container dataset */ 1556 be_make_container_ds(pool, container_ds, 1557 sizeof (container_ds)); 1558 1559 /* 1560 * If what we're processing is this pool's BE container 1561 * dataset, skip it. 1562 */ 1563 if (strcmp(name, container_ds) == 0) { 1564 ZFS_CLOSE(zhp); 1565 return (0); 1566 } 1567 } else { 1568 /* Getting the pool name failed, return error */ 1569 be_print_err(gettext("iter_shared_fs_callback: " 1570 "failed to get pool name from %s\n"), name); 1571 ZFS_CLOSE(zhp); 1572 return (BE_ERR_POOL_NOENT); 1573 } 1574 1575 /* Mount this shared filesystem */ 1576 (void) loopback_mount_shared_fs(zhp, md); 1577 1578 /* Iterate this dataset's children file systems */ 1579 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md); 1580 ZFS_CLOSE(zhp); 1581 1582 return (0); 1583 } 1584 1585 /* 1586 * Function: loopback_mount_shared_fs 1587 * Description: This function loopback mounts a file system into the altroot 1588 * area of the BE being mounted. Since these are shared file 1589 * systems, they are expected to be already mounted for the 1590 * current BE, and this function just loopback mounts them into 1591 * the BE mountpoint. If they are not mounted for the current 1592 * live system, they are skipped and not mounted into the BE 1593 * we're mounting. 1594 * Parameters: 1595 * zhp - zfs_handle_t pointer to the dataset to loopback mount 1596 * md - be_mount_data_t pointer 1597 * Returns: 1598 * BE_SUCCESS - Success 1599 * be_errno_t - Failure 1600 * Scope: 1601 * Private 1602 */ 1603 static int 1604 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md) 1605 { 1606 char zhp_mountpoint[MAXPATHLEN]; 1607 char mountpoint[MAXPATHLEN]; 1608 char *mp = NULL; 1609 char optstr[MAX_MNTOPT_STR]; 1610 int mflag = MS_OPTIONSTR; 1611 int err; 1612 1613 /* 1614 * Check if file system is currently mounted and not delegated 1615 * to a non-global zone (if we're in the global zone) 1616 */ 1617 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID || 1618 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) { 1619 /* 1620 * If we didn't get a mountpoint from the zfs_is_mounted call, 1621 * get it from the mountpoint property. 1622 */ 1623 if (mp == NULL) { 1624 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 1625 zhp_mountpoint, sizeof (zhp_mountpoint), NULL, 1626 NULL, 0, B_FALSE) != 0) { 1627 be_print_err( 1628 gettext("loopback_mount_shared_fs: " 1629 "failed to get mountpoint property\n")); 1630 return (BE_ERR_ZFS); 1631 } 1632 } else { 1633 (void) strlcpy(zhp_mountpoint, mp, 1634 sizeof (zhp_mountpoint)); 1635 free(mp); 1636 } 1637 1638 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s", 1639 md->altroot, zhp_mountpoint); 1640 1641 /* Mount it read-only if read-write was not requested */ 1642 if (!md->shared_rw) { 1643 mflag |= MS_RDONLY; 1644 } 1645 1646 /* Add the "nosub" option to the mount options string */ 1647 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr)); 1648 1649 /* Loopback mount this dataset at the altroot */ 1650 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS, 1651 NULL, 0, optstr, sizeof (optstr)) != 0) { 1652 err = errno; 1653 be_print_err(gettext("loopback_mount_shared_fs: " 1654 "failed to loopback mount %s at %s: %s\n"), 1655 zhp_mountpoint, mountpoint, strerror(err)); 1656 return (BE_ERR_MOUNT); 1657 } 1658 } 1659 1660 return (BE_SUCCESS); 1661 } 1662 1663 /* 1664 * Function: loopback_mount_zonepath 1665 * Description: This function loopback mounts a zonepath into the altroot 1666 * area of the BE being mounted. Since these are shared file 1667 * systems, they are expected to be already mounted for the 1668 * current BE, and this function just loopback mounts them into 1669 * the BE mountpoint. 1670 * Parameters: 1671 * zonepath - pointer to zone path in the current BE 1672 * md - be_mount_data_t pointer 1673 * Returns: 1674 * BE_SUCCESS - Success 1675 * be_errno_t - Failure 1676 * Scope: 1677 * Private 1678 */ 1679 static int 1680 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md) 1681 { 1682 FILE *fp = (FILE *)NULL; 1683 struct stat st; 1684 char *p; 1685 char *p1; 1686 char *parent_dir; 1687 struct extmnttab extmtab; 1688 dev_t dev = NODEV; 1689 char *parentmnt; 1690 char alt_parentmnt[MAXPATHLEN]; 1691 struct mnttab mntref; 1692 char altzonepath[MAXPATHLEN]; 1693 char optstr[MAX_MNTOPT_STR]; 1694 int mflag = MS_OPTIONSTR; 1695 int ret; 1696 int err; 1697 1698 fp = fopen(MNTTAB, "r"); 1699 if (fp == NULL) { 1700 err = errno; 1701 be_print_err(gettext("loopback_mount_zonepath: " 1702 "failed to open /etc/mnttab\n")); 1703 return (errno_to_be_err(err)); 1704 } 1705 1706 /* 1707 * before attempting the loopback mount of zonepath under altroot, 1708 * we need to make sure that all intermediate file systems in the 1709 * zone path are also mounted under altroot 1710 */ 1711 1712 /* get the parent directory for zonepath */ 1713 p = strrchr(zonepath, '/'); 1714 if (p != NULL && p != zonepath) { 1715 if ((parent_dir = (char *)calloc(sizeof (char), 1716 p - zonepath + 1)) == NULL) { 1717 ret = BE_ERR_NOMEM; 1718 goto done; 1719 } 1720 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1); 1721 if (stat(parent_dir, &st) < 0) { 1722 ret = errno_to_be_err(errno); 1723 be_print_err(gettext("loopback_mount_zonepath: " 1724 "failed to stat %s"), 1725 parent_dir); 1726 free(parent_dir); 1727 goto done; 1728 } 1729 free(parent_dir); 1730 1731 /* 1732 * After the above stat call, st.st_dev contains ID of the 1733 * device over which parent dir resides. 1734 * Now, search mnttab and find mount point of parent dir device. 1735 */ 1736 1737 resetmnttab(fp); 1738 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) { 1739 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor); 1740 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype, 1741 MNTTYPE_ZFS) == 0) { 1742 p1 = strchr(extmtab.mnt_special, '/'); 1743 if (p1 == NULL || strncmp(p1 + 1, 1744 BE_CONTAINER_DS_NAME, 4) != 0 || 1745 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) { 1746 /* 1747 * if parent dir is in a shared file 1748 * system, check whether it is already 1749 * loopback mounted under altroot or 1750 * not. It would have been mounted 1751 * already under altroot if it is in 1752 * a non-shared filesystem. 1753 */ 1754 parentmnt = strdup(extmtab.mnt_mountp); 1755 (void) snprintf(alt_parentmnt, 1756 sizeof (alt_parentmnt), "%s%s", 1757 md->altroot, parentmnt); 1758 mntref.mnt_mountp = alt_parentmnt; 1759 mntref.mnt_special = parentmnt; 1760 mntref.mnt_fstype = MNTTYPE_LOFS; 1761 mntref.mnt_mntopts = NULL; 1762 mntref.mnt_time = NULL; 1763 resetmnttab(fp); 1764 if (getmntany(fp, (struct mnttab *) 1765 &extmtab, &mntref) != 0) { 1766 ret = loopback_mount_zonepath( 1767 parentmnt, md); 1768 if (ret != BE_SUCCESS) { 1769 free(parentmnt); 1770 goto done; 1771 } 1772 } 1773 free(parentmnt); 1774 } 1775 break; 1776 } 1777 } 1778 } 1779 1780 1781 if (!md->shared_rw) { 1782 mflag |= MS_RDONLY; 1783 } 1784 1785 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s", 1786 md->altroot, zonepath); 1787 1788 /* Add the "nosub" option to the mount options string */ 1789 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr)); 1790 1791 /* Loopback mount this dataset at the altroot */ 1792 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS, 1793 NULL, 0, optstr, sizeof (optstr)) != 0) { 1794 err = errno; 1795 be_print_err(gettext("loopback_mount_zonepath: " 1796 "failed to loopback mount %s at %s: %s\n"), 1797 zonepath, altzonepath, strerror(err)); 1798 ret = BE_ERR_MOUNT; 1799 goto done; 1800 } 1801 ret = BE_SUCCESS; 1802 1803 done : 1804 (void) fclose(fp); 1805 return (ret); 1806 } 1807 1808 /* 1809 * Function: unmount_shared_fs 1810 * Description: This function iterates through the mnttab and finds all 1811 * loopback mount entries that reside within the altroot of 1812 * where the BE is mounted, and unmounts it. 1813 * Parameters: 1814 * ud - be_unmount_data_t pointer 1815 * Returns: 1816 * BE_SUCCESS - Success 1817 * be_errno_t - Failure 1818 * Scope: 1819 * Private 1820 */ 1821 static int 1822 unmount_shared_fs(be_unmount_data_t *ud) 1823 { 1824 FILE *fp = NULL; 1825 struct mnttab *table = NULL; 1826 struct mnttab ent; 1827 struct mnttab *entp = NULL; 1828 size_t size = 0; 1829 int read_chunk = 32; 1830 int i; 1831 int altroot_len; 1832 int err = 0; 1833 1834 errno = 0; 1835 1836 /* Read in the mnttab into a table */ 1837 if ((fp = fopen(MNTTAB, "r")) == NULL) { 1838 err = errno; 1839 be_print_err(gettext("unmount_shared_fs: " 1840 "failed to open mnttab\n")); 1841 return (errno_to_be_err(err)); 1842 } 1843 1844 while (getmntent(fp, &ent) == 0) { 1845 if (size % read_chunk == 0) { 1846 table = (struct mnttab *)realloc(table, 1847 (size + read_chunk) * sizeof (ent)); 1848 } 1849 entp = &table[size++]; 1850 1851 /* 1852 * Copy over the current mnttab entry into our table, 1853 * copying only the fields that we care about. 1854 */ 1855 (void) memset(entp, 0, sizeof (*entp)); 1856 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL || 1857 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) { 1858 be_print_err(gettext("unmount_shared_fs: " 1859 "memory allocation failed\n")); 1860 return (BE_ERR_NOMEM); 1861 } 1862 } 1863 (void) fclose(fp); 1864 1865 /* 1866 * Process the mnttab entries in reverse order, looking for 1867 * loopback mount entries mounted under our altroot. 1868 */ 1869 altroot_len = strlen(ud->altroot); 1870 for (i = size; i > 0; i--) { 1871 entp = &table[i - 1]; 1872 1873 /* If not of type lofs, skip */ 1874 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0) 1875 continue; 1876 1877 /* If inside the altroot, unmount it */ 1878 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 && 1879 entp->mnt_mountp[altroot_len] == '/') { 1880 if (umount(entp->mnt_mountp) != 0) { 1881 err = errno; 1882 if (err == EBUSY) { 1883 (void) sleep(1); 1884 err = errno = 0; 1885 if (umount(entp->mnt_mountp) != 0) 1886 err = errno; 1887 } 1888 if (err != 0) { 1889 be_print_err(gettext( 1890 "unmount_shared_fs: " 1891 "failed to unmount shared file " 1892 "system %s: %s\n"), 1893 entp->mnt_mountp, strerror(err)); 1894 return (errno_to_be_err(err)); 1895 } 1896 } 1897 } 1898 } 1899 1900 return (BE_SUCCESS); 1901 } 1902 1903 /* 1904 * Function: get_mountpoint_from_vfstab 1905 * Description: This function digs into the vfstab in the given altroot, 1906 * and searches for an entry for the fs passed in. If found, 1907 * it returns the mountpoint of that fs in the mountpoint 1908 * buffer passed in. If the get_alt_mountpoint flag is set, 1909 * it returns the mountpoint with the altroot prepended. 1910 * Parameters: 1911 * altroot - pointer to the alternate root location 1912 * fs - pointer to the file system name to look for in the 1913 * vfstab in altroot 1914 * mountpoint - pointer to buffer of where the mountpoint of 1915 * fs will be returned. 1916 * size_mp - size of mountpoint argument 1917 * get_alt_mountpoint - flag to indicate whether or not the 1918 * mountpoint should be populated with the altroot 1919 * prepended. 1920 * Returns: 1921 * BE_SUCCESS - Success 1922 * 1 - Failure 1923 * Scope: 1924 * Private 1925 */ 1926 static int 1927 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint, 1928 size_t size_mp, boolean_t get_alt_mountpoint) 1929 { 1930 struct vfstab vp; 1931 FILE *fp = NULL; 1932 char alt_vfstab[MAXPATHLEN]; 1933 1934 /* Generate path to alternate root vfstab */ 1935 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab", 1936 altroot); 1937 1938 /* Open alternate root vfstab */ 1939 if ((fp = fopen(alt_vfstab, "r")) == NULL) { 1940 be_print_err(gettext("get_mountpoint_from_vfstab: " 1941 "failed to open vfstab (%s)\n"), alt_vfstab); 1942 return (1); 1943 } 1944 1945 if (getvfsspec(fp, &vp, (char *)fs) == 0) { 1946 /* 1947 * Found entry for fs, grab its mountpoint. 1948 * If the flag to prepend the altroot into the mountpoint 1949 * is set, prepend it. Otherwise, just return the mountpoint. 1950 */ 1951 if (get_alt_mountpoint) { 1952 (void) snprintf(mountpoint, size_mp, "%s%s", altroot, 1953 vp.vfs_mountp); 1954 } else { 1955 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp); 1956 } 1957 } else { 1958 (void) fclose(fp); 1959 return (1); 1960 } 1961 1962 (void) fclose(fp); 1963 1964 return (BE_SUCCESS); 1965 } 1966 1967 /* 1968 * Function: fix_mountpoint_callback 1969 * Description: This callback function is used to iterate through a BE's 1970 * children filesystems to check if its mountpoint is currently 1971 * set to be mounted at some specified altroot. If so, fix it by 1972 * removing altroot from the beginning of its mountpoint. 1973 * 1974 * Note - There's no way to tell if a child filesystem's 1975 * mountpoint isn't broken, and just happens to begin with 1976 * the altroot we're looking for. In this case, this function 1977 * will errantly remove the altroot portion from the beginning 1978 * of this filesystem's mountpoint. 1979 * 1980 * Parameters: 1981 * zhp - zfs_handle_t pointer to filesystem being processed. 1982 * data - altroot of where BE is to be mounted. 1983 * Returns: 1984 * 0 - Success 1985 * be_errno_t - Failure 1986 * Scope: 1987 * Private 1988 */ 1989 static int 1990 fix_mountpoint_callback(zfs_handle_t *zhp, void *data) 1991 { 1992 zprop_source_t sourcetype; 1993 char source[ZFS_MAXNAMELEN]; 1994 char mountpoint[MAXPATHLEN]; 1995 char *zhp_mountpoint = NULL; 1996 char *altroot = data; 1997 int ret = 0; 1998 1999 /* Get dataset's mountpoint and source values */ 2000 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2001 sizeof (mountpoint), &sourcetype, source, sizeof (source), 2002 B_FALSE) != 0) { 2003 be_print_err(gettext("fix_mountpoint_callback: " 2004 "failed to get mountpoint and sourcetype for %s\n"), 2005 zfs_get_name(zhp)); 2006 ZFS_CLOSE(zhp); 2007 return (BE_ERR_ZFS); 2008 } 2009 2010 /* 2011 * If the mountpoint is not inherited and the mountpoint is not 2012 * 'legacy', this file system potentially needs its mountpoint 2013 * fixed. 2014 */ 2015 if (!(sourcetype & ZPROP_SRC_INHERITED) && 2016 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 2017 2018 /* 2019 * Check if this file system's current mountpoint is 2020 * under the altroot we're fixing it against. 2021 */ 2022 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 && 2023 mountpoint[strlen(altroot)] == '/') { 2024 2025 /* 2026 * Get this dataset's mountpoint relative to the 2027 * altroot. 2028 */ 2029 zhp_mountpoint = mountpoint + strlen(altroot); 2030 2031 /* Fix this dataset's mountpoint value */ 2032 if (zfs_prop_set(zhp, 2033 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 2034 zhp_mountpoint)) { 2035 be_print_err(gettext("fix_mountpoint_callback: " 2036 "failed to set mountpoint for %s to " 2037 "%s: %s\n"), zfs_get_name(zhp), 2038 zhp_mountpoint, 2039 libzfs_error_description(g_zfs)); 2040 ret = zfs_err_to_be_err(g_zfs); 2041 ZFS_CLOSE(zhp); 2042 return (ret); 2043 } 2044 } 2045 } 2046 2047 /* Iterate through this dataset's children and fix them */ 2048 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback, 2049 altroot)) != 0) { 2050 ZFS_CLOSE(zhp); 2051 return (ret); 2052 } 2053 2054 2055 ZFS_CLOSE(zhp); 2056 return (0); 2057 } 2058 2059 /* 2060 * Function: be_mount_root 2061 * Description: This function mounts the root dataset of a BE at the 2062 * specified altroot. 2063 * Parameters: 2064 * zhp - zfs_handle_t pointer to root dataset of a BE that is 2065 * to be mounted at altroot. 2066 * altroot - location of where to mount the BE root. 2067 * Return: 2068 * BE_SUCCESS - Success 2069 * be_errno_t - Failure 2070 * Scope: 2071 * Private 2072 */ 2073 static int 2074 be_mount_root(zfs_handle_t *zhp, char *altroot) 2075 { 2076 char mountpoint[MAXPATHLEN]; 2077 2078 /* Get mountpoint property of dataset */ 2079 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2080 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 2081 be_print_err(gettext("be_mount_root: failed to " 2082 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp), 2083 libzfs_error_description(g_zfs)); 2084 return (zfs_err_to_be_err(g_zfs)); 2085 } 2086 2087 /* 2088 * Set the canmount property for the BE's root dataset to 'noauto' just 2089 * in case it's been set to 'on'. We do this so that when we change its 2090 * mountpoint, zfs won't immediately try to mount it. 2091 */ 2092 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") 2093 != 0) { 2094 be_print_err(gettext("be_mount_root: failed to " 2095 "set canmount property to 'noauto' (%s): %s\n"), 2096 zfs_get_name(zhp), libzfs_error_description(g_zfs)); 2097 return (zfs_err_to_be_err(g_zfs)); 2098 } 2099 2100 /* Set mountpoint for BE's root filesystem */ 2101 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), altroot) 2102 != 0) { 2103 be_print_err(gettext("be_mount_root: failed to " 2104 "set mountpoint of %s to %s: %s\n"), 2105 zfs_get_name(zhp), altroot, 2106 libzfs_error_description(g_zfs)); 2107 return (zfs_err_to_be_err(g_zfs)); 2108 } 2109 2110 /* Mount the BE's root filesystem */ 2111 if (zfs_mount(zhp, NULL, 0) != 0) { 2112 be_print_err(gettext("be_mount_root: failed to " 2113 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp), 2114 altroot, libzfs_error_description(g_zfs)); 2115 /* 2116 * Set this BE's root filesystem 'mountpoint' property 2117 * back to what it was before. 2118 */ 2119 (void) zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 2120 mountpoint); 2121 return (zfs_err_to_be_err(g_zfs)); 2122 } 2123 2124 return (BE_SUCCESS); 2125 } 2126 2127 /* 2128 * Function: be_unmount_root 2129 * Description: This function unmounts the root dataset of a BE, but before 2130 * unmounting, it looks at the BE's vfstab to determine 2131 * if the root dataset mountpoint should be left as 'legacy' 2132 * or '/'. If the vfstab contains an entry for this root 2133 * dataset with a mountpoint of '/', it sets the mountpoint 2134 * property to 'legacy'. 2135 * 2136 * Parameters: 2137 * zhp - zfs_handle_t pointer of the BE root dataset that 2138 * is currently mounted. 2139 * ud - be_unmount_data_t pointer providing unmount data 2140 * for the given BE root dataset. 2141 * Returns: 2142 * BE_SUCCESS - Success 2143 * be_errno_t - Failure 2144 * Scope: 2145 * Private 2146 */ 2147 static int 2148 be_unmount_root(zfs_handle_t *zhp, be_unmount_data_t *ud) 2149 { 2150 char mountpoint[MAXPATHLEN]; 2151 boolean_t is_legacy = B_FALSE; 2152 2153 /* See if this is a legacy mounted root */ 2154 if (get_mountpoint_from_vfstab(ud->altroot, zfs_get_name(zhp), 2155 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS && 2156 strcmp(mountpoint, "/") == 0) { 2157 is_legacy = B_TRUE; 2158 } 2159 2160 /* Unmount the dataset */ 2161 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) { 2162 be_print_err(gettext("be_unmount_root: failed to " 2163 "unmount BE root dataset %s: %s\n"), zfs_get_name(zhp), 2164 libzfs_error_description(g_zfs)); 2165 return (zfs_err_to_be_err(g_zfs)); 2166 } 2167 2168 /* Set canmount property for this BE's root filesystem to noauto */ 2169 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") 2170 != 0) { 2171 be_print_err(gettext("be_unmount_root: failed to " 2172 "set canmount property for %s to 'noauto': %s\n"), 2173 zfs_get_name(zhp), libzfs_error_description(g_zfs)); 2174 return (zfs_err_to_be_err(g_zfs)); 2175 } 2176 2177 /* 2178 * Set mountpoint for BE's root dataset back to '/', or 'legacy' 2179 * if its a legacy mounted root. 2180 */ 2181 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 2182 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/") != 0) { 2183 be_print_err(gettext("be_unmount_root: failed to " 2184 "set mountpoint of %s to %s\n"), zfs_get_name(zhp), 2185 is_legacy ? ZFS_MOUNTPOINT_LEGACY : "/"); 2186 return (zfs_err_to_be_err(g_zfs)); 2187 } 2188 2189 return (BE_SUCCESS); 2190 } 2191 2192 /* 2193 * Function: fix_mountpoint 2194 * Description: This function checks the mountpoint of an unmounted BE to make 2195 * sure that it is set to either 'legacy' or '/'. If it's not, 2196 * then we're in a situation where an unmounted BE has some random 2197 * mountpoint set for it. (This could happen if the system was 2198 * rebooted while an inactive BE was mounted). This function 2199 * attempts to fix its mountpoints. 2200 * Parameters: 2201 * zhp - zfs_handle_t pointer to root dataset of the BE 2202 * whose mountpoint needs to be checked. 2203 * Return: 2204 * BE_SUCCESS - Success 2205 * be_errno_t - Failure 2206 * Scope: 2207 * Private 2208 */ 2209 static int 2210 fix_mountpoint(zfs_handle_t *zhp) 2211 { 2212 be_unmount_data_t ud = { 0 }; 2213 char *altroot = NULL; 2214 char mountpoint[MAXPATHLEN]; 2215 int ret = BE_SUCCESS; 2216 2217 /* 2218 * Record what this BE's root dataset mountpoint property is currently 2219 * set to. 2220 */ 2221 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2222 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 2223 be_print_err(gettext("fix_mountpoint: failed to get " 2224 "mountpoint property of (%s): %s\n"), zfs_get_name(zhp), 2225 libzfs_error_description(g_zfs)); 2226 return (BE_ERR_ZFS); 2227 } 2228 2229 /* 2230 * If the root dataset mountpoint is set to 'legacy' or '/', we're okay. 2231 */ 2232 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 || 2233 strcmp(mountpoint, "/") == 0) { 2234 return (BE_SUCCESS); 2235 } 2236 2237 /* 2238 * Iterate through this BE's children datasets and fix 2239 * them if they need fixing. 2240 */ 2241 if (zfs_iter_filesystems(zhp, fix_mountpoint_callback, mountpoint) 2242 != 0) { 2243 return (BE_ERR_ZFS); 2244 } 2245 2246 /* 2247 * The process of mounting and unmounting the root file system 2248 * will fix its mountpoint to correctly be either 'legacy' or '/' 2249 * since be_unmount_root will do the right thing by looking at 2250 * its vfstab. 2251 */ 2252 2253 /* Generate temporary altroot to mount the root file system */ 2254 if ((ret = be_make_tmp_mountpoint(&altroot)) != BE_SUCCESS) { 2255 be_print_err(gettext("fix_mountpoint: failed to " 2256 "make temporary mountpoint\n")); 2257 return (ret); 2258 } 2259 2260 /* Mount and unmount the root. */ 2261 if ((ret = be_mount_root(zhp, altroot)) != BE_SUCCESS) { 2262 be_print_err(gettext("fix_mountpoint: failed to " 2263 "mount BE root file system\n")); 2264 goto cleanup; 2265 } 2266 ud.altroot = altroot; 2267 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) { 2268 be_print_err(gettext("fix_mountpoint: failed to " 2269 "unmount BE root file system\n")); 2270 goto cleanup; 2271 } 2272 2273 cleanup: 2274 free(altroot); 2275 2276 return (ret); 2277 } 2278 2279 /* 2280 * Function: be_mount_zones 2281 * Description: This function finds all supported non-global zones in the 2282 * given global BE and mounts them with respect to where the 2283 * global BE is currently mounted. The global BE datasets 2284 * (including its shared datasets) are expected to already 2285 * be mounted. 2286 * Parameters: 2287 * be_zhp - zfs_handle_t pointer to the root dataset of the 2288 * global BE. 2289 * md - be_mount_data_t pointer to data for global BE. 2290 * Returns: 2291 * BE_SUCCESS - Success 2292 * be_errno_t - Failure 2293 * Scope: 2294 * Private 2295 */ 2296 static int 2297 be_mount_zones(zfs_handle_t *be_zhp, be_mount_data_t *md) 2298 { 2299 zoneBrandList_t *brands = NULL; 2300 zoneList_t zlst = NULL; 2301 char *zonename = NULL; 2302 char *zonepath = NULL; 2303 char *zonepath_ds = NULL; 2304 int k; 2305 int ret = BE_SUCCESS; 2306 2307 z_set_zone_root(md->altroot); 2308 2309 if ((brands = be_get_supported_brandlist()) == NULL) { 2310 be_print_err(gettext("be_mount_zones: " 2311 "no supported brands\n")); 2312 return (BE_SUCCESS); 2313 } 2314 2315 zlst = z_get_nonglobal_zone_list_by_brand(brands); 2316 if (zlst == NULL) { 2317 z_free_brand_list(brands); 2318 return (BE_SUCCESS); 2319 } 2320 2321 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) { 2322 if (z_zlist_get_current_state(zlst, k) == 2323 ZONE_STATE_INSTALLED) { 2324 zonepath = z_zlist_get_zonepath(zlst, k); 2325 2326 /* 2327 * Get the dataset of this zonepath in current BE. 2328 * If its not a dataset, skip it. 2329 */ 2330 if ((zonepath_ds = be_get_ds_from_dir(zonepath)) 2331 == NULL) 2332 continue; 2333 2334 /* 2335 * Check if this zone is supported based on 2336 * the dataset of its zonepath 2337 */ 2338 if (!be_zone_supported(zonepath_ds)) { 2339 free(zonepath_ds); 2340 zonepath_ds = NULL; 2341 continue; 2342 } 2343 2344 /* 2345 * if BE's shared file systems are already mounted, 2346 * zone path dataset would have already been lofs 2347 * mounted under altroot. Otherwise, we need to do 2348 * it here. 2349 */ 2350 if (!md->shared_fs) { 2351 ret = loopback_mount_zonepath(zonepath, md); 2352 if (ret != BE_SUCCESS) 2353 goto done; 2354 } 2355 2356 2357 /* Mount this zone */ 2358 ret = be_mount_one_zone(be_zhp, md, zonename, 2359 zonepath, zonepath_ds); 2360 2361 free(zonepath_ds); 2362 zonepath_ds = NULL; 2363 2364 if (ret != BE_SUCCESS) { 2365 be_print_err(gettext("be_mount_zones: " 2366 "failed to mount zone %s under " 2367 "altroot %s\n"), zonename, md->altroot); 2368 goto done; 2369 } 2370 } 2371 } 2372 2373 done: 2374 z_free_brand_list(brands); 2375 z_free_zone_list(zlst); 2376 /* 2377 * libinstzones caches mnttab and uses cached version for resolving lofs 2378 * mounts when we call z_resolve_lofs. It creates the cached version 2379 * when the first call to z_resolve_lofs happens. So, library's cached 2380 * mnttab doesn't contain entries for lofs mounts created in the above 2381 * loop. Because of this, subsequent calls to z_resolve_lofs would fail 2382 * to resolve these lofs mounts. So, here we destroy library's cached 2383 * mnttab to force its recreation when the next call to z_resolve_lofs 2384 * happens. 2385 */ 2386 z_destroyMountTable(); 2387 return (ret); 2388 } 2389 2390 /* 2391 * Function: be_unmount_zones 2392 * Description: This function finds all supported non-global zones in the 2393 * given mounted global BE and unmounts them. 2394 * Parameters: 2395 * ud - unmount_data_t pointer data for the global BE. 2396 * Returns: 2397 * BE_SUCCESS - Success 2398 * be_errno_t - Failure 2399 * Scope: 2400 * Private 2401 */ 2402 static int 2403 be_unmount_zones(be_unmount_data_t *ud) 2404 { 2405 zoneBrandList_t *brands = NULL; 2406 zoneList_t zlst = NULL; 2407 char *zonename = NULL; 2408 char *zonepath = NULL; 2409 char alt_zonepath[MAXPATHLEN]; 2410 char *zonepath_ds = NULL; 2411 int k; 2412 int ret = BE_SUCCESS; 2413 2414 z_set_zone_root(ud->altroot); 2415 2416 if ((brands = be_get_supported_brandlist()) == NULL) { 2417 be_print_err(gettext("be_unmount_zones: " 2418 "no supported brands\n")); 2419 return (BE_SUCCESS); 2420 } 2421 2422 zlst = z_get_nonglobal_zone_list_by_brand(brands); 2423 if (zlst == NULL) { 2424 z_free_brand_list(brands); 2425 return (BE_SUCCESS); 2426 } 2427 2428 for (k = 0; (zonename = z_zlist_get_zonename(zlst, k)) != NULL; k++) { 2429 if (z_zlist_get_current_state(zlst, k) == 2430 ZONE_STATE_INSTALLED) { 2431 zonepath = z_zlist_get_zonepath(zlst, k); 2432 2433 /* Build zone's zonepath wrt the global BE altroot */ 2434 (void) snprintf(alt_zonepath, sizeof (alt_zonepath), 2435 "%s%s", ud->altroot, zonepath); 2436 2437 /* 2438 * Get the dataset of this zonepath. If its not 2439 * a dataset, skip it. 2440 */ 2441 if ((zonepath_ds = be_get_ds_from_dir(alt_zonepath)) 2442 == NULL) 2443 continue; 2444 2445 /* 2446 * Check if this zone is supported based on the 2447 * dataset of its zonepath. 2448 */ 2449 if (!be_zone_supported(zonepath_ds)) { 2450 free(zonepath_ds); 2451 zonepath_ds = NULL; 2452 continue; 2453 } 2454 2455 /* Unmount this zone */ 2456 ret = be_unmount_one_zone(ud, zonename, zonepath, 2457 zonepath_ds); 2458 2459 free(zonepath_ds); 2460 zonepath_ds = NULL; 2461 2462 if (ret != BE_SUCCESS) { 2463 be_print_err(gettext("be_unmount_zones:" 2464 " failed to unmount zone %s from " 2465 "altroot %s\n"), zonename, ud->altroot); 2466 goto done; 2467 } 2468 } 2469 } 2470 2471 done: 2472 z_free_brand_list(brands); 2473 z_free_zone_list(zlst); 2474 return (ret); 2475 } 2476 2477 /* 2478 * Function: be_mount_one_zone 2479 * Description: This function is called to mount one zone for a given 2480 * global BE. 2481 * Parameters: 2482 * be_zhp - zfs_handle_t pointer to the root dataset of the 2483 * global BE 2484 * md - be_mount_data_t pointer to data for global BE 2485 * zonename - name of zone to mount 2486 * zonepath - zonepath of zone to mount 2487 * zonepath_ds - dataset for the zonepath 2488 * Returns: 2489 * BE_SUCCESS - Success 2490 * be_errno_t - Failure 2491 * Scope: 2492 * Private 2493 */ 2494 static int 2495 be_mount_one_zone(zfs_handle_t *be_zhp, be_mount_data_t *md, char *zonename, 2496 char *zonepath, char *zonepath_ds) 2497 { 2498 be_mount_data_t zone_md = { 0 }; 2499 zfs_handle_t *zone_zhp = NULL; 2500 char zone_altroot[MAXPATHLEN]; 2501 char zoneroot[MAXPATHLEN]; 2502 char zoneroot_ds[MAXPATHLEN]; 2503 int ret = BE_SUCCESS; 2504 2505 /* Find the active zone root dataset for this zone for this BE */ 2506 if ((ret = be_find_active_zone_root(be_zhp, zonepath_ds, zoneroot_ds, 2507 sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) { 2508 be_print_err(gettext("be_mount_one_zone: did not " 2509 "find active zone root for zone %s, skipping ...\n"), 2510 zonename); 2511 return (BE_SUCCESS); 2512 } else if (ret != BE_SUCCESS) { 2513 be_print_err(gettext("be_mount_one_zone: failed to " 2514 "find active zone root for zone %s\n"), zonename); 2515 return (ret); 2516 } 2517 2518 /* Get handle to active zoneroot dataset */ 2519 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM)) 2520 == NULL) { 2521 be_print_err(gettext("be_mount_one_zone: failed to " 2522 "open zone root dataset (%s): %s\n"), zoneroot_ds, 2523 libzfs_error_description(g_zfs)); 2524 return (zfs_err_to_be_err(g_zfs)); 2525 } 2526 2527 /* Generate string for zone's altroot path */ 2528 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot)); 2529 (void) strlcpy(zone_altroot, md->altroot, sizeof (zone_altroot)); 2530 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot)); 2531 2532 /* Build mount_data for the zone */ 2533 zone_md.altroot = zone_altroot; 2534 zone_md.shared_fs = md->shared_fs; 2535 zone_md.shared_rw = md->shared_rw; 2536 2537 /* Mount the zone's root file system */ 2538 if ((ret = be_mount_zone_root(zone_zhp, &zone_md)) != BE_SUCCESS) { 2539 be_print_err(gettext("be_mount_one_zone: failed to " 2540 "mount zone root file system at %s\n"), zone_altroot); 2541 goto done; 2542 } 2543 2544 /* Iterate through zone's children filesystems */ 2545 if ((ret = zfs_iter_filesystems(zone_zhp, be_mount_callback, 2546 zone_altroot)) != 0) { 2547 be_print_err(gettext("be_mount_one_zone: failed to " 2548 "mount zone subordinate file systems at %s\n"), 2549 zone_altroot); 2550 goto done; 2551 } 2552 2553 /* TODO: Mount all shared file systems for this zone */ 2554 2555 done: 2556 ZFS_CLOSE(zone_zhp); 2557 return (ret); 2558 } 2559 2560 /* 2561 * Function: be_unmount_one_zone 2562 * Description: This function unmount one zone for a give global BE. 2563 * Parameters: 2564 * ud - be_unmount_data_t pointer to data for global BE 2565 * zonename - name of zone to unmount 2566 * zonepath - zonepath of the zone to unmount 2567 * zonepath_ds - dataset for the zonepath 2568 * Returns: 2569 * BE_SUCCESS - Success 2570 * be_errno_t - Failure 2571 * Scope: 2572 * Private 2573 */ 2574 static int 2575 be_unmount_one_zone(be_unmount_data_t *ud, char *zonename, char *zonepath, 2576 char *zonepath_ds) 2577 { 2578 be_unmount_data_t zone_ud = { 0 }; 2579 zfs_handle_t *zone_zhp = NULL; 2580 char zone_altroot[MAXPATHLEN]; 2581 char zoneroot[MAXPATHLEN]; 2582 char zoneroot_ds[MAXPATHLEN]; 2583 int ret = BE_SUCCESS; 2584 2585 /* Generate string for zone's alternate root path */ 2586 be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot)); 2587 (void) strlcpy(zone_altroot, ud->altroot, sizeof (zone_altroot)); 2588 (void) strlcat(zone_altroot, zoneroot, sizeof (zone_altroot)); 2589 2590 /* Build be_unmount_data for zone */ 2591 zone_ud.altroot = zone_altroot; 2592 zone_ud.force = ud->force; 2593 2594 /* Find the mounted zone root dataset for this zone for this BE */ 2595 if ((ret = be_find_mounted_zone_root(zone_altroot, zonepath_ds, 2596 zoneroot_ds, sizeof (zoneroot_ds))) == BE_ERR_NO_MOUNTED_ZONE) { 2597 be_print_err(gettext("be_unmount_one_zone: did not " 2598 "find any zone root mounted for zone %s\n"), zonename); 2599 return (BE_SUCCESS); 2600 } else if (ret != BE_SUCCESS) { 2601 be_print_err(gettext("be_unmount_one_zone: failed to " 2602 "find mounted zone root for zone %s\n"), zonename); 2603 return (ret); 2604 } 2605 2606 /* Get handle to zoneroot dataset mounted for this BE */ 2607 if ((zone_zhp = zfs_open(g_zfs, zoneroot_ds, ZFS_TYPE_FILESYSTEM)) 2608 == NULL) { 2609 be_print_err(gettext("be_unmount_one_zone: failed to " 2610 "open mounted zone root dataset (%s): %s\n"), zoneroot_ds, 2611 libzfs_error_description(g_zfs)); 2612 return (zfs_err_to_be_err(g_zfs)); 2613 } 2614 2615 /* TODO: Unmount all shared file systems for this zone */ 2616 2617 /* Iterate through zone's children filesystems and unmount them */ 2618 if ((ret = zfs_iter_filesystems(zone_zhp, be_unmount_callback, 2619 &zone_ud)) != 0) { 2620 be_print_err(gettext("be_unmount_one_zone: failed to " 2621 "unmount zone subordinate file systems at %s\n"), 2622 zone_altroot); 2623 goto done; 2624 } 2625 2626 /* Unmount the zone's root filesystem */ 2627 if ((ret = be_unmount_zone_root(zone_zhp, &zone_ud)) != BE_SUCCESS) { 2628 be_print_err(gettext("be_unmount_one_zone: failed to " 2629 "unmount zone root file system at %s\n"), zone_altroot); 2630 goto done; 2631 } 2632 2633 done: 2634 ZFS_CLOSE(zone_zhp); 2635 return (ret); 2636 } 2637 2638 /* 2639 * Function: be_get_ds_from_dir_callback 2640 * Description: This is a callback function used to iterate all datasets 2641 * to find the one that is currently mounted at the directory 2642 * being searched for. If matched, the name of the dataset is 2643 * returned in heap storage, so the caller is responsible for 2644 * freeing it. 2645 * Parameters: 2646 * zhp - zfs_handle_t pointer to current dataset being processed. 2647 * data - dir_data_t pointer providing name of directory being 2648 * searched for. 2649 * Returns: 2650 * 1 - This dataset is mounted at directory being searched for. 2651 * 0 - This dataset is not mounted at directory being searched for. 2652 * Scope: 2653 * Private 2654 */ 2655 static int 2656 be_get_ds_from_dir_callback(zfs_handle_t *zhp, void *data) 2657 { 2658 dir_data_t *dd = data; 2659 char *mp = NULL; 2660 int zret = 0; 2661 2662 if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 2663 ZFS_CLOSE(zhp); 2664 return (0); 2665 } 2666 2667 if (zfs_is_mounted(zhp, &mp) && mp != NULL && 2668 strcmp(mp, dd->dir) == 0) { 2669 if ((dd->ds = strdup(zfs_get_name(zhp))) == NULL) { 2670 be_print_err(gettext("be_get_ds_from_dir_callback: " 2671 "memory allocation failed\n")); 2672 ZFS_CLOSE(zhp); 2673 return (0); 2674 } 2675 ZFS_CLOSE(zhp); 2676 return (1); 2677 } 2678 2679 zret = zfs_iter_filesystems(zhp, be_get_ds_from_dir_callback, dd); 2680 2681 ZFS_CLOSE(zhp); 2682 2683 return (zret); 2684 } 2685