1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 25 * Copyright 2016 Toomas Soome <tsoome@me.com> 26 * Copyright (c) 2015 by Delphix. All rights reserved. 27 * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. 28 * Copyright (c) 2018, Joyent, Inc. 29 */ 30 31 32 /* 33 * System includes 34 */ 35 #include <assert.h> 36 #include <errno.h> 37 #include <libgen.h> 38 #include <libintl.h> 39 #include <libnvpair.h> 40 #include <libzfs.h> 41 #include <libgen.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <sys/stat.h> 46 #include <sys/types.h> 47 #include <sys/vfstab.h> 48 #include <sys/param.h> 49 #include <sys/systeminfo.h> 50 #include <ctype.h> 51 #include <time.h> 52 #include <unistd.h> 53 #include <fcntl.h> 54 #include <deflt.h> 55 #include <wait.h> 56 #include <libdevinfo.h> 57 #include <libgen.h> 58 59 #include <libbe.h> 60 #include <libbe_priv.h> 61 #include <boot_utils.h> 62 #include <ficl.h> 63 #include <ficlplatform/emu.h> 64 65 /* Private function prototypes */ 66 static int update_dataset(char *, int, char *, char *, char *); 67 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *); 68 static int be_open_menu(char *, char *, FILE **, char *, boolean_t); 69 static int be_create_menu(char *, char *, FILE **, char *); 70 static char *be_get_auto_name(char *, char *, boolean_t); 71 72 /* 73 * Global error printing 74 */ 75 boolean_t do_print = B_FALSE; 76 77 /* 78 * Private datatypes 79 */ 80 typedef struct zone_be_name_cb_data { 81 char *base_be_name; 82 int num; 83 } zone_be_name_cb_data_t; 84 85 /* ******************************************************************** */ 86 /* Public Functions */ 87 /* ******************************************************************** */ 88 89 /* 90 * Callback for ficl to suppress all output from ficl, as we do not 91 * want to confuse user with messages from ficl, and we are only 92 * checking results from function calls. 93 */ 94 /*ARGSUSED*/ 95 static void 96 ficlSuppressTextOutput(ficlCallback *cb, char *text) 97 { 98 /* This function is intentionally doing nothing. */ 99 } 100 101 /* 102 * Function: be_get_boot_args 103 * Description: Returns the fast boot argument string for enumerated BE. 104 * Parameters: 105 * fbarg - pointer to argument string. 106 * entry - index of BE. 107 * Returns: 108 * fast boot argument string. 109 * Scope: 110 * Public 111 */ 112 int 113 be_get_boot_args(char **fbarg, int entry) 114 { 115 be_node_list_t *node, *be_nodes = NULL; 116 be_transaction_data_t bt = {0}; 117 char *mountpoint = NULL; 118 boolean_t be_mounted = B_FALSE; 119 int ret = BE_SUCCESS; 120 int index; 121 ficlVm *vm; 122 123 *fbarg = NULL; 124 if (!be_zfs_init()) 125 return (BE_ERR_INIT); 126 127 /* 128 * need pool name, menu.lst has entries from our pool only 129 */ 130 ret = be_find_current_be(&bt); 131 if (ret != BE_SUCCESS) { 132 be_zfs_fini(); 133 return (ret); 134 } 135 136 /* 137 * be_get_boot_args() is for loader, fail with grub will trigger 138 * normal boot. 139 */ 140 if (be_has_grub()) { 141 ret = BE_ERR_INIT; 142 goto done; 143 } 144 145 ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT); 146 if (ret != BE_SUCCESS) 147 goto done; 148 149 /* 150 * iterate through be_nodes, 151 * if entry == -1, stop if be_active_on_boot, 152 * else stop if index == entry. 153 */ 154 index = 0; 155 for (node = be_nodes; node != NULL; node = node->be_next_node) { 156 if (strcmp(node->be_rpool, bt.obe_zpool) != 0) 157 continue; 158 if (entry == BE_ENTRY_DEFAULT && 159 node->be_active_on_boot == B_TRUE) 160 break; 161 if (index == entry) 162 break; 163 index++; 164 } 165 if (node == NULL) { 166 be_free_list(be_nodes); 167 ret = BE_ERR_NOENT; 168 goto done; 169 } 170 171 /* try to mount inactive be */ 172 if (node->be_active == B_FALSE) { 173 ret = _be_mount(node->be_node_name, &mountpoint, 174 BE_MOUNT_FLAG_NO_ZONES); 175 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) { 176 be_free_list(be_nodes); 177 goto done; 178 } else 179 be_mounted = B_TRUE; 180 } 181 182 vm = bf_init("", ficlSuppressTextOutput); 183 if (vm != NULL) { 184 /* 185 * zfs MAXNAMELEN is 256, so we need to pick buf large enough 186 * to contain such names. 187 */ 188 char buf[MAXNAMELEN * 2]; 189 char *kernel_options = NULL; 190 char *kernel = NULL; 191 char *tmp; 192 zpool_handle_t *zph; 193 194 /* 195 * just try to interpret following words. on error 196 * we will be missing kernelname, and will get out. 197 */ 198 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:", 199 node->be_root_ds); 200 ret = ficlVmEvaluate(vm, buf); 201 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 202 be_print_err(gettext("be_get_boot_args: error " 203 "interpreting boot config: %d\n"), ret); 204 bf_fini(); 205 ret = BE_ERR_NO_MENU; 206 goto cleanup; 207 } 208 (void) snprintf(buf, sizeof (buf), 209 "include /boot/forth/loader.4th"); 210 ret = ficlVmEvaluate(vm, buf); 211 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 212 be_print_err(gettext("be_get_boot_args: error " 213 "interpreting boot config: %d\n"), ret); 214 bf_fini(); 215 ret = BE_ERR_NO_MENU; 216 goto cleanup; 217 } 218 (void) snprintf(buf, sizeof (buf), "start"); 219 ret = ficlVmEvaluate(vm, buf); 220 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 221 be_print_err(gettext("be_get_boot_args: error " 222 "interpreting boot config: %d\n"), ret); 223 bf_fini(); 224 ret = BE_ERR_NO_MENU; 225 goto cleanup; 226 } 227 (void) snprintf(buf, sizeof (buf), "boot"); 228 ret = ficlVmEvaluate(vm, buf); 229 bf_fini(); 230 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) { 231 be_print_err(gettext("be_get_boot_args: error " 232 "interpreting boot config: %d\n"), ret); 233 ret = BE_ERR_NO_MENU; 234 goto cleanup; 235 } 236 237 kernel_options = getenv("boot-args"); 238 kernel = getenv("kernelname"); 239 240 if (kernel == NULL) { 241 be_print_err(gettext("be_get_boot_args: no kernel\n")); 242 ret = BE_ERR_NOENT; 243 goto cleanup; 244 } 245 246 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) { 247 be_print_err(gettext("be_get_boot_args: failed to " 248 "open root pool (%s): %s\n"), node->be_rpool, 249 libzfs_error_description(g_zfs)); 250 ret = zfs_err_to_be_err(g_zfs); 251 goto cleanup; 252 } 253 ret = zpool_get_physpath(zph, buf, sizeof (buf)); 254 zpool_close(zph); 255 if (ret != 0) { 256 be_print_err(gettext("be_get_boot_args: failed to " 257 "get physpath\n")); 258 goto cleanup; 259 } 260 261 /* zpool_get_physpath() can return space separated list */ 262 tmp = buf; 263 tmp = strsep(&tmp, " "); 264 265 if (kernel_options == NULL || *kernel_options == '\0') 266 (void) asprintf(fbarg, "/ %s " 267 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel, 268 node->be_root_ds, tmp); 269 else 270 (void) asprintf(fbarg, "/ %s %s " 271 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel, 272 kernel_options, node->be_root_ds, tmp); 273 274 if (*fbarg == NULL) 275 ret = BE_ERR_NOMEM; 276 else 277 ret = 0; 278 } else 279 ret = BE_ERR_NOMEM; 280 cleanup: 281 if (be_mounted == B_TRUE) 282 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE); 283 be_free_list(be_nodes); 284 done: 285 free(mountpoint); 286 free(bt.obe_name); 287 free(bt.obe_root_ds); 288 free(bt.obe_zpool); 289 free(bt.obe_snap_name); 290 free(bt.obe_altroot); 291 be_zfs_fini(); 292 return (ret); 293 } 294 295 /* 296 * Function: be_max_avail 297 * Description: Returns the available size for the zfs dataset passed in. 298 * Parameters: 299 * dataset - The dataset we want to get the available space for. 300 * ret - The available size will be returned in this. 301 * Returns: 302 * The error returned by the zfs get property function. 303 * Scope: 304 * Public 305 */ 306 int 307 be_max_avail(char *dataset, uint64_t *ret) 308 { 309 zfs_handle_t *zhp; 310 int err = 0; 311 312 /* Initialize libzfs handle */ 313 if (!be_zfs_init()) 314 return (BE_ERR_INIT); 315 316 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET); 317 if (zhp == NULL) { 318 /* 319 * The zfs_open failed return an error 320 */ 321 err = zfs_err_to_be_err(g_zfs); 322 } else { 323 err = be_maxsize_avail(zhp, ret); 324 } 325 ZFS_CLOSE(zhp); 326 be_zfs_fini(); 327 return (err); 328 } 329 330 /* 331 * Function: libbe_print_errors 332 * Description: Turns on/off error output for the library. 333 * Parameter: 334 * set_do_print - Boolean that turns library error 335 * printing on or off. 336 * Returns: 337 * None 338 * Scope: 339 * Public; 340 */ 341 void 342 libbe_print_errors(boolean_t set_do_print) 343 { 344 do_print = set_do_print; 345 } 346 347 /* ******************************************************************** */ 348 /* Semi-Private Functions */ 349 /* ******************************************************************** */ 350 351 /* 352 * Function: be_zfs_init 353 * Description: Initializes the libary global libzfs handle. 354 * Parameters: 355 * None 356 * Returns: 357 * B_TRUE - Success 358 * B_FALSE - Failure 359 * Scope: 360 * Semi-private (library wide use only) 361 */ 362 boolean_t 363 be_zfs_init(void) 364 { 365 be_zfs_fini(); 366 367 if ((g_zfs = libzfs_init()) == NULL) { 368 be_print_err(gettext("be_zfs_init: failed to initialize ZFS " 369 "library\n")); 370 return (B_FALSE); 371 } 372 373 return (B_TRUE); 374 } 375 376 /* 377 * Function: be_zfs_fini 378 * Description: Closes the library global libzfs handle if it currently open. 379 * Parameter: 380 * None 381 * Returns: 382 * None 383 * Scope: 384 * Semi-private (library wide use only) 385 */ 386 void 387 be_zfs_fini(void) 388 { 389 if (g_zfs) 390 libzfs_fini(g_zfs); 391 392 g_zfs = NULL; 393 } 394 395 /* 396 * Function: be_get_defaults 397 * Description: Open defaults and gets be default paramets 398 * Parameters: 399 * defaults - be defaults struct 400 * Returns: 401 * None 402 * Scope: 403 * Semi-private (library wide use only) 404 */ 405 void 406 be_get_defaults(struct be_defaults *defaults) 407 { 408 void *defp; 409 410 defaults->be_deflt_grub = B_FALSE; 411 defaults->be_deflt_rpool_container = B_FALSE; 412 defaults->be_deflt_bename_starts_with[0] = '\0'; 413 414 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) { 415 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp); 416 if (res != NULL && res[0] != '\0') { 417 (void) strlcpy(defaults->be_deflt_bename_starts_with, 418 res, ZFS_MAX_DATASET_NAME_LEN); 419 defaults->be_deflt_rpool_container = B_TRUE; 420 } 421 if (be_is_isa("i386")) { 422 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp); 423 if (res != NULL && res[0] != '\0') { 424 if (strcasecmp(res, "true") == 0) 425 defaults->be_deflt_grub = B_TRUE; 426 } 427 } 428 defclose_r(defp); 429 } 430 } 431 432 /* 433 * Function: be_make_root_ds 434 * Description: Generate string for BE's root dataset given the pool 435 * it lives in and the BE name. 436 * Parameters: 437 * zpool - pointer zpool name. 438 * be_name - pointer to BE name. 439 * be_root_ds - pointer to buffer to return BE root dataset in. 440 * be_root_ds_size - size of be_root_ds 441 * Returns: 442 * None 443 * Scope: 444 * Semi-private (library wide use only) 445 */ 446 void 447 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds, 448 int be_root_ds_size) 449 { 450 struct be_defaults be_defaults; 451 be_get_defaults(&be_defaults); 452 char *root_ds = NULL; 453 454 if (getzoneid() == GLOBAL_ZONEID) { 455 if (be_defaults.be_deflt_rpool_container) { 456 (void) snprintf(be_root_ds, be_root_ds_size, 457 "%s/%s", zpool, be_name); 458 } else { 459 (void) snprintf(be_root_ds, be_root_ds_size, 460 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name); 461 } 462 } else { 463 /* 464 * In non-global zone we can use path from mounted root dataset 465 * to generate BE's root dataset string. 466 */ 467 if ((root_ds = be_get_ds_from_dir("/")) != NULL) { 468 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s", 469 dirname(root_ds), be_name); 470 } else { 471 be_print_err(gettext("be_make_root_ds: zone root " 472 "dataset is not mounted\n")); 473 return; 474 } 475 } 476 } 477 478 /* 479 * Function: be_make_container_ds 480 * Description: Generate string for the BE container dataset given a pool name. 481 * Parameters: 482 * zpool - pointer zpool name. 483 * container_ds - pointer to buffer to return BE container 484 * dataset in. 485 * container_ds_size - size of container_ds 486 * Returns: 487 * None 488 * Scope: 489 * Semi-private (library wide use only) 490 */ 491 void 492 be_make_container_ds(const char *zpool, char *container_ds, 493 int container_ds_size) 494 { 495 struct be_defaults be_defaults; 496 be_get_defaults(&be_defaults); 497 char *root_ds = NULL; 498 499 if (getzoneid() == GLOBAL_ZONEID) { 500 if (be_defaults.be_deflt_rpool_container) { 501 (void) snprintf(container_ds, container_ds_size, 502 "%s", zpool); 503 } else { 504 (void) snprintf(container_ds, container_ds_size, 505 "%s/%s", zpool, BE_CONTAINER_DS_NAME); 506 } 507 } else { 508 if ((root_ds = be_get_ds_from_dir("/")) != NULL) { 509 (void) strlcpy(container_ds, dirname(root_ds), 510 container_ds_size); 511 } else { 512 be_print_err(gettext("be_make_container_ds: zone root " 513 "dataset is not mounted\n")); 514 return; 515 } 516 } 517 } 518 519 /* 520 * Function: be_make_root_container_ds 521 * Description: Generate string for the BE root container dataset given a pool 522 * name. 523 * Parameters: 524 * zpool - pointer zpool name. 525 * container_ds - pointer to buffer in which to return result 526 * container_ds_size - size of container_ds 527 * Returns: 528 * None 529 * Scope: 530 * Semi-private (library wide use only) 531 */ 532 void 533 be_make_root_container_ds(const char *zpool, char *container_ds, 534 int container_ds_size) 535 { 536 char *root; 537 538 be_make_container_ds(zpool, container_ds, container_ds_size); 539 540 /* If the container DS ends with /ROOT, remove it. */ 541 542 if ((root = strrchr(container_ds, '/')) != NULL && 543 strcmp(root + 1, BE_CONTAINER_DS_NAME) == 0) { 544 *root = '\0'; 545 } 546 } 547 548 /* 549 * Function: be_make_name_from_ds 550 * Description: This function takes a dataset name and strips off the 551 * BE container dataset portion from the beginning. The 552 * returned name is allocated in heap storage, so the caller 553 * is responsible for freeing it. 554 * Parameters: 555 * dataset - dataset to get name from. 556 * rc_loc - dataset underwhich the root container dataset lives. 557 * Returns: 558 * name of dataset relative to BE container dataset. 559 * NULL if dataset is not under a BE root dataset. 560 * Scope: 561 * Semi-primate (library wide use only) 562 */ 563 char * 564 be_make_name_from_ds(const char *dataset, char *rc_loc) 565 { 566 char ds[ZFS_MAX_DATASET_NAME_LEN]; 567 char *tok = NULL; 568 char *name = NULL; 569 struct be_defaults be_defaults; 570 int rlen = strlen(rc_loc); 571 572 be_get_defaults(&be_defaults); 573 574 /* 575 * First token is the location of where the root container dataset 576 * lives; it must match rc_loc. 577 */ 578 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/') 579 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds)); 580 else 581 return (NULL); 582 583 if (be_defaults.be_deflt_rpool_container) { 584 if ((name = strdup(ds)) == NULL) { 585 be_print_err(gettext("be_make_name_from_ds: " 586 "memory allocation failed\n")); 587 return (NULL); 588 } 589 } else { 590 /* Second token must be BE container dataset name */ 591 if ((tok = strtok(ds, "/")) == NULL || 592 strcmp(tok, BE_CONTAINER_DS_NAME) != 0) 593 return (NULL); 594 595 /* Return the remaining token if one exists */ 596 if ((tok = strtok(NULL, "")) == NULL) 597 return (NULL); 598 599 if ((name = strdup(tok)) == NULL) { 600 be_print_err(gettext("be_make_name_from_ds: " 601 "memory allocation failed\n")); 602 return (NULL); 603 } 604 } 605 606 return (name); 607 } 608 609 /* 610 * Function: be_maxsize_avail 611 * Description: Returns the available size for the zfs handle passed in. 612 * Parameters: 613 * zhp - A pointer to the open zfs handle. 614 * ret - The available size will be returned in this. 615 * Returns: 616 * The error returned by the zfs get property function. 617 * Scope: 618 * Semi-private (library wide use only) 619 */ 620 int 621 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret) 622 { 623 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE))); 624 } 625 626 /* 627 * Function: be_append_menu 628 * Description: Appends an entry for a BE into the menu.lst. 629 * Parameters: 630 * be_name - pointer to name of BE to add boot menu entry for. 631 * be_root_pool - pointer to name of pool BE lives in. 632 * boot_pool - Used if the pool containing the grub menu is 633 * different than the one contaiing the BE. This 634 * will normally be NULL. 635 * be_orig_root_ds - The root dataset for the BE. This is 636 * used to check to see if an entry already exists 637 * for this BE. 638 * description - pointer to description of BE to be added in 639 * the title line for this BEs entry. 640 * Returns: 641 * BE_SUCCESS - Success 642 * be_errno_t - Failure 643 * Scope: 644 * Semi-private (library wide use only) 645 */ 646 int 647 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool, 648 char *be_orig_root_ds, char *description) 649 { 650 zfs_handle_t *zhp = NULL; 651 char menu_file[MAXPATHLEN]; 652 char be_root_ds[MAXPATHLEN]; 653 char line[BUFSIZ]; 654 char temp_line[BUFSIZ]; 655 char title[MAXPATHLEN]; 656 char *entries[BUFSIZ]; 657 char *tmp_entries[BUFSIZ]; 658 char *pool_mntpnt = NULL; 659 char *ptmp_mntpnt = NULL; 660 char *orig_mntpnt = NULL; 661 boolean_t found_be = B_FALSE; 662 boolean_t found_orig_be = B_FALSE; 663 boolean_t found_title = B_FALSE; 664 boolean_t pool_mounted = B_FALSE; 665 boolean_t collect_lines = B_FALSE; 666 FILE *menu_fp = NULL; 667 int err = 0, ret = BE_SUCCESS; 668 int i, num_tmp_lines = 0, num_lines = 0; 669 670 if (be_name == NULL || be_root_pool == NULL) 671 return (BE_ERR_INVAL); 672 673 if (boot_pool == NULL) 674 boot_pool = be_root_pool; 675 676 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 677 be_print_err(gettext("be_append_menu: failed to open " 678 "pool dataset for %s: %s\n"), be_root_pool, 679 libzfs_error_description(g_zfs)); 680 return (zfs_err_to_be_err(g_zfs)); 681 } 682 683 /* 684 * Check to see if the pool's dataset is mounted. If it isn't we'll 685 * attempt to mount it. 686 */ 687 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 688 &pool_mounted)) != BE_SUCCESS) { 689 be_print_err(gettext("be_append_menu: pool dataset " 690 "(%s) could not be mounted\n"), be_root_pool); 691 ZFS_CLOSE(zhp); 692 return (ret); 693 } 694 695 /* 696 * Get the mountpoint for the root pool dataset. 697 */ 698 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 699 be_print_err(gettext("be_append_menu: pool " 700 "dataset (%s) is not mounted. Can't set " 701 "the default BE in the grub menu.\n"), be_root_pool); 702 ret = BE_ERR_NO_MENU; 703 goto cleanup; 704 } 705 706 /* 707 * Check to see if this system supports grub 708 */ 709 if (be_has_grub()) { 710 (void) snprintf(menu_file, sizeof (menu_file), 711 "%s%s", pool_mntpnt, BE_GRUB_MENU); 712 } else { 713 (void) snprintf(menu_file, sizeof (menu_file), 714 "%s%s", pool_mntpnt, BE_SPARC_MENU); 715 } 716 717 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds)); 718 719 /* 720 * Iterate through menu first to make sure the BE doesn't already 721 * have an entry in the menu. 722 * 723 * Additionally while iterating through the menu, if we have an 724 * original root dataset for a BE we're cloning from, we need to keep 725 * track of that BE's menu entry. We will then use the lines from 726 * that entry to create the entry for the new BE. 727 */ 728 if ((ret = be_open_menu(be_root_pool, menu_file, 729 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) { 730 goto cleanup; 731 } else if (menu_fp == NULL) { 732 ret = BE_ERR_NO_MENU; 733 goto cleanup; 734 } 735 736 free(pool_mntpnt); 737 pool_mntpnt = NULL; 738 739 while (fgets(line, BUFSIZ, menu_fp)) { 740 char *tok = NULL; 741 742 (void) strlcpy(temp_line, line, BUFSIZ); 743 tok = strtok(line, BE_WHITE_SPACE); 744 745 if (tok == NULL || tok[0] == '#') { 746 continue; 747 } else if (strcmp(tok, "title") == 0) { 748 collect_lines = B_FALSE; 749 if ((tok = strtok(NULL, "\n")) == NULL) 750 (void) strlcpy(title, "", sizeof (title)); 751 else 752 (void) strlcpy(title, tok, sizeof (title)); 753 found_title = B_TRUE; 754 755 if (num_tmp_lines != 0) { 756 for (i = 0; i < num_tmp_lines; i++) { 757 free(tmp_entries[i]); 758 tmp_entries[i] = NULL; 759 } 760 num_tmp_lines = 0; 761 } 762 } else if (strcmp(tok, "bootfs") == 0) { 763 char *bootfs = strtok(NULL, BE_WHITE_SPACE); 764 found_title = B_FALSE; 765 if (bootfs == NULL) 766 continue; 767 768 if (strcmp(bootfs, be_root_ds) == 0) { 769 found_be = B_TRUE; 770 break; 771 } 772 773 if (be_orig_root_ds != NULL && 774 strcmp(bootfs, be_orig_root_ds) == 0 && 775 !found_orig_be) { 776 char str[BUFSIZ]; 777 found_orig_be = B_TRUE; 778 num_lines = 0; 779 /* 780 * Store the new title line 781 */ 782 (void) snprintf(str, BUFSIZ, "title %s\n", 783 description ? description : be_name); 784 entries[num_lines] = strdup(str); 785 num_lines++; 786 /* 787 * If there are any lines between the title 788 * and the bootfs line store these. Also 789 * free the temporary lines. 790 */ 791 for (i = 0; i < num_tmp_lines; i++) { 792 entries[num_lines] = tmp_entries[i]; 793 tmp_entries[i] = NULL; 794 num_lines++; 795 } 796 num_tmp_lines = 0; 797 /* 798 * Store the new bootfs line. 799 */ 800 (void) snprintf(str, BUFSIZ, "bootfs %s\n", 801 be_root_ds); 802 entries[num_lines] = strdup(str); 803 num_lines++; 804 collect_lines = B_TRUE; 805 } 806 } else if (found_orig_be && collect_lines) { 807 /* 808 * get the rest of the lines for the original BE and 809 * store them. 810 */ 811 if (strstr(line, BE_GRUB_COMMENT) != NULL || 812 strstr(line, "BOOTADM") != NULL) 813 continue; 814 if (strcmp(tok, "splashimage") == 0) { 815 entries[num_lines] = 816 strdup("splashimage " 817 "/boot/splashimage.xpm\n"); 818 } else { 819 entries[num_lines] = strdup(temp_line); 820 } 821 num_lines++; 822 } else if (found_title && !found_orig_be) { 823 tmp_entries[num_tmp_lines] = strdup(temp_line); 824 num_tmp_lines++; 825 } 826 } 827 828 (void) fclose(menu_fp); 829 830 if (found_be) { 831 /* 832 * If an entry for this BE was already in the menu, then if 833 * that entry's title matches what we would have put in 834 * return success. Otherwise return failure. 835 */ 836 char *new_title = description ? description : be_name; 837 838 if (strcmp(title, new_title) == 0) { 839 ret = BE_SUCCESS; 840 goto cleanup; 841 } else { 842 if (be_remove_menu(be_name, be_root_pool, 843 boot_pool) != BE_SUCCESS) { 844 be_print_err(gettext("be_append_menu: " 845 "Failed to remove existing unusable " 846 "entry '%s' in boot menu.\n"), be_name); 847 ret = BE_ERR_BE_EXISTS; 848 goto cleanup; 849 } 850 } 851 } 852 853 /* Append BE entry to the end of the file */ 854 menu_fp = fopen(menu_file, "a+"); 855 err = errno; 856 if (menu_fp == NULL) { 857 be_print_err(gettext("be_append_menu: failed " 858 "to open menu.lst file %s\n"), menu_file); 859 ret = errno_to_be_err(err); 860 goto cleanup; 861 } 862 863 if (found_orig_be) { 864 /* 865 * write out all the stored lines 866 */ 867 for (i = 0; i < num_lines; i++) { 868 (void) fprintf(menu_fp, "%s", entries[i]); 869 free(entries[i]); 870 } 871 num_lines = 0; 872 873 /* 874 * Check to see if this system supports grub 875 */ 876 if (be_has_grub()) 877 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT); 878 ret = BE_SUCCESS; 879 } else { 880 (void) fprintf(menu_fp, "title %s\n", 881 description ? description : be_name); 882 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds); 883 884 /* 885 * Check to see if this system supports grub 886 */ 887 if (be_has_grub()) { 888 (void) fprintf(menu_fp, "kernel$ " 889 "/platform/i86pc/kernel/$ISADIR/unix -B " 890 "$ZFS-BOOTFS\n"); 891 (void) fprintf(menu_fp, "module$ " 892 "/platform/i86pc/$ISADIR/boot_archive\n"); 893 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT); 894 } 895 ret = BE_SUCCESS; 896 } 897 (void) fclose(menu_fp); 898 cleanup: 899 if (pool_mounted) { 900 int err = BE_SUCCESS; 901 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 902 if (ret == BE_SUCCESS) 903 ret = err; 904 free(orig_mntpnt); 905 free(ptmp_mntpnt); 906 } 907 ZFS_CLOSE(zhp); 908 if (num_tmp_lines > 0) { 909 for (i = 0; i < num_tmp_lines; i++) { 910 free(tmp_entries[i]); 911 tmp_entries[i] = NULL; 912 } 913 } 914 if (num_lines > 0) { 915 for (i = 0; i < num_lines; i++) { 916 free(entries[i]); 917 entries[i] = NULL; 918 } 919 } 920 return (ret); 921 } 922 923 /* 924 * Function: be_remove_menu 925 * Description: Removes a BE's entry from a menu.lst file. 926 * Parameters: 927 * be_name - the name of BE whose entry is to be removed from 928 * the menu.lst file. 929 * be_root_pool - the pool that be_name lives in. 930 * boot_pool - the pool where the BE is, if different than 931 * the pool containing the boot menu. If this is 932 * NULL it will be set to be_root_pool. 933 * Returns: 934 * BE_SUCCESS - Success 935 * be_errno_t - Failure 936 * Scope: 937 * Semi-private (library wide use only) 938 */ 939 int 940 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool) 941 { 942 zfs_handle_t *zhp = NULL; 943 char be_root_ds[MAXPATHLEN]; 944 char **buffer = NULL; 945 char menu_buf[BUFSIZ]; 946 char menu[MAXPATHLEN]; 947 char *pool_mntpnt = NULL; 948 char *ptmp_mntpnt = NULL; 949 char *orig_mntpnt = NULL; 950 char *tmp_menu = NULL; 951 FILE *menu_fp = NULL; 952 FILE *tmp_menu_fp = NULL; 953 struct stat sb; 954 int ret = BE_SUCCESS; 955 int i; 956 int fd; 957 int err = 0; 958 int nlines = 0; 959 int default_entry = 0; 960 int entry_cnt = 0; 961 int entry_del = 0; 962 int num_entry_del = 0; 963 int tmp_menu_len = 0; 964 boolean_t write = B_TRUE; 965 boolean_t do_buffer = B_FALSE; 966 boolean_t pool_mounted = B_FALSE; 967 968 if (boot_pool == NULL) 969 boot_pool = be_root_pool; 970 971 /* Get name of BE's root dataset */ 972 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds)); 973 974 /* Get handle to pool dataset */ 975 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 976 be_print_err(gettext("be_remove_menu: " 977 "failed to open pool dataset for %s: %s"), 978 be_root_pool, libzfs_error_description(g_zfs)); 979 return (zfs_err_to_be_err(g_zfs)); 980 } 981 982 /* 983 * Check to see if the pool's dataset is mounted. If it isn't we'll 984 * attempt to mount it. 985 */ 986 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 987 &pool_mounted)) != BE_SUCCESS) { 988 be_print_err(gettext("be_remove_menu: pool dataset " 989 "(%s) could not be mounted\n"), be_root_pool); 990 ZFS_CLOSE(zhp); 991 return (ret); 992 } 993 994 /* 995 * Get the mountpoint for the root pool dataset. 996 */ 997 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 998 be_print_err(gettext("be_remove_menu: pool " 999 "dataset (%s) is not mounted. Can't set " 1000 "the default BE in the grub menu.\n"), be_root_pool); 1001 ret = BE_ERR_NO_MENU; 1002 goto cleanup; 1003 } 1004 1005 /* Get path to boot menu */ 1006 (void) strlcpy(menu, pool_mntpnt, sizeof (menu)); 1007 1008 /* 1009 * Check to see if this system supports grub 1010 */ 1011 if (be_has_grub()) 1012 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu)); 1013 else 1014 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu)); 1015 1016 /* Get handle to boot menu file */ 1017 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r", 1018 B_TRUE)) != BE_SUCCESS) { 1019 goto cleanup; 1020 } else if (menu_fp == NULL) { 1021 ret = BE_ERR_NO_MENU; 1022 goto cleanup; 1023 } 1024 1025 free(pool_mntpnt); 1026 pool_mntpnt = NULL; 1027 1028 /* Grab the stats of the original menu file */ 1029 if (stat(menu, &sb) != 0) { 1030 err = errno; 1031 be_print_err(gettext("be_remove_menu: " 1032 "failed to stat file %s: %s\n"), menu, strerror(err)); 1033 ret = errno_to_be_err(err); 1034 goto cleanup; 1035 } 1036 1037 /* Create a tmp file for the modified menu.lst */ 1038 tmp_menu_len = strlen(menu) + 7; 1039 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) { 1040 be_print_err(gettext("be_remove_menu: malloc failed\n")); 1041 ret = BE_ERR_NOMEM; 1042 goto cleanup; 1043 } 1044 (void) memset(tmp_menu, 0, tmp_menu_len); 1045 (void) strlcpy(tmp_menu, menu, tmp_menu_len); 1046 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len); 1047 if ((fd = mkstemp(tmp_menu)) == -1) { 1048 err = errno; 1049 be_print_err(gettext("be_remove_menu: mkstemp failed\n")); 1050 ret = errno_to_be_err(err); 1051 free(tmp_menu); 1052 tmp_menu = NULL; 1053 goto cleanup; 1054 } 1055 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) { 1056 err = errno; 1057 be_print_err(gettext("be_remove_menu: " 1058 "could not open tmp file for write: %s\n"), strerror(err)); 1059 (void) close(fd); 1060 ret = errno_to_be_err(err); 1061 goto cleanup; 1062 } 1063 1064 while (fgets(menu_buf, BUFSIZ, menu_fp)) { 1065 char tline [BUFSIZ]; 1066 char *tok = NULL; 1067 1068 (void) strlcpy(tline, menu_buf, sizeof (tline)); 1069 1070 /* Tokenize line */ 1071 tok = strtok(tline, BE_WHITE_SPACE); 1072 1073 if (tok == NULL || tok[0] == '#') { 1074 /* Found empty line or comment line */ 1075 if (do_buffer) { 1076 /* Buffer this line */ 1077 if ((buffer = (char **)realloc(buffer, 1078 sizeof (char *)*(nlines + 1))) == NULL) { 1079 ret = BE_ERR_NOMEM; 1080 goto cleanup; 1081 } 1082 if ((buffer[nlines++] = strdup(menu_buf)) 1083 == NULL) { 1084 ret = BE_ERR_NOMEM; 1085 goto cleanup; 1086 } 1087 1088 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT, 1089 strlen(BE_GRUB_COMMENT)) != 0) { 1090 /* Write this line out */ 1091 (void) fputs(menu_buf, tmp_menu_fp); 1092 } 1093 } else if (strcmp(tok, "default") == 0) { 1094 /* 1095 * Record what 'default' is set to because we might 1096 * need to adjust this upon deleting an entry. 1097 */ 1098 tok = strtok(NULL, BE_WHITE_SPACE); 1099 1100 if (tok != NULL) { 1101 default_entry = atoi(tok); 1102 } 1103 1104 (void) fputs(menu_buf, tmp_menu_fp); 1105 } else if (strcmp(tok, "title") == 0) { 1106 /* 1107 * If we've reached a 'title' line and do_buffer is 1108 * is true, that means we've just buffered an entire 1109 * entry without finding a 'bootfs' directive. We 1110 * need to write that entry out and keep searching. 1111 */ 1112 if (do_buffer) { 1113 for (i = 0; i < nlines; i++) { 1114 (void) fputs(buffer[i], tmp_menu_fp); 1115 free(buffer[i]); 1116 } 1117 free(buffer); 1118 buffer = NULL; 1119 nlines = 0; 1120 } 1121 1122 /* 1123 * Turn writing off and buffering on, and increment 1124 * our entry counter. 1125 */ 1126 write = B_FALSE; 1127 do_buffer = B_TRUE; 1128 entry_cnt++; 1129 1130 /* Buffer this 'title' line */ 1131 if ((buffer = (char **)realloc(buffer, 1132 sizeof (char *)*(nlines + 1))) == NULL) { 1133 ret = BE_ERR_NOMEM; 1134 goto cleanup; 1135 } 1136 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) { 1137 ret = BE_ERR_NOMEM; 1138 goto cleanup; 1139 } 1140 1141 } else if (strcmp(tok, "bootfs") == 0) { 1142 char *bootfs = NULL; 1143 1144 /* 1145 * Found a 'bootfs' line. See if it matches the 1146 * BE we're looking for. 1147 */ 1148 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL || 1149 strcmp(bootfs, be_root_ds) != 0) { 1150 /* 1151 * Either there's nothing after the 'bootfs' 1152 * or this is not the BE we're looking for, 1153 * write out the line(s) we've buffered since 1154 * finding the title. 1155 */ 1156 for (i = 0; i < nlines; i++) { 1157 (void) fputs(buffer[i], tmp_menu_fp); 1158 free(buffer[i]); 1159 } 1160 free(buffer); 1161 buffer = NULL; 1162 nlines = 0; 1163 1164 /* 1165 * Turn writing back on, and turn off buffering 1166 * since this isn't the entry we're looking 1167 * for. 1168 */ 1169 write = B_TRUE; 1170 do_buffer = B_FALSE; 1171 1172 /* Write this 'bootfs' line out. */ 1173 (void) fputs(menu_buf, tmp_menu_fp); 1174 } else { 1175 /* 1176 * Found the entry we're looking for. 1177 * Record its entry number, increment the 1178 * number of entries we've deleted, and turn 1179 * writing off. Also, throw away the lines 1180 * we've buffered for this entry so far, we 1181 * don't need them. 1182 */ 1183 entry_del = entry_cnt - 1; 1184 num_entry_del++; 1185 write = B_FALSE; 1186 do_buffer = B_FALSE; 1187 1188 for (i = 0; i < nlines; i++) { 1189 free(buffer[i]); 1190 } 1191 free(buffer); 1192 buffer = NULL; 1193 nlines = 0; 1194 } 1195 } else { 1196 if (do_buffer) { 1197 /* Buffer this line */ 1198 if ((buffer = (char **)realloc(buffer, 1199 sizeof (char *)*(nlines + 1))) == NULL) { 1200 ret = BE_ERR_NOMEM; 1201 goto cleanup; 1202 } 1203 if ((buffer[nlines++] = strdup(menu_buf)) 1204 == NULL) { 1205 ret = BE_ERR_NOMEM; 1206 goto cleanup; 1207 } 1208 } else if (write) { 1209 /* Write this line out */ 1210 (void) fputs(menu_buf, tmp_menu_fp); 1211 } 1212 } 1213 } 1214 1215 (void) fclose(menu_fp); 1216 menu_fp = NULL; 1217 (void) fclose(tmp_menu_fp); 1218 tmp_menu_fp = NULL; 1219 1220 /* Copy the modified menu.lst into place */ 1221 if (rename(tmp_menu, menu) != 0) { 1222 err = errno; 1223 be_print_err(gettext("be_remove_menu: " 1224 "failed to rename file %s to %s: %s\n"), 1225 tmp_menu, menu, strerror(err)); 1226 ret = errno_to_be_err(err); 1227 goto cleanup; 1228 } 1229 free(tmp_menu); 1230 tmp_menu = NULL; 1231 1232 /* 1233 * If we've removed an entry, see if we need to 1234 * adjust the default value in the menu.lst. If the 1235 * entry we've deleted comes before the default entry 1236 * we need to adjust the default value accordingly. 1237 * 1238 * be_has_grub is used here to check to see if this system 1239 * supports grub. 1240 */ 1241 if (be_has_grub() && num_entry_del > 0) { 1242 if (entry_del <= default_entry) { 1243 default_entry = default_entry - num_entry_del; 1244 if (default_entry < 0) 1245 default_entry = 0; 1246 1247 /* 1248 * Adjust the default value by rewriting the 1249 * menu.lst file. This may be overkill, but to 1250 * preserve the location of the 'default' entry 1251 * in the file, we need to do this. 1252 */ 1253 1254 /* Get handle to boot menu file */ 1255 if ((menu_fp = fopen(menu, "r")) == NULL) { 1256 err = errno; 1257 be_print_err(gettext("be_remove_menu: " 1258 "failed to open menu.lst (%s): %s\n"), 1259 menu, strerror(err)); 1260 ret = errno_to_be_err(err); 1261 goto cleanup; 1262 } 1263 1264 /* Create a tmp file for the modified menu.lst */ 1265 tmp_menu_len = strlen(menu) + 7; 1266 if ((tmp_menu = (char *)malloc(tmp_menu_len)) 1267 == NULL) { 1268 be_print_err(gettext("be_remove_menu: " 1269 "malloc failed\n")); 1270 ret = BE_ERR_NOMEM; 1271 goto cleanup; 1272 } 1273 (void) memset(tmp_menu, 0, tmp_menu_len); 1274 (void) strlcpy(tmp_menu, menu, tmp_menu_len); 1275 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len); 1276 if ((fd = mkstemp(tmp_menu)) == -1) { 1277 err = errno; 1278 be_print_err(gettext("be_remove_menu: " 1279 "mkstemp failed: %s\n"), strerror(err)); 1280 ret = errno_to_be_err(err); 1281 free(tmp_menu); 1282 tmp_menu = NULL; 1283 goto cleanup; 1284 } 1285 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) { 1286 err = errno; 1287 be_print_err(gettext("be_remove_menu: " 1288 "could not open tmp file for write: %s\n"), 1289 strerror(err)); 1290 (void) close(fd); 1291 ret = errno_to_be_err(err); 1292 goto cleanup; 1293 } 1294 1295 while (fgets(menu_buf, BUFSIZ, menu_fp)) { 1296 char tline [BUFSIZ]; 1297 char *tok = NULL; 1298 1299 (void) strlcpy(tline, menu_buf, sizeof (tline)); 1300 1301 /* Tokenize line */ 1302 tok = strtok(tline, BE_WHITE_SPACE); 1303 1304 if (tok == NULL) { 1305 /* Found empty line, write it out */ 1306 (void) fputs(menu_buf, tmp_menu_fp); 1307 } else if (strcmp(tok, "default") == 0) { 1308 /* Found the default line, adjust it */ 1309 (void) snprintf(tline, sizeof (tline), 1310 "default %d\n", default_entry); 1311 1312 (void) fputs(tline, tmp_menu_fp); 1313 } else { 1314 /* Pass through all other lines */ 1315 (void) fputs(menu_buf, tmp_menu_fp); 1316 } 1317 } 1318 1319 (void) fclose(menu_fp); 1320 menu_fp = NULL; 1321 (void) fclose(tmp_menu_fp); 1322 tmp_menu_fp = NULL; 1323 1324 /* Copy the modified menu.lst into place */ 1325 if (rename(tmp_menu, menu) != 0) { 1326 err = errno; 1327 be_print_err(gettext("be_remove_menu: " 1328 "failed to rename file %s to %s: %s\n"), 1329 tmp_menu, menu, strerror(err)); 1330 ret = errno_to_be_err(err); 1331 goto cleanup; 1332 } 1333 1334 free(tmp_menu); 1335 tmp_menu = NULL; 1336 } 1337 } 1338 1339 /* Set the perms and ownership of the updated file */ 1340 if (chmod(menu, sb.st_mode) != 0) { 1341 err = errno; 1342 be_print_err(gettext("be_remove_menu: " 1343 "failed to chmod %s: %s\n"), menu, strerror(err)); 1344 ret = errno_to_be_err(err); 1345 goto cleanup; 1346 } 1347 if (chown(menu, sb.st_uid, sb.st_gid) != 0) { 1348 err = errno; 1349 be_print_err(gettext("be_remove_menu: " 1350 "failed to chown %s: %s\n"), menu, strerror(err)); 1351 ret = errno_to_be_err(err); 1352 goto cleanup; 1353 } 1354 1355 cleanup: 1356 if (pool_mounted) { 1357 int err = BE_SUCCESS; 1358 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1359 if (ret == BE_SUCCESS) 1360 ret = err; 1361 free(orig_mntpnt); 1362 free(ptmp_mntpnt); 1363 } 1364 ZFS_CLOSE(zhp); 1365 1366 free(buffer); 1367 if (menu_fp != NULL) 1368 (void) fclose(menu_fp); 1369 if (tmp_menu_fp != NULL) 1370 (void) fclose(tmp_menu_fp); 1371 if (tmp_menu != NULL) { 1372 (void) unlink(tmp_menu); 1373 free(tmp_menu); 1374 } 1375 1376 return (ret); 1377 } 1378 1379 /* 1380 * Function: be_default_grub_bootfs 1381 * Description: This function returns the dataset in the default entry of 1382 * the grub menu. If no default entry is found with a valid bootfs 1383 * entry NULL is returned. 1384 * Parameters: 1385 * be_root_pool - This is the name of the root pool where the 1386 * grub menu can be found. 1387 * def_bootfs - This is used to pass back the bootfs string. On 1388 * error NULL is returned here. 1389 * Returns: 1390 * Success - BE_SUCCESS is returned. 1391 * Failure - a be_errno_t is returned. 1392 * Scope: 1393 * Semi-private (library wide use only) 1394 */ 1395 int 1396 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs) 1397 { 1398 zfs_handle_t *zhp = NULL; 1399 char grub_file[MAXPATHLEN]; 1400 FILE *menu_fp; 1401 char line[BUFSIZ]; 1402 char *pool_mntpnt = NULL; 1403 char *ptmp_mntpnt = NULL; 1404 char *orig_mntpnt = NULL; 1405 int default_entry = 0, entries = 0; 1406 int found_default = 0; 1407 int ret = BE_SUCCESS; 1408 boolean_t pool_mounted = B_FALSE; 1409 1410 errno = 0; 1411 1412 /* 1413 * Check to see if this system supports grub 1414 */ 1415 if (!be_has_grub()) { 1416 be_print_err(gettext("be_default_grub_bootfs: operation " 1417 "not supported on this architecture\n")); 1418 return (BE_ERR_NOTSUP); 1419 } 1420 1421 *def_bootfs = NULL; 1422 1423 /* Get handle to pool dataset */ 1424 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1425 be_print_err(gettext("be_default_grub_bootfs: " 1426 "failed to open pool dataset for %s: %s"), 1427 be_root_pool, libzfs_error_description(g_zfs)); 1428 return (zfs_err_to_be_err(g_zfs)); 1429 } 1430 1431 /* 1432 * Check to see if the pool's dataset is mounted. If it isn't we'll 1433 * attempt to mount it. 1434 */ 1435 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1436 &pool_mounted)) != BE_SUCCESS) { 1437 be_print_err(gettext("be_default_grub_bootfs: pool dataset " 1438 "(%s) could not be mounted\n"), be_root_pool); 1439 ZFS_CLOSE(zhp); 1440 return (ret); 1441 } 1442 1443 /* 1444 * Get the mountpoint for the root pool dataset. 1445 */ 1446 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1447 be_print_err(gettext("be_default_grub_bootfs: failed " 1448 "to get mount point for the root pool. Can't set " 1449 "the default BE in the grub menu.\n")); 1450 ret = BE_ERR_NO_MENU; 1451 goto cleanup; 1452 } 1453 1454 (void) snprintf(grub_file, MAXPATHLEN, "%s%s", 1455 pool_mntpnt, BE_GRUB_MENU); 1456 1457 if ((ret = be_open_menu((char *)be_root_pool, grub_file, 1458 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) { 1459 goto cleanup; 1460 } else if (menu_fp == NULL) { 1461 ret = BE_ERR_NO_MENU; 1462 goto cleanup; 1463 } 1464 1465 free(pool_mntpnt); 1466 pool_mntpnt = NULL; 1467 1468 while (fgets(line, BUFSIZ, menu_fp)) { 1469 char *tok = strtok(line, BE_WHITE_SPACE); 1470 1471 if (tok != NULL && tok[0] != '#') { 1472 if (!found_default) { 1473 if (strcmp(tok, "default") == 0) { 1474 tok = strtok(NULL, BE_WHITE_SPACE); 1475 if (tok != NULL) { 1476 default_entry = atoi(tok); 1477 rewind(menu_fp); 1478 found_default = 1; 1479 } 1480 } 1481 continue; 1482 } 1483 if (strcmp(tok, "title") == 0) { 1484 entries++; 1485 } else if (default_entry == entries - 1) { 1486 if (strcmp(tok, "bootfs") == 0) { 1487 tok = strtok(NULL, BE_WHITE_SPACE); 1488 (void) fclose(menu_fp); 1489 1490 if (tok == NULL) { 1491 ret = BE_SUCCESS; 1492 goto cleanup; 1493 } 1494 1495 if ((*def_bootfs = strdup(tok)) != 1496 NULL) { 1497 ret = BE_SUCCESS; 1498 goto cleanup; 1499 } 1500 be_print_err(gettext( 1501 "be_default_grub_bootfs: " 1502 "memory allocation failed\n")); 1503 ret = BE_ERR_NOMEM; 1504 goto cleanup; 1505 } 1506 } else if (default_entry < entries - 1) { 1507 /* 1508 * no bootfs entry for the default entry. 1509 */ 1510 break; 1511 } 1512 } 1513 } 1514 (void) fclose(menu_fp); 1515 1516 cleanup: 1517 if (pool_mounted) { 1518 int err = BE_SUCCESS; 1519 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1520 if (ret == BE_SUCCESS) 1521 ret = err; 1522 free(orig_mntpnt); 1523 free(ptmp_mntpnt); 1524 } 1525 ZFS_CLOSE(zhp); 1526 return (ret); 1527 } 1528 1529 /* 1530 * Function: be_change_grub_default 1531 * Description: This function takes two parameters. These are the name of 1532 * the BE we want to have as the default booted in the grub 1533 * menu and the root pool where the path to the grub menu exists. 1534 * The code takes this and finds the BE's entry in the grub menu 1535 * and changes the default entry to point to that entry in the 1536 * list. 1537 * Parameters: 1538 * be_name - This is the name of the BE wanted as the default 1539 * for the next boot. 1540 * be_root_pool - This is the name of the root pool where the 1541 * grub menu can be found. 1542 * Returns: 1543 * BE_SUCCESS - Success 1544 * be_errno_t - Failure 1545 * Scope: 1546 * Semi-private (library wide use only) 1547 */ 1548 int 1549 be_change_grub_default(char *be_name, char *be_root_pool) 1550 { 1551 zfs_handle_t *zhp = NULL; 1552 char grub_file[MAXPATHLEN]; 1553 char *temp_grub = NULL; 1554 char *pool_mntpnt = NULL; 1555 char *ptmp_mntpnt = NULL; 1556 char *orig_mntpnt = NULL; 1557 char line[BUFSIZ]; 1558 char temp_line[BUFSIZ]; 1559 char be_root_ds[MAXPATHLEN]; 1560 FILE *grub_fp = NULL; 1561 FILE *temp_fp = NULL; 1562 struct stat sb; 1563 int temp_grub_len = 0; 1564 int fd, entries = 0; 1565 int err = 0; 1566 int ret = BE_SUCCESS; 1567 boolean_t found_default = B_FALSE; 1568 boolean_t pool_mounted = B_FALSE; 1569 1570 errno = 0; 1571 1572 /* 1573 * Check to see if this system supports grub 1574 */ 1575 if (!be_has_grub()) { 1576 be_print_err(gettext("be_change_grub_default: operation " 1577 "not supported on this architecture\n")); 1578 return (BE_ERR_NOTSUP); 1579 } 1580 1581 /* Generate string for BE's root dataset */ 1582 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds)); 1583 1584 /* Get handle to pool dataset */ 1585 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1586 be_print_err(gettext("be_change_grub_default: " 1587 "failed to open pool dataset for %s: %s"), 1588 be_root_pool, libzfs_error_description(g_zfs)); 1589 return (zfs_err_to_be_err(g_zfs)); 1590 } 1591 1592 /* 1593 * Check to see if the pool's dataset is mounted. If it isn't we'll 1594 * attempt to mount it. 1595 */ 1596 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1597 &pool_mounted)) != BE_SUCCESS) { 1598 be_print_err(gettext("be_change_grub_default: pool dataset " 1599 "(%s) could not be mounted\n"), be_root_pool); 1600 ZFS_CLOSE(zhp); 1601 return (ret); 1602 } 1603 1604 /* 1605 * Get the mountpoint for the root pool dataset. 1606 */ 1607 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1608 be_print_err(gettext("be_change_grub_default: pool " 1609 "dataset (%s) is not mounted. Can't set " 1610 "the default BE in the grub menu.\n"), be_root_pool); 1611 ret = BE_ERR_NO_MENU; 1612 goto cleanup; 1613 } 1614 1615 (void) snprintf(grub_file, MAXPATHLEN, "%s%s", 1616 pool_mntpnt, BE_GRUB_MENU); 1617 1618 if ((ret = be_open_menu(be_root_pool, grub_file, 1619 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) { 1620 goto cleanup; 1621 } else if (grub_fp == NULL) { 1622 ret = BE_ERR_NO_MENU; 1623 goto cleanup; 1624 } 1625 1626 free(pool_mntpnt); 1627 pool_mntpnt = NULL; 1628 1629 /* Grab the stats of the original menu file */ 1630 if (stat(grub_file, &sb) != 0) { 1631 err = errno; 1632 be_print_err(gettext("be_change_grub_default: " 1633 "failed to stat file %s: %s\n"), grub_file, strerror(err)); 1634 ret = errno_to_be_err(err); 1635 goto cleanup; 1636 } 1637 1638 /* Create a tmp file for the modified menu.lst */ 1639 temp_grub_len = strlen(grub_file) + 7; 1640 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) { 1641 be_print_err(gettext("be_change_grub_default: " 1642 "malloc failed\n")); 1643 ret = BE_ERR_NOMEM; 1644 goto cleanup; 1645 } 1646 (void) memset(temp_grub, 0, temp_grub_len); 1647 (void) strlcpy(temp_grub, grub_file, temp_grub_len); 1648 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len); 1649 if ((fd = mkstemp(temp_grub)) == -1) { 1650 err = errno; 1651 be_print_err(gettext("be_change_grub_default: " 1652 "mkstemp failed: %s\n"), strerror(err)); 1653 ret = errno_to_be_err(err); 1654 free(temp_grub); 1655 temp_grub = NULL; 1656 goto cleanup; 1657 } 1658 if ((temp_fp = fdopen(fd, "w")) == NULL) { 1659 err = errno; 1660 be_print_err(gettext("be_change_grub_default: " 1661 "failed to open %s file: %s\n"), 1662 temp_grub, strerror(err)); 1663 (void) close(fd); 1664 ret = errno_to_be_err(err); 1665 goto cleanup; 1666 } 1667 1668 while (fgets(line, BUFSIZ, grub_fp)) { 1669 char *tok = strtok(line, BE_WHITE_SPACE); 1670 1671 if (tok == NULL || tok[0] == '#') { 1672 continue; 1673 } else if (strcmp(tok, "title") == 0) { 1674 entries++; 1675 continue; 1676 } else if (strcmp(tok, "bootfs") == 0) { 1677 char *bootfs = strtok(NULL, BE_WHITE_SPACE); 1678 if (bootfs == NULL) 1679 continue; 1680 1681 if (strcmp(bootfs, be_root_ds) == 0) { 1682 found_default = B_TRUE; 1683 break; 1684 } 1685 } 1686 } 1687 1688 if (!found_default) { 1689 be_print_err(gettext("be_change_grub_default: failed " 1690 "to find entry for %s in the grub menu\n"), 1691 be_name); 1692 ret = BE_ERR_BE_NOENT; 1693 goto cleanup; 1694 } 1695 1696 rewind(grub_fp); 1697 1698 while (fgets(line, BUFSIZ, grub_fp)) { 1699 char *tok = NULL; 1700 1701 (void) strncpy(temp_line, line, BUFSIZ); 1702 1703 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL && 1704 strcmp(tok, "default") == 0) { 1705 (void) snprintf(temp_line, BUFSIZ, "default %d\n", 1706 entries - 1 >= 0 ? entries - 1 : 0); 1707 (void) fputs(temp_line, temp_fp); 1708 } else { 1709 (void) fputs(line, temp_fp); 1710 } 1711 } 1712 1713 (void) fclose(grub_fp); 1714 grub_fp = NULL; 1715 (void) fclose(temp_fp); 1716 temp_fp = NULL; 1717 1718 if (rename(temp_grub, grub_file) != 0) { 1719 err = errno; 1720 be_print_err(gettext("be_change_grub_default: " 1721 "failed to rename file %s to %s: %s\n"), 1722 temp_grub, grub_file, strerror(err)); 1723 ret = errno_to_be_err(err); 1724 goto cleanup; 1725 } 1726 free(temp_grub); 1727 temp_grub = NULL; 1728 1729 /* Set the perms and ownership of the updated file */ 1730 if (chmod(grub_file, sb.st_mode) != 0) { 1731 err = errno; 1732 be_print_err(gettext("be_change_grub_default: " 1733 "failed to chmod %s: %s\n"), grub_file, strerror(err)); 1734 ret = errno_to_be_err(err); 1735 goto cleanup; 1736 } 1737 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) { 1738 err = errno; 1739 be_print_err(gettext("be_change_grub_default: " 1740 "failed to chown %s: %s\n"), grub_file, strerror(err)); 1741 ret = errno_to_be_err(err); 1742 } 1743 1744 cleanup: 1745 if (pool_mounted) { 1746 int err = BE_SUCCESS; 1747 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 1748 if (ret == BE_SUCCESS) 1749 ret = err; 1750 free(orig_mntpnt); 1751 free(ptmp_mntpnt); 1752 } 1753 ZFS_CLOSE(zhp); 1754 if (grub_fp != NULL) 1755 (void) fclose(grub_fp); 1756 if (temp_fp != NULL) 1757 (void) fclose(temp_fp); 1758 if (temp_grub != NULL) { 1759 (void) unlink(temp_grub); 1760 free(temp_grub); 1761 } 1762 1763 return (ret); 1764 } 1765 1766 /* 1767 * Function: be_update_menu 1768 * Description: This function is used by be_rename to change the BE name in 1769 * an existing entry in the grub menu to the new name of the BE. 1770 * Parameters: 1771 * be_orig_name - the original name of the BE 1772 * be_new_name - the new name the BE is being renameed to. 1773 * be_root_pool - The pool which contains the grub menu 1774 * boot_pool - the pool where the BE is, if different than 1775 * the pool containing the boot menu. If this is 1776 * NULL it will be set to be_root_pool. 1777 * Returns: 1778 * BE_SUCCESS - Success 1779 * be_errno_t - Failure 1780 * Scope: 1781 * Semi-private (library wide use only) 1782 */ 1783 int 1784 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool, 1785 char *boot_pool) 1786 { 1787 zfs_handle_t *zhp = NULL; 1788 char menu_file[MAXPATHLEN]; 1789 char be_root_ds[MAXPATHLEN]; 1790 char be_new_root_ds[MAXPATHLEN]; 1791 char line[BUFSIZ]; 1792 char *pool_mntpnt = NULL; 1793 char *ptmp_mntpnt = NULL; 1794 char *orig_mntpnt = NULL; 1795 char *temp_menu = NULL; 1796 FILE *menu_fp = NULL; 1797 FILE *new_fp = NULL; 1798 struct stat sb; 1799 int temp_menu_len = 0; 1800 int tmp_fd; 1801 int ret = BE_SUCCESS; 1802 int err = 0; 1803 boolean_t pool_mounted = B_FALSE; 1804 1805 errno = 0; 1806 1807 if (boot_pool == NULL) 1808 boot_pool = be_root_pool; 1809 1810 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 1811 be_print_err(gettext("be_update_menu: failed to open " 1812 "pool dataset for %s: %s\n"), be_root_pool, 1813 libzfs_error_description(g_zfs)); 1814 return (zfs_err_to_be_err(g_zfs)); 1815 } 1816 1817 /* 1818 * Check to see if the pool's dataset is mounted. If it isn't we'll 1819 * attempt to mount it. 1820 */ 1821 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 1822 &pool_mounted)) != BE_SUCCESS) { 1823 be_print_err(gettext("be_update_menu: pool dataset " 1824 "(%s) could not be mounted\n"), be_root_pool); 1825 ZFS_CLOSE(zhp); 1826 return (ret); 1827 } 1828 1829 /* 1830 * Get the mountpoint for the root pool dataset. 1831 */ 1832 if (!zfs_is_mounted(zhp, &pool_mntpnt)) { 1833 be_print_err(gettext("be_update_menu: failed " 1834 "to get mount point for the root pool. Can't set " 1835 "the default BE in the grub menu.\n")); 1836 ret = BE_ERR_NO_MENU; 1837 goto cleanup; 1838 } 1839 1840 /* 1841 * Check to see if this system supports grub 1842 */ 1843 if (be_has_grub()) { 1844 (void) snprintf(menu_file, sizeof (menu_file), 1845 "%s%s", pool_mntpnt, BE_GRUB_MENU); 1846 } else { 1847 (void) snprintf(menu_file, sizeof (menu_file), 1848 "%s%s", pool_mntpnt, BE_SPARC_MENU); 1849 } 1850 1851 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds, 1852 sizeof (be_root_ds)); 1853 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds, 1854 sizeof (be_new_root_ds)); 1855 1856 if ((ret = be_open_menu(be_root_pool, menu_file, 1857 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) { 1858 goto cleanup; 1859 } else if (menu_fp == NULL) { 1860 ret = BE_ERR_NO_MENU; 1861 goto cleanup; 1862 } 1863 1864 free(pool_mntpnt); 1865 pool_mntpnt = NULL; 1866 1867 /* Grab the stat of the original menu file */ 1868 if (stat(menu_file, &sb) != 0) { 1869 err = errno; 1870 be_print_err(gettext("be_update_menu: " 1871 "failed to stat file %s: %s\n"), menu_file, strerror(err)); 1872 (void) fclose(menu_fp); 1873 ret = errno_to_be_err(err); 1874 goto cleanup; 1875 } 1876 1877 /* Create tmp file for modified menu.lst */ 1878 temp_menu_len = strlen(menu_file) + 7; 1879 if ((temp_menu = (char *)malloc(temp_menu_len)) 1880 == NULL) { 1881 be_print_err(gettext("be_update_menu: " 1882 "malloc failed\n")); 1883 (void) fclose(menu_fp); 1884 ret = BE_ERR_NOMEM; 1885 goto cleanup; 1886 } 1887 (void) memset(temp_menu, 0, temp_menu_len); 1888 (void) strlcpy(temp_menu, menu_file, temp_menu_len); 1889 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len); 1890 if ((tmp_fd = mkstemp(temp_menu)) == -1) { 1891 err = errno; 1892 be_print_err(gettext("be_update_menu: " 1893 "mkstemp failed: %s\n"), strerror(err)); 1894 (void) fclose(menu_fp); 1895 free(temp_menu); 1896 ret = errno_to_be_err(err); 1897 goto cleanup; 1898 } 1899 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) { 1900 err = errno; 1901 be_print_err(gettext("be_update_menu: " 1902 "fdopen failed: %s\n"), strerror(err)); 1903 (void) close(tmp_fd); 1904 (void) fclose(menu_fp); 1905 free(temp_menu); 1906 ret = errno_to_be_err(err); 1907 goto cleanup; 1908 } 1909 1910 while (fgets(line, BUFSIZ, menu_fp)) { 1911 char tline[BUFSIZ]; 1912 char new_line[BUFSIZ]; 1913 char *c = NULL; 1914 1915 (void) strlcpy(tline, line, sizeof (tline)); 1916 1917 /* Tokenize line */ 1918 c = strtok(tline, BE_WHITE_SPACE); 1919 1920 if (c == NULL) { 1921 /* Found empty line, write it out. */ 1922 (void) fputs(line, new_fp); 1923 } else if (c[0] == '#') { 1924 /* Found a comment line, write it out. */ 1925 (void) fputs(line, new_fp); 1926 } else if (strcmp(c, "title") == 0) { 1927 char *name = NULL; 1928 char *desc = NULL; 1929 1930 /* 1931 * Found a 'title' line, parse out BE name or 1932 * the description. 1933 */ 1934 name = strtok(NULL, BE_WHITE_SPACE); 1935 1936 if (name == NULL) { 1937 /* 1938 * Nothing after 'title', just push 1939 * this line through 1940 */ 1941 (void) fputs(line, new_fp); 1942 } else { 1943 /* 1944 * Grab the remainder of the title which 1945 * could be a multi worded description 1946 */ 1947 desc = strtok(NULL, "\n"); 1948 1949 if (strcmp(name, be_orig_name) == 0) { 1950 /* 1951 * The first token of the title is 1952 * the old BE name, replace it with 1953 * the new one, and write it out 1954 * along with the remainder of 1955 * description if there is one. 1956 */ 1957 if (desc) { 1958 (void) snprintf(new_line, 1959 sizeof (new_line), 1960 "title %s %s\n", 1961 be_new_name, desc); 1962 } else { 1963 (void) snprintf(new_line, 1964 sizeof (new_line), 1965 "title %s\n", be_new_name); 1966 } 1967 1968 (void) fputs(new_line, new_fp); 1969 } else { 1970 (void) fputs(line, new_fp); 1971 } 1972 } 1973 } else if (strcmp(c, "bootfs") == 0) { 1974 /* 1975 * Found a 'bootfs' line, parse out the BE root 1976 * dataset value. 1977 */ 1978 char *root_ds = strtok(NULL, BE_WHITE_SPACE); 1979 1980 if (root_ds == NULL) { 1981 /* 1982 * Nothing after 'bootfs', just push 1983 * this line through 1984 */ 1985 (void) fputs(line, new_fp); 1986 } else { 1987 /* 1988 * If this bootfs is the one we're renaming, 1989 * write out the new root dataset value 1990 */ 1991 if (strcmp(root_ds, be_root_ds) == 0) { 1992 (void) snprintf(new_line, 1993 sizeof (new_line), "bootfs %s\n", 1994 be_new_root_ds); 1995 1996 (void) fputs(new_line, new_fp); 1997 } else { 1998 (void) fputs(line, new_fp); 1999 } 2000 } 2001 } else { 2002 /* 2003 * Found some other line we don't care 2004 * about, write it out. 2005 */ 2006 (void) fputs(line, new_fp); 2007 } 2008 } 2009 2010 (void) fclose(menu_fp); 2011 (void) fclose(new_fp); 2012 (void) close(tmp_fd); 2013 2014 if (rename(temp_menu, menu_file) != 0) { 2015 err = errno; 2016 be_print_err(gettext("be_update_menu: " 2017 "failed to rename file %s to %s: %s\n"), 2018 temp_menu, menu_file, strerror(err)); 2019 ret = errno_to_be_err(err); 2020 } 2021 free(temp_menu); 2022 2023 /* Set the perms and ownership of the updated file */ 2024 if (chmod(menu_file, sb.st_mode) != 0) { 2025 err = errno; 2026 be_print_err(gettext("be_update_menu: " 2027 "failed to chmod %s: %s\n"), menu_file, strerror(err)); 2028 ret = errno_to_be_err(err); 2029 goto cleanup; 2030 } 2031 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) { 2032 err = errno; 2033 be_print_err(gettext("be_update_menu: " 2034 "failed to chown %s: %s\n"), menu_file, strerror(err)); 2035 ret = errno_to_be_err(err); 2036 } 2037 2038 cleanup: 2039 if (pool_mounted) { 2040 int err = BE_SUCCESS; 2041 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 2042 if (ret == BE_SUCCESS) 2043 ret = err; 2044 free(orig_mntpnt); 2045 free(ptmp_mntpnt); 2046 } 2047 ZFS_CLOSE(zhp); 2048 return (ret); 2049 } 2050 2051 /* 2052 * Function: be_has_menu_entry 2053 * Description: Checks to see if the BEs root dataset has an entry in the grub 2054 * menu. 2055 * Parameters: 2056 * be_dataset - The root dataset of the BE 2057 * be_root_pool - The pool which contains the boot menu 2058 * entry - A pointer the the entry number of the BE if found. 2059 * Returns: 2060 * B_TRUE - Success 2061 * B_FALSE - Failure 2062 * Scope: 2063 * Semi-private (library wide use only) 2064 */ 2065 boolean_t 2066 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry) 2067 { 2068 zfs_handle_t *zhp = NULL; 2069 char menu_file[MAXPATHLEN]; 2070 FILE *menu_fp = NULL; 2071 char line[BUFSIZ]; 2072 char *last; 2073 char *rpool_mntpnt = NULL; 2074 char *ptmp_mntpnt = NULL; 2075 char *orig_mntpnt = NULL; 2076 int ent_num = 0; 2077 boolean_t ret = 0; 2078 boolean_t pool_mounted = B_FALSE; 2079 2080 2081 /* 2082 * Check to see if this system supports grub 2083 */ 2084 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) { 2085 be_print_err(gettext("be_has_menu_entry: failed to open " 2086 "pool dataset for %s: %s\n"), be_root_pool, 2087 libzfs_error_description(g_zfs)); 2088 return (B_FALSE); 2089 } 2090 2091 /* 2092 * Check to see if the pool's dataset is mounted. If it isn't we'll 2093 * attempt to mount it. 2094 */ 2095 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt, 2096 &pool_mounted) != 0) { 2097 be_print_err(gettext("be_has_menu_entry: pool dataset " 2098 "(%s) could not be mounted\n"), be_root_pool); 2099 ZFS_CLOSE(zhp); 2100 return (B_FALSE); 2101 } 2102 2103 /* 2104 * Get the mountpoint for the root pool dataset. 2105 */ 2106 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) { 2107 be_print_err(gettext("be_has_menu_entry: pool " 2108 "dataset (%s) is not mounted. Can't set " 2109 "the default BE in the grub menu.\n"), be_root_pool); 2110 ret = B_FALSE; 2111 goto cleanup; 2112 } 2113 2114 if (be_has_grub()) { 2115 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s", 2116 rpool_mntpnt, BE_GRUB_MENU); 2117 } else { 2118 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s", 2119 rpool_mntpnt, BE_SPARC_MENU); 2120 } 2121 2122 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r", 2123 B_FALSE) != 0) { 2124 ret = B_FALSE; 2125 goto cleanup; 2126 } else if (menu_fp == NULL) { 2127 ret = B_FALSE; 2128 goto cleanup; 2129 } 2130 2131 free(rpool_mntpnt); 2132 rpool_mntpnt = NULL; 2133 2134 while (fgets(line, BUFSIZ, menu_fp)) { 2135 char *tok = strtok_r(line, BE_WHITE_SPACE, &last); 2136 2137 if (tok != NULL && tok[0] != '#') { 2138 if (strcmp(tok, "bootfs") == 0) { 2139 tok = strtok_r(last, BE_WHITE_SPACE, &last); 2140 if (tok != NULL && strcmp(tok, 2141 be_dataset) == 0) { 2142 /* 2143 * The entry number needs to be 2144 * decremented here because the title 2145 * will always be the first line for 2146 * an entry. Because of this we'll 2147 * always be off by one entry when we 2148 * check for bootfs. 2149 */ 2150 *entry = ent_num - 1; 2151 ret = B_TRUE; 2152 goto cleanup; 2153 } 2154 } else if (strcmp(tok, "title") == 0) 2155 ent_num++; 2156 } 2157 } 2158 2159 cleanup: 2160 if (pool_mounted) { 2161 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt); 2162 free(orig_mntpnt); 2163 free(ptmp_mntpnt); 2164 } 2165 ZFS_CLOSE(zhp); 2166 (void) fclose(menu_fp); 2167 return (ret); 2168 } 2169 2170 /* 2171 * Function: be_update_vfstab 2172 * Description: This function digs into a BE's vfstab and updates all 2173 * entries with file systems listed in be_fs_list_data_t. 2174 * The entry's root container dataset and be_name will be 2175 * updated with the parameters passed in. 2176 * Parameters: 2177 * be_name - name of BE to update 2178 * old_rc_loc - dataset under which the root container dataset 2179 * of the old BE resides in. 2180 * new_rc_loc - dataset under which the root container dataset 2181 * of the new BE resides in. 2182 * fld - be_fs_list_data_t pointer providing the list of 2183 * file systems to look for in vfstab. 2184 * mountpoint - directory of where BE is currently mounted. 2185 * If NULL, then BE is not currently mounted. 2186 * Returns: 2187 * BE_SUCCESS - Success 2188 * be_errno_t - Failure 2189 * Scope: 2190 * Semi-private (library wide use only) 2191 */ 2192 int 2193 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc, 2194 be_fs_list_data_t *fld, char *mountpoint) 2195 { 2196 char *tmp_mountpoint = NULL; 2197 char alt_vfstab[MAXPATHLEN]; 2198 int ret = BE_SUCCESS, err = BE_SUCCESS; 2199 2200 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0) 2201 return (BE_SUCCESS); 2202 2203 /* If BE not already mounted, mount the BE */ 2204 if (mountpoint == NULL) { 2205 if ((ret = _be_mount(be_name, &tmp_mountpoint, 2206 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 2207 be_print_err(gettext("be_update_vfstab: " 2208 "failed to mount BE (%s)\n"), be_name); 2209 return (ret); 2210 } 2211 } else { 2212 tmp_mountpoint = mountpoint; 2213 } 2214 2215 /* Get string for vfstab in the mounted BE. */ 2216 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab", 2217 tmp_mountpoint); 2218 2219 /* Update the vfstab */ 2220 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc, 2221 fld); 2222 2223 /* Unmount BE if we mounted it */ 2224 if (mountpoint == NULL) { 2225 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) { 2226 /* Remove temporary mountpoint */ 2227 (void) rmdir(tmp_mountpoint); 2228 } else { 2229 be_print_err(gettext("be_update_vfstab: " 2230 "failed to unmount BE %s mounted at %s\n"), 2231 be_name, tmp_mountpoint); 2232 if (ret == BE_SUCCESS) 2233 ret = err; 2234 } 2235 2236 free(tmp_mountpoint); 2237 } 2238 2239 return (ret); 2240 } 2241 2242 /* 2243 * Function: be_update_zone_vfstab 2244 * Description: This function digs into a zone BE's vfstab and updates all 2245 * entries with file systems listed in be_fs_list_data_t. 2246 * The entry's root container dataset and be_name will be 2247 * updated with the parameters passed in. 2248 * Parameters: 2249 * zhp - zfs_handle_t pointer to zone root dataset. 2250 * be_name - name of zone BE to update 2251 * old_rc_loc - dataset under which the root container dataset 2252 * of the old zone BE resides in. 2253 * new_rc_loc - dataset under which the root container dataset 2254 * of the new zone BE resides in. 2255 * fld - be_fs_list_data_t pointer providing the list of 2256 * file systems to look for in vfstab. 2257 * Returns: 2258 * BE_SUCCESS - Success 2259 * be_errno_t - Failure 2260 * Scope: 2261 * Semi-private (library wide use only) 2262 */ 2263 int 2264 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc, 2265 char *new_rc_loc, be_fs_list_data_t *fld) 2266 { 2267 be_mount_data_t md = { 0 }; 2268 be_unmount_data_t ud = { 0 }; 2269 char alt_vfstab[MAXPATHLEN]; 2270 boolean_t mounted_here = B_FALSE; 2271 int ret = BE_SUCCESS; 2272 2273 /* 2274 * If zone root not already mounted, mount it at a 2275 * temporary location. 2276 */ 2277 if (!zfs_is_mounted(zhp, &md.altroot)) { 2278 /* Generate temporary mountpoint to mount zone root */ 2279 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) { 2280 be_print_err(gettext("be_update_zone_vfstab: " 2281 "failed to make temporary mountpoint to " 2282 "mount zone root\n")); 2283 return (ret); 2284 } 2285 2286 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) { 2287 be_print_err(gettext("be_update_zone_vfstab: " 2288 "failed to mount zone root %s\n"), 2289 zfs_get_name(zhp)); 2290 free(md.altroot); 2291 return (BE_ERR_MOUNT_ZONEROOT); 2292 } 2293 mounted_here = B_TRUE; 2294 } 2295 2296 /* Get string from vfstab in the mounted zone BE */ 2297 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab", 2298 md.altroot); 2299 2300 /* Update the vfstab */ 2301 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc, 2302 fld); 2303 2304 /* Unmount zone root if we mounted it */ 2305 if (mounted_here) { 2306 ud.force = B_TRUE; 2307 2308 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) { 2309 /* Remove the temporary mountpoint */ 2310 (void) rmdir(md.altroot); 2311 } else { 2312 be_print_err(gettext("be_update_zone_vfstab: " 2313 "failed to unmount zone root %s from %s\n"), 2314 zfs_get_name(zhp), md.altroot); 2315 if (ret == 0) 2316 ret = BE_ERR_UMOUNT_ZONEROOT; 2317 } 2318 } 2319 2320 free(md.altroot); 2321 return (ret); 2322 } 2323 2324 /* 2325 * Function: be_auto_snap_name 2326 * Description: Generate an auto snapshot name constructed based on the 2327 * current date and time. The auto snapshot name is of the form: 2328 * 2329 * <date>-<time> 2330 * 2331 * where <date> is in ISO standard format, so the resultant name 2332 * is of the form: 2333 * 2334 * %Y-%m-%d-%H:%M:%S 2335 * 2336 * Parameters: 2337 * None 2338 * Returns: 2339 * Success - pointer to auto generated snapshot name. The name 2340 * is allocated in heap storage so the caller is 2341 * responsible for free'ing the name. 2342 * Failure - NULL 2343 * Scope: 2344 * Semi-private (library wide use only) 2345 */ 2346 char * 2347 be_auto_snap_name(void) 2348 { 2349 time_t utc_tm = 0; 2350 struct tm *gmt_tm = NULL; 2351 char gmt_time_str[64]; 2352 char *auto_snap_name = NULL; 2353 2354 if (time(&utc_tm) == -1) { 2355 be_print_err(gettext("be_auto_snap_name: time() failed\n")); 2356 return (NULL); 2357 } 2358 2359 if ((gmt_tm = gmtime(&utc_tm)) == NULL) { 2360 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n")); 2361 return (NULL); 2362 } 2363 2364 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm); 2365 2366 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) { 2367 be_print_err(gettext("be_auto_snap_name: " 2368 "memory allocation failed\n")); 2369 return (NULL); 2370 } 2371 2372 return (auto_snap_name); 2373 } 2374 2375 /* 2376 * Function: be_auto_be_name 2377 * Description: Generate an auto BE name constructed based on the BE name 2378 * of the original BE being cloned. 2379 * Parameters: 2380 * obe_name - name of the original BE being cloned. 2381 * Returns: 2382 * Success - pointer to auto generated BE name. The name 2383 * is allocated in heap storage so the caller is 2384 * responsible for free'ing the name. 2385 * Failure - NULL 2386 * Scope: 2387 * Semi-private (library wide use only) 2388 */ 2389 char * 2390 be_auto_be_name(char *obe_name) 2391 { 2392 return (be_get_auto_name(obe_name, NULL, B_FALSE)); 2393 } 2394 2395 /* 2396 * Function: be_auto_zone_be_name 2397 * Description: Generate an auto BE name for a zone constructed based on 2398 * the BE name of the original zone BE being cloned. 2399 * Parameters: 2400 * container_ds - container dataset for the zone. 2401 * zbe_name - name of the original zone BE being cloned. 2402 * Returns: 2403 * Success - pointer to auto generated BE name. The name 2404 * is allocated in heap storage so the caller is 2405 * responsible for free'ing the name. 2406 * Failure - NULL 2407 * Scope: 2408 * Semi-private (library wide use only) 2409 */ 2410 char * 2411 be_auto_zone_be_name(char *container_ds, char *zbe_name) 2412 { 2413 return (be_get_auto_name(zbe_name, container_ds, B_TRUE)); 2414 } 2415 2416 /* 2417 * Function: be_valid_be_name 2418 * Description: Validates a BE name. 2419 * Parameters: 2420 * be_name - name of BE to validate 2421 * Returns: 2422 * B_TRUE - be_name is valid 2423 * B_FALSE - be_name is invalid 2424 * Scope: 2425 * Semi-private (library wide use only) 2426 */ 2427 2428 boolean_t 2429 be_valid_be_name(const char *be_name) 2430 { 2431 const char *c = NULL; 2432 struct be_defaults be_defaults; 2433 2434 if (be_name == NULL) 2435 return (B_FALSE); 2436 2437 be_get_defaults(&be_defaults); 2438 2439 /* 2440 * A BE name must not be a multi-level dataset name. We also check 2441 * that it does not contain the ' ' and '%' characters. The ' ' is 2442 * a valid character for datasets, however we don't allow that in a 2443 * BE name. The '%' is invalid, but zfs_name_valid() allows it for 2444 * internal reasons, so we explicitly check for it here. 2445 */ 2446 c = be_name; 2447 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%') 2448 c++; 2449 2450 if (*c != '\0') 2451 return (B_FALSE); 2452 2453 /* 2454 * The BE name must comply with a zfs dataset filesystem. We also 2455 * verify its length to be < BE_NAME_MAX_LEN. 2456 */ 2457 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) || 2458 strlen(be_name) > BE_NAME_MAX_LEN) 2459 return (B_FALSE); 2460 2461 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' && 2462 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) { 2463 return (B_FALSE); 2464 } 2465 2466 return (B_TRUE); 2467 } 2468 2469 /* 2470 * Function: be_valid_auto_snap_name 2471 * Description: This function checks that a snapshot name is a valid auto 2472 * generated snapshot name. A valid auto generated snapshot 2473 * name is of the form: 2474 * 2475 * %Y-%m-%d-%H:%M:%S 2476 * 2477 * An older form of the auto generated snapshot name also 2478 * included the snapshot's BE cleanup policy and a reserved 2479 * field. Those names will also be verified by this function. 2480 * 2481 * Examples of valid auto snapshot names are: 2482 * 2483 * 2008-03-31-18:41:30 2484 * 2008-03-31-22:17:24 2485 * <policy>:-:2008:04-05-09:12:55 2486 * <policy>:-:2008:04-06-15:34:12 2487 * 2488 * Parameters: 2489 * name - name of the snapshot to be validated. 2490 * Returns: 2491 * B_TRUE - the name is a valid auto snapshot name. 2492 * B_FALSE - the name is not a valid auto snapshot name. 2493 * Scope: 2494 * Semi-private (library wide use only) 2495 */ 2496 boolean_t 2497 be_valid_auto_snap_name(char *name) 2498 { 2499 struct tm gmt_tm; 2500 2501 char *policy = NULL; 2502 char *reserved = NULL; 2503 char *date = NULL; 2504 char *c = NULL; 2505 2506 /* Validate the snapshot name by converting it into utc time */ 2507 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL && 2508 (mktime(&gmt_tm) != -1)) { 2509 return (B_TRUE); 2510 } 2511 2512 /* 2513 * Validate the snapshot name against the older form of an 2514 * auto generated snapshot name. 2515 */ 2516 policy = strdup(name); 2517 2518 /* 2519 * Get the first field from the snapshot name, 2520 * which is the BE policy 2521 */ 2522 c = strchr(policy, ':'); 2523 if (c == NULL) { 2524 free(policy); 2525 return (B_FALSE); 2526 } 2527 c[0] = '\0'; 2528 2529 /* Validate the policy name */ 2530 if (!valid_be_policy(policy)) { 2531 free(policy); 2532 return (B_FALSE); 2533 } 2534 2535 /* Get the next field, which is the reserved field. */ 2536 if (c[1] == '\0') { 2537 free(policy); 2538 return (B_FALSE); 2539 } 2540 reserved = c+1; 2541 c = strchr(reserved, ':'); 2542 if (c == NULL) { 2543 free(policy); 2544 return (B_FALSE); 2545 } 2546 c[0] = '\0'; 2547 2548 /* Validate the reserved field */ 2549 if (strcmp(reserved, "-") != 0) { 2550 free(policy); 2551 return (B_FALSE); 2552 } 2553 2554 /* The remaining string should be the date field */ 2555 if (c[1] == '\0') { 2556 free(policy); 2557 return (B_FALSE); 2558 } 2559 date = c+1; 2560 2561 /* Validate the date string by converting it into utc time */ 2562 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL || 2563 (mktime(&gmt_tm) == -1)) { 2564 be_print_err(gettext("be_valid_auto_snap_name: " 2565 "invalid auto snapshot name\n")); 2566 free(policy); 2567 return (B_FALSE); 2568 } 2569 2570 free(policy); 2571 return (B_TRUE); 2572 } 2573 2574 /* 2575 * Function: be_default_policy 2576 * Description: Temporary hardcoded policy support. This function returns 2577 * the default policy type to be used to create a BE or a BE 2578 * snapshot. 2579 * Parameters: 2580 * None 2581 * Returns: 2582 * Name of default BE policy. 2583 * Scope: 2584 * Semi-private (library wide use only) 2585 */ 2586 char * 2587 be_default_policy(void) 2588 { 2589 return (BE_PLCY_STATIC); 2590 } 2591 2592 /* 2593 * Function: valid_be_policy 2594 * Description: Temporary hardcoded policy support. This function valids 2595 * whether a policy is a valid known policy or not. 2596 * Paramters: 2597 * policy - name of policy to validate. 2598 * Returns: 2599 * B_TRUE - policy is a valid. 2600 * B_FALSE - policy is invalid. 2601 * Scope: 2602 * Semi-private (library wide use only) 2603 */ 2604 boolean_t 2605 valid_be_policy(char *policy) 2606 { 2607 if (policy == NULL) 2608 return (B_FALSE); 2609 2610 if (strcmp(policy, BE_PLCY_STATIC) == 0 || 2611 strcmp(policy, BE_PLCY_VOLATILE) == 0) { 2612 return (B_TRUE); 2613 } 2614 2615 return (B_FALSE); 2616 } 2617 2618 /* 2619 * Function: be_print_err 2620 * Description: This function prints out error messages if do_print is 2621 * set to B_TRUE or if the BE_PRINT_ERR environment variable 2622 * is set to true. 2623 * Paramters: 2624 * prnt_str - the string we wish to print and any arguments 2625 * for the format of that string. 2626 * Returns: 2627 * void 2628 * Scope: 2629 * Semi-private (library wide use only) 2630 */ 2631 void 2632 be_print_err(char *prnt_str, ...) 2633 { 2634 va_list ap; 2635 char buf[BUFSIZ]; 2636 char *env_buf; 2637 static boolean_t env_checked = B_FALSE; 2638 2639 if (!env_checked) { 2640 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) { 2641 if (strcasecmp(env_buf, "true") == 0) { 2642 do_print = B_TRUE; 2643 } 2644 } 2645 env_checked = B_TRUE; 2646 } 2647 2648 if (do_print) { 2649 va_start(ap, prnt_str); 2650 /* LINTED variable format specifier */ 2651 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap); 2652 (void) fputs(buf, stderr); 2653 va_end(ap); 2654 } 2655 } 2656 2657 /* 2658 * Function: be_find_current_be 2659 * Description: Find the currently "active" BE. Fill in the 2660 * passed in be_transaction_data_t reference with the 2661 * active BE's data. 2662 * Paramters: 2663 * none 2664 * Returns: 2665 * BE_SUCCESS - Success 2666 * be_errnot_t - Failure 2667 * Scope: 2668 * Semi-private (library wide use only) 2669 * Notes: 2670 * The caller is responsible for initializing the libzfs handle 2671 * and freeing the memory used by the active be_name. 2672 */ 2673 int 2674 be_find_current_be(be_transaction_data_t *bt) 2675 { 2676 int zret; 2677 2678 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback, 2679 bt)) == 0) { 2680 be_print_err(gettext("be_find_current_be: failed to " 2681 "find current BE name\n")); 2682 return (BE_ERR_BE_NOENT); 2683 } else if (zret < 0) { 2684 be_print_err(gettext("be_find_current_be: " 2685 "zpool_iter failed: %s\n"), 2686 libzfs_error_description(g_zfs)); 2687 return (zfs_err_to_be_err(g_zfs)); 2688 } 2689 2690 return (BE_SUCCESS); 2691 } 2692 2693 /* 2694 * Function: be_zpool_find_current_be_callback 2695 * Description: Callback function used to iterate through all existing pools 2696 * to find the BE that is the currently booted BE. 2697 * Parameters: 2698 * zlp - zpool_handle_t pointer to the current pool being 2699 * looked at. 2700 * data - be_transaction_data_t pointer. 2701 * Upon successfully finding the current BE, the 2702 * obe_zpool member of this parameter is set to the 2703 * pool it is found in. 2704 * Return: 2705 * 1 - Found current BE in this pool. 2706 * 0 - Did not find current BE in this pool. 2707 * Scope: 2708 * Semi-private (library wide use only) 2709 */ 2710 int 2711 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data) 2712 { 2713 be_transaction_data_t *bt = data; 2714 zfs_handle_t *zhp = NULL; 2715 const char *zpool = zpool_get_name(zlp); 2716 char be_container_ds[MAXPATHLEN]; 2717 char *zpath = NULL; 2718 2719 /* 2720 * Generate string for BE container dataset 2721 */ 2722 if (getzoneid() != GLOBAL_ZONEID) { 2723 if ((zpath = be_get_ds_from_dir("/")) != NULL) { 2724 (void) strlcpy(be_container_ds, dirname(zpath), 2725 sizeof (be_container_ds)); 2726 } else { 2727 be_print_err(gettext( 2728 "be_zpool_find_current_be_callback: " 2729 "zone root dataset is not mounted\n")); 2730 return (0); 2731 } 2732 } else { 2733 be_make_container_ds(zpool, be_container_ds, 2734 sizeof (be_container_ds)); 2735 } 2736 2737 /* 2738 * Check if a BE container dataset exists in this pool. 2739 */ 2740 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) { 2741 zpool_close(zlp); 2742 return (0); 2743 } 2744 2745 /* 2746 * Get handle to this zpool's BE container dataset. 2747 */ 2748 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) == 2749 NULL) { 2750 be_print_err(gettext("be_zpool_find_current_be_callback: " 2751 "failed to open BE container dataset (%s)\n"), 2752 be_container_ds); 2753 zpool_close(zlp); 2754 return (0); 2755 } 2756 2757 /* 2758 * Iterate through all potential BEs in this zpool 2759 */ 2760 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) { 2761 /* 2762 * Found current BE dataset; set obe_zpool 2763 */ 2764 if ((bt->obe_zpool = strdup(zpool)) == NULL) { 2765 be_print_err(gettext( 2766 "be_zpool_find_current_be_callback: " 2767 "memory allocation failed\n")); 2768 ZFS_CLOSE(zhp); 2769 zpool_close(zlp); 2770 return (0); 2771 } 2772 2773 ZFS_CLOSE(zhp); 2774 zpool_close(zlp); 2775 return (1); 2776 } 2777 2778 ZFS_CLOSE(zhp); 2779 zpool_close(zlp); 2780 2781 return (0); 2782 } 2783 2784 /* 2785 * Function: be_zfs_find_current_be_callback 2786 * Description: Callback function used to iterate through all BEs in a 2787 * pool to find the BE that is the currently booted BE. 2788 * Parameters: 2789 * zhp - zfs_handle_t pointer to current filesystem being checked. 2790 * data - be_transaction-data_t pointer 2791 * Upon successfully finding the current BE, the 2792 * obe_name and obe_root_ds members of this parameter 2793 * are set to the BE name and BE's root dataset 2794 * respectively. 2795 * Return: 2796 * 1 - Found current BE. 2797 * 0 - Did not find current BE. 2798 * Scope: 2799 * Semi-private (library wide use only) 2800 */ 2801 int 2802 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data) 2803 { 2804 be_transaction_data_t *bt = data; 2805 char *mp = NULL; 2806 2807 /* 2808 * Check if dataset is mounted, and if so where. 2809 */ 2810 if (zfs_is_mounted(zhp, &mp)) { 2811 /* 2812 * If mounted at root, set obe_root_ds and obe_name 2813 */ 2814 if (mp != NULL && strcmp(mp, "/") == 0) { 2815 free(mp); 2816 2817 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp))) 2818 == NULL) { 2819 be_print_err(gettext( 2820 "be_zfs_find_current_be_callback: " 2821 "memory allocation failed\n")); 2822 ZFS_CLOSE(zhp); 2823 return (0); 2824 } 2825 2826 if ((bt->obe_name = strdup(basename(bt->obe_root_ds))) 2827 == NULL) { 2828 be_print_err(gettext( 2829 "be_zfs_find_current_be_callback: " 2830 "memory allocation failed\n")); 2831 ZFS_CLOSE(zhp); 2832 return (0); 2833 } 2834 2835 ZFS_CLOSE(zhp); 2836 return (1); 2837 } 2838 2839 free(mp); 2840 } 2841 ZFS_CLOSE(zhp); 2842 2843 return (0); 2844 } 2845 2846 /* 2847 * Function: be_check_be_roots_callback 2848 * Description: This function checks whether or not the dataset name passed 2849 * is hierachically located under the BE root container dataset 2850 * for this pool. 2851 * Parameters: 2852 * zlp - zpool_handle_t pointer to current pool being processed. 2853 * data - name of dataset to check 2854 * Returns: 2855 * 0 - dataset is not in this pool's BE root container dataset 2856 * 1 - dataset is in this pool's BE root container dataset 2857 * Scope: 2858 * Semi-private (library wide use only) 2859 */ 2860 int 2861 be_check_be_roots_callback(zpool_handle_t *zlp, void *data) 2862 { 2863 const char *zpool = zpool_get_name(zlp); 2864 char *ds = data; 2865 char be_container_ds[MAXPATHLEN]; 2866 2867 /* Generate string for this pool's BE root container dataset */ 2868 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds)); 2869 2870 /* 2871 * If dataset lives under the BE root container dataset 2872 * of this pool, return failure. 2873 */ 2874 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 && 2875 ds[strlen(be_container_ds)] == '/') { 2876 zpool_close(zlp); 2877 return (1); 2878 } 2879 2880 zpool_close(zlp); 2881 return (0); 2882 } 2883 2884 /* 2885 * Function: zfs_err_to_be_err 2886 * Description: This function takes the error stored in the libzfs handle 2887 * and maps it to an be_errno_t. If there are no matching 2888 * be_errno_t's then BE_ERR_ZFS is returned. 2889 * Paramters: 2890 * zfsh - The libzfs handle containing the error we're looking up. 2891 * Returns: 2892 * be_errno_t 2893 * Scope: 2894 * Semi-private (library wide use only) 2895 */ 2896 int 2897 zfs_err_to_be_err(libzfs_handle_t *zfsh) 2898 { 2899 int err = libzfs_errno(zfsh); 2900 2901 switch (err) { 2902 case 0: 2903 return (BE_SUCCESS); 2904 case EZFS_PERM: 2905 return (BE_ERR_PERM); 2906 case EZFS_INTR: 2907 return (BE_ERR_INTR); 2908 case EZFS_NOENT: 2909 return (BE_ERR_NOENT); 2910 case EZFS_NOSPC: 2911 return (BE_ERR_NOSPC); 2912 case EZFS_MOUNTFAILED: 2913 return (BE_ERR_MOUNT); 2914 case EZFS_UMOUNTFAILED: 2915 return (BE_ERR_UMOUNT); 2916 case EZFS_EXISTS: 2917 return (BE_ERR_BE_EXISTS); 2918 case EZFS_BUSY: 2919 return (BE_ERR_DEV_BUSY); 2920 case EZFS_POOLREADONLY: 2921 return (BE_ERR_ROFS); 2922 case EZFS_NAMETOOLONG: 2923 return (BE_ERR_NAMETOOLONG); 2924 case EZFS_NODEVICE: 2925 return (BE_ERR_NODEV); 2926 case EZFS_POOL_INVALARG: 2927 return (BE_ERR_INVAL); 2928 case EZFS_PROPTYPE: 2929 return (BE_ERR_INVALPROP); 2930 case EZFS_BADTYPE: 2931 return (BE_ERR_DSTYPE); 2932 case EZFS_PROPNONINHERIT: 2933 return (BE_ERR_NONINHERIT); 2934 case EZFS_PROPREADONLY: 2935 return (BE_ERR_READONLYPROP); 2936 case EZFS_RESILVERING: 2937 case EZFS_POOLUNAVAIL: 2938 return (BE_ERR_UNAVAIL); 2939 case EZFS_DSREADONLY: 2940 return (BE_ERR_READONLYDS); 2941 default: 2942 return (BE_ERR_ZFS); 2943 } 2944 } 2945 2946 /* 2947 * Function: errno_to_be_err 2948 * Description: This function takes an errno and maps it to an be_errno_t. 2949 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is 2950 * returned. 2951 * Paramters: 2952 * err - The errno we're compairing against. 2953 * Returns: 2954 * be_errno_t 2955 * Scope: 2956 * Semi-private (library wide use only) 2957 */ 2958 int 2959 errno_to_be_err(int err) 2960 { 2961 switch (err) { 2962 case EPERM: 2963 return (BE_ERR_PERM); 2964 case EACCES: 2965 return (BE_ERR_ACCESS); 2966 case ECANCELED: 2967 return (BE_ERR_CANCELED); 2968 case EINTR: 2969 return (BE_ERR_INTR); 2970 case ENOENT: 2971 return (BE_ERR_NOENT); 2972 case ENOSPC: 2973 case EDQUOT: 2974 return (BE_ERR_NOSPC); 2975 case EEXIST: 2976 return (BE_ERR_BE_EXISTS); 2977 case EBUSY: 2978 return (BE_ERR_BUSY); 2979 case EROFS: 2980 return (BE_ERR_ROFS); 2981 case ENAMETOOLONG: 2982 return (BE_ERR_NAMETOOLONG); 2983 case ENXIO: 2984 return (BE_ERR_NXIO); 2985 case EINVAL: 2986 return (BE_ERR_INVAL); 2987 case EFAULT: 2988 return (BE_ERR_FAULT); 2989 default: 2990 return (BE_ERR_UNKNOWN); 2991 } 2992 } 2993 2994 /* 2995 * Function: be_err_to_str 2996 * Description: This function takes a be_errno_t and maps it to a message. 2997 * If there are no matching be_errno_t's then NULL is returned. 2998 * Paramters: 2999 * be_errno_t - The be_errno_t we're mapping. 3000 * Returns: 3001 * string or NULL if the error code is not known. 3002 * Scope: 3003 * Semi-private (library wide use only) 3004 */ 3005 char * 3006 be_err_to_str(int err) 3007 { 3008 switch (err) { 3009 case BE_ERR_ACCESS: 3010 return (gettext("Permission denied.")); 3011 case BE_ERR_ACTIVATE_CURR: 3012 return (gettext("Activation of current BE failed.")); 3013 case BE_ERR_AUTONAME: 3014 return (gettext("Auto naming failed.")); 3015 case BE_ERR_BE_NOENT: 3016 return (gettext("No such BE.")); 3017 case BE_ERR_BUSY: 3018 return (gettext("Mount busy.")); 3019 case BE_ERR_DEV_BUSY: 3020 return (gettext("Device busy.")); 3021 case BE_ERR_CANCELED: 3022 return (gettext("Operation canceled.")); 3023 case BE_ERR_CLONE: 3024 return (gettext("BE clone failed.")); 3025 case BE_ERR_COPY: 3026 return (gettext("BE copy failed.")); 3027 case BE_ERR_CREATDS: 3028 return (gettext("Dataset creation failed.")); 3029 case BE_ERR_CURR_BE_NOT_FOUND: 3030 return (gettext("Can't find current BE.")); 3031 case BE_ERR_DESTROY: 3032 return (gettext("Failed to destroy BE or snapshot.")); 3033 case BE_ERR_DESTROY_CURR_BE: 3034 return (gettext("Cannot destroy current BE.")); 3035 case BE_ERR_DEMOTE: 3036 return (gettext("BE demotion failed.")); 3037 case BE_ERR_DSTYPE: 3038 return (gettext("Invalid dataset type.")); 3039 case BE_ERR_BE_EXISTS: 3040 return (gettext("BE exists.")); 3041 case BE_ERR_INIT: 3042 return (gettext("be_zfs_init failed.")); 3043 case BE_ERR_INTR: 3044 return (gettext("Interupted system call.")); 3045 case BE_ERR_INVAL: 3046 return (gettext("Invalid argument.")); 3047 case BE_ERR_INVALPROP: 3048 return (gettext("Invalid property for dataset.")); 3049 case BE_ERR_INVALMOUNTPOINT: 3050 return (gettext("Unexpected mountpoint.")); 3051 case BE_ERR_MOUNT: 3052 return (gettext("Mount failed.")); 3053 case BE_ERR_MOUNTED: 3054 return (gettext("Already mounted.")); 3055 case BE_ERR_NAMETOOLONG: 3056 return (gettext("name > BUFSIZ.")); 3057 case BE_ERR_NOENT: 3058 return (gettext("Doesn't exist.")); 3059 case BE_ERR_POOL_NOENT: 3060 return (gettext("No such pool.")); 3061 case BE_ERR_NODEV: 3062 return (gettext("No such device.")); 3063 case BE_ERR_NOTMOUNTED: 3064 return (gettext("File system not mounted.")); 3065 case BE_ERR_NOMEM: 3066 return (gettext("Not enough memory.")); 3067 case BE_ERR_NONINHERIT: 3068 return (gettext( 3069 "Property is not inheritable for the BE dataset.")); 3070 case BE_ERR_NXIO: 3071 return (gettext("No such device or address.")); 3072 case BE_ERR_NOSPC: 3073 return (gettext("No space on device.")); 3074 case BE_ERR_NOTSUP: 3075 return (gettext("Operation not supported.")); 3076 case BE_ERR_OPEN: 3077 return (gettext("Open failed.")); 3078 case BE_ERR_PERM: 3079 return (gettext("Not owner.")); 3080 case BE_ERR_UNAVAIL: 3081 return (gettext("The BE is currently unavailable.")); 3082 case BE_ERR_PROMOTE: 3083 return (gettext("BE promotion failed.")); 3084 case BE_ERR_ROFS: 3085 return (gettext("Read only file system.")); 3086 case BE_ERR_READONLYDS: 3087 return (gettext("Read only dataset.")); 3088 case BE_ERR_READONLYPROP: 3089 return (gettext("Read only property.")); 3090 case BE_ERR_RENAME_ACTIVE: 3091 return (gettext("Renaming the active BE is not supported.")); 3092 case BE_ERR_SS_EXISTS: 3093 return (gettext("Snapshot exists.")); 3094 case BE_ERR_SS_NOENT: 3095 return (gettext("No such snapshot.")); 3096 case BE_ERR_UMOUNT: 3097 return (gettext("Unmount failed.")); 3098 case BE_ERR_UMOUNT_CURR_BE: 3099 return (gettext("Can't unmount the current BE.")); 3100 case BE_ERR_UMOUNT_SHARED: 3101 return (gettext("Unmount of a shared File System failed.")); 3102 case BE_ERR_FAULT: 3103 return (gettext("Bad address.")); 3104 case BE_ERR_UNKNOWN: 3105 return (gettext("Unknown error.")); 3106 case BE_ERR_ZFS: 3107 return (gettext("ZFS returned an error.")); 3108 case BE_ERR_GEN_UUID: 3109 return (gettext("Failed to generate uuid.")); 3110 case BE_ERR_PARSE_UUID: 3111 return (gettext("Failed to parse uuid.")); 3112 case BE_ERR_NO_UUID: 3113 return (gettext("No uuid")); 3114 case BE_ERR_ZONE_NO_PARENTBE: 3115 return (gettext("No parent uuid")); 3116 case BE_ERR_ZONE_MULTIPLE_ACTIVE: 3117 return (gettext("Multiple active zone roots")); 3118 case BE_ERR_ZONE_NO_ACTIVE_ROOT: 3119 return (gettext("No active zone root")); 3120 case BE_ERR_ZONE_ROOT_NOT_LEGACY: 3121 return (gettext("Zone root not legacy")); 3122 case BE_ERR_MOUNT_ZONEROOT: 3123 return (gettext("Failed to mount a zone root.")); 3124 case BE_ERR_UMOUNT_ZONEROOT: 3125 return (gettext("Failed to unmount a zone root.")); 3126 case BE_ERR_NO_MOUNTED_ZONE: 3127 return (gettext("Zone is not mounted")); 3128 case BE_ERR_ZONES_UNMOUNT: 3129 return (gettext("Unable to unmount a zone BE.")); 3130 case BE_ERR_NO_MENU: 3131 return (gettext("Missing boot menu file.")); 3132 case BE_ERR_BAD_MENU_PATH: 3133 return (gettext("Invalid path for menu.lst file")); 3134 case BE_ERR_ZONE_SS_EXISTS: 3135 return (gettext("Zone snapshot exists.")); 3136 case BE_ERR_BOOTFILE_INST: 3137 return (gettext("Error installing boot files.")); 3138 case BE_ERR_EXTCMD: 3139 return (gettext("Error running an external command.")); 3140 default: 3141 return (NULL); 3142 } 3143 } 3144 3145 /* 3146 * Function: be_has_grub 3147 * Description: Boolean function indicating whether the current system 3148 * uses grub. 3149 * Return: B_FALSE - the system does not have grub 3150 * B_TRUE - the system does have grub. 3151 * Scope: 3152 * Semi-private (library wide use only) 3153 */ 3154 boolean_t 3155 be_has_grub(void) 3156 { 3157 static struct be_defaults be_defaults; 3158 static boolean_t be_deflts_set = B_FALSE; 3159 3160 /* Cache the defaults, because be_has_grub is used often. */ 3161 if (be_deflts_set == B_FALSE) { 3162 be_get_defaults(&be_defaults); 3163 be_deflts_set = B_TRUE; 3164 } 3165 3166 return (be_defaults.be_deflt_grub); 3167 } 3168 3169 /* 3170 * Function: be_is_isa 3171 * Description: Boolean function indicating whether the instruction set 3172 * architecture of the executing system matches the name provided. 3173 * The string must match a system defined architecture (e.g. 3174 * "i386", "sparc") and is case sensitive. 3175 * Parameters: name - string representing the name of instruction set 3176 * architecture being tested 3177 * Returns: B_FALSE - the system instruction set architecture is different 3178 * from the one specified 3179 * B_TRUE - the system instruction set architecture is the same 3180 * as the one specified 3181 * Scope: 3182 * Semi-private (library wide use only) 3183 */ 3184 boolean_t 3185 be_is_isa(char *name) 3186 { 3187 return ((strcmp((char *)be_get_default_isa(), name) == 0)); 3188 } 3189 3190 /* 3191 * Function: be_get_default_isa 3192 * Description: 3193 * Returns the default instruction set architecture of the 3194 * machine it is executed on. (eg. sparc, i386, ...) 3195 * NOTE: SYS_INST environment variable may override default 3196 * return value 3197 * Parameters: 3198 * none 3199 * Returns: 3200 * NULL - the architecture returned by sysinfo() was too 3201 * long for local variables 3202 * char * - pointer to a string containing the default 3203 * implementation 3204 * Scope: 3205 * Semi-private (library wide use only) 3206 */ 3207 char * 3208 be_get_default_isa(void) 3209 { 3210 int i; 3211 char *envp; 3212 static char default_inst[ARCH_LENGTH] = ""; 3213 3214 if (default_inst[0] == '\0') { 3215 if ((envp = getenv("SYS_INST")) != NULL) { 3216 if ((int)strlen(envp) >= ARCH_LENGTH) 3217 return (NULL); 3218 else 3219 (void) strcpy(default_inst, envp); 3220 } else { 3221 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH); 3222 if (i < 0 || i > ARCH_LENGTH) 3223 return (NULL); 3224 } 3225 } 3226 return (default_inst); 3227 } 3228 3229 /* 3230 * Function: be_get_platform 3231 * Description: 3232 * Returns the platfom name 3233 * Parameters: 3234 * none 3235 * Returns: 3236 * NULL - the platform name returned by sysinfo() was too 3237 * long for local variables 3238 * char * - pointer to a string containing the platform name 3239 * Scope: 3240 * Semi-private (library wide use only) 3241 */ 3242 char * 3243 be_get_platform(void) 3244 { 3245 int i; 3246 static char default_inst[ARCH_LENGTH] = ""; 3247 3248 if (default_inst[0] == '\0') { 3249 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH); 3250 if (i < 0 || i > ARCH_LENGTH) 3251 return (NULL); 3252 } 3253 return (default_inst); 3254 } 3255 3256 /* 3257 * Function: be_run_cmd 3258 * Description: 3259 * Runs a command in a separate subprocess. Splits out stdout from stderr 3260 * and sends each to its own buffer. Buffers must be pre-allocated and 3261 * passed in as arguments. Buffer sizes are also passed in as arguments. 3262 * 3263 * Notes / caveats: 3264 * - Command being run is assumed to not have any stdout or stderr 3265 * redirection. 3266 * - Commands which emit total stderr output of greater than PIPE_BUF 3267 * bytes can hang. For such commands, a different implementation 3268 * which uses poll(2) must be used. 3269 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and 3270 * the stream which would have gone to it is sent to the bit 3271 * bucket. 3272 * - stderr_buf cannot be NULL. 3273 * - Only subprocess errors are appended to the stderr_buf. Errors 3274 * running the command are reported through be_print_err(). 3275 * - Data which would overflow its respective buffer is sent to the bit 3276 * bucket. 3277 * 3278 * Parameters: 3279 * command: command to run. Assumed not to have embedded stdout 3280 * or stderr redirection. May have stdin redirection, 3281 * however. 3282 * stderr_buf: buffer returning subprocess stderr data. Errors 3283 * reported by this function are reported through 3284 * be_print_err(). 3285 * stderr_bufsize: size of stderr_buf 3286 * stdout_buf: buffer returning subprocess stdout data. 3287 * stdout_bufsize: size of stdout_buf 3288 * Returns: 3289 * BE_SUCCESS - The command ran successfully without returning 3290 * errors. 3291 * BE_ERR_EXTCMD 3292 * - The command could not be run. 3293 * - The command terminated with error status. 3294 * - There were errors extracting or returning subprocess 3295 * data. 3296 * BE_ERR_NOMEM - The command exceeds the command buffer size. 3297 * BE_ERR_INVAL - An invalid argument was specified. 3298 * Scope: 3299 * Semi-private (library wide use only) 3300 */ 3301 int 3302 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize, 3303 char *stdout_buf, int stdout_bufsize) 3304 { 3305 char *temp_filename = strdup(tmpnam(NULL)); 3306 FILE *stdout_str = NULL; 3307 FILE *stderr_str = NULL; 3308 char cmdline[BUFSIZ]; 3309 char oneline[BUFSIZ]; 3310 int exit_status; 3311 int rval = BE_SUCCESS; 3312 3313 if ((command == NULL) || (stderr_buf == NULL) || 3314 (stderr_bufsize <= 0) || (stdout_bufsize < 0) || 3315 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) { 3316 return (BE_ERR_INVAL); 3317 } 3318 3319 /* Set up command so popen returns stderr, not stdout */ 3320 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command, 3321 temp_filename) >= BUFSIZ) { 3322 rval = BE_ERR_NOMEM; 3323 goto cleanup; 3324 } 3325 3326 /* Set up the fifo that will make stderr available. */ 3327 if (mkfifo(temp_filename, 0600) != 0) { 3328 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"), 3329 strerror(errno)); 3330 rval = BE_ERR_EXTCMD; 3331 goto cleanup; 3332 } 3333 3334 if ((stdout_str = popen(cmdline, "r")) == NULL) { 3335 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"), 3336 strerror(errno)); 3337 rval = BE_ERR_EXTCMD; 3338 goto cleanup; 3339 } 3340 3341 if ((stderr_str = fopen(temp_filename, "r")) == NULL) { 3342 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"), 3343 strerror(errno)); 3344 (void) pclose(stdout_str); 3345 rval = BE_ERR_EXTCMD; 3346 goto cleanup; 3347 } 3348 3349 /* Read stdout first, as it usually outputs more than stderr. */ 3350 oneline[BUFSIZ-1] = '\0'; 3351 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) { 3352 if (stdout_str != NULL) { 3353 (void) strlcat(stdout_buf, oneline, stdout_bufsize); 3354 } 3355 } 3356 3357 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) { 3358 (void) strlcat(stderr_buf, oneline, stderr_bufsize); 3359 } 3360 3361 /* Close pipe, get exit status. */ 3362 if ((exit_status = pclose(stdout_str)) == -1) { 3363 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"), 3364 strerror(errno)); 3365 rval = BE_ERR_EXTCMD; 3366 } else if (WIFEXITED(exit_status)) { 3367 exit_status = (int)((char)WEXITSTATUS(exit_status)); 3368 /* 3369 * error code BC_NOUPDT means more recent version 3370 * is installed 3371 */ 3372 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) { 3373 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: " 3374 "command terminated with error status: %d\n"), 3375 exit_status); 3376 (void) strlcat(stderr_buf, oneline, stderr_bufsize); 3377 rval = BE_ERR_EXTCMD; 3378 } 3379 } else { 3380 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command " 3381 "terminated on signal: %s\n"), 3382 strsignal(WTERMSIG(exit_status))); 3383 (void) strlcat(stderr_buf, oneline, stderr_bufsize); 3384 rval = BE_ERR_EXTCMD; 3385 } 3386 3387 cleanup: 3388 (void) unlink(temp_filename); 3389 (void) free(temp_filename); 3390 3391 return (rval); 3392 } 3393 3394 /* ******************************************************************** */ 3395 /* Private Functions */ 3396 /* ******************************************************************** */ 3397 3398 /* 3399 * Function: update_dataset 3400 * Description: This function takes a dataset name and replaces the zpool 3401 * and be_name components of the dataset with the new be_name 3402 * zpool passed in. 3403 * Parameters: 3404 * dataset - name of dataset 3405 * dataset_len - lenth of buffer in which dataset is passed in. 3406 * be_name - name of new BE name to update to. 3407 * old_rc_loc - dataset under which the root container dataset 3408 * for the old BE lives. 3409 * new_rc_loc - dataset under which the root container dataset 3410 * for the new BE lives. 3411 * Returns: 3412 * BE_SUCCESS - Success 3413 * be_errno_t - Failure 3414 * Scope: 3415 * Private 3416 */ 3417 static int 3418 update_dataset(char *dataset, int dataset_len, char *be_name, 3419 char *old_rc_loc, char *new_rc_loc) 3420 { 3421 char *ds = NULL; 3422 char *sub_ds = NULL; 3423 3424 /* Tear off the BE container dataset */ 3425 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) { 3426 return (BE_ERR_INVAL); 3427 } 3428 3429 /* Get dataset name relative to BE root, if there is one */ 3430 sub_ds = strchr(ds, '/'); 3431 3432 /* Generate the BE root dataset name */ 3433 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len); 3434 3435 /* If a subordinate dataset name was found, append it */ 3436 if (sub_ds != NULL) 3437 (void) strlcat(dataset, sub_ds, dataset_len); 3438 3439 free(ds); 3440 return (BE_SUCCESS); 3441 } 3442 3443 /* 3444 * Function: _update_vfstab 3445 * Description: This function updates a vfstab file to reflect the new 3446 * root container dataset location and be_name for all 3447 * entries listed in the be_fs_list_data_t structure passed in. 3448 * Parameters: 3449 * vfstab - vfstab file to modify 3450 * be_name - name of BE to update. 3451 * old_rc_loc - dataset under which the root container dataset 3452 * of the old BE resides in. 3453 * new_rc_loc - dataset under which the root container dataset 3454 * of the new BE resides in. 3455 * fld - be_fs_list_data_t pointer providing the list of 3456 * file systems to look for in vfstab. 3457 * Returns: 3458 * BE_SUCCESS - Success 3459 * be_errno_t - Failure 3460 * Scope: 3461 * Private 3462 */ 3463 static int 3464 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc, 3465 char *new_rc_loc, be_fs_list_data_t *fld) 3466 { 3467 struct vfstab vp; 3468 char *tmp_vfstab = NULL; 3469 char comments_buf[BUFSIZ]; 3470 FILE *comments = NULL; 3471 FILE *vfs_ents = NULL; 3472 FILE *tfile = NULL; 3473 struct stat sb; 3474 char dev[MAXPATHLEN]; 3475 char *c; 3476 int fd; 3477 int ret = BE_SUCCESS, err = 0; 3478 int i; 3479 int tmp_vfstab_len = 0; 3480 3481 errno = 0; 3482 3483 /* 3484 * Open vfstab for reading twice. First is for comments, 3485 * second is for actual entries. 3486 */ 3487 if ((comments = fopen(vfstab, "r")) == NULL || 3488 (vfs_ents = fopen(vfstab, "r")) == NULL) { 3489 err = errno; 3490 be_print_err(gettext("_update_vfstab: " 3491 "failed to open vfstab (%s): %s\n"), vfstab, 3492 strerror(err)); 3493 ret = errno_to_be_err(err); 3494 goto cleanup; 3495 } 3496 3497 /* Grab the stats of the original vfstab file */ 3498 if (stat(vfstab, &sb) != 0) { 3499 err = errno; 3500 be_print_err(gettext("_update_vfstab: " 3501 "failed to stat file %s: %s\n"), vfstab, 3502 strerror(err)); 3503 ret = errno_to_be_err(err); 3504 goto cleanup; 3505 } 3506 3507 /* Create tmp file for modified vfstab */ 3508 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7)) 3509 == NULL) { 3510 be_print_err(gettext("_update_vfstab: " 3511 "malloc failed\n")); 3512 ret = BE_ERR_NOMEM; 3513 goto cleanup; 3514 } 3515 tmp_vfstab_len = strlen(vfstab) + 7; 3516 (void) memset(tmp_vfstab, 0, tmp_vfstab_len); 3517 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len); 3518 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len); 3519 if ((fd = mkstemp(tmp_vfstab)) == -1) { 3520 err = errno; 3521 be_print_err(gettext("_update_vfstab: " 3522 "mkstemp failed: %s\n"), strerror(err)); 3523 ret = errno_to_be_err(err); 3524 goto cleanup; 3525 } 3526 if ((tfile = fdopen(fd, "w")) == NULL) { 3527 err = errno; 3528 be_print_err(gettext("_update_vfstab: " 3529 "could not open file for write\n")); 3530 (void) close(fd); 3531 ret = errno_to_be_err(err); 3532 goto cleanup; 3533 } 3534 3535 while (fgets(comments_buf, BUFSIZ, comments)) { 3536 for (c = comments_buf; *c != '\0' && isspace(*c); c++) 3537 ; 3538 if (*c == '\0') { 3539 continue; 3540 } else if (*c == '#') { 3541 /* 3542 * If line is a comment line, just put 3543 * it through to the tmp vfstab. 3544 */ 3545 (void) fputs(comments_buf, tfile); 3546 } else { 3547 /* 3548 * Else line is a vfstab entry, grab it 3549 * into a vfstab struct. 3550 */ 3551 if (getvfsent(vfs_ents, &vp) != 0) { 3552 err = errno; 3553 be_print_err(gettext("_update_vfstab: " 3554 "getvfsent failed: %s\n"), strerror(err)); 3555 ret = errno_to_be_err(err); 3556 goto cleanup; 3557 } 3558 3559 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) { 3560 (void) putvfsent(tfile, &vp); 3561 continue; 3562 } 3563 3564 /* 3565 * If the entry is one of the entries in the list 3566 * of file systems to update, modify it's device 3567 * field to be correct for this BE. 3568 */ 3569 for (i = 0; i < fld->fs_num; i++) { 3570 if (strcmp(vp.vfs_special, fld->fs_list[i]) 3571 == 0) { 3572 /* 3573 * Found entry that needs an update. 3574 * Replace the root container dataset 3575 * location and be_name in the 3576 * entry's device. 3577 */ 3578 (void) strlcpy(dev, vp.vfs_special, 3579 sizeof (dev)); 3580 3581 if ((ret = update_dataset(dev, 3582 sizeof (dev), be_name, old_rc_loc, 3583 new_rc_loc)) != 0) { 3584 be_print_err( 3585 gettext("_update_vfstab: " 3586 "Failed to update device " 3587 "field for vfstab entry " 3588 "%s\n"), fld->fs_list[i]); 3589 goto cleanup; 3590 } 3591 3592 vp.vfs_special = dev; 3593 break; 3594 } 3595 } 3596 3597 /* Put entry through to tmp vfstab */ 3598 (void) putvfsent(tfile, &vp); 3599 } 3600 } 3601 3602 (void) fclose(comments); 3603 comments = NULL; 3604 (void) fclose(vfs_ents); 3605 vfs_ents = NULL; 3606 (void) fclose(tfile); 3607 tfile = NULL; 3608 3609 /* Copy tmp vfstab into place */ 3610 if (rename(tmp_vfstab, vfstab) != 0) { 3611 err = errno; 3612 be_print_err(gettext("_update_vfstab: " 3613 "failed to rename file %s to %s: %s\n"), tmp_vfstab, 3614 vfstab, strerror(err)); 3615 ret = errno_to_be_err(err); 3616 goto cleanup; 3617 } 3618 3619 /* Set the perms and ownership of the updated file */ 3620 if (chmod(vfstab, sb.st_mode) != 0) { 3621 err = errno; 3622 be_print_err(gettext("_update_vfstab: " 3623 "failed to chmod %s: %s\n"), vfstab, strerror(err)); 3624 ret = errno_to_be_err(err); 3625 goto cleanup; 3626 } 3627 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) { 3628 err = errno; 3629 be_print_err(gettext("_update_vfstab: " 3630 "failed to chown %s: %s\n"), vfstab, strerror(err)); 3631 ret = errno_to_be_err(err); 3632 goto cleanup; 3633 } 3634 3635 cleanup: 3636 if (comments != NULL) 3637 (void) fclose(comments); 3638 if (vfs_ents != NULL) 3639 (void) fclose(vfs_ents); 3640 (void) unlink(tmp_vfstab); 3641 (void) free(tmp_vfstab); 3642 if (tfile != NULL) 3643 (void) fclose(tfile); 3644 3645 return (ret); 3646 } 3647 3648 3649 /* 3650 * Function: be_get_auto_name 3651 * Description: Generate an auto name constructed based on the BE name 3652 * of the original BE or zone BE being cloned. 3653 * Parameters: 3654 * obe_name - name of the original BE or zone BE being cloned. 3655 * container_ds - container dataset for the zone. 3656 * Note: if zone_be is false this should be 3657 * NULL. 3658 * zone_be - flag that indicates if we are operating on a zone BE. 3659 * Returns: 3660 * Success - pointer to auto generated BE name. The name 3661 * is allocated in heap storage so the caller is 3662 * responsible for free'ing the name. 3663 * Failure - NULL 3664 * Scope: 3665 * Private 3666 */ 3667 static char * 3668 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be) 3669 { 3670 be_node_list_t *be_nodes = NULL; 3671 be_node_list_t *cur_be = NULL; 3672 char auto_be_name[MAXPATHLEN]; 3673 char base_be_name[MAXPATHLEN]; 3674 char cur_be_name[MAXPATHLEN]; 3675 char *num_str = NULL; 3676 char *c = NULL; 3677 int num = 0; 3678 int cur_num = 0; 3679 3680 errno = 0; 3681 3682 /* 3683 * Check if obe_name is already in an auto BE name format. 3684 * If it is, then strip off the increment number to get the 3685 * base name. 3686 */ 3687 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name)); 3688 3689 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM)) 3690 != NULL) { 3691 /* Make sure remaining string is all digits */ 3692 c = num_str + 1; 3693 while (c[0] != '\0' && isdigit(c[0])) 3694 c++; 3695 /* 3696 * If we're now at the end of the string strip off the 3697 * increment number. 3698 */ 3699 if (c[0] == '\0') 3700 num_str[0] = '\0'; 3701 } 3702 3703 if (zone_be) { 3704 if (be_container_ds == NULL) 3705 return (NULL); 3706 if (be_get_zone_be_list(obe_name, be_container_ds, 3707 &be_nodes) != BE_SUCCESS) { 3708 be_print_err(gettext("be_get_auto_name: " 3709 "be_get_zone_be_list failed\n")); 3710 return (NULL); 3711 } 3712 } else if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) { 3713 be_print_err(gettext("be_get_auto_name: be_list failed\n")); 3714 return (NULL); 3715 } 3716 3717 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) { 3718 (void) strlcpy(cur_be_name, cur_be->be_node_name, 3719 sizeof (cur_be_name)); 3720 3721 /* If cur_be_name doesn't match at least base be name, skip. */ 3722 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name)) 3723 != 0) 3724 continue; 3725 3726 /* Get the string following the base be name */ 3727 num_str = cur_be_name + strlen(base_be_name); 3728 3729 /* 3730 * If nothing follows the base be name, this cur_be_name 3731 * is the BE named with the base be name, skip. 3732 */ 3733 if (num_str == NULL || num_str[0] == '\0') 3734 continue; 3735 3736 /* 3737 * Remove the name delimiter. If its not there, 3738 * cur_be_name isn't part of this BE name stream, skip. 3739 */ 3740 if (num_str[0] == BE_AUTO_NAME_DELIM) 3741 num_str++; 3742 else 3743 continue; 3744 3745 /* Make sure remaining string is all digits */ 3746 c = num_str; 3747 while (c[0] != '\0' && isdigit(c[0])) 3748 c++; 3749 if (c[0] != '\0') 3750 continue; 3751 3752 /* Convert the number string to an int */ 3753 cur_num = atoi(num_str); 3754 3755 /* 3756 * If failed to convert the string, skip it. If its too 3757 * long to be converted to an int, we wouldn't auto generate 3758 * this number anyway so there couldn't be a conflict. 3759 * We treat it as a manually created BE name. 3760 */ 3761 if (cur_num == 0 && errno == EINVAL) 3762 continue; 3763 3764 /* 3765 * Compare current number to current max number, 3766 * take higher of the two. 3767 */ 3768 if (cur_num > num) 3769 num = cur_num; 3770 } 3771 3772 /* 3773 * Store off a copy of 'num' incase we need it later. If incrementing 3774 * 'num' causes it to roll over, this means 'num' is the largest 3775 * positive int possible; we'll need it later in the loop to determine 3776 * if we've exhausted all possible increment numbers. We store it in 3777 * 'cur_num'. 3778 */ 3779 cur_num = num; 3780 3781 /* Increment 'num' to get new auto BE name number */ 3782 if (++num <= 0) { 3783 int ret = 0; 3784 3785 /* 3786 * Since incrementing 'num' caused it to rollover, start 3787 * over at 0 and find the first available number. 3788 */ 3789 for (num = 0; num < cur_num; num++) { 3790 3791 (void) snprintf(cur_be_name, sizeof (cur_be_name), 3792 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num); 3793 3794 ret = zpool_iter(g_zfs, be_exists_callback, 3795 cur_be_name); 3796 3797 if (ret == 0) { 3798 /* 3799 * BE name doesn't exist, break out 3800 * to use 'num'. 3801 */ 3802 break; 3803 } else if (ret == 1) { 3804 /* BE name exists, continue looking */ 3805 continue; 3806 } else { 3807 be_print_err(gettext("be_get_auto_name: " 3808 "zpool_iter failed: %s\n"), 3809 libzfs_error_description(g_zfs)); 3810 be_free_list(be_nodes); 3811 return (NULL); 3812 } 3813 } 3814 3815 /* 3816 * If 'num' equals 'cur_num', we've exhausted all possible 3817 * auto BE names for this base BE name. 3818 */ 3819 if (num == cur_num) { 3820 be_print_err(gettext("be_get_auto_name: " 3821 "No more available auto BE names for base " 3822 "BE name %s\n"), base_be_name); 3823 be_free_list(be_nodes); 3824 return (NULL); 3825 } 3826 } 3827 3828 be_free_list(be_nodes); 3829 3830 /* 3831 * Generate string for auto BE name. 3832 */ 3833 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d", 3834 base_be_name, BE_AUTO_NAME_DELIM, num); 3835 3836 if ((c = strdup(auto_be_name)) == NULL) { 3837 be_print_err(gettext("be_get_auto_name: " 3838 "memory allocation failed\n")); 3839 return (NULL); 3840 } 3841 3842 return (c); 3843 } 3844 3845 /* 3846 * Function: be_get_console_prop 3847 * Description: Determine console device. 3848 * Returns: 3849 * Success - pointer to console setting. 3850 * Failure - NULL 3851 * Scope: 3852 * Private 3853 */ 3854 static char * 3855 be_get_console_prop(void) 3856 { 3857 di_node_t dn; 3858 char *console = NULL; 3859 3860 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) { 3861 be_print_err(gettext("be_get_console_prop: " 3862 "di_init() failed\n")); 3863 return (NULL); 3864 } 3865 3866 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn, 3867 "console", &console) != -1) { 3868 di_fini(dn); 3869 return (console); 3870 } 3871 3872 if (console == NULL) { 3873 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn, 3874 "output-device", &console) != -1) { 3875 di_fini(dn); 3876 if (strncmp(console, "screen", strlen("screen")) == 0) 3877 console = BE_DEFAULT_CONSOLE; 3878 } 3879 } 3880 3881 /* 3882 * Default console to text 3883 */ 3884 if (console == NULL) { 3885 console = BE_DEFAULT_CONSOLE; 3886 } 3887 3888 return (console); 3889 } 3890 3891 /* 3892 * Function: be_create_menu 3893 * Description: 3894 * This function is used if no menu.lst file exists. In 3895 * this case a new file is created and if needed default 3896 * lines are added to the file. 3897 * Parameters: 3898 * pool - The name of the pool the menu.lst file is on 3899 * menu_file - The name of the file we're creating. 3900 * menu_fp - A pointer to the file pointer of the file we 3901 * created. This is also used to pass back the file 3902 * pointer to the newly created file. 3903 * mode - the original mode used for the failed attempt to 3904 * non-existent file. 3905 * Returns: 3906 * BE_SUCCESS - Success 3907 * be_errno_t - Failure 3908 * Scope: 3909 * Private 3910 */ 3911 static int 3912 be_create_menu( 3913 char *pool, 3914 char *menu_file, 3915 FILE **menu_fp, 3916 char *mode) 3917 { 3918 be_node_list_t *be_nodes = NULL; 3919 char *menu_path = NULL; 3920 char *be_rpool = NULL; 3921 char *be_name = NULL; 3922 char *console = NULL; 3923 errno = 0; 3924 3925 if (menu_file == NULL || menu_fp == NULL || mode == NULL) 3926 return (BE_ERR_INVAL); 3927 3928 menu_path = strdup(menu_file); 3929 if (menu_path == NULL) 3930 return (BE_ERR_NOMEM); 3931 3932 (void) dirname(menu_path); 3933 if (*menu_path == '.') { 3934 free(menu_path); 3935 return (BE_ERR_BAD_MENU_PATH); 3936 } 3937 if (mkdirp(menu_path, 3938 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 && 3939 errno != EEXIST) { 3940 be_print_err(gettext("be_create_menu: Failed to create the %s " 3941 "directory: %s\n"), menu_path, strerror(errno)); 3942 free(menu_path); 3943 return (errno_to_be_err(errno)); 3944 } 3945 free(menu_path); 3946 3947 /* 3948 * Check to see if this system supports grub 3949 */ 3950 if (be_has_grub()) { 3951 /* 3952 * The grub menu is missing so we need to create it 3953 * and fill in the first few lines. 3954 */ 3955 FILE *temp_fp = fopen(menu_file, "a+"); 3956 if (temp_fp == NULL) { 3957 *menu_fp = NULL; 3958 return (errno_to_be_err(errno)); 3959 } 3960 3961 if ((console = be_get_console_prop()) != NULL) { 3962 3963 /* 3964 * If console is redirected to serial line, 3965 * GRUB splash screen will not be enabled. 3966 */ 3967 if (strncmp(console, "text", strlen("text")) == 0 || 3968 strncmp(console, "graphics", 3969 strlen("graphics")) == 0) { 3970 3971 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH); 3972 (void) fprintf(temp_fp, "%s\n", 3973 BE_GRUB_FOREGROUND); 3974 (void) fprintf(temp_fp, "%s\n", 3975 BE_GRUB_BACKGROUND); 3976 (void) fprintf(temp_fp, "%s\n", 3977 BE_GRUB_DEFAULT); 3978 } else { 3979 be_print_err(gettext("be_create_menu: " 3980 "console on serial line, " 3981 "GRUB splash image will be disabled\n")); 3982 } 3983 } 3984 3985 (void) fprintf(temp_fp, "timeout 30\n"); 3986 (void) fclose(temp_fp); 3987 3988 } else { 3989 /* 3990 * The menu file doesn't exist so we need to create a 3991 * blank file. 3992 */ 3993 FILE *temp_fp = fopen(menu_file, "w+"); 3994 if (temp_fp == NULL) { 3995 *menu_fp = NULL; 3996 return (errno_to_be_err(errno)); 3997 } 3998 (void) fclose(temp_fp); 3999 } 4000 4001 /* 4002 * Now we need to add all the BE's back into the the file. 4003 */ 4004 if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) { 4005 while (be_nodes != NULL) { 4006 if (strcmp(pool, be_nodes->be_rpool) == 0) { 4007 (void) be_append_menu(be_nodes->be_node_name, 4008 be_nodes->be_rpool, NULL, NULL, NULL); 4009 } 4010 if (be_nodes->be_active_on_boot) { 4011 be_rpool = strdup(be_nodes->be_rpool); 4012 be_name = strdup(be_nodes->be_node_name); 4013 } 4014 4015 be_nodes = be_nodes->be_next_node; 4016 } 4017 } 4018 be_free_list(be_nodes); 4019 4020 /* 4021 * Check to see if this system supports grub 4022 */ 4023 if (be_has_grub()) { 4024 int err = be_change_grub_default(be_name, be_rpool); 4025 if (err != BE_SUCCESS) 4026 return (err); 4027 } 4028 *menu_fp = fopen(menu_file, mode); 4029 if (*menu_fp == NULL) 4030 return (errno_to_be_err(errno)); 4031 4032 return (BE_SUCCESS); 4033 } 4034 4035 /* 4036 * Function: be_open_menu 4037 * Description: 4038 * This function is used it open the menu.lst file. If this 4039 * file does not exist be_create_menu is called to create it 4040 * and the open file pointer is returned. If the file does 4041 * exist it is simply opened using the mode passed in. 4042 * Parameters: 4043 * pool - The name of the pool the menu.lst file is on 4044 * menu_file - The name of the file we're opening. 4045 * menu_fp - A pointer to the file pointer of the file we're 4046 * opening. This is also used to pass back the file 4047 * pointer. 4048 * mode - the original mode to be used for opening the menu.lst 4049 * file. 4050 * create_menu - If this is true and the menu.lst file does not 4051 * exist we will attempt to re-create it. However 4052 * if it's false the error returned from the fopen 4053 * will be returned. 4054 * Returns: 4055 * BE_SUCCESS - Success 4056 * be_errno_t - Failure 4057 * Scope: 4058 * Private 4059 */ 4060 static int 4061 be_open_menu( 4062 char *pool, 4063 char *menu_file, 4064 FILE **menu_fp, 4065 char *mode, 4066 boolean_t create_menu) 4067 { 4068 int err = 0; 4069 boolean_t set_print = B_FALSE; 4070 4071 *menu_fp = fopen(menu_file, mode); 4072 err = errno; 4073 if (*menu_fp == NULL) { 4074 if (err == ENOENT && create_menu) { 4075 be_print_err(gettext("be_open_menu: menu.lst " 4076 "file %s does not exist,\n"), menu_file); 4077 if (!do_print) { 4078 set_print = B_TRUE; 4079 do_print = B_TRUE; 4080 } 4081 be_print_err(gettext("WARNING: menu.lst " 4082 "file %s does not exist,\n generating " 4083 "a new menu.lst file\n"), menu_file); 4084 if (set_print) 4085 do_print = B_FALSE; 4086 err = 0; 4087 if ((err = be_create_menu(pool, menu_file, 4088 menu_fp, mode)) == ENOENT) 4089 return (BE_ERR_NO_MENU); 4090 else if (err != BE_SUCCESS) 4091 return (err); 4092 else if (*menu_fp == NULL) 4093 return (BE_ERR_NO_MENU); 4094 } else { 4095 be_print_err(gettext("be_open_menu: failed " 4096 "to open menu.lst file %s\n"), menu_file); 4097 if (err == ENOENT) 4098 return (BE_ERR_NO_MENU); 4099 else 4100 return (errno_to_be_err(err)); 4101 } 4102 } 4103 return (BE_SUCCESS); 4104 } 4105