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