view usr/src/lib/libbe/common/be_utils.c @ 13305:c0863802718a

720 be_utils should not use python Reviewed by: Garrett D'Amore <garrett@nexenta.com> Reviewed by: Dan McDonald <danmcd@nexenta.com> Approved by: Gordon Ross <gwr@nexenta.com>
author Alexander Eremin <a.eremin@nexenta.com>
date Fri, 18 Mar 2011 14:06:44 -0400
parents f6c814f505e4
children 70ccb19abd77
line wrap: on
line source

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/*
 * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
 */


/*
 * System includes
 */
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libzfs.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/vfstab.h>
#include <sys/param.h>
#include <sys/systeminfo.h>
#include <ctype.h>
#include <time.h>
#include <unistd.h>
#include <fcntl.h>
#include <wait.h>
#include <libdevinfo.h>

#include <libbe.h>
#include <libbe_priv.h>

/* Private function prototypes */
static int update_dataset(char *, int, char *, char *, char *);
static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
static int be_open_menu(char *, char *, char *, FILE **, char *, boolean_t);
static int be_create_menu(char *, char *, char *, FILE **, char *);
static char *be_get_auto_name(char *, char *, boolean_t);

/*
 * Global error printing
 */
boolean_t do_print = B_FALSE;

/*
 * Private datatypes
 */
typedef struct zone_be_name_cb_data {
	char *base_be_name;
	int num;
} zone_be_name_cb_data_t;

/* ********************************************************************	*/
/*			Public Functions				*/
/* ******************************************************************** */

/*
 * Function:	be_max_avail
 * Description:	Returns the available size for the zfs dataset passed in.
 * Parameters:
 *		dataset - The dataset we want to get the available space for.
 *		ret - The available size will be returned in this.
 * Returns:
 *		The error returned by the zfs get property function.
 * Scope:
 *		Public
 */
int
be_max_avail(char *dataset, uint64_t *ret)
{
	zfs_handle_t *zhp;
	int err = 0;

	/* Initialize libzfs handle */
	if (!be_zfs_init())
		return (BE_ERR_INIT);

	zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
	if (zhp == NULL) {
		/*
		 * The zfs_open failed return an error
		 */
		err = zfs_err_to_be_err(g_zfs);
	} else {
		err = be_maxsize_avail(zhp, ret);
	}
	ZFS_CLOSE(zhp);
	be_zfs_fini();
	return (err);
}

/*
 * Function:	libbe_print_errors
 * Description:	Turns on/off error output for the library.
 * Parameter:
 *		set_do_print - Boolean that turns library error
 *			       printing on or off.
 * Returns:
 *		None
 * Scope:
 *		Public;
 */
void
libbe_print_errors(boolean_t set_do_print)
{
	do_print = set_do_print;
}

/* ********************************************************************	*/
/*			Semi-Private Functions				*/
/* ******************************************************************** */

/*
 * Function:	be_zfs_init
 * Description:	Initializes the libary global libzfs handle.
 * Parameters:
 *		None
 * Returns:
 *		B_TRUE - Success
 *		B_FALSE - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
boolean_t
be_zfs_init(void)
{
	be_zfs_fini();

	if ((g_zfs = libzfs_init()) == NULL) {
		be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
		    "library\n"));
		return (B_FALSE);
	}

	return (B_TRUE);
}

/*
 * Function:	be_zfs_fini
 * Description:	Closes the library global libzfs handle if it currently open.
 * Parameter:
 *		None
 * Returns:
 *		None
 * Scope:
 *		Semi-private (library wide use only)
 */
void
be_zfs_fini(void)
{
	if (g_zfs)
		libzfs_fini(g_zfs);

	g_zfs = NULL;
}

/*
 * Function:	be_make_root_ds
 * Description:	Generate string for BE's root dataset given the pool
 *		it lives in and the BE name.
 * Parameters:
 *		zpool - pointer zpool name.
 *		be_name - pointer to BE name.
 *		be_root_ds - pointer to buffer to return BE root dataset in.
 *		be_root_ds_size - size of be_root_ds
 * Returns:
 *		None
 * Scope:
 *		Semi-private (library wide use only)
 */
void
be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
    int be_root_ds_size)
{
	(void) snprintf(be_root_ds, be_root_ds_size, "%s/%s/%s", zpool,
	    BE_CONTAINER_DS_NAME, be_name);
}

/*
 * Function:	be_make_container_ds
 * Description:	Generate string for the BE container dataset given a pool name.
 * Parameters:
 *		zpool - pointer zpool name.
 *		container_ds - pointer to buffer to return BE container
 *			dataset in.
 *		container_ds_size - size of container_ds
 * Returns:
 *		None
 * Scope:
 *		Semi-private (library wide use only)
 */
void
be_make_container_ds(const char *zpool,  char *container_ds,
    int container_ds_size)
{
	(void) snprintf(container_ds, container_ds_size, "%s/%s", zpool,
	    BE_CONTAINER_DS_NAME);
}

/*
 * Function:	be_make_name_from_ds
 * Description:	This function takes a dataset name and strips off the
 *		BE container dataset portion from the beginning.  The
 *		returned name is allocated in heap storage, so the caller
 *		is responsible for freeing it.
 * Parameters:
 *		dataset - dataset to get name from.
 *		rc_loc - dataset underwhich the root container dataset lives.
 * Returns:
 *		name of dataset relative to BE container dataset.
 *		NULL if dataset is not under a BE root dataset.
 * Scope:
 *		Semi-primate (library wide use only)
 */
char *
be_make_name_from_ds(const char *dataset, char *rc_loc)
{
	char	ds[ZFS_MAXNAMELEN];
	char	*tok = NULL;
	char	*name = NULL;

	/*
	 * First token is the location of where the root container dataset
	 * lives; it must match rc_loc.
	 */
	if (strncmp(dataset, rc_loc, strlen(rc_loc)) == 0 &&
	    dataset[strlen(rc_loc)] == '/') {
		(void) strlcpy(ds, dataset + strlen(rc_loc) + 1, sizeof (ds));
	} else {
		return (NULL);
	}

	/* Second token must be BE container dataset name */
	if ((tok = strtok(ds, "/")) == NULL ||
	    strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
		return (NULL);

	/* Return the remaining token if one exists */
	if ((tok = strtok(NULL, "")) == NULL)
		return (NULL);

	if ((name = strdup(tok)) == NULL) {
		be_print_err(gettext("be_make_name_from_ds: "
		    "memory allocation failed\n"));
		return (NULL);
	}

	return (name);
}

/*
 * Function:	be_maxsize_avail
 * Description:	Returns the available size for the zfs handle passed in.
 * Parameters:
 *		zhp - A pointer to the open zfs handle.
 *		ret - The available size will be returned in this.
 * Returns:
 *		The error returned by the zfs get property function.
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
{
	return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
}

/*
 * Function:	be_append_menu
 * Description:	Appends an entry for a BE into the menu.lst.
 * Parameters:
 *		be_name - pointer to name of BE to add boot menu entry for.
 *		be_root_pool - pointer to name of pool BE lives in.
 *		boot_pool - Used if the pool containing the grub menu is
 *			    different than the one contaiing the BE. This
 *			    will normally be NULL.
 *		be_orig_root_ds - The root dataset for the BE. This is
 *			used to check to see if an entry already exists
 *			for this BE.
 *		description - pointer to description of BE to be added in
 *			the title line for this BEs entry.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
    char *be_orig_root_ds, char *description)
{
	zfs_handle_t *zhp = NULL;
	char menu_file[MAXPATHLEN];
	char be_root_ds[MAXPATHLEN];
	char line[BUFSIZ];
	char temp_line[BUFSIZ];
	char title[MAXPATHLEN];
	char *entries[BUFSIZ];
	char *tmp_entries[BUFSIZ];
	char *pool_mntpnt = NULL;
	char *ptmp_mntpnt = NULL;
	char *orig_mntpnt = NULL;
	boolean_t found_be = B_FALSE;
	boolean_t found_orig_be = B_FALSE;
	boolean_t found_title = B_FALSE;
	boolean_t pool_mounted = B_FALSE;
	boolean_t collect_lines = B_FALSE;
	FILE *menu_fp = NULL;
	int err = 0, ret = BE_SUCCESS;
	int i, num_tmp_lines = 0, num_lines = 0;

	if (be_name == NULL || be_root_pool == NULL)
		return (BE_ERR_INVAL);

	if (boot_pool == NULL)
		boot_pool = be_root_pool;

	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
		be_print_err(gettext("be_append_menu: failed to open "
		    "pool dataset for %s: %s\n"), be_root_pool,
		    libzfs_error_description(g_zfs));
		return (zfs_err_to_be_err(g_zfs));
	}

	/*
	 * Check to see if the pool's dataset is mounted. If it isn't we'll
	 * attempt to mount it.
	 */
	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
	    &pool_mounted)) != BE_SUCCESS) {
		be_print_err(gettext("be_append_menu: pool dataset "
		    "(%s) could not be mounted\n"), be_root_pool);
		ZFS_CLOSE(zhp);
		return (ret);
	}

	/*
	 * Get the mountpoint for the root pool dataset.
	 */
	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
		be_print_err(gettext("be_append_menu: pool "
		    "dataset (%s) is not mounted. Can't set "
		    "the default BE in the grub menu.\n"), be_root_pool);
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	/*
	 * Check to see if this system supports grub
	 */
	if (be_has_grub()) {
		(void) snprintf(menu_file, sizeof (menu_file),
		    "%s%s", pool_mntpnt, BE_GRUB_MENU);
	} else {
		(void) snprintf(menu_file, sizeof (menu_file),
		    "%s%s", pool_mntpnt, BE_SPARC_MENU);
	}

	be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));

	/*
	 * Iterate through menu first to make sure the BE doesn't already
	 * have an entry in the menu.
	 *
	 * Additionally while iterating through the menu, if we have an
	 * original root dataset for a BE we're cloning from, we need to keep
	 * track of that BE's menu entry. We will then use the lines from
	 * that entry to create the entry for the new BE.
	 */
	if ((ret = be_open_menu(be_root_pool, pool_mntpnt, menu_file,
	    &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
		goto cleanup;
	} else if (menu_fp == NULL) {
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	free(pool_mntpnt);
	pool_mntpnt = NULL;

	while (fgets(line, BUFSIZ, menu_fp)) {
		char *tok = NULL;

		(void) strlcpy(temp_line, line, BUFSIZ);
		tok = strtok(line, BE_WHITE_SPACE);

		if (tok == NULL || tok[0] == '#') {
			continue;
		} else if (strcmp(tok, "title") == 0) {
			collect_lines = B_FALSE;
			if ((tok = strtok(NULL, "\n")) == NULL)
				(void) strlcpy(title, "", sizeof (title));
			else
				(void) strlcpy(title, tok, sizeof (title));
			found_title = B_TRUE;

			if (num_tmp_lines != 0) {
				for (i = 0; i < num_tmp_lines; i++) {
					free(tmp_entries[i]);
					tmp_entries[i] = NULL;
				}
				num_tmp_lines = 0;
			}
		} else if (strcmp(tok, "bootfs") == 0) {
			char *bootfs = strtok(NULL, BE_WHITE_SPACE);
			found_title = B_FALSE;
			if (bootfs == NULL)
				continue;

			if (strcmp(bootfs, be_root_ds) == 0) {
				found_be = B_TRUE;
				break;
			}

			if (be_orig_root_ds != NULL &&
			    strcmp(bootfs, be_orig_root_ds) == 0 &&
			    !found_orig_be) {
				char str[BUFSIZ];
				found_orig_be = B_TRUE;
				num_lines = 0;
				/*
				 * Store the new title line
				 */
				(void) snprintf(str, BUFSIZ, "title %s\n",
				    description ? description : be_name);
				entries[num_lines] = strdup(str);
				num_lines++;
				/*
				 * If there are any lines between the title
				 * and the bootfs line store these. Also
				 * free the temporary lines.
				 */
				for (i = 0; i < num_tmp_lines; i++) {
					entries[num_lines] = tmp_entries[i];
					tmp_entries[i] = NULL;
					num_lines++;
				}
				num_tmp_lines = 0;
				/*
				 * Store the new bootfs line.
				 */
				(void) snprintf(str, BUFSIZ, "bootfs %s\n",
				    be_root_ds);
				entries[num_lines] = strdup(str);
				num_lines++;
				collect_lines = B_TRUE;
			}
		} else if (found_orig_be && collect_lines) {
			/*
			 * get the rest of the lines for the original BE and
			 * store them.
			 */
			if (strstr(line, BE_GRUB_COMMENT) != NULL ||
			    strstr(line, "BOOTADM") != NULL)
				continue;
			if (strcmp(tok, "splashimage") == 0) {
				entries[num_lines] =
				    strdup("splashimage "
				    "/boot/splashimage.xpm\n");
			} else {
				entries[num_lines] = strdup(temp_line);
			}
			num_lines++;
		} else if (found_title && !found_orig_be) {
			tmp_entries[num_tmp_lines] = strdup(temp_line);
			num_tmp_lines++;
		}
	}

	(void) fclose(menu_fp);

	if (found_be) {
		/*
		 * If an entry for this BE was already in the menu, then if
		 * that entry's title matches what we would have put in
		 * return success.  Otherwise return failure.
		 */
		char *new_title = description ? description : be_name;

		if (strcmp(title, new_title) == 0) {
			ret = BE_SUCCESS;
			goto cleanup;
		} else {
			if (be_remove_menu(be_name, be_root_pool,
			    boot_pool) != BE_SUCCESS) {
				be_print_err(gettext("be_append_menu: "
				    "Failed to remove existing unusable "
				    "entry '%s' in boot menu.\n"), be_name);
				ret = BE_ERR_BE_EXISTS;
				goto cleanup;
			}
		}
	}

	/* Append BE entry to the end of the file */
	menu_fp = fopen(menu_file, "a+");
	err = errno;
	if (menu_fp == NULL) {
		be_print_err(gettext("be_append_menu: failed "
		    "to open menu.lst file %s\n"), menu_file);
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	if (found_orig_be) {
		/*
		 * write out all the stored lines
		 */
		for (i = 0; i < num_lines; i++) {
			(void) fprintf(menu_fp, "%s", entries[i]);
			free(entries[i]);
		}
		num_lines = 0;

		/*
		 * Check to see if this system supports grub
		 */
		if (be_has_grub())
			(void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
		ret = BE_SUCCESS;
	} else {
		(void) fprintf(menu_fp, "title %s\n",
		    description ? description : be_name);
		(void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);

		/*
		 * Check to see if this system supports grub
		 */
		if (be_has_grub()) {
			(void) fprintf(menu_fp, "kernel$ "
			    "/platform/i86pc/kernel/$ISADIR/unix -B "
			    "$ZFS-BOOTFS\n");
			(void) fprintf(menu_fp, "module$ "
			    "/platform/i86pc/$ISADIR/boot_archive\n");
			(void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
		}
		ret = BE_SUCCESS;
	}
	(void) fclose(menu_fp);
cleanup:
	if (pool_mounted) {
		int err = BE_SUCCESS;
		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
		if (ret == BE_SUCCESS)
			ret = err;
		free(orig_mntpnt);
		free(ptmp_mntpnt);
	}
	ZFS_CLOSE(zhp);
	if (num_tmp_lines > 0) {
		for (i = 0; i < num_tmp_lines; i++) {
			free(tmp_entries[i]);
			tmp_entries[i] = NULL;
		}
	}
	if (num_lines > 0) {
		for (i = 0; i < num_lines; i++) {
			free(entries[i]);
			entries[i] = NULL;
		}
	}
	return (ret);
}

/*
 * Function:	be_remove_menu
 * Description:	Removes a BE's entry from a menu.lst file.
 * Parameters:
 *		be_name - the name of BE whose entry is to be removed from
 *			the menu.lst file.
 *		be_root_pool - the pool that be_name lives in.
 *		boot_pool - the pool where the BE is, if different than
 *			the pool containing the boot menu.  If this is
 *			NULL it will be set to be_root_pool.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
{
	zfs_handle_t	*zhp = NULL;
	char		be_root_ds[MAXPATHLEN];
	char		**buffer = NULL;
	char		menu_buf[BUFSIZ];
	char		menu[MAXPATHLEN];
	char		*pool_mntpnt = NULL;
	char		*ptmp_mntpnt = NULL;
	char		*orig_mntpnt = NULL;
	char		*tmp_menu = NULL;
	FILE		*menu_fp = NULL;
	FILE		*tmp_menu_fp = NULL;
	struct stat	sb;
	int		ret = BE_SUCCESS;
	int		i;
	int		fd;
	int		err = 0;
	int		nlines = 0;
	int		default_entry = 0;
	int		entry_cnt = 0;
	int		entry_del = 0;
	int		num_entry_del = 0;
	int		tmp_menu_len = 0;
	boolean_t	write = B_TRUE;
	boolean_t	do_buffer = B_FALSE;
	boolean_t	pool_mounted = B_FALSE;

	if (boot_pool == NULL)
		boot_pool = be_root_pool;

	/* Get name of BE's root dataset */
	be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));

	/* Get handle to pool dataset */
	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
		be_print_err(gettext("be_remove_menu: "
		    "failed to open pool dataset for %s: %s"),
		    be_root_pool, libzfs_error_description(g_zfs));
		return (zfs_err_to_be_err(g_zfs));
	}

	/*
	 * Check to see if the pool's dataset is mounted. If it isn't we'll
	 * attempt to mount it.
	 */
	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
	    &pool_mounted)) != BE_SUCCESS) {
		be_print_err(gettext("be_remove_menu: pool dataset "
		    "(%s) could not be mounted\n"), be_root_pool);
		ZFS_CLOSE(zhp);
		return (ret);
	}

	/*
	 * Get the mountpoint for the root pool dataset.
	 */
	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
		be_print_err(gettext("be_remove_menu: pool "
		    "dataset (%s) is not mounted. Can't set "
		    "the default BE in the grub menu.\n"), be_root_pool);
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	/* Get path to boot menu */
	(void) strlcpy(menu, pool_mntpnt, sizeof (menu));

	/*
	 * Check to see if this system supports grub
	 */
	if (be_has_grub())
		(void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
	else
		(void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));

	/* Get handle to boot menu file */
	if ((ret = be_open_menu(be_root_pool, pool_mntpnt, menu, &menu_fp, "r",
	    B_TRUE)) != BE_SUCCESS) {
		goto cleanup;
	} else if (menu_fp == NULL) {
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	free(pool_mntpnt);
	pool_mntpnt = NULL;

	/* Grab the stats of the original menu file */
	if (stat(menu, &sb) != 0) {
		err = errno;
		be_print_err(gettext("be_remove_menu: "
		    "failed to stat file %s: %s\n"), menu, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	/* Create a tmp file for the modified menu.lst */
	tmp_menu_len = strlen(menu) + 7;
	if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
		be_print_err(gettext("be_remove_menu: malloc failed\n"));
		ret = BE_ERR_NOMEM;
		goto cleanup;
	}
	(void) memset(tmp_menu, 0, tmp_menu_len);
	(void) strlcpy(tmp_menu, menu, tmp_menu_len);
	(void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
	if ((fd = mkstemp(tmp_menu)) == -1) {
		err = errno;
		be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
		ret = errno_to_be_err(err);
		free(tmp_menu);
		tmp_menu = NULL;
		goto cleanup;
	}
	if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
		err = errno;
		be_print_err(gettext("be_remove_menu: "
		    "could not open tmp file for write: %s\n"), strerror(err));
		(void) close(fd);
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	while (fgets(menu_buf, BUFSIZ, menu_fp)) {
		char tline [BUFSIZ];
		char *tok = NULL;

		(void) strlcpy(tline, menu_buf, sizeof (tline));

		/* Tokenize line */
		tok = strtok(tline, BE_WHITE_SPACE);

		if (tok == NULL || tok[0] == '#') {
			/* Found empty line or comment line */
			if (do_buffer) {
				/* Buffer this line */
				if ((buffer = (char **)realloc(buffer,
				    sizeof (char *)*(nlines + 1))) == NULL) {
					ret = BE_ERR_NOMEM;
					goto cleanup;
				}
				if ((buffer[nlines++] = strdup(menu_buf))
				    == NULL) {
					ret = BE_ERR_NOMEM;
					goto cleanup;
				}

			} else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
			    strlen(BE_GRUB_COMMENT)) != 0) {
				/* Write this line out */
				(void) fputs(menu_buf, tmp_menu_fp);
			}
		} else if (strcmp(tok, "default") == 0) {
			/*
			 * Record what 'default' is set to because we might
			 * need to adjust this upon deleting an entry.
			 */
			tok = strtok(NULL, BE_WHITE_SPACE);

			if (tok != NULL) {
				default_entry = atoi(tok);
			}

			(void) fputs(menu_buf, tmp_menu_fp);
		} else if (strcmp(tok, "title") == 0) {
			/*
			 * If we've reached a 'title' line and do_buffer is
			 * is true, that means we've just buffered an entire
			 * entry without finding a 'bootfs' directive.  We
			 * need to write that entry out and keep searching.
			 */
			if (do_buffer) {
				for (i = 0; i < nlines; i++) {
					(void) fputs(buffer[i], tmp_menu_fp);
					free(buffer[i]);
				}
				free(buffer);
				buffer = NULL;
				nlines = 0;
			}

			/*
			 * Turn writing off and buffering on, and increment
			 * our entry counter.
			 */
			write = B_FALSE;
			do_buffer = B_TRUE;
			entry_cnt++;

			/* Buffer this 'title' line */
			if ((buffer = (char **)realloc(buffer,
			    sizeof (char *)*(nlines + 1))) == NULL) {
				ret = BE_ERR_NOMEM;
				goto cleanup;
			}
			if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
				ret = BE_ERR_NOMEM;
				goto cleanup;
			}

		} else if (strcmp(tok, "bootfs") == 0) {
			char *bootfs = NULL;

			/*
			 * Found a 'bootfs' line.  See if it matches the
			 * BE we're looking for.
			 */
			if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
			    strcmp(bootfs, be_root_ds) != 0) {
				/*
				 * Either there's nothing after the 'bootfs'
				 * or this is not the BE we're looking for,
				 * write out the line(s) we've buffered since
				 * finding the title.
				 */
				for (i = 0; i < nlines; i++) {
					(void) fputs(buffer[i], tmp_menu_fp);
					free(buffer[i]);
				}
				free(buffer);
				buffer = NULL;
				nlines = 0;

				/*
				 * Turn writing back on, and turn off buffering
				 * since this isn't the entry we're looking
				 * for.
				 */
				write = B_TRUE;
				do_buffer = B_FALSE;

				/* Write this 'bootfs' line out. */
				(void) fputs(menu_buf, tmp_menu_fp);
			} else {
				/*
				 * Found the entry we're looking for.
				 * Record its entry number, increment the
				 * number of entries we've deleted, and turn
				 * writing off.  Also, throw away the lines
				 * we've buffered for this entry so far, we
				 * don't need them.
				 */
				entry_del = entry_cnt - 1;
				num_entry_del++;
				write = B_FALSE;
				do_buffer = B_FALSE;

				for (i = 0; i < nlines; i++) {
					free(buffer[i]);
				}
				free(buffer);
				buffer = NULL;
				nlines = 0;
			}
		} else {
			if (do_buffer) {
				/* Buffer this line */
				if ((buffer = (char **)realloc(buffer,
				    sizeof (char *)*(nlines + 1))) == NULL) {
					ret = BE_ERR_NOMEM;
					goto cleanup;
				}
				if ((buffer[nlines++] = strdup(menu_buf))
				    == NULL) {
					ret = BE_ERR_NOMEM;
					goto cleanup;
				}
			} else if (write) {
				/* Write this line out */
				(void) fputs(menu_buf, tmp_menu_fp);
			}
		}
	}

	(void) fclose(menu_fp);
	menu_fp = NULL;
	(void) fclose(tmp_menu_fp);
	tmp_menu_fp = NULL;

	/* Copy the modified menu.lst into place */
	if (rename(tmp_menu, menu) != 0) {
		err = errno;
		be_print_err(gettext("be_remove_menu: "
		    "failed to rename file %s to %s: %s\n"),
		    tmp_menu, menu, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	free(tmp_menu);
	tmp_menu = NULL;

	/*
	 * If we've removed an entry, see if we need to
	 * adjust the default value in the menu.lst.  If the
	 * entry we've deleted comes before the default entry
	 * we need to adjust the default value accordingly.
	 *
	 * be_has_grub is used here to check to see if this system
	 * supports grub.
	 */
	if (be_has_grub() && num_entry_del > 0) {
		if (entry_del <= default_entry) {
			default_entry = default_entry - num_entry_del;
			if (default_entry < 0)
				default_entry = 0;

			/*
			 * Adjust the default value by rewriting the
			 * menu.lst file.  This may be overkill, but to
			 * preserve the location of the 'default' entry
			 * in the file, we need to do this.
			 */

			/* Get handle to boot menu file */
			if ((menu_fp = fopen(menu, "r")) == NULL) {
				err = errno;
				be_print_err(gettext("be_remove_menu: "
				    "failed to open menu.lst (%s): %s\n"),
				    menu, strerror(err));
				ret = errno_to_be_err(err);
				goto cleanup;
			}

			/* Create a tmp file for the modified menu.lst */
			tmp_menu_len = strlen(menu) + 7;
			if ((tmp_menu = (char *)malloc(tmp_menu_len))
			    == NULL) {
				be_print_err(gettext("be_remove_menu: "
				    "malloc failed\n"));
				ret = BE_ERR_NOMEM;
				goto cleanup;
			}
			(void) memset(tmp_menu, 0, tmp_menu_len);
			(void) strlcpy(tmp_menu, menu, tmp_menu_len);
			(void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
			if ((fd = mkstemp(tmp_menu)) == -1) {
				err = errno;
				be_print_err(gettext("be_remove_menu: "
				    "mkstemp failed: %s\n"), strerror(err));
				ret = errno_to_be_err(err);
				free(tmp_menu);
				tmp_menu = NULL;
				goto cleanup;
			}
			if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
				err = errno;
				be_print_err(gettext("be_remove_menu: "
				    "could not open tmp file for write: %s\n"),
				    strerror(err));
				(void) close(fd);
				ret = errno_to_be_err(err);
				goto cleanup;
			}

			while (fgets(menu_buf, BUFSIZ, menu_fp)) {
				char tline [BUFSIZ];
				char *tok = NULL;

				(void) strlcpy(tline, menu_buf, sizeof (tline));

				/* Tokenize line */
				tok = strtok(tline, BE_WHITE_SPACE);

				if (tok == NULL) {
					/* Found empty line, write it out */
					(void) fputs(menu_buf, tmp_menu_fp);
				} else if (strcmp(tok, "default") == 0) {
					/* Found the default line, adjust it */
					(void) snprintf(tline, sizeof (tline),
					    "default %d\n", default_entry);

					(void) fputs(tline, tmp_menu_fp);
				} else {
					/* Pass through all other lines */
					(void) fputs(menu_buf, tmp_menu_fp);
				}
			}

			(void) fclose(menu_fp);
			menu_fp = NULL;
			(void) fclose(tmp_menu_fp);
			tmp_menu_fp = NULL;

			/* Copy the modified menu.lst into place */
			if (rename(tmp_menu, menu) != 0) {
				err = errno;
				be_print_err(gettext("be_remove_menu: "
				    "failed to rename file %s to %s: %s\n"),
				    tmp_menu, menu, strerror(err));
				ret = errno_to_be_err(err);
				goto cleanup;
			}

			free(tmp_menu);
			tmp_menu = NULL;
		}
	}

	/* Set the perms and ownership of the updated file */
	if (chmod(menu, sb.st_mode) != 0) {
		err = errno;
		be_print_err(gettext("be_remove_menu: "
		    "failed to chmod %s: %s\n"), menu, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
		err = errno;
		be_print_err(gettext("be_remove_menu: "
		    "failed to chown %s: %s\n"), menu, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

cleanup:
	if (pool_mounted) {
		int err = BE_SUCCESS;
		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
		if (ret == BE_SUCCESS)
			ret = err;
		free(orig_mntpnt);
		free(ptmp_mntpnt);
	}
	ZFS_CLOSE(zhp);

	free(buffer);
	if (menu_fp != NULL)
		(void) fclose(menu_fp);
	if (tmp_menu_fp != NULL)
		(void) fclose(tmp_menu_fp);
	if (tmp_menu != NULL) {
		(void) unlink(tmp_menu);
		free(tmp_menu);
	}

	return (ret);
}

/*
 * Function:	be_default_grub_bootfs
 * Description:	This function returns the dataset in the default entry of
 *		the grub menu. If no default entry is found with a valid bootfs
 *		entry NULL is returned.
 * Parameters:
 *		be_root_pool - This is the name of the root pool where the
 *			       grub menu can be found.
 *              def_bootfs - This is used to pass back the bootfs string. On
 *				error NULL is returned here.
 * Returns:
 *		Success - BE_SUCCESS is returned.
 *		Failure - a be_errno_t is returned.
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
{
	zfs_handle_t	*zhp = NULL;
	char		grub_file[MAXPATHLEN];
	FILE		*menu_fp;
	char		line[BUFSIZ];
	char		*pool_mntpnt = NULL;
	char		*ptmp_mntpnt = NULL;
	char		*orig_mntpnt = NULL;
	int		default_entry = 0, entries = 0;
	int		found_default = 0;
	int		ret = BE_SUCCESS;
	boolean_t	pool_mounted = B_FALSE;

	errno = 0;

	/*
	 * Check to see if this system supports grub
	 */
	if (!be_has_grub()) {
		be_print_err(gettext("be_default_grub_bootfs: operation "
		    "not supported on this architecture\n"));
		return (BE_ERR_NOTSUP);
	}

	*def_bootfs = NULL;

	/* Get handle to pool dataset */
	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
		be_print_err(gettext("be_default_grub_bootfs: "
		    "failed to open pool dataset for %s: %s"),
		    be_root_pool, libzfs_error_description(g_zfs));
		return (zfs_err_to_be_err(g_zfs));
	}

	/*
	 * Check to see if the pool's dataset is mounted. If it isn't we'll
	 * attempt to mount it.
	 */
	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
	    &pool_mounted)) != BE_SUCCESS) {
		be_print_err(gettext("be_default_grub_bootfs: pool dataset "
		    "(%s) could not be mounted\n"), be_root_pool);
		ZFS_CLOSE(zhp);
		return (ret);
	}

	/*
	 * Get the mountpoint for the root pool dataset.
	 */
	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
		be_print_err(gettext("be_default_grub_bootfs: failed "
		    "to get mount point for the root pool. Can't set "
		    "the default BE in the grub menu.\n"));
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	(void) snprintf(grub_file, MAXPATHLEN, "%s%s",
	    pool_mntpnt, BE_GRUB_MENU);

	if ((ret = be_open_menu((char *)be_root_pool, pool_mntpnt, grub_file,
	    &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
		goto cleanup;
	} else if (menu_fp == NULL) {
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	free(pool_mntpnt);
	pool_mntpnt = NULL;

	while (fgets(line, BUFSIZ, menu_fp)) {
		char *tok = strtok(line, BE_WHITE_SPACE);

		if (tok != NULL && tok[0] != '#') {
			if (!found_default) {
				if (strcmp(tok, "default") == 0) {
					tok = strtok(NULL, BE_WHITE_SPACE);
					if (tok != NULL) {
						default_entry = atoi(tok);
						rewind(menu_fp);
						found_default = 1;
					}
				}
				continue;
			}
			if (strcmp(tok, "title") == 0) {
				entries++;
			} else if (default_entry == entries - 1) {
				if (strcmp(tok, "bootfs") == 0) {
					tok = strtok(NULL, BE_WHITE_SPACE);
					(void) fclose(menu_fp);

					if (tok == NULL) {
						ret = BE_SUCCESS;
						goto cleanup;
					}

					if ((*def_bootfs = strdup(tok)) !=
					    NULL) {
						ret = BE_SUCCESS;
						goto cleanup;
					}
					be_print_err(gettext(
					    "be_default_grub_bootfs: "
					    "memory allocation failed\n"));
					ret = BE_ERR_NOMEM;
					goto cleanup;
				}
			} else if (default_entry < entries - 1) {
				/*
				 * no bootfs entry for the default entry.
				 */
				break;
			}
		}
	}
	(void) fclose(menu_fp);

cleanup:
	if (pool_mounted) {
		int err = BE_SUCCESS;
		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
		if (ret == BE_SUCCESS)
			ret = err;
		free(orig_mntpnt);
		free(ptmp_mntpnt);
	}
	ZFS_CLOSE(zhp);
	return (ret);
}

/*
 * Function:	be_change_grub_default
 * Description:	This function takes two parameters. These are the name of
 *		the BE we want to have as the default booted in the grub
 *		menu and the root pool where the path to the grub menu exists.
 *		The code takes this and finds the BE's entry in the grub menu
 *		and changes the default entry to point to that entry in the
 *		list.
 * Parameters:
 *		be_name - This is the name of the BE wanted as the default
 *			for the next boot.
 *		be_root_pool - This is the name of the root pool where the
 *			grub menu can be found.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_change_grub_default(char *be_name, char *be_root_pool)
{
	zfs_handle_t	*zhp = NULL;
	char	grub_file[MAXPATHLEN];
	char	*temp_grub;
	char	*pool_mntpnt = NULL;
	char	*ptmp_mntpnt = NULL;
	char	*orig_mntpnt = NULL;
	char	line[BUFSIZ];
	char	temp_line[BUFSIZ];
	char	be_root_ds[MAXPATHLEN];
	FILE	*grub_fp = NULL;
	FILE	*temp_fp = NULL;
	struct stat	sb;
	int	temp_grub_len = 0;
	int	fd, entries = 0;
	int	err = 0;
	int	ret = BE_SUCCESS;
	boolean_t	found_default = B_FALSE;
	boolean_t	pool_mounted = B_FALSE;

	errno = 0;

	/*
	 * Check to see if this system supports grub
	 */
	if (!be_has_grub()) {
		be_print_err(gettext("be_change_grub_default: operation "
		    "not supported on this architecture\n"));
		return (BE_ERR_NOTSUP);
	}

	/* Generate string for BE's root dataset */
	be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));

	/* Get handle to pool dataset */
	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
		be_print_err(gettext("be_change_grub_default: "
		    "failed to open pool dataset for %s: %s"),
		    be_root_pool, libzfs_error_description(g_zfs));
		return (zfs_err_to_be_err(g_zfs));
	}

	/*
	 * Check to see if the pool's dataset is mounted. If it isn't we'll
	 * attempt to mount it.
	 */
	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
	    &pool_mounted)) != BE_SUCCESS) {
		be_print_err(gettext("be_change_grub_default: pool dataset "
		    "(%s) could not be mounted\n"), be_root_pool);
		ZFS_CLOSE(zhp);
		return (ret);
	}

	/*
	 * Get the mountpoint for the root pool dataset.
	 */
	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
		be_print_err(gettext("be_change_grub_default: pool "
		    "dataset (%s) is not mounted. Can't set "
		    "the default BE in the grub menu.\n"), be_root_pool);
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	(void) snprintf(grub_file, MAXPATHLEN, "%s%s",
	    pool_mntpnt, BE_GRUB_MENU);

	if ((ret = be_open_menu(be_root_pool, pool_mntpnt, grub_file,
	    &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
		goto cleanup;
	} else if (grub_fp == NULL) {
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	free(pool_mntpnt);
	pool_mntpnt = NULL;

	/* Grab the stats of the original menu file */
	if (stat(grub_file, &sb) != 0) {
		err = errno;
		be_print_err(gettext("be_change_grub_default: "
		    "failed to stat file %s: %s\n"), grub_file, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	/* Create a tmp file for the modified menu.lst */
	temp_grub_len = strlen(grub_file) + 7;
	if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
		be_print_err(gettext("be_change_grub_default: "
		    "malloc failed\n"));
		ret = BE_ERR_NOMEM;
		goto cleanup;
	}
	(void) memset(temp_grub, 0, temp_grub_len);
	(void) strlcpy(temp_grub, grub_file, temp_grub_len);
	(void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
	if ((fd = mkstemp(temp_grub)) == -1) {
		err = errno;
		be_print_err(gettext("be_change_grub_default: "
		    "mkstemp failed: %s\n"), strerror(err));
		ret = errno_to_be_err(err);
		free(temp_grub);
		temp_grub = NULL;
		goto cleanup;
	}
	if ((temp_fp = fdopen(fd, "w")) == NULL) {
		err = errno;
		be_print_err(gettext("be_change_grub_default: "
		    "failed to open %s file: %s\n"),
		    temp_grub, strerror(err));
		(void) close(fd);
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	while (fgets(line, BUFSIZ, grub_fp)) {
		char *tok = strtok(line, BE_WHITE_SPACE);

		if (tok == NULL || tok[0] == '#') {
			continue;
		} else if (strcmp(tok, "title") == 0) {
			entries++;
			continue;
		} else if (strcmp(tok, "bootfs") == 0) {
			char *bootfs = strtok(NULL, BE_WHITE_SPACE);
			if (bootfs == NULL)
				continue;

			if (strcmp(bootfs, be_root_ds) == 0) {
				found_default = B_TRUE;
				break;
			}
		}
	}

	if (!found_default) {
		be_print_err(gettext("be_change_grub_default: failed "
		    "to find entry for %s in the grub menu\n"),
		    be_name);
		ret = BE_ERR_BE_NOENT;
		goto cleanup;
	}

	rewind(grub_fp);

	while (fgets(line, BUFSIZ, grub_fp)) {
		char *tok = NULL;

		(void) strncpy(temp_line, line, BUFSIZ);

		if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
		    strcmp(tok, "default") == 0) {
			(void) snprintf(temp_line, BUFSIZ, "default %d\n",
			    entries - 1 >= 0 ? entries - 1 : 0);
			(void) fputs(temp_line, temp_fp);
		} else {
			(void) fputs(line, temp_fp);
		}
	}

	(void) fclose(grub_fp);
	grub_fp = NULL;
	(void) fclose(temp_fp);
	temp_fp = NULL;

	if (rename(temp_grub, grub_file) != 0) {
		err = errno;
		be_print_err(gettext("be_change_grub_default: "
		    "failed to rename file %s to %s: %s\n"),
		    temp_grub, grub_file, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	free(temp_grub);
	temp_grub = NULL;

	/* Set the perms and ownership of the updated file */
	if (chmod(grub_file, sb.st_mode) != 0) {
		err = errno;
		be_print_err(gettext("be_change_grub_default: "
		    "failed to chmod %s: %s\n"), grub_file, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
		err = errno;
		be_print_err(gettext("be_change_grub_default: "
		    "failed to chown %s: %s\n"), grub_file, strerror(err));
		ret = errno_to_be_err(err);
	}

cleanup:
	if (pool_mounted) {
		int err = BE_SUCCESS;
		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
		if (ret == BE_SUCCESS)
			ret = err;
		free(orig_mntpnt);
		free(ptmp_mntpnt);
	}
	ZFS_CLOSE(zhp);
	if (grub_fp != NULL)
		(void) fclose(grub_fp);
	if (temp_fp != NULL)
		(void) fclose(temp_fp);
	if (temp_grub != NULL) {
		(void) unlink(temp_grub);
		free(temp_grub);
	}

	return (ret);
}

/*
 * Function:	be_update_menu
 * Description:	This function is used by be_rename to change the BE name in
 *		an existing entry in the grub menu to the new name of the BE.
 * Parameters:
 *		be_orig_name - the original name of the BE
 *		be_new_name - the new name the BE is being renameed to.
 *		be_root_pool - The pool which contains the grub menu
 *		boot_pool - the pool where the BE is, if different than
 *			the pool containing the boot menu.  If this is
 *			NULL it will be set to be_root_pool.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
    char *boot_pool)
{
	zfs_handle_t *zhp = NULL;
	char menu_file[MAXPATHLEN];
	char be_root_ds[MAXPATHLEN];
	char be_new_root_ds[MAXPATHLEN];
	char line[BUFSIZ];
	char *pool_mntpnt = NULL;
	char *ptmp_mntpnt = NULL;
	char *orig_mntpnt = NULL;
	char *temp_menu = NULL;
	FILE *menu_fp = NULL;
	FILE *new_fp = NULL;
	struct stat sb;
	int temp_menu_len = 0;
	int tmp_fd;
	int ret = BE_SUCCESS;
	int err = 0;
	boolean_t pool_mounted = B_FALSE;

	errno = 0;

	if (boot_pool == NULL)
		boot_pool = be_root_pool;

	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
		be_print_err(gettext("be_update_menu: failed to open "
		    "pool dataset for %s: %s\n"), be_root_pool,
		    libzfs_error_description(g_zfs));
		return (zfs_err_to_be_err(g_zfs));
	}

	/*
	 * Check to see if the pool's dataset is mounted. If it isn't we'll
	 * attempt to mount it.
	 */
	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
	    &pool_mounted)) != BE_SUCCESS) {
		be_print_err(gettext("be_update_menu: pool dataset "
		    "(%s) could not be mounted\n"), be_root_pool);
		ZFS_CLOSE(zhp);
		return (ret);
	}

	/*
	 * Get the mountpoint for the root pool dataset.
	 */
	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
		be_print_err(gettext("be_update_menu: failed "
		    "to get mount point for the root pool. Can't set "
		    "the default BE in the grub menu.\n"));
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	/*
	 * Check to see if this system supports grub
	 */
	if (be_has_grub()) {
		(void) snprintf(menu_file, sizeof (menu_file),
		    "%s%s", pool_mntpnt, BE_GRUB_MENU);
	} else {
		(void) snprintf(menu_file, sizeof (menu_file),
		    "%s%s", pool_mntpnt, BE_SPARC_MENU);
	}

	be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
	    sizeof (be_root_ds));
	be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
	    sizeof (be_new_root_ds));

	if ((ret = be_open_menu(be_root_pool, pool_mntpnt, menu_file,
	    &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
		goto cleanup;
	} else if (menu_fp == NULL) {
		ret = BE_ERR_NO_MENU;
		goto cleanup;
	}

	free(pool_mntpnt);
	pool_mntpnt = NULL;

	/* Grab the stat of the original menu file */
	if (stat(menu_file, &sb) != 0) {
		err = errno;
		be_print_err(gettext("be_update_menu: "
		    "failed to stat file %s: %s\n"), menu_file, strerror(err));
		(void) fclose(menu_fp);
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	/* Create tmp file for modified menu.lst */
	temp_menu_len = strlen(menu_file) + 7;
	if ((temp_menu = (char *)malloc(temp_menu_len))
	    == NULL) {
		be_print_err(gettext("be_update_menu: "
		    "malloc failed\n"));
		(void) fclose(menu_fp);
		ret = BE_ERR_NOMEM;
		goto cleanup;
	}
	(void) memset(temp_menu, 0, temp_menu_len);
	(void) strlcpy(temp_menu, menu_file, temp_menu_len);
	(void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
	if ((tmp_fd = mkstemp(temp_menu)) == -1) {
		err = errno;
		be_print_err(gettext("be_update_menu: "
		    "mkstemp failed: %s\n"), strerror(err));
		(void) fclose(menu_fp);
		free(temp_menu);
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
		err = errno;
		be_print_err(gettext("be_update_menu: "
		    "fdopen failed: %s\n"), strerror(err));
		(void) close(tmp_fd);
		(void) fclose(menu_fp);
		free(temp_menu);
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	while (fgets(line, BUFSIZ, menu_fp)) {
		char tline[BUFSIZ];
		char new_line[BUFSIZ];
		char *c = NULL;

		(void) strlcpy(tline, line, sizeof (tline));

		/* Tokenize line */
		c = strtok(tline, BE_WHITE_SPACE);

		if (c == NULL) {
			/* Found empty line, write it out. */
			(void) fputs(line, new_fp);
		} else if (c[0] == '#') {
			/* Found a comment line, write it out. */
			(void) fputs(line, new_fp);
		} else if (strcmp(c, "title") == 0) {
			char *name = NULL;
			char *desc = NULL;

			/*
			 * Found a 'title' line, parse out BE name or
			 * the description.
			 */
			name = strtok(NULL, BE_WHITE_SPACE);

			if (name == NULL) {
				/*
				 * Nothing after 'title', just push
				 * this line through
				 */
				(void) fputs(line, new_fp);
			} else {
				/*
				 * Grab the remainder of the title which
				 * could be a multi worded description
				 */
				desc = strtok(NULL, "\n");

				if (strcmp(name, be_orig_name) == 0) {
					/*
					 * The first token of the title is
					 * the old BE name, replace it with
					 * the new one, and write it out
					 * along with the remainder of
					 * description if there is one.
					 */
					if (desc) {
						(void) snprintf(new_line,
						    sizeof (new_line),
						    "title %s %s\n",
						    be_new_name, desc);
					} else {
						(void) snprintf(new_line,
						    sizeof (new_line),
						    "title %s\n", be_new_name);
					}

					(void) fputs(new_line, new_fp);
				} else {
					(void) fputs(line, new_fp);
				}
			}
		} else if (strcmp(c, "bootfs") == 0) {
			/*
			 * Found a 'bootfs' line, parse out the BE root
			 * dataset value.
			 */
			char *root_ds = strtok(NULL, BE_WHITE_SPACE);

			if (root_ds == NULL) {
				/*
				 * Nothing after 'bootfs', just push
				 * this line through
				 */
				(void) fputs(line, new_fp);
			} else {
				/*
				 * If this bootfs is the one we're renaming,
				 * write out the new root dataset value
				 */
				if (strcmp(root_ds, be_root_ds) == 0) {
					(void) snprintf(new_line,
					    sizeof (new_line), "bootfs %s\n",
					    be_new_root_ds);

					(void) fputs(new_line, new_fp);
				} else {
					(void) fputs(line, new_fp);
				}
			}
		} else {
			/*
			 * Found some other line we don't care
			 * about, write it out.
			 */
			(void) fputs(line, new_fp);
		}
	}

	(void) fclose(menu_fp);
	(void) fclose(new_fp);
	(void) close(tmp_fd);

	if (rename(temp_menu, menu_file) != 0) {
		err = errno;
		be_print_err(gettext("be_update_menu: "
		    "failed to rename file %s to %s: %s\n"),
		    temp_menu, menu_file, strerror(err));
		ret = errno_to_be_err(err);
	}
	free(temp_menu);

	/* Set the perms and ownership of the updated file */
	if (chmod(menu_file, sb.st_mode) != 0) {
		err = errno;
		be_print_err(gettext("be_update_menu: "
		    "failed to chmod %s: %s\n"), menu_file, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
		err = errno;
		be_print_err(gettext("be_update_menu: "
		    "failed to chown %s: %s\n"), menu_file, strerror(err));
		ret = errno_to_be_err(err);
	}

cleanup:
	if (pool_mounted) {
		int err = BE_SUCCESS;
		err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
		if (ret == BE_SUCCESS)
			ret = err;
		free(orig_mntpnt);
		free(ptmp_mntpnt);
	}
	ZFS_CLOSE(zhp);
	return (ret);
}

/*
 * Function:	be_has_menu_entry
 * Description:	Checks to see if the BEs root dataset has an entry in the grub
 *		menu.
 * Parameters:
 *		be_dataset - The root dataset of the BE
 *		be_root_pool - The pool which contains the boot menu
 *		entry - A pointer the the entry number of the BE if found.
 * Returns:
 *		B_TRUE - Success
 *		B_FALSE - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
boolean_t
be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
{
	zfs_handle_t *zhp = NULL;
	char		menu_file[MAXPATHLEN];
	FILE		*menu_fp;
	char		line[BUFSIZ];
	char		*last;
	char		*rpool_mntpnt = NULL;
	char		*ptmp_mntpnt = NULL;
	char		*orig_mntpnt = NULL;
	int		ent_num = 0;
	boolean_t	ret = 0;
	boolean_t	pool_mounted = B_FALSE;


	/*
	 * Check to see if this system supports grub
	 */
	if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
		be_print_err(gettext("be_has_menu_entry: failed to open "
		    "pool dataset for %s: %s\n"), be_root_pool,
		    libzfs_error_description(g_zfs));
		return (B_FALSE);
	}

	/*
	 * Check to see if the pool's dataset is mounted. If it isn't we'll
	 * attempt to mount it.
	 */
	if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
	    &pool_mounted) != 0) {
		be_print_err(gettext("be_has_menu_entry: pool dataset "
		    "(%s) could not be mounted\n"), be_root_pool);
		ZFS_CLOSE(zhp);
		return (B_FALSE);
	}

	/*
	 * Get the mountpoint for the root pool dataset.
	 */
	if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
		be_print_err(gettext("be_has_menu_entry: pool "
		    "dataset (%s) is not mounted. Can't set "
		    "the default BE in the grub menu.\n"), be_root_pool);
		ret = B_FALSE;
		goto cleanup;
	}

	if (be_has_grub()) {
		(void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
		    rpool_mntpnt, BE_GRUB_MENU);
	} else {
		(void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
		    rpool_mntpnt, BE_SPARC_MENU);
	}

	if (be_open_menu(be_root_pool, rpool_mntpnt, menu_file, &menu_fp, "r",
	    B_FALSE) != 0) {
		ret = B_FALSE;
		goto cleanup;
	} else if (menu_fp == NULL) {
		ret = B_FALSE;
		goto cleanup;
	}

	free(rpool_mntpnt);
	rpool_mntpnt = NULL;

	while (fgets(line, BUFSIZ, menu_fp)) {
		char *tok = strtok_r(line, BE_WHITE_SPACE, &last);

		if (tok != NULL && tok[0] != '#') {
			if (strcmp(tok, "bootfs") == 0) {
				tok = strtok_r(last, BE_WHITE_SPACE, &last);
				if (tok != NULL && strcmp(tok,
				    be_dataset) == 0) {
					(void) fclose(menu_fp);
					/*
					 * The entry number needs to be
					 * decremented here because the title
					 * will always be the first line for
					 * an entry. Because of this we'll
					 * always be off by one entry when we
					 * check for bootfs.
					 */
					*entry = ent_num - 1;
					ret = B_TRUE;
					goto cleanup;
				}
			} else if (strcmp(tok, "title") == 0)
				ent_num++;
		}
	}

cleanup:
	if (pool_mounted) {
		(void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
		free(orig_mntpnt);
		free(ptmp_mntpnt);
	}
	ZFS_CLOSE(zhp);
	(void) fclose(menu_fp);
	return (ret);
}

/*
 * Function:	be_update_vfstab
 * Description:	This function digs into a BE's vfstab and updates all
 *		entries with file systems listed in be_fs_list_data_t.
 *		The entry's root container dataset and be_name will be
 *		updated with the parameters passed in.
 * Parameters:
 *		be_name - name of BE to update
 *		old_rc_loc - dataset under which the root container dataset
 *			of the old BE resides in.
 *		new_rc_loc - dataset under which the root container dataset
 *			of the new BE resides in.
 *		fld - be_fs_list_data_t pointer providing the list of
 *			file systems to look for in vfstab.
 *		mountpoint - directory of where BE is currently mounted.
 *			If NULL, then BE is not currently mounted.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
    be_fs_list_data_t *fld, char *mountpoint)
{
	char		*tmp_mountpoint = NULL;
	char		alt_vfstab[MAXPATHLEN];
	int		ret = BE_SUCCESS, err = BE_SUCCESS;

	if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
		return (BE_SUCCESS);

	/* If BE not already mounted, mount the BE */
	if (mountpoint == NULL) {
		if ((ret = _be_mount(be_name, &tmp_mountpoint,
		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
			be_print_err(gettext("be_update_vfstab: "
			    "failed to mount BE (%s)\n"), be_name);
			return (ret);
		}
	} else {
		tmp_mountpoint = mountpoint;
	}

	/* Get string for vfstab in the mounted BE. */
	(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
	    tmp_mountpoint);

	/* Update the vfstab */
	ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
	    fld);

	/* Unmount BE if we mounted it */
	if (mountpoint == NULL) {
		if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
			/* Remove temporary mountpoint */
			(void) rmdir(tmp_mountpoint);
		} else {
			be_print_err(gettext("be_update_vfstab: "
			    "failed to unmount BE %s mounted at %s\n"),
			    be_name, tmp_mountpoint);
			if (ret == BE_SUCCESS)
				ret = err;
		}

		free(tmp_mountpoint);
	}

	return (ret);
}

/*
 * Function:	be_update_zone_vfstab
 * Description:	This function digs into a zone BE's vfstab and updates all
 *		entries with file systems listed in be_fs_list_data_t.
 *		The entry's root container dataset and be_name will be
 *		updated with the parameters passed in.
 * Parameters:
 *		zhp - zfs_handle_t pointer to zone root dataset.
 *		be_name - name of zone BE to update
 *		old_rc_loc - dataset under which the root container dataset
 *			of the old zone BE resides in.
 *		new_rc_loc - dataset under which the root container dataset
 *			of the new zone BE resides in.
 *		fld - be_fs_list_data_t pointer providing the list of
 *			file systems to look for in vfstab.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
    char *new_rc_loc, be_fs_list_data_t *fld)
{
	be_mount_data_t		md = { 0 };
	be_unmount_data_t	ud = { 0 };
	char			alt_vfstab[MAXPATHLEN];
	boolean_t		mounted_here = B_FALSE;
	int			ret = BE_SUCCESS;

	/*
	 * If zone root not already mounted, mount it at a
	 * temporary location.
	 */
	if (!zfs_is_mounted(zhp, &md.altroot)) {
		/* Generate temporary mountpoint to mount zone root */
		if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
			be_print_err(gettext("be_update_zone_vfstab: "
			    "failed to make temporary mountpoint to "
			    "mount zone root\n"));
			return (ret);
		}

		if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
			be_print_err(gettext("be_update_zone_vfstab: "
			    "failed to mount zone root %s\n"),
			    zfs_get_name(zhp));
			free(md.altroot);
			return (BE_ERR_MOUNT_ZONEROOT);
		}
		mounted_here = B_TRUE;
	}

	/* Get string from vfstab in the mounted zone BE */
	(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
	    md.altroot);

	/* Update the vfstab */
	ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
	    fld);

	/* Unmount zone root if we mounted it */
	if (mounted_here) {
		ud.force = B_TRUE;

		if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
			/* Remove the temporary mountpoint */
			(void) rmdir(md.altroot);
		} else {
			be_print_err(gettext("be_update_zone_vfstab: "
			    "failed to unmount zone root %s from %s\n"),
			    zfs_get_name(zhp), md.altroot);
			if (ret == 0)
				ret = BE_ERR_UMOUNT_ZONEROOT;
		}
	}

	free(md.altroot);
	return (ret);
}

/*
 * Function:	be_auto_snap_name
 * Description:	Generate an auto snapshot name constructed based on the
 *		current date and time.  The auto snapshot name is of the form:
 *
 *			<date>-<time>
 *
 *		where <date> is in ISO standard format, so the resultant name
 *		is of the form:
 *
 *			%Y-%m-%d-%H:%M:%S
 *
 * Parameters:
 *		None
 * Returns:
 *		Success - pointer to auto generated snapshot name.  The name
 *			is allocated in heap storage so the caller is
 *			responsible for free'ing the name.
 *		Failure - NULL
 * Scope:
 *		Semi-private (library wide use only)
 */
char *
be_auto_snap_name(void)
{
	time_t		utc_tm = NULL;
	struct tm	*gmt_tm = NULL;
	char		gmt_time_str[64];
	char		*auto_snap_name = NULL;

	if (time(&utc_tm) == -1) {
		be_print_err(gettext("be_auto_snap_name: time() failed\n"));
		return (NULL);
	}

	if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
		be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
		return (NULL);
	}

	(void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);

	if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
		be_print_err(gettext("be_auto_snap_name: "
		    "memory allocation failed\n"));
		return (NULL);
	}

	return (auto_snap_name);
}

/*
 * Function:	be_auto_be_name
 * Description:	Generate an auto BE name constructed based on the BE name
 *		of the original BE being cloned.
 * Parameters:
 *		obe_name - name of the original BE being cloned.
 * Returns:
 *		Success - pointer to auto generated BE name.  The name
 *			is allocated in heap storage so the caller is
 *			responsible for free'ing the name.
 *		Failure - NULL
 * Scope:
 *		Semi-private (library wide use only)
 */
char *
be_auto_be_name(char *obe_name)
{
	return (be_get_auto_name(obe_name, NULL, B_FALSE));
}

/*
 * Function:	be_auto_zone_be_name
 * Description:	Generate an auto BE name for a zone constructed based on
 *              the BE name of the original zone BE being cloned.
 * Parameters:
 *              container_ds - container dataset for the zone.
 *		zbe_name - name of the original zone BE being cloned.
 * Returns:
 *		Success - pointer to auto generated BE name.  The name
 *			is allocated in heap storage so the caller is
 *			responsible for free'ing the name.
 *		Failure - NULL
 * Scope:
 *		Semi-private (library wide use only)
 */
char *
be_auto_zone_be_name(char *container_ds, char *zbe_name)
{
	return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
}

/*
 * Function:	be_valid_be_name
 * Description:	Validates a BE name.
 * Parameters:
 *		be_name - name of BE to validate
 * Returns:
 *		B_TRUE - be_name is valid
 *		B_FALSE - be_name is invalid
 * Scope:
 *		Semi-private (library wide use only)
 */

boolean_t
be_valid_be_name(const char *be_name)
{
	const char	*c = NULL;

	if (be_name == NULL)
		return (B_FALSE);

	/*
	 * A BE name must not be a multi-level dataset name.  We also check
	 * that it does not contain the ' ' and '%' characters.  The ' ' is
	 * a valid character for datasets, however we don't allow that in a
	 * BE name.  The '%' is invalid, but zfs_name_valid() allows it for
	 * internal reasons, so we explicitly check for it here.
	 */
	c = be_name;
	while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
		c++;

	if (*c != '\0')
		return (B_FALSE);

	/*
	 * The BE name must comply with a zfs dataset filesystem. We also
	 * verify its length to be < BE_NAME_MAX_LEN.
	 */
	if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
	    strlen(be_name) > BE_NAME_MAX_LEN)
		return (B_FALSE);

	return (B_TRUE);
}

/*
 * Function:	be_valid_auto_snap_name
 * Description:	This function checks that a snapshot name is a valid auto
 *		generated snapshot name.  A valid auto generated snapshot
 *		name is of the form:
 *
 *			%Y-%m-%d-%H:%M:%S
 *
 *		An older form of the auto generated snapshot name also
 *		included the snapshot's BE cleanup policy and a reserved
 *		field.  Those names will also be verified by this function.
 *
 *		Examples of valid auto snapshot names are:
 *
 *			2008-03-31-18:41:30
 *			2008-03-31-22:17:24
 *			<policy>:-:2008:04-05-09:12:55
 *			<policy>:-:2008:04-06-15:34:12
 *
 * Parameters:
 *		name - name of the snapshot to be validated.
 * Returns:
 *		B_TRUE - the name is a valid auto snapshot name.
 *		B_FALSE - the name is not a valid auto snapshot name.
 * Scope:
 *		Semi-private (library wide use only)
 */
boolean_t
be_valid_auto_snap_name(char *name)
{
	struct tm gmt_tm;

	char *policy = NULL;
	char *reserved = NULL;
	char *date = NULL;
	char *c = NULL;

	/* Validate the snapshot name by converting it into utc time */
	if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
	    (mktime(&gmt_tm) != -1)) {
		return (B_TRUE);
	}

	/*
	 * Validate the snapshot name against the older form of an
	 * auto generated snapshot name.
	 */
	policy = strdup(name);

	/*
	 * Get the first field from the snapshot name,
	 * which is the BE policy
	 */
	c = strchr(policy, ':');
	if (c == NULL) {
		free(policy);
		return (B_FALSE);
	}
	c[0] = '\0';

	/* Validate the policy name */
	if (!valid_be_policy(policy)) {
		free(policy);
		return (B_FALSE);
	}

	/* Get the next field, which is the reserved field. */
	if (c[1] == NULL || c[1] == '\0') {
		free(policy);
		return (B_FALSE);
	}
	reserved = c+1;
	c = strchr(reserved, ':');
	if (c == NULL) {
		free(policy);
		return (B_FALSE);
	}
	c[0] = '\0';

	/* Validate the reserved field */
	if (strcmp(reserved, "-") != 0) {
		free(policy);
		return (B_FALSE);
	}

	/* The remaining string should be the date field */
	if (c[1] == NULL || c[1] == '\0') {
		free(policy);
		return (B_FALSE);
	}
	date = c+1;

	/* Validate the date string by converting it into utc time */
	if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
	    (mktime(&gmt_tm) == -1)) {
		be_print_err(gettext("be_valid_auto_snap_name: "
		    "invalid auto snapshot name\n"));
		free(policy);
		return (B_FALSE);
	}

	free(policy);
	return (B_TRUE);
}

/*
 * Function:	be_default_policy
 * Description:	Temporary hardcoded policy support.  This function returns
 *		the default policy type to be used to create a BE or a BE
 *		snapshot.
 * Parameters:
 *		None
 * Returns:
 *		Name of default BE policy.
 * Scope:
 *		Semi-private (library wide use only)
 */
char *
be_default_policy(void)
{
	return (BE_PLCY_STATIC);
}

/*
 * Function:	valid_be_policy
 * Description:	Temporary hardcoded policy support.  This function valids
 *		whether a policy is a valid known policy or not.
 * Paramters:
 *		policy - name of policy to validate.
 * Returns:
 *		B_TRUE - policy is a valid.
 *		B_FALSE - policy is invalid.
 * Scope:
 *		Semi-private (library wide use only)
 */
boolean_t
valid_be_policy(char *policy)
{
	if (policy == NULL)
		return (B_FALSE);

	if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
	    strcmp(policy, BE_PLCY_VOLATILE) == 0) {
		return (B_TRUE);
	}

	return (B_FALSE);
}

/*
 * Function:	be_print_err
 * Description:	This function prints out error messages if do_print is
 *		set to B_TRUE or if the BE_PRINT_ERR environment variable
 *		is set to true.
 * Paramters:
 *		prnt_str - the string we wish to print and any arguments
 *		for the format of that string.
 * Returns:
 *		void
 * Scope:
 *		Semi-private (library wide use only)
 */
void
be_print_err(char *prnt_str, ...)
{
	va_list ap;
	char buf[BUFSIZ];
	char *env_buf;
	static boolean_t env_checked = B_FALSE;

	if (!env_checked) {
		if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
			if (strcasecmp(env_buf, "true") == 0) {
				do_print = B_TRUE;
			}
		}
		env_checked = B_TRUE;
	}

	if (do_print) {
		va_start(ap, prnt_str);
		/* LINTED variable format specifier */
		(void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
		(void) fputs(buf, stderr);
		va_end(ap);
	}
}

/*
 * Function:	be_find_current_be
 * Description:	Find the currently "active" BE. Fill in the
 * 		passed in be_transaction_data_t reference with the
 *		active BE's data.
 * Paramters:
 *		none
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errnot_t - Failure
 * Scope:
 *		Semi-private (library wide use only)
 * Notes:
 *		The caller is responsible for initializing the libzfs handle
 *		and freeing the memory used by the active be_name.
 */
int
be_find_current_be(be_transaction_data_t *bt)
{
	int	zret;

	if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
	    bt)) == 0) {
		be_print_err(gettext("be_find_current_be: failed to "
		    "find current BE name\n"));
		return (BE_ERR_BE_NOENT);
	} else if (zret < 0) {
		be_print_err(gettext("be_find_current_be: "
		    "zpool_iter failed: %s\n"),
		    libzfs_error_description(g_zfs));
		return (zfs_err_to_be_err(g_zfs));
	}

	return (BE_SUCCESS);
}

/*
 * Function:	be_zpool_find_current_be_callback
 * Description: Callback function used to iterate through all existing pools
 *		to find the BE that is the currently booted BE.
 * Parameters:
 *		zlp - zpool_handle_t pointer to the current pool being
 *			looked at.
 *		data - be_transaction_data_t pointer.
 *			Upon successfully finding the current BE, the
 *			obe_zpool member of this parameter is set to the
 *			pool it is found in.
 * Return:
 *		1 - Found current BE in this pool.
 *		0 - Did not find current BE in this pool.
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
{
	be_transaction_data_t	*bt = data;
	zfs_handle_t		*zhp = NULL;
	const char		*zpool =  zpool_get_name(zlp);
	char			be_container_ds[MAXPATHLEN];

	/*
	 * Generate string for BE container dataset
	 */
	be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));

	/*
	 * Check if a BE container dataset exists in this pool.
	 */
	if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
		zpool_close(zlp);
		return (0);
	}

	/*
	 * Get handle to this zpool's BE container dataset.
	 */
	if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
	    NULL) {
		be_print_err(gettext("be_zpool_find_current_be_callback: "
		    "failed to open BE container dataset (%s)\n"),
		    be_container_ds);
		zpool_close(zlp);
		return (0);
	}

	/*
	 * Iterate through all potential BEs in this zpool
	 */
	if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
		/*
		 * Found current BE dataset; set obe_zpool
		 */
		if ((bt->obe_zpool = strdup(zpool)) == NULL) {
			be_print_err(gettext(
			    "be_zpool_find_current_be_callback: "
			    "memory allocation failed\n"));
			ZFS_CLOSE(zhp);
			zpool_close(zlp);
			return (0);
		}

		ZFS_CLOSE(zhp);
		zpool_close(zlp);
		return (1);
	}

	ZFS_CLOSE(zhp);
	zpool_close(zlp);

	return (0);
}

/*
 * Function:	be_zfs_find_current_be_callback
 * Description:	Callback function used to iterate through all BEs in a
 *		pool to find the BE that is the currently booted BE.
 * Parameters:
 *		zhp - zfs_handle_t pointer to current filesystem being checked.
 *		data - be_transaction-data_t pointer
 *			Upon successfully finding the current BE, the
 *			obe_name and obe_root_ds members of this parameter
 *			are set to the BE name and BE's root dataset
 *			respectively.
 * Return:
 *		1 - Found current BE.
 *		0 - Did not find current BE.
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
{
	be_transaction_data_t	*bt = data;
	char			*mp = NULL;

	/*
	 * Check if dataset is mounted, and if so where.
	 */
	if (zfs_is_mounted(zhp, &mp)) {
		/*
		 * If mounted at root, set obe_root_ds and obe_name
		 */
		if (mp != NULL && strcmp(mp, "/") == 0) {
			free(mp);

			if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
			    == NULL) {
				be_print_err(gettext(
				    "be_zfs_find_current_be_callback: "
				    "memory allocation failed\n"));
				ZFS_CLOSE(zhp);
				return (0);
			}
			if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
			    == NULL) {
				be_print_err(gettext(
				    "be_zfs_find_current_be_callback: "
				    "memory allocation failed\n"));
				ZFS_CLOSE(zhp);
				return (0);
			}

			ZFS_CLOSE(zhp);
			return (1);
		}

		free(mp);
	}
	ZFS_CLOSE(zhp);

	return (0);
}

/*
 * Function:	be_check_be_roots_callback
 * Description:	This function checks whether or not the dataset name passed
 *		is hierachically located under the BE root container dataset
 *		for this pool.
 * Parameters:
 *		zlp - zpool_handle_t pointer to current pool being processed.
 *		data - name of dataset to check
 * Returns:
 *		0 - dataset is not in this pool's BE root container dataset
 *		1 - dataset is in this pool's BE root container dataset
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
{
	const char	*zpool = zpool_get_name(zlp);
	char		*ds = data;
	char		be_container_ds[MAXPATHLEN];

	/* Generate string for this pool's BE root container dataset */
	be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));

	/*
	 * If dataset lives under the BE root container dataset
	 * of this pool, return failure.
	 */
	if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
	    ds[strlen(be_container_ds)] == '/') {
		zpool_close(zlp);
		return (1);
	}

	zpool_close(zlp);
	return (0);
}

/*
 * Function:	zfs_err_to_be_err
 * Description:	This function takes the error stored in the libzfs handle
 *		and maps it to an be_errno_t. If there are no matching
 *		be_errno_t's then BE_ERR_ZFS is returned.
 * Paramters:
 *		zfsh - The libzfs handle containing the error we're looking up.
 * Returns:
 *		be_errno_t
 * Scope:
 *		Semi-private (library wide use only)
 */
int
zfs_err_to_be_err(libzfs_handle_t *zfsh)
{
	int err = libzfs_errno(zfsh);

	switch (err) {
	case 0:
		return (BE_SUCCESS);
	case EZFS_PERM:
		return (BE_ERR_PERM);
	case EZFS_INTR:
		return (BE_ERR_INTR);
	case EZFS_NOENT:
		return (BE_ERR_NOENT);
	case EZFS_NOSPC:
		return (BE_ERR_NOSPC);
	case EZFS_MOUNTFAILED:
		return (BE_ERR_MOUNT);
	case EZFS_UMOUNTFAILED:
		return (BE_ERR_UMOUNT);
	case EZFS_EXISTS:
		return (BE_ERR_BE_EXISTS);
	case EZFS_BUSY:
		return (BE_ERR_DEV_BUSY);
	case EZFS_POOLREADONLY:
		return (BE_ERR_ROFS);
	case EZFS_NAMETOOLONG:
		return (BE_ERR_NAMETOOLONG);
	case EZFS_NODEVICE:
		return (BE_ERR_NODEV);
	case EZFS_POOL_INVALARG:
		return (BE_ERR_INVAL);
	case EZFS_PROPTYPE:
		return (BE_ERR_INVALPROP);
	case EZFS_BADTYPE:
		return (BE_ERR_DSTYPE);
	case EZFS_PROPNONINHERIT:
		return (BE_ERR_NONINHERIT);
	case EZFS_PROPREADONLY:
		return (BE_ERR_READONLYPROP);
	case EZFS_RESILVERING:
	case EZFS_POOLUNAVAIL:
		return (BE_ERR_UNAVAIL);
	case EZFS_DSREADONLY:
		return (BE_ERR_READONLYDS);
	default:
		return (BE_ERR_ZFS);
	}
}

/*
 * Function:	errno_to_be_err
 * Description:	This function takes an errno and maps it to an be_errno_t.
 *		If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
 *		returned.
 * Paramters:
 *		err - The errno we're compairing against.
 * Returns:
 *		be_errno_t
 * Scope:
 *		Semi-private (library wide use only)
 */
int
errno_to_be_err(int err)
{
	switch (err) {
	case EPERM:
		return (BE_ERR_PERM);
	case EACCES:
		return (BE_ERR_ACCESS);
	case ECANCELED:
		return (BE_ERR_CANCELED);
	case EINTR:
		return (BE_ERR_INTR);
	case ENOENT:
		return (BE_ERR_NOENT);
	case ENOSPC:
	case EDQUOT:
		return (BE_ERR_NOSPC);
	case EEXIST:
		return (BE_ERR_BE_EXISTS);
	case EBUSY:
		return (BE_ERR_BUSY);
	case EROFS:
		return (BE_ERR_ROFS);
	case ENAMETOOLONG:
		return (BE_ERR_NAMETOOLONG);
	case ENXIO:
		return (BE_ERR_NXIO);
	case EINVAL:
		return (BE_ERR_INVAL);
	case EFAULT:
		return (BE_ERR_FAULT);
	default:
		return (BE_ERR_UNKNOWN);
	}
}

/*
 * Function:	be_err_to_str
 * Description:	This function takes a be_errno_t and maps it to a message.
 *		If there are no matching be_errno_t's then NULL is returned.
 * Paramters:
 *		be_errno_t - The be_errno_t we're mapping.
 * Returns:
 *		string or NULL if the error code is not known.
 * Scope:
 *		Semi-private (library wide use only)
 */
char *
be_err_to_str(int err)
{
	switch (err) {
	case BE_ERR_ACCESS:
		return (gettext("Permission denied."));
	case BE_ERR_ACTIVATE_CURR:
		return (gettext("Activation of current BE failed."));
	case BE_ERR_AUTONAME:
		return (gettext("Auto naming failed."));
	case BE_ERR_BE_NOENT:
		return (gettext("No such BE."));
	case BE_ERR_BUSY:
		return (gettext("Mount busy."));
	case BE_ERR_DEV_BUSY:
		return (gettext("Device busy."));
	case BE_ERR_CANCELED:
		return (gettext("Operation canceled."));
	case BE_ERR_CLONE:
		return (gettext("BE clone failed."));
	case BE_ERR_COPY:
		return (gettext("BE copy failed."));
	case BE_ERR_CREATDS:
		return (gettext("Dataset creation failed."));
	case BE_ERR_CURR_BE_NOT_FOUND:
		return (gettext("Can't find current BE."));
	case BE_ERR_DESTROY:
		return (gettext("Failed to destroy BE or snapshot."));
	case BE_ERR_DESTROY_CURR_BE:
		return (gettext("Cannot destroy current BE."));
	case BE_ERR_DEMOTE:
		return (gettext("BE demotion failed."));
	case BE_ERR_DSTYPE:
		return (gettext("Invalid dataset type."));
	case BE_ERR_BE_EXISTS:
		return (gettext("BE exists."));
	case BE_ERR_INIT:
		return (gettext("be_zfs_init failed."));
	case BE_ERR_INTR:
		return (gettext("Interupted system call."));
	case BE_ERR_INVAL:
		return (gettext("Invalid argument."));
	case BE_ERR_INVALPROP:
		return (gettext("Invalid property for dataset."));
	case BE_ERR_INVALMOUNTPOINT:
		return (gettext("Unexpected mountpoint."));
	case BE_ERR_MOUNT:
		return (gettext("Mount failed."));
	case BE_ERR_MOUNTED:
		return (gettext("Already mounted."));
	case BE_ERR_NAMETOOLONG:
		return (gettext("name > BUFSIZ."));
	case BE_ERR_NOENT:
		return (gettext("Doesn't exist."));
	case BE_ERR_POOL_NOENT:
		return (gettext("No such pool."));
	case BE_ERR_NODEV:
		return (gettext("No such device."));
	case BE_ERR_NOTMOUNTED:
		return (gettext("File system not mounted."));
	case BE_ERR_NOMEM:
		return (gettext("Not enough memory."));
	case BE_ERR_NONINHERIT:
		return (gettext(
		    "Property is not inheritable for the BE dataset."));
	case BE_ERR_NXIO:
		return (gettext("No such device or address."));
	case BE_ERR_NOSPC:
		return (gettext("No space on device."));
	case BE_ERR_NOTSUP:
		return (gettext("Operation not supported."));
	case BE_ERR_OPEN:
		return (gettext("Open failed."));
	case BE_ERR_PERM:
		return (gettext("Not owner."));
	case BE_ERR_UNAVAIL:
		return (gettext("The BE is currently unavailable."));
	case BE_ERR_PROMOTE:
		return (gettext("BE promotion failed."));
	case BE_ERR_ROFS:
		return (gettext("Read only file system."));
	case BE_ERR_READONLYDS:
		return (gettext("Read only dataset."));
	case BE_ERR_READONLYPROP:
		return (gettext("Read only property."));
	case BE_ERR_RENAME_ACTIVE:
		return (gettext("Renaming the active BE is not supported."));
	case BE_ERR_SS_EXISTS:
		return (gettext("Snapshot exists."));
	case BE_ERR_SS_NOENT:
		return (gettext("No such snapshot."));
	case BE_ERR_UMOUNT:
		return (gettext("Unmount failed."));
	case BE_ERR_UMOUNT_CURR_BE:
		return (gettext("Can't unmount the current BE."));
	case BE_ERR_UMOUNT_SHARED:
		return (gettext("Unmount of a shared File System failed."));
	case BE_ERR_FAULT:
		return (gettext("Bad address."));
	case BE_ERR_UNKNOWN:
		return (gettext("Unknown error."));
	case BE_ERR_ZFS:
		return (gettext("ZFS returned an error."));
	case BE_ERR_GEN_UUID:
		return (gettext("Failed to generate uuid."));
	case BE_ERR_PARSE_UUID:
		return (gettext("Failed to parse uuid."));
	case BE_ERR_NO_UUID:
		return (gettext("No uuid"));
	case BE_ERR_ZONE_NO_PARENTBE:
		return (gettext("No parent uuid"));
	case BE_ERR_ZONE_MULTIPLE_ACTIVE:
		return (gettext("Multiple active zone roots"));
	case BE_ERR_ZONE_NO_ACTIVE_ROOT:
		return (gettext("No active zone root"));
	case BE_ERR_ZONE_ROOT_NOT_LEGACY:
		return (gettext("Zone root not legacy"));
	case BE_ERR_MOUNT_ZONEROOT:
		return (gettext("Failed to mount a zone root."));
	case BE_ERR_UMOUNT_ZONEROOT:
		return (gettext("Failed to unmount a zone root."));
	case BE_ERR_NO_MOUNTED_ZONE:
		return (gettext("Zone is not mounted"));
	case BE_ERR_ZONES_UNMOUNT:
		return (gettext("Unable to unmount a zone BE."));
	case BE_ERR_NO_MENU:
		return (gettext("Missing boot menu file."));
	case BE_ERR_BAD_MENU_PATH:
		return (gettext("Invalid path for menu.lst file"));
	case BE_ERR_ZONE_SS_EXISTS:
		return (gettext("Zone snapshot exists."));
	case BE_ERR_BOOTFILE_INST:
		return (gettext("Error installing boot files."));
	case BE_ERR_EXTCMD:
		return (gettext("Error running an external command."));
	default:
		return (NULL);
	}
}

/*
 * Function:    be_has_grub
 * Description: Boolean function indicating whether the current system
 *		uses grub.
 * Return:      B_FALSE - the system does not have grub
 *              B_TRUE - the system does have grub.
 * Scope:
 *		Semi-private (library wide use only)
 */
boolean_t
be_has_grub(void)
{
	/*
	 * TODO: This will need to be expanded to check for the existence of
	 * grub if and when there is grub support for SPARC.
	 */
	return (be_is_isa("i386"));
}

/*
 * Function:    be_is_isa
 * Description: Boolean function indicating whether the instruction set
 *              architecture of the executing system matches the name provided.
 *              The string must match a system defined architecture (e.g.
 *              "i386", "sparc") and is case sensitive.
 * Parameters:  name - string representing the name of instruction set
 *			architecture being tested
 * Returns:     B_FALSE - the system instruction set architecture is different
 *			from the one specified
 *              B_TRUE - the system instruction set architecture is the same
 *			as the one specified
 * Scope:
 *		Semi-private (library wide use only)
 */
boolean_t
be_is_isa(char *name)
{
	return ((strcmp((char *)be_get_default_isa(), name) == 0));
}

/*
 * Function: be_get_default_isa
 * Description:
 *      Returns the default instruction set architecture of the
 *      machine it is executed on. (eg. sparc, i386, ...)
 *      NOTE:   SYS_INST environment variable may override default
 *              return value
 * Parameters:
 *		none
 * Returns:
 *		NULL - the architecture returned by sysinfo() was too
 *			long for local variables
 *		char * - pointer to a string containing the default
 *			implementation
 * Scope:
 *		Semi-private (library wide use only)
 */
char *
be_get_default_isa(void)
{
	int	i;
	char	*envp;
	static char	default_inst[ARCH_LENGTH] = "";

	if (default_inst[0] == '\0') {
		if ((envp = getenv("SYS_INST")) != NULL) {
			if ((int)strlen(envp) >= ARCH_LENGTH)
				return (NULL);
			else
				(void) strcpy(default_inst, envp);
		} else  {
			i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
			if (i < 0 || i > ARCH_LENGTH)
				return (NULL);
		}
	}
	return (default_inst);
}

/*
 * Function: be_run_cmd
 * Description:
 *	Runs a command in a separate subprocess.  Splits out stdout from stderr
 *	and sends each to its own buffer.  Buffers must be pre-allocated and
 *	passed in as arguments.  Buffer sizes are also passed in as arguments.
 *
 *	Notes / caveats:
 *	- Command being run is assumed to not have any stdout or stderr
 *		redirection.
 *	- Commands which emit total stderr output of greater than PIPE_BUF
 *		bytes can hang.  For such commands, a different implementation
 *		which uses poll(2) must be used.
 *	- stdout_buf can be NULL.  In this case, stdout_bufsize is ignored, and
 *		the stream which would have gone to it is sent to the bit
 *		bucket.
 *	- stderr_buf cannot be NULL.
 *	- Only subprocess errors are appended to the stderr_buf.  Errors
 *		running the command are reported through be_print_err().
 *	- Data which would overflow its respective buffer is sent to the bit
 *		bucket.
 *
 * Parameters:
 *		command: command to run.  Assumed not to have embedded stdout
 *			or stderr redirection.  May have stdin redirection,
 *			however.
 *		stderr_buf: buffer returning subprocess stderr data.  Errors
 *			reported by this function are reported through
 *			be_print_err().
 *		stderr_bufsize: size of stderr_buf
 *		stdout_buf: buffer returning subprocess stdout data.
 *		stdout_bufsize: size of stdout_buf
 * Returns:
 *		BE_SUCCESS - The command ran successfully without returning
 *			errors.
 *		BE_ERR_EXTCMD
 *			- The command could not be run.
 *			- The command terminated with error status.
 *			- There were errors extracting or returning subprocess
 *				data.
 *		BE_ERR_NOMEM - The command exceeds the command buffer size.
 *		BE_ERR_INVAL - An invalid argument was specified.
 * Scope:
 *		Semi-private (library wide use only)
 */
int
be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
    char *stdout_buf, int stdout_bufsize)
{
	char *temp_filename = strdup(tmpnam(NULL));
	FILE *stdout_str = NULL;
	FILE *stderr_str = NULL;
	char cmdline[BUFSIZ];
	char oneline[BUFSIZ];
	int exit_status;
	int rval = BE_SUCCESS;

	if ((command == NULL) || (stderr_buf == NULL) ||
	    (stderr_bufsize <= 0) || (stdout_bufsize <  0) ||
	    ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
		return (BE_ERR_INVAL);
}

	/* Set up command so popen returns stderr, not stdout */
	if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
	    temp_filename) >= BUFSIZ) {
		rval = BE_ERR_NOMEM;
		goto cleanup;
	}

	/* Set up the fifo that will make stderr available. */
	if (mkfifo(temp_filename, 0600) != 0) {
		(void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
		    strerror(errno));
		rval = BE_ERR_EXTCMD;
		goto cleanup;
	}

	if ((stdout_str = popen(cmdline, "r")) == NULL) {
		(void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
		    strerror(errno));
		rval = BE_ERR_EXTCMD;
		goto cleanup;
	}

	if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
		(void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
		    strerror(errno));
		(void) pclose(stdout_str);
		rval = BE_ERR_EXTCMD;
		goto cleanup;
	}

	/* Read stdout first, as it usually outputs more than stderr. */
	oneline[BUFSIZ-1] = '\0';
	while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
		if (stdout_str != NULL) {
			(void) strlcat(stdout_buf, oneline, stdout_bufsize);
		}
	}

	while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
		(void) strlcat(stderr_buf, oneline, stderr_bufsize);
	}

	/* Close pipe, get exit status. */
	if ((exit_status = pclose(stdout_str)) == -1) {
		(void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
		    strerror(errno));
		rval = BE_ERR_EXTCMD;
	} else if (WIFEXITED(exit_status)) {
		exit_status = (int)((char)WEXITSTATUS(exit_status));
		if (exit_status != 0) {
			(void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
			    "command terminated with error status: %d\n"),
			    exit_status);
			(void) strlcat(stderr_buf, oneline, stderr_bufsize);
			rval = BE_ERR_EXTCMD;
		}
	} else {
		(void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
		    "terminated on signal: %s\n"),
		    strsignal(WTERMSIG(exit_status)));
		(void) strlcat(stderr_buf, oneline, stderr_bufsize);
		rval = BE_ERR_EXTCMD;
	}

cleanup:
	(void) unlink(temp_filename);
	(void) free(temp_filename);

	return (rval);
}

/* ********************************************************************	*/
/*			Private Functions				*/
/* ******************************************************************** */

/*
 * Function:	update_dataset
 * Description:	This function takes a dataset name and replaces the zpool
 *		and be_name components of the dataset with the new be_name
 *		zpool passed in.
 * Parameters:
 *		dataset - name of dataset
 *		dataset_len - lenth of buffer in which dataset is passed in.
 *		be_name - name of new BE name to update to.
 *		old_rc_loc - dataset under which the root container dataset
 *			for the old BE lives.
 *		new_rc_loc - dataset under which the root container dataset
 *			for the new BE lives.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Private
 */
static int
update_dataset(char *dataset, int dataset_len, char *be_name,
    char *old_rc_loc, char *new_rc_loc)
{
	char	*ds = NULL;
	char	*sub_ds = NULL;

	/* Tear off the BE container dataset */
	if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
		return (BE_ERR_INVAL);
	}

	/* Get dataset name relative to BE root, if there is one */
	sub_ds = strchr(ds, '/');

	/* Generate the BE root dataset name */
	be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);

	/* If a subordinate dataset name was found, append it */
	if (sub_ds != NULL)
		(void) strlcat(dataset, sub_ds, dataset_len);

	free(ds);
	return (BE_SUCCESS);
}

/*
 * Function:	_update_vfstab
 * Description:	This function updates a vfstab file to reflect the new
 *		root container dataset location and be_name for all
 *		entries listed in the be_fs_list_data_t structure passed in.
 * Parameters:
 *		vfstab - vfstab file to modify
 *		be_name - name of BE to update.
 *		old_rc_loc - dataset under which the root container dataset
 *			of the old BE resides in.
 *		new_rc_loc - dataset under which the root container dataset
 *			of the new BE resides in.
 *		fld - be_fs_list_data_t pointer providing the list of
 *			file systems to look for in vfstab.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Private
 */
static int
_update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
    char *new_rc_loc, be_fs_list_data_t *fld)
{
	struct vfstab	vp;
	char		*tmp_vfstab = NULL;
	char		comments_buf[BUFSIZ];
	FILE		*comments = NULL;
	FILE		*vfs_ents = NULL;
	FILE		*tfile = NULL;
	struct stat	sb;
	char		dev[MAXPATHLEN];
	char		*c;
	int		fd;
	int		ret = BE_SUCCESS, err = 0;
	int		i;
	int		tmp_vfstab_len = 0;

	errno = 0;

	/*
	 * Open vfstab for reading twice.  First is for comments,
	 * second is for actual entries.
	 */
	if ((comments = fopen(vfstab, "r")) == NULL ||
	    (vfs_ents = fopen(vfstab, "r")) == NULL) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "failed to open vfstab (%s): %s\n"), vfstab,
		    strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	/* Grab the stats of the original vfstab file */
	if (stat(vfstab, &sb) != 0) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "failed to stat file %s: %s\n"), vfstab,
		    strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	/* Create tmp file for modified vfstab */
	if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
	    == NULL) {
		be_print_err(gettext("_update_vfstab: "
		    "malloc failed\n"));
		ret = BE_ERR_NOMEM;
		goto cleanup;
	}
	tmp_vfstab_len = strlen(vfstab) + 7;
	(void) memset(tmp_vfstab, 0, tmp_vfstab_len);
	(void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
	(void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
	if ((fd = mkstemp(tmp_vfstab)) == -1) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "mkstemp failed: %s\n"), strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	if ((tfile = fdopen(fd, "w")) == NULL) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "could not open file for write\n"));
		(void) close(fd);
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	while (fgets(comments_buf, BUFSIZ, comments)) {
		for (c = comments_buf; *c != '\0' && isspace(*c); c++)
			;
		if (*c == '\0') {
			continue;
		} else if (*c == '#') {
			/*
			 * If line is a comment line, just put
			 * it through to the tmp vfstab.
			 */
			(void) fputs(comments_buf, tfile);
		} else {
			/*
			 * Else line is a vfstab entry, grab it
			 * into a vfstab struct.
			 */
			if (getvfsent(vfs_ents, &vp) != 0) {
				err = errno;
				be_print_err(gettext("_update_vfstab: "
				    "getvfsent failed: %s\n"), strerror(err));
				ret = errno_to_be_err(err);
				goto cleanup;
			}

			if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
				(void) putvfsent(tfile, &vp);
				continue;
			}

			/*
			 * If the entry is one of the entries in the list
			 * of file systems to update, modify it's device
			 * field to be correct for this BE.
			 */
			for (i = 0; i < fld->fs_num; i++) {
				if (strcmp(vp.vfs_special, fld->fs_list[i])
				    == 0) {
					/*
					 * Found entry that needs an update.
					 * Replace the root container dataset
					 * location and be_name in the
					 * entry's device.
					 */
					(void) strlcpy(dev, vp.vfs_special,
					    sizeof (dev));

					if ((ret = update_dataset(dev,
					    sizeof (dev), be_name, old_rc_loc,
					    new_rc_loc)) != 0) {
						be_print_err(
						    gettext("_update_vfstab: "
						    "Failed to update device "
						    "field for vfstab entry "
						    "%s\n"), fld->fs_list[i]);
						goto cleanup;
					}

					vp.vfs_special = dev;
					break;
				}
			}

			/* Put entry through to tmp vfstab */
			(void) putvfsent(tfile, &vp);
		}
	}

	(void) fclose(comments);
	comments = NULL;
	(void) fclose(vfs_ents);
	vfs_ents = NULL;
	(void) fclose(tfile);
	tfile = NULL;

	/* Copy tmp vfstab into place */
	if (rename(tmp_vfstab, vfstab) != 0) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "failed to rename file %s to %s: %s\n"), tmp_vfstab,
		    vfstab, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

	/* Set the perms and ownership of the updated file */
	if (chmod(vfstab, sb.st_mode) != 0) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "failed to chmod %s: %s\n"), vfstab, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}
	if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
		err = errno;
		be_print_err(gettext("_update_vfstab: "
		    "failed to chown %s: %s\n"), vfstab, strerror(err));
		ret = errno_to_be_err(err);
		goto cleanup;
	}

cleanup:
	if (comments != NULL)
		(void) fclose(comments);
	if (vfs_ents != NULL)
		(void) fclose(vfs_ents);
	(void) unlink(tmp_vfstab);
	(void) free(tmp_vfstab);
	if (tfile != NULL)
		(void) fclose(tfile);

	return (ret);
}


/*
 * Function:	be_get_auto_name
 * Description:	Generate an auto name constructed based on the BE name
 *		of the original BE or zone BE being cloned.
 * Parameters:
 *		obe_name - name of the original BE or zone BE being cloned.
 *              container_ds - container dataset for the zone.
 *                             Note: if zone_be is false this should be
 *                                  NULL.
 *		zone_be - flag that indicates if we are operating on a zone BE.
 * Returns:
 *		Success - pointer to auto generated BE name.  The name
 *			is allocated in heap storage so the caller is
 *			responsible for free'ing the name.
 *		Failure - NULL
 * Scope:
 *		Private
 */
static char *
be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
{
	be_node_list_t	*be_nodes = NULL;
	be_node_list_t	*cur_be = NULL;
	char		auto_be_name[MAXPATHLEN];
	char		base_be_name[MAXPATHLEN];
	char		cur_be_name[MAXPATHLEN];
	char		*num_str = NULL;
	char		*c = NULL;
	int		num = 0;
	int		cur_num = 0;

	errno = 0;

	/*
	 * Check if obe_name is already in an auto BE name format.
	 * If it is, then strip off the increment number to get the
	 * base name.
	 */
	(void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));

	if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
	    != NULL) {
		/* Make sure remaining string is all digits */
		c = num_str + 1;
		while (c[0] != '\0' && isdigit(c[0]))
			c++;
		/*
		 * If we're now at the end of the string strip off the
		 * increment number.
		 */
		if (c[0] == '\0')
			num_str[0] = '\0';
	}

	if (zone_be) {
		if (be_container_ds == NULL)
			return (NULL);
		if (be_get_zone_be_list(obe_name, be_container_ds,
		    &be_nodes) != BE_SUCCESS) {
			be_print_err(gettext("be_get_auto_name: "
			    "be_get_zone_be_list failed\n"));
			return (NULL);
		}
	} else if (_be_list(NULL, &be_nodes) != BE_SUCCESS) {
		be_print_err(gettext("be_get_auto_name: be_list failed\n"));
		return (NULL);
	}

	for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
		(void) strlcpy(cur_be_name, cur_be->be_node_name,
		    sizeof (cur_be_name));

		/* If cur_be_name doesn't match at least base be name, skip. */
		if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
		    != 0)
			continue;

		/* Get the string following the base be name */
		num_str = cur_be_name + strlen(base_be_name);

		/*
		 * If nothing follows the base be name, this cur_be_name
		 * is the BE named with the base be name, skip.
		 */
		if (num_str == NULL || num_str[0] == '\0')
			continue;

		/*
		 * Remove the name delimiter.  If its not there,
		 * cur_be_name isn't part of this BE name stream, skip.
		 */
		if (num_str[0] == BE_AUTO_NAME_DELIM)
			num_str++;
		else
			continue;

		/* Make sure remaining string is all digits */
		c = num_str;
		while (c[0] != '\0' && isdigit(c[0]))
			c++;
		if (c[0] != '\0')
			continue;

		/* Convert the number string to an int */
		cur_num = atoi(num_str);

		/*
		 * If failed to convert the string, skip it.  If its too
		 * long to be converted to an int, we wouldn't auto generate
		 * this number anyway so there couldn't be a conflict.
		 * We treat it as a manually created BE name.
		 */
		if (cur_num == 0 && errno == EINVAL)
			continue;

		/*
		 * Compare current number to current max number,
		 * take higher of the two.
		 */
		if (cur_num > num)
			num = cur_num;
	}

	/*
	 * Store off a copy of 'num' incase we need it later.  If incrementing
	 * 'num' causes it to roll over, this means 'num' is the largest
	 * positive int possible; we'll need it later in the loop to determine
	 * if we've exhausted all possible increment numbers.  We store it in
	 * 'cur_num'.
	 */
	cur_num = num;

	/* Increment 'num' to get new auto BE name number */
	if (++num <= 0) {
		int ret = 0;

		/*
		 * Since incrementing 'num' caused it to rollover, start
		 * over at 0 and find the first available number.
		 */
		for (num = 0; num < cur_num; num++) {

			(void) snprintf(cur_be_name, sizeof (cur_be_name),
			    "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);

			ret = zpool_iter(g_zfs, be_exists_callback,
			    cur_be_name);

			if (ret == 0) {
				/*
				 * BE name doesn't exist, break out
				 * to use 'num'.
				 */
				break;
			} else if (ret == 1) {
				/* BE name exists, continue looking */
				continue;
			} else {
				be_print_err(gettext("be_get_auto_name: "
				    "zpool_iter failed: %s\n"),
				    libzfs_error_description(g_zfs));
				be_free_list(be_nodes);
				return (NULL);
			}
		}

		/*
		 * If 'num' equals 'cur_num', we've exhausted all possible
		 * auto BE names for this base BE name.
		 */
		if (num == cur_num) {
			be_print_err(gettext("be_get_auto_name: "
			    "No more available auto BE names for base "
			    "BE name %s\n"), base_be_name);
			be_free_list(be_nodes);
			return (NULL);
		}
	}

	be_free_list(be_nodes);

	/*
	 * Generate string for auto BE name.
	 */
	(void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
	    base_be_name, BE_AUTO_NAME_DELIM, num);

	if ((c = strdup(auto_be_name)) == NULL) {
		be_print_err(gettext("be_get_auto_name: "
		    "memory allocation failed\n"));
		return (NULL);
	}

	return (c);
}

/*
 * Function:	be_get_console_prop
 * Description:	Determine console device.
 * Returns:
 *		Success - pointer to console setting.
 *		Failure - NULL
 * Scope:
 *		Private
 */
static char *
be_get_console_prop(void)
{
	di_node_t	dn;
	char *console = NULL;

	if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
		be_print_err(gettext("be_get_console_prop: "
		    "di_init() failed\n"));
		return (NULL);
	}

	if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
	    "console", &console) != -1) {
		di_fini(dn);
		return (console);
	}

	if (console == NULL) {
		if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
		    "output-device", &console) != -1) {
			di_fini(dn);
			if (strncmp(console, "screen", strlen("screen")) == 0)
				console = BE_DEFAULT_CONSOLE;
		}
	}

	/*
	 * Default console to text
	 */
	if (console == NULL) {
		console = BE_DEFAULT_CONSOLE;
	}

	return (console);
}

/*
 * Function:	be_create_menu
 * Description:
 *		This function is used if no menu.lst file exists. In
 *		this case a new file is created and if needed default
 *		lines are added to the file.
 * Parameters:
 *		pool - The name of the pool the menu.lst file is on
 *		pool_mntpt - The mountpoint for the pool we're using.
 *		menu_file - The name of the file we're creating.
 *		menu_fp - A pointer to the file pointer of the file we
 *			  created. This is also used to pass back the file
 *			  pointer to the newly created file.
 *		mode - the original mode used for the failed attempt to
 *		       non-existent file.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Private
 */
static int
be_create_menu(
	char *pool,
	char *pool_mntpt,
	char *menu_file,
	FILE **menu_fp,
	char *mode)
{
	be_node_list_t	*be_nodes = NULL;
	char *menu_path = NULL;
	char *be_rpool = NULL;
	char *be_name = NULL;
	char *console = NULL;
	errno = 0;

	if (menu_file == NULL || menu_fp == NULL || mode == NULL)
		return (BE_ERR_INVAL);

	menu_path = strdup(menu_file);
	if (menu_path == NULL)
		return (BE_ERR_NOMEM);

	(void) dirname(menu_path);
	if (*menu_path == '.') {
		free(menu_path);
		return (BE_ERR_BAD_MENU_PATH);
	}
	if (mkdirp(menu_path,
	    S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
	    errno != EEXIST) {
		free(menu_path);
		be_print_err(gettext("be_create_menu: Failed to create the %s "
		    "directory: %s\n"), menu_path, strerror(errno));
		return (errno_to_be_err(errno));
	}
	free(menu_path);

	/*
	 * Check to see if this system supports grub
	 */
	if (be_has_grub()) {
		/*
		 * The grub menu is missing so we need to create it
		 * and fill in the first few lines.
		 */
		FILE *temp_fp = fopen(menu_file, "a+");
		if (temp_fp == NULL) {
			*menu_fp = NULL;
			return (errno_to_be_err(errno));
		}

		if ((console = be_get_console_prop()) != NULL) {

			/*
			 * If console is redirected to serial line,
			 * GRUB splash screen will not be enabled.
			 */
			if (strncmp(console, "text", strlen("text")) == 0 ||
			    strncmp(console, "graphics",
			    strlen("graphics")) == 0) {

				(void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
				(void) fprintf(temp_fp, "%s\n",
				    BE_GRUB_FOREGROUND);
				(void) fprintf(temp_fp, "%s\n",
				    BE_GRUB_BACKGROUND);
				(void) fprintf(temp_fp, "%s\n",
				    BE_GRUB_DEFAULT);
			} else {
				be_print_err(gettext("be_create_menu: "
				    "console on serial line, "
				    "GRUB splash image will be disabled\n"));
			}
		}

		(void) fprintf(temp_fp,	"timeout 30\n");
		(void) fclose(temp_fp);

	} else {
		/*
		 * The menu file doesn't exist so we need to create a
		 * blank file.
		 */
		FILE *temp_fp = fopen(menu_file, "w+");
		if (temp_fp == NULL) {
			*menu_fp = NULL;
			return (errno_to_be_err(errno));
		}
		(void) fclose(temp_fp);
	}

	/*
	 * Now we need to add all the BE's back into the the file.
	 */
	if (_be_list(NULL, &be_nodes) == BE_SUCCESS) {
		while (be_nodes != NULL) {
			if (strcmp(pool, be_nodes->be_rpool) == 0) {
				(void) be_append_menu(be_nodes->be_node_name,
				    be_nodes->be_rpool, NULL, NULL, NULL);
			}
			if (be_nodes->be_active_on_boot) {
				be_rpool = strdup(be_nodes->be_rpool);
				be_name = strdup(be_nodes->be_node_name);
			}

			be_nodes = be_nodes->be_next_node;
		}
	}
	be_free_list(be_nodes);

	/*
	 * Check to see if this system supports grub
	 */
	if (be_has_grub()) {
		int err = be_change_grub_default(be_name, be_rpool);
		if (err != BE_SUCCESS)
			return (err);
	}
	*menu_fp = fopen(menu_file, mode);
	if (*menu_fp == NULL)
		return (errno_to_be_err(errno));

	return (BE_SUCCESS);
}

/*
 * Function:	be_open_menu
 * Description:
 *		This function is used it open the menu.lst file. If this
 *              file does not exist be_create_menu is called to create it
 *              and the open file pointer is returned. If the file does
 *              exist it is simply opened using the mode passed in.
 * Parameters:
 *		pool - The name of the pool the menu.lst file is on
 *		pool_mntpt - The mountpoint for the pool we're using.
 *			     The mountpoint is used since the mountpoint
 *			     name can differ from the pool name.
 *		menu_file - The name of the file we're opening.
 *		menu_fp - A pointer to the file pointer of the file we're
 *			  opening. This is also used to pass back the file
 *			  pointer.
 *		mode - the original mode to be used for opening the menu.lst
 *                     file.
 *              create_menu - If this is true and the menu.lst file does not
 *                            exist we will attempt to re-create it. However
 *                            if it's false the error returned from the fopen
 *                            will be returned.
 * Returns:
 *		BE_SUCCESS - Success
 *		be_errno_t - Failure
 * Scope:
 *		Private
 */
static int
be_open_menu(
	char *pool,
	char *pool_mntpt,
	char *menu_file,
	FILE **menu_fp,
	char *mode,
	boolean_t create_menu)
{
	int	err = 0;
	boolean_t	set_print = B_FALSE;

	*menu_fp = fopen(menu_file, mode);
	err = errno;
	if (*menu_fp == NULL) {
		if (err == ENOENT && create_menu) {
			be_print_err(gettext("be_open_menu: menu.lst "
			    "file %s does not exist,\n"), menu_file);
			if (!do_print) {
				set_print = B_TRUE;
				do_print = B_TRUE;
			}
			be_print_err(gettext("WARNING: menu.lst "
			    "file %s does not exist,\n         generating "
			    "a new menu.lst file\n"), menu_file);
			if (set_print)
				do_print = B_FALSE;
			err = 0;
			if ((err = be_create_menu(pool, pool_mntpt, menu_file,
			    menu_fp, mode)) == ENOENT)
				return (BE_ERR_NO_MENU);
			else if (err != BE_SUCCESS)
				return (err);
			else if (*menu_fp == NULL)
				return (BE_ERR_NO_MENU);
		} else {
			be_print_err(gettext("be_open_menu: failed "
			    "to open menu.lst file %s\n"), menu_file);
			if (err == ENOENT)
				return (BE_ERR_NO_MENU);
			else
				return (errno_to_be_err(err));
		}
	}
	return (BE_SUCCESS);
}