/*
* 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 <errno.h>
#include <libintl.h>
#include <libgen.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>
typedef struct dir_data {
char *dir;
char *ds;
} dir_data_t;
/* Private function prototypes */
static int be_mount_callback(zfs_handle_t *, void *);
static int be_unmount_callback(zfs_handle_t *, void *);
static int be_get_legacy_fs_callback(zfs_handle_t *, void *);
static int fix_mountpoint(zfs_handle_t *);
static int fix_mountpoint_callback(zfs_handle_t *, void *);
static int get_mountpoint_from_vfstab(char *, const char *, char *, size_t,
static int iter_shared_fs_callback(zfs_handle_t *, void *);
static int zpool_shared_fs_callback(zpool_handle_t *, void *);
static int unmount_shared_fs(be_unmount_data_t *);
static int add_to_fs_list(be_fs_list_data_t *, const char *);
static int be_mount_root(zfs_handle_t *, char *);
static int be_unmount_zones(be_unmount_data_t *);
char *);
static int be_unmount_one_zone(be_unmount_data_t *, char *, char *, char *);
static int be_get_ds_from_dir_callback(zfs_handle_t *, void *);
/* ******************************************************************** */
/* Public Functions */
/* ******************************************************************** */
/*
* Function: be_mount
* Description: Mounts a BE and its subordinate datasets at a given mountpoint.
* 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_MOUNTPOINT *required
* BE_ATTR_ALT_POOL *optional
* BE_ATTR_MOUNT_FLAGS *optional
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
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 original BE name */
!= 0) {
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/* Validate original BE name */
if (!be_valid_be_name(be_name)) {
be_name);
return (BE_ERR_INVAL);
}
/* Get mountpoint */
!= 0) {
"BE_ATTR_MOUNTPOINT attribute\n"));
return (BE_ERR_INVAL);
}
/* Get alternate "pool" */
"BE_ATTR_ALT_POOL attribute\n"));
return (BE_ERR_INVAL);
}
/* Get flags */
"BE_ATTR_MOUNT_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
be_zfs_fini();
return (ret);
}
/*
* Function: be_unmount
* Description: Unmounts a BE and its subordinate datasets.
* 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_ALT_POOL *optional
* BE_ATTR_UNMOUNT_FLAGS *optional
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Public
*/
int
{
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 original BE name */
!= 0) {
"BE_ATTR_ORIG_BE_NAME attribute\n"));
return (BE_ERR_INVAL);
}
/* Validate original BE name */
if (!be_valid_be_name(be_name)) {
be_name);
return (BE_ERR_INVAL);
}
/* Get alternate "pool" area */
"BE_ATTR_ALT_POOL attribute\n"));
return (BE_ERR_INVAL);
}
/* Get unmount flags */
"BE_ATTR_UNMOUNT_FLAGS attribute\n"));
return (BE_ERR_INVAL);
}
be_zfs_fini();
return (ret);
}
/* ******************************************************************** */
/* Semi-Private Functions */
/* ******************************************************************** */
/*
* Function: _be_mount
* Description: Mounts a BE. If the altroot is not provided, this function
* will generate a temporary mountpoint to mount the BE at. It
* will return this temporary mountpoint to the caller via the
* altroot reference pointer passed in. This returned value is
* allocated on heap storage and is the repsonsibility of the
* caller to free.
* Parameters:
* be_name - pointer to name of BE to mount.
* altpool - pointer to alternate "pool" area to find the BE.
* altroot - reference pointer to altroot of where to mount BE.
* flags - flag indicating special handling for mounting the BE
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
{
/*
* Check to see if we're operating inside a Solaris Container
* or the Global Zone.
*/
if (getzoneid() != GLOBAL_ZONEID)
return (BE_ERR_INVAL);
/* Set be_name as obe_name in bt structure */
/* Find which zpool obe_name lives in if alternate "pool" not provide */
== 0) {
return (BE_ERR_BE_NOENT);
} else if (err < 0) {
return (zfs_err_to_be_err(g_zfs));
}
} else {
}
/* 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));
}
/* Make sure BE's root dataset isn't already mounted somewhere */
return (BE_ERR_MOUNTED);
}
/*
* Fix this BE's mountpoint if its root dataset isn't set to
* either 'legacy' or '/'.
*/
return (ret);
}
/*
* If altroot not provided, create a temporary alternate root
* to mount on
*/
!= BE_SUCCESS) {
"make temporary mountpoint\n"));
return (ret);
}
} else {
tmp_altroot = *altroot;
}
/* Mount the BE's root file system */
"mount BE root file system\n"));
if (gen_tmp_altroot)
return (ret);
}
/* Iterate through BE's children filesystems */
tmp_altroot)) != 0) {
if (gen_tmp_altroot)
return (err);
}
/*
* Mount shared file systems if mount flag says so.
*/
/*
* Mount all ZFS file systems not under the BE's
* root dataset.
*/
}
/*
* If we're in the global zone and the global zone has
* a valid uuid, mount all supported non-global zones.
*/
!= BE_SUCCESS) {
if (gen_tmp_altroot)
return (ret);
}
}
/*
* If a NULL altroot was passed in, pass the generated altroot
* back to the caller in altroot.
*/
if (gen_tmp_altroot)
*altroot = tmp_altroot;
return (BE_SUCCESS);
}
/*
* Function: _be_unmount
* Description: Unmount a BE.
* Parameters:
* be_name - pointer to name of BE to unmount.
* altpool - pointer to alternate "pool" area to find the BE.
* flags - flags for unmounting the BE.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
{
int zret = 0;
/*
* Check to see if we're operating inside a Solaris Container
* or the Global Zone.
*/
if (getzoneid() != GLOBAL_ZONEID)
return (BE_ERR_INVAL);
/* Set be_name as obe_name in bt structure */
/* Find which zpool obe_name lives in if alternate "pool" not provide */
== 0) {
return (BE_ERR_BE_NOENT);
} else if (zret < 0) {
"zpool_iter failed: %s\n"),
return (ret);
}
} else {
}
/* Generate string for obe_name's root dataset */
sizeof (obe_root_ds));
/* Get handle to BE's root dataset */
NULL) {
return (ret);
}
/* Make sure BE's root dataset is mounted somewhere */
/*
* BE is not mounted, fix this BE's mountpoint if its root
* dataset isn't set to either 'legacy' or '/'.
*/
return (ret);
}
return (BE_SUCCESS);
}
/*
* If we didn't get a mountpoint from the zfs_is_mounted call,
* try and get it from its property.
*/
return (BE_ERR_ZFS);
}
} else {
}
/* If BE mounted as current root, fail */
"cannot unmount currently running BE\n"));
return (BE_ERR_UMOUNT_CURR_BE);
}
/* Unmount all supported non-global zones if we're in the global zone */
return (ret);
}
}
/* TODO: Unmount all non-ZFS file systems - Not supported yet */
/* Unmount all ZFS file systems not under the BE root dataset */
"unmount shared file systems\n"));
return (ret);
}
/* Unmount all children datasets under the BE's root dataset */
&ud)) != 0) {
return (zret);
}
/* Unmount this BE's root filesystem */
return (ret);
}
return (BE_SUCCESS);
}
/*
* Function: be_mount_zone_root
* Description: Mounts the zone root dataset for a zone.
* Parameters:
* zfs - zfs_handle_t pointer to zone root dataset
* md - be_mount_data_t pointer to data for zone to be mounted
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
{
/* Get mountpoint property of dataset */
return (zfs_err_to_be_err(g_zfs));
}
/*
* Make sure zone's root dataset is set to /. The ZFS temporary
* mountpoint feature is used to mount at the zone root in the
* global zone.
*/
"zone root dataset mountpoint is not '/'\n"));
return (BE_ERR_ZONE_ROOT_NOT_SLASH);
}
/*
* Check to see if the zone root is already mounted, perhaps
* within the zone itself.
*/
"mount zone root dataset %s \nbecause it is "
"already mounted at %s. This is likely due to "
"the zone root\nbeing mounted within the zone "
return (BE_ERR_MOUNT);
}
/*
* Temporary mount the zone root dataset.
*/
"temporary mount zone root dataset (%s) at %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
return (BE_SUCCESS);
}
/*
* Function: be_unmount_zone_root
* Description: Unmounts the zone root dataset for a zone.
* Parameters:
* zhp - zfs_handle_t pointer to zone root dataset
* ud - be_unmount_data_t pointer to data for zone to be unmounted
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wise use only)
*/
int
{
/* Unmount the dataset */
return (zfs_err_to_be_err(g_zfs));
}
/* Get the current mountpoint property for the zone root dataset */
"get mountpoint property for zone root dataset (%s): %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/* If mountpoint not already set to '/', set it to '/' */
"/") != 0) {
"failed to set mountpoint of zone root dataset "
return (zfs_err_to_be_err(g_zfs));
}
}
return (BE_SUCCESS);
}
/*
* Function: be_get_legacy_fs
* Description: This function iterates through all non-shared file systems
* of a BE and finds the ones with a legacy mountpoint. For
* those file systems, it reads the BE's vfstab to get the
* mountpoint. If found, it adds that file system to the
* be_fs_list_data_t passed in.
*
* This function can be used to gather legacy mounted file systems
* for both global BEs and non-global zone BEs. To get data for
* a non-global zone BE, the zoneroot_ds and zoneroot parameters
* will be specified, otherwise they should be set to NULL.
* Parameters:
* be_name - global BE name from which to get legacy file
* system list.
* be_root_ds - root dataset of global BE.
* zoneroot_ds - root dataset of zone.
* zoneroot - zoneroot path of zone.
* fld - be_fs_list_data_t pointer.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
{
return (BE_ERR_INVAL);
/* Get handle to BE's root dataset */
== NULL) {
"open BE root dataset (%s): %s\n"), be_root_ds,
return (ret);
}
/* If BE is not already mounted, mount it. */
BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
"failed to mount BE %s\n"), be_name);
goto cleanup;
}
if (zoneroot_ds == NULL)
"get altroot of mounted BE %s: %s\n"),
goto cleanup;
}
/*
* If a zone root dataset was passed in, we're wanting to get
* legacy mounted file systems for that zone, not the global
* BE.
*/
if (zoneroot_ds != NULL) {
/* Close off handle to global BE's root dataset */
/* Get handle to zone's root dataset */
ZFS_TYPE_FILESYSTEM)) == NULL) {
"open zone BE root dataset (%s): %s\n"),
goto cleanup;
}
/* Make sure the zone we're looking for is mounted */
/* Generate alternate root path for zone */
"memory allocation failed\n"));
ret = BE_ERR_NOMEM;
goto cleanup;
}
!= BE_SUCCESS) {
"failed to mount zone root %s\n"),
goto cleanup;
}
}
}
/*
* If the root dataset is in the vfstab with a mountpoint of "/",
* add it to the list
*/
!= BE_SUCCESS) {
"failed to add %s to fs list\n"),
zfs_get_name(zhp));
ret = BE_ERR_INVAL;
goto cleanup;
}
}
}
/* Iterate subordinate file systems looking for legacy mounts */
fld)) != 0) {
"failed to iterate %s to get legacy mounts\n"),
zfs_get_name(zhp));
}
/* If we mounted the zone BE, unmount it */
if (zone_mounted_here) {
"failed to unmount zone root %s\n"),
if (ret == BE_SUCCESS)
}
}
/* If we mounted this BE, unmount it */
if (mounted_here) {
!= BE_SUCCESS) {
"failed to unmount %s\n"), be_name);
if (ret == BE_SUCCESS)
}
}
return (ret);
}
/*
* Function: be_free_fs_list
* Description: Function used to free the members of a be_fs_list_data_t
* structure.
* Parameters:
* fld - be_fs_list_data_t pointer to free.
* Returns:
* None
* Scope:
* Semi-private (library wide use only)
*/
void
{
int i;
return;
return;
}
/*
* Function: be_get_ds_from_dir(char *dir)
* Description: Given a directory path, find the underlying dataset mounted
* at that directory path if there is one. The returned name
* is allocated in heap storage, so the caller is responsible
* for freeing it.
* Parameters:
* dir - char pointer of directory to find.
* Returns:
* NULL - if directory is not mounted from a dataset.
* name of dataset mounted at dir.
* Scope:
* Semi-private (library wide use only)
*/
char *
{
/* Make sure length of dir is within the max length */
return (NULL);
/* Resolve dir in case its lofs mounted */
}
/*
* Function: be_make_tmp_mountpoint
* Description: This function generates a random temporary mountpoint
* and creates that mountpoint directory. It returns the
* mountpoint in heap storage, so the caller is responsible
* for freeing it.
* Parameters:
* tmp_mp - reference to pointer of where to store generated
* temporary mountpoint.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
{
int err = 0;
"malloc failed\n"));
return (BE_ERR_NOMEM);
}
return (errno_to_be_err(err));
}
return (BE_SUCCESS);
}
/*
* Function: be_tmp_mount_ds
* Description: This function will temporarily mount a dataset on a randomly
* generated tmp mountpoint.
* Parameters:
* zhp - handle to the dataset to mount.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Semi-private (library wide use only)
*/
int
{
int ret = 0;
/* Dataset already mounted, return success */
return (BE_SUCCESS);
}
/*
* Attempt to mount on a temp mountpoint
*/
"to make temporary mountpoint\n"));
return (ret);
}
/* Temporarily mount this filesystem */
return (BE_ERR_MOUNT);
}
return (BE_SUCCESS);
}
/* ******************************************************************** */
/* Private Functions */
/* ******************************************************************** */
/*
* Function: be_mount_callback
* Description: Callback function used to iterate through all of a BE's
* subordinate file systems and to mount them accordingly.
* Parameters:
* zhp - zfs_handle_t pointer to current file system being
* processed.
* data - pointer to the altroot of where to mount BE.
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
sizeof (MNTOPT_ZFS_MOUNTPOINT) + 1];
int ret = 0;
int err = 0;
/* Get dataset's persistent mountpoint and source values */
"get mountpoint and sourcetype for %s\n"),
fs_name);
return (BE_ERR_ZFS);
}
/*
* Set this filesystem's 'canmount' property to 'noauto' just incase
* it's been set 'on'. We do this so that when we change its
* mountpoint zfs won't immediately try to mount it.
*/
"set canmount to 'noauto' (%s)\n"), fs_name);
return (BE_ERR_ZFS);
}
/*
* If the mountpoint is none, there's nothing to do, goto next.
* If the mountpoint is legacy, legacy mount it with mount(2).
* If the mountpoint is inherited, its mountpoint should
* already be set. If it's not, then explicitly fix-up
* the mountpoint now by appending its explicitly set
* mountpoint value to the BE mountpoint.
*/
goto next;
/*
* If the mountpoint is set to 'legacy', we need to
* dig into this BE's vfstab to figure out where to
* mount it, and just mount it via mount(2) relative
* to altroot.
*/
/* Legacy mount the file system */
gettext("be_mount_callback: "
"failed to mount %s on %s\n"),
}
} else {
gettext("be_mount_callback: "
"no entry for %s in vfstab, "
"skipping ...\n"), fs_name);
}
goto next;
} else if (sourcetype & ZPROP_SRC_INHERITED ||
/*
* Else process dataset mountpoint property relative to altroot.
*/
} else {
"mountpoint sourcetype of %s is %d, skipping ...\n"),
goto next;
}
/*
* Check to see if the mountpoint we need to mount at
* exists and if not, create it.
*/
errno = 0;
if (mkdirp(mountpoint,
"create mount point %s\n"), mountpoint);
return (errno_to_be_err(err));
}
/* Temporarily mount this filesystem */
return (BE_ERR_MOUNT);
}
next:
/* Iterate through this dataset's children and mount them */
altroot)) != 0) {
return (ret);
}
return (0);
}
/*
* Function: be_unmount_callback
* Description: Callback function used to iterate through all of a BE's
* subordinate file systems and to unmount them.
* Parameters:
* zhp - zfs_handle_t pointer to current file system being
* processed.
* data - pointer to the mountpoint of where BE is mounted.
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/* Iterate down this dataset's children first */
ret = BE_ERR_UMOUNT;
goto done;
}
/* Is dataset even mounted ? */
goto done;
/* Unmount this file system */
"failed to unmount %s: %s\n"), fs_name,
}
done:
/* Set this filesystem's 'canmount' property to 'noauto' */
"failed to set canmount to 'noauto' (%s)\n"), fs_name);
if (ret == 0)
ret = BE_ERR_ZFS;
}
return (ret);
}
/*
* Function: be_get_legacy_fs_callback
* Description: The callback function is used to iterate through all
* non-shared file systems of a BE, finding ones that have
* a legacy mountpoint and an entry in the BE's vfstab.
* It adds these file systems to the callback data.
* Parameters:
* zhp - zfs_handle_t pointer to current file system being
* processed.
* data - be_fs_list_data_t pointer
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/* Get this dataset's persistent mountpoint property */
"failed to get mountpoint for %s: %s\n"),
return (ret);
}
/*
* If mountpoint is legacy, try to get its mountpoint from this BE's
* vfstab. If it exists in the vfstab, add this file system to the
* callback data.
*/
"no entry for %s in vfstab, "
"skipping ...\n"), fs_name);
goto next;
}
/* Record file system into the callback data. */
"failed to add %s to fs list\n"), mountpoint);
return (BE_ERR_NOMEM);
}
}
next:
/* Iterate through this dataset's children file systems */
fld)) != 0) {
return (ret);
}
return (0);
}
/*
* Function: add_to_fs_list
* Description: Function used to add a file system to the fs_list array in
* a be_fs_list_data_t structure.
* Parameters:
* fld - be_fs_list_data_t pointer
* fs - file system to add
* Returns:
* BE_SUCCESS - Success
* 1 - Failure
* Scope:
* Private
*/
static int
{
return (1);
"memory allocation failed\n"));
return (1);
}
"memory allocation failed\n"));
return (1);
}
return (BE_SUCCESS);
}
/*
* Function: zpool_shared_fs_callback
* Description: Callback function used to iterate through all existing pools
* to find and mount all shared filesystems. This function
* processes the pool's "pool data" dataset, then uses
* iter_shared_fs_callback to iterate through the pool's
* datasets.
* Parameters:
* zlp - zpool_handle_t pointer to the current pool being
* looked at.
* data - be_mount_data_t pointer
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/*
* Get handle to pool's "pool data" dataset
*/
"failed to open pool dataset %s: %s\n"), zpool,
return (ret);
}
/* Process this pool's "pool data" dataset */
/* Interate through this pool's children */
return (0);
}
/*
* Function: iter_shared_fs_callback
* Description: Callback function used to iterate through a pool's datasets
* to find and mount all shared filesystems. It makes sure to
* find the BE container dataset of the pool, if it exists, and
* does not process and iterate down that path.
*
* Note - This function iterates linearly down the
* hierarchical dataset paths and mounts things as it goes
* along. It does not make sure that something deeper down
* a dataset path has an interim mountpoint for something
* processed earlier.
*
* Parameters:
* zhp - zfs_handle_t pointer to the current dataset being
* processed.
* data - be_mount_data_t pointer
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
char *pool;
/* Get the pool's name */
if (pool) {
/* Get the name of this pool's container dataset */
sizeof (container_ds));
/*
* If what we're processing is this pool's BE container
* dataset, skip it.
*/
return (0);
}
} else {
/* Getting the pool name failed, return error */
"failed to get pool name from %s\n"), name);
return (BE_ERR_POOL_NOENT);
}
/* Mount this shared filesystem */
/* Iterate this dataset's children file systems */
return (0);
}
/*
* Function: loopback_mount_shared_fs
* Description: This function loopback mounts a file system into the altroot
* area of the BE being mounted. Since these are shared file
* systems, they are expected to be already mounted for the
* current BE, and this function just loopback mounts them into
* the BE mountpoint. If they are not mounted for the current
* live system, they are skipped and not mounted into the BE
* we're mounting.
* Parameters:
* zhp - zfs_handle_t pointer to the dataset to loopback mount
* md - be_mount_data_t pointer
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int err;
/*
* Check if file system is currently mounted and not delegated
* to a non-global zone (if we're in the global zone)
*/
/*
* If we didn't get a mountpoint from the zfs_is_mounted call,
* get it from the mountpoint property.
*/
gettext("loopback_mount_shared_fs: "
"failed to get mountpoint property\n"));
return (BE_ERR_ZFS);
}
} else {
sizeof (zhp_mountpoint));
}
/* Mount it read-only if read-write was not requested */
}
/* Add the "nosub" option to the mount options string */
/* Loopback mount this dataset at the altroot */
"failed to loopback mount %s at %s: %s\n"),
return (BE_ERR_MOUNT);
}
}
return (BE_SUCCESS);
}
/*
* Function: loopback_mount_zonepath
* Description: This function loopback mounts a zonepath into the altroot
* area of the BE being mounted. Since these are shared file
* systems, they are expected to be already mounted for the
* current BE, and this function just loopback mounts them into
* the BE mountpoint.
* Parameters:
* zonepath - pointer to zone path in the current BE
* md - be_mount_data_t pointer
* use_tmpfs - mount using tmpfs instead of lofs
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
const char *zonepath,
{
char *p;
char *p1;
char *parent_dir;
char *parentmnt;
int ret;
int err;
return (errno_to_be_err(err));
}
/*
* before attempting the loopback mount of zonepath under altroot,
* we need to make sure that all intermediate file systems in the
* zone path are also mounted under altroot
*/
/* get the parent directory for zonepath */
if ((parent_dir = (char *)calloc(sizeof (char),
ret = BE_ERR_NOMEM;
goto done;
}
"failed to stat %s"),
goto done;
}
/*
* After the above stat call, st.st_dev contains ID of the
* device over which parent dir resides.
* Now, search mnttab and find mount point of parent dir device.
*/
MNTTYPE_ZFS) == 0) {
BE_CONTAINER_DS_NAME, 4) != 0 ||
/*
* if parent dir is in a shared file
* system, check whether it is already
* loopback mounted under altroot or
* not. It would have been mounted
* already under altroot if it is in
* a non-shared filesystem.
*/
(void) snprintf(alt_parentmnt,
sizeof (alt_parentmnt), "%s%s",
if (ret != BE_SUCCESS) {
goto done;
}
}
}
break;
}
}
}
}
/* Add the "nosub" option to the mount options string */
if (use_tmpfs) {
"failed to tmpfs mount zone path %s\n"),
goto done;
}
/*
* We need to fixup the permissions for the zonepath
* we just mounted in the altroot so that the zoneroot
* isn't available to everyone on the system
*/
"failed to chmod zone path %s\n"),
goto done;
}
} else {
/* Loopback mount this dataset at the altroot */
"failed to loopback mount %s at %s: %s\n"),
ret = BE_ERR_MOUNT;
goto done;
}
}
ret = BE_SUCCESS;
done :
return (ret);
}
/*
* Function: unmount_shared_fs
* Description: This function iterates through the mnttab and finds all
* loopback mount entries that reside within the altroot of
* where the BE is mounted, and unmounts it.
* Parameters:
* ud - be_unmount_data_t pointer
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int i;
int altroot_len;
int err = 0;
errno = 0;
/* Read in the mnttab into a table */
"failed to open mnttab\n"));
return (errno_to_be_err(err));
}
if (size % read_chunk == 0) {
}
/*
* Copy over the current mnttab entry into our table,
* copying only the fields that we care about.
*/
"memory allocation failed\n"));
return (BE_ERR_NOMEM);
}
}
/*
* Process the mnttab entries in reverse order, looking for
* loopback mount entries mounted under our altroot.
*/
for (i = size; i > 0; i--) {
/* If not of type lofs, skip */
continue;
/* If inside the altroot, unmount it */
(void) sleep(1);
}
if (err != 0) {
"unmount_shared_fs: "
"failed to unmount shared file "
"system %s: %s\n"),
return (errno_to_be_err(err));
}
}
}
}
return (BE_SUCCESS);
}
/*
* Function: get_mountpoint_from_vfstab
* Description: This function digs into the vfstab in the given altroot,
* and searches for an entry for the fs passed in. If found,
* it returns the mountpoint of that fs in the mountpoint
* buffer passed in. If the get_alt_mountpoint flag is set,
* it returns the mountpoint with the altroot prepended.
* Parameters:
* altroot - pointer to the alternate root location
* fs - pointer to the file system name to look for in the
* vfstab in altroot
* mountpoint - pointer to buffer of where the mountpoint of
* fs will be returned.
* size_mp - size of mountpoint argument
* get_alt_mountpoint - flag to indicate whether or not the
* mountpoint should be populated with the altroot
* prepended.
* Returns:
* BE_SUCCESS - Success
* 1 - Failure
* Scope:
* Private
*/
static int
{
/* Generate path to alternate root vfstab */
altroot);
/* Open alternate root vfstab */
"failed to open vfstab (%s)\n"), alt_vfstab);
return (1);
}
/*
* Found entry for fs, grab its mountpoint.
* If the flag to prepend the altroot into the mountpoint
* is set, prepend it. Otherwise, just return the mountpoint.
*/
if (get_alt_mountpoint) {
vp.vfs_mountp);
} else {
}
} else {
return (1);
}
return (BE_SUCCESS);
}
/*
* Function: fix_mountpoint_callback
* Description: This callback function is used to iterate through a BE's
* children filesystems to check if its mountpoint is currently
* set to be mounted at some specified altroot. If so, fix it by
* removing altroot from the beginning of its mountpoint.
*
* Note - There's no way to tell if a child filesystem's
* mountpoint isn't broken, and just happens to begin with
* the altroot we're looking for. In this case, this function
* will errantly remove the altroot portion from the beginning
* of this filesystem's mountpoint.
*
* Parameters:
* zhp - zfs_handle_t pointer to filesystem being processed.
* data - altroot of where BE is to be mounted.
* Returns:
* 0 - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int ret = 0;
/* Get dataset's persistent mountpoint and source values */
"failed to get mountpoint and sourcetype for %s\n"),
zfs_get_name(zhp));
return (BE_ERR_ZFS);
}
/*
* If the mountpoint is not inherited and the mountpoint is not
* 'legacy', this file system potentially needs its mountpoint
* fixed.
*/
if (!(sourcetype & ZPROP_SRC_INHERITED) &&
/*
* Check if this file system's current mountpoint is
* under the altroot we're fixing it against.
*/
/*
* Get this dataset's mountpoint relative to the
* altroot.
*/
/* Fix this dataset's mountpoint value */
if (zfs_prop_set(zhp,
zhp_mountpoint)) {
"failed to set mountpoint for %s to "
return (ret);
}
}
}
/* Iterate through this dataset's children and fix them */
altroot)) != 0) {
return (ret);
}
return (0);
}
/*
* Function: be_mount_root
* Description: This function mounts the root dataset of a BE at the
* specified altroot.
* Parameters:
* zhp - zfs_handle_t pointer to root dataset of a BE that is
* to be mounted at altroot.
* altroot - location of where to mount the BE root.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int err;
/*
* Check to see if the mountpoint we've been given exists and if
* not, create it.
*/
errno = 0;
"alternate mountpoint %s\n"), altroot);
return (errno_to_be_err(err));
}
/* Temporary mount the BE's root filesystem */
altroot);
return (zfs_err_to_be_err(g_zfs));
}
return (BE_SUCCESS);
}
/*
* Function: be_unmount_root
* Description: This function unmounts the root dataset of a BE, but before
* unmounting, it looks at the BE's vfstab to determine
* if the root dataset mountpoint should be left as 'legacy'
* or '/'. If the vfstab contains an entry for this root
* dataset with a mountpoint of '/', it sets the mountpoint
* property to 'legacy'.
*
* Parameters:
* zhp - zfs_handle_t pointer of the BE root dataset that
* is currently mounted.
* ud - be_unmount_data_t pointer providing unmount data
* for the given BE root dataset.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
/* See if this is a legacy mounted root */
}
/* Unmount the dataset */
return (zfs_err_to_be_err(g_zfs));
}
/* Set canmount property for this BE's root filesystem to noauto */
!= 0) {
"set canmount property for %s to 'noauto': %s\n"),
return (zfs_err_to_be_err(g_zfs));
}
/*
* Set mountpoint for BE's root dataset back to '/', or 'legacy'
* if its a legacy mounted root.
*/
return (zfs_err_to_be_err(g_zfs));
}
return (BE_SUCCESS);
}
/*
* Function: fix_mountpoint
* Description: This function checks the mountpoint of an unmounted BE to make
* sure that it is set to either 'legacy' or '/'. If it's not,
* then we're in a situation where an unmounted BE has some random
* mountpoint set for it. (This could happen if the system was
* rebooted while an inactive BE was mounted). This function
* attempts to fix its mountpoints.
* Parameters:
* zhp - zfs_handle_t pointer to root dataset of the BE
* whose mountpoint needs to be checked.
* Return:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
/*
* Record what this BE's root dataset mountpoint property is currently
* set to.
*/
return (BE_ERR_ZFS);
}
/*
* If the root dataset mountpoint is set to 'legacy' or '/', we're okay.
*/
return (BE_SUCCESS);
}
/*
* Iterate through this BE's children datasets and fix
* them if they need fixing.
*/
!= 0) {
return (BE_ERR_ZFS);
}
/*
* The process of mounting and unmounting the root file system
* will fix its mountpoint to correctly be either 'legacy' or '/'
* since be_unmount_root will do the right thing by looking at
* its vfstab.
*/
/* Generate temporary altroot to mount the root file system */
"make temporary mountpoint\n"));
return (ret);
}
/* Mount and unmount the root. */
"mount BE root file system\n"));
goto cleanup;
}
"unmount BE root file system\n"));
goto cleanup;
}
return (ret);
}
/*
* Function: be_mount_zones
* Description: This function finds all supported non-global zones in the
* given global BE and mounts them with respect to where the
* global BE is currently mounted. The global BE datasets
* (including its shared datasets) are expected to already
* be mounted.
* Parameters:
* be_zhp - zfs_handle_t pointer to the root dataset of the
* global BE.
* md - be_mount_data_t pointer to data for global BE.
* be_name - name of the BE.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int k;
"no supported brands\n"));
return (BE_SUCCESS);
}
return (BE_SUCCESS);
}
if (z_zlist_get_current_state(zlst, k) ==
/*
* Get the dataset of this zonepath in current BE.
* If not a dataset or otherwise not available skip it
* but do not fail the BE operation.
*/
== NULL) {
"failed to mount zone %s under altroot "
"%s.\npkg(1) operations which attempt to "
"modify ABE %s may fail\nUse 'zoneadm -R"
"%s -z %s mark unavailable' to manually "
"mark the zone as unavailable.\n"),
continue;
}
/*
* Check if this zone is supported based on
* the dataset of its zonepath
*/
if (!be_zone_supported(zonepath_ds)) {
zonepath_ds = NULL;
continue;
}
/*
* if BE's shared file systems are already mounted,
* zone path dataset would have already been lofs
* mounted under altroot. Otherwise, we need to do
* it here.
*/
B_TRUE);
if (ret != BE_SUCCESS)
goto done;
}
/* Mount this zone */
zonepath_ds = NULL;
if (ret != BE_SUCCESS) {
"failed to mount zone %s under "
goto done;
}
}
}
done:
/*
* libinstzones caches mnttab and uses cached version for resolving lofs
* mounts when we call z_resolve_lofs. It creates the cached version
* when the first call to z_resolve_lofs happens. So, library's cached
* mnttab doesn't contain entries for lofs mounts created in the above
* loop. Because of this, subsequent calls to z_resolve_lofs would fail
* to resolve these lofs mounts. So, here we destroy library's cached
* mnttab to force its recreation when the next call to z_resolve_lofs
* happens.
*/
return (ret);
}
/*
* Function: be_unmount_zones
* Description: This function finds all supported non-global zones in the
* given mounted global BE and unmounts them.
* Parameters:
* ud - unmount_data_t pointer data for the global BE.
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
{
int k;
"no supported brands\n"));
return (BE_SUCCESS);
}
return (BE_SUCCESS);
}
if (z_zlist_get_current_state(zlst, k) ==
/* Build zone's zonepath wrt the global BE altroot */
/*
* Get the dataset of the zonepath. If its not
* a dataset, skip it.
*
* Since the alternate zone path is a tmpfs
* mount we need to look for the zonepath
* dataset based on the zonepath not the
* alt_zonepath.
*/
if ((zonepath_ds =
continue;
}
/*
* Check if this zone is supported based on the
* dataset of its zonepath.
*/
if (!be_zone_supported(zonepath_ds)) {
zonepath_ds = NULL;
continue;
}
/* Unmount this zone */
zonepath_ds = NULL;
if (ret != BE_SUCCESS) {
" failed to unmount zone %s from "
goto done;
}
}
}
done:
return (ret);
}
/*
* Function: be_mount_one_zone
* Description: This function is called to mount one zone for a given
* global BE.
* Parameters:
* be_zhp - zfs_handle_t pointer to the root dataset of the
* global BE
* md - be_mount_data_t pointer to data for global BE
* zonename - name of zone to mount
* zonepath - zonepath of zone to mount
* zonepath_ds - dataset for the zonepath
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
char *zonepath, char *zonepath_ds)
{
int err = 0;
errno = 0;
/* Find the active zone root dataset for this zone for this BE */
sizeof (zoneroot_ds))) == BE_ERR_ZONE_NO_ACTIVE_ROOT) {
"find active zone root for zone %s, skipping ...\n"),
zonename);
return (BE_SUCCESS);
} else if (ret != BE_SUCCESS) {
"find active zone root for zone %s\n"), zonename);
return (ret);
}
/* Get handle to active zoneroot dataset */
== NULL) {
"open zone root dataset (%s): %s\n"), zoneroot_ds,
return (zfs_err_to_be_err(g_zfs));
}
/* Generate string for zone's altroot path */
if (mkdirp(zone_altroot,
"create mount point %s\n"), zone_altroot);
goto done;
}
/* Build mount_data for the zone */
/* Mount the zone's root file system */
"mount zone root file system at %s\n"), zone_altroot);
goto done;
}
/* Iterate through zone's children filesystems */
zone_altroot)) != 0) {
"mount zone subordinate file systems at %s\n"),
goto done;
}
/* TODO: Mount all shared file systems for this zone */
done:
return (ret);
}
/*
* Function: be_unmount_one_zone
* Description: This function unmount one zone for a give global BE.
* Parameters:
* ud - be_unmount_data_t pointer to data for global BE
* zonename - name of zone to unmount
* zonepath - zonepath of the zone to unmount
* zonepath_ds - dataset for the zonepath
* Returns:
* BE_SUCCESS - Success
* be_errno_t - Failure
* Scope:
* Private
*/
static int
char *zonepath_ds)
{
int err = 0;
errno = 0;
/* Generate string for zone's alternate root path */
/* Generate string for zone's alternate zone path */
/* Build be_unmount_data for zone */
/* Find the mounted zone root dataset for this zone for this BE */
"find any zone root mounted for zone %s\n"), zonename);
ret = BE_SUCCESS;
goto unmount_zonepath;
} else if (ret != BE_SUCCESS) {
"find mounted zone root for zone %s\n"), zonename);
return (ret);
}
/* Get handle to zoneroot dataset mounted for this BE */
== NULL) {
"open mounted zone root dataset (%s): %s\n"), zoneroot_ds,
return (zfs_err_to_be_err(g_zfs));
}
/* TODO: Unmount all shared file systems for this zone */
/* Iterate through zone's children filesystems and unmount them */
&zone_ud)) != 0) {
"unmount zone subordinate file systems at %s\n"),
goto done;
}
/* Unmount the zone's root filesystem */
"unmount zone root file system at %s\n"), zone_altroot);
goto done;
}
/*
* We want to unmount this here since we mounted this as a tmpfs mount
* in our zone mount code.
*/
/*
* Check to see if zonepath unmount generated EINVAL which
* indicates that the zonepath we're trying to unmount was
* never in fact mounted (because we mount zones in a for-loop
* but if any individual zone mount encounters an error we stop
* processing). In which case, just move along.
*/
ret = 0;
goto done;
}
"unmount alternate zone path (%s): %s\n"), zone_altpath,
}
done:
return (ret);
}
/*
* Function: be_get_ds_from_dir_callback
* Description: This is a callback function used to iterate all datasets
* to find the one that is currently mounted at the directory
* being searched for. If matched, the name of the dataset is
* returned in heap storage, so the caller is responsible for
* freeing it.
* Parameters:
* zhp - zfs_handle_t pointer to current dataset being processed.
* data - dir_data_t pointer providing name of directory being
* searched for.
* Returns:
* 1 - This dataset is mounted at directory being searched for.
* 0 - This dataset is not mounted at directory being searched for.
* Scope:
* Private
*/
static int
{
int zret = 0;
return (0);
}
"memory allocation failed\n"));
return (0);
}
return (1);
}
return (zret);
}