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