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