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