zfs.c revision 286822dd6ee35fa0959e7b55e659b92ea1c12f71
/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains the functions used to support the ZFS integration
* with zones. This includes validation (e.g. zonecfg dataset), cloning,
* file system creation and destruction.
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <locale.h>
#include <libintl.h>
#include <libgen.h>
#include <libzonecfg.h>
#include <libzfs.h>
#include <values.h>
#include "zoneadm.h"
typedef struct zfs_mount_data {
char *match_name;
typedef struct zfs_snapshot_data {
char *match_name; /* zonename@SUNWzone */
int len; /* strlen of match_name */
int max; /* highest digit appended to snap name */
int num; /* number of snapshots to rename */
int cntr; /* counter for renaming snapshots */
typedef struct clone_data {
const char *snapshot; /* snapshot of dataset being demoted */
} clone_data_t;
/*
* A ZFS file system iterator call-back function which is used to validate
* datasets imported into the zone.
*/
/* ARGSUSED */
static int
{
int ret;
/*
* TRANSLATION_NOTE
* zfs and dataset are literals that should not be translated.
*/
"volumes cannot be specified as a zone dataset resource\n"),
zfs_get_name(zhp));
ret = -1;
} else {
}
return (ret);
}
/*
* A ZFS file system iterator call-back function which returns the
* zfs_handle_t for a ZFS file system on the specified mount point.
*/
static int
{
int res;
char mp[ZFS_MAXPROPLEN];
return (0);
}
/* First check if the dataset is mounted. */
return (0);
}
/* Now check mount point. */
0, B_FALSE) != 0) {
return (0);
}
/* If legacy, must look in mnttab for mountpoint. */
const char *nm;
return (0);
}
== 0) {
return (1);
}
break;
}
}
return (1);
}
/* Iterate over any nested datasets. */
return (res);
}
/*
* Get ZFS handle for the specified mount point.
*/
static zfs_handle_t *
mount2zhandle(char *mountpoint)
{
return (cb.match_handle);
}
/*
* Check if there is already a file system (zfs or any other type) mounted on
* path.
*/
static boolean_t
is_mountpnt(char *path)
{
return (B_FALSE);
return (B_TRUE);
}
}
return (B_FALSE);
}
/*
* Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
*/
static int
pre_snapshot(char *presnapbuf)
{
int status;
/* No brand-specific handler */
if (presnapbuf[0] == '\0')
return (Z_OK);
/* Run the hook */
return (Z_ERR);
return (Z_OK);
}
/*
* Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
*/
static int
post_snapshot(char *postsnapbuf)
{
int status;
/* No brand-specific handler */
if (postsnapbuf[0] == '\0')
return (Z_OK);
/* Run the hook */
return (Z_ERR);
return (Z_OK);
}
/*
* This is a ZFS snapshot iterator call-back function which returns the
* highest number of SUNWzone snapshots that have been taken.
*/
static int
{
int res;
return (0);
}
char *nump;
int num;
}
return (res);
}
/*
* Take a ZFS snapshot to be used for cloning the zone.
*/
static int
char *presnapbuf, char *postsnapbuf)
{
int res;
char template[ZFS_MAXNAMELEN];
/*
* First we need to figure out the next available name for the
* zone snapshot. Look through the list of zones snapshots for
* this file system to determine the maximum snapshot name.
*/
return (Z_ERR);
return (Z_ERR);
return (Z_ERR);
return (Z_ERR);
return (Z_ERR);
if (res != 0)
return (Z_ERR);
return (Z_OK);
}
/*
* We are using an explicit snapshot from some earlier point in time so
* we need to validate it. Run the brand specific hook.
*/
static int
{
int status;
char cmdbuf[MAXPATHLEN];
/* No brand-specific handler */
if (validsnapbuf[0] == '\0')
return (Z_OK);
/* pass args - snapshot_name & snap_path */
zerror("Command line too long");
return (Z_ERR);
}
/* Run the hook */
return (Z_ERR);
return (Z_OK);
}
/*
* Remove the sw inventory file from inside this zonepath that we picked up out
* of the snapshot.
*/
static int
{
int err;
return (Z_ERR);
}
return (Z_ERR);
}
return (Z_OK);
}
/*
* Make a ZFS clone on zonepath from snapshot_name.
*/
static int
{
int err;
return (Z_NO_ENTRY);
"off") != 0) {
"%s: out of memory\n"), zonepath);
return (Z_ERR);
}
if (err != 0)
return (Z_ERR);
/* create the mountpoint if necessary */
return (Z_ERR);
/*
* The clone has been created so we need to print a diagnostic
* message if one of the following steps fails for some reason.
*/
} else if (clean_out_clone() != Z_OK) {
"software inventory from ZFS clone %s\n"),
}
return (res);
}
/*
* This function takes a zonepath and attempts to determine what the ZFS
* file system name (not mountpoint) should be for that path. We do not
* assume that zonepath is an existing directory or ZFS fs since we use
* this function as part of the process of creating a new ZFS fs or clone.
*
* The way this works is that we look at the parent directory of the zonepath
* to see if it is a ZFS fs. If it is, we get the name of that ZFS fs and
* append the last component of the zonepath to generate the ZFS name for the
* zonepath. This matches the algorithm that ZFS uses for automatically
* mounting a new fs after it is created.
*
* Although a ZFS fs can be mounted anywhere, we don't worry about handling
* all of the complexity that a user could possibly configure with arbitrary
* mounts since there is no way to generate a ZFS name from a random path in
* the file system. We only try to handle the automatic mounts that ZFS does
* for each file system. ZFS restricts this so that a new fs must be created
* in an existing parent ZFS fs. It then automatically mounts the new fs
* directly under the mountpoint for the parent fs using the last component
* of the name as the mountpoint directory.
*
* For example:
* Name Mountpoint
*
* Return Z_OK if the path mapped to a ZFS file system name, otherwise return
* Z_ERR.
*/
static int
{
int res;
/*
* We need two tmp strings to handle paths directly in / (e.g. /foo)
* since dirname will overwrite the first char after "/" in this case.
*/
return (Z_ERR);
return (Z_ERR);
}
/*
* This is a quick test to save iterating over all of the zfs datasets
* on the system (which can be a lot). If the parent dir is not in a
* ZFS fs, then we're done.
*/
return (Z_ERR);
}
/* See if the parent directory is its own ZFS dataset. */
/*
* The parent is not a ZFS dataset so we can't automatically
* create a dataset on the given path.
*/
return (Z_ERR);
}
return (Z_ERR);
return (Z_OK);
}
/*
* A ZFS file system iterator call-back function used to determine if the
* file system has dependents (snapshots & clones).
*/
/* ARGSUSED */
static int
{
return (1);
}
/*
* Given a snapshot name, get the file system path where the snapshot lives.
* A snapshot name is of the form fs_name@snap_name. For example, snapshot
*/
static int
{
char *p;
char mp[ZFS_MAXPROPLEN];
return (Z_ERR);
/* Get the file system name from the snap_name. */
*p = '\0';
*p = '@';
return (Z_ERR);
/* Get the file system mount point. */
0, B_FALSE) != 0) {
return (Z_ERR);
}
p++;
return (Z_ERR);
return (Z_OK);
}
/*
* 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.
*/
static int
{
char origin[ZFS_MAXNAMELEN];
char ds_path[ZFS_MAXNAMELEN];
return (0);
}
/* Make sure this is a direct clone of the snapshot we're iterating. */
return (0);
}
return (1);
}
/*
* A ZFS file system iterator call-back function used to determine the clone
* to promote. This function finds the youngest (i.e. last one taken) snapshot
* that has a clone. If found, it returns a reference to that clone in the
* callback data.
*/
static int
{
int zret = 0;
/* If snapshot has no clones, skip it */
return (0);
}
/* Get the creation time of this snapshot */
/*
* If this snapshot's creation time is greater than (i.e. younger than)
* the current youngest snapshot found, iterate this snapshot to
* get the right clone.
*/
/*
* Iterate the dependents of this snapshot to find a clone
* that's a direct dependent.
*/
cd)) == -1) {
return (1);
} else if (zret == 1) {
/*
* Found a clone, update the origin_creation time
* in the callback data.
*/
}
}
return (0);
}
/*
* A ZFS file system iterator call-back function used to remove standalone
* snapshots.
*/
/* ARGSUSED */
static int
{
/* If snapshot has clones, something is wrong */
return (1);
}
(void) zfs_destroy(zhp);
}
return (0);
}
/*
* A ZFS snapshot iterator call-back function which renames snapshots.
*/
static int
{
int res;
char template[ZFS_MAXNAMELEN];
/*
* When renaming snapshots with the iterator, the iterator can see
* the same snapshot after we've renamed up in the namespace. To
* prevent this we check the count for the number of snapshots we have
* to rename and stop at that point.
*/
return (0);
}
return (0);
}
/* Only rename the snapshots we automatically generate when we clone. */
return (0);
}
if (res != 0)
return (res);
}
/*
* Rename the source dataset's snapshots that are automatically generated when
* we clone a zone so that there won't be a name collision when we promote the
* cloned dataset. Once the snapshots have been renamed, then promote the
* clone.
*
* The snapshot rename process gets the highest number on the snapshot names
* (the format is zonename@SUNWzoneXX where XX are digits) on both the source
* and clone datasets, then renames the source dataset snapshots starting at
* the next number.
*/
static int
{
char nm[ZFS_MAXNAMELEN];
char template[ZFS_MAXNAMELEN];
/*
* Start by getting the clone's snapshot max which we use
* during the rename of the original dataset's snapshots.
*/
return (Z_ERR);
/*
* Now make sure the source's snapshot max is at least as high as
* the clone's snapshot max.
*/
return (Z_ERR);
/*
* Now rename the source dataset's snapshots so there's no
* conflict when we promote the clone.
*/
return (Z_ERR);
/* close and reopen the clone dataset to get the latest info */
return (Z_ERR);
if (zfs_promote(cln_zhp) != 0) {
return (Z_ERR);
}
return (Z_OK);
}
/*
* Promote the youngest clone. That clone will then become the origin of all
* of the other clones that were hanging off of the source dataset.
*/
int
{
char nm[ZFS_MAXNAMELEN];
cd.origin_creation = 0;
return (Z_ERR);
}
/* Nothing to promote. */
return (Z_OK);
/* Found the youngest clone to promote. Promote it. */
return (Z_ERR);
}
/* close and reopen the main dataset to get the latest info */
return (Z_ERR);
return (Z_OK);
}
/*
* Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
* possible, or by copying the data from the snapshot to the zonepath.
*/
int
{
char clone_name[MAXPATHLEN];
char snap_path[MAXPATHLEN];
return (Z_ERR);
}
return (Z_NO_ENTRY);
/*
* The zonepath cannot be ZFS cloned, try to copy the data from
* within the snapshot to the zonepath.
*/
if (clean_out_clone() != Z_OK)
gettext("could not remove the "
"software inventory from %s\n"), zonepath);
return (err);
}
if (err != Z_NO_ENTRY) {
/*
* Cloning the snapshot failed. Fall back to trying
* to install the zone by copying from the snapshot.
*/
if (clean_out_clone() != Z_OK)
gettext("could not remove the "
"software inventory from %s\n"),
zonepath);
} else {
/*
* The snapshot is unusable for some reason so restore
* the zone state to configured since we were unable to
* actually do anything about getting the zone
* installed.
*/
int tmp;
ZONE_STATE_CONFIGURED)) != Z_OK) {
gettext("could not set state"));
}
}
}
return (err);
}
/*
* Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
*/
int
char *postsnapbuf)
{
char clone_name[MAXPATHLEN];
char snap_name[MAXPATHLEN];
/*
* Try to get a zfs handle for the source_zonepath. If this fails
* the source_zonepath is not ZFS so return an error.
*/
return (Z_ERR);
/*
* Check if there is a file system already mounted on zonepath. If so,
* we can't clone to the path so we should fall back to copying.
*/
if (is_mountpnt(zonepath)) {
gettext("A file system is already mounted on %s,\n"
"preventing use of a ZFS clone.\n"), zonepath);
return (Z_ERR);
}
/*
* Instead of using path2name to get the clone name from the zonepath,
* we could generate a name from the source zone ZFS name. However,
* this would mean we would create the clone under the ZFS fs of the
* source instead of what the zonepath says. For example,
*
* source_zonepath zonepath
*
* We don't want the clone to be under "dev", we want it under
* "deploy", so that we can leverage the normal attribute inheritance
* that ZFS provides in the fs hierarchy.
*/
return (Z_ERR);
}
postsnapbuf) != Z_OK) {
return (Z_ERR);
}
/* Clean up the snapshot we just took. */
!= NULL) {
(void) zfs_destroy(zhp);
}
return (Z_ERR);
}
"created for this zone.\n"));
return (Z_OK);
}
/*
* Attempt to create a ZFS file system for the specified zonepath.
* We either will successfully create a ZFS file system and get it mounted
* on the zonepath or we don't. The caller doesn't care since a regular
* directory is used for the zonepath if no ZFS file system is mounted there.
*/
void
create_zfs_zonepath(char *zonepath)
{
char zfs_name[MAXPATHLEN];
return;
/* Check if the dataset already exists. */
return;
}
"off") != 0) {
"out of memory\n"), zfs_name);
}
return;
}
(void) zfs_destroy(zhp);
} else {
"successfully created, but chmod %o failed: %s\n"),
(void) destroy_zfs(zonepath);
} else {
"created for this zone.\n"));
}
}
}
/*
* If the zonepath is a ZFS file system, attempt to destroy it. We return Z_OK
* if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
* which means the caller should clean up the zonepath in the traditional
* way.
*/
int
destroy_zfs(char *zonepath)
{
char origin[ZFS_MAXPROPLEN];
return (Z_ERR);
if (promote_all_clones(zhp) != 0)
return (Z_ERR);
/* Now cleanup any snapshots remaining. */
return (Z_ERR);
}
/*
* We can't destroy the file system if it has still has dependents.
* There shouldn't be any at this point, but we'll double check.
*/
return (Z_ERR);
}
/*
* This might be a clone. Try to get the snapshot so we can attempt
* to destroy that as well.
*/
return (Z_ERR);
}
if (zfs_destroy(zhp) != 0) {
/*
* If the destroy fails for some reason, try to remount
* the file system so that we can use "rm -rf" to clean up
* instead.
*/
return (Z_ERR);
}
/*
* If the zone has ever been moved then the mountpoint dir will not be
* cleaned up by the zfs_destroy(). To handle this case try to clean
* it up now but don't worry if it fails, that will be normal.
*/
"destroyed.\n"));
if (is_clone) {
/*
* Try to clean up the snapshot that the clone was taken from.
*/
ZFS_TYPE_SNAPSHOT)) != NULL) {
(void) zfs_destroy(ohp);
}
}
return (Z_OK);
}
/*
* Return true if the path is its own zfs file system. We determine this
* by stat-ing the path to see if it is zfs and stat-ing the parent to see
* if it is a different fs.
*/
is_zonepath_zfs(char *zonepath)
{
int res;
char *path;
char *parent;
return (B_FALSE);
return (B_FALSE);
return (B_FALSE);
if (res != 0)
return (B_FALSE);
return (B_FALSE);
return (B_TRUE);
}
/*
* Implement the fast move of a ZFS file system by simply updating the
* mountpoint. Since it is file system already, we don't have the
* issue of cross-file system copying.
*/
int
{
return (Z_ERR);
new_zonepath) == 0) {
/*
* Clean up the old mount point. We ignore any failure since
* the zone is already successfully mounted on the new path.
*/
}
return (ret);
}
/*
* Validate that the given dataset exists on the system, and that neither it nor
* its children are zvols.
*
* Note that we don't do anything with the 'zoned' property here. All
* management is done in zoneadmd when the zone is actually rebooted. This
* allows us to automatically set the zoned property even when a zone is
* rebooted by the administrator.
*/
int
{
int return_code = Z_OK;
struct zone_dstab dstab;
char propbuf[ZFS_MAXPROPLEN];
char source[ZFS_MAXNAMELEN];
/*
* TRANSLATION_NOTE
* zfs and dataset are literals that should not be translated.
*/
"unable to enumerate datasets\n"));
return (Z_ERR);
}
return_code = Z_ERR;
continue;
}
sizeof (source), 0) == 0 &&
(srctype == ZPROP_SRC_INHERITED)) {
"dataset %s: mountpoint cannot be inherited\n"),
return_code = Z_ERR;
continue;
}
"dataset %s: volumes cannot be specified as a "
"zone dataset resource\n"),
return_code = Z_ERR;
}
return_code = Z_ERR;
}
(void) zonecfg_enddsent(handle);
return (return_code);
}
/*
* Verify that the ZFS dataset exists, and its mountpoint
* property is set to "legacy".
*/
int
{
char propbuf[ZFS_MAXPROPLEN];
ZFS_TYPE_DATASET)) == NULL) {
"could not access zfs dataset '%s'\n"),
return (Z_ERR);
}
"'%s' is not a file system\n"),
return (Z_ERR);
}
"zfs '%s' mountpoint is not \"legacy\"\n"),
return (Z_ERR);
}
return (Z_OK);
}
int
init_zfs(void)
{
"library\n"));
return (Z_ERR);
}
return (Z_OK);
}