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