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 #include <assert.h> 31 #include <libintl.h> 32 #include <libnvpair.h> 33 #include <libzfs.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 #include <errno.h> 38 #include <sys/mnttab.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <unistd.h> 42 43 #include <libbe.h> 44 #include <libbe_priv.h> 45 46 char *mnttab = MNTTAB; 47 48 /* 49 * Private function prototypes 50 */ 51 static int set_bootfs(char *boot_rpool, char *be_root_ds); 52 static int set_canmount(be_node_list_t *, char *); 53 static int be_do_installgrub(be_transaction_data_t *); 54 static int be_get_grub_vers(be_transaction_data_t *, char **, char **); 55 static int get_ver_from_capfile(char *, char **); 56 static int be_promote_zone_ds(char *, char *); 57 static int be_promote_ds_callback(zfs_handle_t *, void *); 58 59 /* ******************************************************************** */ 60 /* Public Functions */ 61 /* ******************************************************************** */ 62 63 /* 64 * Function: be_activate 65 * Description: Calls _be_activate which activates the BE named in the 66 * attributes passed in through be_attrs. The process of 67 * activation sets the bootfs property of the root pool, resets 68 * the canmount property to noauto, and sets the default in the 69 * grub menu to the entry corresponding to the entry for the named 70 * BE. 71 * Parameters: 72 * be_attrs - pointer to nvlist_t of attributes being passed in. 73 * The follow attribute values are used by this function: 74 * 75 * BE_ATTR_ORIG_BE_NAME *required 76 * Return: 77 * BE_SUCCESS - Success 78 * be_errno_t - Failure 79 * Scope: 80 * Public 81 */ 82 int 83 be_activate(nvlist_t *be_attrs) 84 { 85 int ret = BE_SUCCESS; 86 char *be_name = NULL; 87 88 /* Initialize libzfs handle */ 89 if (!be_zfs_init()) 90 return (BE_ERR_INIT); 91 92 /* Get the BE name to activate */ 93 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name) 94 != 0) { 95 be_print_err(gettext("be_activate: failed to " 96 "lookup BE_ATTR_ORIG_BE_NAME attribute\n")); 97 be_zfs_fini(); 98 return (BE_ERR_INVAL); 99 } 100 101 /* Validate BE name */ 102 if (!be_valid_be_name(be_name)) { 103 be_print_err(gettext("be_activate: invalid BE name %s\n"), 104 be_name); 105 be_zfs_fini(); 106 return (BE_ERR_INVAL); 107 } 108 109 ret = _be_activate(be_name); 110 111 be_zfs_fini(); 112 113 return (ret); 114 } 115 116 /* ******************************************************************** */ 117 /* Semi Private Functions */ 118 /* ******************************************************************** */ 119 120 /* 121 * Function: _be_activate 122 * Description: This does the actual work described in be_activate. 123 * Parameters: 124 * be_name - pointer to the name of BE to activate. 125 * 126 * Return: 127 * BE_SUCCESS - Success 128 * be_errnot_t - Failure 129 * Scope: 130 * Public 131 */ 132 int 133 _be_activate(char *be_name) 134 { 135 be_transaction_data_t cb = { 0 }; 136 zfs_handle_t *zhp = NULL; 137 char root_ds[MAXPATHLEN]; 138 char active_ds[MAXPATHLEN]; 139 char *cur_vers = NULL, *new_vers = NULL; 140 be_node_list_t *be_nodes = NULL; 141 uuid_t uu = {0}; 142 int entry, ret = BE_SUCCESS; 143 int zret = 0; 144 145 /* 146 * TODO: The BE needs to be validated to make sure that it is actually 147 * a bootable BE. 148 */ 149 150 if (be_name == NULL) 151 return (BE_ERR_INVAL); 152 153 /* Set obe_name to be_name in the cb structure */ 154 cb.obe_name = be_name; 155 156 /* find which zpool the be is in */ 157 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) { 158 be_print_err(gettext("be_activate: failed to " 159 "find zpool for BE (%s)\n"), cb.obe_name); 160 return (BE_ERR_BE_NOENT); 161 } else if (zret < 0) { 162 be_print_err(gettext("be_activate: " 163 "zpool_iter failed: %s\n"), 164 libzfs_error_description(g_zfs)); 165 ret = zfs_err_to_be_err(g_zfs); 166 return (ret); 167 } 168 169 be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds)); 170 cb.obe_root_ds = strdup(root_ds); 171 172 if (getzoneid() == GLOBAL_ZONEID) { 173 if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers, 174 &new_vers)) != BE_SUCCESS) { 175 be_print_err(gettext("be_activate: failed to get grub " 176 "versions from capability files.\n")); 177 return (ret); 178 } 179 if (cur_vers != NULL) { 180 /* 181 * We need to check to see if the version number from 182 * the BE being activated is greater than the current 183 * one. 184 */ 185 if (new_vers != NULL && 186 atof(cur_vers) < atof(new_vers)) { 187 if ((ret = be_do_installgrub(&cb)) 188 != BE_SUCCESS) { 189 free(new_vers); 190 free(cur_vers); 191 return (ret); 192 } 193 free(new_vers); 194 } 195 free(cur_vers); 196 } else if (new_vers != NULL) { 197 if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) { 198 free(new_vers); 199 return (ret); 200 } 201 free(new_vers); 202 } 203 if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) { 204 if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool, 205 NULL, NULL, NULL)) != BE_SUCCESS) { 206 be_print_err(gettext("be_activate: Failed to " 207 "add BE (%s) to the GRUB menu\n"), 208 cb.obe_name); 209 goto done; 210 } 211 } 212 if (be_has_grub()) { 213 if ((ret = be_change_grub_default(cb.obe_name, 214 cb.obe_zpool)) != BE_SUCCESS) { 215 be_print_err(gettext("be_activate: failed to " 216 "change the default entry in menu.lst\n")); 217 goto done; 218 } 219 } 220 } 221 222 if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) { 223 return (ret); 224 } 225 226 if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) { 227 be_print_err(gettext("be_activate: failed to set " 228 "canmount dataset property\n")); 229 goto done; 230 } 231 232 if (getzoneid() == GLOBAL_ZONEID) { 233 if ((ret = set_bootfs(be_nodes->be_rpool, 234 root_ds)) != BE_SUCCESS) { 235 be_print_err(gettext("be_activate: failed to set " 236 "bootfs pool property for %s\n"), root_ds); 237 goto done; 238 } 239 } 240 241 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) { 242 /* 243 * We don't need to close the zfs handle at this 244 * point because The callback funtion 245 * be_promote_ds_callback() will close it for us. 246 */ 247 if (be_promote_ds_callback(zhp, NULL) != 0) { 248 be_print_err(gettext("be_activate: " 249 "failed to activate the " 250 "datasets for %s: %s\n"), 251 root_ds, 252 libzfs_error_description(g_zfs)); 253 ret = BE_ERR_PROMOTE; 254 goto done; 255 } 256 } else { 257 be_print_err(gettext("be_activate: failed to open " 258 "dataset (%s): %s\n"), root_ds, 259 libzfs_error_description(g_zfs)); 260 ret = zfs_err_to_be_err(g_zfs); 261 goto done; 262 } 263 264 if (getzoneid() == GLOBAL_ZONEID && 265 be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS && 266 (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds)) 267 != BE_SUCCESS) { 268 be_print_err(gettext("be_activate: failed to promote " 269 "the active zonepath datasets for zones in BE %s\n"), 270 cb.obe_name); 271 } 272 273 if (getzoneid() != GLOBAL_ZONEID) { 274 if (!be_zone_compare_uuids(root_ds)) { 275 be_print_err(gettext("be_activate: activating zone " 276 "root dataset from non-active global BE is not " 277 "supported\n")); 278 ret = BE_ERR_NOTSUP; 279 goto done; 280 } 281 if ((zhp = zfs_open(g_zfs, root_ds, 282 ZFS_TYPE_FILESYSTEM)) == NULL) { 283 be_print_err(gettext("be_activate: failed to open " 284 "dataset (%s): %s\n"), root_ds, 285 libzfs_error_description(g_zfs)); 286 ret = zfs_err_to_be_err(g_zfs); 287 goto done; 288 } 289 /* Find current active zone root dataset */ 290 if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool, 291 active_ds, sizeof (active_ds))) != BE_SUCCESS) { 292 be_print_err(gettext("be_activate: failed to find " 293 "active zone root dataset\n")); 294 ZFS_CLOSE(zhp); 295 goto done; 296 } 297 /* Do nothing if requested BE is already active */ 298 if (strcmp(root_ds, active_ds) == 0) { 299 ret = BE_SUCCESS; 300 ZFS_CLOSE(zhp); 301 goto done; 302 } 303 304 /* Set active property for BE */ 305 if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) { 306 be_print_err(gettext("be_activate: failed to set " 307 "active property (%s): %s\n"), root_ds, 308 libzfs_error_description(g_zfs)); 309 ret = zfs_err_to_be_err(g_zfs); 310 ZFS_CLOSE(zhp); 311 goto done; 312 } 313 ZFS_CLOSE(zhp); 314 315 /* Unset active property for old active root dataset */ 316 if ((zhp = zfs_open(g_zfs, active_ds, 317 ZFS_TYPE_FILESYSTEM)) == NULL) { 318 be_print_err(gettext("be_activate: failed to open " 319 "dataset (%s): %s\n"), active_ds, 320 libzfs_error_description(g_zfs)); 321 ret = zfs_err_to_be_err(g_zfs); 322 goto done; 323 } 324 if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) { 325 be_print_err(gettext("be_activate: failed to unset " 326 "active property (%s): %s\n"), active_ds, 327 libzfs_error_description(g_zfs)); 328 ret = zfs_err_to_be_err(g_zfs); 329 ZFS_CLOSE(zhp); 330 goto done; 331 } 332 ZFS_CLOSE(zhp); 333 } 334 done: 335 be_free_list(be_nodes); 336 return (ret); 337 } 338 339 /* 340 * Function: be_activate_current_be 341 * Description: Set the currently "active" BE to be "active on boot" 342 * Paramters: 343 * none 344 * Returns: 345 * BE_SUCCESS - Success 346 * be_errnot_t - Failure 347 * Scope: 348 * Semi-private (library wide use only) 349 */ 350 int 351 be_activate_current_be(void) 352 { 353 int ret = BE_SUCCESS; 354 be_transaction_data_t bt = { 0 }; 355 356 if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) { 357 return (ret); 358 } 359 360 if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) { 361 be_print_err(gettext("be_activate_current_be: failed to " 362 "activate %s\n"), bt.obe_name); 363 return (ret); 364 } 365 366 return (BE_SUCCESS); 367 } 368 369 /* 370 * Function: be_is_active_on_boot 371 * Description: Checks if the BE name passed in has the "active on boot" 372 * property set to B_TRUE. 373 * Paramters: 374 * be_name - the name of the BE to check 375 * Returns: 376 * B_TRUE - if active on boot. 377 * B_FALSE - if not active on boot. 378 * Scope: 379 * Semi-private (library wide use only) 380 */ 381 boolean_t 382 be_is_active_on_boot(char *be_name) 383 { 384 be_node_list_t *be_node = NULL; 385 386 if (be_name == NULL) { 387 be_print_err(gettext("be_is_active_on_boot: " 388 "be_name must not be NULL\n")); 389 return (B_FALSE); 390 } 391 392 if (_be_list(be_name, &be_node) != BE_SUCCESS) { 393 return (B_FALSE); 394 } 395 396 if (be_node == NULL) { 397 return (B_FALSE); 398 } 399 400 if (be_node->be_active_on_boot) { 401 be_free_list(be_node); 402 return (B_TRUE); 403 } else { 404 be_free_list(be_node); 405 return (B_FALSE); 406 } 407 } 408 409 /* ******************************************************************** */ 410 /* Private Functions */ 411 /* ******************************************************************** */ 412 413 /* 414 * Function: set_bootfs 415 * Description: Sets the bootfs property on the boot pool to be the 416 * root dataset of the activated BE. 417 * Parameters: 418 * boot_pool - The pool we're setting bootfs in. 419 * be_root_ds - The main dataset for the BE. 420 * Return: 421 * BE_SUCCESS - Success 422 * be_errno_t - Failure 423 * Scope: 424 * Private 425 */ 426 static int 427 set_bootfs(char *boot_rpool, char *be_root_ds) 428 { 429 zpool_handle_t *zhp; 430 int err = BE_SUCCESS; 431 432 if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) { 433 be_print_err(gettext("set_bootfs: failed to open pool " 434 "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs)); 435 err = zfs_err_to_be_err(g_zfs); 436 return (err); 437 } 438 439 err = zpool_set_prop(zhp, "bootfs", be_root_ds); 440 if (err) { 441 be_print_err(gettext("set_bootfs: failed to set " 442 "bootfs property for pool %s: %s\n"), boot_rpool, 443 libzfs_error_description(g_zfs)); 444 err = zfs_err_to_be_err(g_zfs); 445 zpool_close(zhp); 446 return (err); 447 } 448 449 zpool_close(zhp); 450 return (BE_SUCCESS); 451 } 452 453 /* 454 * Function: set_canmount 455 * Description: Sets the canmount property on the datasets of the 456 * activated BE. 457 * Parameters: 458 * be_nodes - The be_node_t returned from be_list 459 * value - The value of canmount we setting, on|off|noauto. 460 * Return: 461 * BE_SUCCESS - Success 462 * be_errno_t - Failure 463 * Scope: 464 * Private 465 */ 466 static int 467 set_canmount(be_node_list_t *be_nodes, char *value) 468 { 469 char ds_path[MAXPATHLEN]; 470 zfs_handle_t *zhp = NULL; 471 be_node_list_t *list = be_nodes; 472 int err = BE_SUCCESS; 473 474 while (list != NULL) { 475 be_dataset_list_t *datasets = list->be_node_datasets; 476 477 be_make_root_ds(list->be_rpool, list->be_node_name, ds_path, 478 sizeof (ds_path)); 479 480 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) == 481 NULL) { 482 be_print_err(gettext("set_canmount: failed to open " 483 "dataset (%s): %s\n"), ds_path, 484 libzfs_error_description(g_zfs)); 485 err = zfs_err_to_be_err(g_zfs); 486 return (err); 487 } 488 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) { 489 /* 490 * it's already mounted so we can't change the 491 * canmount property anyway. 492 */ 493 err = BE_SUCCESS; 494 } else { 495 err = zfs_prop_set(zhp, 496 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value); 497 if (err) { 498 ZFS_CLOSE(zhp); 499 be_print_err(gettext("set_canmount: failed to " 500 "set dataset property (%s): %s\n"), 501 ds_path, libzfs_error_description(g_zfs)); 502 err = zfs_err_to_be_err(g_zfs); 503 return (err); 504 } 505 } 506 ZFS_CLOSE(zhp); 507 508 while (datasets != NULL) { 509 be_make_root_ds(list->be_rpool, 510 datasets->be_dataset_name, ds_path, 511 sizeof (ds_path)); 512 513 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) 514 == NULL) { 515 be_print_err(gettext("set_canmount: failed to " 516 "open dataset %s: %s\n"), ds_path, 517 libzfs_error_description(g_zfs)); 518 err = zfs_err_to_be_err(g_zfs); 519 return (err); 520 } 521 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) { 522 /* 523 * it's already mounted so we can't change the 524 * canmount property anyway. 525 */ 526 err = BE_SUCCESS; 527 ZFS_CLOSE(zhp); 528 break; 529 } 530 err = zfs_prop_set(zhp, 531 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value); 532 if (err) { 533 ZFS_CLOSE(zhp); 534 be_print_err(gettext("set_canmount: " 535 "Failed to set property value %s " 536 "for dataset %s: %s\n"), value, ds_path, 537 libzfs_error_description(g_zfs)); 538 err = zfs_err_to_be_err(g_zfs); 539 return (err); 540 } 541 ZFS_CLOSE(zhp); 542 datasets = datasets->be_next_dataset; 543 } 544 list = list->be_next_node; 545 } 546 return (err); 547 } 548 549 /* 550 * Function: be_get_grub_vers 551 * Description: Gets the grub version number from /boot/grub/capability. If 552 * capability file doesn't exist NULL is returned. 553 * Parameters: 554 * bt - The transaction data for the BE we're getting the grub 555 * version for. 556 * cur_vers - used to return the current version of grub from 557 * the root pool. 558 * new_vers - used to return the grub version of the BE we're 559 * activating. 560 * Return: 561 * BE_SUCCESS - Success 562 * be_errno_t - Failed to find version 563 * Scope: 564 * Private 565 */ 566 static int 567 be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers) 568 { 569 zfs_handle_t *zhp = NULL; 570 zfs_handle_t *pool_zhp = NULL; 571 int ret = BE_SUCCESS; 572 char cap_file[MAXPATHLEN]; 573 char *temp_mntpnt = NULL; 574 char *zpool_mntpt = NULL; 575 char *ptmp_mntpnt = NULL; 576 char *orig_mntpnt = NULL; 577 boolean_t be_mounted = B_FALSE; 578 boolean_t pool_mounted = B_FALSE; 579 580 if (!be_has_grub()) { 581 be_print_err(gettext("be_get_grub_vers: Not supported on " 582 "this architecture\n")); 583 return (BE_ERR_NOTSUP); 584 } 585 586 if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL || 587 bt->obe_root_ds == NULL) { 588 be_print_err(gettext("be_get_grub_vers: Invalid BE\n")); 589 return (BE_ERR_INVAL); 590 } 591 592 if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) == 593 NULL) { 594 be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"), 595 libzfs_error_description(g_zfs)); 596 return (zfs_err_to_be_err(g_zfs)); 597 } 598 599 /* 600 * Check to see if the pool's dataset is mounted. If it isn't we'll 601 * attempt to mount it. 602 */ 603 if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt, 604 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) { 605 be_print_err(gettext("be_get_grub_vers: pool dataset " 606 "(%s) could not be mounted\n"), bt->obe_zpool); 607 ZFS_CLOSE(pool_zhp); 608 return (ret); 609 } 610 611 /* 612 * Get the mountpoint for the root pool dataset. 613 */ 614 if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) { 615 be_print_err(gettext("be_get_grub_vers: pool " 616 "dataset (%s) is not mounted. Can't set the " 617 "default BE in the grub menu.\n"), bt->obe_zpool); 618 ret = BE_ERR_NO_MENU; 619 goto cleanup; 620 } 621 622 /* 623 * get the version of the most recent grub update. 624 */ 625 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", 626 zpool_mntpt, BE_CAP_FILE); 627 free(zpool_mntpt); 628 zpool_mntpt = NULL; 629 630 if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS) 631 goto cleanup; 632 633 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 634 NULL) { 635 be_print_err(gettext("be_get_grub_vers: failed to " 636 "open BE root dataset (%s): %s\n"), bt->obe_root_ds, 637 libzfs_error_description(g_zfs)); 638 free(cur_vers); 639 ret = zfs_err_to_be_err(g_zfs); 640 goto cleanup; 641 } 642 if (!zfs_is_mounted(zhp, &temp_mntpnt)) { 643 if ((ret = _be_mount(bt->obe_name, &temp_mntpnt, 644 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 645 be_print_err(gettext("be_get_grub_vers: failed to " 646 "mount BE (%s)\n"), bt->obe_name); 647 free(*cur_vers); 648 *cur_vers = NULL; 649 ZFS_CLOSE(zhp); 650 goto cleanup; 651 } 652 be_mounted = B_TRUE; 653 } 654 ZFS_CLOSE(zhp); 655 656 /* 657 * Now get the grub version for the BE being activated. 658 */ 659 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt, 660 BE_CAP_FILE); 661 ret = get_ver_from_capfile(cap_file, new_vers); 662 if (ret != BE_SUCCESS) { 663 free(*cur_vers); 664 *cur_vers = NULL; 665 } 666 if (be_mounted) 667 (void) _be_unmount(bt->obe_name, 0); 668 669 cleanup: 670 if (pool_mounted) { 671 int iret = BE_SUCCESS; 672 iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt); 673 if (ret == BE_SUCCESS) 674 ret = iret; 675 free(orig_mntpnt); 676 free(ptmp_mntpnt); 677 } 678 ZFS_CLOSE(pool_zhp); 679 680 free(temp_mntpnt); 681 return (ret); 682 } 683 684 /* 685 * Function: get_ver_from_capfile 686 * Description: Parses the capability file passed in looking for the VERSION 687 * line. If found the version is returned in vers, if not then 688 * NULL is returned in vers. 689 * 690 * Parameters: 691 * file - the path to the capability file we want to parse. 692 * vers - the version string that will be passed back. 693 * Return: 694 * BE_SUCCESS - Success 695 * be_errno_t - Failed to find version 696 * Scope: 697 * Private 698 */ 699 static int 700 get_ver_from_capfile(char *file, char **vers) 701 { 702 FILE *fp = NULL; 703 char line[BUFSIZ]; 704 char *last = NULL; 705 int err = BE_SUCCESS; 706 errno = 0; 707 708 if (!be_has_grub()) { 709 be_print_err(gettext("get_ver_from_capfile: Not supported " 710 "on this architecture\n")); 711 return (BE_ERR_NOTSUP); 712 } 713 714 /* 715 * Set version string to NULL; the only case this shouldn't be set 716 * to be NULL is when we've actually found a version in the capability 717 * file, which is set below. 718 */ 719 *vers = NULL; 720 721 /* 722 * If the capability file doesn't exist, we're returning success 723 * because on older releases, the capability file did not exist 724 * so this is a valid scenario. 725 */ 726 if (access(file, F_OK) == 0) { 727 if ((fp = fopen(file, "r")) == NULL) { 728 err = errno; 729 be_print_err(gettext("get_ver_from_capfile: failed to " 730 "open file %s with error %s\n"), file, 731 strerror(err)); 732 err = errno_to_be_err(err); 733 return (err); 734 } 735 736 while (fgets(line, BUFSIZ, fp)) { 737 char *tok = strtok_r(line, "=", &last); 738 739 if (tok == NULL || tok[0] == '#') { 740 continue; 741 } else if (strcmp(tok, "VERSION") == 0) { 742 *vers = strdup(last); 743 break; 744 } 745 } 746 (void) fclose(fp); 747 } 748 749 return (BE_SUCCESS); 750 } 751 752 /* 753 * Function: be_do_installgrub 754 * Description: This function runs installgrub using the grub loader files 755 * from the BE we're activating and installing them on the 756 * pool the BE lives in. 757 * 758 * Parameters: 759 * bt - The transaction data for the BE we're activating. 760 * Return: 761 * BE_SUCCESS - Success 762 * be_errno_t - Failure 763 * 764 * Scope: 765 * Private 766 */ 767 static int 768 be_do_installgrub(be_transaction_data_t *bt) 769 { 770 zpool_handle_t *zphp = NULL; 771 zfs_handle_t *zhp = NULL; 772 nvlist_t **child, *nv, *config; 773 uint_t c, children = 0; 774 char *tmp_mntpt = NULL; 775 char *pool_mntpnt = NULL; 776 char *ptmp_mntpnt = NULL; 777 char *orig_mntpnt = NULL; 778 FILE *cap_fp = NULL; 779 FILE *zpool_cap_fp = NULL; 780 char line[BUFSIZ]; 781 char cap_file[MAXPATHLEN]; 782 char zpool_cap_file[MAXPATHLEN]; 783 char stage1[MAXPATHLEN]; 784 char stage2[MAXPATHLEN]; 785 char installgrub_cmd[MAXPATHLEN]; 786 char *vname; 787 char be_run_cmd_errbuf[BUFSIZ]; 788 int ret = BE_SUCCESS; 789 int err = 0; 790 boolean_t be_mounted = B_FALSE; 791 boolean_t pool_mounted = B_FALSE; 792 793 if (!be_has_grub()) { 794 be_print_err(gettext("be_do_installgrub: Not supported " 795 "on this architecture\n")); 796 return (BE_ERR_NOTSUP); 797 } 798 799 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 800 NULL) { 801 be_print_err(gettext("be_do_installgrub: failed to " 802 "open BE root dataset (%s): %s\n"), bt->obe_root_ds, 803 libzfs_error_description(g_zfs)); 804 ret = zfs_err_to_be_err(g_zfs); 805 return (ret); 806 } 807 if (!zfs_is_mounted(zhp, &tmp_mntpt)) { 808 if ((ret = _be_mount(bt->obe_name, &tmp_mntpt, 809 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 810 be_print_err(gettext("be_do_installgrub: failed to " 811 "mount BE (%s)\n"), bt->obe_name); 812 ZFS_CLOSE(zhp); 813 return (ret); 814 } 815 be_mounted = B_TRUE; 816 } 817 ZFS_CLOSE(zhp); 818 819 (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1); 820 (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2); 821 822 if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) { 823 be_print_err(gettext("be_do_installgrub: failed to open " 824 "pool (%s): %s\n"), bt->obe_zpool, 825 libzfs_error_description(g_zfs)); 826 ret = zfs_err_to_be_err(g_zfs); 827 if (be_mounted) 828 (void) _be_unmount(bt->obe_name, 0); 829 free(tmp_mntpt); 830 return (ret); 831 } 832 833 if ((config = zpool_get_config(zphp, NULL)) == NULL) { 834 be_print_err(gettext("be_do_installgrub: failed to get zpool " 835 "configuration information. %s\n"), 836 libzfs_error_description(g_zfs)); 837 ret = zfs_err_to_be_err(g_zfs); 838 goto done; 839 } 840 841 /* 842 * Get the vdev tree 843 */ 844 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) { 845 be_print_err(gettext("be_do_installgrub: failed to get vdev " 846 "tree: %s\n"), libzfs_error_description(g_zfs)); 847 ret = zfs_err_to_be_err(g_zfs); 848 goto done; 849 } 850 851 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child, 852 &children) != 0) { 853 be_print_err(gettext("be_do_installgrub: failed to traverse " 854 "the vdev tree: %s\n"), libzfs_error_description(g_zfs)); 855 ret = zfs_err_to_be_err(g_zfs); 856 goto done; 857 } 858 for (c = 0; c < children; c++) { 859 uint_t i, nchildren = 0; 860 nvlist_t **nvchild; 861 vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE); 862 if (vname == NULL) { 863 be_print_err(gettext( 864 "be_do_installgrub: " 865 "failed to get device name: %s\n"), 866 libzfs_error_description(g_zfs)); 867 ret = zfs_err_to_be_err(g_zfs); 868 goto done; 869 } 870 if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') { 871 872 if (nvlist_lookup_nvlist_array(child[c], 873 ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) { 874 be_print_err(gettext("be_do_installgrub: " 875 "failed to traverse the vdev tree: %s\n"), 876 libzfs_error_description(g_zfs)); 877 ret = zfs_err_to_be_err(g_zfs); 878 goto done; 879 } 880 881 for (i = 0; i < nchildren; i++) { 882 vname = zpool_vdev_name(g_zfs, zphp, 883 nvchild[i], B_FALSE); 884 if (vname == NULL) { 885 be_print_err(gettext( 886 "be_do_installgrub: " 887 "failed to get device name: %s\n"), 888 libzfs_error_description(g_zfs)); 889 ret = zfs_err_to_be_err(g_zfs); 890 goto done; 891 } 892 893 (void) snprintf(installgrub_cmd, 894 sizeof (installgrub_cmd), 895 "%s %s %s /dev/rdsk/%s", 896 BE_INSTALL_GRUB, stage1, stage2, vname); 897 if (be_run_cmd(installgrub_cmd, 898 be_run_cmd_errbuf, BUFSIZ, NULL, 0) != 899 BE_SUCCESS) { 900 be_print_err(gettext( 901 "be_do_installgrub: installgrub " 902 "failed for device %s.\n"), vname); 903 /* Assume localized cmd err output. */ 904 be_print_err(gettext( 905 " Command: \"%s\"\n"), 906 installgrub_cmd); 907 be_print_err("%s", be_run_cmd_errbuf); 908 free(vname); 909 ret = BE_ERR_BOOTFILE_INST; 910 goto done; 911 } 912 free(vname); 913 } 914 } else { 915 (void) snprintf(installgrub_cmd, 916 sizeof (installgrub_cmd), "%s %s %s /dev/rdsk/%s", 917 BE_INSTALL_GRUB, stage1, stage2, vname); 918 if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf, 919 BUFSIZ, NULL, 0) != BE_SUCCESS) { 920 be_print_err(gettext( 921 "be_do_installgrub: installgrub " 922 "failed for device %s.\n"), vname); 923 /* Assume localized cmd err output. */ 924 be_print_err(gettext(" Command: \"%s\"\n"), 925 installgrub_cmd); 926 be_print_err("%s", be_run_cmd_errbuf); 927 free(vname); 928 ret = BE_ERR_BOOTFILE_INST; 929 goto done; 930 } 931 free(vname); 932 } 933 } 934 935 /* 936 * Copy the grub capability file from the BE we're activating into 937 * the root pool. 938 */ 939 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt, 940 BE_CAP_FILE); 941 942 if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) == 943 NULL) { 944 be_print_err(gettext("be_do_installgrub: zfs_open " 945 "failed: %s\n"), libzfs_error_description(g_zfs)); 946 zpool_close(zphp); 947 return (zfs_err_to_be_err(g_zfs)); 948 } 949 950 /* 951 * Check to see if the pool's dataset is mounted. If it isn't we'll 952 * attempt to mount it. 953 */ 954 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, 955 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) { 956 be_print_err(gettext("be_do_installgrub: pool dataset " 957 "(%s) could not be mounted\n"), bt->obe_zpool); 958 ZFS_CLOSE(zhp); 959 zpool_close(zphp); 960 return (ret); 961 } 962 963 /* 964 * Get the mountpoint for the root pool dataset. 965 */ 966 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 967 be_print_err(gettext("be_do_installgrub: pool " 968 "dataset (%s) is not mounted. Can't check the grub " 969 "version from the grub capability file.\n"), bt->obe_zpool); 970 ret = BE_ERR_NO_MENU; 971 goto done; 972 } 973 974 (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s", 975 pool_mntpnt, BE_CAP_FILE); 976 977 free(pool_mntpnt); 978 pool_mntpnt = NULL; 979 980 if ((cap_fp = fopen(cap_file, "r")) == NULL) { 981 err = errno; 982 be_print_err(gettext("be_do_installgrub: failed to open grub " 983 "capability file\n")); 984 ret = errno_to_be_err(err); 985 goto done; 986 } 987 if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) { 988 err = errno; 989 be_print_err(gettext("be_do_installgrub: failed to open new " 990 "grub capability file\n")); 991 ret = errno_to_be_err(err); 992 (void) fclose(cap_fp); 993 goto done; 994 } 995 996 while (fgets(line, BUFSIZ, cap_fp)) { 997 (void) fputs(line, zpool_cap_fp); 998 } 999 1000 (void) fclose(zpool_cap_fp); 1001 (void) fclose(cap_fp); 1002 1003 done: 1004 if (pool_mounted) { 1005 int iret = 0; 1006 iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1007 if (ret == BE_SUCCESS) 1008 ret = iret; 1009 free(orig_mntpnt); 1010 free(ptmp_mntpnt); 1011 } 1012 ZFS_CLOSE(zhp); 1013 if (be_mounted) 1014 (void) _be_unmount(bt->obe_name, 0); 1015 zpool_close(zphp); 1016 free(tmp_mntpt); 1017 return (ret); 1018 } 1019 1020 /* 1021 * Function: be_promote_zone_ds 1022 * Description: This function finds the zones for the BE being activated 1023 * and the active zonepath dataset for each zone. Then each 1024 * active zonepath dataset is promoted. 1025 * 1026 * Parameters: 1027 * be_name - the name of the global zone BE that we need to 1028 * find the zones for. 1029 * be_root_ds - the root dataset for be_name. 1030 * Return: 1031 * BE_SUCCESS - Success 1032 * be_errno_t - Failure 1033 * 1034 * Scope: 1035 * Private 1036 */ 1037 static int 1038 be_promote_zone_ds(char *be_name, char *be_root_ds) 1039 { 1040 char *zone_ds = NULL; 1041 char *temp_mntpt = NULL; 1042 char origin[MAXPATHLEN]; 1043 char zoneroot_ds[MAXPATHLEN]; 1044 zfs_handle_t *zhp = NULL; 1045 zfs_handle_t *z_zhp = NULL; 1046 zoneList_t zone_list = NULL; 1047 zoneBrandList_t *brands = NULL; 1048 boolean_t be_mounted = B_FALSE; 1049 int zone_index = 0; 1050 int err = BE_SUCCESS; 1051 1052 /* 1053 * Get the supported zone brands so we can pass that 1054 * to z_get_nonglobal_zone_list_by_brand. Currently 1055 * only the ipkg and labeled brand zones are supported 1056 * 1057 */ 1058 if ((brands = be_get_supported_brandlist()) == NULL) { 1059 be_print_err(gettext("be_promote_zone_ds: no supported " 1060 "brands\n")); 1061 return (BE_SUCCESS); 1062 } 1063 1064 if ((zhp = zfs_open(g_zfs, be_root_ds, 1065 ZFS_TYPE_FILESYSTEM)) == NULL) { 1066 be_print_err(gettext("be_promote_zone_ds: Failed to open " 1067 "dataset (%s): %s\n"), be_root_ds, 1068 libzfs_error_description(g_zfs)); 1069 err = zfs_err_to_be_err(g_zfs); 1070 z_free_brand_list(brands); 1071 return (err); 1072 } 1073 1074 if (!zfs_is_mounted(zhp, &temp_mntpt)) { 1075 if ((err = _be_mount(be_name, &temp_mntpt, 1076 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 1077 be_print_err(gettext("be_promote_zone_ds: failed to " 1078 "mount the BE for zones procesing.\n")); 1079 ZFS_CLOSE(zhp); 1080 z_free_brand_list(brands); 1081 return (err); 1082 } 1083 be_mounted = B_TRUE; 1084 } 1085 1086 /* 1087 * Set the zone root to the temp mount point for the BE we just mounted. 1088 */ 1089 z_set_zone_root(temp_mntpt); 1090 1091 /* 1092 * Get all the zones based on the brands we're looking for. If no zones 1093 * are found that we're interested in unmount the BE and move on. 1094 */ 1095 if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) { 1096 if (be_mounted) 1097 (void) _be_unmount(be_name, 0); 1098 ZFS_CLOSE(zhp); 1099 z_free_brand_list(brands); 1100 free(temp_mntpt); 1101 return (BE_SUCCESS); 1102 } 1103 for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index) 1104 != NULL; zone_index++) { 1105 char *zone_path = NULL; 1106 1107 /* Skip zones that aren't at least installed */ 1108 if (z_zlist_get_current_state(zone_list, zone_index) < 1109 ZONE_STATE_INSTALLED) 1110 continue; 1111 1112 if (((zone_path = 1113 z_zlist_get_zonepath(zone_list, zone_index)) == NULL) || 1114 ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) || 1115 !be_zone_supported(zone_ds)) 1116 continue; 1117 1118 if (be_find_active_zone_root(zhp, zone_ds, 1119 zoneroot_ds, sizeof (zoneroot_ds)) != 0) { 1120 be_print_err(gettext("be_promote_zone_ds: " 1121 "Zone does not have an active root " 1122 "dataset, skipping this zone.\n")); 1123 continue; 1124 } 1125 1126 if ((z_zhp = zfs_open(g_zfs, zoneroot_ds, 1127 ZFS_TYPE_FILESYSTEM)) == NULL) { 1128 be_print_err(gettext("be_promote_zone_ds: " 1129 "Failed to open dataset " 1130 "(%s): %s\n"), zoneroot_ds, 1131 libzfs_error_description(g_zfs)); 1132 err = zfs_err_to_be_err(g_zfs); 1133 goto done; 1134 } 1135 1136 if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin, 1137 sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) { 1138 ZFS_CLOSE(z_zhp); 1139 continue; 1140 } 1141 1142 /* 1143 * We don't need to close the zfs handle at this 1144 * point because the callback funtion 1145 * be_promote_ds_callback() will close it for us. 1146 */ 1147 if (be_promote_ds_callback(z_zhp, NULL) != 0) { 1148 be_print_err(gettext("be_promote_zone_ds: " 1149 "failed to activate the " 1150 "datasets for %s: %s\n"), 1151 zoneroot_ds, 1152 libzfs_error_description(g_zfs)); 1153 err = BE_ERR_PROMOTE; 1154 goto done; 1155 } 1156 } 1157 done: 1158 if (be_mounted) 1159 (void) _be_unmount(be_name, 0); 1160 ZFS_CLOSE(zhp); 1161 free(temp_mntpt); 1162 z_free_brand_list(brands); 1163 z_free_zone_list(zone_list); 1164 return (err); 1165 } 1166 1167 /* 1168 * Function: be_promote_ds_callback 1169 * Description: This function is used to promote the datasets for the BE 1170 * being activated as well as the datasets for the zones BE 1171 * being activated. 1172 * 1173 * Parameters: 1174 * zhp - the zfs handle for zone BE being activated. 1175 * data - not used. 1176 * Return: 1177 * 0 - Success 1178 * be_errno_t - Failure 1179 * 1180 * Scope: 1181 * Private 1182 */ 1183 static int 1184 /* LINTED */ 1185 be_promote_ds_callback(zfs_handle_t *zhp, void *data) 1186 { 1187 char origin[MAXPATHLEN]; 1188 char *sub_dataset = NULL; 1189 int ret = 0; 1190 1191 if (zhp != NULL) { 1192 sub_dataset = strdup(zfs_get_name(zhp)); 1193 if (sub_dataset == NULL) { 1194 ret = BE_ERR_NOMEM; 1195 goto done; 1196 } 1197 } else { 1198 be_print_err(gettext("be_promote_ds_callback: " 1199 "Invalid zfs handle passed into function\n")); 1200 ret = BE_ERR_INVAL; 1201 goto done; 1202 } 1203 1204 /* 1205 * This loop makes sure that we promote the dataset to the 1206 * top of the tree so that it is no longer a decendent of any 1207 * dataset. The ZFS close and then open is used to make sure that 1208 * the promotion is updated before we move on. 1209 */ 1210 while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, 1211 sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) { 1212 if (zfs_promote(zhp) != 0) { 1213 if (libzfs_errno(g_zfs) != EZFS_EXISTS) { 1214 be_print_err(gettext("be_promote_ds_callback: " 1215 "promote of %s failed: %s\n"), 1216 zfs_get_name(zhp), 1217 libzfs_error_description(g_zfs)); 1218 ret = zfs_err_to_be_err(g_zfs); 1219 goto done; 1220 } else { 1221 /* 1222 * If the call to zfs_promote returns the 1223 * error EZFS_EXISTS we've hit a snapshot name 1224 * collision. This means we're probably 1225 * attemping to promote a zone dataset above a 1226 * parent dataset that belongs to another zone 1227 * which this zone was cloned from. 1228 * 1229 * TODO: If this is a zone dataset at some 1230 * point we should skip this if the zone 1231 * paths for the dataset and the snapshot 1232 * don't match. 1233 */ 1234 be_print_err(gettext("be_promote_ds_callback: " 1235 "promote of %s failed due to snapshot " 1236 "name collision: %s\n"), zfs_get_name(zhp), 1237 libzfs_error_description(g_zfs)); 1238 ret = zfs_err_to_be_err(g_zfs); 1239 goto done; 1240 } 1241 } 1242 ZFS_CLOSE(zhp); 1243 if ((zhp = zfs_open(g_zfs, sub_dataset, 1244 ZFS_TYPE_FILESYSTEM)) == NULL) { 1245 be_print_err(gettext("be_promote_ds_callback: " 1246 "Failed to open dataset (%s): %s\n"), sub_dataset, 1247 libzfs_error_description(g_zfs)); 1248 ret = zfs_err_to_be_err(g_zfs); 1249 goto done; 1250 } 1251 } 1252 1253 /* Iterate down this dataset's children and promote them */ 1254 ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL); 1255 1256 done: 1257 free(sub_dataset); 1258 ZFS_CLOSE(zhp); 1259 return (ret); 1260 }