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