#include <assert.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libzfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <sys/mnttab.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/efi_partition.h>
#include <libbe.h>
#include <libbe_priv.h>
#include <libzfsbootenv.h>
char *mnttab = MNTTAB;
static int set_bootfs(char *boot_rpool, char *be_root_ds);
static int set_canmount(be_node_list_t *, char *);
static boolean_t be_do_install_mbr(char *, nvlist_t *);
static int be_do_installboot_helper(zpool_handle_t *, nvlist_t *, char *,
char *, uint16_t);
static int be_do_installboot(be_transaction_data_t *, uint16_t);
static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
static int get_ver_from_capfile(char *, char **);
static int be_promote_zone_ds(char *, char *);
static int be_promote_ds_callback(zfs_handle_t *, void *);
int
be_activate(nvlist_t *be_attrs)
{
int ret = BE_SUCCESS;
char *be_name = NULL;
be_nextboot_state_t nextboot;
boolean_t next_boot;
if (!be_zfs_init())
return (BE_ERR_INIT);
if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
!= 0) {
be_print_err(gettext("be_activate: failed to "
"lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
be_zfs_fini();
return (BE_ERR_INVAL);
}
if (!be_valid_be_name(be_name)) {
be_print_err(gettext("be_activate: invalid BE name %s\n"),
be_name);
be_zfs_fini();
return (BE_ERR_INVAL);
}
if (nvlist_lookup_boolean_value(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT,
&next_boot) == 0) {
if (next_boot)
nextboot = BE_NEXTBOOT_SET;
else
nextboot = BE_NEXTBOOT_UNSET;
} else {
nextboot = BE_NEXTBOOT_IGNORE;
}
ret = _be_activate(be_name, nextboot);
be_zfs_fini();
return (ret);
}
int
be_installboot(nvlist_t *be_attrs)
{
int ret = BE_SUCCESS;
uint16_t flags = 0;
uint16_t verbose;
be_transaction_data_t bt = { 0 };
if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
BE_ATTR_INSTALL_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
be_print_err(gettext("be_installboot: failed to lookup "
"BE_ATTR_INSTALL_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
verbose = flags & BE_INSTALLBOOT_FLAG_VERBOSE;
if (verbose == BE_INSTALLBOOT_FLAG_VERBOSE)
libbe_print_errors(B_TRUE);
ret = nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
&bt.obe_name);
if (ret != 0) {
be_print_err(gettext("be_installboot: failed to "
"lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
ret = nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_POOL,
&bt.obe_zpool);
if (ret != 0) {
be_print_err(gettext("be_installboot: failed to "
"lookup BE_ATTR_ORIG_BE_POOL attribute\n"));
return (BE_ERR_INVAL);
}
ret = nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_ROOT,
&bt.obe_root_ds);
if (ret != 0) {
be_print_err(gettext("be_installboot: failed to "
"lookup BE_ATTR_ORIG_BE_ROOT attribute\n"));
return (BE_ERR_INVAL);
}
if (!be_zfs_init())
return (BE_ERR_INIT);
ret = be_do_installboot(&bt, flags);
be_zfs_fini();
return (ret);
}
int
_be_activate(char *be_name, be_nextboot_state_t nextboot)
{
be_transaction_data_t cb = { 0 };
zfs_handle_t *zhp = NULL;
char root_ds[MAXPATHLEN];
char active_ds[MAXPATHLEN];
be_node_list_t *be_nodes = NULL;
uuid_t uu = {0};
int entry, ret = BE_SUCCESS;
int zret = 0;
if (be_name == NULL)
return (BE_ERR_INVAL);
if (nextboot == BE_NEXTBOOT_SET && getzoneid() != GLOBAL_ZONEID)
return (BE_ERR_INVAL);
cb.obe_name = be_name;
if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
be_print_err(gettext("be_activate: failed to "
"find zpool for BE (%s)\n"), cb.obe_name);
return (BE_ERR_BE_NOENT);
} else if (zret < 0) {
be_print_err(gettext("be_activate: "
"zpool_iter failed: %s\n"),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if ((ret = be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds,
sizeof (root_ds))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container dataset "
"for %s/%s\n"), __func__, cb.obe_zpool, cb.obe_name);
return (ret);
}
cb.obe_root_ds = strdup(root_ds);
if (getzoneid() == GLOBAL_ZONEID) {
ret = be_do_installboot(&cb, BE_INSTALLBOOT_FLAG_NULL);
if (ret != BE_SUCCESS)
return (ret);
if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
NULL, NULL, NULL)) != BE_SUCCESS) {
be_print_err(gettext("be_activate: Failed to "
"add BE (%s) to the menu\n"),
cb.obe_name);
goto done;
}
}
if (be_has_grub()) {
if ((ret = be_change_grub_default(cb.obe_name,
cb.obe_zpool)) != BE_SUCCESS) {
be_print_err(gettext("be_activate: failed to "
"change the default entry in menu.lst\n"));
goto done;
}
}
}
if ((ret = _be_list(cb.obe_name, &be_nodes, BE_LIST_DEFAULT))
!= BE_SUCCESS) {
return (ret);
}
if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
be_print_err(gettext("be_activate: failed to set "
"canmount dataset property\n"));
goto done;
}
if (getzoneid() == GLOBAL_ZONEID) {
switch (nextboot) {
case BE_NEXTBOOT_SET:
if ((ret = lzbe_set_boot_device(be_nodes->be_rpool,
lzbe_add, root_ds)) != 0) {
be_print_err(gettext("be_activate: failed to "
"set nextboot for %s\n"), root_ds);
goto done;
}
break;
case BE_NEXTBOOT_UNSET:
if ((ret = lzbe_set_boot_device(be_nodes->be_rpool,
lzbe_add, "")) != 0) {
be_print_err(gettext("be_activate: failed to "
"clear nextboot for %s\n"), root_ds);
goto done;
}
break;
default:
if ((ret = set_bootfs(be_nodes->be_rpool,
root_ds)) != BE_SUCCESS) {
be_print_err(gettext("be_activate: failed to "
"set bootfs pool property for %s\n"),
root_ds);
goto done;
}
}
}
if (nextboot == BE_NEXTBOOT_IGNORE) {
if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) !=
NULL) {
if (be_promote_ds_callback(zhp, NULL) != 0) {
be_print_err(gettext("be_activate: "
"failed to activate the "
"datasets for %s: %s\n"),
root_ds,
libzfs_error_description(g_zfs));
ret = BE_ERR_PROMOTE;
goto done;
}
} else {
be_print_err(gettext("be_activate: failed to open "
"dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (getzoneid() == GLOBAL_ZONEID &&
be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
(ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
!= BE_SUCCESS) {
be_print_err(gettext("be_activate: failed to promote "
"the active zonepath datasets for zones in BE "
"%s\n"), cb.obe_name);
}
}
if (getzoneid() != GLOBAL_ZONEID) {
if (!be_zone_compare_uuids(root_ds)) {
be_print_err(gettext("be_activate: activating zone "
"root dataset from non-active global BE is not "
"supported\n"));
ret = BE_ERR_NOTSUP;
goto done;
}
if ((zhp = zfs_open(g_zfs, root_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_activate: failed to open "
"dataset (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
active_ds, sizeof (active_ds))) != BE_SUCCESS) {
be_print_err(gettext("be_activate: failed to find "
"active zone root dataset\n"));
ZFS_CLOSE(zhp);
goto done;
}
if (strcmp(root_ds, active_ds) == 0) {
ret = BE_SUCCESS;
ZFS_CLOSE(zhp);
goto done;
}
if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
be_print_err(gettext("be_activate: failed to set "
"active property (%s): %s\n"), root_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
goto done;
}
ZFS_CLOSE(zhp);
if ((zhp = zfs_open(g_zfs, active_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_activate: failed to open "
"dataset (%s): %s\n"), active_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
be_print_err(gettext("be_activate: failed to unset "
"active property (%s): %s\n"), active_ds,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
ZFS_CLOSE(zhp);
goto done;
}
ZFS_CLOSE(zhp);
}
done:
be_free_list(be_nodes);
return (ret);
}
int
be_activate_current_be(void)
{
int ret = BE_SUCCESS;
be_transaction_data_t bt = { 0 };
if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
return (ret);
}
ret = _be_activate(bt.obe_name, BE_NEXTBOOT_IGNORE);
if (ret != BE_SUCCESS) {
be_print_err(gettext("be_activate_current_be: failed to "
"activate %s\n"), bt.obe_name);
return (ret);
}
return (BE_SUCCESS);
}
boolean_t
be_is_active_on_boot(char *be_name)
{
be_node_list_t *be_node = NULL;
if (be_name == NULL) {
be_print_err(gettext("be_is_active_on_boot: "
"be_name must not be NULL\n"));
return (B_FALSE);
}
if (_be_list(be_name, &be_node, BE_LIST_DEFAULT) != BE_SUCCESS) {
return (B_FALSE);
}
if (be_node == NULL) {
return (B_FALSE);
}
if (be_node->be_active_on_boot) {
be_free_list(be_node);
return (B_TRUE);
} else {
be_free_list(be_node);
return (B_FALSE);
}
}
static int
set_bootfs(char *boot_rpool, char *be_root_ds)
{
zpool_handle_t *zhp;
int err = BE_SUCCESS;
if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
be_print_err(gettext("set_bootfs: failed to open pool "
"(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
return (err);
}
err = zpool_set_prop(zhp, "bootfs", be_root_ds);
if (err) {
be_print_err(gettext("set_bootfs: failed to set "
"bootfs property for pool %s: %s\n"), boot_rpool,
libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
zpool_close(zhp);
return (err);
}
zpool_close(zhp);
return (BE_SUCCESS);
}
static int
set_canmount(be_node_list_t *be_nodes, char *value)
{
char ds_path[MAXPATHLEN];
zfs_handle_t *zhp = NULL;
be_node_list_t *list = be_nodes;
int err = BE_SUCCESS;
while (list != NULL) {
be_dataset_list_t *datasets = list->be_node_datasets;
if ((err = be_make_root_ds(list->be_rpool, list->be_node_name,
ds_path, sizeof (ds_path))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE container "
"dataset for %s/%s\n"), __func__,
list->be_rpool, list->be_node_name);
return (err);
}
if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
NULL) {
be_print_err(gettext("set_canmount: failed to open "
"dataset (%s): %s\n"), ds_path,
libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
return (err);
}
if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
err = BE_SUCCESS;
} else {
err = zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
if (err) {
ZFS_CLOSE(zhp);
be_print_err(gettext("set_canmount: failed to "
"set dataset property (%s): %s\n"),
ds_path, libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
return (err);
}
}
ZFS_CLOSE(zhp);
while (datasets != NULL) {
if ((err = be_make_root_ds(list->be_rpool,
datasets->be_dataset_name, ds_path,
sizeof (ds_path))) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to get BE "
"container dataset for %s/%s\n"), __func__,
list->be_rpool, datasets->be_dataset_name);
return (err);
}
if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
== NULL) {
be_print_err(gettext("set_canmount: failed to "
"open dataset %s: %s\n"), ds_path,
libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
return (err);
}
if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
err = BE_SUCCESS;
ZFS_CLOSE(zhp);
break;
}
err = zfs_prop_set(zhp,
zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
if (err) {
ZFS_CLOSE(zhp);
be_print_err(gettext("set_canmount: "
"Failed to set property value %s "
"for dataset %s: %s\n"), value, ds_path,
libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
return (err);
}
ZFS_CLOSE(zhp);
datasets = datasets->be_next_dataset;
}
list = list->be_next_node;
}
return (err);
}
static int
be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
{
zfs_handle_t *zhp = NULL;
zfs_handle_t *pool_zhp = NULL;
int ret = BE_SUCCESS;
char cap_file[MAXPATHLEN];
char *temp_mntpnt = NULL;
char *zpool_mntpt = NULL;
char *ptmp_mntpnt = NULL;
char *orig_mntpnt = NULL;
boolean_t be_mounted = B_FALSE;
boolean_t pool_mounted = B_FALSE;
if (!be_has_grub()) {
be_print_err(gettext("be_get_grub_vers: Not supported on "
"this architecture\n"));
return (BE_ERR_NOTSUP);
}
if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
bt->obe_root_ds == NULL) {
be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
return (BE_ERR_INVAL);
}
if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
&orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
be_print_err(gettext("be_get_grub_vers: pool dataset "
"(%s) could not be mounted\n"), bt->obe_zpool);
ZFS_CLOSE(pool_zhp);
return (ret);
}
if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
be_print_err(gettext("be_get_grub_vers: pool "
"dataset (%s) is not mounted. Can't read the "
"grub capability file.\n"), bt->obe_zpool);
ret = BE_ERR_NO_MENU;
goto cleanup;
}
(void) snprintf(cap_file, sizeof (cap_file), "%s%s",
zpool_mntpt, BE_CAP_FILE);
free(zpool_mntpt);
zpool_mntpt = NULL;
if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
goto cleanup;
if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("be_get_grub_vers: failed to "
"open BE root dataset (%s): %s\n"), bt->obe_root_ds,
libzfs_error_description(g_zfs));
free(cur_vers);
ret = zfs_err_to_be_err(g_zfs);
goto cleanup;
}
if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
be_print_err(gettext("be_get_grub_vers: failed to "
"mount BE (%s)\n"), bt->obe_name);
free(*cur_vers);
*cur_vers = NULL;
ZFS_CLOSE(zhp);
goto cleanup;
}
be_mounted = B_TRUE;
}
ZFS_CLOSE(zhp);
(void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
BE_CAP_FILE);
ret = get_ver_from_capfile(cap_file, new_vers);
if (ret != BE_SUCCESS) {
free(*cur_vers);
*cur_vers = NULL;
}
if (be_mounted)
(void) _be_unmount(bt->obe_name, 0);
cleanup:
if (pool_mounted) {
int iret = BE_SUCCESS;
iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
if (ret == BE_SUCCESS)
ret = iret;
free(orig_mntpnt);
free(ptmp_mntpnt);
}
ZFS_CLOSE(pool_zhp);
free(temp_mntpnt);
return (ret);
}
static int
get_ver_from_capfile(char *file, char **vers)
{
FILE *fp = NULL;
char line[BUFSIZ];
char *last = NULL;
int err = BE_SUCCESS;
errno = 0;
if (!be_has_grub()) {
be_print_err(gettext("get_ver_from_capfile: Not supported "
"on this architecture\n"));
return (BE_ERR_NOTSUP);
}
*vers = NULL;
if (access(file, F_OK) == 0) {
if ((fp = fopen(file, "r")) == NULL) {
err = errno;
be_print_err(gettext("get_ver_from_capfile: failed to "
"open file %s with error %s\n"), file,
strerror(err));
err = errno_to_be_err(err);
return (err);
}
while (fgets(line, BUFSIZ, fp)) {
char *tok = strtok_r(line, "=", &last);
if (tok == NULL || tok[0] == '#') {
continue;
} else if (strcmp(tok, "VERSION") == 0) {
*vers = strdup(last);
break;
}
}
(void) fclose(fp);
}
return (BE_SUCCESS);
}
static boolean_t
be_do_install_mbr(char *diskname, nvlist_t *child)
{
struct uuid allowed_uuids[] = {
EFI_UNUSED,
EFI_RESV1,
EFI_BOOT,
EFI_ROOT,
EFI_SWAP,
EFI_USR,
EFI_BACKUP,
EFI_RESV2,
EFI_VAR,
EFI_HOME,
EFI_ALTSCTR,
EFI_RESERVED,
EFI_SYSTEM,
EFI_BIOS_BOOT,
EFI_SYMC_PUB,
EFI_SYMC_CDS
};
uint64_t whole;
struct dk_gpt *gpt;
struct uuid *u;
int fd, npart, i, j;
(void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_WHOLE_DISK,
&whole);
if (whole)
return (B_TRUE);
if ((fd = open(diskname, O_RDONLY|O_NDELAY)) < 0)
return (B_FALSE);
if ((npart = efi_alloc_and_read(fd, &gpt)) <= 0)
return (B_FALSE);
for (i = 0; i != npart; i++) {
int match = 0;
u = &gpt->efi_parts[i].p_guid;
for (j = 0;
j != sizeof (allowed_uuids) / sizeof (struct uuid);
j++)
if (bcmp(u, &allowed_uuids[j],
sizeof (struct uuid)) == 0)
match++;
if (match == 0)
return (B_FALSE);
}
return (B_TRUE);
}
static int
be_do_installboot_helper(zpool_handle_t *zphp, nvlist_t *child, char *stage1,
char *stage2, uint16_t flags)
{
char install_cmd[MAXPATHLEN];
char be_run_cmd_errbuf[BUFSIZ];
char be_run_cmd_outbuf[BUFSIZ];
char diskname[MAXPATHLEN];
char *vname;
char *path, *type, *dsk_ptr;
char *flag = "";
int ret;
vdev_stat_t *vs;
uint_t vsc;
if (nvlist_lookup_string(child, ZPOOL_CONFIG_TYPE, &type) != 0) {
be_print_err(gettext("%s: failed to get device type\n"),
__func__);
return (BE_ERR_NODEV);
}
if (strcmp(type, VDEV_TYPE_INDIRECT) == 0)
return (BE_ERR_NOTSUP);
if (nvlist_lookup_string(child, ZPOOL_CONFIG_PATH, &path) != 0) {
be_print_err(gettext("%s: failed to get device path\n"),
__func__);
return (BE_ERR_NODEV);
}
if ((nvlist_lookup_uint64_array(child, ZPOOL_CONFIG_VDEV_STATS,
(uint64_t **)&vs, &vsc) != 0) ||
vs->vs_state < VDEV_STATE_DEGRADED) {
be_print_err(gettext("%s: vdev %s is %s, can't install "
"boot loader\n"), __func__, path,
zpool_state_to_name(vs->vs_state, vs->vs_aux));
return (BE_SUCCESS);
}
path = strdup(path);
if (path == NULL)
return (BE_ERR_NOMEM);
dsk_ptr = strstr(path, "/dsk/");
if (dsk_ptr != NULL) {
*dsk_ptr = '\0';
dsk_ptr++;
} else {
dsk_ptr = "";
}
(void) snprintf(diskname, sizeof (diskname), "%s/r%s", path, dsk_ptr);
free(path);
vname = zpool_vdev_name(g_zfs, zphp, child, B_FALSE);
if (vname == NULL) {
be_print_err(gettext("%s: failed to get device name: %s\n"),
__func__, libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (be_is_isa("i386")) {
uint16_t force = flags & BE_INSTALLBOOT_FLAG_FORCE;
uint16_t mbr = flags & BE_INSTALLBOOT_FLAG_MBR;
if (force == BE_INSTALLBOOT_FLAG_FORCE) {
if (mbr == BE_INSTALLBOOT_FLAG_MBR ||
be_do_install_mbr(diskname, child))
flag = "-F -m -f";
else
flag = "-F";
} else {
if (mbr == BE_INSTALLBOOT_FLAG_MBR ||
be_do_install_mbr(diskname, child))
flag = "-m -f";
}
if (be_has_grub()) {
(void) snprintf(install_cmd, sizeof (install_cmd),
"%s %s %s %s %s", BE_INSTALL_GRUB, flag,
stage1, stage2, diskname);
} else {
(void) snprintf(install_cmd, sizeof (install_cmd),
"%s %s -b %s %s", BE_INSTALL_BOOT, flag,
stage1, diskname);
}
} else if (be_is_isa("sparc")) {
if ((flags & BE_INSTALLBOOT_FLAG_FORCE) ==
BE_INSTALLBOOT_FLAG_FORCE)
flag = "-f -F zfs";
else
flag = "-F zfs";
(void) snprintf(install_cmd, sizeof (install_cmd),
"%s %s %s %s", BE_INSTALL_BOOT, flag, stage2, diskname);
} else {
be_print_err(gettext("%s: unsupported architecture.\n"),
__func__);
return (BE_ERR_BOOTFILE_INST);
}
*be_run_cmd_outbuf = '\0';
*be_run_cmd_errbuf = '\0';
ret = be_run_cmd(install_cmd, be_run_cmd_errbuf, BUFSIZ,
be_run_cmd_outbuf, BUFSIZ);
if (ret != BE_SUCCESS) {
be_print_err(gettext("%s: install failed for device %s.\n"),
__func__, vname);
ret = BE_ERR_BOOTFILE_INST;
}
be_print_err(gettext(" Command: \"%s\"\n"), install_cmd);
if (be_run_cmd_outbuf[0] != 0) {
be_print_err(gettext(" Output:\n"));
be_print_err("%s", be_run_cmd_outbuf);
}
if (be_run_cmd_errbuf[0] != 0) {
be_print_err(gettext(" Errors:\n"));
be_print_err("%s", be_run_cmd_errbuf);
}
free(vname);
return (ret);
}
static int
be_do_copy_grub_cap(be_transaction_data_t *bt)
{
zfs_handle_t *zhp = NULL;
char cap_file[MAXPATHLEN];
char zpool_cap_file[MAXPATHLEN];
char line[BUFSIZ];
char *tmp_mntpnt = NULL;
char *orig_mntpnt = NULL;
char *pool_mntpnt = NULL;
FILE *cap_fp = NULL;
FILE *zpool_cap_fp = NULL;
int err = 0;
int ret = BE_SUCCESS;
boolean_t pool_mounted = B_FALSE;
boolean_t be_mounted = B_FALSE;
if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("%s: failed to "
"open BE root dataset (%s): %s\n"), __func__,
bt->obe_root_ds, libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
}
if (!zfs_is_mounted(zhp, &tmp_mntpnt)) {
if ((ret = _be_mount(bt->obe_name, &tmp_mntpnt,
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to "
"mount BE (%s)\n"), __func__, bt->obe_name);
ZFS_CLOSE(zhp);
goto done;
}
be_mounted = B_TRUE;
}
ZFS_CLOSE(zhp);
(void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpnt,
BE_CAP_FILE);
free(tmp_mntpnt);
zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM);
if (zhp == NULL) {
be_print_err(gettext("%s: zfs_open failed: %s\n"),
__func__, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if ((ret = be_mount_pool(zhp, &tmp_mntpnt,
&orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
be_print_err(gettext("%s: pool dataset "
"(%s) could not be mounted\n"), __func__, bt->obe_zpool);
ZFS_CLOSE(zhp);
goto done;
}
if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
be_print_err(gettext("%s: pool "
"dataset (%s) is not mounted. Can't check the grub "
"version from the grub capability file.\n"), __func__,
bt->obe_zpool);
ret = BE_ERR_NO_MENU;
goto done;
}
(void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
pool_mntpnt, BE_CAP_FILE);
free(pool_mntpnt);
if ((cap_fp = fopen(cap_file, "r")) == NULL) {
err = errno;
be_print_err(gettext("%s: failed to open grub "
"capability file\n"), __func__);
ret = errno_to_be_err(err);
goto done;
}
if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
err = errno;
be_print_err(gettext("%s: failed to open new "
"grub capability file\n"), __func__);
ret = errno_to_be_err(err);
(void) fclose(cap_fp);
goto done;
}
while (fgets(line, BUFSIZ, cap_fp)) {
(void) fputs(line, zpool_cap_fp);
}
(void) fclose(zpool_cap_fp);
(void) fclose(cap_fp);
done:
if (be_mounted)
(void) _be_unmount(bt->obe_name, 0);
if (pool_mounted) {
err = be_unmount_pool(zhp, tmp_mntpnt, orig_mntpnt);
if (ret == BE_SUCCESS)
ret = err;
free(orig_mntpnt);
free(tmp_mntpnt);
zfs_close(zhp);
}
return (ret);
}
static int
be_is_install_needed(be_transaction_data_t *bt, boolean_t *update)
{
int ret = BE_SUCCESS;
char *cur_vers = NULL, *new_vers = NULL;
assert(bt != NULL);
assert(update != NULL);
if (!be_has_grub()) {
*update = B_TRUE;
return (ret);
}
*update = B_FALSE;
ret = be_get_grub_vers(bt, &cur_vers, &new_vers);
if (ret != BE_SUCCESS) {
be_print_err(gettext("be_activate: failed to get grub "
"versions from capability files.\n"));
return (ret);
}
if (cur_vers != NULL) {
if (new_vers != NULL) {
if (atof(cur_vers) < atof(new_vers))
*update = B_TRUE;
free(new_vers);
}
free(cur_vers);
} else if (new_vers != NULL) {
*update = B_TRUE;
free(new_vers);
}
return (ret);
}
static int
be_do_installboot_walk(zpool_handle_t *zphp, nvlist_t *nv, char *stage1,
char *stage2, uint16_t flags)
{
boolean_t verbose = do_print;
nvlist_t **child;
uint_t children = 0;
int ret = -1;
(void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
&children);
for (int c = 0; c < children; c++) {
char *vname;
int rv;
vname = zpool_vdev_name(g_zfs, zphp, child[c], verbose);
if (vname == NULL) {
be_print_err(gettext("%s: "
"failed to get device name: %s\n"), __func__,
libzfs_error_description(g_zfs));
return (zfs_err_to_be_err(g_zfs));
} else {
be_print_err(gettext("%s: child %d of %d device %s\n"),
__func__, c, children, vname);
}
rv = be_do_installboot_walk(zphp, child[c], stage1, stage2,
flags);
switch (rv) {
case BE_ERR_NOTSUP:
be_print_err(
gettext("%s: device %s is not supported\n"),
__func__, vname);
break;
case BE_SUCCESS:
ret = rv;
break;
default:
if (ret == -1)
ret = rv;
break;
}
free(vname);
}
if (children > 0)
return (ret == -1? BE_ERR_NOTSUP : ret);
return (be_do_installboot_helper(zphp, nv, stage1, stage2, flags));
}
static int
be_do_installboot(be_transaction_data_t *bt, uint16_t flags)
{
zpool_handle_t *zphp = NULL;
zfs_handle_t *zhp = NULL;
nvlist_t *nv, *config;
char *tmp_mntpt = NULL;
char stage1[MAXPATHLEN];
char stage2[MAXPATHLEN];
int ret = BE_SUCCESS;
boolean_t be_mounted = B_FALSE;
boolean_t update = B_FALSE;
if ((flags & BE_INSTALLBOOT_FLAG_FORCE) != BE_INSTALLBOOT_FLAG_FORCE) {
ret = be_is_install_needed(bt, &update);
if (ret != BE_SUCCESS || update == B_FALSE)
return (ret);
}
if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
NULL) {
be_print_err(gettext("%s: failed to "
"open BE root dataset (%s): %s\n"), __func__,
bt->obe_root_ds, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
return (ret);
}
if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
be_print_err(gettext("%s: failed to "
"mount BE (%s)\n"), __func__, bt->obe_name);
ZFS_CLOSE(zhp);
return (ret);
}
be_mounted = B_TRUE;
}
ZFS_CLOSE(zhp);
if (be_is_isa("i386")) {
if (be_has_grub()) {
(void) snprintf(stage1, sizeof (stage1), "%s%s",
tmp_mntpt, BE_GRUB_STAGE_1);
(void) snprintf(stage2, sizeof (stage2), "%s%s",
tmp_mntpt, BE_GRUB_STAGE_2);
} else {
(void) snprintf(stage1, sizeof (stage1), "%s%s",
tmp_mntpt, BE_LOADER_STAGES);
}
} else if (be_is_isa("sparc")) {
char *platform = be_get_platform();
if (platform == NULL) {
be_print_err(gettext("%s: failed to detect system "
"platform name\n"), __func__);
if (be_mounted)
(void) _be_unmount(bt->obe_name, 0);
free(tmp_mntpt);
return (BE_ERR_BOOTFILE_INST);
}
stage1[0] = '\0';
(void) snprintf(stage2, sizeof (stage2),
"%s/usr/platform/%s%s", tmp_mntpt,
platform, BE_SPARC_BOOTBLK);
} else {
be_print_err(gettext("%s: unsupported architecture.\n"),
__func__);
return (BE_ERR_BOOTFILE_INST);
}
if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
be_print_err(gettext("%s: failed to open "
"pool (%s): %s\n"), __func__, bt->obe_zpool,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
if (be_mounted)
(void) _be_unmount(bt->obe_name, 0);
free(tmp_mntpt);
return (ret);
}
if ((config = zpool_get_config(zphp, NULL)) == NULL) {
be_print_err(gettext("%s: failed to get zpool "
"configuration information. %s\n"), __func__,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
be_print_err(gettext("%s: failed to get vdev "
"tree: %s\n"), __func__, libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
ret = be_do_installboot_walk(zphp, nv, stage1, stage2, flags);
if (be_has_grub()) {
ret = be_do_copy_grub_cap(bt);
}
done:
ZFS_CLOSE(zhp);
if (be_mounted)
(void) _be_unmount(bt->obe_name, 0);
zpool_close(zphp);
free(tmp_mntpt);
return (ret);
}
static int
be_promote_zone_ds(char *be_name, char *be_root_ds)
{
char *zone_ds = NULL;
char *temp_mntpt = NULL;
char origin[MAXPATHLEN];
char zoneroot_ds[MAXPATHLEN];
zfs_handle_t *zhp = NULL;
zfs_handle_t *z_zhp = NULL;
zoneList_t zone_list = NULL;
zoneBrandList_t *brands = NULL;
boolean_t be_mounted = B_FALSE;
int zone_index = 0;
int err = BE_SUCCESS;
if ((brands = be_get_supported_brandlist()) == NULL) {
be_print_err(gettext("be_promote_zone_ds: no supported "
"brands\n"));
return (BE_SUCCESS);
}
if ((zhp = zfs_open(g_zfs, be_root_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_promote_zone_ds: Failed to open "
"dataset (%s): %s\n"), be_root_ds,
libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
z_free_brand_list(brands);
return (err);
}
if (!zfs_is_mounted(zhp, &temp_mntpt)) {
if ((err = _be_mount(be_name, &temp_mntpt,
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
be_print_err(gettext("be_promote_zone_ds: failed to "
"mount the BE for zones procesing.\n"));
ZFS_CLOSE(zhp);
z_free_brand_list(brands);
return (err);
}
be_mounted = B_TRUE;
}
z_set_zone_root(temp_mntpt);
if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
if (be_mounted)
(void) _be_unmount(be_name, 0);
ZFS_CLOSE(zhp);
z_free_brand_list(brands);
free(temp_mntpt);
return (BE_SUCCESS);
}
for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
!= NULL; zone_index++) {
char *zone_path = NULL;
if (z_zlist_get_current_state(zone_list, zone_index) <
ZONE_STATE_INSTALLED)
continue;
if (((zone_path =
z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
!be_zone_supported(zone_ds))
continue;
if (be_find_active_zone_root(zhp, zone_ds,
zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
be_print_err(gettext("be_promote_zone_ds: "
"Zone does not have an active root "
"dataset, skipping this zone.\n"));
continue;
}
if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_promote_zone_ds: "
"Failed to open dataset "
"(%s): %s\n"), zoneroot_ds,
libzfs_error_description(g_zfs));
err = zfs_err_to_be_err(g_zfs);
goto done;
}
if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
ZFS_CLOSE(z_zhp);
continue;
}
if (be_promote_ds_callback(z_zhp, NULL) != 0) {
be_print_err(gettext("be_promote_zone_ds: "
"failed to activate the "
"datasets for %s: %s\n"),
zoneroot_ds,
libzfs_error_description(g_zfs));
err = BE_ERR_PROMOTE;
goto done;
}
}
done:
if (be_mounted)
(void) _be_unmount(be_name, 0);
ZFS_CLOSE(zhp);
free(temp_mntpt);
z_free_brand_list(brands);
z_free_zone_list(zone_list);
return (err);
}
static int
be_promote_ds_callback(zfs_handle_t *zhp, void *data)
{
char origin[MAXPATHLEN];
char *sub_dataset = NULL;
int ret = 0;
if (zhp != NULL) {
sub_dataset = strdup(zfs_get_name(zhp));
if (sub_dataset == NULL) {
ret = BE_ERR_NOMEM;
goto done;
}
} else {
be_print_err(gettext("be_promote_ds_callback: "
"Invalid zfs handle passed into function\n"));
ret = BE_ERR_INVAL;
goto done;
}
while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
if (zfs_promote(zhp) != 0) {
if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
be_print_err(gettext("be_promote_ds_callback: "
"promote of %s failed: %s\n"),
zfs_get_name(zhp),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
} else {
be_print_err(gettext("be_promote_ds_callback: "
"promote of %s failed due to snapshot "
"name collision: %s\n"), zfs_get_name(zhp),
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
}
ZFS_CLOSE(zhp);
if ((zhp = zfs_open(g_zfs, sub_dataset,
ZFS_TYPE_FILESYSTEM)) == NULL) {
be_print_err(gettext("be_promote_ds_callback: "
"Failed to open dataset (%s): %s\n"), sub_dataset,
libzfs_error_description(g_zfs));
ret = zfs_err_to_be_err(g_zfs);
goto done;
}
}
ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
done:
free(sub_dataset);
ZFS_CLOSE(zhp);
return (ret);
}