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