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