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