10b5de56gjelinek/*
20b5de56gjelinek * CDDL HEADER START
30b5de56gjelinek *
40b5de56gjelinek * The contents of this file are subject to the terms of the
50b5de56gjelinek * Common Development and Distribution License (the "License").
60b5de56gjelinek * You may not use this file except in compliance with the License.
70b5de56gjelinek *
80b5de56gjelinek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90b5de56gjelinek * or http://www.opensolaris.org/os/licensing.
100b5de56gjelinek * See the License for the specific language governing permissions
110b5de56gjelinek * and limitations under the License.
120b5de56gjelinek *
130b5de56gjelinek * When distributing Covered Code, include this CDDL HEADER in each
140b5de56gjelinek * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150b5de56gjelinek * If applicable, add the following below this CDDL HEADER, with the
160b5de56gjelinek * fields enclosed by brackets "[]" replaced with your own identifying
170b5de56gjelinek * information: Portions Copyright [yyyy] [name of copyright owner]
180b5de56gjelinek *
190b5de56gjelinek * CDDL HEADER END
200b5de56gjelinek */
210b5de56gjelinek
220b5de56gjelinek/*
232b6c28bbatschul * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
249adfa60Matthew Ahrens * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
25dd9c3b2Jerry Jelinek * Copyright (c) 2012, Joyent, Inc. All rights reserved.
260d8fa8fMartin Matuska * Copyright (c) 2016 Martin Matuska. All rights reserved.
270b5de56gjelinek */
280b5de56gjelinek
290b5de56gjelinek/*
300b5de56gjelinek * This file contains the functions used to support the ZFS integration
310b5de56gjelinek * with zones.  This includes validation (e.g. zonecfg dataset), cloning,
320b5de56gjelinek * file system creation and destruction.
330b5de56gjelinek */
340b5de56gjelinek
350b5de56gjelinek#include <stdio.h>
360b5de56gjelinek#include <errno.h>
370b5de56gjelinek#include <unistd.h>
380b5de56gjelinek#include <string.h>
390b5de56gjelinek#include <locale.h>
400b5de56gjelinek#include <libintl.h>
410b5de56gjelinek#include <sys/stat.h>
420b5de56gjelinek#include <sys/statvfs.h>
430b5de56gjelinek#include <libgen.h>
440b5de56gjelinek#include <libzonecfg.h>
450b5de56gjelinek#include <sys/mnttab.h>
460b5de56gjelinek#include <libzfs.h>
4711506c4gjelinek#include <sys/mntent.h>
48286822d<gerald.jelinek@sun.com>#include <values.h>
490094b37jv#include <strings.h>
500094b37jv#include <assert.h>
510b5de56gjelinek
520b5de56gjelinek#include "zoneadm.h"
530b5de56gjelinek
5499653d4eschrocklibzfs_handle_t *g_zfs;
550b5de56gjelinek
560b5de56gjelinektypedef struct zfs_mount_data {
570b5de56gjelinek	char		*match_name;
580b5de56gjelinek	zfs_handle_t	*match_handle;
590b5de56gjelinek} zfs_mount_data_t;
600b5de56gjelinek
610b5de56gjelinektypedef struct zfs_snapshot_data {
62286822d<gerald.jelinek@sun.com>	char	*match_name;	/* zonename@SUNWzone */
63286822d<gerald.jelinek@sun.com>	int	len;		/* strlen of match_name */
64286822d<gerald.jelinek@sun.com>	int	max;		/* highest digit appended to snap name */
65286822d<gerald.jelinek@sun.com>	int	num;		/* number of snapshots to rename */
66286822d<gerald.jelinek@sun.com>	int	cntr;		/* counter for renaming snapshots */
670b5de56gjelinek} zfs_snapshot_data_t;
680b5de56gjelinek
69286822d<gerald.jelinek@sun.com>typedef struct clone_data {
70286822d<gerald.jelinek@sun.com>	zfs_handle_t	*clone_zhp;	/* clone dataset to promote */
71286822d<gerald.jelinek@sun.com>	time_t		origin_creation; /* snapshot creation time of clone */
72286822d<gerald.jelinek@sun.com>	const char	*snapshot;	/* snapshot of dataset being demoted */
73286822d<gerald.jelinek@sun.com>} clone_data_t;
74286822d<gerald.jelinek@sun.com>
750b5de56gjelinek/*
760b5de56gjelinek * A ZFS file system iterator call-back function which returns the
770b5de56gjelinek * zfs_handle_t for a ZFS file system on the specified mount point.
780b5de56gjelinek */
790b5de56gjelinekstatic int
800b5de56gjelinekmatch_mountpoint(zfs_handle_t *zhp, void *data)
810b5de56gjelinek{
820b5de56gjelinek	int			res;
830b5de56gjelinek	zfs_mount_data_t	*cbp;
840b5de56gjelinek	char			mp[ZFS_MAXPROPLEN];
850b5de56gjelinek
860b5de56gjelinek	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
870b5de56gjelinek		zfs_close(zhp);
880b5de56gjelinek		return (0);
890b5de56gjelinek	}
900b5de56gjelinek
9111506c4gjelinek	/* First check if the dataset is mounted. */
9211506c4gjelinek	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
9311506c4gjelinek	    0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
9411506c4gjelinek		zfs_close(zhp);
9511506c4gjelinek		return (0);
9611506c4gjelinek	}
9711506c4gjelinek
9811506c4gjelinek	/* Now check mount point. */
990b5de56gjelinek	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
10011506c4gjelinek	    0, B_FALSE) != 0) {
10111506c4gjelinek		zfs_close(zhp);
10211506c4gjelinek		return (0);
10311506c4gjelinek	}
10411506c4gjelinek
10511506c4gjelinek	cbp = (zfs_mount_data_t *)data;
10611506c4gjelinek
10711506c4gjelinek	if (strcmp(mp, "legacy") == 0) {
10811506c4gjelinek		/* If legacy, must look in mnttab for mountpoint. */
10911506c4gjelinek		FILE		*fp;
11011506c4gjelinek		struct mnttab	entry;
11111506c4gjelinek		const char	*nm;
11211506c4gjelinek
11311506c4gjelinek		nm = zfs_get_name(zhp);
11411506c4gjelinek		if ((fp = fopen(MNTTAB, "r")) == NULL) {
11511506c4gjelinek			zfs_close(zhp);
11611506c4gjelinek			return (0);
11711506c4gjelinek		}
11811506c4gjelinek
11911506c4gjelinek		while (getmntent(fp, &entry) == 0) {
12011506c4gjelinek			if (strcmp(nm, entry.mnt_special) == 0) {
12111506c4gjelinek				if (strcmp(entry.mnt_mountp, cbp->match_name)
12211506c4gjelinek				    == 0) {
12311506c4gjelinek					(void) fclose(fp);
12411506c4gjelinek					cbp->match_handle = zhp;
12511506c4gjelinek					return (1);
12611506c4gjelinek				}
12711506c4gjelinek				break;
12811506c4gjelinek			}
12911506c4gjelinek		}
13011506c4gjelinek		(void) fclose(fp);
13111506c4gjelinek
13211506c4gjelinek	} else if (strcmp(mp, cbp->match_name) == 0) {
1330b5de56gjelinek		cbp->match_handle = zhp;
1340b5de56gjelinek		return (1);
1350b5de56gjelinek	}
1360b5de56gjelinek
13711506c4gjelinek	/* Iterate over any nested datasets. */
1380b5de56gjelinek	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1390b5de56gjelinek	zfs_close(zhp);
1400b5de56gjelinek	return (res);
1410b5de56gjelinek}
1420b5de56gjelinek
1430b5de56gjelinek/*
1440b5de56gjelinek * Get ZFS handle for the specified mount point.
1450b5de56gjelinek */
1460b5de56gjelinekstatic zfs_handle_t *
1470b5de56gjelinekmount2zhandle(char *mountpoint)
1480b5de56gjelinek{
1490b5de56gjelinek	zfs_mount_data_t	cb;
1500b5de56gjelinek
1510b5de56gjelinek	cb.match_name = mountpoint;
1520b5de56gjelinek	cb.match_handle = NULL;
15399653d4eschrock	(void) zfs_iter_root(g_zfs, match_mountpoint, &cb);
1540b5de56gjelinek	return (cb.match_handle);
1550b5de56gjelinek}
1560b5de56gjelinek
1570b5de56gjelinek/*
1580b5de56gjelinek * Check if there is already a file system (zfs or any other type) mounted on
1590b5de56gjelinek * path.
1600b5de56gjelinek */
1610b5de56gjelinekstatic boolean_t
1620b5de56gjelinekis_mountpnt(char *path)
1630b5de56gjelinek{
1640b5de56gjelinek	FILE		*fp;
1650b5de56gjelinek	struct mnttab	entry;
1660b5de56gjelinek
16711506c4gjelinek	if ((fp = fopen(MNTTAB, "r")) == NULL)
1680b5de56gjelinek		return (B_FALSE);
1690b5de56gjelinek
1700b5de56gjelinek	while (getmntent(fp, &entry) == 0) {
1710b5de56gjelinek		if (strcmp(path, entry.mnt_mountp) == 0) {
1720b5de56gjelinek			(void) fclose(fp);
1730b5de56gjelinek			return (B_TRUE);
1740b5de56gjelinek		}
1750b5de56gjelinek	}
1760b5de56gjelinek
1770b5de56gjelinek	(void) fclose(fp);
1780b5de56gjelinek	return (B_FALSE);
1790b5de56gjelinek}
1800b5de56gjelinek
1810b5de56gjelinek/*
182ff17c8bgjelinek * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
1830b5de56gjelinek */
1840b5de56gjelinekstatic int
185ff17c8bgjelinekpre_snapshot(char *presnapbuf)
1860b5de56gjelinek{
187ff17c8bgjelinek	int status;
1880b5de56gjelinek
189ff17c8bgjelinek	/* No brand-specific handler */
190ff17c8bgjelinek	if (presnapbuf[0] == '\0')
191ff17c8bgjelinek		return (Z_OK);
1920b5de56gjelinek
193ff17c8bgjelinek	/* Run the hook */
194c75cc34<gerald.jelinek@sun.com>	status = do_subproc(presnapbuf);
195ff17c8bgjelinek	if ((status = subproc_status(gettext("brand-specific presnapshot"),
196ff17c8bgjelinek	    status, B_FALSE)) != ZONE_SUBPROC_OK)
1970b5de56gjelinek		return (Z_ERR);
1980b5de56gjelinek
1990b5de56gjelinek	return (Z_OK);
2000b5de56gjelinek}
2010b5de56gjelinek
2020b5de56gjelinek/*
203ff17c8bgjelinek * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
2040b5de56gjelinek */
2050b5de56gjelinekstatic int
206ff17c8bgjelinekpost_snapshot(char *postsnapbuf)
2070b5de56gjelinek{
208ff17c8bgjelinek	int status;
2090b5de56gjelinek
210ff17c8bgjelinek	/* No brand-specific handler */
211ff17c8bgjelinek	if (postsnapbuf[0] == '\0')
212ff17c8bgjelinek		return (Z_OK);
2130b5de56gjelinek
214ff17c8bgjelinek	/* Run the hook */
215c75cc34<gerald.jelinek@sun.com>	status = do_subproc(postsnapbuf);
216ff17c8bgjelinek	if ((status = subproc_status(gettext("brand-specific postsnapshot"),
217ff17c8bgjelinek	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2180b5de56gjelinek		return (Z_ERR);
2190b5de56gjelinek
2200b5de56gjelinek	return (Z_OK);
2210b5de56gjelinek}
2220b5de56gjelinek
2230b5de56gjelinek/*
2240b5de56gjelinek * This is a ZFS snapshot iterator call-back function which returns the
2250b5de56gjelinek * highest number of SUNWzone snapshots that have been taken.
2260b5de56gjelinek */
2270b5de56gjelinekstatic int
2280b5de56gjelinekget_snap_max(zfs_handle_t *zhp, void *data)
2290b5de56gjelinek{
2300b5de56gjelinek	int			res;
2310b5de56gjelinek	zfs_snapshot_data_t	*cbp;
2320b5de56gjelinek
2330b5de56gjelinek	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2340b5de56gjelinek		zfs_close(zhp);
2350b5de56gjelinek		return (0);
2360b5de56gjelinek	}
2370b5de56gjelinek
2380b5de56gjelinek	cbp = (zfs_snapshot_data_t *)data;
2390b5de56gjelinek
2400b5de56gjelinek	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2410b5de56gjelinek		char	*nump;
2420b5de56gjelinek		int	num;
2430b5de56gjelinek
244286822d<gerald.jelinek@sun.com>		cbp->num++;
2450b5de56gjelinek		nump = (char *)(zfs_get_name(zhp) + cbp->len);
2460b5de56gjelinek		num = atoi(nump);
2470b5de56gjelinek		if (num > cbp->max)
2480b5de56gjelinek			cbp->max = num;
2490b5de56gjelinek	}
2500b5de56gjelinek
2510d8fa8fMartin Matuska	res = zfs_iter_snapshots(zhp, B_FALSE, get_snap_max, data);
2520b5de56gjelinek	zfs_close(zhp);
2530b5de56gjelinek	return (res);
2540b5de56gjelinek}
2550b5de56gjelinek
2560b5de56gjelinek/*
2570b5de56gjelinek * Take a ZFS snapshot to be used for cloning the zone.
2580b5de56gjelinek */
2590b5de56gjelinekstatic int
260ff17c8bgjelinektake_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
261ff17c8bgjelinek    char *presnapbuf, char *postsnapbuf)
2620b5de56gjelinek{
2630b5de56gjelinek	int			res;
2649adfa60Matthew Ahrens	char			template[ZFS_MAX_DATASET_NAME_LEN];
2650b5de56gjelinek	zfs_snapshot_data_t	cb;
2660b5de56gjelinek
2670b5de56gjelinek	/*
2680b5de56gjelinek	 * First we need to figure out the next available name for the
2690b5de56gjelinek	 * zone snapshot.  Look through the list of zones snapshots for
2700b5de56gjelinek	 * this file system to determine the maximum snapshot name.
2710b5de56gjelinek	 */
2720b5de56gjelinek	if (snprintf(template, sizeof (template), "%s@SUNWzone",
2730b5de56gjelinek	    zfs_get_name(zhp)) >=  sizeof (template))
2740b5de56gjelinek		return (Z_ERR);
2750b5de56gjelinek
2760b5de56gjelinek	cb.match_name = template;
2770b5de56gjelinek	cb.len = strlen(template);
2780b5de56gjelinek	cb.max = 0;
2790b5de56gjelinek
2800d8fa8fMartin Matuska	if (zfs_iter_snapshots(zhp, B_FALSE, get_snap_max, &cb) != 0)
2810b5de56gjelinek		return (Z_ERR);
2820b5de56gjelinek
2830b5de56gjelinek	cb.max++;
2840b5de56gjelinek
2850b5de56gjelinek	if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
2860b5de56gjelinek	    zfs_get_name(zhp), cb.max) >= snap_size)
2870b5de56gjelinek		return (Z_ERR);
2880b5de56gjelinek
289ff17c8bgjelinek	if (pre_snapshot(presnapbuf) != Z_OK)
2900b5de56gjelinek		return (Z_ERR);
291bb0ade0ahrens	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
292ff17c8bgjelinek	if (post_snapshot(postsnapbuf) != Z_OK)
2930b5de56gjelinek		return (Z_ERR);
2940b5de56gjelinek
2950b5de56gjelinek	if (res != 0)
2960b5de56gjelinek		return (Z_ERR);
2970b5de56gjelinek	return (Z_OK);
2980b5de56gjelinek}
2990b5de56gjelinek
3000b5de56gjelinek/*
3010b5de56gjelinek * We are using an explicit snapshot from some earlier point in time so
302ff17c8bgjelinek * we need to validate it.  Run the brand specific hook.
3030b5de56gjelinek */
3040b5de56gjelinekstatic int
305ff17c8bgjelinekvalidate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
3060b5de56gjelinek{
307ff17c8bgjelinek	int status;
308ff17c8bgjelinek	char cmdbuf[MAXPATHLEN];
3090b5de56gjelinek
310ff17c8bgjelinek	/* No brand-specific handler */
311ff17c8bgjelinek	if (validsnapbuf[0] == '\0')
312ff17c8bgjelinek		return (Z_OK);
3130b5de56gjelinek
314ff17c8bgjelinek	/* pass args - snapshot_name & snap_path */
315ff17c8bgjelinek	if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
316ff17c8bgjelinek	    snapshot_name, snap_path) >= sizeof (cmdbuf)) {
317ff17c8bgjelinek		zerror("Command line too long");
3180b5de56gjelinek		return (Z_ERR);
3190b5de56gjelinek	}
3200b5de56gjelinek
321ff17c8bgjelinek	/* Run the hook */
322c75cc34<gerald.jelinek@sun.com>	status = do_subproc(cmdbuf);
323ff17c8bgjelinek	if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
324ff17c8bgjelinek	    status, B_FALSE)) != ZONE_SUBPROC_OK)
325ff17c8bgjelinek		return (Z_ERR);
3260b5de56gjelinek
327ff17c8bgjelinek	return (Z_OK);
3280b5de56gjelinek}
3290b5de56gjelinek
3300b5de56gjelinek/*
3310b5de56gjelinek * Remove the sw inventory file from inside this zonepath that we picked up out
3320b5de56gjelinek * of the snapshot.
3330b5de56gjelinek */
3340b5de56gjelinekstatic int
3350b5de56gjelinekclean_out_clone()
3360b5de56gjelinek{
3370b5de56gjelinek	int err;
3380b5de56gjelinek	zone_dochandle_t handle;
3390b5de56gjelinek
3400b5de56gjelinek	if ((handle = zonecfg_init_handle()) == NULL) {
3410b5de56gjelinek		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3420b5de56gjelinek		return (Z_ERR);
3430b5de56gjelinek	}
3440b5de56gjelinek
3450b5de56gjelinek	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3460b5de56gjelinek		errno = err;
3470b5de56gjelinek		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3480b5de56gjelinek		zonecfg_fini_handle(handle);
3490b5de56gjelinek		return (Z_ERR);
3500b5de56gjelinek	}
3510b5de56gjelinek
3520b5de56gjelinek	zonecfg_rm_detached(handle, B_FALSE);
3530b5de56gjelinek	zonecfg_fini_handle(handle);
3540b5de56gjelinek
3550b5de56gjelinek	return (Z_OK);
3560b5de56gjelinek}
3570b5de56gjelinek
3580b5de56gjelinek/*
3590b5de56gjelinek * Make a ZFS clone on zonepath from snapshot_name.
3600b5de56gjelinek */
3610b5de56gjelinekstatic int
3620b5de56gjelinekclone_snap(char *snapshot_name, char *zonepath)
3630b5de56gjelinek{
3640b5de56gjelinek	int		res = Z_OK;
3650b5de56gjelinek	int		err;
3660b5de56gjelinek	zfs_handle_t	*zhp;
3670b5de56gjelinek	zfs_handle_t	*clone;
368e9dbad6eschrock	nvlist_t	*props = NULL;
3690b5de56gjelinek
37099653d4eschrock	if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
3710b5de56gjelinek		return (Z_NO_ENTRY);
3720b5de56gjelinek
3730b5de56gjelinek	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
3740b5de56gjelinek
3752b6c28bbatschul	/*
3762b6c28bbatschul	 * We turn off zfs SHARENFS and SHARESMB properties on the
3772b6c28bbatschul	 * zoneroot dataset in order to prevent the GZ from sharing
3782b6c28bbatschul	 * NGZ data by accident.
3792b6c28bbatschul	 */
3802b6c28bbatschul	if ((nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) ||
3812b6c28bbatschul	    (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
3822b6c28bbatschul	    "off") != 0) ||
3832b6c28bbatschul	    (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARESMB),
3842b6c28bbatschul	    "off") != 0)) {
385aab83bbJosef 'Jeff' Sipek		nvlist_free(props);
386e9dbad6eschrock		(void) fprintf(stderr, gettext("could not create ZFS clone "
387e9dbad6eschrock		    "%s: out of memory\n"), zonepath);
388e9dbad6eschrock		return (Z_ERR);
389e9dbad6eschrock	}
390e9dbad6eschrock
391e9dbad6eschrock	err = zfs_clone(zhp, zonepath, props);
3920b5de56gjelinek	zfs_close(zhp);
393e9dbad6eschrock
394e9dbad6eschrock	nvlist_free(props);
395e9dbad6eschrock
3960b5de56gjelinek	if (err != 0)
3970b5de56gjelinek		return (Z_ERR);
3980b5de56gjelinek
3990b5de56gjelinek	/* create the mountpoint if necessary */
400990b485lling	if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL)
4010b5de56gjelinek		return (Z_ERR);
4020b5de56gjelinek
4030b5de56gjelinek	/*
4040b5de56gjelinek	 * The clone has been created so we need to print a diagnostic
4050b5de56gjelinek	 * message if one of the following steps fails for some reason.
4060b5de56gjelinek	 */
4070b5de56gjelinek	if (zfs_mount(clone, NULL, 0) != 0) {
4080b5de56gjelinek		(void) fprintf(stderr, gettext("could not mount ZFS clone "
4090b5de56gjelinek		    "%s\n"), zfs_get_name(clone));
4100b5de56gjelinek		res = Z_ERR;
4110b5de56gjelinek
412e9dbad6eschrock	} else if (clean_out_clone() != Z_OK) {
413e9dbad6eschrock		(void) fprintf(stderr, gettext("could not remove the "
414e9dbad6eschrock		    "software inventory from ZFS clone %s\n"),
415e9dbad6eschrock		    zfs_get_name(clone));
416e9dbad6eschrock		res = Z_ERR;
4170b5de56gjelinek	}
4180b5de56gjelinek
4190b5de56gjelinek	zfs_close(clone);
4200b5de56gjelinek	return (res);
4210b5de56gjelinek}
4220b5de56gjelinek
4230b5de56gjelinek/*
4240b5de56gjelinek * This function takes a zonepath and attempts to determine what the ZFS
4250b5de56gjelinek * file system name (not mountpoint) should be for that path.  We do not
4260b5de56gjelinek * assume that zonepath is an existing directory or ZFS fs since we use
4270b5de56gjelinek * this function as part of the process of creating a new ZFS fs or clone.
4280b5de56gjelinek *
4290b5de56gjelinek * The way this works is that we look at the parent directory of the zonepath
4300b5de56gjelinek * to see if it is a ZFS fs.  If it is, we get the name of that ZFS fs and
4310b5de56gjelinek * append the last component of the zonepath to generate the ZFS name for the
4320b5de56gjelinek * zonepath.  This matches the algorithm that ZFS uses for automatically
4330b5de56gjelinek * mounting a new fs after it is created.
4340b5de56gjelinek *
4350b5de56gjelinek * Although a ZFS fs can be mounted anywhere, we don't worry about handling
4360b5de56gjelinek * all of the complexity that a user could possibly configure with arbitrary
4370b5de56gjelinek * mounts since there is no way to generate a ZFS name from a random path in
4380b5de56gjelinek * the file system.  We only try to handle the automatic mounts that ZFS does
4390b5de56gjelinek * for each file system.  ZFS restricts this so that a new fs must be created
4400b5de56gjelinek * in an existing parent ZFS fs.  It then automatically mounts the new fs
4410b5de56gjelinek * directly under the mountpoint for the parent fs using the last component
4420b5de56gjelinek * of the name as the mountpoint directory.
4430b5de56gjelinek *
4440b5de56gjelinek * For example:
4450b5de56gjelinek *    Name			Mountpoint
4460b5de56gjelinek *    space/eng/dev/test/zone1	/project1/eng/dev/test/zone1
4470b5de56gjelinek *
4480b5de56gjelinek * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
4490b5de56gjelinek * Z_ERR.
4500b5de56gjelinek */
4510b5de56gjelinekstatic int
4520b5de56gjelinekpath2name(char *zonepath, char *zfs_name, int len)
4530b5de56gjelinek{
4540b5de56gjelinek	int		res;
45511506c4gjelinek	char		*bnm, *dnm, *dname, *bname;
4560b5de56gjelinek	zfs_handle_t	*zhp;
45711506c4gjelinek	struct stat	stbuf;
45811506c4gjelinek
45911506c4gjelinek	/*
46011506c4gjelinek	 * We need two tmp strings to handle paths directly in / (e.g. /foo)
46111506c4gjelinek	 * since dirname will overwrite the first char after "/" in this case.
46211506c4gjelinek	 */
46311506c4gjelinek	if ((bnm = strdup(zonepath)) == NULL)
46411506c4gjelinek		return (Z_ERR);
4650b5de56gjelinek
46611506c4gjelinek	if ((dnm = strdup(zonepath)) == NULL) {
46711506c4gjelinek		free(bnm);
4680b5de56gjelinek		return (Z_ERR);
46911506c4gjelinek	}
47011506c4gjelinek
47111506c4gjelinek	bname = basename(bnm);
47211506c4gjelinek	dname = dirname(dnm);
4730b5de56gjelinek
4740b5de56gjelinek	/*
47511506c4gjelinek	 * This is a quick test to save iterating over all of the zfs datasets
47611506c4gjelinek	 * on the system (which can be a lot).  If the parent dir is not in a
47711506c4gjelinek	 * ZFS fs, then we're done.
4780b5de56gjelinek	 */
47911506c4gjelinek	if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) ||
48011506c4gjelinek	    strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) {
48111506c4gjelinek		free(bnm);
48211506c4gjelinek		free(dnm);
4830b5de56gjelinek		return (Z_ERR);
48411506c4gjelinek	}
48511506c4gjelinek
48611506c4gjelinek	/* See if the parent directory is its own ZFS dataset. */
48711506c4gjelinek	if ((zhp = mount2zhandle(dname)) == NULL) {
48811506c4gjelinek		/*
48911506c4gjelinek		 * The parent is not a ZFS dataset so we can't automatically
49011506c4gjelinek		 * create a dataset on the given path.
49111506c4gjelinek		 */
49211506c4gjelinek		free(bnm);
49311506c4gjelinek		free(dnm);
49411506c4gjelinek		return (Z_ERR);
49511506c4gjelinek	}
4960b5de56gjelinek
49711506c4gjelinek	res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname);
4980b5de56gjelinek
49911506c4gjelinek	free(bnm);
50011506c4gjelinek	free(dnm);
5010b5de56gjelinek	zfs_close(zhp);
5020b5de56gjelinek	if (res >= len)
5030b5de56gjelinek		return (Z_ERR);
5040b5de56gjelinek
5050b5de56gjelinek	return (Z_OK);
5060b5de56gjelinek}
5070b5de56gjelinek
5080b5de56gjelinek/*
5090b5de56gjelinek * A ZFS file system iterator call-back function used to determine if the
5100b5de56gjelinek * file system has dependents (snapshots & clones).
5110b5de56gjelinek */
5120b5de56gjelinek/* ARGSUSED */
5130b5de56gjelinekstatic int
5140b5de56gjelinekhas_dependent(zfs_handle_t *zhp, void *data)
5150b5de56gjelinek{
5160b5de56gjelinek	zfs_close(zhp);
5170b5de56gjelinek	return (1);
5180b5de56gjelinek}
5190b5de56gjelinek
5200b5de56gjelinek/*
5210b5de56gjelinek * Given a snapshot name, get the file system path where the snapshot lives.
5220b5de56gjelinek * A snapshot name is of the form fs_name@snap_name.  For example, snapshot
5230b5de56gjelinek * pl/zones/z1@SUNWzone1 would have a path of
5240b5de56gjelinek * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5250b5de56gjelinek */
5260b5de56gjelinekstatic int
5270b5de56gjelineksnap2path(char *snap_name, char *path, int len)
5280b5de56gjelinek{
5290b5de56gjelinek	char		*p;
5300b5de56gjelinek	zfs_handle_t	*zhp;
5310b5de56gjelinek	char		mp[ZFS_MAXPROPLEN];
5320b5de56gjelinek
5330b5de56gjelinek	if ((p = strrchr(snap_name, '@')) == NULL)
5340b5de56gjelinek		return (Z_ERR);
5350b5de56gjelinek
5360b5de56gjelinek	/* Get the file system name from the snap_name. */
5370b5de56gjelinek	*p = '\0';
538990b485lling	zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET);
5390b5de56gjelinek	*p = '@';
5400b5de56gjelinek	if (zhp == NULL)
5410b5de56gjelinek		return (Z_ERR);
5420b5de56gjelinek
5430b5de56gjelinek	/* Get the file system mount point. */
5440b5de56gjelinek	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
54599653d4eschrock	    0, B_FALSE) != 0) {
5460b5de56gjelinek		zfs_close(zhp);
5470b5de56gjelinek		return (Z_ERR);
5480b5de56gjelinek	}
5490b5de56gjelinek	zfs_close(zhp);
5500b5de56gjelinek
5510b5de56gjelinek	p++;
5520b5de56gjelinek	if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5530b5de56gjelinek		return (Z_ERR);
5540b5de56gjelinek
5550b5de56gjelinek	return (Z_OK);
5560b5de56gjelinek}
5570b5de56gjelinek
5580b5de56gjelinek/*
559286822d<gerald.jelinek@sun.com> * This callback function is used to iterate through a snapshot's dependencies
560286822d<gerald.jelinek@sun.com> * to find a filesystem that is a direct clone of the snapshot being iterated.
561286822d<gerald.jelinek@sun.com> */
562286822d<gerald.jelinek@sun.com>static int
563286822d<gerald.jelinek@sun.com>get_direct_clone(zfs_handle_t *zhp, void *data)
564286822d<gerald.jelinek@sun.com>{
565286822d<gerald.jelinek@sun.com>	clone_data_t	*cd = data;
5669adfa60Matthew Ahrens	char		origin[ZFS_MAX_DATASET_NAME_LEN];
5679adfa60Matthew Ahrens	char		ds_path[ZFS_MAX_DATASET_NAME_LEN];
568286822d<gerald.jelinek@sun.com>
569286822d<gerald.jelinek@sun.com>	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
570286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
571286822d<gerald.jelinek@sun.com>		return (0);
572286822d<gerald.jelinek@sun.com>	}
573286822d<gerald.jelinek@sun.com>
574286822d<gerald.jelinek@sun.com>	(void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
575286822d<gerald.jelinek@sun.com>
576286822d<gerald.jelinek@sun.com>	/* Make sure this is a direct clone of the snapshot we're iterating. */
577286822d<gerald.jelinek@sun.com>	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
578286822d<gerald.jelinek@sun.com>	    NULL, 0, B_FALSE) != 0 || strcmp(origin, cd->snapshot) != 0) {
579286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
580286822d<gerald.jelinek@sun.com>		return (0);
581286822d<gerald.jelinek@sun.com>	}
582286822d<gerald.jelinek@sun.com>
583286822d<gerald.jelinek@sun.com>	if (cd->clone_zhp != NULL)
584286822d<gerald.jelinek@sun.com>		zfs_close(cd->clone_zhp);
585286822d<gerald.jelinek@sun.com>
586286822d<gerald.jelinek@sun.com>	cd->clone_zhp = zhp;
587286822d<gerald.jelinek@sun.com>	return (1);
588286822d<gerald.jelinek@sun.com>}
589286822d<gerald.jelinek@sun.com>
590286822d<gerald.jelinek@sun.com>/*
591286822d<gerald.jelinek@sun.com> * A ZFS file system iterator call-back function used to determine the clone
592286822d<gerald.jelinek@sun.com> * to promote.  This function finds the youngest (i.e. last one taken) snapshot
593286822d<gerald.jelinek@sun.com> * that has a clone.  If found, it returns a reference to that clone in the
594286822d<gerald.jelinek@sun.com> * callback data.
595286822d<gerald.jelinek@sun.com> */
596286822d<gerald.jelinek@sun.com>static int
597286822d<gerald.jelinek@sun.com>find_clone(zfs_handle_t *zhp, void *data)
598286822d<gerald.jelinek@sun.com>{
599286822d<gerald.jelinek@sun.com>	clone_data_t	*cd = data;
600286822d<gerald.jelinek@sun.com>	time_t		snap_creation;
601286822d<gerald.jelinek@sun.com>	int		zret = 0;
602286822d<gerald.jelinek@sun.com>
603286822d<gerald.jelinek@sun.com>	/* If snapshot has no clones, skip it */
604286822d<gerald.jelinek@sun.com>	if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) {
605286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
606286822d<gerald.jelinek@sun.com>		return (0);
607286822d<gerald.jelinek@sun.com>	}
608286822d<gerald.jelinek@sun.com>
609286822d<gerald.jelinek@sun.com>	cd->snapshot = zfs_get_name(zhp);
610286822d<gerald.jelinek@sun.com>
611286822d<gerald.jelinek@sun.com>	/* Get the creation time of this snapshot */
612286822d<gerald.jelinek@sun.com>	snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
613286822d<gerald.jelinek@sun.com>
614286822d<gerald.jelinek@sun.com>	/*
615286822d<gerald.jelinek@sun.com>	 * If this snapshot's creation time is greater than (i.e. younger than)
616286822d<gerald.jelinek@sun.com>	 * the current youngest snapshot found, iterate this snapshot to
617286822d<gerald.jelinek@sun.com>	 * get the right clone.
618286822d<gerald.jelinek@sun.com>	 */
619286822d<gerald.jelinek@sun.com>	if (snap_creation >= cd->origin_creation) {
620286822d<gerald.jelinek@sun.com>		/*
621286822d<gerald.jelinek@sun.com>		 * Iterate the dependents of this snapshot to find a clone
622286822d<gerald.jelinek@sun.com>		 * that's a direct dependent.
623286822d<gerald.jelinek@sun.com>		 */
624286822d<gerald.jelinek@sun.com>		if ((zret = zfs_iter_dependents(zhp, B_FALSE, get_direct_clone,
625286822d<gerald.jelinek@sun.com>		    cd)) == -1) {
626286822d<gerald.jelinek@sun.com>			zfs_close(zhp);
627286822d<gerald.jelinek@sun.com>			return (1);
628286822d<gerald.jelinek@sun.com>		} else if (zret == 1) {
629286822d<gerald.jelinek@sun.com>			/*
630286822d<gerald.jelinek@sun.com>			 * Found a clone, update the origin_creation time
631286822d<gerald.jelinek@sun.com>			 * in the callback data.
632286822d<gerald.jelinek@sun.com>			 */
633286822d<gerald.jelinek@sun.com>			cd->origin_creation = snap_creation;
634286822d<gerald.jelinek@sun.com>		}
635286822d<gerald.jelinek@sun.com>	}
636286822d<gerald.jelinek@sun.com>
637286822d<gerald.jelinek@sun.com>	zfs_close(zhp);
638286822d<gerald.jelinek@sun.com>	return (0);
639286822d<gerald.jelinek@sun.com>}
640286822d<gerald.jelinek@sun.com>
641286822d<gerald.jelinek@sun.com>/*
642286822d<gerald.jelinek@sun.com> * A ZFS file system iterator call-back function used to remove standalone
643286822d<gerald.jelinek@sun.com> * snapshots.
644286822d<gerald.jelinek@sun.com> */
645286822d<gerald.jelinek@sun.com>/* ARGSUSED */
646286822d<gerald.jelinek@sun.com>static int
647286822d<gerald.jelinek@sun.com>rm_snap(zfs_handle_t *zhp, void *data)
648286822d<gerald.jelinek@sun.com>{
649286822d<gerald.jelinek@sun.com>	/* If snapshot has clones, something is wrong */
650286822d<gerald.jelinek@sun.com>	if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) {
651286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
652286822d<gerald.jelinek@sun.com>		return (1);
653286822d<gerald.jelinek@sun.com>	}
654286822d<gerald.jelinek@sun.com>
655286822d<gerald.jelinek@sun.com>	if (zfs_unmount(zhp, NULL, 0) == 0) {
656842727cChris Kirby		(void) zfs_destroy(zhp, B_FALSE);
657286822d<gerald.jelinek@sun.com>	}
658286822d<gerald.jelinek@sun.com>
659286822d<gerald.jelinek@sun.com>	zfs_close(zhp);
660286822d<gerald.jelinek@sun.com>	return (0);
661286822d<gerald.jelinek@sun.com>}
662286822d<gerald.jelinek@sun.com>
663286822d<gerald.jelinek@sun.com>/*
664286822d<gerald.jelinek@sun.com> * A ZFS snapshot iterator call-back function which renames snapshots.
665286822d<gerald.jelinek@sun.com> */
666286822d<gerald.jelinek@sun.com>static int
667286822d<gerald.jelinek@sun.com>rename_snap(zfs_handle_t *zhp, void *data)
668286822d<gerald.jelinek@sun.com>{
669286822d<gerald.jelinek@sun.com>	int			res;
670286822d<gerald.jelinek@sun.com>	zfs_snapshot_data_t	*cbp;
6719adfa60Matthew Ahrens	char			template[ZFS_MAX_DATASET_NAME_LEN];
672286822d<gerald.jelinek@sun.com>
673286822d<gerald.jelinek@sun.com>	cbp = (zfs_snapshot_data_t *)data;
674286822d<gerald.jelinek@sun.com>
675286822d<gerald.jelinek@sun.com>	/*
676286822d<gerald.jelinek@sun.com>	 * When renaming snapshots with the iterator, the iterator can see
677286822d<gerald.jelinek@sun.com>	 * the same snapshot after we've renamed up in the namespace.  To
678286822d<gerald.jelinek@sun.com>	 * prevent this we check the count for the number of snapshots we have
679286822d<gerald.jelinek@sun.com>	 * to rename and stop at that point.
680286822d<gerald.jelinek@sun.com>	 */
681286822d<gerald.jelinek@sun.com>	if (cbp->cntr >= cbp->num) {
682286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
683286822d<gerald.jelinek@sun.com>		return (0);
684286822d<gerald.jelinek@sun.com>	}
685286822d<gerald.jelinek@sun.com>
686286822d<gerald.jelinek@sun.com>	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
687286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
688286822d<gerald.jelinek@sun.com>		return (0);
689286822d<gerald.jelinek@sun.com>	}
690286822d<gerald.jelinek@sun.com>
691286822d<gerald.jelinek@sun.com>	/* Only rename the snapshots we automatically generate when we clone. */
692286822d<gerald.jelinek@sun.com>	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) != 0) {
693286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
694286822d<gerald.jelinek@sun.com>		return (0);
695286822d<gerald.jelinek@sun.com>	}
696286822d<gerald.jelinek@sun.com>
697286822d<gerald.jelinek@sun.com>	(void) snprintf(template, sizeof (template), "%s%d", cbp->match_name,
698286822d<gerald.jelinek@sun.com>	    cbp->max++);
699286822d<gerald.jelinek@sun.com>
7006a9cb0eEric Schrock	res = (zfs_rename(zhp, template, B_FALSE, B_FALSE) != 0);
701286822d<gerald.jelinek@sun.com>	if (res != 0)
702286822d<gerald.jelinek@sun.com>		(void) fprintf(stderr, gettext("failed to rename snapshot %s "
703286822d<gerald.jelinek@sun.com>		    "to %s: %s\n"), zfs_get_name(zhp), template,
704286822d<gerald.jelinek@sun.com>		    libzfs_error_description(g_zfs));
705286822d<gerald.jelinek@sun.com>
706286822d<gerald.jelinek@sun.com>	cbp->cntr++;
707286822d<gerald.jelinek@sun.com>
708286822d<gerald.jelinek@sun.com>	zfs_close(zhp);
709286822d<gerald.jelinek@sun.com>	return (res);
710286822d<gerald.jelinek@sun.com>}
711286822d<gerald.jelinek@sun.com>
712286822d<gerald.jelinek@sun.com>/*
713286822d<gerald.jelinek@sun.com> * Rename the source dataset's snapshots that are automatically generated when
714286822d<gerald.jelinek@sun.com> * we clone a zone so that there won't be a name collision when we promote the
715286822d<gerald.jelinek@sun.com> * cloned dataset.  Once the snapshots have been renamed, then promote the
716286822d<gerald.jelinek@sun.com> * clone.
717286822d<gerald.jelinek@sun.com> *
718286822d<gerald.jelinek@sun.com> * The snapshot rename process gets the highest number on the snapshot names
719286822d<gerald.jelinek@sun.com> * (the format is zonename@SUNWzoneXX where XX are digits) on both the source
720286822d<gerald.jelinek@sun.com> * and clone datasets, then renames the source dataset snapshots starting at
721286822d<gerald.jelinek@sun.com> * the next number.
722286822d<gerald.jelinek@sun.com> */
723286822d<gerald.jelinek@sun.com>static int
724286822d<gerald.jelinek@sun.com>promote_clone(zfs_handle_t *src_zhp, zfs_handle_t *cln_zhp)
725286822d<gerald.jelinek@sun.com>{
726286822d<gerald.jelinek@sun.com>	zfs_snapshot_data_t	sd;
7279adfa60Matthew Ahrens	char			nm[ZFS_MAX_DATASET_NAME_LEN];
7289adfa60Matthew Ahrens	char			template[ZFS_MAX_DATASET_NAME_LEN];
729286822d<gerald.jelinek@sun.com>
730286822d<gerald.jelinek@sun.com>	(void) strlcpy(nm, zfs_get_name(cln_zhp), sizeof (nm));
731286822d<gerald.jelinek@sun.com>	/*
732286822d<gerald.jelinek@sun.com>	 * Start by getting the clone's snapshot max which we use
733286822d<gerald.jelinek@sun.com>	 * during the rename of the original dataset's snapshots.
734286822d<gerald.jelinek@sun.com>	 */
735286822d<gerald.jelinek@sun.com>	(void) snprintf(template, sizeof (template), "%s@SUNWzone", nm);
736286822d<gerald.jelinek@sun.com>	sd.match_name = template;
737286822d<gerald.jelinek@sun.com>	sd.len = strlen(template);
738286822d<gerald.jelinek@sun.com>	sd.max = 0;
739286822d<gerald.jelinek@sun.com>
7400d8fa8fMartin Matuska	if (zfs_iter_snapshots(cln_zhp, B_FALSE, get_snap_max, &sd) != 0)
741286822d<gerald.jelinek@sun.com>		return (Z_ERR);
742286822d<gerald.jelinek@sun.com>
743286822d<gerald.jelinek@sun.com>	/*
744286822d<gerald.jelinek@sun.com>	 * Now make sure the source's snapshot max is at least as high as
745286822d<gerald.jelinek@sun.com>	 * the clone's snapshot max.
746286822d<gerald.jelinek@sun.com>	 */
747286822d<gerald.jelinek@sun.com>	(void) snprintf(template, sizeof (template), "%s@SUNWzone",
748286822d<gerald.jelinek@sun.com>	    zfs_get_name(src_zhp));
749286822d<gerald.jelinek@sun.com>	sd.match_name = template;
750286822d<gerald.jelinek@sun.com>	sd.len = strlen(template);
751286822d<gerald.jelinek@sun.com>	sd.num = 0;
752286822d<gerald.jelinek@sun.com>
7530d8fa8fMartin Matuska	if (zfs_iter_snapshots(src_zhp, B_FALSE, get_snap_max, &sd) != 0)
754286822d<gerald.jelinek@sun.com>		return (Z_ERR);
755286822d<gerald.jelinek@sun.com>
756286822d<gerald.jelinek@sun.com>	/*
757286822d<gerald.jelinek@sun.com>	 * Now rename the source dataset's snapshots so there's no
758286822d<gerald.jelinek@sun.com>	 * conflict when we promote the clone.
759286822d<gerald.jelinek@sun.com>	 */
760286822d<gerald.jelinek@sun.com>	sd.max++;
761286822d<gerald.jelinek@sun.com>	sd.cntr = 0;
7620d8fa8fMartin Matuska	if (zfs_iter_snapshots(src_zhp, B_FALSE, rename_snap, &sd) != 0)
763286822d<gerald.jelinek@sun.com>		return (Z_ERR);
764286822d<gerald.jelinek@sun.com>
765286822d<gerald.jelinek@sun.com>	/* close and reopen the clone dataset to get the latest info */
766286822d<gerald.jelinek@sun.com>	zfs_close(cln_zhp);
767286822d<gerald.jelinek@sun.com>	if ((cln_zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
768286822d<gerald.jelinek@sun.com>		return (Z_ERR);
769286822d<gerald.jelinek@sun.com>
770286822d<gerald.jelinek@sun.com>	if (zfs_promote(cln_zhp) != 0) {
771286822d<gerald.jelinek@sun.com>		(void) fprintf(stderr, gettext("failed to promote %s: %s\n"),
772286822d<gerald.jelinek@sun.com>		    nm, libzfs_error_description(g_zfs));
773286822d<gerald.jelinek@sun.com>		return (Z_ERR);
774286822d<gerald.jelinek@sun.com>	}
775286822d<gerald.jelinek@sun.com>
776286822d<gerald.jelinek@sun.com>	zfs_close(cln_zhp);
777286822d<gerald.jelinek@sun.com>	return (Z_OK);
778286822d<gerald.jelinek@sun.com>}
779286822d<gerald.jelinek@sun.com>
780286822d<gerald.jelinek@sun.com>/*
781286822d<gerald.jelinek@sun.com> * Promote the youngest clone.  That clone will then become the origin of all
782286822d<gerald.jelinek@sun.com> * of the other clones that were hanging off of the source dataset.
783286822d<gerald.jelinek@sun.com> */
784286822d<gerald.jelinek@sun.com>int
785286822d<gerald.jelinek@sun.com>promote_all_clones(zfs_handle_t *zhp)
786286822d<gerald.jelinek@sun.com>{
787286822d<gerald.jelinek@sun.com>	clone_data_t	cd;
7889adfa60Matthew Ahrens	char		nm[ZFS_MAX_DATASET_NAME_LEN];
789286822d<gerald.jelinek@sun.com>
790286822d<gerald.jelinek@sun.com>	cd.clone_zhp = NULL;
791286822d<gerald.jelinek@sun.com>	cd.origin_creation = 0;
792286822d<gerald.jelinek@sun.com>	cd.snapshot = NULL;
793286822d<gerald.jelinek@sun.com>
7940d8fa8fMartin Matuska	if (zfs_iter_snapshots(zhp, B_FALSE, find_clone, &cd) != 0) {
795286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
796286822d<gerald.jelinek@sun.com>		return (Z_ERR);
797286822d<gerald.jelinek@sun.com>	}
798286822d<gerald.jelinek@sun.com>
799286822d<gerald.jelinek@sun.com>	/* Nothing to promote. */
800286822d<gerald.jelinek@sun.com>	if (cd.clone_zhp == NULL)
801286822d<gerald.jelinek@sun.com>		return (Z_OK);
802286822d<gerald.jelinek@sun.com>
803286822d<gerald.jelinek@sun.com>	/* Found the youngest clone to promote.  Promote it. */
804286822d<gerald.jelinek@sun.com>	if (promote_clone(zhp, cd.clone_zhp) != 0) {
805286822d<gerald.jelinek@sun.com>		zfs_close(cd.clone_zhp);
806286822d<gerald.jelinek@sun.com>		zfs_close(zhp);
807286822d<gerald.jelinek@sun.com>		return (Z_ERR);
808286822d<gerald.jelinek@sun.com>	}
809286822d<gerald.jelinek@sun.com>
810286822d<gerald.jelinek@sun.com>	/* close and reopen the main dataset to get the latest info */
811286822d<gerald.jelinek@sun.com>	(void) strlcpy(nm, zfs_get_name(zhp), sizeof (nm));
812286822d<gerald.jelinek@sun.com>	zfs_close(zhp);
813286822d<gerald.jelinek@sun.com>	if ((zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
814286822d<gerald.jelinek@sun.com>		return (Z_ERR);
815286822d<gerald.jelinek@sun.com>
816286822d<gerald.jelinek@sun.com>	return (Z_OK);
817286822d<gerald.jelinek@sun.com>}
818286822d<gerald.jelinek@sun.com>
819286822d<gerald.jelinek@sun.com>/*
8200b5de56gjelinek * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
8210b5de56gjelinek * possible, or by copying the data from the snapshot to the zonepath.
8220b5de56gjelinek */
8230b5de56gjelinekint
824ff17c8bgjelinekclone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
8250b5de56gjelinek{
8260b5de56gjelinek	int	err = Z_OK;
8270b5de56gjelinek	char	clone_name[MAXPATHLEN];
8280b5de56gjelinek	char	snap_path[MAXPATHLEN];
8290b5de56gjelinek
8300b5de56gjelinek	if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
8310b5de56gjelinek		(void) fprintf(stderr, gettext("unable to find path for %s.\n"),
8320b5de56gjelinek		    snap_name);
8330b5de56gjelinek		return (Z_ERR);
8340b5de56gjelinek	}
8350b5de56gjelinek
836ff17c8bgjelinek	if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
8370b5de56gjelinek		return (Z_NO_ENTRY);
8380b5de56gjelinek
8390b5de56gjelinek	/*
8400b5de56gjelinek	 * The zonepath cannot be ZFS cloned, try to copy the data from
8410b5de56gjelinek	 * within the snapshot to the zonepath.
8420b5de56gjelinek	 */
8430b5de56gjelinek	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
8440b5de56gjelinek		if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8450b5de56gjelinek			if (clean_out_clone() != Z_OK)
8460b5de56gjelinek				(void) fprintf(stderr,
8470b5de56gjelinek				    gettext("could not remove the "
8480b5de56gjelinek				    "software inventory from %s\n"), zonepath);
8490b5de56gjelinek
8500b5de56gjelinek		return (err);
8510b5de56gjelinek	}
8520b5de56gjelinek
8530b5de56gjelinek	if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
8540b5de56gjelinek		if (err != Z_NO_ENTRY) {
8550b5de56gjelinek			/*
8560b5de56gjelinek			 * Cloning the snapshot failed.  Fall back to trying
8570b5de56gjelinek			 * to install the zone by copying from the snapshot.
8580b5de56gjelinek			 */
8590b5de56gjelinek			if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8600b5de56gjelinek				if (clean_out_clone() != Z_OK)
8610b5de56gjelinek					(void) fprintf(stderr,
8620b5de56gjelinek					    gettext("could not remove the "
8630b5de56gjelinek					    "software inventory from %s\n"),
8640b5de56gjelinek					    zonepath);
8650b5de56gjelinek		} else {
8660b5de56gjelinek			/*
8670b5de56gjelinek			 * The snapshot is unusable for some reason so restore
8680b5de56gjelinek			 * the zone state to configured since we were unable to
8690b5de56gjelinek			 * actually do anything about getting the zone
8700b5de56gjelinek			 * installed.
8710b5de56gjelinek			 */
8720b5de56gjelinek			int tmp;
8730b5de56gjelinek
8740b5de56gjelinek			if ((tmp = zone_set_state(target_zone,
8750b5de56gjelinek			    ZONE_STATE_CONFIGURED)) != Z_OK) {
8760b5de56gjelinek				errno = tmp;
8770b5de56gjelinek				zperror2(target_zone,
8780b5de56gjelinek				    gettext("could not set state"));
8790b5de56gjelinek			}
8800b5de56gjelinek		}
8810b5de56gjelinek	}
8820b5de56gjelinek
8830b5de56gjelinek	return (err);
8840b5de56gjelinek}
8850b5de56gjelinek
8860b5de56gjelinek/*
8870b5de56gjelinek * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
8880b5de56gjelinek */
8890b5de56gjelinekint
890ff17c8bgjelinekclone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
891ff17c8bgjelinek    char *postsnapbuf)
8920b5de56gjelinek{
8930b5de56gjelinek	zfs_handle_t	*zhp;
8940b5de56gjelinek	char		clone_name[MAXPATHLEN];
8950b5de56gjelinek	char		snap_name[MAXPATHLEN];
8960b5de56gjelinek
8970b5de56gjelinek	/*
8980b5de56gjelinek	 * Try to get a zfs handle for the source_zonepath.  If this fails
8990b5de56gjelinek	 * the source_zonepath is not ZFS so return an error.
9000b5de56gjelinek	 */
9010b5de56gjelinek	if ((zhp = mount2zhandle(source_zonepath)) == NULL)
9020b5de56gjelinek		return (Z_ERR);
9030b5de56gjelinek
9040b5de56gjelinek	/*
9050b5de56gjelinek	 * Check if there is a file system already mounted on zonepath.  If so,
9060b5de56gjelinek	 * we can't clone to the path so we should fall back to copying.
9070b5de56gjelinek	 */
9080b5de56gjelinek	if (is_mountpnt(zonepath)) {
9090b5de56gjelinek		zfs_close(zhp);
9100b5de56gjelinek		(void) fprintf(stderr,
9110b5de56gjelinek		    gettext("A file system is already mounted on %s,\n"
9120b5de56gjelinek		    "preventing use of a ZFS clone.\n"), zonepath);
9130b5de56gjelinek		return (Z_ERR);
9140b5de56gjelinek	}
9150b5de56gjelinek
9160b5de56gjelinek	/*
9170b5de56gjelinek	 * Instead of using path2name to get the clone name from the zonepath,
918