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 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 * Copyright 2015 EveryCity Ltd. 26 * Copyright (c) 2015 by Delphix. All rights reserved. 27 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. 28 */ 29 30 /* 31 * System includes 32 */ 33 #include <assert.h> 34 #include <errno.h> 35 #include <libgen.h> 36 #include <libintl.h> 37 #include <libnvpair.h> 38 #include <libzfs.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sys/mntent.h> 43 #include <sys/mnttab.h> 44 #include <sys/mount.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/vfstab.h> 48 #include <sys/zone.h> 49 #include <sys/mkdev.h> 50 #include <unistd.h> 51 52 #include <libbe.h> 53 #include <libbe_priv.h> 54 55 #define BE_TMP_MNTPNT "/tmp/.be.XXXXXX" 56 57 typedef struct dir_data { 58 char *dir; 59 char *ds; 60 } dir_data_t; 61 62 /* Private function prototypes */ 63 static int be_mount_callback(zfs_handle_t *, void *); 64 static int be_unmount_callback(zfs_handle_t *, void *); 65 static int be_get_legacy_fs_callback(zfs_handle_t *, void *); 66 static int fix_mountpoint(zfs_handle_t *); 67 static int fix_mountpoint_callback(zfs_handle_t *, void *); 68 static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t, 69 boolean_t); 70 static int loopback_mount_shared_fs(zfs_handle_t *, be_mount_data_t *); 71 static int loopback_mount_zonepath(const char *, be_mount_data_t *); 72 static int iter_shared_fs_callback(zfs_handle_t *, void *); 73 static int zpool_shared_fs_callback(zpool_handle_t *, void *); 74 static int unmount_shared_fs(be_unmount_data_t *); 75 static int add_to_fs_list(be_fs_list_data_t *, const char *); 76 static int be_mount_root(zfs_handle_t *, char *); 77 static int be_unmount_root(zfs_handle_t *, be_unmount_data_t *); 78 static int be_mount_zones(zfs_handle_t *, be_mount_data_t *); 79 static int be_unmount_zones(be_unmount_data_t *); 80 static int be_mount_one_zone(zfs_handle_t *, be_mount_data_t *, char *, char *, 81 char *); 82 static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *); 83 static int be_get_ds_from_dir_callback(zfs_handle_t *, void *); 84 static int mount_zfs(zfs_handle_t *, char *); 85 86 87 /* ******************************************************************** */ 88 /* Public Functions */ 89 /* ******************************************************************** */ 90 91 /* 92 * Function: be_mount 93 * Description: Mounts a BE and its subordinate datasets at a given mountpoint. 94 * Parameters: 95 * be_attrs - pointer to nvlist_t of attributes being passed in. 96 * The following attributes are used by this function: 97 * 98 * BE_ATTR_ORIG_BE_NAME *required 99 * BE_ATTR_MOUNTPOINT *required 100 * BE_ATTR_MOUNT_FLAGS *optional 101 * Return: 102 * BE_SUCCESS - Success 103 * be_errno_t - Failure 104 * Scope: 105 * Public 106 */ 107 int 108 be_mount(nvlist_t *be_attrs) 109 { 110 char *be_name = NULL; 111 char *mountpoint = NULL; 112 uint16_t flags = 0; 113 int ret = BE_SUCCESS; 114 115 /* Initialize libzfs handle */ 116 if (!be_zfs_init()) 117 return (BE_ERR_INIT); 118 119 /* Get original BE name */ 120 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name) 121 != 0) { 122 be_print_err(gettext("be_mount: failed to lookup " 123 "BE_ATTR_ORIG_BE_NAME attribute\n")); 124 return (BE_ERR_INVAL); 125 } 126 127 /* Validate original BE name */ 128 if (!be_valid_be_name(be_name)) { 129 be_print_err(gettext("be_mount: invalid BE name %s\n"), 130 be_name); 131 return (BE_ERR_INVAL); 132 } 133 134 /* Get mountpoint */ 135 if (nvlist_lookup_string(be_attrs, BE_ATTR_MOUNTPOINT, &mountpoint) 136 != 0) { 137 be_print_err(gettext("be_mount: failed to lookup " 138 "BE_ATTR_MOUNTPOINT attribute\n")); 139 return (BE_ERR_INVAL); 140 } 141 142 /* Get flags */ 143 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 144 BE_ATTR_MOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) { 145 be_print_err(gettext("be_mount: failed to lookup " 146 "BE_ATTR_MOUNT_FLAGS attribute\n")); 147 return (BE_ERR_INVAL); 148 } 149 150 ret = _be_mount(be_name, &mountpoint, flags); 151 152 be_zfs_fini(); 153 154 return (ret); 155 } 156 157 /* 158 * Function: be_unmount 159 * Description: Unmounts a BE and its subordinate datasets. 160 * Parameters: 161 * be_attrs - pointer to nvlist_t of attributes being passed in. 162 * The following attributes are used by this function: 163 * 164 * BE_ATTR_ORIG_BE_NAME *required 165 * BE_ATTR_UNMOUNT_FLAGS *optional 166 * Return: 167 * BE_SUCCESS - Success 168 * be_errno_t - Failure 169 * Scope: 170 * Public 171 */ 172 int 173 be_unmount(nvlist_t *be_attrs) 174 { 175 char *be_name = NULL; 176 char *be_name_mnt = NULL; 177 char *ds = NULL; 178 uint16_t flags = 0; 179 int ret = BE_SUCCESS; 180 181 /* Initialize libzfs handle */ 182 if (!be_zfs_init()) 183 return (BE_ERR_INIT); 184 185 /* Get original BE name */ 186 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name) 187 != 0) { 188 be_print_err(gettext("be_unmount: failed to lookup " 189 "BE_ATTR_ORIG_BE_NAME attribute\n")); 190 return (BE_ERR_INVAL); 191 } 192 193 /* Check if we have mountpoint argument instead of BE name */ 194 if (be_name[0] == '/') { 195 if ((ds = be_get_ds_from_dir(be_name)) != NULL) { 196 if ((be_name_mnt = strrchr(ds, '/')) != NULL) { 197 be_name = be_name_mnt + 1; 198 } 199 } else { 200 be_print_err(gettext("be_unmount: no datasets mounted " 201 "at '%s'\n"), be_name); 202 return (BE_ERR_INVAL); 203 } 204 } 205 206 /* Validate original BE name */ 207 if (!be_valid_be_name(be_name)) { 208 be_print_err(gettext("be_unmount: invalid BE name %s\n"), 209 be_name); 210 return (BE_ERR_INVAL); 211 } 212 213 /* Get unmount flags */ 214 if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 215 BE_ATTR_UNMOUNT_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) { 216 be_print_err(gettext("be_unmount: failed to loookup " 217 "BE_ATTR_UNMOUNT_FLAGS attribute\n")); 218 return (BE_ERR_INVAL); 219 } 220 221 ret = _be_unmount(be_name, flags); 222 223 be_zfs_fini(); 224 225 return (ret); 226 } 227 228 /* ******************************************************************** */ 229 /* Semi-Private Functions */ 230 /* ******************************************************************** */ 231 232 /* 233 * Function: _be_mount 234 * Description: Mounts a BE. If the altroot is not provided, this function 235 * will generate a temporary mountpoint to mount the BE at. It 236 * will return this temporary mountpoint to the caller via the 237 * altroot reference pointer passed in. This returned value is 238 * allocated on heap storage and is the repsonsibility of the 239 * caller to free. 240 * Parameters: 241 * be_name - pointer to name of BE to mount. 242 * altroot - reference pointer to altroot of where to mount BE. 243 * flags - flag indicating special handling for mounting the BE 244 * Return: 245 * BE_SUCCESS - Success 246 * be_errno_t - Failure 247 * Scope: 248 * Semi-private (library wide use only) 249 */ 250 int 251 _be_mount(char *be_name, char **altroot, int flags) 252 { 253 be_transaction_data_t bt = { 0 }; 254 be_mount_data_t md = { 0 }; 255 zfs_handle_t *zhp; 256 char obe_root_ds[MAXPATHLEN]; 257 char *mp = NULL; 258 char *tmp_altroot = NULL; 259 int ret = BE_SUCCESS, err = 0; 260 uuid_t uu = { 0 }; 261 boolean_t gen_tmp_altroot = B_FALSE; 262 263 if (be_name == NULL || altroot == NULL) 264 return (BE_ERR_INVAL); 265 266 /* Set be_name as obe_name in bt structure */ 267 bt.obe_name = be_name; 268 269 /* Find which zpool obe_name lives in */ 270 if ((err = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 271 be_print_err(gettext("be_mount: failed to " 272 "find zpool for BE (%s)\n"), bt.obe_name); 273 return (BE_ERR_BE_NOENT); 274 } else if (err < 0) { 275 be_print_err(gettext("be_mount: zpool_iter failed: %s\n"), 276 libzfs_error_description(g_zfs)); 277 return (zfs_err_to_be_err(g_zfs)); 278 } 279 280 /* Generate string for obe_name's root dataset */ 281 if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 282 sizeof (obe_root_ds))) != BE_SUCCESS) { 283 be_print_err(gettext("%s: failed to get BE container dataset " 284 "for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name); 285 return (ret); 286 } 287 bt.obe_root_ds = obe_root_ds; 288 289 /* Get handle to BE's root dataset */ 290 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 291 NULL) { 292 be_print_err(gettext("be_mount: failed to " 293 "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 294 libzfs_error_description(g_zfs)); 295 return (zfs_err_to_be_err(g_zfs)); 296 } 297 298 /* Make sure BE's root dataset isn't already mounted somewhere */ 299 if (zfs_is_mounted(zhp, &mp)) { 300 ZFS_CLOSE(zhp); 301 be_print_err(gettext("be_mount: %s is already mounted " 302 "at %s\n"), bt.obe_name, mp != NULL ? mp : ""); 303 free(mp); 304 return (BE_ERR_MOUNTED); 305 } 306 307 /* 308 * Fix this BE's mountpoint if its root dataset isn't set to 309 * either 'legacy' or '/'. 310 */ 311 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) { 312 be_print_err(gettext("be_mount: mountpoint check " 313 "failed for %s\n"), bt.obe_root_ds); 314 ZFS_CLOSE(zhp); 315 return (ret); 316 } 317 318 /* 319 * If altroot not provided, create a temporary alternate root 320 * to mount on 321 */ 322 if (*altroot == NULL) { 323 if ((ret = be_make_tmp_mountpoint(&tmp_altroot)) 324 != BE_SUCCESS) { 325 be_print_err(gettext("be_mount: failed to " 326 "make temporary mountpoint\n")); 327 ZFS_CLOSE(zhp); 328 return (ret); 329 } 330 gen_tmp_altroot = B_TRUE; 331 } else { 332 tmp_altroot = *altroot; 333 } 334 335 md.altroot = tmp_altroot; 336 md.shared_fs = flags & BE_MOUNT_FLAG_SHARED_FS; 337 md.shared_rw = flags & BE_MOUNT_FLAG_SHARED_RW; 338 339 /* Mount the BE's root file system */ 340 if (getzoneid() == GLOBAL_ZONEID) { 341 if ((ret = be_mount_root(zhp, tmp_altroot)) != BE_SUCCESS) { 342 be_print_err(gettext("be_mount: failed to " 343 "mount BE root file system\n")); 344 if (gen_tmp_altroot) 345 free(tmp_altroot); 346 ZFS_CLOSE(zhp); 347 return (ret); 348 } 349 } else { 350 /* Legacy mount the zone root dataset */ 351 if ((ret = be_mount_zone_root(zhp, &md)) != BE_SUCCESS) { 352 be_print_err(gettext("be_mount: failed to " 353 "mount BE zone root file system\n")); 354 free(md.altroot); 355 ZFS_CLOSE(zhp); 356 return (ret); 357 } 358 } 359 360 /* Iterate through BE's children filesystems */ 361 if ((err = zfs_iter_filesystems(zhp, be_mount_callback, 362 tmp_altroot)) != 0) { 363 be_print_err(gettext("be_mount: failed to " 364 "mount BE (%s) on %s\n"), bt.obe_name, tmp_altroot); 365 if (gen_tmp_altroot) 366 free(tmp_altroot); 367 ZFS_CLOSE(zhp); 368 return (err); 369 } 370 371 /* 372 * Mount shared file systems if mount flag says so. 373 */ 374 if (md.shared_fs) { 375 /* 376 * Mount all ZFS file systems not under the BE's root dataset 377 */ 378 (void) zpool_iter(g_zfs, zpool_shared_fs_callback, &md); 379 380 /* TODO: Mount all non-ZFS file systems - Not supported yet */ 381 } 382 383 /* 384 * If we're in the global zone and the global zone has a valid uuid, 385 * mount all supported non-global zones. 386 */ 387 if (getzoneid() == GLOBAL_ZONEID && 388 !(flags & BE_MOUNT_FLAG_NO_ZONES) && 389 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) { 390 if (be_mount_zones(zhp, &md) != BE_SUCCESS) { 391 ret = BE_ERR_NO_MOUNTED_ZONE; 392 } 393 } 394 395 ZFS_CLOSE(zhp); 396 397 /* 398 * If a NULL altroot was passed in, pass the generated altroot 399 * back to the caller in altroot. 400 */ 401 if (gen_tmp_altroot) { 402 if (ret == BE_SUCCESS || ret == BE_ERR_NO_MOUNTED_ZONE) 403 *altroot = tmp_altroot; 404 else 405 free(tmp_altroot); 406 } 407 408 return (ret); 409 } 410 411 /* 412 * Function: _be_unmount 413 * Description: Unmount a BE. 414 * Parameters: 415 * be_name - pointer to name of BE to unmount. 416 * flags - flags for unmounting the BE. 417 * Returns: 418 * BE_SUCCESS - Success 419 * be_errno_t - Failure 420 * Scope: 421 * Semi-private (library wide use only) 422 */ 423 int 424 _be_unmount(char *be_name, int flags) 425 { 426 be_transaction_data_t bt = { 0 }; 427 be_unmount_data_t ud = { 0 }; 428 zfs_handle_t *zhp; 429 uuid_t uu = { 0 }; 430 char obe_root_ds[MAXPATHLEN]; 431 char mountpoint[MAXPATHLEN]; 432 char *mp = NULL; 433 int ret = BE_SUCCESS; 434 int zret = 0; 435 436 if (be_name == NULL) 437 return (BE_ERR_INVAL); 438 439 /* Set be_name as obe_name in bt structure */ 440 bt.obe_name = be_name; 441 442 /* Find which zpool obe_name lives in */ 443 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 444 be_print_err(gettext("be_unmount: failed to " 445 "find zpool for BE (%s)\n"), bt.obe_name); 446 return (BE_ERR_BE_NOENT); 447 } else if (zret < 0) { 448 be_print_err(gettext("be_unmount: " 449 "zpool_iter failed: %s\n"), 450 libzfs_error_description(g_zfs)); 451 ret = zfs_err_to_be_err(g_zfs); 452 return (ret); 453 } 454 455 /* Generate string for obe_name's root dataset */ 456 if ((ret = be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 457 sizeof (obe_root_ds))) != BE_SUCCESS) { 458 be_print_err(gettext("%s: failed to get BE container dataset " 459 "for %s/%s\n"), __func__, bt.obe_zpool, bt.obe_name); 460 return (ret); 461 } 462 bt.obe_root_ds = obe_root_ds; 463 464 /* Get handle to BE's root dataset */ 465 if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 466 NULL) { 467 be_print_err(gettext("be_unmount: failed to " 468 "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 469 libzfs_error_description(g_zfs)); 470 ret = zfs_err_to_be_err(g_zfs); 471 return (ret); 472 } 473 474 /* Make sure BE's root dataset is mounted somewhere */ 475 if (!zfs_is_mounted(zhp, &mp)) { 476 477 be_print_err(gettext("be_unmount: " 478 "(%s) not mounted\n"), bt.obe_name); 479 480 /* 481 * BE is not mounted, fix this BE's mountpoint if its root 482 * dataset isn't set to either 'legacy' or '/'. 483 */ 484 if ((ret = fix_mountpoint(zhp)) != BE_SUCCESS) { 485 be_print_err(gettext("be_unmount: mountpoint check " 486 "failed for %s\n"), bt.obe_root_ds); 487 ZFS_CLOSE(zhp); 488 return (ret); 489 } 490 491 ZFS_CLOSE(zhp); 492 return (BE_ERR_NOTMOUNTED); 493 } 494 495 /* 496 * If we didn't get a mountpoint from the zfs_is_mounted call, 497 * try and get it from its property. 498 */ 499 if (mp == NULL) { 500 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 501 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 502 be_print_err(gettext("be_unmount: failed to " 503 "get mountpoint of (%s)\n"), bt.obe_name); 504 ZFS_CLOSE(zhp); 505 return (BE_ERR_ZFS); 506 } 507 } else { 508 (void) strlcpy(mountpoint, mp, sizeof (mountpoint)); 509 free(mp); 510 } 511 512 /* If BE mounted as current root, fail */ 513 if (strcmp(mountpoint, "/") == 0) { 514 be_print_err(gettext("be_unmount: " 515 "cannot unmount currently running BE\n")); 516 ZFS_CLOSE(zhp); 517 return (BE_ERR_UMOUNT_CURR_BE); 518 } 519 520 ud.altroot = mountpoint; 521 ud.force = flags & BE_UNMOUNT_FLAG_FORCE; 522 523 /* Unmount all supported non-global zones if we're in the global zone */ 524 if (getzoneid() == GLOBAL_ZONEID && 525 be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) { 526 if ((ret = be_unmount_zones(&ud)) != BE_SUCCESS) { 527 ZFS_CLOSE(zhp); 528 return (ret); 529 } 530 } 531 532 /* TODO: Unmount all non-ZFS file systems - Not supported yet */ 533 534 /* Unmount all ZFS file systems not under the BE root dataset */ 535 if ((ret = unmount_shared_fs(&ud)) != BE_SUCCESS) { 536 be_print_err(gettext("be_unmount: failed to " 537 "unmount shared file systems\n")); 538 ZFS_CLOSE(zhp); 539 return (ret); 540 } 541 542 /* Unmount all children datasets under the BE's root dataset */ 543 if ((zret = zfs_iter_filesystems(zhp, be_unmount_callback, 544 &ud)) != 0) { 545 be_print_err(gettext("be_unmount: failed to " 546 "unmount BE (%s)\n"), bt.obe_name); 547 ZFS_CLOSE(zhp); 548 return (zret); 549 } 550 551 /* Unmount this BE's root filesystem */ 552 if (getzoneid() == GLOBAL_ZONEID) { 553 if ((ret = be_unmount_root(zhp, &ud)) != BE_SUCCESS) { 554 ZFS_CLOSE(zhp); 555 return (ret); 556 } 557 } else { 558 if ((ret = be_unmount_zone_root(zhp, &ud)) != BE_SUCCESS) { 559 ZFS_CLOSE(zhp); 560 return (ret); 561 } 562 } 563 564 ZFS_CLOSE(zhp); 565 566 return (BE_SUCCESS); 567 } 568 569 /* 570 * Function: be_mount_zone_root 571 * Description: Mounts the zone root dataset for a zone. 572 * Parameters: 573 * zfs - zfs_handle_t pointer to zone root dataset 574 * md - be_mount_data_t pointer to data for zone to be mounted 575 * Returns: 576 * BE_SUCCESS - Success 577 * be_errno_t - Failure 578 * Scope: 579 * Semi-private (library wide use only) 580 */ 581 int 582 be_mount_zone_root(zfs_handle_t *zhp, be_mount_data_t *md) 583 { 584 struct stat buf; 585 char mountpoint[MAXPATHLEN]; 586 int err = 0; 587 588 /* Get mountpoint property of dataset */ 589 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 590 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 591 be_print_err(gettext("be_mount_zone_root: failed to " 592 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp), 593 libzfs_error_description(g_zfs)); 594 return (zfs_err_to_be_err(g_zfs)); 595 } 596 597 /* 598 * Make sure zone's root dataset is set to 'legacy'. This is 599 * currently a requirement in this implementation of zones 600 * support. 601 */ 602 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 603 be_print_err(gettext("be_mount_zone_root: " 604 "zone root dataset mountpoint is not 'legacy'\n")); 605 return (BE_ERR_ZONE_ROOT_NOT_LEGACY); 606 } 607 608 /* Create the mountpoint if it doesn't exist */ 609 if (lstat(md->altroot, &buf) != 0) { 610 if (mkdirp(md->altroot, 0755) != 0) { 611 err = errno; 612 be_print_err(gettext("be_mount_zone_root: failed " 613 "to create mountpoint %s\n"), md->altroot); 614 return (errno_to_be_err(err)); 615 } 616 } 617 618 /* 619 * Legacy mount the zone root dataset. 620 * 621 * As a workaround for 6176743, we mount the zone's root with the 622 * MS_OVERLAY option in case an alternate BE is mounted, and we're 623 * mounting the root for the zone from the current BE here. When an 624 * alternate BE is mounted, it ties up the zone's zoneroot directory 625 * for the current BE since the zone's zonepath is loopback mounted 626 * from the current BE. 627 * 628 * TODO: The MS_OVERLAY option needs to be removed when 6176743 629 * is fixed. 630 */ 631 if (mount(zfs_get_name(zhp), md->altroot, MS_OVERLAY, MNTTYPE_ZFS, 632 NULL, 0, NULL, 0) != 0) { 633 err = errno; 634 be_print_err(gettext("be_mount_zone_root: failed to " 635 "legacy mount zone root dataset (%s) at %s\n"), 636 zfs_get_name(zhp), md->altroot); 637 return (errno_to_be_err(err)); 638 } 639 640 return (BE_SUCCESS); 641 } 642 643 /* 644 * Function: be_unmount_zone_root 645 * Description: Unmounts the zone root dataset for a zone. 646 * Parameters: 647 * zhp - zfs_handle_t pointer to zone root dataset 648 * ud - be_unmount_data_t pointer to data for zone to be unmounted 649 * Returns: 650 * BE_SUCCESS - Success 651 * be_errno_t - Failure 652 * Scope: 653 * Semi-private (library wise use only) 654 */ 655 int 656 be_unmount_zone_root(zfs_handle_t *zhp, be_unmount_data_t *ud) 657 { 658 char mountpoint[MAXPATHLEN]; 659 660 /* Unmount the dataset */ 661 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) { 662 be_print_err(gettext("be_unmount_zone_root: failed to " 663 "unmount zone root dataset %s: %s\n"), zfs_get_name(zhp), 664 libzfs_error_description(g_zfs)); 665 return (zfs_err_to_be_err(g_zfs)); 666 } 667 668 /* Get the current mountpoint property for the zone root dataset */ 669 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 670 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 671 be_print_err(gettext("be_unmount_zone_root: failed to " 672 "get mountpoint property for zone root dataset (%s): %s\n"), 673 zfs_get_name(zhp), libzfs_error_description(g_zfs)); 674 return (zfs_err_to_be_err(g_zfs)); 675 } 676 677 /* If mountpoint not already set to 'legacy', set it to 'legacy' */ 678 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 679 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 680 ZFS_MOUNTPOINT_LEGACY) != 0) { 681 be_print_err(gettext("be_unmount_zone_root: " 682 "failed to set mountpoint of zone root dataset " 683 "%s to 'legacy': %s\n"), zfs_get_name(zhp), 684 libzfs_error_description(g_zfs)); 685 return (zfs_err_to_be_err(g_zfs)); 686 } 687 } 688 689 return (BE_SUCCESS); 690 } 691 692 /* 693 * Function: be_get_legacy_fs 694 * Description: This function iterates through all non-shared file systems 695 * of a BE and finds the ones with a legacy mountpoint. For 696 * those file systems, it reads the BE's vfstab to get the 697 * mountpoint. If found, it adds that file system to the 698 * be_fs_list_data_t passed in. 699 * 700 * This function can be used to gather legacy mounted file systems 701 * for both global BEs and non-global zone BEs. To get data for 702 * a non-global zone BE, the zoneroot_ds and zoneroot parameters 703 * will be specified, otherwise they should be set to NULL. 704 * Parameters: 705 * be_name - global BE name from which to get legacy file 706 * system list. 707 * be_root_ds - root dataset of global BE. 708 * zoneroot_ds - root dataset of zone. 709 * zoneroot - zoneroot path of zone. 710 * fld - be_fs_list_data_t pointer. 711 * Returns: 712 * BE_SUCCESS - Success 713 * be_errno_t - Failure 714 * Scope: 715 * Semi-private (library wide use only) 716 */ 717 int 718 be_get_legacy_fs(char *be_name, char *be_root_ds, char *zoneroot_ds, 719 char *zoneroot, be_fs_list_data_t *fld) 720 { 721 zfs_handle_t *zhp = NULL; 722 char mountpoint[MAXPATHLEN]; 723 boolean_t mounted_here = B_FALSE; 724 boolean_t zone_mounted_here = B_FALSE; 725 int ret = BE_SUCCESS, err = 0; 726 727 if (be_name == NULL || be_root_ds == NULL || fld == NULL) 728 return (BE_ERR_INVAL); 729 730 /* Get handle to BE's root dataset */ 731 if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) 732 == NULL) { 733 be_print_err(gettext("be_get_legacy_fs: failed to " 734 "open BE root dataset (%s): %s\n"), be_root_ds, 735 libzfs_error_description(g_zfs)); 736 ret = zfs_err_to_be_err(g_zfs); 737 return (ret); 738 } 739 740 /* If BE is not already mounted, mount it. */ 741 if (!zfs_is_mounted(zhp, &fld->altroot)) { 742 if ((ret = _be_mount(be_name, &fld->altroot, 743 zoneroot_ds ? BE_MOUNT_FLAG_NULL : 744 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 745 be_print_err(gettext("be_get_legacy_fs: " 746 "failed to mount BE %s\n"), be_name); 747 goto cleanup; 748 } 749 750 mounted_here = B_TRUE; 751 } else if (fld->altroot == NULL) { 752 be_print_err(gettext("be_get_legacy_fs: failed to " 753 "get altroot of mounted BE %s: %s\n"), 754 be_name, libzfs_error_description(g_zfs)); 755 ret = zfs_err_to_be_err(g_zfs); 756 goto cleanup; 757 } 758 759 /* 760 * If a zone root dataset was passed in, we're wanting to get 761 * legacy mounted file systems for that zone, not the global 762 * BE. 763 */ 764 if (zoneroot_ds != NULL) { 765 be_mount_data_t zone_md = { 0 }; 766 767 /* Close off handle to global BE's root dataset */ 768 ZFS_CLOSE(zhp); 769 770 /* Get handle to zone's root dataset */ 771 if ((zhp = zfs_open(g_zfs, zoneroot_ds, 772 ZFS_TYPE_FILESYSTEM)) == NULL) { 773 be_print_err(gettext("be_get_legacy_fs: failed to " 774 "open zone BE root dataset (%s): %s\n"), 775 zoneroot_ds, libzfs_error_description(g_zfs)); 776 ret = zfs_err_to_be_err(g_zfs); 777 goto cleanup; 778 } 779 780 /* Make sure the zone we're looking for is mounted */ 781 if (!zfs_is_mounted(zhp, &zone_md.altroot)) { 782 char zone_altroot[MAXPATHLEN]; 783 784 /* Generate alternate root path for zone */ 785 (void) snprintf(zone_altroot, sizeof (zone_altroot), 786 "%s%s", fld->altroot, zoneroot); 787 if ((zone_md.altroot = strdup(zone_altroot)) == NULL) { 788 be_print_err(gettext("be_get_legacy_fs: " 789 "memory allocation failed\n")); 790 ret = BE_ERR_NOMEM; 791 goto cleanup; 792 } 793 794 if ((ret = be_mount_zone_root(zhp, &zone_md)) 795 != BE_SUCCESS) { 796 be_print_err(gettext("be_get_legacy_fs: " 797 "failed to mount zone root %s\n"), 798 zoneroot_ds); 799 free(zone_md.altroot); 800 zone_md.altroot = NULL; 801 goto cleanup; 802 } 803 zone_mounted_here = B_TRUE; 804 } 805 806 free(fld->altroot); 807 fld->altroot = zone_md.altroot; 808 } 809 810 /* 811 * If the root dataset is in the vfstab with a mountpoint of "/", 812 * add it to the list 813 */ 814 if (get_mountpoint_from_vfstab(fld->altroot, zfs_get_name(zhp), 815 mountpoint, sizeof (mountpoint), B_FALSE) == BE_SUCCESS) { 816 if (strcmp(mountpoint, "/") == 0) { 817 if (add_to_fs_list(fld, zfs_get_name(zhp)) 818 != BE_SUCCESS) { 819 be_print_err(gettext("be_get_legacy_fs: " 820 "failed to add %s to fs list\n"), 821 zfs_get_name(zhp)); 822 ret = BE_ERR_INVAL; 823 goto cleanup; 824 } 825 } 826 } 827 828 /* Iterate subordinate file systems looking for legacy mounts */ 829 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback, 830 fld)) != 0) { 831 be_print_err(gettext("be_get_legacy_fs: " 832 "failed to iterate %s to get legacy mounts\n"), 833 zfs_get_name(zhp)); 834 } 835 836 cleanup: 837 /* If we mounted the zone BE, unmount it */ 838 if (zone_mounted_here) { 839 be_unmount_data_t zone_ud = { 0 }; 840 841 zone_ud.altroot = fld->altroot; 842 zone_ud.force = B_TRUE; 843 if ((err = be_unmount_zone_root(zhp, &zone_ud)) != BE_SUCCESS) { 844 be_print_err(gettext("be_get_legacy_fs: " 845 "failed to unmount zone root %s\n"), 846 zoneroot_ds); 847 if (ret == BE_SUCCESS) 848 ret = err; 849 } 850 } 851 852 /* If we mounted this BE, unmount it */ 853 if (mounted_here) { 854 if ((err = _be_unmount(be_name, 0)) != BE_SUCCESS) { 855 be_print_err(gettext("be_get_legacy_fs: " 856 "failed to unmount %s\n"), be_name); 857 if (ret == BE_SUCCESS) 858 ret = err; 859 } 860 } 861 862 ZFS_CLOSE(zhp); 863 864 free(fld->altroot); 865 fld->altroot = NULL; 866 867 return (ret); 868 } 869 870 /* 871 * Function: be_free_fs_list 872 * Description: Function used to free the members of a be_fs_list_data_t 873 * structure. 874 * Parameters: 875 * fld - be_fs_list_data_t pointer to free. 876 * Returns: 877 * None 878 * Scope: 879 * Semi-private (library wide use only) 880 */ 881 void 882 be_free_fs_list(be_fs_list_data_t *fld) 883 { 884 int i; 885 886 if (fld == NULL) 887 return; 888 889 free(fld->altroot); 890 891 if (fld->fs_list == NULL) 892 return; 893 894 for (i = 0; i < fld->fs_num; i++) 895 free(fld->fs_list[i]); 896 897 free(fld->fs_list); 898 } 899 900 /* 901 * Function: be_get_ds_from_dir(char *dir) 902 * Description: Given a directory path, find the underlying dataset mounted 903 * at that directory path if there is one. The returned name 904 * is allocated in heap storage, so the caller is responsible 905 * for freeing it. 906 * Parameters: 907 * dir - char pointer of directory to find. 908 * Returns: 909 * NULL - if directory is not mounted from a dataset. 910 * name of dataset mounted at dir. 911 * Scope: 912 * Semi-private (library wide use only) 913 */ 914 char * 915 be_get_ds_from_dir(char *dir) 916 { 917 dir_data_t dd = { 0 }; 918 char resolved_dir[MAXPATHLEN]; 919 920 /* Make sure length of dir is within the max length */ 921 if (dir == NULL || strlen(dir) >= MAXPATHLEN) 922 return (NULL); 923 924 /* Resolve dir in case its lofs mounted */ 925 (void) strlcpy(resolved_dir, dir, sizeof (resolved_dir)); 926 z_resolve_lofs(resolved_dir, sizeof (resolved_dir)); 927 928 dd.dir = resolved_dir; 929 930 (void) zfs_iter_root(g_zfs, be_get_ds_from_dir_callback, &dd); 931 932 return (dd.ds); 933 } 934 935 /* 936 * Function: be_make_tmp_mountpoint 937 * Description: This function generates a random temporary mountpoint 938 * and creates that mountpoint directory. It returns the 939 * mountpoint in heap storage, so the caller is responsible 940 * for freeing it. 941 * Parameters: 942 * tmp_mp - reference to pointer of where to store generated 943 * temporary mountpoint. 944 * Returns: 945 * BE_SUCCESS - Success 946 * be_errno_t - Failure 947 * Scope: 948 * Semi-private (library wide use only) 949 */ 950 int 951 be_make_tmp_mountpoint(char **tmp_mp) 952 { 953 int err = 0; 954 955 if ((*tmp_mp = (char *)calloc(1, sizeof (BE_TMP_MNTPNT) + 1)) == NULL) { 956 be_print_err(gettext("be_make_tmp_mountpoint: " 957 "malloc failed\n")); 958 return (BE_ERR_NOMEM); 959 } 960 (void) strlcpy(*tmp_mp, BE_TMP_MNTPNT, sizeof (BE_TMP_MNTPNT) + 1); 961 if (mkdtemp(*tmp_mp) == NULL) { 962 err = errno; 963 be_print_err(gettext("be_make_tmp_mountpoint: mkdtemp() failed " 964 "for %s: %s\n"), *tmp_mp, strerror(err)); 965 free(*tmp_mp); 966 *tmp_mp = NULL; 967 return (errno_to_be_err(err)); 968 } 969 970 return (BE_SUCCESS); 971 } 972 973 /* 974 * Function: be_mount_pool 975 * Description: This function determines if the pool's datase is mounted 976 * and if not it is used to mount the pool's dataset. The 977 * function returns the current mountpoint if we are able 978 * to mount the dataset. 979 * Parameters: 980 * zhp - handle to the pool's dataset 981 * tmp_mntpnt - The temporary mountpoint that the pool's 982 * dataset is mounted on. This is set only 983 * if the attempt to mount the dataset at it's 984 * set mountpoint fails, and we've used a 985 * temporary mount point for this dataset. It 986 * is expected that the caller will free this 987 * memory. 988 * orig_mntpnt - The original mountpoint for the pool. If a 989 * temporary mount point was needed this will 990 * be used to reset the mountpoint property to 991 * it's original mountpoint. It is expected that 992 * the caller will free this memory. 993 * pool_mounted - This flag indicates that the pool was mounted 994 * in this function. 995 * Returns: 996 * BE_SUCCESS - Success 997 * be_errno_t - Failure 998 * Scope: 999 * Semi-private (library wide use only) 1000 */ 1001 int 1002 be_mount_pool( 1003 zfs_handle_t *zhp, 1004 char **tmp_mntpnt, 1005 char **orig_mntpnt, 1006 boolean_t *pool_mounted) 1007 { 1008 1009 char mountpoint[MAXPATHLEN]; 1010 int ret = 0; 1011 1012 *tmp_mntpnt = NULL; 1013 *orig_mntpnt = NULL; 1014 *pool_mounted = B_FALSE; 1015 1016 if (!zfs_is_mounted(zhp, NULL)) { 1017 if (zfs_mount(zhp, NULL, 0) != 0) { 1018 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 1019 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 1020 be_print_err(gettext("be_mount_pool: failed to " 1021 "get mountpoint of (%s): %s\n"), 1022 zfs_get_name(zhp), 1023 libzfs_error_description(g_zfs)); 1024 return (zfs_err_to_be_err(g_zfs)); 1025 } 1026 if ((*orig_mntpnt = strdup(mountpoint)) == NULL) { 1027 be_print_err(gettext("be_mount_pool: memory " 1028 "allocation failed\n")); 1029 return (BE_ERR_NOMEM); 1030 } 1031 /* 1032 * attempt to mount on a temp mountpoint 1033 */ 1034 if ((ret = be_make_tmp_mountpoint(tmp_mntpnt)) 1035 != BE_SUCCESS) { 1036 be_print_err(gettext("be_mount_pool: failed " 1037 "to make temporary mountpoint\n")); 1038 free(*orig_mntpnt); 1039 *orig_mntpnt = NULL; 1040 return (ret); 1041 } 1042 1043 if (zfs_prop_set(zhp, 1044 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1045 *tmp_mntpnt) != 0) { 1046 be_print_err(gettext("be_mount_pool: failed " 1047 "to set mountpoint of pool dataset %s to " 1048 "%s: %s\n"), zfs_get_name(zhp), 1049 *orig_mntpnt, 1050 libzfs_error_description(g_zfs)); 1051 free(*tmp_mntpnt); 1052 free(*orig_mntpnt); 1053 *orig_mntpnt = NULL; 1054 *tmp_mntpnt = NULL; 1055 return (zfs_err_to_be_err(g_zfs)); 1056 } 1057 1058 if (zfs_mount(zhp, NULL, 0) != 0) { 1059 be_print_err(gettext("be_mount_pool: failed " 1060 "to mount dataset %s at %s: %s\n"), 1061 zfs_get_name(zhp), *tmp_mntpnt, 1062 libzfs_error_description(g_zfs)); 1063 ret = zfs_err_to_be_err(g_zfs); 1064 if (zfs_prop_set(zhp, 1065 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1066 mountpoint) != 0) { 1067 be_print_err(gettext("be_mount_pool: " 1068 "failed to set mountpoint of pool " 1069 "dataset %s to %s: %s\n"), 1070 zfs_get_name(zhp), *tmp_mntpnt, 1071 libzfs_error_description(g_zfs)); 1072 } 1073 free(*tmp_mntpnt); 1074 free(*orig_mntpnt); 1075 *orig_mntpnt = NULL; 1076 *tmp_mntpnt = NULL; 1077 return (ret); 1078 } 1079 } 1080 *pool_mounted = B_TRUE; 1081 } 1082 1083 return (BE_SUCCESS); 1084 } 1085 1086 /* 1087 * Function: be_unmount_pool 1088 * Description: This function is used to unmount the pool's dataset if we 1089 * mounted it previously using be_mount_pool(). 1090 * Parameters: 1091 * zhp - handle to the pool's dataset 1092 * tmp_mntpnt - If a temprary mount point was used this will 1093 * be set. Since this was created in be_mount_pool 1094 * we will need to clean it up here. 1095 * orig_mntpnt - The original mountpoint for the pool. This is 1096 * used to set the dataset mountpoint property 1097 * back to it's original value in the case where a 1098 * temporary mountpoint was used. 1099 * Returns: 1100 * BE_SUCCESS - Success 1101 * be_errno_t - Failure 1102 * Scope: 1103 * Semi-private (library wide use only) 1104 */ 1105 int 1106 be_unmount_pool( 1107 zfs_handle_t *zhp, 1108 char *tmp_mntpnt, 1109 char *orig_mntpnt) 1110 { 1111 if (zfs_unmount(zhp, NULL, 0) != 0) { 1112 be_print_err(gettext("be_unmount_pool: failed to " 1113 "unmount pool (%s): %s\n"), zfs_get_name(zhp), 1114 libzfs_error_description(g_zfs)); 1115 return (zfs_err_to_be_err(g_zfs)); 1116 } 1117 if (orig_mntpnt != NULL) { 1118 if (tmp_mntpnt != NULL && 1119 strcmp(orig_mntpnt, tmp_mntpnt) != 0) { 1120 (void) rmdir(tmp_mntpnt); 1121 } 1122 if (zfs_prop_set(zhp, 1123 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1124 orig_mntpnt) != 0) { 1125 be_print_err(gettext("be_unmount_pool: failed " 1126 "to set the mountpoint for dataset (%s) to " 1127 "%s: %s\n"), zfs_get_name(zhp), orig_mntpnt, 1128 libzfs_error_description(g_zfs)); 1129 return (zfs_err_to_be_err(g_zfs)); 1130 } 1131 } 1132 1133 return (BE_SUCCESS); 1134 } 1135 1136 /* ******************************************************************** */ 1137 /* Private Functions */ 1138 /* ******************************************************************** */ 1139 1140 /* 1141 * Function: be_mount_callback 1142 * Description: Callback function used to iterate through all of a BE's 1143 * subordinate file systems and to mount them accordingly. 1144 * Parameters: 1145 * zhp - zfs_handle_t pointer to current file system being 1146 * processed. 1147 * data - pointer to the altroot of where to mount BE. 1148 * Returns: 1149 * 0 - Success 1150 * be_errno_t - Failure 1151 * Scope: 1152 * Private 1153 */ 1154 static int 1155 be_mount_callback(zfs_handle_t *zhp, void *data) 1156 { 1157 zprop_source_t sourcetype; 1158 const char *fs_name = zfs_get_name(zhp); 1159 char source[ZFS_MAX_DATASET_NAME_LEN]; 1160 char *altroot = data; 1161 char zhp_mountpoint[MAXPATHLEN]; 1162 char mountpoint[MAXPATHLEN]; 1163 int ret = 0; 1164 1165 /* Get dataset's mountpoint and source values */ 1166 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint, 1167 sizeof (zhp_mountpoint), &sourcetype, source, sizeof (source), 1168 B_FALSE) != 0) { 1169 be_print_err(gettext("be_mount_callback: failed to " 1170 "get mountpoint and sourcetype for %s\n"), 1171 fs_name); 1172 ZFS_CLOSE(zhp); 1173 return (BE_ERR_ZFS); 1174 } 1175 1176 /* 1177 * Set this filesystem's 'canmount' property to 'noauto' just incase 1178 * it's been set 'on'. 1179 */ 1180 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) { 1181 be_print_err(gettext("be_mount_callback: failed to " 1182 "set canmount to 'noauto' (%s)\n"), fs_name); 1183 ZFS_CLOSE(zhp); 1184 return (BE_ERR_ZFS); 1185 } 1186 1187 /* 1188 * If the mountpoint is none, there's nothing to do, goto next. 1189 * If the mountpoint is legacy, legacy mount it with mount(2). 1190 * If the mountpoint is inherited, its mountpoint should 1191 * already be set. If it's not, then explicitly fix-up 1192 * the mountpoint now by appending its explicitly set 1193 * mountpoint value to the BE mountpoint. 1194 */ 1195 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_NONE) == 0) { 1196 goto next; 1197 } else if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1198 /* 1199 * If the mountpoint is set to 'legacy', we need to 1200 * dig into this BE's vfstab to figure out where to 1201 * mount it, and just mount it via mount(2). 1202 */ 1203 if (get_mountpoint_from_vfstab(altroot, fs_name, 1204 mountpoint, sizeof (mountpoint), B_TRUE) == BE_SUCCESS) { 1205 1206 /* Legacy mount the file system */ 1207 if (mount(fs_name, mountpoint, MS_DATA, 1208 MNTTYPE_ZFS, NULL, 0, NULL, 0) != 0) { 1209 be_print_err( 1210 gettext("be_mount_callback: " 1211 "failed to mount %s on %s\n"), 1212 fs_name, mountpoint); 1213 } 1214 } else { 1215 be_print_err( 1216 gettext("be_mount_callback: " 1217 "no entry for %s in vfstab, " 1218 "skipping ...\n"), fs_name); 1219 } 1220 1221 goto next; 1222 1223 } else if ((sourcetype & (ZPROP_SRC_INHERITED|ZPROP_SRC_LOCAL)) == 0) { 1224 /* 1225 * Skip dataset if mountpoint is not inherited 1226 * or explicitly set. 1227 */ 1228 be_print_err(gettext("be_mount_callback: " 1229 "mountpoint sourcetype of %s is %d, skipping ...\n"), 1230 fs_name, sourcetype); 1231 1232 goto next; 1233 } 1234 1235 /* Mount this filesystem */ 1236 if (mount_zfs(zhp, altroot) != 0) { 1237 be_print_err(gettext("be_mount_callback: failed to " 1238 "mount dataset %s at %s: %s\n"), fs_name, mountpoint, 1239 strerror(errno)); 1240 ZFS_CLOSE(zhp); 1241 return (BE_ERR_MOUNT); 1242 } 1243 1244 next: 1245 /* Iterate through this dataset's children and mount them */ 1246 if ((ret = zfs_iter_filesystems(zhp, be_mount_callback, 1247 altroot)) != 0) { 1248 ZFS_CLOSE(zhp); 1249 return (ret); 1250 } 1251 1252 1253 ZFS_CLOSE(zhp); 1254 return (0); 1255 } 1256 1257 /* 1258 * Function: be_unmount_callback 1259 * Description: Callback function used to iterate through all of a BE's 1260 * subordinate file systems and to unmount them. 1261 * Parameters: 1262 * zhp - zfs_handle_t pointer to current file system being 1263 * processed. 1264 * data - pointer to the mountpoint of where BE is mounted. 1265 * Returns: 1266 * 0 - Success 1267 * be_errno_t - Failure 1268 * Scope: 1269 * Private 1270 */ 1271 static int 1272 be_unmount_callback(zfs_handle_t *zhp, void *data) 1273 { 1274 be_unmount_data_t *ud = data; 1275 zprop_source_t sourcetype; 1276 const char *fs_name = zfs_get_name(zhp); 1277 char source[ZFS_MAX_DATASET_NAME_LEN]; 1278 char mountpoint[MAXPATHLEN]; 1279 char *zhp_mountpoint; 1280 int ret = 0; 1281 1282 /* Iterate down this dataset's children first */ 1283 if (zfs_iter_filesystems(zhp, be_unmount_callback, ud)) { 1284 ret = BE_ERR_UMOUNT; 1285 goto done; 1286 } 1287 1288 /* Is dataset even mounted ? */ 1289 if (!zfs_is_mounted(zhp, NULL)) 1290 goto done; 1291 1292 /* Unmount this file system */ 1293 if (zfs_unmount(zhp, NULL, ud->force ? MS_FORCE : 0) != 0) { 1294 be_print_err(gettext("be_unmount_callback: " 1295 "failed to unmount %s: %s\n"), fs_name, 1296 libzfs_error_description(g_zfs)); 1297 ret = zfs_err_to_be_err(g_zfs); 1298 goto done; 1299 } 1300 1301 /* Get dataset's current mountpoint and source value */ 1302 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 1303 sizeof (mountpoint), &sourcetype, source, sizeof (source), 1304 B_FALSE) != 0) { 1305 be_print_err(gettext("be_unmount_callback: " 1306 "failed to get mountpoint and sourcetype for %s: %s\n"), 1307 fs_name, libzfs_error_description(g_zfs)); 1308 ret = zfs_err_to_be_err(g_zfs); 1309 goto done; 1310 } 1311 1312 if (sourcetype & ZPROP_SRC_INHERITED) { 1313 /* 1314 * If the mountpoint is inherited we don't need to 1315 * do anything. When its parent gets processed 1316 * its mountpoint will be set accordingly. 1317 */ 1318 goto done; 1319 } else if (sourcetype & ZPROP_SRC_LOCAL) { 1320 1321 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1322 /* 1323 * If the mountpoint is set to 'legacy', its already 1324 * been unmounted (from above call to zfs_unmount), and 1325 * we don't need to do anything else with it. 1326 */ 1327 goto done; 1328 1329 } else { 1330 /* 1331 * Else process dataset with explicitly set mountpoint. 1332 */ 1333 1334 /* 1335 * Get this dataset's mountpoint relative to 1336 * the BE's mountpoint. 1337 */ 1338 if ((strncmp(mountpoint, ud->altroot, 1339 strlen(ud->altroot)) == 0) && 1340 (mountpoint[strlen(ud->altroot)] == '/')) { 1341 1342 zhp_mountpoint = mountpoint + 1343 strlen(ud->altroot); 1344 1345 /* Set this dataset's mountpoint value */ 1346 if (zfs_prop_set(zhp, 1347 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1348 zhp_mountpoint)) { 1349 be_print_err( 1350 gettext("be_unmount_callback: " 1351 "failed to set mountpoint for " 1352 "%s to %s: %s\n"), fs_name, 1353 zhp_mountpoint, 1354 libzfs_error_description(g_zfs)); 1355 ret = zfs_err_to_be_err(g_zfs); 1356 } 1357 } else { 1358 /* 1359 * Nothing to do, mountpoint shouldn't be 1360 * corrected. 1361 */ 1362 goto done; 1363 } 1364 } 1365 } else { 1366 be_print_err(gettext("be_unmount_callback: " 1367 "mountpoint sourcetype of %s is %d, skipping ...\n"), 1368 fs_name, sourcetype); 1369 ret = BE_ERR_ZFS; 1370 } 1371 1372 done: 1373 /* Set this filesystem's 'canmount' property to 'noauto' */ 1374 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto")) { 1375 be_print_err(gettext("be_unmount_callback: " 1376 "failed to set canmount to 'noauto' (%s)\n"), fs_name); 1377 if (ret == 0) 1378 ret = BE_ERR_ZFS; 1379 } 1380 1381 ZFS_CLOSE(zhp); 1382 return (ret); 1383 } 1384 1385 /* 1386 * Function: be_get_legacy_fs_callback 1387 * Description: The callback function is used to iterate through all 1388 * non-shared file systems of a BE, finding ones that have 1389 * a legacy mountpoint and an entry in the BE's vfstab. 1390 * It adds these file systems to the callback data. 1391 * Parameters: 1392 * zhp - zfs_handle_t pointer to current file system being 1393 * processed. 1394 * data - be_fs_list_data_t pointer 1395 * Returns: 1396 * 0 - Success 1397 * be_errno_t - Failure 1398 * Scope: 1399 * Private 1400 */ 1401 static int 1402 be_get_legacy_fs_callback(zfs_handle_t *zhp, void *data) 1403 { 1404 be_fs_list_data_t *fld = data; 1405 const char *fs_name = zfs_get_name(zhp); 1406 char zhp_mountpoint[MAXPATHLEN]; 1407 char mountpoint[MAXPATHLEN]; 1408 int ret = 0; 1409 1410 /* Get this dataset's mountpoint property */ 1411 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, zhp_mountpoint, 1412 sizeof (zhp_mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 1413 be_print_err(gettext("be_get_legacy_fs_callback: " 1414 "failed to get mountpoint for %s: %s\n"), 1415 fs_name, libzfs_error_description(g_zfs)); 1416 ret = zfs_err_to_be_err(g_zfs); 1417 ZFS_CLOSE(zhp); 1418 return (ret); 1419 } 1420 1421 /* 1422 * If mountpoint is legacy, try to get its mountpoint from this BE's 1423 * vfstab. If it exists in the vfstab, add this file system to the 1424 * callback data. 1425 */ 1426 if (strcmp(zhp_mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0) { 1427 if (get_mountpoint_from_vfstab(fld->altroot, fs_name, 1428 mountpoint, sizeof (mountpoint), B_FALSE) != BE_SUCCESS) { 1429 be_print_err(gettext("be_get_legacy_fs_callback: " 1430 "no entry for %s in vfstab, " 1431 "skipping ...\n"), fs_name); 1432 1433 goto next; 1434 } 1435 1436 /* Record file system into the callback data. */ 1437 if (add_to_fs_list(fld, zfs_get_name(zhp)) != BE_SUCCESS) { 1438 be_print_err(gettext("be_get_legacy_fs_callback: " 1439 "failed to add %s to fs list\n"), mountpoint); 1440 ZFS_CLOSE(zhp); 1441 return (BE_ERR_NOMEM); 1442 } 1443 } 1444 1445 next: 1446 /* Iterate through this dataset's children file systems */ 1447 if ((ret = zfs_iter_filesystems(zhp, be_get_legacy_fs_callback, 1448 fld)) != 0) { 1449 ZFS_CLOSE(zhp); 1450 return (ret); 1451 } 1452 ZFS_CLOSE(zhp); 1453 return (0); 1454 } 1455 1456 /* 1457 * Function: add_to_fs_list 1458 * Description: Function used to add a file system to the fs_list array in 1459 * a be_fs_list_data_t structure. 1460 * Parameters: 1461 * fld - be_fs_list_data_t pointer 1462 * fs - file system to add 1463 * Returns: 1464 * BE_SUCCESS - Success 1465 * 1 - Failure 1466 * Scope: 1467 * Private 1468 */ 1469 static int 1470 add_to_fs_list(be_fs_list_data_t *fld, const char *fs) 1471 { 1472 if (fld == NULL || fs == NULL) 1473 return (1); 1474 1475 if ((fld->fs_list = (char **)realloc(fld->fs_list, 1476 sizeof (char *)*(fld->fs_num + 1))) == NULL) { 1477 be_print_err(gettext("add_to_fs_list: " 1478 "memory allocation failed\n")); 1479 return (1); 1480 } 1481 1482 if ((fld->fs_list[fld->fs_num++] = strdup(fs)) == NULL) { 1483 be_print_err(gettext("add_to_fs_list: " 1484 "memory allocation failed\n")); 1485 return (1); 1486 } 1487 1488 return (BE_SUCCESS); 1489 } 1490 1491 /* 1492 * Function: zpool_shared_fs_callback 1493 * Description: Callback function used to iterate through all existing pools 1494 * to find and mount all shared filesystems. This function 1495 * processes the pool's "pool data" dataset, then uses 1496 * iter_shared_fs_callback to iterate through the pool's 1497 * datasets. 1498 * Parameters: 1499 * zlp - zpool_handle_t pointer to the current pool being 1500 * looked at. 1501 * data - be_mount_data_t pointer 1502 * Returns: 1503 * 0 - Success 1504 * be_errno_t - Failure 1505 * Scope: 1506 * Private 1507 */ 1508 static int 1509 zpool_shared_fs_callback(zpool_handle_t *zlp, void *data) 1510 { 1511 be_mount_data_t *md = data; 1512 zfs_handle_t *zhp = NULL; 1513 const char *zpool = zpool_get_name(zlp); 1514 int ret = 0; 1515 1516 /* 1517 * Get handle to pool's "pool data" dataset 1518 */ 1519 if ((zhp = zfs_open(g_zfs, zpool, ZFS_TYPE_FILESYSTEM)) == NULL) { 1520 be_print_err(gettext("zpool_shared_fs: " 1521 "failed to open pool dataset %s: %s\n"), zpool, 1522 libzfs_error_description(g_zfs)); 1523 ret = zfs_err_to_be_err(g_zfs); 1524 zpool_close(zlp); 1525 return (ret); 1526 } 1527 1528 /* Process this pool's "pool data" dataset */ 1529 (void) loopback_mount_shared_fs(zhp, md); 1530 1531 /* Interate through this pool's children */ 1532 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md); 1533 1534 ZFS_CLOSE(zhp); 1535 zpool_close(zlp); 1536 1537 return (0); 1538 } 1539 1540 /* 1541 * Function: iter_shared_fs_callback 1542 * Description: Callback function used to iterate through a pool's datasets 1543 * to find and mount all shared filesystems. It makes sure to 1544 * find the BE container dataset of the pool, if it exists, and 1545 * does not process and iterate down that path. 1546 * 1547 * Note - This function iterates linearly down the 1548 * hierarchical dataset paths and mounts things as it goes 1549 * along. It does not make sure that something deeper down 1550 * a dataset path has an interim mountpoint for something 1551 * processed earlier. 1552 * 1553 * Parameters: 1554 * zhp - zfs_handle_t pointer to the current dataset being 1555 * processed. 1556 * data - be_mount_data_t pointer 1557 * Returns: 1558 * 0 - Success 1559 * be_errno_t - Failure 1560 * Scope: 1561 * Private 1562 */ 1563 static int 1564 iter_shared_fs_callback(zfs_handle_t *zhp, void *data) 1565 { 1566 be_mount_data_t *md = data; 1567 const char *name = zfs_get_name(zhp); 1568 char container_ds[MAXPATHLEN]; 1569 char tmp_name[MAXPATHLEN]; 1570 char *pool; 1571 1572 /* Get the pool's name */ 1573 (void) strlcpy(tmp_name, name, sizeof (tmp_name)); 1574 pool = strtok(tmp_name, "/"); 1575 1576 if (pool) { 1577 /* 1578 * If what we're processing is this pool's BE container 1579 * dataset, skip it. 1580 */ 1581 if (be_make_container_ds(pool, container_ds, 1582 sizeof (container_ds)) == BE_SUCCESS && 1583 strcmp(name, container_ds) == 0) { 1584 ZFS_CLOSE(zhp); 1585 return (0); 1586 } 1587 } else { 1588 /* Getting the pool name failed, return error */ 1589 be_print_err(gettext("iter_shared_fs_callback: " 1590 "failed to get pool name from %s\n"), name); 1591 ZFS_CLOSE(zhp); 1592 return (BE_ERR_POOL_NOENT); 1593 } 1594 1595 /* Mount this shared filesystem */ 1596 (void) loopback_mount_shared_fs(zhp, md); 1597 1598 /* Iterate this dataset's children file systems */ 1599 (void) zfs_iter_filesystems(zhp, iter_shared_fs_callback, md); 1600 ZFS_CLOSE(zhp); 1601 1602 return (0); 1603 } 1604 1605 /* 1606 * Function: loopback_mount_shared_fs 1607 * Description: This function loopback mounts a file system into the altroot 1608 * area of the BE being mounted. Since these are shared file 1609 * systems, they are expected to be already mounted for the 1610 * current BE, and this function just loopback mounts them into 1611 * the BE mountpoint. If they are not mounted for the current 1612 * live system, they are skipped and not mounted into the BE 1613 * we're mounting. 1614 * Parameters: 1615 * zhp - zfs_handle_t pointer to the dataset to loopback mount 1616 * md - be_mount_data_t pointer 1617 * Returns: 1618 * BE_SUCCESS - Success 1619 * be_errno_t - Failure 1620 * Scope: 1621 * Private 1622 */ 1623 static int 1624 loopback_mount_shared_fs(zfs_handle_t *zhp, be_mount_data_t *md) 1625 { 1626 char zhp_mountpoint[MAXPATHLEN]; 1627 char mountpoint[MAXPATHLEN]; 1628 char *mp = NULL; 1629 char optstr[MAX_MNTOPT_STR]; 1630 int mflag = MS_OPTIONSTR; 1631 int err; 1632 1633 /* 1634 * Check if file system is currently mounted and not delegated 1635 * to a non-global zone (if we're in the global zone) 1636 */ 1637 if (zfs_is_mounted(zhp, &mp) && (getzoneid() != GLOBAL_ZONEID || 1638 !zfs_prop_get_int(zhp, ZFS_PROP_ZONED))) { 1639 /* 1640 * If we didn't get a mountpoint from the zfs_is_mounted call, 1641 * get it from the mountpoint property. 1642 */ 1643 if (mp == NULL) { 1644 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, 1645 zhp_mountpoint, sizeof (zhp_mountpoint), NULL, 1646 NULL, 0, B_FALSE) != 0) { 1647 be_print_err( 1648 gettext("loopback_mount_shared_fs: " 1649 "failed to get mountpoint property\n")); 1650 return (BE_ERR_ZFS); 1651 } 1652 } else { 1653 (void) strlcpy(zhp_mountpoint, mp, 1654 sizeof (zhp_mountpoint)); 1655 free(mp); 1656 } 1657 1658 (void) snprintf(mountpoint, sizeof (mountpoint), "%s%s", 1659 md->altroot, zhp_mountpoint); 1660 1661 /* Mount it read-only if read-write was not requested */ 1662 if (!md->shared_rw) { 1663 mflag |= MS_RDONLY; 1664 } 1665 1666 /* Add the "nosub" option to the mount options string */ 1667 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr)); 1668 1669 /* Loopback mount this dataset at the altroot */ 1670 if (mount(zhp_mountpoint, mountpoint, mflag, MNTTYPE_LOFS, 1671 NULL, 0, optstr, sizeof (optstr)) != 0) { 1672 err = errno; 1673 be_print_err(gettext("loopback_mount_shared_fs: " 1674 "failed to loopback mount %s at %s: %s\n"), 1675 zhp_mountpoint, mountpoint, strerror(err)); 1676 return (BE_ERR_MOUNT); 1677 } 1678 } 1679 1680 return (BE_SUCCESS); 1681 } 1682 1683 /* 1684 * Function: loopback_mount_zonepath 1685 * Description: This function loopback mounts a zonepath into the altroot 1686 * area of the BE being mounted. 1687 * Parameters: 1688 * zonepath - pointer to zone path in the current BE 1689 * md - be_mount_data_t pointer 1690 * Returns: 1691 * BE_SUCCESS - Success 1692 * be_errno_t - Failure 1693 * Scope: 1694 * Private 1695 */ 1696 static int 1697 loopback_mount_zonepath(const char *zonepath, be_mount_data_t *md) 1698 { 1699 FILE *fp = (FILE *)NULL; 1700 struct stat st; 1701 char *p; 1702 char *p1; 1703 char *parent_dir; 1704 struct extmnttab extmtab; 1705 dev_t dev = NODEV; 1706 char *parentmnt; 1707 char alt_parentmnt[MAXPATHLEN]; 1708 struct mnttab mntref; 1709 char altzonepath[MAXPATHLEN]; 1710 char optstr[MAX_MNTOPT_STR]; 1711 int mflag = MS_OPTIONSTR; 1712 int ret; 1713 int err; 1714 1715 fp = fopen(MNTTAB, "r"); 1716 if (fp == NULL) { 1717 err = errno; 1718 be_print_err(gettext("loopback_mount_zonepath: " 1719 "failed to open /etc/mnttab\n")); 1720 return (errno_to_be_err(err)); 1721 } 1722 1723 /* 1724 * before attempting the loopback mount of zonepath under altroot, 1725 * we need to make sure that all intermediate file systems in the 1726 * zone path are also mounted under altroot 1727 */ 1728 1729 /* get the parent directory for zonepath */ 1730 p = strrchr(zonepath, '/'); 1731 if (p != NULL && p != zonepath) { 1732 if ((parent_dir = (char *)calloc(sizeof (char), 1733 p - zonepath + 1)) == NULL) { 1734 ret = BE_ERR_NOMEM; 1735 goto done; 1736 } 1737 (void) strlcpy(parent_dir, zonepath, p - zonepath + 1); 1738 if (stat(parent_dir, &st) < 0) { 1739 ret = errno_to_be_err(errno); 1740 be_print_err(gettext("loopback_mount_zonepath: " 1741 "failed to stat %s"), 1742 parent_dir); 1743 free(parent_dir); 1744 goto done; 1745 } 1746 free(parent_dir); 1747 1748 /* 1749 * After the above stat call, st.st_dev contains ID of the 1750 * device over which parent dir resides. 1751 * Now, search mnttab and find mount point of parent dir device. 1752 */ 1753 1754 resetmnttab(fp); 1755 while (getextmntent(fp, &extmtab, sizeof (extmtab)) == 0) { 1756 dev = makedev(extmtab.mnt_major, extmtab.mnt_minor); 1757 if (st.st_dev == dev && strcmp(extmtab.mnt_fstype, 1758 MNTTYPE_ZFS) == 0) { 1759 p1 = strchr(extmtab.mnt_special, '/'); 1760 if (p1 == NULL || strncmp(p1 + 1, 1761 BE_CONTAINER_DS_NAME, 4) != 0 || 1762 (*(p1 + 5) != '/' && *(p1 + 5) != '\0')) { 1763 /* 1764 * if parent dir is in a shared file 1765 * system, check whether it is already 1766 * loopback mounted under altroot or 1767 * not. It would have been mounted 1768 * already under altroot if it is in 1769 * a non-shared filesystem. 1770 */ 1771 parentmnt = strdup(extmtab.mnt_mountp); 1772 (void) snprintf(alt_parentmnt, 1773 sizeof (alt_parentmnt), "%s%s", 1774 md->altroot, parentmnt); 1775 mntref.mnt_mountp = alt_parentmnt; 1776 mntref.mnt_special = parentmnt; 1777 mntref.mnt_fstype = MNTTYPE_LOFS; 1778 mntref.mnt_mntopts = NULL; 1779 mntref.mnt_time = NULL; 1780 resetmnttab(fp); 1781 if (getmntany(fp, (struct mnttab *) 1782 &extmtab, &mntref) != 0) { 1783 ret = loopback_mount_zonepath( 1784 parentmnt, md); 1785 if (ret != BE_SUCCESS) { 1786 free(parentmnt); 1787 goto done; 1788 } 1789 } 1790 free(parentmnt); 1791 } 1792 break; 1793 } 1794 } 1795 } 1796 1797 1798 if (!md->shared_rw) { 1799 mflag |= MS_RDONLY; 1800 } 1801 1802 (void) snprintf(altzonepath, sizeof (altzonepath), "%s%s", 1803 md->altroot, zonepath); 1804 1805 /* Add the "nosub" option to the mount options string */ 1806 (void) strlcpy(optstr, MNTOPT_NOSUB, sizeof (optstr)); 1807 1808 /* Loopback mount this dataset at the altroot */ 1809 if (mount(zonepath, altzonepath, mflag, MNTTYPE_LOFS, 1810 NULL, 0, optstr, sizeof (optstr)) != 0) { 1811 err = errno; 1812 be_print_err(gettext("loopback_mount_zonepath: " 1813 "failed to loopback mount %s at %s: %s\n"), 1814 zonepath, altzonepath, strerror(err)); 1815 ret = BE_ERR_MOUNT; 1816 goto done; 1817 } 1818 ret = BE_SUCCESS; 1819 1820 done : 1821 (void) fclose(fp); 1822 return (ret); 1823 } 1824 1825 /* 1826 * Function: unmount_shared_fs 1827 * Description: This function iterates through the mnttab and finds all 1828 * loopback mount entries that reside within the altroot of 1829 * where the BE is mounted, and unmounts it. 1830 * Parameters: 1831 * ud - be_unmount_data_t pointer 1832 * Returns: 1833 * BE_SUCCESS - Success 1834 * be_errno_t - Failure 1835 * Scope: 1836 * Private 1837 */ 1838 static int 1839 unmount_shared_fs(be_unmount_data_t *ud) 1840 { 1841 FILE *fp = NULL; 1842 struct mnttab *table = NULL; 1843 struct mnttab ent; 1844 struct mnttab *entp = NULL; 1845 size_t size = 0; 1846 int read_chunk = 32; 1847 int i; 1848 int altroot_len; 1849 int err = 0; 1850 1851 errno = 0; 1852 1853 /* Read in the mnttab into a table */ 1854 if ((fp = fopen(MNTTAB, "r")) == NULL) { 1855 err = errno; 1856 be_print_err(gettext("unmount_shared_fs: " 1857 "failed to open mnttab\n")); 1858 return (errno_to_be_err(err)); 1859 } 1860 1861 while (getmntent(fp, &ent) == 0) { 1862 if (size % read_chunk == 0) { 1863 table = (struct mnttab *)realloc(table, 1864 (size + read_chunk) * sizeof (ent)); 1865 } 1866 entp = &table[size++]; 1867 1868 /* 1869 * Copy over the current mnttab entry into our table, 1870 * copying only the fields that we care about. 1871 */ 1872 (void) memset(entp, 0, sizeof (*entp)); 1873 if ((entp->mnt_mountp = strdup(ent.mnt_mountp)) == NULL || 1874 (entp->mnt_fstype = strdup(ent.mnt_fstype)) == NULL) { 1875 be_print_err(gettext("unmount_shared_fs: " 1876 "memory allocation failed\n")); 1877 return (BE_ERR_NOMEM); 1878 } 1879 } 1880 (void) fclose(fp); 1881 1882 /* 1883 * Process the mnttab entries in reverse order, looking for 1884 * loopback mount entries mounted under our altroot. 1885 */ 1886 altroot_len = strlen(ud->altroot); 1887 for (i = size; i > 0; i--) { 1888 entp = &table[i - 1]; 1889 1890 /* If not of type lofs, skip */ 1891 if (strcmp(entp->mnt_fstype, MNTTYPE_LOFS) != 0) 1892 continue; 1893 1894 /* If inside the altroot, unmount it */ 1895 if (strncmp(entp->mnt_mountp, ud->altroot, altroot_len) == 0 && 1896 entp->mnt_mountp[altroot_len] == '/') { 1897 if (umount(entp->mnt_mountp) != 0) { 1898 err = errno; 1899 if (err == EBUSY) { 1900 (void) sleep(1); 1901 err = errno = 0; 1902 if (umount(entp->mnt_mountp) != 0) 1903 err = errno; 1904 } 1905 if (err != 0) { 1906 be_print_err(gettext( 1907 "unmount_shared_fs: " 1908 "failed to unmount shared file " 1909 "system %s: %s\n"), 1910 entp->mnt_mountp, strerror(err)); 1911 return (errno_to_be_err(err)); 1912 } 1913 } 1914 } 1915 } 1916 1917 return (BE_SUCCESS); 1918 } 1919 1920 /* 1921 * Function: get_mountpoint_from_vfstab 1922 * Description: This function digs into the vfstab in the given altroot, 1923 * and searches for an entry for the fs passed in. If found, 1924 * it returns the mountpoint of that fs in the mountpoint 1925 * buffer passed in. If the get_alt_mountpoint flag is set, 1926 * it returns the mountpoint with the altroot prepended. 1927 * Parameters: 1928 * altroot - pointer to the alternate root location 1929 * fs - pointer to the file system name to look for in the 1930 * vfstab in altroot 1931 * mountpoint - pointer to buffer of where the mountpoint of 1932 * fs will be returned. 1933 * size_mp - size of mountpoint argument 1934 * get_alt_mountpoint - flag to indicate whether or not the 1935 * mountpoint should be populated with the altroot 1936 * prepended. 1937 * Returns: 1938 * BE_SUCCESS - Success 1939 * 1 - Failure 1940 * Scope: 1941 * Private 1942 */ 1943 static int 1944 get_mountpoint_from_vfstab(char *altroot, const char *fs, char *mountpoint, 1945 size_t size_mp, boolean_t get_alt_mountpoint) 1946 { 1947 struct vfstab vp; 1948 FILE *fp = NULL; 1949 char alt_vfstab[MAXPATHLEN]; 1950 1951 /* Generate path to alternate root vfstab */ 1952 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab", 1953 altroot); 1954 1955 /* Open alternate root vfstab */ 1956 if ((fp = fopen(alt_vfstab, "r")) == NULL) { 1957 be_print_err(gettext("get_mountpoint_from_vfstab: " 1958 "failed to open vfstab (%s)\n"), alt_vfstab); 1959 return (1); 1960 } 1961 1962 if (getvfsspec(fp, &vp, (char *)fs) == 0) { 1963 /* 1964 * Found entry for fs, grab its mountpoint. 1965 * If the flag to prepend the altroot into the mountpoint 1966 * is set, prepend it. Otherwise, just return the mountpoint. 1967 */ 1968 if (get_alt_mountpoint) { 1969 (void) snprintf(mountpoint, size_mp, "%s%s", altroot, 1970 vp.vfs_mountp); 1971 } else { 1972 (void) strlcpy(mountpoint, vp.vfs_mountp, size_mp); 1973 } 1974 } else { 1975 (void) fclose(fp); 1976 return (1); 1977 } 1978 1979 (void) fclose(fp); 1980 1981 return (BE_SUCCESS); 1982 } 1983 1984 /* 1985 * Function: fix_mountpoint_callback 1986 * Description: This callback function is used to iterate through a BE's 1987 * children filesystems to check if its mountpoint is currently 1988 * set to be mounted at some specified altroot. If so, fix it by 1989 * removing altroot from the beginning of its mountpoint. 1990 * 1991 * Note - There's no way to tell if a child filesystem's 1992 * mountpoint isn't broken, and just happens to begin with 1993 * the altroot we're looking for. In this case, this function 1994 * will errantly remove the altroot portion from the beginning 1995 * of this filesystem's mountpoint. 1996 * 1997 * Parameters: 1998 * zhp - zfs_handle_t pointer to filesystem being processed. 1999 * data - altroot of where BE is to be mounted. 2000 * Returns: 2001 * 0 - Success 2002 * be_errno_t - Failure 2003 * Scope: 2004 * Private 2005 */ 2006 static int 2007 fix_mountpoint_callback(zfs_handle_t *zhp, void *data) 2008 { 2009 zprop_source_t sourcetype; 2010 char source[ZFS_MAX_DATASET_NAME_LEN]; 2011 char mountpoint[MAXPATHLEN]; 2012 char *zhp_mountpoint = NULL; 2013 char *altroot = data; 2014 int ret = 0; 2015 2016 /* Get dataset's mountpoint and source values */ 2017 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2018 sizeof (mountpoint), &sourcetype, source, sizeof (source), 2019 B_FALSE) != 0) { 2020 be_print_err(gettext("fix_mountpoint_callback: " 2021 "failed to get mountpoint and sourcetype for %s\n"), 2022 zfs_get_name(zhp)); 2023 ZFS_CLOSE(zhp); 2024 return (BE_ERR_ZFS); 2025 } 2026 2027 /* 2028 * If the mountpoint is not inherited and the mountpoint is not 2029 * 'legacy', this file system potentially needs its mountpoint 2030 * fixed. 2031 */ 2032 if (!(sourcetype & ZPROP_SRC_INHERITED) && 2033 strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0) { 2034 2035 /* 2036 * Check if this file system's current mountpoint is 2037 * under the altroot we're fixing it against. 2038 */ 2039 if (strncmp(mountpoint, altroot, strlen(altroot)) == 0 && 2040 mountpoint[strlen(altroot)] == '/') { 2041 2042 /* 2043 * Get this dataset's mountpoint relative to the 2044 * altroot. 2045 */ 2046 zhp_mountpoint = mountpoint + strlen(altroot); 2047 2048 /* Fix this dataset's mountpoint value */ 2049 if (zfs_prop_set(zhp, 2050 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 2051 zhp_mountpoint)) { 2052 be_print_err(gettext("fix_mountpoint_callback: " 2053 "failed to set mountpoint for %s to " 2054 "%s: %s\n"), zfs_get_name(zhp), 2055 zhp_mountpoint, 2056 libzfs_error_description(g_zfs)); 2057 ret = zfs_err_to_be_err(g_zfs); 2058 ZFS_CLOSE(zhp); 2059 return (ret); 2060 } 2061 } 2062 } 2063 2064 /* Iterate through this dataset's children and fix them */ 2065 if ((ret = zfs_iter_filesystems(zhp, fix_mountpoint_callback, 2066 altroot)) != 0) { 2067 ZFS_CLOSE(zhp); 2068 return (ret); 2069 } 2070 2071 2072 ZFS_CLOSE(zhp); 2073 return (0); 2074 } 2075 2076 /* 2077 * Function: be_mount_root 2078 * Description: This function mounts the root dataset of a BE at the 2079 * specified altroot. 2080 * Parameters: 2081 * zhp - zfs_handle_t pointer to root dataset of a BE that is 2082 * to be mounted at altroot. 2083 * altroot - location of where to mount the BE root. 2084 * Return: 2085 * BE_SUCCESS - Success 2086 * be_errno_t - Failure 2087 * Scope: 2088 * Private 2089 */ 2090 static int 2091 be_mount_root(zfs_handle_t *zhp, char *altroot) 2092 { 2093 char mountpoint[MAXPATHLEN]; 2094 2095 /* Get mountpoint property of dataset */ 2096 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2097 sizeof (mountpoint), NULL, NULL, 0, B_FALSE) != 0) { 2098 be_print_err(gettext("be_mount_root: failed to " 2099 "get mountpoint property for %s: %s\n"), zfs_get_name(zhp), 2100 libzfs_error_description(g_zfs)); 2101 return (zfs_err_to_be_err(g_zfs)); 2102 } 2103 2104 /* 2105 * Set the canmount property for the BE's root dataset to 'noauto' just 2106 * in case it's been set to 'on'. 2107 */ 2108 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") 2109 != 0) { 2110 be_print_err(gettext("be_mount_root: failed to " 2111 "set canmount property to 'noauto' (%s): %s\n"), 2112 zfs_get_name(zhp), libzfs_error_description(g_zfs)); 2113 return (zfs_err_to_be_err(g_zfs)); 2114 } 2115 2116 /* Mount the BE's root filesystem */ 2117 if (mount_zfs(zhp, altroot) != 0) { 2118 be_print_err(gettext("be_mount_root: failed to " 2119 "mount dataset %s at %s: %s\n"), zfs_get_name(zhp), 2120 altroot, strerror(errno)); 2121 return (BE_ERR_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 2686 /* 2687 * Function: mount_zfs 2688 * Description: This is a function to mount zfs filesystem to alternative 2689 * root without changing zfs mountpoint property. Logic is 2690 * similar to zfs_mount. 2691 * Parameters: 2692 * zhp - zfs_handle_t pointer to current dataset being processed. 2693 * altroot - char pointer to current alternative root. 2694 * Returns: 2695 * BE_SUCCESS - Success 2696 * be_errno_t - Failure 2697 * Scope: 2698 * Private 2699 */ 2700 static int 2701 mount_zfs(zfs_handle_t *zhp, char *altroot) 2702 { 2703 int flags = 0; 2704 char mountpoint[MAXPATHLEN]; 2705 char real_mountpoint[MAXPATHLEN]; 2706 char source[MAXNAMELEN]; 2707 char optstr[MAX_MNTOPT_STR]; 2708 zprop_source_t sourcetype; 2709 struct stat buf; 2710 2711 optstr[0] = '\0'; 2712 2713 /* Get dataset's mountpoint and source values */ 2714 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2715 sizeof (mountpoint), &sourcetype, source, sizeof (source), 2716 B_FALSE) != 0) { 2717 be_print_err(gettext("mount_zfs: " 2718 "failed to get mountpoint and sourcetype for %s\n"), 2719 zfs_get_name(zhp)); 2720 ZFS_CLOSE(zhp); 2721 return (BE_ERR_ZFS); 2722 } 2723 2724 if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) == 0 || 2725 strcmp(mountpoint, "/") == 0) { 2726 /* 2727 * We are called only from be_mount_root or be_mount_callback 2728 * when mountpoint != LEGACY 2729 */ 2730 mountpoint[0] = '\0'; 2731 } 2732 2733 (void) snprintf(real_mountpoint, MAXPATHLEN, "%s%s", altroot, 2734 mountpoint); 2735 2736 if (zpool_get_prop_int(zfs_get_pool_handle(zhp), ZPOOL_PROP_READONLY, 2737 NULL)) 2738 flags |= MS_RDONLY; 2739 2740 /* Create the directory if it doesn't already exist */ 2741 if (lstat(real_mountpoint, &buf) != 0) { 2742 if (mkdirp(real_mountpoint, 0755) != 0) { 2743 be_print_err(gettext("mount_zfs: " 2744 "failed to create mountpoint for %s\n"), 2745 zfs_get_name(zhp)); 2746 ZFS_CLOSE(zhp); 2747 return (BE_ERR_ZFS); 2748 } 2749 } 2750 2751 if (mount(zfs_get_name(zhp), real_mountpoint, MS_OPTIONSTR | flags, 2752 MNTTYPE_ZFS, NULL, 0, optstr, sizeof (optstr))) { 2753 be_print_err(gettext("mount_zfs: failed to " 2754 "mount dataset %s at %s\n"), zfs_get_name(zhp), 2755 real_mountpoint); 2756 return (BE_ERR_ZFS); 2757 } 2758 2759 return (BE_SUCCESS); 2760 } 2761