1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 28 * Copyright 2022 OmniOS Community Edition (OmniOSce) Association. 29 */ 30 31 /* 32 * System includes 33 */ 34 #include <assert.h> 35 #include <errno.h> 36 #include <libintl.h> 37 #include <libnvpair.h> 38 #include <libzfs.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <sys/mntent.h> 43 #include <sys/mnttab.h> 44 #include <sys/mount.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/vfstab.h> 48 #include <unistd.h> 49 50 #include <libbe.h> 51 #include <libbe_priv.h> 52 53 typedef struct active_zone_root_data { 54 uuid_t parent_uuid; 55 char *zoneroot_ds; 56 } active_zone_root_data_t; 57 58 typedef struct mounted_zone_root_data { 59 char *zone_altroot; 60 char *zoneroot_ds; 61 } mounted_zone_root_data_t; 62 63 /* Private function prototypes */ 64 static int be_find_active_zone_root_callback(zfs_handle_t *, void *); 65 static int be_find_mounted_zone_root_callback(zfs_handle_t *, void *); 66 static boolean_t be_zone_get_active(zfs_handle_t *); 67 68 69 /* ******************************************************************** */ 70 /* Semi-Private Functions */ 71 /* ******************************************************************** */ 72 73 /* 74 * Function: be_make_zoneroot 75 * Description: Generate a string for a zone's zoneroot given the 76 * zone's zonepath. 77 * Parameters: 78 * zonepath - pointer to zonepath 79 * zoneroot - pointer to buffer to retrn zoneroot in. 80 * zoneroot_size - size of zoneroot 81 * Returns: 82 * None 83 * Scope: 84 * Semi-private (library wise use only) 85 */ 86 void 87 be_make_zoneroot(char *zonepath, char *zoneroot, int zoneroot_size) 88 { 89 (void) snprintf(zoneroot, zoneroot_size, "%s/root", zonepath); 90 } 91 92 /* 93 * Function: be_find_active_zone_root 94 * Description: This function will find the active zone root of a zone for 95 * a given global BE. It will iterate all of the zone roots 96 * under a zonepath, find the zone roots that belong to the 97 * specified global BE, and return the one that is active. 98 * Parameters: 99 * be_zhp - zfs handle to global BE root dataset. 100 * zonepath_ds - pointer to zone's zonepath dataset. 101 * zoneroot_ds - pointer to a buffer to store the dataset name of 102 * the zone's zoneroot that's currently active for this 103 * given global BE.. 104 * zoneroot-ds_size - size of zoneroot_ds. 105 * Returns: 106 * BE_SUCCESS - Success 107 * be_errno_t - Failure 108 * Scope: 109 * Semi-private (library wide use only) 110 */ 111 int 112 be_find_active_zone_root(zfs_handle_t *be_zhp, char *zonepath_ds, 113 char *zoneroot_ds, int zoneroot_ds_size) 114 { 115 active_zone_root_data_t azr_data = { { 0 }, NULL }; 116 zfs_handle_t *zhp; 117 char zone_container_ds[MAXPATHLEN]; 118 int ret = BE_SUCCESS; 119 120 /* Get the uuid of the parent global BE */ 121 if (getzoneid() == GLOBAL_ZONEID) { 122 if ((ret = be_get_uuid(zfs_get_name(be_zhp), 123 &azr_data.parent_uuid)) != BE_SUCCESS) { 124 be_print_err(gettext("be_find_active_zone_root: failed " 125 "to get uuid for BE root dataset %s\n"), 126 zfs_get_name(be_zhp)); 127 return (ret); 128 } 129 } else { 130 if ((ret = be_zone_get_parent_uuid(zfs_get_name(be_zhp), 131 &azr_data.parent_uuid)) != BE_SUCCESS) { 132 be_print_err(gettext("be_find_active_zone_root: failed " 133 "to get parentbe uuid for zone root dataset %s\n"), 134 zfs_get_name(be_zhp)); 135 return (ret); 136 } 137 } 138 139 /* Generate string for the root container dataset for this zone. */ 140 if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds, 141 sizeof (zone_container_ds))) != BE_SUCCESS) { 142 be_print_err(gettext("%s: failed to get BE container dataset " 143 "for %s\n"), __func__, zonepath_ds); 144 return (ret); 145 } 146 147 /* Get handle to this zone's root container dataset */ 148 if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM)) 149 == NULL) { 150 be_print_err(gettext("be_find_active_zone_root: failed to " 151 "open zone root container dataset (%s): %s\n"), 152 zone_container_ds, libzfs_error_description(g_zfs)); 153 return (zfs_err_to_be_err(g_zfs)); 154 } 155 156 /* 157 * Iterate through all of this zone's BEs, looking for ones 158 * that belong to the parent global BE, and finding the one 159 * that is marked active. 160 */ 161 if ((ret = zfs_iter_filesystems(zhp, be_find_active_zone_root_callback, 162 &azr_data)) != 0) { 163 be_print_err(gettext("be_find_active_zone_root: failed to " 164 "find active zone root in zonepath dataset %s: %s\n"), 165 zonepath_ds, be_err_to_str(ret)); 166 goto done; 167 } 168 169 if (azr_data.zoneroot_ds != NULL) { 170 (void) strlcpy(zoneroot_ds, azr_data.zoneroot_ds, 171 zoneroot_ds_size); 172 free(azr_data.zoneroot_ds); 173 } else { 174 be_print_err(gettext("be_find_active_zone_root: failed to " 175 "find active zone root in zonepath dataset %s\n"), 176 zonepath_ds); 177 ret = BE_ERR_ZONE_NO_ACTIVE_ROOT; 178 } 179 180 done: 181 ZFS_CLOSE(zhp); 182 return (ret); 183 } 184 185 /* 186 * Function: be_find_mounted_zone_root 187 * Description: This function will find the dataset mounted as the zoneroot 188 * of a zone for a given mounted global BE. 189 * Parameters: 190 * zone_altroot - path of zoneroot wrt the mounted global BE. 191 * zonepath_ds - dataset of the zone's zonepath. 192 * zoneroot_ds - pointer to a buffer to store the dataset of 193 * the zoneroot that currently mounted for this zone 194 * in the mounted global BE. 195 * zoneroot_ds_size - size of zoneroot_ds 196 * Returns: 197 * BE_SUCCESS - Success 198 * be_errno_t - Failure 199 * Scope: 200 * Semi-private (library wide use only) 201 */ 202 int 203 be_find_mounted_zone_root(char *zone_altroot, char *zonepath_ds, 204 char *zoneroot_ds, int zoneroot_ds_size) 205 { 206 mounted_zone_root_data_t mzr_data = { 0 }; 207 zfs_handle_t *zhp = NULL; 208 char zone_container_ds[MAXPATHLEN]; 209 int ret = BE_SUCCESS; 210 int zret = 0; 211 212 /* Generate string for the root container dataset for this zone. */ 213 if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds, 214 sizeof (zone_container_ds))) != BE_SUCCESS) { 215 be_print_err(gettext("%s: failed to get BE container dataset " 216 "for %s\n"), __func__, zonepath_ds); 217 return (ret); 218 } 219 220 /* Get handle to this zone's root container dataset. */ 221 if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM)) 222 == NULL) { 223 be_print_err(gettext("be_find_mounted_zone_root: failed to " 224 "open zone root container dataset (%s): %s\n"), 225 zone_container_ds, libzfs_error_description(g_zfs)); 226 return (zfs_err_to_be_err(g_zfs)); 227 } 228 229 mzr_data.zone_altroot = zone_altroot; 230 231 /* 232 * Iterate through all of the zone's BEs, looking for the one 233 * that is currently mounted at the zone altroot in the mounted 234 * global BE. 235 */ 236 if ((zret = zfs_iter_filesystems(zhp, 237 be_find_mounted_zone_root_callback, &mzr_data)) == 0) { 238 be_print_err(gettext("be_find_mounted_zone_root: did not " 239 "find mounted zone under altroot zonepath %s\n"), 240 zonepath_ds); 241 ret = BE_ERR_NO_MOUNTED_ZONE; 242 goto done; 243 } else if (zret < 0) { 244 be_print_err(gettext("be_find_mounted_zone_root: " 245 "zfs_iter_filesystems failed: %s\n"), 246 libzfs_error_description(g_zfs)); 247 ret = zfs_err_to_be_err(g_zfs); 248 goto done; 249 } 250 251 if (mzr_data.zoneroot_ds != NULL) { 252 (void) strlcpy(zoneroot_ds, mzr_data.zoneroot_ds, 253 zoneroot_ds_size); 254 free(mzr_data.zoneroot_ds); 255 } 256 257 done: 258 ZFS_CLOSE(zhp); 259 return (ret); 260 } 261 262 /* 263 * Function: be_zone_supported 264 * Description: This function will determine if a zone is supported 265 * based on its zonepath dataset. The zonepath dataset 266 * must: 267 * - not be under any global BE root dataset. 268 * - have a root container dataset underneath it. 269 * 270 * Parameters: 271 * zonepath_ds - name of dataset of the zonepath of the 272 * zone to check. 273 * Returns: 274 * B_TRUE - zone is supported 275 * B_FALSE - zone is not supported 276 * Scope: 277 * Semi-private (library wide use only) 278 */ 279 boolean_t 280 be_zone_supported(char *zonepath_ds) 281 { 282 char zone_container_ds[MAXPATHLEN]; 283 int ret = 0; 284 285 /* 286 * Make sure the dataset for the zonepath is not hierarchically 287 * under any reserved BE root container dataset of any pool. 288 */ 289 if ((ret = zpool_iter(g_zfs, be_check_be_roots_callback, 290 zonepath_ds)) > 0) { 291 be_print_err(gettext("be_zone_supported: " 292 "zonepath dataset %s not supported\n"), zonepath_ds); 293 return (B_FALSE); 294 } else if (ret < 0) { 295 be_print_err(gettext("be_zone_supported: " 296 "zpool_iter failed: %s\n"), 297 libzfs_error_description(g_zfs)); 298 return (B_FALSE); 299 } 300 301 /* 302 * Make sure the zonepath has a zone root container dataset 303 * underneath it. 304 */ 305 if ((ret = be_make_container_ds(zonepath_ds, zone_container_ds, 306 sizeof (zone_container_ds))) != BE_SUCCESS) { 307 be_print_err(gettext("%s: failed to get BE container dataset " 308 "for %s\n"), __func__, zonepath_ds); 309 return (B_FALSE); 310 } 311 312 if (!zfs_dataset_exists(g_zfs, zone_container_ds, 313 ZFS_TYPE_FILESYSTEM)) { 314 be_print_err(gettext("be_zone_supported: " 315 "zonepath dataset (%s) does not have a zone root container " 316 "dataset, zone is not supported, skipping ...\n"), 317 zonepath_ds); 318 return (B_FALSE); 319 } 320 321 return (B_TRUE); 322 } 323 324 /* 325 * Function: be_get_supported_brandlist 326 * Desciption: This functions retuns a list of supported brands in 327 * a zoneBrandList_t object. 328 * Parameters: 329 * None 330 * Returns: 331 * Failure - NULL if no supported brands found. 332 * Success - pointer to zoneBrandList structure. 333 * Scope: 334 * Semi-private (library wide use only) 335 */ 336 zoneBrandList_t * 337 be_get_supported_brandlist(void) 338 { 339 return (z_make_brand_list(BE_ZONE_SUPPORTED_BRANDS, 340 BE_ZONE_SUPPORTED_BRANDS_DELIM)); 341 } 342 343 /* 344 * Function: be_zone_get_parent_uuid 345 * Description: This function gets the parentbe property of a zone root 346 * dataset, parsed it into internal uuid format, and returns 347 * it in the uuid_t reference pointer passed in. 348 * Parameters: 349 * root_ds - dataset name of a zone root dataset 350 * uu - pointer to a uuid_t to return the parentbe uuid in 351 * Returns: 352 * BE_SUCCESS - Success 353 * be_errno_t - Failure 354 * Scope: 355 * Private 356 */ 357 int 358 be_zone_get_parent_uuid(const char *root_ds, uuid_t *uu) 359 { 360 zfs_handle_t *zhp = NULL; 361 nvlist_t *userprops = NULL; 362 nvlist_t *propname = NULL; 363 char *uu_string = NULL; 364 int ret = BE_SUCCESS; 365 366 /* Get handle to zone root dataset */ 367 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { 368 be_print_err(gettext("be_zone_get_parent_uuid: failed to " 369 "open zone root dataset (%s): %s\n"), root_ds, 370 libzfs_error_description(g_zfs)); 371 return (zfs_err_to_be_err(g_zfs)); 372 } 373 374 /* Get user properties for zone root dataset */ 375 if ((userprops = zfs_get_user_props(zhp)) == NULL) { 376 be_print_err(gettext("be_zone_get_parent_uuid: " 377 "failed to get user properties for zone root " 378 "dataset (%s): %s\n"), root_ds, 379 libzfs_error_description(g_zfs)); 380 ret = zfs_err_to_be_err(g_zfs); 381 goto done; 382 } 383 384 /* Get UUID string from zone's root dataset user properties */ 385 if (nvlist_lookup_nvlist(userprops, BE_ZONE_PARENTBE_PROPERTY, 386 &propname) != 0 || nvlist_lookup_string(propname, ZPROP_VALUE, 387 &uu_string) != 0) { 388 be_print_err(gettext("be_zone_get_parent_uuid: failed to " 389 "get parent uuid property from zone root dataset user " 390 "properties.\n")); 391 ret = BE_ERR_ZONE_NO_PARENTBE; 392 goto done; 393 } 394 395 /* Parse the uuid string into internal format */ 396 if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) { 397 be_print_err(gettext("be_zone_get_parent_uuid: failed to " 398 "parse parentuuid\n")); 399 ret = BE_ERR_PARSE_UUID; 400 } 401 402 done: 403 ZFS_CLOSE(zhp); 404 return (ret); 405 } 406 407 /* 408 * Function: be_zone_set_parent_uuid 409 * Description: This function sets parentbe uuid into 410 * a zfs user property for a root zone dataset. 411 * Parameters: 412 * root_ds - Root zone dataset of the BE to set a uuid on. 413 * Return: 414 * be_errno_t - Failure 415 * BE_SUCCESS - Success 416 * Scope: 417 * Semi-private (library wide uses only) 418 */ 419 int 420 be_zone_set_parent_uuid(char *root_ds, uuid_t uu) 421 { 422 zfs_handle_t *zhp = NULL; 423 char uu_string[UUID_PRINTABLE_STRING_LENGTH]; 424 int ret = BE_SUCCESS; 425 426 uuid_unparse(uu, uu_string); 427 428 /* Get handle to the root zone dataset. */ 429 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { 430 be_print_err(gettext("be_zone_set_parent_uuid: failed to " 431 "open root zone dataset (%s): %s\n"), root_ds, 432 libzfs_error_description(g_zfs)); 433 return (zfs_err_to_be_err(g_zfs)); 434 } 435 436 /* Set parentbe uuid property for the root zone dataset */ 437 if (zfs_prop_set(zhp, BE_ZONE_PARENTBE_PROPERTY, uu_string) != 0) { 438 be_print_err(gettext("be_zone_set_parent_uuid: failed to " 439 "set parentbe uuid property for root zone dataset: %s\n"), 440 libzfs_error_description(g_zfs)); 441 ret = zfs_err_to_be_err(g_zfs); 442 } 443 444 ZFS_CLOSE(zhp); 445 return (ret); 446 } 447 448 /* 449 * Function: be_zone_compare_uuids 450 * Description: This function compare the parentbe uuid of the 451 * current running root zone dataset with the parentbe 452 * uuid of the given root zone dataset. 453 * Parameters: 454 * root_ds - Root zone dataset of the BE to compare. 455 * Return: 456 * B_TRUE - root dataset has right parentbe uuid 457 * B_FALSE - root dataset has wrong parentbe uuid 458 * Scope: 459 * Semi-private (library wide uses only) 460 */ 461 boolean_t 462 be_zone_compare_uuids(char *root_ds) 463 { 464 char *active_ds; 465 uuid_t parent_uuid = {0}; 466 uuid_t cur_parent_uuid = {0}; 467 468 /* Get parentbe uuid from given zone root dataset */ 469 if ((be_zone_get_parent_uuid(root_ds, 470 &parent_uuid)) != BE_SUCCESS) { 471 be_print_err(gettext("be_zone_compare_uuids: failed to get " 472 "parentbe uuid from the given BE\n")); 473 return (B_FALSE); 474 } 475 476 /* 477 * Find current running zone root dataset and get it's parentbe 478 * uuid property. 479 */ 480 if ((active_ds = be_get_ds_from_dir("/")) != NULL) { 481 if ((be_zone_get_parent_uuid(active_ds, 482 &cur_parent_uuid)) != BE_SUCCESS) { 483 be_print_err(gettext("be_zone_compare_uuids: failed " 484 "to get parentbe uuid from the current running zone " 485 "root dataset\n")); 486 return (B_FALSE); 487 } 488 } else { 489 be_print_err(gettext("be_zone_compare_uuids: zone root dataset " 490 "is not mounted\n")); 491 return (B_FALSE); 492 } 493 494 if (uuid_compare(parent_uuid, cur_parent_uuid) != 0) { 495 return (B_FALSE); 496 } 497 498 return (B_TRUE); 499 } 500 501 /* ******************************************************************** */ 502 /* Private Functions */ 503 /* ******************************************************************** */ 504 505 /* 506 * Function: be_find_active_zone_root_callback 507 * Description: This function is used as a callback to iterate over all of 508 * a zone's root datasets, finding the one that is marked active 509 * for the parent BE specified in the data passed in. The name 510 * of the zone's active root dataset is returned in heap storage 511 * in the active_zone_root_data_t structure passed in, so the 512 * caller is responsible for freeing it. 513 * Parameters: 514 * zhp - zfs_handle_t pointer to current dataset being processed 515 * data - active_zone_root_data_t pointer 516 * Returns: 517 * 0 - Success 518 * >0 - Failure 519 * Scope: 520 * Private 521 */ 522 static int 523 be_find_active_zone_root_callback(zfs_handle_t *zhp, void *data) 524 { 525 active_zone_root_data_t *azr_data = data; 526 uuid_t parent_uuid = { 0 }; 527 int iret = 0; 528 int ret = 0; 529 530 if ((iret = be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid)) 531 != BE_SUCCESS) { 532 be_print_err(gettext("be_find_active_zone_root_callback: " 533 "skipping zone root dataset (%s): %s\n"), 534 zfs_get_name(zhp), be_err_to_str(iret)); 535 goto done; 536 } 537 538 if (uuid_compare(azr_data->parent_uuid, parent_uuid) == 0) { 539 /* 540 * Found a zone root dataset belonging to the right parent, 541 * check if its active. 542 */ 543 if (be_zone_get_active(zhp)) { 544 /* 545 * Found active zone root dataset, if its already 546 * set in the callback data, that means this 547 * is the second one we've found. Return error. 548 */ 549 if (azr_data->zoneroot_ds != NULL) { 550 ret = BE_ERR_ZONE_MULTIPLE_ACTIVE; 551 goto done; 552 } 553 554 azr_data->zoneroot_ds = strdup(zfs_get_name(zhp)); 555 if (azr_data->zoneroot_ds == NULL) { 556 ret = BE_ERR_NOMEM; 557 } 558 } 559 } 560 561 done: 562 ZFS_CLOSE(zhp); 563 return (ret); 564 } 565 566 /* 567 * Function: be_find_mounted_zone_root_callback 568 * Description: This function is used as a callback to iterate over all of 569 * a zone's root datasets, find the one that is currently 570 * mounted for the parent BE specified in the data passed in. 571 * The name of the zone's mounted root dataset is returned in 572 * heap storage the mounted_zone_data_t structure passed in, 573 * so the caller is responsible for freeing it. 574 * Parameters: 575 * zhp - zfs_handle_t pointer to the current dataset being 576 * processed 577 * data - mounted_zone_data_t pointer 578 * Returns: 579 * 0 - not mounted as zone's root 580 * 1 - this dataset is mounted as zone's root 581 * Scope: 582 * Private 583 */ 584 static int 585 be_find_mounted_zone_root_callback(zfs_handle_t *zhp, void *data) 586 { 587 mounted_zone_root_data_t *mzr_data = data; 588 char *mp = NULL; 589 590 if (zfs_is_mounted(zhp, &mp) && mp != NULL && 591 strcmp(mp, mzr_data->zone_altroot) == 0) { 592 mzr_data->zoneroot_ds = strdup(zfs_get_name(zhp)); 593 free(mp); 594 return (1); 595 } 596 597 free(mp); 598 return (0); 599 } 600 601 /* 602 * Function: be_zone_get_active 603 * Description: This function gets the active property of a zone root 604 * dataset, and returns true if active property is on. 605 * Parameters: 606 * zfs - zfs_handle_t pointer to zone root dataset to check 607 * Returns: 608 * B_TRUE - zone root dataset is active 609 * B_FALSE - zone root dataset is not active 610 * Scope: 611 * Private 612 */ 613 static boolean_t 614 be_zone_get_active(zfs_handle_t *zhp) 615 { 616 nvlist_t *userprops = NULL; 617 nvlist_t *propname = NULL; 618 char *active_str = NULL; 619 620 /* Get user properties for the zone root dataset */ 621 if ((userprops = zfs_get_user_props(zhp)) == NULL) { 622 be_print_err(gettext("be_zone_get_active: " 623 "failed to get user properties for zone root " 624 "dataset (%s): %s\n"), zfs_get_name(zhp), 625 libzfs_error_description(g_zfs)); 626 return (B_FALSE); 627 } 628 629 /* Get active property from the zone root dataset user properties */ 630 if (nvlist_lookup_nvlist(userprops, BE_ZONE_ACTIVE_PROPERTY, &propname) 631 != 0 || nvlist_lookup_string(propname, ZPROP_VALUE, &active_str) 632 != 0) { 633 return (B_FALSE); 634 } 635 636 if (strcmp(active_str, "on") == 0) 637 return (B_TRUE); 638 639 return (B_FALSE); 640 } 641