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