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