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