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