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