#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 <deflt.h>
#include <wait.h>
#include <libdevinfo.h>
#include <libgen.h>
#include <libbe.h>
#include <libbe_priv.h>
#include <boot_utils.h>
#include <ficl.h>
#include <ficlplatform/emu.h>
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 *, FILE **, char *, boolean_t);
static int be_create_menu(char *, char *, FILE **, char *);
static char *be_get_auto_name(char *, char *, boolean_t);
boolean_t do_print = B_FALSE;
typedef struct zone_be_name_cb_data {
char *base_be_name;
int num;
} zone_be_name_cb_data_t;
static void
ficlSuppressTextOutput(ficlCallback *cb, char *text)
{
}
int
be_get_boot_args(char **fbarg, int entry)
{
be_node_list_t *node, *be_nodes = NULL;
be_transaction_data_t bt = {0};
char *mountpoint = NULL;
boolean_t be_mounted = B_FALSE;
int ret = BE_SUCCESS;
int index;
ficlVm *vm;
*fbarg = NULL;
if (!be_zfs_init())
return (BE_ERR_INIT);
ret = be_find_current_be(&bt);
if (ret != BE_SUCCESS) {
be_zfs_fini();
return (ret);
}
if (be_has_grub()) {
ret = BE_ERR_INIT;
goto done;
}
ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
if (ret != BE_SUCCESS)
goto done;
index = 0;
for (node = be_nodes; node != NULL; node = node->be_next_node) {
if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
continue;
if (entry == BE_ENTRY_DEFAULT &&
node->be_active_on_boot == B_TRUE)
break;
if (index == entry)
break;
index++;
}
if (node == NULL) {
be_free_list(be_nodes);
ret = BE_ERR_NOENT;
goto done;
}
if (node->be_active == B_FALSE) {
ret = _be_mount(node->be_node_name, &mountpoint,
BE_MOUNT_FLAG_NO_ZONES);
if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
be_free_list(be_nodes);
goto done;
} else
be_mounted = B_TRUE;
}
vm = bf_init("", ficlSuppressTextOutput);
if (vm != NULL) {
char buf[MAXNAMELEN * 2];
char *kernel_options = NULL;
char *kernel = NULL;
char *tmp;
zpool_handle_t *zph;
(void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
node->be_root_ds);
ret = ficlVmEvaluate(vm, buf);
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
be_print_err(gettext("be_get_boot_args: error "
"interpreting boot config: %d\n"), ret);
bf_fini();
ret = BE_ERR_NO_MENU;
goto cleanup;
}
(void) snprintf(buf, sizeof (buf),
"include /boot/forth/loader.4th");
ret = ficlVmEvaluate(vm, buf);
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
be_print_err(gettext("be_get_boot_args: error "
"interpreting boot config: %d\n"), ret);
bf_fini();
ret = BE_ERR_NO_MENU;
goto cleanup;
}
(void) snprintf(buf, sizeof (buf), "start");
ret = ficlVmEvaluate(vm, buf);
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
be_print_err(gettext("be_get_boot_args: error "
"interpreting boot config: %d\n"), ret);
bf_fini();
ret = BE_ERR_NO_MENU;
goto cleanup;
}
(void) snprintf(buf, sizeof (buf), "boot");
ret = ficlVmEvaluate(vm, buf);
bf_fini();
if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
be_print_err(gettext("be_get_boot_args: error "
"interpreting boot config: %d\n"), ret);
ret = BE_ERR_NO_MENU;
goto cleanup;
}
kernel_options = getenv("boot-args");
kernel = getenv("kernelname");
if (kernel == NULL) {
be_print_err(gettext("be_get_boot_args: no kernel\n"));
ret = BE_ERR_NOENT;
goto cleanup;
}
if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
be_print_err(gettext("be_get_boot_args: failed to "
"open root pool (%s): %s\n"), node->be_rpool,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto cleanup;
}
ret = zpool_get_physpath(zph, buf, sizeof (buf));
zpool_close(zph);
if (ret != 0) {
be_print_err(gettext("be_get_boot_args: failed to "
"get physpath\n"));
goto cleanup;
}
tmp = buf;
tmp = strsep(&tmp, " ");
if (kernel_options == NULL || *kernel_options == '\0')
(void) asprintf(fbarg, "/ %s "
"-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
node->be_root_ds, tmp);
else
(void) asprintf(fbarg, "/ %s %s "
"-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
kernel_options, node->be_root_ds, tmp);
if (*fbarg == NULL)
ret = BE_ERR_NOMEM;
else
ret = 0;
} else
ret = BE_ERR_NOMEM;
cleanup:
if (be_mounted == B_TRUE)
(void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
be_free_list(be_nodes);
done:
free(mountpoint);
free(bt.obe_name);
free(bt.obe_root_ds);
free(bt.obe_zpool);
free(bt.obe_snap_name);
free(bt.obe_altroot);
be_zfs_fini();
return (ret);
}
int
be_max_avail(char *dataset, uint64_t *ret)
{
zfs_handle_t *zhp;
int err = 0;
if (!be_zfs_init())
return (BE_ERR_INIT);
zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
if (zhp == NULL) {
err = zfs_err_to_be_err(g_zfs);
} else {
err = be_maxsize_avail(zhp, ret);
}
ZFS_CLOSE(zhp);
be_zfs_fini();
return (err);
}
void
libbe_print_errors(boolean_t set_do_print)
{
do_print = set_do_print;
}
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);
}
void
be_zfs_fini(void)
{
if (g_zfs)
libzfs_fini(g_zfs);
g_zfs = NULL;
}
void
be_get_defaults(struct be_defaults *defaults)
{
void *defp;
defaults->be_deflt_grub = B_FALSE;
defaults->be_deflt_rpool_container = B_FALSE;
defaults->be_deflt_bename_starts_with[0] = '\0';
if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
if (res != NULL && res[0] != '\0') {
(void) strlcpy(defaults->be_deflt_bename_starts_with,
res, ZFS_MAX_DATASET_NAME_LEN);
defaults->be_deflt_rpool_container = B_TRUE;
}
if (be_is_isa("i386")) {
res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
if (res != NULL && res[0] != '\0') {
if (strcasecmp(res, "true") == 0)
defaults->be_deflt_grub = B_TRUE;
}
}
defclose_r(defp);
}
}
int
be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
int be_root_ds_size)
{
struct be_defaults be_defaults;
be_get_defaults(&be_defaults);
assert(zpool != NULL);
if (getzoneid() == GLOBAL_ZONEID) {
if (be_defaults.be_deflt_rpool_container) {
(void) snprintf(be_root_ds, be_root_ds_size,
"%s/%s", zpool, be_name);
} else {
(void) snprintf(be_root_ds, be_root_ds_size,
"%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
}
} else {
char *root_ds = be_get_ds_from_dir("/");
if (root_ds == NULL) {
be_print_err(gettext("be_make_root_ds: zone root "
"dataset is not mounted\n"));
return (BE_ERR_NOTMOUNTED);
}
if (strncmp(root_ds, zpool, strlen(zpool)) != 0 ||
root_ds[strlen(zpool)] != '/') {
return (BE_ERR_ACCESS);
}
(void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
dirname(root_ds), be_name);
}
return (BE_SUCCESS);
}
int
be_make_container_ds(const char *zpool, char *container_ds,
int container_ds_size)
{
struct be_defaults be_defaults;
be_get_defaults(&be_defaults);
if (getzoneid() == GLOBAL_ZONEID) {
if (be_defaults.be_deflt_rpool_container) {
(void) snprintf(container_ds, container_ds_size,
"%s", zpool);
} else {
(void) snprintf(container_ds, container_ds_size,
"%s/%s", zpool, BE_CONTAINER_DS_NAME);
}
} else {
char *root_ds = be_get_ds_from_dir("/");
if (root_ds == NULL) {
be_print_err(gettext("be_make_container_ds: zone root "
"dataset is not mounted\n"));
return (BE_ERR_NOTMOUNTED);
}
if (strncmp(root_ds, zpool, strlen(zpool)) != 0 ||
root_ds[strlen(zpool)] != '/') {
return (BE_ERR_ACCESS);
}
(void) strlcpy(container_ds, dirname(root_ds),
container_ds_size);
}
return (BE_SUCCESS);
}
int
be_make_root_container_ds(const char *zpool, char *container_ds,
int container_ds_size)
{
char *root;
int ret;
if ((ret = be_make_container_ds(zpool, container_ds,
container_ds_size)) != BE_SUCCESS) {
return (ret);
}
if ((root = strrchr(container_ds, '/')) != NULL &&
strcmp(root + 1, BE_CONTAINER_DS_NAME) == 0) {
*root = '\0';
}
return (BE_SUCCESS);
}
char *
be_make_name_from_ds(const char *dataset, char *rc_loc)
{
char ds[ZFS_MAX_DATASET_NAME_LEN];
char *tok = NULL;
char *name = NULL;
struct be_defaults be_defaults;
int rlen = strlen(rc_loc);
be_get_defaults(&be_defaults);
if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
(void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
else
return (NULL);
if (be_defaults.be_deflt_rpool_container) {
if ((name = strdup(ds)) == NULL) {
be_print_err(gettext("be_make_name_from_ds: "
"memory allocation failed\n"));
return (NULL);
}
} else {
if ((tok = strtok(ds, "/")) == NULL ||
strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
return (NULL);
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);
}
int
be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
{
return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
}
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));
}
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);
}
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;
}
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);
}
if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds,
sizeof (be_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, be_root_pool, be_name);
goto cleanup;
}
if ((ret = be_open_menu(be_root_pool, 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;
(void) snprintf(str, BUFSIZ, "title %s\n",
description ? description : be_name);
entries[num_lines] = strdup(str);
num_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;
(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) {
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) {
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;
}
}
}
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) {
for (i = 0; i < num_lines; i++) {
(void) fprintf(menu_fp, "%s", entries[i]);
free(entries[i]);
}
num_lines = 0;
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);
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);
}
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;
if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds,
sizeof (be_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, be_root_pool, be_name);
return (ret);
}
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));
}
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);
}
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;
}
(void) strlcpy(menu, pool_mntpnt, sizeof (menu));
if (be_has_grub())
(void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
else
(void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
if ((ret = be_open_menu(be_root_pool, 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;
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;
}
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));
tok = strtok(tline, BE_WHITE_SPACE);
if (tok == NULL || tok[0] == '#') {
if (do_buffer) {
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) {
(void) fputs(menu_buf, tmp_menu_fp);
}
} else if (strcmp(tok, "default") == 0) {
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 (do_buffer) {
for (i = 0; i < nlines; i++) {
(void) fputs(buffer[i], tmp_menu_fp);
free(buffer[i]);
}
free(buffer);
buffer = NULL;
nlines = 0;
}
write = B_FALSE;
do_buffer = B_TRUE;
entry_cnt++;
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;
if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
strcmp(bootfs, be_root_ds) != 0) {
for (i = 0; i < nlines; i++) {
(void) fputs(buffer[i], tmp_menu_fp);
free(buffer[i]);
}
free(buffer);
buffer = NULL;
nlines = 0;
write = B_TRUE;
do_buffer = B_FALSE;
(void) fputs(menu_buf, tmp_menu_fp);
} else {
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) {
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) {
(void) fputs(menu_buf, tmp_menu_fp);
}
}
}
(void) fclose(menu_fp);
menu_fp = NULL;
(void) fclose(tmp_menu_fp);
tmp_menu_fp = NULL;
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 (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;
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;
}
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));
tok = strtok(tline, BE_WHITE_SPACE);
if (tok == NULL) {
(void) fputs(menu_buf, tmp_menu_fp);
} else if (strcmp(tok, "default") == 0) {
(void) snprintf(tline, sizeof (tline),
"default %d\n", default_entry);
(void) fputs(tline, tmp_menu_fp);
} else {
(void) fputs(menu_buf, tmp_menu_fp);
}
}
(void) fclose(menu_fp);
menu_fp = NULL;
(void) fclose(tmp_menu_fp);
tmp_menu_fp = NULL;
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 (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);
}
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;
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;
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));
}
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);
}
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, 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) {
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);
}
int
be_change_grub_default(char *be_name, char *be_root_pool)
{
zfs_handle_t *zhp = NULL;
char grub_file[MAXPATHLEN];
char *temp_grub = NULL;
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;
if (!be_has_grub()) {
be_print_err(gettext("be_change_grub_default: operation "
"not supported on this architecture\n"));
return (BE_ERR_NOTSUP);
}
if ((ret = be_make_root_ds(be_root_pool, be_name, be_root_ds,
sizeof (be_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, be_root_pool, be_name);
return (ret);
}
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));
}
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);
}
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, 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;
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;
}
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;
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);
}
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));
}
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);
}
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;
}
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);
}
if ((ret = be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
sizeof (be_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, be_root_pool, be_orig_name);
goto cleanup;
}
if ((ret = be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
sizeof (be_new_root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, be_root_pool, be_new_name);
goto cleanup;
}
if ((ret = be_open_menu(be_root_pool, 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;
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;
}
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));
c = strtok(tline, BE_WHITE_SPACE);
if (c == NULL) {
(void) fputs(line, new_fp);
} else if (c[0] == '#') {
(void) fputs(line, new_fp);
} else if (strcmp(c, "title") == 0) {
char *name = NULL;
char *desc = NULL;
name = strtok(NULL, BE_WHITE_SPACE);
if (name == NULL) {
(void) fputs(line, new_fp);
} else {
desc = strtok(NULL, "\n");
if (strcmp(name, be_orig_name) == 0) {
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) {
char *root_ds = strtok(NULL, BE_WHITE_SPACE);
if (root_ds == NULL) {
(void) fputs(line, new_fp);
} else {
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 {
(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);
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);
}
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 = NULL;
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;
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);
}
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);
}
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, 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) {
*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);
}
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 (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;
}
(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
tmp_mountpoint);
ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
fld);
if (mountpoint == NULL) {
if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
(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);
}
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 (!zfs_is_mounted(zhp, &md.altroot)) {
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;
}
(void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
md.altroot);
ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
fld);
if (mounted_here) {
ud.force = B_TRUE;
if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
(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);
}
char *
be_auto_snap_name(void)
{
time_t utc_tm = 0;
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);
}
char *
be_auto_be_name(char *obe_name)
{
return (be_get_auto_name(obe_name, NULL, B_FALSE));
}
char *
be_auto_zone_be_name(char *container_ds, char *zbe_name)
{
return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
}
boolean_t
be_valid_be_name(const char *be_name)
{
const char *c = NULL;
struct be_defaults be_defaults;
if (be_name == NULL)
return (B_FALSE);
be_get_defaults(&be_defaults);
c = be_name;
while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
c++;
if (*c != '\0')
return (B_FALSE);
if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
strlen(be_name) > BE_NAME_MAX_LEN)
return (B_FALSE);
if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
return (B_FALSE);
}
return (B_TRUE);
}
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;
if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
(mktime(&gmt_tm) != -1)) {
return (B_TRUE);
}
policy = strdup(name);
c = strchr(policy, ':');
if (c == NULL) {
free(policy);
return (B_FALSE);
}
c[0] = '\0';
if (!valid_be_policy(policy)) {
free(policy);
return (B_FALSE);
}
if (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';
if (strcmp(reserved, "-") != 0) {
free(policy);
return (B_FALSE);
}
if (c[1] == '\0') {
free(policy);
return (B_FALSE);
}
date = c+1;
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);
}
char *
be_default_policy(void)
{
return (BE_PLCY_STATIC);
}
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);
}
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);
(void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
(void) fputs(buf, stderr);
va_end(ap);
}
}
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);
}
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];
if (be_make_container_ds(zpool, be_container_ds,
sizeof (be_container_ds)) != BE_SUCCESS) {
zpool_close(zlp);
return (0);
}
if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
zpool_close(zlp);
return (0);
}
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);
}
if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
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);
}
int
be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
{
be_transaction_data_t *bt = data;
char *mp = NULL;
if (zfs_is_mounted(zhp, &mp)) {
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);
}
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];
if (be_make_container_ds(zpool, be_container_ds,
sizeof (be_container_ds)) != BE_SUCCESS) {
return (0);
}
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);
}
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);
}
}
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);
}
}
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);
}
}
boolean_t
be_has_grub(void)
{
static struct be_defaults be_defaults;
static boolean_t be_deflts_set = B_FALSE;
if (be_deflts_set == B_FALSE) {
be_get_defaults(&be_defaults);
be_deflts_set = B_TRUE;
}
return (be_defaults.be_deflt_grub);
}
boolean_t
be_is_isa(char *name)
{
return ((strcmp((char *)be_get_default_isa(), name) == 0));
}
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);
}
char *
be_get_platform(void)
{
int i;
static char default_inst[ARCH_LENGTH] = "";
if (default_inst[0] == '\0') {
i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
if (i < 0 || i > ARCH_LENGTH)
return (NULL);
}
return (default_inst);
}
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);
}
if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
temp_filename) >= BUFSIZ) {
rval = BE_ERR_NOMEM;
goto cleanup;
}
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;
}
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);
}
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 != BC_SUCCESS && exit_status != BC_NOUPDT) {
(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);
}
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;
int ret;
if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
return (BE_ERR_INVAL);
}
sub_ds = strchr(ds, '/');
if ((ret = be_make_root_ds(new_rc_loc, be_name, dataset,
dataset_len)) != BE_SUCCESS) {
return (ret);
}
if (sub_ds != NULL)
(void) strlcat(dataset, sub_ds, dataset_len);
free(ds);
return (BE_SUCCESS);
}
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;
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;
}
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;
}
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 == '#') {
(void) fputs(comments_buf, tfile);
} else {
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;
}
for (i = 0; i < fld->fs_num; i++) {
if (strcmp(vp.vfs_special, fld->fs_list[i])
== 0) {
(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;
}
}
(void) putvfsent(tfile, &vp);
}
}
(void) fclose(comments);
comments = NULL;
(void) fclose(vfs_ents);
vfs_ents = NULL;
(void) fclose(tfile);
tfile = NULL;
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;
}
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);
}
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;
(void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
!= NULL) {
c = num_str + 1;
while (c[0] != '\0' && isdigit(c[0]))
c++;
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_LIST_DEFAULT) != 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 (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
!= 0)
continue;
num_str = cur_be_name + strlen(base_be_name);
if (num_str == NULL || num_str[0] == '\0')
continue;
if (num_str[0] == BE_AUTO_NAME_DELIM)
num_str++;
else
continue;
c = num_str;
while (c[0] != '\0' && isdigit(c[0]))
c++;
if (c[0] != '\0')
continue;
cur_num = atoi(num_str);
if (cur_num == 0 && errno == EINVAL)
continue;
if (cur_num > num)
num = cur_num;
}
cur_num = num;
if (++num <= 0) {
int ret = 0;
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) {
break;
} else if (ret == 1) {
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 == 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);
(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);
}
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;
}
}
if (console == NULL) {
console = BE_DEFAULT_CONSOLE;
}
return (console);
}
static int
be_create_menu(
char *pool,
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) {
be_print_err(gettext("be_create_menu: Failed to create the %s "
"directory: %s\n"), menu_path, strerror(errno));
free(menu_path);
return (errno_to_be_err(errno));
}
free(menu_path);
if (be_has_grub()) {
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 (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 {
FILE *temp_fp = fopen(menu_file, "w+");
if (temp_fp == NULL) {
*menu_fp = NULL;
return (errno_to_be_err(errno));
}
(void) fclose(temp_fp);
}
if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == 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);
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);
}
static int
be_open_menu(
char *pool,
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, 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);
}