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