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