/*
* 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
*/
/*
*/
/*
* System includes
*/
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <libgen.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libzfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libbe.h>
#include <libbe_priv.h>
/* Library wide variables */
/* Private function prototypes */
static int be_destroy_zones(char *, char *, be_destroy_data_t *);
static int be_destroy_zone_roots(char *, be_destroy_data_t *);
static int be_destroy_zone_roots_callback(zfs_handle_t *, void *);
static int be_copy_zones(char *, char *, char *);
static int be_clone_fs_callback(zfs_handle_t *, void *);
static int be_destroy_callback(zfs_handle_t *, void *);
static int be_send_fs_callback(zfs_handle_t *, void *);
static int be_demote_callback(zfs_handle_t *, void *);
static int be_demote_find_clone_callback(zfs_handle_t *, void *);
static int be_demote_get_one_clone(zfs_handle_t *, void *);
static int be_get_snap(char *, char **);
char *, int);
static boolean_t be_create_container_ds(char *);
static int be_zone_root_exists_callback(zfs_handle_t *, void *);
/* ******************************************************************** */
/* Public Functions */
/* ******************************************************************** */
/*
* Function: be_init
* Description: Creates the initial datasets for a BE and leaves them
* unpopulated. The resultant BE can be mounted but can't
* yet be activated or booted.
*
* To initialize a nested BE, the optional BE_ATTR_NEW_BE_NESTED_BE
* attribute must be passed in and must have a boolean value of
* true. When initializing a nested BE, the BE_ATTR_NEW_BE_POOL
* attribute will be interpreted as the nested BE's alternate
* "pool" area.
*
* Parameters:
* be_attrs - pointer to nvlist_t of attributes being passed in.
* The following attributes are used by this function:
*
* BE_ATTR_NEW_BE_NAME *required
* BE_ATTR_NEW_BE_POOL *required
* BE_ATTR_NEW_BE_NESTED_BE *optional
* BE_ATTR_NEW_BE_ALLOW_AUTO_NAMING *optional
* BE_ATTR_NEW_BE_PARENTBE *optional
* BE_ATTR_ZFS_PROPERTIES *optional
* BE_ATTR_FS_NAMES *optional
* BE_ATTR_FS_ZFS_PROPERTIES *optional
* BE_ATTR_FS_NUM *optional
* BE_ATTR_SHARED_FS_NAMES *optional
* BE_ATTR_SHARED_FS_ZFS_PROPERTIES *optional
* BE_ATTR_SHARED_FS_NUM *optional
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
int i;
if (getzoneid() != GLOBAL_ZONEID) {
/*
* Check to see if we have write access to the root filesystem
*/
ret = be_check_rozr();
if (ret != BE_SUCCESS)
return (ret);
}
/* Initialize libzfs handle */
if (!be_zfs_init())
return (BE_ERR_INIT);
/* Get new BE name */
!= 0) {
"BE_ATTR_NEW_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/* Validate new BE name */
return (BE_ERR_INVAL);
}
/* Get zpool name */
!= 0) {
"BE_ATTR_NEW_BE_POOL attribute\n"));
return (BE_ERR_INVAL);
}
/* Get whether or not we're initializing a global or nested BE. */
"BE_ATTR_NEW_BE_NESTED_BE attribute\n"));
return (BE_ERR_INVAL);
}
/*
* Get whether or not auto-naming will be
* allowed in case of BE name conflict.
*/
BE_ATTR_NEW_BE_ALLOW_AUTO_NAMING, &allow_auto_naming)) != 0 &&
"BE_ATTR_NEW_BE_ALLOW_AUTO_NAMING attribute\n"));
return (BE_ERR_INVAL);
}
/* Get file system attributes */
nelem = 0;
"attributes\n"));
return (BE_ERR_INVAL);
}
return (BE_ERR_INVAL);
}
return (BE_ERR_INVAL);
}
/* Get shared file system attributes */
nelem = 0;
"shared fs attributes\n"));
return (BE_ERR_INVAL);
}
if (nelem != shared_fs_num) {
"array does not match SHARED_FS_NUM\n"));
return (BE_ERR_INVAL);
}
"SHARED_FS_ZFS_PROPERTIES array (%d) does not match "
return (BE_ERR_INVAL);
}
/* Generate string for BE's root dataset */
sizeof (nbe_root_ds));
if (!nested_be) {
/* Verify that nbe_zpool exists */
return (zfs_err_to_be_err(g_zfs));
}
/*
* Verify BE container dataset in nbe_zpool exists.
* If not, create it.
*/
return (BE_ERR_CREATDS);
/*
* Verify that nbe_name doesn't already exist in some pool.
*/
> 0) {
/*
* nbe_name already exists. Make an attampt at
* initializing an auto-named BE based off of the
* requested BE name.
*/
if (allow_auto_naming) {
== NULL) {
"to generate auto BE name\n"));
return (BE_ERR_AUTONAME);
}
/*
* Regenerate string for new BE's
* root dataset name.
*/
nbe_root_ds, sizeof (nbe_root_ds));
/*
* Return the auto generated name
* to the caller.
*/
!= 0) {
"to add new BE name to "
"be_attrs\n"));
return (BE_ERR_INVAL);
}
} else {
return (BE_ERR_BE_EXISTS);
}
} else if (zret < 0) {
return (zfs_err_to_be_err(g_zfs));
}
} else {
/*
* Verify that nbe_zpool (the nested BE's alternate
* "pool" area) exists.
*/
return (BE_ERR_NOENT);
}
/*
* Verify BE container dataset in nbe_zpool exists.
* If not, create it.
*/
return (BE_ERR_CREATDS);
/*
* Verify nbe_name doesn't already exist in this nested BE's
* alternate "pool" area. If it does, and we've been passed
* in the flag to allow auto-naming, then generate a new name
* based on the given BE name. We do this when initializing
* a nested BE because we're essentially creating into an
* existing "pool" area which has existing BEs, so we provide
* a way to react to this case by optionally generating an auto
* named BE.
*/
if (allow_auto_naming) {
be_container_ds, sizeof (be_container_ds));
"to generate auto BE name\n"));
return (BE_ERR_AUTONAME);
}
/*
* Regenerate string for new BE's
* root dataset name.
*/
nbe_root_ds, sizeof (nbe_root_ds));
/*
* Return the auto generated name
* to the caller.
*/
!= 0) {
"to add new BE name to "
"be_attrs\n"));
return (BE_ERR_INVAL);
}
} else {
"already exists (%s)"), nbe_root_ds);
return (BE_ERR_BE_EXISTS);
}
}
}
/*
* Create property list for new BE root dataset. If some
* zfs properties were already provided by the caller, dup
* that list. Otherwise initialize a new property list.
*/
!= 0) {
"BE_ATTR_ZFS_PROPERTIES attribute\n"));
return (BE_ERR_INVAL);
}
/* Make sure its a unique nvlist */
"not unique\n"));
return (BE_ERR_INVAL);
}
/* Dup the list */
"property list\n"));
return (BE_ERR_NOMEM);
}
} else {
/* Initialize new nvlist */
"error: out of memory\n"));
return (BE_ERR_NOMEM);
}
}
/* Set the mountpoint property for the root dataset */
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
/* Set the 'canmount' property */
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
/*
* If this is a nested BE, add the parentbe and active properties
* to the list of properties for the root dataset.
*/
if (nested_be) {
/* Get parentbe uuid provided by the caller. */
!= 0) {
"BE_ATTR_NEW_BE_PARENTBE attribute\n"));
ret = BE_ERR_INVAL;
goto done;
}
/*
* If parentbe uuid not provided, use current
* global BE's uuid.
*/
goto done;
BE_SUCCESS) {
"current BE's uuid\n"));
goto done;
}
} else {
}
BE_ZONE_PARENTBE_PROPERTY, parentbe) != 0) {
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
}
/* Create BE root dataset for the new BE */
bt.nbe_zfs_props) != 0) {
"create BE root dataset (%s): %s\n"), nbe_root_ds,
goto done;
}
if (!nested_be) {
/* For a global zone BE, set a UUID on its root dataset */
"set uuid for new BE\n"));
}
} else {
/*
* For a nested BE, make sure the nbe_handle property is set
* on its base dataset.
*/
/* Get handle to the nested BE's rpool dataset. */
== NULL) {
"open nested BE's rpool dataset (%s): %s\n"),
goto done;
}
/* Set nbe_handle property */
"set nbe_handle property on nested BEs rpool "
goto done;
}
}
/* Create the new BE's non-shared file systems */
/*
* If fs == "/", skip it;
* we already created the root dataset
*/
continue;
/*
* Generate string for file system. Support names that are
* are passed in with a preceding '/' (the old installer) and
* names without.
*/
if (fs_names[i][0] == '/') {
nbe_root_ds, fs_names[i]);
} else {
nbe_root_ds, fs_names[i]);
}
if (fs_zfs_props && fs_zfs_props[i]) {
/* Make sure its a unique nvlist */
!((fs_zfs_props[i])->nvl_nvflag &
"list not unique\n"));
ret = BE_ERR_INVAL;
goto done;
}
/* Dup the list */
"ZFS property list\n"));
ret = BE_ERR_NOMEM;
goto done;
}
} else {
/* Initialize new nvlist */
"error: out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
}
/* Set the 'canmount' property */
if (nvlist_add_string(props,
"out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
/* Create file system */
!= 0) {
"BE's child dataset (%s): %s\n"), child_fs,
goto done;
}
}
/* Create the new BE's shared file systems */
for (i = 0; i < shared_fs_num && shared_fs_names[i]; i++) {
/*
* Generate string for file system. Support names that are
* are passed in with a preceding '/' (the old installer) and
* names without.
*/
if (shared_fs_names[i][0] == '/') {
} else {
}
if (shared_fs_zfs_props && shared_fs_zfs_props[i]) {
/* Make sure its a unique nvlist */
if (!((shared_fs_zfs_props[i])->nvl_nvflag &
NV_UNIQUE_NAME) &&
!((shared_fs_zfs_props[i])->nvl_nvflag &
"list not unique\n"));
ret = BE_ERR_INVAL;
goto done;
}
/* Dup the list */
!= 0) {
"ZFS property list\n"));
ret = BE_ERR_NOMEM;
goto done;
}
} else {
/* Initialize new nvlist */
"error: out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
}
/*
* If the mountpoint property isn't specified in this shared
* filesystem's zfs properties AND this dataset has no other
* parent dataset between it and the pool dataset, we
* implicitly set its mountpoint to its own name.
*/
int ds_tok = 0;
"memory allocation failed\n"));
ret = BE_ERR_NOMEM;
goto done;
}
ds_tok++;
}
if (ds_tok == 1) {
if (shared_fs_names[i][0] == '/') {
(void) strlcpy(mountpoint,
shared_fs_names[i],
sizeof (mountpoint));
} else {
(void) snprintf(mountpoint,
sizeof (mountpoint), "/%s",
shared_fs_names[i]);
}
if (nvlist_add_string(props,
mountpoint) != 0) {
"internal error: out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
}
}
/* Create file system if it doesn't already exist */
ZFS_TYPE_DATASET)) {
"filesystem already exists, not creating (%s)\n"),
child_fs);
} else {
props) != 0) {
"create BE's shared dataset (%s): %s\n"),
goto done;
}
}
}
done:
be_zfs_fini();
return (ret);
}
/*
* Function: be_destroy
* Description: Destroy a BE and all of its children datasets, snapshots and
* zones that belong to the parent BE. Any boot menu manipulation
* must be performed by the caller.
* Parameters:
* be_attrs - pointer to nvlist_t of attributes being passed in.
* The following attributes are used by this function:
*
* BE_ATTR_ORIG_BE_NAME *required
* BE_ATTR_DESTROY_FLAGS *optional
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
int zret;
/*
* Check to see if we're operating inside a Solaris Container
* or the Global Zone.
*/
if (getzoneid() != GLOBAL_ZONEID)
if (in_ngz) {
/*
* Check to see if we have write access to the root filesystem
*/
ret = be_check_rozr();
if (ret != BE_SUCCESS)
return (ret);
}
/* Initialize libzfs handle */
if (!be_zfs_init())
return (BE_ERR_INIT);
/* Get name of BE to delete */
!= 0) {
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/*
* Validate BE name. If valid, then check that the original BE is not
* the active BE. If it is the 'active' BE then return an error code
* since we can't destroy the active BE.
*/
return (BE_ERR_INVAL);
return (ret);
}
return (BE_ERR_DESTROY_CURR_BE);
}
}
/* Get destroy flags if provided */
!= 0) {
"BE_ATTR_DESTROY_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
/* Find which zpool obe_name lives in */
return (BE_ERR_BE_NOENT);
} else if (zret < 0) {
return (zfs_err_to_be_err(g_zfs));
}
/* Generate string for obe_name's root dataset */
sizeof (obe_root_ds));
/* Get handle to BE's root dataset */
NULL) {
return (zfs_err_to_be_err(g_zfs));
}
if (!in_ngz) {
/* Get the UUID of the global BE */
BE_SUCCESS) {
}
}
/*
* If the global BE is mounted, make sure we've been given the
* flag to forcibly unmount it.
*/
if (!(dd.force_unmount)) {
"%s is currently mounted at %s, cannot destroy\n"),
return (BE_ERR_MOUNTED);
}
}
/*
* Detect if the BE to destroy is associated with the current active
* global zone.
*/
if (in_ngz) {
"not supported on active unbootable "
"BE\n"));
return (BE_ERR_ZONE_NOTSUP);
}
}
/*
* Detect if the BE to destroy has the 'active on boot' property set.
* If so, set the 'active on boot' property on the the 'active' BE.
*/
"make the current BE 'active on boot'\n"));
return (ret);
}
}
/*
* Destroy the non-global zone BE's if we are in the global zone
* and there is a UUID associated with the global zone BE
*/
!= BE_SUCCESS) {
"destroy one or more zones for BE %s\n"),
goto done;
}
}
/* Unmount the BE if it was mounted */
BE_UNMOUNT_FLAG_FORCE)) != BE_SUCCESS) {
return (ret);
}
}
/* Destroy this BE */
!= BE_SUCCESS) {
goto done;
}
done:
be_zfs_fini();
return (ret);
}
/*
* Function: be_copy
* Description: This function makes a copy of an existing BE. If the original
* BE and the new BE are in the same pool, it uses zfs cloning to
* create the new BE, otherwise it does a physical copy.
* If the original BE name isn't provided, it uses the currently
* booted BE. If the new BE name isn't provided, it creates an
* auto named BE and returns that name to the caller.
* Parameters:
* be_attrs - pointer to nvlist_t of attributes being passed in.
* The following attributes are used by this function:
*
* BE_ATTR_ORIG_BE_NAME *optional
* BE_ATTR_SNAP_NAME *optional
* BE_ATTR_NEW_BE_NAME *optional
* BE_ATTR_NEW_BE_POOL *optional
* BE_ATTR_NEW_BE_DESC *optional
* BE_ATTR_ZFS_PROPERTIES *optional
* BE_ATTR_POLICY *optional
*
* If the BE_ATTR_NEW_BE_NAME was not passed in, upon
* successful BE creation, the following attribute values
* will be returned to the caller by setting them in the
* be_attrs parameter passed in:
*
* BE_ATTR_SNAP_NAME
* BE_ATTR_NEW_BE_NAME
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
int i;
int zret;
/*
* Check to see if we're operating inside a Solaris Container
* or the Global Zone.
*/
if (getzoneid() != GLOBAL_ZONEID)
if (in_ngz) {
/*
* Check to see if we have write access to the root filesystem
*/
ret = be_check_rozr();
if (ret != BE_SUCCESS)
return (ret);
}
/* Initialize libzfs handle */
if (!be_zfs_init())
return (BE_ERR_INIT);
/* Get original BE name */
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/* If original BE name not provided, use current BE */
return (ret);
}
} else {
/* Validate original BE name */
return (BE_ERR_INVAL);
}
}
/* Find which zpool obe_name lives in */
return (BE_ERR_BE_NOENT);
} else if (zret < 0) {
"zpool_iter failed: %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/*
* Check to see if this is an attempt to create a new zone BE
* from a zone BE that is not associated with the
* currently active global zone.
*/
if (in_ngz) {
sizeof (be_root_ds));
if (!be_zone_is_bootable(be_root_ds)) {
"on unbootable BE\n"));
return (BE_ERR_ZONE_NOTSUP);
}
}
/* Get snapshot name of original BE if one was provided */
!= 0) {
"BE_ATTR_SNAP_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/* Get new BE name */
!= 0) {
"BE_ATTR_NEW_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/* Get zpool name to create new BE in */
"BE_ATTR_NEW_BE_POOL attribute\n"));
return (BE_ERR_INVAL);
} else {
"supported in a zone\n"));
return (BE_ERR_ZONE_MPOOL_NOTSUP);
}
}
/* Get new BE's description if one was provided */
"BE_ATTR_NEW_BE_DESC attribute\n"));
return (BE_ERR_INVAL);
}
/* Get BE policy to create this snapshot under */
"BE_ATTR_POLICY attribute\n"));
return (BE_ERR_INVAL);
}
/*
* Create property list for new BE root dataset. If some
* zfs properties were already provided by the caller, dup
* that list. Otherwise initialize a new property list.
*/
!= 0) {
"BE_ATTR_ZFS_PROPERTIES attribute\n"));
return (BE_ERR_INVAL);
}
/* Make sure its a unique nvlist */
"not unique\n"));
return (BE_ERR_INVAL);
}
/* Dup the list */
"failed to dup ZFS property list\n"));
return (BE_ERR_NOMEM);
}
} else {
/* Initialize new nvlist */
"error: out of memory\n"));
return (BE_ERR_NOMEM);
}
}
/*
* If new BE name provided, validate the BE name and then verify
* that new BE name doesn't already exist in some pool.
*/
/* Validate original BE name */
ret = BE_ERR_INVAL;
goto done;
}
if (!in_ngz) {
/* Verify it doesn't already exist */
goto done;
} else if (zret < 0) {
"failed: %s\n"),
goto done;
}
} else {
/*
* if we're in a solaris container we don't need
* to iter over all zpools since all containers
* live under the same dataset.
*/
sizeof (be_root_ds));
goto done;
}
}
} else {
/*
* If an auto named BE is desired, it must be in the same
* pool is the original BE.
*/
"name when creating an auto named BE\n"));
ret = BE_ERR_INVAL;
goto done;
}
/*
* Generate auto named BE
*/
== NULL) {
"failed to generate auto BE name\n"));
goto done;
}
}
/*
* If zpool name to create new BE in is not provided,
* create new BE in original BE's pool.
*/
}
/* Get root dataset names for obe_name and nbe_name */
sizeof (obe_root_ds));
sizeof (nbe_root_ds));
/*
* If an existing snapshot name has been provided to create from,
* verify that it exists for the original BE's root dataset.
*/
/* Generate dataset name for snapshot to use. */
/* Verify snapshot exists */
"snapshot does not exist (%s): %s\n"), ss,
goto done;
}
} else {
/*
* Else snapshot name was not provided, generate an
* auto named snapshot to use as its origin.
*/
"failed to create auto named snapshot\n"));
goto done;
}
bt.obe_snap_name) != 0) {
"failed to add snap name to be_attrs\n"));
ret = BE_ERR_NOMEM;
goto done;
}
}
/* Get handle to original BE's root dataset. */
== NULL) {
goto done;
}
/* If original BE is currently mounted, record its altroot. */
"get altroot of mounted BE %s: %s\n"),
goto done;
}
/* Do clone */
/*
* Iterate through original BE's datasets and clone
* them to create new BE. This call will end up closing
* the zfs handle passed in whether it succeeds for fails.
*/
/* Creating clone BE failed */
"failed to clone new BE (%s) from "
"orig BE (%s)\n"),
ret = BE_ERR_CLONE;
goto done;
}
/*
* We failed to create the new BE because a BE with
* the auto-name we generated above has since come
* into existence. Regenerate a new auto-name
* and retry.
*/
for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) {
/* Sleep 1 before retrying */
(void) sleep(1);
/* Generate new auto BE name */
== NULL) {
"failed to generate auto "
"BE name\n"));
goto done;
}
/*
* Regenerate string for new BE's
* root dataset name
*/
nbe_root_ds, sizeof (nbe_root_ds));
/*
* Get handle to original BE's root dataset.
*/
ZFS_TYPE_FILESYSTEM)) == NULL) {
"failed to open BE root dataset "
goto done;
}
/*
* Try to clone the BE again. This
* call will end up closing the zfs
* handle passed in whether it
* succeeds or fails.
*/
if (ret == 0) {
break;
} else if (ret != BE_ERR_BE_EXISTS) {
"failed to clone new BE "
"(%s) from orig BE (%s)\n"),
ret = BE_ERR_CLONE;
goto done;
}
}
/*
* If we've exhausted the maximum number of
* tries, free the auto BE name and return
* error.
*/
if (i == BE_AUTO_NAME_MAX_TRY) {
"to create unique auto BE name\n"));
goto done;
}
}
} else {
if (in_ngz) {
"zone can not be supported because multiple pools "
"are not supported.\n"));
goto done;
}
/*
* Verify BE container dataset in nbe_zpool exists.
* If not, create it.
*/
goto done;
}
/*
* Iterate through original BE's datasets and send
* them to the other pool. This call will end up closing
* the zfs handle passed in whether it succeeds or fails.
*/
ret = BE_ERR_COPY;
goto done;
}
}
/*
* Set flag to note that the dataset(s) for the new BE have been
* successfully created so that if a failure happens from this point
* on, we know to cleanup these datasets.
*/
be_created = B_TRUE;
/*
* Validate that the new BE is mountable.
* Do not attempt to mount non-global zone datasets
* since they are not cloned yet.
*/
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
"mount newly created BE\n"));
goto done;
}
if (!in_ngz) {
/* Set UUID for new BE */
"set uuid for new BE\n"));
}
} else {
&parentbe_id)) != BE_SUCCESS) {
"id for existing BE\n"));
goto done;
} else {
parentbe_id)) != BE_SUCCESS) {
"id for new BE\n"));
goto done;
}
}
}
/*
* Process zones outside of the private BE namespace.
* This has to be done here because we need the uuid set in the
* root dataset of the new BE. The uuid is use to set the parentbe
* property for the new zones datasets.
*/
"zones\n"));
goto done;
}
}
/*
* Generate a list of file systems from the original BE that are
* legacy mounted. We use this list to determine which entries in
* vfstab we need to update for the new BE we've just created.
*/
&fld)) != BE_SUCCESS) {
"get legacy mounted file system list for %s\n"),
goto done;
}
/*
* Update new BE's vfstab.
*/
goto done;
}
/* Unmount the new BE */
"unmount newly created BE\n"));
goto done;
}
/*
* If we succeeded in creating an auto named BE, set its policy
* type and return the auto generated name to the caller by storing
* it in the nvlist passed in by the caller.
*/
if (autoname) {
/* Get handle to new BE's root dataset. */
ZFS_TYPE_FILESYSTEM)) == NULL) {
goto done;
}
/*
* Set the policy type property into the new BE's root dataset
*/
/* If no policy type provided, use default type */
}
goto done;
}
/*
* Return the auto generated name to the caller
*/
"add snap name to be_attrs\n"));
}
}
}
done:
/*
* If a failure occurred and we already created the datasets for
* the new boot environment, destroy them.
*/
/*
* If we created this new BE via a snapshot, we don't
* want to destroy the snapshot on cleanup.
*/
"destroying partially created boot environment\n"));
&cdd.gz_be_uuid) == 0)
&cdd);
}
be_zfs_fini();
return (ret);
}
/* ******************************************************************** */
/* Semi-Private Functions */
/* ******************************************************************** */
/*
* Function: be_find_zpool_by_bename
* Description: Find the name of the zpool containing the BE of the given name.
* Parameters
* bename - the name of the BE used to find the zpool
* pool (return) - the name of the pool
* zerr (return) - the zfs errno
* zerrdesc (return) - the zfs error description
* Returns:
* BE_ERR_BE_NOENT - BE not found
* 0 - BE found
* else - ZFS error
* Scope:
* Semi-private (library-wide use only)
*
* XXX Probably should move to be_utils.c, along with be_find_zpool_callback().
*/
int
{
if (!g_zfs) {
if (!be_zfs_init())
return (BE_ERR_INIT);
zfs_init = 1;
}
if (zfs_init)
be_zfs_fini();
if (zret == 0)
return (BE_ERR_BE_NOENT);
else if (zret < 0)
return (zfs_err_to_be_err(g_zfs));
return (0);
}
/*
* Function: be_find_zpool_callback
* Description: Callback function used to find the pool that a BE lives in.
* Parameters:
* zlp - zpool_handle_t pointer for the current pool being
* looked at.
* data - be_transaction_data_t pointer providing information
* about the BE that's being searched for.
* This function uses the obe_name member of this
* parameter to use as the BE name to search for.
* Upon successfully locating the BE, it populates
* obe_zpool with the pool name that the BE is found in.
* Returns:
* 1 - BE exists in this pool.
* 0 - BE does not exist in this pool.
* Scope:
* Semi-private (library wide use only)
*/
int
{
const char *zpool;
if (getzoneid() != GLOBAL_ZONEID) {
/*
* This code determines what the zpool analog should be for
* zone BE datasets. Because a zone BE dataset currently
* contains the zonepath in front of (what is essentially)
* the 'zpool' we have to derive it differently than we
* do in a global zone. When dataset aliasing is introduced
* by the zones team we can revert to calling zpool_get_name
* since zone BE datasets will no longer have the zonepath
* as a part of the dataset (at least it won't be visible
* inside the zone).
* TODO: Look into removing the call to be_zone_get_zpool_analog
* zone.
*/
} else {
return (0);
}
} else {
}
/*
* Generate string for the BE's root dataset
*/
/*
* Check if dataset exists
*/
/* BE's root dataset exists in zpool */
return (1);
}
return (0);
}
/*
* Function: be_exists_callback
* Description: Callback function used to find out if a BE exists.
* Parameters:
* zlp - zpool_handle_t pointer to the current pool being
* looked at.
* data - BE name to look for.
* Return:
* 1 - BE exists in this pool.
* 0 - BE does not exist in this pool.
* Scope:
* Semi-private (library wide use only)
*/
int
{
/*
* Generate string for the BE's root dataset
*/
/*
* Check if dataset exists
*/
/* BE's root dataset exists in zpool */
return (1);
}
return (0);
}
/*
* Function: be_set_uuid
* Description: This function generates a uuid, unparses it into
* string representation, and sets that string into
* a zfs user property for a root dataset of a BE.
* The name of the user property used to store the
* uuid is org.opensolaris.libbe:uuid
*
* Parameters:
* root_ds - Root dataset of the BE to set a uuid on.
* Return:
* be_errno_t - Failure
* BE_SUCCESS - Success
* Scope:
* Semi-private (library wide ues only)
*/
int
{
/* Generate a UUID and unparse it into string form */
if (uuid_is_null(uu) != 0) {
"generate uuid\n"));
return (BE_ERR_GEN_UUID);
}
/* Get handle to the BE's root dataset. */
"open BE root dataset (%s): %s\n"), root_ds,
return (zfs_err_to_be_err(g_zfs));
}
/* Set uuid property for the BE */
"set uuid property for BE: %s\n"),
}
return (ret);
}
/*
* Function: be_get_uuid
* Description: This function gets the uuid string from a BE root
* dataset, parses it into internal format, and returns
* it the caller via a reference pointer passed in.
*
* Parameters:
* rootds - Root dataset of the BE to get the uuid from.
* uu - reference pointer to a uuid_t to return uuid in.
* Return:
* be_errno_t - Failure
* BE_SUCCESS - Success
* Scope:
* Semi-private (library wide use only)
*/
int
{
/* Get handle to the BE's root dataset. */
"open BE root dataset (%s): %s\n"), root_ds,
return (zfs_err_to_be_err(g_zfs));
}
/* Get user properties for BE's root dataset */
"get user properties for BE root dataset (%s): %s\n"),
goto done;
}
/* Get UUID string from BE's root dataset user properties */
/*
* This probably just means that the BE is simply too old
* to have a uuid or that we haven't created a uuid for
* this BE yet.
*/
"get uuid property from BE root dataset user "
"properties.\n"));
goto done;
}
/* Parse uuid string into internal format */
"parse uuid\n"));
goto done;
}
done:
return (ret);
}
/* ******************************************************************** */
/* Private Functions */
/* ******************************************************************** */
/*
* Function: _be_destroy
* Description: Destroy a BE and all of its children datasets and snapshots.
* This function is called for both global BEs and non-global BEs.
* The root dataset of either the global BE or non-global BE to be
* destroyed is passed in.
* Parameters:
* root_ds - pointer to the name of the root dataset of the
* BE to destroy.
* dd - pointer to a be_destroy_data_t structure.
* d_snap - boolean flag to delete BE origin or not.
* B_TRUE - destroy, B_FALSE - keep.
*
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
/* Get handle to BE's root dataset */
NULL) {
"open BE root dataset (%s): %s\n"), root_ds,
return (zfs_err_to_be_err(g_zfs));
}
/*
* Demote this BE in case it has dependent clones. This call
* will end up closing the zfs handle passed in whether it
* succeeds or fails.
*/
"failed to demote BE %s\n"), root_ds);
return (BE_ERR_DEMOTE);
}
/* Get handle to BE's root dataset */
NULL) {
"open BE root dataset (%s): %s\n"), root_ds,
return (zfs_err_to_be_err(g_zfs));
}
/*
* Get the origin of this BE's root dataset. This will be used
* later to destroy the snapshots originally used to create this BE.
*/
"get snapshot name from origin %s\n"), origin);
return (BE_ERR_INVAL);
}
/*
* Only fret about the origin if the origin snapshot won't
* self-destruct.
*/
NULL) {
"open origin snapshot %s\n"), origin);
return (BE_ERR_INVAL);
}
}
/*
* Destroy the BE's root and its hierarchical children. This call
* will end up closing the zfs handle passed in whether it succeeds
* or fails.
*/
"destroy BE %s\n"), root_ds);
return (BE_ERR_DESTROY);
}
/* If BE has an origin */
if (has_origin) {
/*
* If the origin was an existing snapshot, we don't want
* to destroy it since that's not what the user would
* expect.
*/
if (d_snap == 0) {
return (ret);
}
/*
* If origin snapshot doesn't have any other
* dependents, delete the origin.
*/
NULL) {
"open BE's origin (%s): %s\n"), origin,
return (ret);
}
/* If origin has dependents, don't delete it. */
return (ret);
}
/* Get handle to BE's parent's root dataset */
NULL) {
"open BE's parent root dataset (%s): %s\n"), parent,
return (ret);
}
/* Destroy the snapshot origin used to create this BE. */
/*
* The boolean set to B_FALSE and passed to zfs_destroy_snaps()
* tells zfs to process and destroy the snapshots now.
* Otherwise the call will potentially return where the
* snapshot isn't actually destroyed yet, and ZFS is waiting
* until all the references to the snapshot have been
* released before actually destroying the snapshot.
*/
"destroy original snapshots used to create "
/*
* If a failure happened because a clone exists,
* don't return a failure to the user. Above, we're
* only checking that the root dataset's origin
* snapshot doesn't have dependent clones, but its
* possible that a subordinate dataset origin snapshot
* has a clone. We really need to check for that
* before trying to destroy the origin snapshot.
*/
return (ret);
}
}
}
return (ret);
}
/*
* Function: be_destroy_zones
* Description: Find valid zone's and call be_destroy_zone_roots to destroy its
* corresponding dataset and all of its children datasets
* and snapshots.
* Parameters:
* be_name - name of global boot environment being destroyed
* be_root_ds - root dataset of global boot environment being
* destroyed.
* dd - be_destroy_data_t pointer
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*
* NOTES - Requires that the BE being deleted has no dependent BEs. If it
* does, the destroy will fail.
*/
static int
{
int i;
/* If zones are not implemented, then get out. */
if (!z_zones_are_implemented()) {
return (BE_SUCCESS);
}
/* Get list of supported brands */
"no supported brands\n"));
return (BE_SUCCESS);
}
/* Get handle to BE's root dataset */
NULL) {
"open BE root dataset (%s): %s\n"), be_root_ds,
return (zfs_err_to_be_err(g_zfs));
}
/*
* If the global BE is not mounted, we must mount it here to
* gather data about the non-global zones in it.
*/
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
"mount the BE (%s) for zones processing.\n"),
be_name);
return (ret);
}
}
/* Get list of supported zones. */
return (BE_SUCCESS);
}
/* Unmount the BE before destroying the zones in it. */
if (dd->force_unmount)
"unmount the BE (%s)\n"), be_name);
goto done;
}
/* Iterate through the zones and destroy them. */
/* Skip zones that aren't at least installed */
continue;
/*
* Get the dataset of this zonepath. If its not
* a dataset, skip it.
*/
continue;
/*
* Check if this zone is supported based on the
* dataset of its zonepath.
*/
if (!be_zone_supported(zonepath_ds)) {
continue;
}
/* Find the zone BE root datasets for this zone. */
!= BE_SUCCESS) {
"find and destroy zone roots for zone %s\n"),
zonename);
goto done;
}
}
done:
return (ret);
}
/*
* Function: be_destroy_zone_roots
* Description: This function will open the zone's root container dataset
* and iterate the datasets within, looking for roots that
* belong to the given global BE and destroying them.
* If no other zone roots remain in the zone's root container
* dataset, the function will destroy it and the zone's
* zonepath dataset as well.
* Parameters:
* zonepath_ds - pointer to zone's zonepath dataset.
* dd - pointer to a linked destroy data.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
/* Generate string for the zpool dataset for this zone. */
sizeof (zone_rpool_ds));
/* Generate string for the root container dataset for this zone. */
sizeof (zone_container_ds));
/* Get handle to this zone's root container dataset. */
== NULL) {
"open zone root container dataset (%s): %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/*
* Iterate through all of this zone's BEs, destroying the ones
* that belong to the parent global BE.
*/
dd)) != 0) {
"destroy zone roots under zonepath dataset %s: %s\n"),
return (ret);
}
/* Get handle to this zone's root container dataset. */
== NULL) {
"open zone root container dataset (%s): %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/*
* If there are no more zone roots in this zone's root container,
* dataset, destroy it and the zonepath dataset as well.
*/
== 0) {
/* Destroy the zone root container dataset */
"destroy zone root container dataset (%s): %s\n"),
goto done;
}
/* Get handle to zonepath dataset */
== NULL) {
"open zonepath dataset (%s): %s\n"),
goto done;
}
/* Destroy zonepath dataset */
"failed to destroy zonepath dataest %s: %s\n"),
goto done;
}
}
done:
return (ret);
}
/*
* Function: be_destroy_zone_roots_callback
* Description: This function is used as a callback to iterate over all of
* a zone's root datasets, finding the one's that
* correspond to the current BE. The name's
* of the zone root datasets are then destroyed by _be_destroy().
* Parameters:
* zhp - zfs_handle_t pointer to current dataset being processed
* data - be_destroy_data_t pointer
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
!= BE_SUCCESS) {
"could not get parentuuid for zone root dataset %s\n"),
zfs_get_name(zhp));
return (0);
}
/*
* Found a zone root dataset belonging to the parent
* BE being destroyed. Destroy this zone BE.
*/
!= BE_SUCCESS) {
"failed to destroy zone root %s\n"),
zfs_get_name(zhp));
return (ret);
}
}
return (ret);
}
/*
* Function: be_copy_zones
* Description: Find valid zones and clone them to create their
* corresponding datasets for the BE being created.
* Parameters:
* obe_name - name of source global BE being copied.
* obe_root_ds - root dataset of source global BE being copied.
* nbe_root_ds - root dataset of target global BE.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int i, num_retries;
int iret = 0;
/* If zones are not implemented, then get out. */
if (!z_zones_are_implemented()) {
return (BE_SUCCESS);
}
/* Get list of supported brands */
"no supported brands\n"));
return (BE_SUCCESS);
}
/* Get handle to origin BE's root dataset */
== NULL) {
"the origin BE root dataset (%s) for zones processing: "
return (zfs_err_to_be_err(g_zfs));
}
/* Get handle to newly cloned BE's root dataset */
== NULL) {
"the new BE root dataset (%s): %s\n"), nbe_root_ds,
return (zfs_err_to_be_err(g_zfs));
}
/* Get the uuid of the newly cloned parent BE. */
"failed to get uuid for BE root "
goto done;
}
/*
* If the origin BE is not mounted, we must mount it here to
* gather data about the non-global zones in it.
*/
BE_MOUNT_FLAG_NULL)) != BE_SUCCESS) {
"mount the BE (%s) for zones procesing.\n"),
obe_name);
goto done;
}
}
/* Get list of supported zones. */
ret = BE_SUCCESS;
goto done;
}
/* Get zonepath of zone */
/* Skip zones that aren't at least installed */
continue;
/*
* Get the dataset of this zonepath. If its not
* a dataset, skip it.
*/
continue;
/* Get zoneroot directory */
/* If zonepath dataset not supported, skip it. */
if (!be_zone_supported(zonepath_ds)) {
continue;
}
"failed to find active zone root for zone %s "
goto done;
}
sizeof (zone_rpool_ds));
sizeof (zone_container_ds));
ZFS_TYPE_FILESYSTEM)) == NULL) {
"failed to open zone root dataset (%s): %s\n"),
goto done;
}
zone_be_name)) == NULL) {
"to generate auto name for zone BE.\n"));
goto done;
}
"generate snapshot name for zone BE.\n"));
goto done;
}
"failed to snapshot zone BE (%s): %s\n"),
else
goto done;
}
"internal error: out of memory\n"));
ret = BE_ERR_NOMEM;
goto done;
}
/*
* The call to be_clone_fs_callback always closes the
* zfs_handle so there's no need to close z_zhp.
*/
if (iret != BE_ERR_BE_EXISTS) {
"failed to create zone BE clone for new "
"zone BE %s\n"), new_zone_be_name);
goto done;
}
/*
* We failed to create the new zone BE because a zone
* BE with the auto-name we generated above has since
* come into existence. Regenerate a new auto-name
* and retry.
*/
for (num_retries = 1;
num_retries++) {
/* Sleep 1 before retrying */
(void) sleep(1);
/* Generate new auto zone BE name */
if ((new_zone_be_name = be_auto_zone_be_name(
zone_be_name)) == NULL) {
"failed to generate auto name "
"for zone BE.\n"));
goto done;
}
(void) snprintf(new_zoneroot_ds,
sizeof (new_zoneroot_ds),
"%s/%s", zone_container_ds,
/*
* Get handle to original zone BE's root
* dataset.
*/
ZFS_TYPE_FILESYSTEM)) == NULL) {
"failed to open zone root "
"dataset (%s): %s\n"),
goto done;
}
/*
* Try to clone the zone BE again. This
* call will end up closing the zfs
* handle passed in whether it
* succeeds or fails.
*/
if (iret == 0) {
break;
} else if (iret != BE_ERR_BE_EXISTS) {
"failed to create zone BE clone "
"for new zone BE %s\n"),
goto done;
}
}
/*
* If we've exhausted the maximum number of
* tries, free the auto zone BE name and return
* error.
*/
if (num_retries == BE_AUTO_NAME_MAX_TRY) {
"to create a unique auto zone BE name\n"));
goto done;
}
}
ZFS_TYPE_FILESYSTEM)) == NULL) {
"failed to open the new zone BE root dataset "
"(%s): %s\n"), new_zoneroot_ds,
goto done;
}
uu_string) != 0) {
"failed to set parentbe property\n"));
goto done;
}
"failed to set active property\n"));
goto done;
}
}
done:
if (mounted_here)
return (ret);
}
/*
* Function: be_clone_fs_callback
* Description: Callback function used to iterate through a BE's filesystems
* to clone them for the new BE.
* Parameters:
* zhp - zfs_handle_t pointer for the filesystem being processed.
* data - be_transaction_data_t pointer providing information
* about original BE and new BE.
* Return:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/*
* Get a copy of the dataset name from the zfs handle
*/
/*
* Get the clone dataset name and prepare the zfs properties for it.
*/
sizeof (clone_ds))) != BE_SUCCESS) {
return (ret);
}
/*
* Generate the name of the snapshot to use.
*/
bt->obe_snap_name);
/*
* Get handle to snapshot.
*/
"failed to get handle to snapshot (%s): %s\n"), ss,
return (ret);
}
/*
* Clone the dataset.
*/
"failed to create clone dataset (%s): %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/*
* Iterate through zhp's children datasets (if any)
* and clone them accordingly.
*/
/*
* Error occurred while processing a child dataset.
* Destroy this dataset and return error.
*/
== NULL) {
return (ret);
}
return (ret);
}
return (0);
}
/*
* Function: be_send_fs_callback
* Description: Callback function used to iterate through a BE's filesystems
* to copy them for the new BE.
* Parameters:
* zhp - zfs_handle_t pointer for the filesystem being processed.
* data - be_transaction_data_t pointer providing information
* about original BE and new BE.
* Return:
* 0 - Success
* be_errnot_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/*
* Get a copy of the dataset name from the zfs handle
*/
/*
* Get the clone dataset name and prepare the zfs properties for it.
*/
sizeof (clone_ds))) != BE_SUCCESS) {
return (ret);
}
/*
* Create the new dataset.
*/
!= 0) {
"failed to create new dataset '%s': %s\n"),
return (ret);
}
/*
* Destination file system is already created
* hence we need to set the force flag on
*/
/*
* Initiate the pipe to be used for the send and recv
*/
"open pipe\n"));
return (errno_to_be_err(err));
}
/*
* Fork off a child to send the dataset
*/
return (errno_to_be_err(err));
} else if (pid == 0) { /* child process */
/* Send dataset */
_exit(1);
}
_exit(0);
}
/* Receive dataset */
"recv dataset (%s)\n"), clone_ds);
}
/* wait for child to exit */
do {
if (retval == -1) {
status = 0;
}
if (WEXITSTATUS(status) != 0) {
"send dataset (%s)\n"), zhp_name);
return (BE_ERR_ZFS);
}
/*
* Iterate through zhp's children datasets (if any)
* and send them accordingly.
*/
/*
* Error occurred while processing a child dataset.
* Destroy this dataset and return error.
*/
== NULL) {
return (ret);
}
return (ret);
}
return (0);
}
/*
* Function: be_destroy_callback
* Description: Callback function used to destroy a BEs children datasets
* and snapshots.
* Parameters:
* zhp - zfs_handle_t pointer to the filesystem being processed.
* data - Not used.
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/*
* Iterate down this file system's hierarchical children
* and destroy them first.
*/
return (ret);
}
if (dd->destroy_snaps) {
/*
* Iterate through this file system's snapshots and
* destroy them before destroying the file system itself.
*/
!= 0) {
return (ret);
}
}
/* Attempt to unmount the dataset before destroying it */
if (dd->force_unmount) {
return (ret);
}
}
return (ret);
}
return (0);
}
/*
* Function: be_demote_callback
* Description: This callback function is used to iterate through the file
* systems of a BE, looking for the right clone to promote such
* that this file system is left without any dependent clones.
* If the file system has no dependent clones, it doesn't need
* to get demoted, and the function will return success.
*
* The demotion will be done in two passes. The first pass
* will attempt to find the youngest snapshot that has a clone
* that is part of some other BE. The second pass will attempt
* to find the youngest snapshot that has a clone that is not
* part of a BE. Doing this helps ensure the aggregated set of
* file systems that compose a BE stay coordinated wrt BE
* snapshots and BE dependents. It also prevents a random user
* generated clone of a BE dataset to become the parent of other
* BE datasets after demoting this dataset.
*
* Parameters:
* zhp - zfs_handle_t pointer to the current file system being
* processed.
* data - not used.
* Return:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
/* LINTED */
{
int i, ret = 0;
/*
* Initialize be_demote_data for the first pass - this will find a
* clone in another BE, if one exists.
*/
for (i = 0; i < 2; i++) {
!= 0) {
"failed to iterate snapshots for %s: %s\n"),
return (ret);
}
/* Found the clone to promote. Promote it. */
"failed to promote %s: %s\n"),
return (ret);
}
}
/*
* Reinitialize be_demote_data for the second pass.
* This will find a user created clone outside of any BE
* namespace, if one exists.
*/
dd.origin_creation = 0;
}
/* Iterate down this file system's children and demote them */
return (ret);
}
return (0);
}
/*
* Function: be_demote_find_clone_callback
* Description: This callback function is used to iterate through the
* snapshots of a dataset, looking for the youngest snapshot
* that has a clone. If found, it returns a reference to the
* clone back to the caller in the callback data.
* Parameters:
* zhp - zfs_handle_t pointer to current snapshot being looked at
* data - be_demote_data_t pointer used to store the clone that
* is found.
* Returns:
* 0 - Successfully iterated through all snapshots.
* 1 - Failed to iterate through all snapshots.
* Scope:
* Private
*/
static int
{
int zret = 0;
/* If snapshot has no clones, no need to look at it */
return (0);
}
/* Get the creation time of this snapshot */
/*
* If this snapshot's creation time is greater than (or younger than)
* the current youngest snapshot found, iterate this snapshot to
* check if it has a clone that we're looking for.
*/
/*
* Iterate the dependents of this snapshot to find a
* a clone that's a direct dependent.
*/
"failed to iterate dependents of %s\n"),
zfs_get_name(zhp));
return (1);
} else if (zret == 1) {
/*
* Found a clone, update the origin_creation time
* in the callback data.
*/
}
}
return (0);
}
/*
* Function: be_demote_get_one_clone
* Description: This callback function is used to iterate through a
* snapshot's dependencies to find a filesystem that is a
* direct clone of the snapshot being iterated.
* Parameters:
* zhp - zfs_handle_t pointer to current dataset being looked at
* data - be_demote_data_t pointer used to store the clone
* that is found, and also provides flag to note
* whether or not the clone filesystem being searched
* for needs to be found in a BE dataset hierarchy.
* Return:
* 1 - Success, found clone and its also a BE's root dataset.
* 0 - Failure, clone not found.
* Scope:
* Private
*/
static int
{
return (0);
}
/*
* Make sure this is a direct clone of the snapshot
* we're iterating.
*/
/*
* If the origin property is not set, zfs_prop_get returns !0.
* This is a normal condition and as such no error message
* should be displayed.
*/
return (0);
}
return (0);
}
if (dd->find_in_BE) {
ds_path)) > 0) {
return (1);
}
return (0);
}
return (1);
}
/*
* Function: be_get_snap
* Description: This function takes a snapshot dataset name and separates
* out the parent dataset portion from the snapshot name.
* I.e. it finds the '@' in the snapshot dataset name and
* replaces it with a '\0'.
* Parameters:
* origin - char pointer to a snapshot dataset name. Its
* contents will be modified by this function.
* *snap - pointer to a char pointer. Will be set to the
* snapshot name portion upon success.
* Return:
* BE_SUCCESS - Success
* 1 - Failure
* Scope:
* Private
*/
static int
{
char *cp;
/*
* Separate out the origin's dataset and snapshot portions by
* replacing the @ with a '\0'
*/
cp[0] = '\0';
} else {
return (1);
}
} else {
return (1);
}
return (BE_SUCCESS);
}
/*
* Function: be_create_container_ds
* Description: This function checks that the zpool passed has the BE
* container dataset, and if not, then creates it.
* Parameters:
* zpool - name of pool to create BE container dataset in.
* Return:
* B_TRUE - Successfully created BE container dataset, or it
* already existed.
* B_FALSE - Failed to create container dataset.
* Scope:
* Private
*/
static boolean_t
{
/* Generate string for BE container dataset for this pool */
sizeof (be_container_ds));
"nvlist_alloc failed\n"));
return (B_FALSE);
}
if (nvlist_add_string(props,
ZFS_MOUNTPOINT_LEGACY) != 0) {
"internal error: out of memory\n"));
return (B_FALSE);
}
if (nvlist_add_string(props,
"internal error: out of memory\n"));
return (B_FALSE);
}
props) != 0) {
"failed to create container dataset (%s): %s\n"),
return (B_FALSE);
}
}
return (B_TRUE);
}
/*
* Function: be_prep_clone_send_fs
* Description: This function takes a zfs handle to a dataset from the
* original BE, and generates the name of the clone dataset
* to create for the new BE. It also prepares the zfs
* properties to be used for the new BE.
* Parameters:
* zhp - pointer to zfs_handle_t of the file system being
* bt - be_transaction_data pointer providing information
* about the original BE and new BE.
* clone_ds - buffer to store the name of the dataset
* for the new BE.
* clone_ds_len - length of clone_ds buffer
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
char *clone_ds, int clone_ds_len)
{
int err = 0;
/*
* Get a copy of the dataset name zfs_name from zhp
*/
/*
* Get file system name relative to the root.
*/
/*
* if child_fs is NULL, this means we're processing the
* root dataset itself; set child_fs to the empty string.
*/
child_fs = "";
} else {
return (BE_ERR_INVAL);
}
/*
* Generate the name of the clone file system.
*/
child_fs);
/*
* Get the mountpoint and source properties of the existing dataset.
* Be sure that the value returned is the persistent, not temporary,
* value.
*/
"failed to get mountpoint for (%s): %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/*
* Workaround for 6668667 where a mountpoint property of "/" comes
* back as "".
*/
}
/*
* Figure out what to set as the mountpoint for the new dataset.
* If the source of the mountpoint property is local or received,
* use the mountpoint value itself. Otherwise, remove it from the
* zfs properties list so that it gets inherited.
*/
/*
* Since we got the persistent mountpoint value above,
* it doesn't matter if the BE that this dataset is a
* part of is currently mounted at some altroot. We just
* have to set the mountopint for the new dataset with
* this same persistent value.
*/
mountpoint) != 0) {
"internal error: out of memory\n"));
return (BE_ERR_NOMEM);
}
} else {
"failed to remove mountpoint from "
"nvlist\n"));
return (BE_ERR_INVAL);
}
}
/*
* Set the 'canmount' property
*/
"internal error: out of memory\n"));
return (BE_ERR_NOMEM);
}
return (BE_SUCCESS);
}
/*
* Function: be_get_zone_be_name
* Description: This function takes the zones root dataset, the container
* dataset and returns the zones BE name based on the zone
* root dataset.
* Parameters:
* root_ds - the zones root dataset.
* container_ds - the container dataset for the zone.
* Returns:
* char * - the BE name of this zone based on the root dataset.
*/
static char *
{
}
/*
* Function: be_zone_root_exists_callback
* Description: This callback function is used to determine if a
* zone root container dataset has any children. It always
* returns 1, signifying a hierarchical child of the zone
* root container dataset has been traversed and therefore
* it has children.
* Parameters:
* zhp - zfs_handle_t pointer to current dataset being processed.
* data - not used.
* Returns:
* 1 - dataset exists
* Scope:
* Private
*/
static int
/* LINTED */
{
return (1);
}