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