be_activate.c revision a21e16923712d68dbeecfc9660b47a4279005efd
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
*/
#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 <fcntl.h>
#include <unistd.h>
#include <sys/efi_partition.h>
#include <libbe.h>
#include <libbe_priv.h>
/*
* Private function prototypes
*/
static int set_canmount(be_node_list_t *, char *);
char *, 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 *);
/* ******************************************************************** */
/* Public Functions */
/* ******************************************************************** */
/*
* Function: be_activate
* Description: Calls _be_activate which activates the BE named in the
* attributes passed in through be_attrs. The process of
* activation sets the bootfs property of the root pool, resets
* the canmount property to noauto, and sets the default in the
* grub menu to the entry corresponding to the entry for the named
* BE.
* Parameters:
* be_attrs - pointer to nvlist_t of attributes being passed in.
* The follow attribute values are used by this function:
*
* BE_ATTR_ORIG_BE_NAME *required
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
int ret = BE_SUCCESS;
/* Initialize libzfs handle */
if (!be_zfs_init())
return (BE_ERR_INIT);
/* Get the BE name to activate */
!= 0) {
"lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
be_zfs_fini();
return (BE_ERR_INVAL);
}
/* Validate BE name */
if (!be_valid_be_name(be_name)) {
be_name);
be_zfs_fini();
return (BE_ERR_INVAL);
}
be_zfs_fini();
return (ret);
}
/*
* Function: be_installboot
* pool passed in through be_attrs. The primary consumer is
* bootadm command to avoid duplication of the code.
* Parameters:
* be_attrs - pointer to nvlist_t of attributes being passed in.
* The following attribute values are used:
*
* BE_ATTR_ORIG_BE_NAME *required
* BE_ATTR_ORIG_BE_POOL *required
* BE_ATTR_ORIG_BE_ROOT *required
* BE_ATTR_INSTALL_FLAGS optional
*
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
int ret = BE_SUCCESS;
be_transaction_data_t bt = { 0 };
/* Get flags */
"BE_ATTR_INSTALL_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
/* Set verbose early, so we get all messages */
if (verbose == BE_INSTALLBOOT_FLAG_VERBOSE)
if (ret != 0) {
"lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
if (ret != 0) {
"lookup BE_ATTR_ORIG_BE_POOL attribute\n"));
return (BE_ERR_INVAL);
}
&bt.obe_root_ds);
if (ret != 0) {
"lookup BE_ATTR_ORIG_BE_ROOT attribute\n"));
return (BE_ERR_INVAL);
}
/* Initialize libzfs handle */
if (!be_zfs_init())
return (BE_ERR_INIT);
be_zfs_fini();
return (ret);
}
/* ******************************************************************** */
/* Semi Private Functions */
/* ******************************************************************** */
/*
* Function: _be_activate
* Description: This does the actual work described in be_activate.
* Parameters:
* be_name - pointer to the name of BE to activate.
*
* Return:
* BE_SUCCESS - Success
* be_errnot_t - Failure
* Scope:
* Public
*/
int
_be_activate(char *be_name)
{
be_transaction_data_t cb = { 0 };
char root_ds[MAXPATHLEN];
char active_ds[MAXPATHLEN];
int zret = 0;
/*
* TODO: The BE needs to be validated to make sure that it is actually
* a bootable BE.
*/
return (BE_ERR_INVAL);
/* Set obe_name to be_name in the cb structure */
/* find which zpool the be is in */
return (BE_ERR_BE_NOENT);
} else if (zret < 0) {
"zpool_iter failed: %s\n"),
return (ret);
}
if (getzoneid() == GLOBAL_ZONEID) {
if (ret != BE_SUCCESS)
return (ret);
"add BE (%s) to the menu\n"),
goto done;
}
}
if (be_has_grub()) {
"change the default entry in menu.lst\n"));
goto done;
}
}
}
return (ret);
}
"canmount dataset property\n"));
goto done;
}
if (getzoneid() == GLOBAL_ZONEID) {
root_ds)) != BE_SUCCESS) {
"bootfs pool property for %s\n"), root_ds);
goto done;
}
}
/*
* We don't need to close the zfs handle at this
* point because The callback funtion
* be_promote_ds_callback() will close it for us.
*/
"failed to activate the "
"datasets for %s: %s\n"),
goto done;
}
} else {
"dataset (%s): %s\n"), root_ds,
goto done;
}
if (getzoneid() == GLOBAL_ZONEID &&
!= BE_SUCCESS) {
"the active zonepath datasets for zones in BE %s\n"),
}
if (getzoneid() != GLOBAL_ZONEID) {
if (!be_zone_compare_uuids(root_ds)) {
"root dataset from non-active global BE is not "
"supported\n"));
ret = BE_ERR_NOTSUP;
goto done;
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
"dataset (%s): %s\n"), root_ds,
goto done;
}
/* Find current active zone root dataset */
"active zone root dataset\n"));
goto done;
}
/* Do nothing if requested BE is already active */
ret = BE_SUCCESS;
goto done;
}
/* Set active property for BE */
"active property (%s): %s\n"), root_ds,
goto done;
}
/* Unset active property for old active root dataset */
ZFS_TYPE_FILESYSTEM)) == NULL) {
"dataset (%s): %s\n"), active_ds,
goto done;
}
"active property (%s): %s\n"), active_ds,
goto done;
}
}
done:
return (ret);
}
/*
* Function: be_activate_current_be
* Description: Set the currently "active" BE to be "active on boot"
* Paramters:
* none
* Returns:
* BE_SUCCESS - Success
* be_errnot_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
be_activate_current_be(void)
{
int ret = BE_SUCCESS;
be_transaction_data_t bt = { 0 };
return (ret);
}
return (ret);
}
return (BE_SUCCESS);
}
/*
* Function: be_is_active_on_boot
* Description: Checks if the BE name passed in has the "active on boot"
* property set to B_TRUE.
* Paramters:
* be_name - the name of the BE to check
* Returns:
* B_TRUE - if active on boot.
* B_FALSE - if not active on boot.
* Scope:
* Semi-private (library wide use only)
*/
be_is_active_on_boot(char *be_name)
{
"be_name must not be NULL\n"));
return (B_FALSE);
}
return (B_FALSE);
}
return (B_FALSE);
}
if (be_node->be_active_on_boot) {
return (B_TRUE);
} else {
return (B_FALSE);
}
}
/* ******************************************************************** */
/* Private Functions */
/* ******************************************************************** */
/*
* Function: set_bootfs
* Description: Sets the bootfs property on the boot pool to be the
* root dataset of the activated BE.
* Parameters:
* boot_pool - The pool we're setting bootfs in.
* be_root_ds - The main dataset for the BE.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int err = BE_SUCCESS;
return (err);
}
if (err) {
"bootfs property for pool %s: %s\n"), boot_rpool,
return (err);
}
return (BE_SUCCESS);
}
/*
* Function: set_canmount
* Description: Sets the canmount property on the datasets of the
* activated BE.
* Parameters:
* be_nodes - The be_node_t returned from be_list
* value - The value of canmount we setting, on|off|noauto.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
char ds_path[MAXPATHLEN];
int err = BE_SUCCESS;
sizeof (ds_path));
NULL) {
"dataset (%s): %s\n"), ds_path,
return (err);
}
/*
* it's already mounted so we can't change the
* canmount property anyway.
*/
err = BE_SUCCESS;
} else {
if (err) {
"set dataset property (%s): %s\n"),
return (err);
}
}
sizeof (ds_path));
== NULL) {
"open dataset %s: %s\n"), ds_path,
return (err);
}
/*
* it's already mounted so we can't change the
* canmount property anyway.
*/
err = BE_SUCCESS;
break;
}
if (err) {
"Failed to set property value %s "
return (err);
}
}
}
return (err);
}
/*
* Function: be_get_grub_vers
* Description: Gets the grub version number from /boot/grub/capability. If
* capability file doesn't exist NULL is returned.
* Parameters:
* bt - The transaction data for the BE we're getting the grub
* version for.
* cur_vers - used to return the current version of grub from
* the root pool.
* new_vers - used to return the grub version of the BE we're
* activating.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failed to find version
* Scope:
* Private
*/
static int
{
int ret = BE_SUCCESS;
char cap_file[MAXPATHLEN];
char *temp_mntpnt = NULL;
char *zpool_mntpt = NULL;
char *ptmp_mntpnt = NULL;
char *orig_mntpnt = NULL;
if (!be_has_grub()) {
"this architecture\n"));
return (BE_ERR_NOTSUP);
}
return (BE_ERR_INVAL);
}
NULL) {
return (zfs_err_to_be_err(g_zfs));
}
/*
* Check to see if the pool's dataset is mounted. If it isn't we'll
* attempt to mount it.
*/
return (ret);
}
/*
* Get the mountpoint for the root pool dataset.
*/
"dataset (%s) is not mounted. Can't read the "
goto cleanup;
}
/*
* get the version of the most recent grub update.
*/
zpool_mntpt = NULL;
goto cleanup;
NULL) {
goto cleanup;
}
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
goto cleanup;
}
be_mounted = B_TRUE;
}
/*
* Now get the grub version for the BE being activated.
*/
if (ret != BE_SUCCESS) {
}
if (be_mounted)
if (pool_mounted) {
int iret = BE_SUCCESS;
if (ret == BE_SUCCESS)
}
return (ret);
}
/*
* Function: get_ver_from_capfile
* Description: Parses the capability file passed in looking for the VERSION
* line. If found the version is returned in vers, if not then
* NULL is returned in vers.
*
* Parameters:
* file - the path to the capability file we want to parse.
* vers - the version string that will be passed back.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failed to find version
* Scope:
* Private
*/
static int
{
int err = BE_SUCCESS;
errno = 0;
if (!be_has_grub()) {
"on this architecture\n"));
return (BE_ERR_NOTSUP);
}
/*
* Set version string to NULL; the only case this shouldn't be set
* to be NULL is when we've actually found a version in the capability
* file, which is set below.
*/
/*
* If the capability file doesn't exist, we're returning success
* because on older releases, the capability file did not exist
* so this is a valid scenario.
*/
"open file %s with error %s\n"), file,
return (err);
}
continue;
break;
}
}
}
return (BE_SUCCESS);
}
/*
* To be able to boot EFI labeled disks, stage1 needs to be written
* into the MBR. We do not do this if we're on disks with a traditional
* fdisk partition table only, or if any foreign EFI partitions exist.
* In the trivial case of a whole-disk vdev we always write stage1 into
* the MBR.
*/
static boolean_t
{
struct uuid allowed_uuids[] = {
};
struct uuid *u;
&whole);
if (whole)
return (B_TRUE);
return (B_FALSE);
return (B_FALSE);
for (i = 0; i != npart; i++) {
int match = 0;
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
{
char install_cmd[MAXPATHLEN];
char be_run_cmd_errbuf[BUFSIZ];
char be_run_cmd_outbuf[BUFSIZ];
char diskname[MAXPATHLEN];
char *vname;
char *flag = "";
int ret;
"failed to get device path\n"));
return (BE_ERR_NODEV);
}
/*
* Don't try to run installgrub on a vdev that is not ONLINE
* or DEGRADED. Try to print a warning for each such vdev.
*/
"vdev %s is %s, can't install boot loader\n"),
return (BE_SUCCESS);
}
/*
* Modify the vdev path to point to the raw disk.
*/
return (BE_ERR_NOMEM);
*dsk_ptr = '\0';
dsk_ptr++;
} else {
dsk_ptr = "";
}
"failed to get device name: %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
if (be_is_isa("i386")) {
if (force == BE_INSTALLBOOT_FLAG_FORCE) {
if (mbr == BE_INSTALLBOOT_FLAG_MBR ||
flag = "-F -m -f";
else
flag = "-F";
} else {
if (mbr == BE_INSTALLBOOT_FLAG_MBR ||
flag = "-m -f";
}
} else if (be_is_isa("sparc")) {
if ((flags & BE_INSTALLBOOT_FLAG_FORCE) ==
flag = "-f -F zfs";
else
flag = "-F zfs";
} else {
"architecture.\n"));
return (BE_ERR_BOOTFILE_INST);
}
*be_run_cmd_outbuf = '\0';
*be_run_cmd_errbuf = '\0';
if (ret != BE_SUCCESS) {
"failed for device %s.\n"), vname);
}
if (be_run_cmd_outbuf[0] != 0) {
}
if (be_run_cmd_errbuf[0] != 0) {
}
return (ret);
}
/*
* Function: be_do_copy_grub_cap
* Description: This function will copy grub capability file to BE.
*
* Parameters:
* bt - The transaction data for the BE we're activating.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
*
* Scope:
* Private
*/
static int
{
char cap_file[MAXPATHLEN];
char zpool_cap_file[MAXPATHLEN];
char *tmp_mntpnt = NULL;
char *orig_mntpnt = NULL;
char *pool_mntpnt = NULL;
int err = 0;
int ret = BE_SUCCESS;
/*
* first get BE dataset mountpoint, we can free all the resources
* once cap_file is built, leaving only be unmount to be done.
*/
NULL) {
return (zfs_err_to_be_err(g_zfs));
}
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
goto done;
}
be_mounted = B_TRUE;
}
/* get pool root dataset mountpoint */
goto done;
}
/*
* Check to see if the pool's dataset is mounted. If it isn't we'll
* attempt to mount it.
*/
goto done;
}
/*
* Get the mountpoint for the root pool dataset.
* NOTE: zhp must be kept for _be_unmount_pool()
*/
"dataset (%s) is not mounted. Can't check the grub "
goto done;
}
"capability file\n"));
goto done;
}
"grub capability file\n"));
goto done;
}
}
(void) fclose(zpool_cap_fp);
done:
if (be_mounted)
if (pool_mounted) {
if (ret == BE_SUCCESS)
}
return (ret);
}
/*
* Function: be_is_install_needed
* Description: Check detached version files to detect if bootloader
*
* Parameters:
* bt - The transaction data for the BE we're activating.
* update - set B_TRUE is update is needed.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
*
* Scope:
* Private
*/
static int
{
int ret = BE_SUCCESS;
if (!be_has_grub()) {
/*
* no detached versioning, let installboot to manage
* versioning.
*/
return (ret);
}
/*
* We need to check to see if the version number from
* the BE being activated is greater than the current
* one.
*/
if (ret != BE_SUCCESS) {
"versions from capability files.\n"));
return (ret);
}
/* update if we have both versions and can compare */
}
/* we only got new version - update */
}
return (ret);
}
/*
* Function: be_do_installboot
* Description: This function runs installgrub/installboot using the boot
* loader files from the BE we're activating and installing
* them on the pool the BE lives in.
*
* Parameters:
* bt - The transaction data for the BE we're activating.
* flags - flags for bootloader install
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
*
* Scope:
* Private
*/
static int
{
char stage1[MAXPATHLEN];
char stage2[MAXPATHLEN];
char *vname;
int ret = BE_SUCCESS;
/*
* check versions. This call is to support detached
* version implementation like grub. Embedded versioning is
* checked by actual installer.
*/
return (ret);
}
NULL) {
return (ret);
}
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
return (ret);
}
be_mounted = B_TRUE;
}
if (be_has_grub()) {
} else {
char *platform = be_get_platform();
"detect system platform name\n"));
if (be_mounted)
return (BE_ERR_BOOTFILE_INST);
}
}
if (be_mounted)
return (ret);
}
"configuration information. %s\n"),
goto done;
}
/*
* Get the vdev tree
*/
goto done;
}
&children) != 0) {
goto done;
}
for (c = 0; c < children; c++) {
"be_do_installboot: "
"failed to get device name: %s\n"),
goto done;
}
if (nvlist_lookup_nvlist_array(child[c],
"failed to traverse the vdev tree: %s\n"),
goto done;
}
for (i = 0; i < nchildren; i++) {
if (ret != BE_SUCCESS)
goto done;
}
} else {
if (ret != BE_SUCCESS)
goto done;
}
}
if (be_has_grub()) {
}
done:
if (be_mounted)
return (ret);
}
/*
* Function: be_promote_zone_ds
* Description: This function finds the zones for the BE being activated
* and the active zonepath dataset for each zone. Then each
* active zonepath dataset is promoted.
*
* Parameters:
* be_name - the name of the global zone BE that we need to
* find the zones for.
* be_root_ds - the root dataset for be_name.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
*
* Scope:
* Private
*/
static int
{
char *temp_mntpt = NULL;
char origin[MAXPATHLEN];
char zoneroot_ds[MAXPATHLEN];
int zone_index = 0;
int err = BE_SUCCESS;
/*
* Get the supported zone brands so we can pass that
* to z_get_nonglobal_zone_list_by_brand. Currently
* only the ipkg and labeled brand zones are supported
*
*/
"brands\n"));
return (BE_SUCCESS);
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
"dataset (%s): %s\n"), be_root_ds,
return (err);
}
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
"mount the BE for zones procesing.\n"));
return (err);
}
be_mounted = B_TRUE;
}
/*
* Set the zone root to the temp mount point for the BE we just mounted.
*/
/*
* Get all the zones based on the brands we're looking for. If no zones
* are found that we're interested in unmount the BE and move on.
*/
if (be_mounted)
(void) _be_unmount(be_name, 0);
return (BE_SUCCESS);
}
!= NULL; zone_index++) {
/* Skip zones that aren't at least installed */
continue;
if (((zone_path =
continue;
zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
"Zone does not have an active root "
"dataset, skipping this zone.\n"));
continue;
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
"Failed to open dataset "
"(%s): %s\n"), zoneroot_ds,
goto done;
}
continue;
}
/*
* We don't need to close the zfs handle at this
* point because the callback funtion
* be_promote_ds_callback() will close it for us.
*/
"failed to activate the "
"datasets for %s: %s\n"),
goto done;
}
}
done:
if (be_mounted)
(void) _be_unmount(be_name, 0);
return (err);
}
/*
* Function: be_promote_ds_callback
* Description: This function is used to promote the datasets for the BE
* being activated as well as the datasets for the zones BE
* being activated.
*
* Parameters:
* zhp - the zfs handle for zone BE being activated.
* data - not used.
* Return:
* 0 - Success
* be_errno_t - Failure
*
* Scope:
* Private
*/
static int
/* LINTED */
{
char origin[MAXPATHLEN];
char *sub_dataset = NULL;
int ret = 0;
if (sub_dataset == NULL) {
ret = BE_ERR_NOMEM;
goto done;
}
} else {
"Invalid zfs handle passed into function\n"));
ret = BE_ERR_INVAL;
goto done;
}
/*
* This loop makes sure that we promote the dataset to the
* top of the tree so that it is no longer a decendent of any
* dataset. The ZFS close and then open is used to make sure that
* the promotion is updated before we move on.
*/
if (zfs_promote(zhp) != 0) {
"promote of %s failed: %s\n"),
goto done;
} else {
/*
* If the call to zfs_promote returns the
* error EZFS_EXISTS we've hit a snapshot name
* collision. This means we're probably
* attemping to promote a zone dataset above a
* parent dataset that belongs to another zone
* which this zone was cloned from.
*
* TODO: If this is a zone dataset at some
* point we should skip this if the zone
* paths for the dataset and the snapshot
* don't match.
*/
"promote of %s failed due to snapshot "
goto done;
}
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
"Failed to open dataset (%s): %s\n"), sub_dataset,
goto done;
}
}
/* Iterate down this dataset's children and promote them */
done:
return (ret);
}