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