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