xref: /titanic_53/usr/src/cmd/zoneadm/zfs.c (revision 8cd327d5cbf74ddf7659863fac9475546ccd58ce)
10b5de56dSgjelinek /*
20b5de56dSgjelinek  * CDDL HEADER START
30b5de56dSgjelinek  *
40b5de56dSgjelinek  * The contents of this file are subject to the terms of the
50b5de56dSgjelinek  * Common Development and Distribution License (the "License").
60b5de56dSgjelinek  * You may not use this file except in compliance with the License.
70b5de56dSgjelinek  *
80b5de56dSgjelinek  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90b5de56dSgjelinek  * or http://www.opensolaris.org/os/licensing.
100b5de56dSgjelinek  * See the License for the specific language governing permissions
110b5de56dSgjelinek  * and limitations under the License.
120b5de56dSgjelinek  *
130b5de56dSgjelinek  * When distributing Covered Code, include this CDDL HEADER in each
140b5de56dSgjelinek  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150b5de56dSgjelinek  * If applicable, add the following below this CDDL HEADER, with the
160b5de56dSgjelinek  * fields enclosed by brackets "[]" replaced with your own identifying
170b5de56dSgjelinek  * information: Portions Copyright [yyyy] [name of copyright owner]
180b5de56dSgjelinek  *
190b5de56dSgjelinek  * CDDL HEADER END
200b5de56dSgjelinek  */
210b5de56dSgjelinek 
220b5de56dSgjelinek /*
230b5de56dSgjelinek  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
240b5de56dSgjelinek  * Use is subject to license terms.
250b5de56dSgjelinek  */
260b5de56dSgjelinek 
270b5de56dSgjelinek #pragma ident	"%Z%%M%	%I%	%E% SMI"
280b5de56dSgjelinek 
290b5de56dSgjelinek /*
300b5de56dSgjelinek  * This file contains the functions used to support the ZFS integration
310b5de56dSgjelinek  * with zones.  This includes validation (e.g. zonecfg dataset), cloning,
320b5de56dSgjelinek  * file system creation and destruction.
330b5de56dSgjelinek  */
340b5de56dSgjelinek 
350b5de56dSgjelinek #include <stdio.h>
360b5de56dSgjelinek #include <errno.h>
370b5de56dSgjelinek #include <unistd.h>
380b5de56dSgjelinek #include <string.h>
390b5de56dSgjelinek #include <locale.h>
400b5de56dSgjelinek #include <libintl.h>
410b5de56dSgjelinek #include <sys/stat.h>
420b5de56dSgjelinek #include <sys/statvfs.h>
430b5de56dSgjelinek #include <libgen.h>
440b5de56dSgjelinek #include <libzonecfg.h>
450b5de56dSgjelinek #include <sys/mnttab.h>
460b5de56dSgjelinek #include <libzfs.h>
470b5de56dSgjelinek 
480b5de56dSgjelinek #include "zoneadm.h"
490b5de56dSgjelinek 
500b5de56dSgjelinek static const char *current_dataset;
510b5de56dSgjelinek 
520b5de56dSgjelinek typedef struct zfs_mount_data {
530b5de56dSgjelinek 	char		*match_name;
540b5de56dSgjelinek 	zfs_handle_t	*match_handle;
550b5de56dSgjelinek } zfs_mount_data_t;
560b5de56dSgjelinek 
570b5de56dSgjelinek typedef struct zfs_snapshot_data {
580b5de56dSgjelinek 	char	*match_name;
590b5de56dSgjelinek 	int	len;
600b5de56dSgjelinek 	int	max;
610b5de56dSgjelinek } zfs_snapshot_data_t;
620b5de56dSgjelinek 
630b5de56dSgjelinek /*
640b5de56dSgjelinek  * ZFS error handler to do nothing - do not print the libzfs error messages.
650b5de56dSgjelinek  */
660b5de56dSgjelinek /* ARGSUSED */
670b5de56dSgjelinek static void
680b5de56dSgjelinek noop_err_handler(const char *fmt, va_list ap)
690b5de56dSgjelinek {
700b5de56dSgjelinek }
710b5de56dSgjelinek 
720b5de56dSgjelinek /*
730b5de56dSgjelinek  * Custom error handler for errors incurred as part of verifying datasets.  We
740b5de56dSgjelinek  * want to trim off the leading 'cannot open ...' to create a better error
750b5de56dSgjelinek  * message.  The only other way this can fail is if we fail to set the 'zoned'
760b5de56dSgjelinek  * property.  In this case we just pass the error on verbatim.
770b5de56dSgjelinek  */
780b5de56dSgjelinek static void
790b5de56dSgjelinek err_handler(const char *fmt, va_list ap)
800b5de56dSgjelinek {
810b5de56dSgjelinek 	char buf[1024];
820b5de56dSgjelinek 
830b5de56dSgjelinek 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
840b5de56dSgjelinek 
850b5de56dSgjelinek 	if (strncmp(gettext("cannot open "), buf,
860b5de56dSgjelinek 	    strlen(gettext("cannot open "))) == 0)
870b5de56dSgjelinek 		/*
880b5de56dSgjelinek 		 * TRANSLATION_NOTE
890b5de56dSgjelinek 		 * zfs and dataset are literals that should not be translated.
900b5de56dSgjelinek 		 */
910b5de56dSgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs "
920b5de56dSgjelinek 		    "dataset %s%s\n"), current_dataset, strchr(buf, ':'));
930b5de56dSgjelinek 	else
940b5de56dSgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs dataset "
950b5de56dSgjelinek 		    "%s: %s\n"), current_dataset, buf);
960b5de56dSgjelinek }
970b5de56dSgjelinek 
980b5de56dSgjelinek /*
990b5de56dSgjelinek  * A ZFS file system iterator call-back function which is used to validate
1000b5de56dSgjelinek  * datasets imported into the zone.
1010b5de56dSgjelinek  */
1020b5de56dSgjelinek /* ARGSUSED */
1030b5de56dSgjelinek static int
1040b5de56dSgjelinek check_zvol(zfs_handle_t *zhp, void *unused)
1050b5de56dSgjelinek {
1060b5de56dSgjelinek 	int ret;
1070b5de56dSgjelinek 
1080b5de56dSgjelinek 	if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
1090b5de56dSgjelinek 		/*
1100b5de56dSgjelinek 		 * TRANSLATION_NOTE
1110b5de56dSgjelinek 		 * zfs and dataset are literals that should not be translated.
1120b5de56dSgjelinek 		 */
1130b5de56dSgjelinek 		(void) fprintf(stderr, gettext("cannot verify zfs dataset %s: "
1140b5de56dSgjelinek 		    "volumes cannot be specified as a zone dataset resource\n"),
1150b5de56dSgjelinek 		    zfs_get_name(zhp));
1160b5de56dSgjelinek 		ret = -1;
1170b5de56dSgjelinek 	} else {
1180b5de56dSgjelinek 		ret = zfs_iter_children(zhp, check_zvol, NULL);
1190b5de56dSgjelinek 	}
1200b5de56dSgjelinek 
1210b5de56dSgjelinek 	zfs_close(zhp);
1220b5de56dSgjelinek 
1230b5de56dSgjelinek 	return (ret);
1240b5de56dSgjelinek }
1250b5de56dSgjelinek 
1260b5de56dSgjelinek /*
1270b5de56dSgjelinek  * A ZFS file system iterator call-back function which returns the
1280b5de56dSgjelinek  * zfs_handle_t for a ZFS file system on the specified mount point.
1290b5de56dSgjelinek  */
1300b5de56dSgjelinek static int
1310b5de56dSgjelinek match_mountpoint(zfs_handle_t *zhp, void *data)
1320b5de56dSgjelinek {
1330b5de56dSgjelinek 	int			res;
1340b5de56dSgjelinek 	zfs_mount_data_t	*cbp;
1350b5de56dSgjelinek 	char			mp[ZFS_MAXPROPLEN];
1360b5de56dSgjelinek 
1370b5de56dSgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1380b5de56dSgjelinek 		zfs_close(zhp);
1390b5de56dSgjelinek 		return (0);
1400b5de56dSgjelinek 	}
1410b5de56dSgjelinek 
1420b5de56dSgjelinek 	cbp = (zfs_mount_data_t *)data;
1430b5de56dSgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
1440b5de56dSgjelinek 	    0, FALSE) == 0 && strcmp(mp, cbp->match_name) == 0) {
1450b5de56dSgjelinek 		cbp->match_handle = zhp;
1460b5de56dSgjelinek 		return (1);
1470b5de56dSgjelinek 	}
1480b5de56dSgjelinek 
1490b5de56dSgjelinek 	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1500b5de56dSgjelinek 	zfs_close(zhp);
1510b5de56dSgjelinek 	return (res);
1520b5de56dSgjelinek }
1530b5de56dSgjelinek 
1540b5de56dSgjelinek /*
1550b5de56dSgjelinek  * Get ZFS handle for the specified mount point.
1560b5de56dSgjelinek  */
1570b5de56dSgjelinek static zfs_handle_t *
1580b5de56dSgjelinek mount2zhandle(char *mountpoint)
1590b5de56dSgjelinek {
1600b5de56dSgjelinek 	zfs_mount_data_t	cb;
1610b5de56dSgjelinek 
1620b5de56dSgjelinek 	cb.match_name = mountpoint;
1630b5de56dSgjelinek 	cb.match_handle = NULL;
1640b5de56dSgjelinek 	(void) zfs_iter_root(match_mountpoint, &cb);
1650b5de56dSgjelinek 	return (cb.match_handle);
1660b5de56dSgjelinek }
1670b5de56dSgjelinek 
1680b5de56dSgjelinek /*
1690b5de56dSgjelinek  * Check if there is already a file system (zfs or any other type) mounted on
1700b5de56dSgjelinek  * path.
1710b5de56dSgjelinek  */
1720b5de56dSgjelinek static boolean_t
1730b5de56dSgjelinek is_mountpnt(char *path)
1740b5de56dSgjelinek {
1750b5de56dSgjelinek 	FILE		*fp;
1760b5de56dSgjelinek 	struct mnttab	entry;
1770b5de56dSgjelinek 
1780b5de56dSgjelinek 	if ((fp = fopen("/etc/mnttab", "r")) == NULL)
1790b5de56dSgjelinek 		return (B_FALSE);
1800b5de56dSgjelinek 
1810b5de56dSgjelinek 	while (getmntent(fp, &entry) == 0) {
1820b5de56dSgjelinek 		if (strcmp(path, entry.mnt_mountp) == 0) {
1830b5de56dSgjelinek 			(void) fclose(fp);
1840b5de56dSgjelinek 			return (B_TRUE);
1850b5de56dSgjelinek 		}
1860b5de56dSgjelinek 	}
1870b5de56dSgjelinek 
1880b5de56dSgjelinek 	(void) fclose(fp);
1890b5de56dSgjelinek 	return (B_FALSE);
1900b5de56dSgjelinek }
1910b5de56dSgjelinek 
1920b5de56dSgjelinek /*
1930b5de56dSgjelinek  * Perform any necessary housekeeping tasks we need to do before we take
1940b5de56dSgjelinek  * a ZFS snapshot of the zone.  What this really entails is that we are
1950b5de56dSgjelinek  * taking a sw inventory of the source zone, like we do when we detach,
1960b5de56dSgjelinek  * so that there is the XML manifest in the snapshot.  We use that to
1970b5de56dSgjelinek  * validate the snapshot if it is the source of a clone at some later time.
1980b5de56dSgjelinek  */
1990b5de56dSgjelinek static int
2000b5de56dSgjelinek pre_snapshot(char *source_zone)
2010b5de56dSgjelinek {
2020b5de56dSgjelinek 	int err;
2030b5de56dSgjelinek 	zone_dochandle_t handle;
2040b5de56dSgjelinek 
2050b5de56dSgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
2060b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
2070b5de56dSgjelinek 		return (Z_ERR);
2080b5de56dSgjelinek 	}
2090b5de56dSgjelinek 
2100b5de56dSgjelinek 	if ((err = zonecfg_get_handle(source_zone, handle)) != Z_OK) {
2110b5de56dSgjelinek 		errno = err;
2120b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
2130b5de56dSgjelinek 		zonecfg_fini_handle(handle);
2140b5de56dSgjelinek 		return (Z_ERR);
2150b5de56dSgjelinek 	}
2160b5de56dSgjelinek 
2170b5de56dSgjelinek 	if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) {
2180b5de56dSgjelinek 		errno = err;
2190b5de56dSgjelinek 		zperror(gettext("getting the software version information "
2200b5de56dSgjelinek 		    "failed"), B_TRUE);
2210b5de56dSgjelinek 		zonecfg_fini_handle(handle);
2220b5de56dSgjelinek 		return (Z_ERR);
2230b5de56dSgjelinek 	}
2240b5de56dSgjelinek 
225*8cd327d5Sgjelinek 	if ((err = zonecfg_detach_save(handle, 0)) != Z_OK) {
2260b5de56dSgjelinek 		errno = err;
2270b5de56dSgjelinek 		zperror(gettext("saving the software version manifest failed"),
2280b5de56dSgjelinek 		    B_TRUE);
2290b5de56dSgjelinek 		zonecfg_fini_handle(handle);
2300b5de56dSgjelinek 		return (Z_ERR);
2310b5de56dSgjelinek 	}
2320b5de56dSgjelinek 
2330b5de56dSgjelinek 	zonecfg_fini_handle(handle);
2340b5de56dSgjelinek 	return (Z_OK);
2350b5de56dSgjelinek }
2360b5de56dSgjelinek 
2370b5de56dSgjelinek /*
2380b5de56dSgjelinek  * Perform any necessary housekeeping tasks we need to do after we take
2390b5de56dSgjelinek  * a ZFS snapshot of the zone.  What this really entails is removing the
2400b5de56dSgjelinek  * sw inventory XML file from the zone.  It is still in the snapshot where
2410b5de56dSgjelinek  * we want it, but we don't want it in the source zone itself.
2420b5de56dSgjelinek  */
2430b5de56dSgjelinek static int
2440b5de56dSgjelinek post_snapshot(char *source_zone)
2450b5de56dSgjelinek {
2460b5de56dSgjelinek 	int err;
2470b5de56dSgjelinek 	zone_dochandle_t handle;
2480b5de56dSgjelinek 
2490b5de56dSgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
2500b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
2510b5de56dSgjelinek 		return (Z_ERR);
2520b5de56dSgjelinek 	}
2530b5de56dSgjelinek 
2540b5de56dSgjelinek 	if ((err = zonecfg_get_handle(source_zone, handle)) != Z_OK) {
2550b5de56dSgjelinek 		errno = err;
2560b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
2570b5de56dSgjelinek 		zonecfg_fini_handle(handle);
2580b5de56dSgjelinek 		return (Z_ERR);
2590b5de56dSgjelinek 	}
2600b5de56dSgjelinek 
2610b5de56dSgjelinek 	zonecfg_rm_detached(handle, B_FALSE);
2620b5de56dSgjelinek 	zonecfg_fini_handle(handle);
2630b5de56dSgjelinek 
2640b5de56dSgjelinek 	return (Z_OK);
2650b5de56dSgjelinek }
2660b5de56dSgjelinek 
2670b5de56dSgjelinek /*
2680b5de56dSgjelinek  * This is a ZFS snapshot iterator call-back function which returns the
2690b5de56dSgjelinek  * highest number of SUNWzone snapshots that have been taken.
2700b5de56dSgjelinek  */
2710b5de56dSgjelinek static int
2720b5de56dSgjelinek get_snap_max(zfs_handle_t *zhp, void *data)
2730b5de56dSgjelinek {
2740b5de56dSgjelinek 	int			res;
2750b5de56dSgjelinek 	zfs_snapshot_data_t	*cbp;
2760b5de56dSgjelinek 
2770b5de56dSgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2780b5de56dSgjelinek 		zfs_close(zhp);
2790b5de56dSgjelinek 		return (0);
2800b5de56dSgjelinek 	}
2810b5de56dSgjelinek 
2820b5de56dSgjelinek 	cbp = (zfs_snapshot_data_t *)data;
2830b5de56dSgjelinek 
2840b5de56dSgjelinek 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2850b5de56dSgjelinek 		char	*nump;
2860b5de56dSgjelinek 		int	num;
2870b5de56dSgjelinek 
2880b5de56dSgjelinek 		nump = (char *)(zfs_get_name(zhp) + cbp->len);
2890b5de56dSgjelinek 		num = atoi(nump);
2900b5de56dSgjelinek 		if (num > cbp->max)
2910b5de56dSgjelinek 			cbp->max = num;
2920b5de56dSgjelinek 	}
2930b5de56dSgjelinek 
2940b5de56dSgjelinek 	res = zfs_iter_snapshots(zhp, get_snap_max, data);
2950b5de56dSgjelinek 	zfs_close(zhp);
2960b5de56dSgjelinek 	return (res);
2970b5de56dSgjelinek }
2980b5de56dSgjelinek 
2990b5de56dSgjelinek /*
3000b5de56dSgjelinek  * Take a ZFS snapshot to be used for cloning the zone.
3010b5de56dSgjelinek  */
3020b5de56dSgjelinek static int
3030b5de56dSgjelinek take_snapshot(char *source_zone, zfs_handle_t *zhp, char *snapshot_name,
3040b5de56dSgjelinek     int snap_size)
3050b5de56dSgjelinek {
3060b5de56dSgjelinek 	int			res;
3070b5de56dSgjelinek 	char			template[ZFS_MAXNAMELEN];
3080b5de56dSgjelinek 	zfs_snapshot_data_t	cb;
3090b5de56dSgjelinek 
3100b5de56dSgjelinek 	/*
3110b5de56dSgjelinek 	 * First we need to figure out the next available name for the
3120b5de56dSgjelinek 	 * zone snapshot.  Look through the list of zones snapshots for
3130b5de56dSgjelinek 	 * this file system to determine the maximum snapshot name.
3140b5de56dSgjelinek 	 */
3150b5de56dSgjelinek 	if (snprintf(template, sizeof (template), "%s@SUNWzone",
3160b5de56dSgjelinek 	    zfs_get_name(zhp)) >=  sizeof (template))
3170b5de56dSgjelinek 		return (Z_ERR);
3180b5de56dSgjelinek 
3190b5de56dSgjelinek 	cb.match_name = template;
3200b5de56dSgjelinek 	cb.len = strlen(template);
3210b5de56dSgjelinek 	cb.max = 0;
3220b5de56dSgjelinek 
3230b5de56dSgjelinek 	if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0)
3240b5de56dSgjelinek 		return (Z_ERR);
3250b5de56dSgjelinek 
3260b5de56dSgjelinek 	cb.max++;
3270b5de56dSgjelinek 
3280b5de56dSgjelinek 	if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
3290b5de56dSgjelinek 	    zfs_get_name(zhp), cb.max) >= snap_size)
3300b5de56dSgjelinek 		return (Z_ERR);
3310b5de56dSgjelinek 
3320b5de56dSgjelinek 	if (pre_snapshot(source_zone) != Z_OK)
3330b5de56dSgjelinek 		return (Z_ERR);
3340b5de56dSgjelinek 	res = zfs_snapshot(snapshot_name);
3350b5de56dSgjelinek 	if (post_snapshot(source_zone) != Z_OK)
3360b5de56dSgjelinek 		return (Z_ERR);
3370b5de56dSgjelinek 
3380b5de56dSgjelinek 	if (res != 0)
3390b5de56dSgjelinek 		return (Z_ERR);
3400b5de56dSgjelinek 	return (Z_OK);
3410b5de56dSgjelinek }
3420b5de56dSgjelinek 
3430b5de56dSgjelinek /*
3440b5de56dSgjelinek  * We are using an explicit snapshot from some earlier point in time so
3450b5de56dSgjelinek  * we need to validate it.  This involves checking the sw inventory that
3460b5de56dSgjelinek  * we took when we made the snapshot to verify that the current sw config
3470b5de56dSgjelinek  * on the host is still valid to run a zone made from this snapshot.
3480b5de56dSgjelinek  */
3490b5de56dSgjelinek static int
3500b5de56dSgjelinek validate_snapshot(char *snapshot_name, char *snap_path)
3510b5de56dSgjelinek {
3520b5de56dSgjelinek 	int err;
3530b5de56dSgjelinek 	zone_dochandle_t handle;
3540b5de56dSgjelinek 	zone_dochandle_t athandle = NULL;
3550b5de56dSgjelinek 
3560b5de56dSgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
3570b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3580b5de56dSgjelinek 		return (Z_ERR);
3590b5de56dSgjelinek 	}
3600b5de56dSgjelinek 
3610b5de56dSgjelinek 	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3620b5de56dSgjelinek 		errno = err;
3630b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3640b5de56dSgjelinek 		zonecfg_fini_handle(handle);
3650b5de56dSgjelinek 		return (Z_ERR);
3660b5de56dSgjelinek 	}
3670b5de56dSgjelinek 
3680b5de56dSgjelinek 	if ((athandle = zonecfg_init_handle()) == NULL) {
3690b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3700b5de56dSgjelinek 		goto done;
3710b5de56dSgjelinek 	}
3720b5de56dSgjelinek 
3730b5de56dSgjelinek 	if ((err = zonecfg_get_attach_handle(snap_path, target_zone, B_TRUE,
3740b5de56dSgjelinek 	    athandle)) != Z_OK) {
3750b5de56dSgjelinek 		if (err == Z_NO_ZONE)
3760b5de56dSgjelinek 			(void) fprintf(stderr, gettext("snapshot %s was not "
3770b5de56dSgjelinek 			    "taken\n\tby a 'zoneadm clone' command.  It can "
3780b5de56dSgjelinek 			    "not be used to clone zones.\n"), snapshot_name);
3790b5de56dSgjelinek 		else
3800b5de56dSgjelinek 			(void) fprintf(stderr, gettext("snapshot %s is "
3810b5de56dSgjelinek 			    "out-dated\n\tIt can no longer be used to clone "
3820b5de56dSgjelinek 			    "zones on this system.\n"), snapshot_name);
3830b5de56dSgjelinek 		goto done;
3840b5de56dSgjelinek 	}
3850b5de56dSgjelinek 
3860b5de56dSgjelinek 	/* Get the detach information for the locally defined zone. */
3870b5de56dSgjelinek 	if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) {
3880b5de56dSgjelinek 		errno = err;
3890b5de56dSgjelinek 		zperror(gettext("getting the attach information failed"),
3900b5de56dSgjelinek 		    B_TRUE);
3910b5de56dSgjelinek 		goto done;
3920b5de56dSgjelinek 	}
3930b5de56dSgjelinek 
3940b5de56dSgjelinek 	if ((err = sw_cmp(handle, athandle, SW_CMP_SILENT)) != Z_OK)
3950b5de56dSgjelinek 		(void) fprintf(stderr, gettext("snapshot %s is out-dated\n\t"
3960b5de56dSgjelinek 		    "It can no longer be used to clone zones on this "
3970b5de56dSgjelinek 		    "system.\n"), snapshot_name);
3980b5de56dSgjelinek 
3990b5de56dSgjelinek done:
4000b5de56dSgjelinek 	zonecfg_fini_handle(handle);
4010b5de56dSgjelinek 	if (athandle != NULL)
4020b5de56dSgjelinek 		zonecfg_fini_handle(athandle);
4030b5de56dSgjelinek 
4040b5de56dSgjelinek 	return (err);
4050b5de56dSgjelinek }
4060b5de56dSgjelinek 
4070b5de56dSgjelinek /*
4080b5de56dSgjelinek  * Remove the sw inventory file from inside this zonepath that we picked up out
4090b5de56dSgjelinek  * of the snapshot.
4100b5de56dSgjelinek  */
4110b5de56dSgjelinek static int
4120b5de56dSgjelinek clean_out_clone()
4130b5de56dSgjelinek {
4140b5de56dSgjelinek 	int err;
4150b5de56dSgjelinek 	zone_dochandle_t handle;
4160b5de56dSgjelinek 
4170b5de56dSgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
4180b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
4190b5de56dSgjelinek 		return (Z_ERR);
4200b5de56dSgjelinek 	}
4210b5de56dSgjelinek 
4220b5de56dSgjelinek 	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
4230b5de56dSgjelinek 		errno = err;
4240b5de56dSgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
4250b5de56dSgjelinek 		zonecfg_fini_handle(handle);
4260b5de56dSgjelinek 		return (Z_ERR);
4270b5de56dSgjelinek 	}
4280b5de56dSgjelinek 
4290b5de56dSgjelinek 	zonecfg_rm_detached(handle, B_FALSE);
4300b5de56dSgjelinek 	zonecfg_fini_handle(handle);
4310b5de56dSgjelinek 
4320b5de56dSgjelinek 	return (Z_OK);
4330b5de56dSgjelinek }
4340b5de56dSgjelinek 
4350b5de56dSgjelinek /*
4360b5de56dSgjelinek  * Make a ZFS clone on zonepath from snapshot_name.
4370b5de56dSgjelinek  */
4380b5de56dSgjelinek static int
4390b5de56dSgjelinek clone_snap(char *snapshot_name, char *zonepath)
4400b5de56dSgjelinek {
4410b5de56dSgjelinek 	int		res = Z_OK;
4420b5de56dSgjelinek 	int		err;
4430b5de56dSgjelinek 	zfs_handle_t	*zhp;
4440b5de56dSgjelinek 	zfs_handle_t	*clone;
4450b5de56dSgjelinek 
4460b5de56dSgjelinek 	if ((zhp = zfs_open(snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
4470b5de56dSgjelinek 		return (Z_NO_ENTRY);
4480b5de56dSgjelinek 
4490b5de56dSgjelinek 	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
4500b5de56dSgjelinek 
4510b5de56dSgjelinek 	err = zfs_clone(zhp, zonepath);
4520b5de56dSgjelinek 	zfs_close(zhp);
4530b5de56dSgjelinek 	if (err != 0)
4540b5de56dSgjelinek 		return (Z_ERR);
4550b5de56dSgjelinek 
4560b5de56dSgjelinek 	/* create the mountpoint if necessary */
4570b5de56dSgjelinek 	if ((clone = zfs_open(zonepath, ZFS_TYPE_ANY)) == NULL)
4580b5de56dSgjelinek 		return (Z_ERR);
4590b5de56dSgjelinek 
4600b5de56dSgjelinek 	/*
4610b5de56dSgjelinek 	 * The clone has been created so we need to print a diagnostic
4620b5de56dSgjelinek 	 * message if one of the following steps fails for some reason.
4630b5de56dSgjelinek 	 */
4640b5de56dSgjelinek 	if (zfs_mount(clone, NULL, 0) != 0) {
4650b5de56dSgjelinek 		(void) fprintf(stderr, gettext("could not mount ZFS clone "
4660b5de56dSgjelinek 		    "%s\n"), zfs_get_name(clone));
4670b5de56dSgjelinek 		res = Z_ERR;
4680b5de56dSgjelinek 
4690b5de56dSgjelinek 	} else {
4700b5de56dSgjelinek 		if (zfs_prop_set(clone, ZFS_PROP_SHARENFS, "off") != 0) {
4710b5de56dSgjelinek 			/* we won't consider this a failure */
4720b5de56dSgjelinek 			(void) fprintf(stderr, gettext("could not turn off the "
4730b5de56dSgjelinek 			    "'sharenfs' property on ZFS clone %s\n"),
4740b5de56dSgjelinek 			    zfs_get_name(clone));
4750b5de56dSgjelinek 		}
4760b5de56dSgjelinek 
4770b5de56dSgjelinek 		if (clean_out_clone() != Z_OK) {
4780b5de56dSgjelinek 			(void) fprintf(stderr, gettext("could not remove the "
4790b5de56dSgjelinek 			    "software inventory from ZFS clone %s\n"),
4800b5de56dSgjelinek 			    zfs_get_name(clone));
4810b5de56dSgjelinek 			res = Z_ERR;
4820b5de56dSgjelinek 		}
4830b5de56dSgjelinek 	}
4840b5de56dSgjelinek 
4850b5de56dSgjelinek 	zfs_close(clone);
4860b5de56dSgjelinek 	return (res);
4870b5de56dSgjelinek }
4880b5de56dSgjelinek 
4890b5de56dSgjelinek /*
4900b5de56dSgjelinek  * This function takes a zonepath and attempts to determine what the ZFS
4910b5de56dSgjelinek  * file system name (not mountpoint) should be for that path.  We do not
4920b5de56dSgjelinek  * assume that zonepath is an existing directory or ZFS fs since we use
4930b5de56dSgjelinek  * this function as part of the process of creating a new ZFS fs or clone.
4940b5de56dSgjelinek  *
4950b5de56dSgjelinek  * The way this works is that we look at the parent directory of the zonepath
4960b5de56dSgjelinek  * to see if it is a ZFS fs.  If it is, we get the name of that ZFS fs and
4970b5de56dSgjelinek  * append the last component of the zonepath to generate the ZFS name for the
4980b5de56dSgjelinek  * zonepath.  This matches the algorithm that ZFS uses for automatically
4990b5de56dSgjelinek  * mounting a new fs after it is created.
5000b5de56dSgjelinek  *
5010b5de56dSgjelinek  * Although a ZFS fs can be mounted anywhere, we don't worry about handling
5020b5de56dSgjelinek  * all of the complexity that a user could possibly configure with arbitrary
5030b5de56dSgjelinek  * mounts since there is no way to generate a ZFS name from a random path in
5040b5de56dSgjelinek  * the file system.  We only try to handle the automatic mounts that ZFS does
5050b5de56dSgjelinek  * for each file system.  ZFS restricts this so that a new fs must be created
5060b5de56dSgjelinek  * in an existing parent ZFS fs.  It then automatically mounts the new fs
5070b5de56dSgjelinek  * directly under the mountpoint for the parent fs using the last component
5080b5de56dSgjelinek  * of the name as the mountpoint directory.
5090b5de56dSgjelinek  *
5100b5de56dSgjelinek  * For example:
5110b5de56dSgjelinek  *    Name			Mountpoint
5120b5de56dSgjelinek  *    space/eng/dev/test/zone1	/project1/eng/dev/test/zone1
5130b5de56dSgjelinek  *
5140b5de56dSgjelinek  * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
5150b5de56dSgjelinek  * Z_ERR.
5160b5de56dSgjelinek  */
5170b5de56dSgjelinek static int
5180b5de56dSgjelinek path2name(char *zonepath, char *zfs_name, int len)
5190b5de56dSgjelinek {
5200b5de56dSgjelinek 	int		res;
5210b5de56dSgjelinek 	char		*p;
5220b5de56dSgjelinek 	zfs_handle_t	*zhp;
5230b5de56dSgjelinek 
5240b5de56dSgjelinek 	if ((p = strrchr(zonepath, '/')) == NULL)
5250b5de56dSgjelinek 		return (Z_ERR);
5260b5de56dSgjelinek 
5270b5de56dSgjelinek 	/*
5280b5de56dSgjelinek 	 * If the parent directory is not its own ZFS fs, then we can't
5290b5de56dSgjelinek 	 * automatically create a new ZFS fs at the 'zonepath' mountpoint
5300b5de56dSgjelinek 	 * so return an error.
5310b5de56dSgjelinek 	 */
5320b5de56dSgjelinek 	*p = '\0';
5330b5de56dSgjelinek 	zhp = mount2zhandle(zonepath);
5340b5de56dSgjelinek 	*p = '/';
5350b5de56dSgjelinek 	if (zhp == NULL)
5360b5de56dSgjelinek 		return (Z_ERR);
5370b5de56dSgjelinek 
5380b5de56dSgjelinek 	res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), p + 1);
5390b5de56dSgjelinek 
5400b5de56dSgjelinek 	zfs_close(zhp);
5410b5de56dSgjelinek 	if (res >= len)
5420b5de56dSgjelinek 		return (Z_ERR);
5430b5de56dSgjelinek 
5440b5de56dSgjelinek 	return (Z_OK);
5450b5de56dSgjelinek }
5460b5de56dSgjelinek 
5470b5de56dSgjelinek /*
5480b5de56dSgjelinek  * A ZFS file system iterator call-back function used to determine if the
5490b5de56dSgjelinek  * file system has dependents (snapshots & clones).
5500b5de56dSgjelinek  */
5510b5de56dSgjelinek /* ARGSUSED */
5520b5de56dSgjelinek static int
5530b5de56dSgjelinek has_dependent(zfs_handle_t *zhp, void *data)
5540b5de56dSgjelinek {
5550b5de56dSgjelinek 	zfs_close(zhp);
5560b5de56dSgjelinek 	return (1);
5570b5de56dSgjelinek }
5580b5de56dSgjelinek 
5590b5de56dSgjelinek /*
5600b5de56dSgjelinek  * Given a snapshot name, get the file system path where the snapshot lives.
5610b5de56dSgjelinek  * A snapshot name is of the form fs_name@snap_name.  For example, snapshot
5620b5de56dSgjelinek  * pl/zones/z1@SUNWzone1 would have a path of
5630b5de56dSgjelinek  * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5640b5de56dSgjelinek  */
5650b5de56dSgjelinek static int
5660b5de56dSgjelinek snap2path(char *snap_name, char *path, int len)
5670b5de56dSgjelinek {
5680b5de56dSgjelinek 	char		*p;
5690b5de56dSgjelinek 	zfs_handle_t	*zhp;
5700b5de56dSgjelinek 	char		mp[ZFS_MAXPROPLEN];
5710b5de56dSgjelinek 
5720b5de56dSgjelinek 	if ((p = strrchr(snap_name, '@')) == NULL)
5730b5de56dSgjelinek 		return (Z_ERR);
5740b5de56dSgjelinek 
5750b5de56dSgjelinek 	/* Get the file system name from the snap_name. */
5760b5de56dSgjelinek 	*p = '\0';
5770b5de56dSgjelinek 	zhp = zfs_open(snap_name, ZFS_TYPE_ANY);
5780b5de56dSgjelinek 	*p = '@';
5790b5de56dSgjelinek 	if (zhp == NULL)
5800b5de56dSgjelinek 		return (Z_ERR);
5810b5de56dSgjelinek 
5820b5de56dSgjelinek 	/* Get the file system mount point. */
5830b5de56dSgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
5840b5de56dSgjelinek 	    0, FALSE) != 0) {
5850b5de56dSgjelinek 		zfs_close(zhp);
5860b5de56dSgjelinek 		return (Z_ERR);
5870b5de56dSgjelinek 	}
5880b5de56dSgjelinek 	zfs_close(zhp);
5890b5de56dSgjelinek 
5900b5de56dSgjelinek 	p++;
5910b5de56dSgjelinek 	if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5920b5de56dSgjelinek 		return (Z_ERR);
5930b5de56dSgjelinek 
5940b5de56dSgjelinek 	return (Z_OK);
5950b5de56dSgjelinek }
5960b5de56dSgjelinek 
5970b5de56dSgjelinek /*
5980b5de56dSgjelinek  * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
5990b5de56dSgjelinek  * possible, or by copying the data from the snapshot to the zonepath.
6000b5de56dSgjelinek  */
6010b5de56dSgjelinek int
6020b5de56dSgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath)
6030b5de56dSgjelinek {
6040b5de56dSgjelinek 	int	err = Z_OK;
6050b5de56dSgjelinek 	char	clone_name[MAXPATHLEN];
6060b5de56dSgjelinek 	char	snap_path[MAXPATHLEN];
6070b5de56dSgjelinek 
6080b5de56dSgjelinek 	if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
6090b5de56dSgjelinek 		(void) fprintf(stderr, gettext("unable to find path for %s.\n"),
6100b5de56dSgjelinek 		    snap_name);
6110b5de56dSgjelinek 		return (Z_ERR);
6120b5de56dSgjelinek 	}
6130b5de56dSgjelinek 
6140b5de56dSgjelinek 	if (validate_snapshot(snap_name, snap_path) != Z_OK)
6150b5de56dSgjelinek 		return (Z_NO_ENTRY);
6160b5de56dSgjelinek 
6170b5de56dSgjelinek 	/*
6180b5de56dSgjelinek 	 * The zonepath cannot be ZFS cloned, try to copy the data from
6190b5de56dSgjelinek 	 * within the snapshot to the zonepath.
6200b5de56dSgjelinek 	 */
6210b5de56dSgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
6220b5de56dSgjelinek 		if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
6230b5de56dSgjelinek 			if (clean_out_clone() != Z_OK)
6240b5de56dSgjelinek 				(void) fprintf(stderr,
6250b5de56dSgjelinek 				    gettext("could not remove the "
6260b5de56dSgjelinek 				    "software inventory from %s\n"), zonepath);
6270b5de56dSgjelinek 
6280b5de56dSgjelinek 		return (err);
6290b5de56dSgjelinek 	}
6300b5de56dSgjelinek 
6310b5de56dSgjelinek 	if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
6320b5de56dSgjelinek 		if (err != Z_NO_ENTRY) {
6330b5de56dSgjelinek 			/*
6340b5de56dSgjelinek 			 * Cloning the snapshot failed.  Fall back to trying
6350b5de56dSgjelinek 			 * to install the zone by copying from the snapshot.
6360b5de56dSgjelinek 			 */
6370b5de56dSgjelinek 			if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
6380b5de56dSgjelinek 				if (clean_out_clone() != Z_OK)
6390b5de56dSgjelinek 					(void) fprintf(stderr,
6400b5de56dSgjelinek 					    gettext("could not remove the "
6410b5de56dSgjelinek 					    "software inventory from %s\n"),
6420b5de56dSgjelinek 					    zonepath);
6430b5de56dSgjelinek 		} else {
6440b5de56dSgjelinek 			/*
6450b5de56dSgjelinek 			 * The snapshot is unusable for some reason so restore
6460b5de56dSgjelinek 			 * the zone state to configured since we were unable to
6470b5de56dSgjelinek 			 * actually do anything about getting the zone
6480b5de56dSgjelinek 			 * installed.
6490b5de56dSgjelinek 			 */
6500b5de56dSgjelinek 			int tmp;
6510b5de56dSgjelinek 
6520b5de56dSgjelinek 			if ((tmp = zone_set_state(target_zone,
6530b5de56dSgjelinek 			    ZONE_STATE_CONFIGURED)) != Z_OK) {
6540b5de56dSgjelinek 				errno = tmp;
6550b5de56dSgjelinek 				zperror2(target_zone,
6560b5de56dSgjelinek 				    gettext("could not set state"));
6570b5de56dSgjelinek 			}
6580b5de56dSgjelinek 		}
6590b5de56dSgjelinek 	}
6600b5de56dSgjelinek 
6610b5de56dSgjelinek 	return (err);
6620b5de56dSgjelinek }
6630b5de56dSgjelinek 
6640b5de56dSgjelinek /*
6650b5de56dSgjelinek  * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
6660b5de56dSgjelinek  */
6670b5de56dSgjelinek int
6680b5de56dSgjelinek clone_zfs(char *source_zone, char *source_zonepath, char *zonepath)
6690b5de56dSgjelinek {
6700b5de56dSgjelinek 	zfs_handle_t	*zhp;
6710b5de56dSgjelinek 	char		clone_name[MAXPATHLEN];
6720b5de56dSgjelinek 	char		snap_name[MAXPATHLEN];
6730b5de56dSgjelinek 
6740b5de56dSgjelinek 	/*
6750b5de56dSgjelinek 	 * Try to get a zfs handle for the source_zonepath.  If this fails
6760b5de56dSgjelinek 	 * the source_zonepath is not ZFS so return an error.
6770b5de56dSgjelinek 	 */
6780b5de56dSgjelinek 	if ((zhp = mount2zhandle(source_zonepath)) == NULL)
6790b5de56dSgjelinek 		return (Z_ERR);
6800b5de56dSgjelinek 
6810b5de56dSgjelinek 	/*
6820b5de56dSgjelinek 	 * Check if there is a file system already mounted on zonepath.  If so,
6830b5de56dSgjelinek 	 * we can't clone to the path so we should fall back to copying.
6840b5de56dSgjelinek 	 */
6850b5de56dSgjelinek 	if (is_mountpnt(zonepath)) {
6860b5de56dSgjelinek 		zfs_close(zhp);
6870b5de56dSgjelinek 		(void) fprintf(stderr,
6880b5de56dSgjelinek 		    gettext("A file system is already mounted on %s,\n"
6890b5de56dSgjelinek 		    "preventing use of a ZFS clone.\n"), zonepath);
6900b5de56dSgjelinek 		return (Z_ERR);
6910b5de56dSgjelinek 	}
6920b5de56dSgjelinek 
6930b5de56dSgjelinek 	/*
6940b5de56dSgjelinek 	 * Instead of using path2name to get the clone name from the zonepath,
6950b5de56dSgjelinek 	 * we could generate a name from the source zone ZFS name.  However,
6960b5de56dSgjelinek 	 * this would mean we would create the clone under the ZFS fs of the
6970b5de56dSgjelinek 	 * source instead of what the zonepath says.  For example,
6980b5de56dSgjelinek 	 *
6990b5de56dSgjelinek 	 * source_zonepath		zonepath
7000b5de56dSgjelinek 	 * /pl/zones/dev/z1		/pl/zones/deploy/z2
7010b5de56dSgjelinek 	 *
7020b5de56dSgjelinek 	 * We don't want the clone to be under "dev", we want it under
7030b5de56dSgjelinek 	 * "deploy", so that we can leverage the normal attribute inheritance
7040b5de56dSgjelinek 	 * that ZFS provides in the fs hierarchy.
7050b5de56dSgjelinek 	 */
7060b5de56dSgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
7070b5de56dSgjelinek 		zfs_close(zhp);
7080b5de56dSgjelinek 		return (Z_ERR);
7090b5de56dSgjelinek 	}
7100b5de56dSgjelinek 
7110b5de56dSgjelinek 	if (take_snapshot(source_zone, zhp, snap_name, sizeof (snap_name))
7120b5de56dSgjelinek 	    != Z_OK) {
7130b5de56dSgjelinek 		zfs_close(zhp);
7140b5de56dSgjelinek 		return (Z_ERR);
7150b5de56dSgjelinek 	}
7160b5de56dSgjelinek 	zfs_close(zhp);
7170b5de56dSgjelinek 
7180b5de56dSgjelinek 	if (clone_snap(snap_name, clone_name) != Z_OK)
7190b5de56dSgjelinek 		return (Z_ERR);
7200b5de56dSgjelinek 
7210b5de56dSgjelinek 	(void) printf(gettext("Instead of copying, a ZFS clone has been "
7220b5de56dSgjelinek 	    "created for this zone.\n"));
7230b5de56dSgjelinek 
7240b5de56dSgjelinek 	return (Z_OK);
7250b5de56dSgjelinek }
7260b5de56dSgjelinek 
7270b5de56dSgjelinek /*
7280b5de56dSgjelinek  * Attempt to create a ZFS file system for the specified zonepath.
7290b5de56dSgjelinek  * We either will successfully create a ZFS file system and get it mounted
7300b5de56dSgjelinek  * on the zonepath or we don't.  The caller doesn't care since a regular
7310b5de56dSgjelinek  * directory is used for the zonepath if no ZFS file system is mounted there.
7320b5de56dSgjelinek  */
7330b5de56dSgjelinek void
7340b5de56dSgjelinek create_zfs_zonepath(char *zonepath)
7350b5de56dSgjelinek {
7360b5de56dSgjelinek 	zfs_handle_t	*zhp;
7370b5de56dSgjelinek 	char		zfs_name[MAXPATHLEN];
7380b5de56dSgjelinek 
7390b5de56dSgjelinek 	if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
7400b5de56dSgjelinek 		return;
7410b5de56dSgjelinek 
7420b5de56dSgjelinek 	zfs_set_error_handler(noop_err_handler);
7430b5de56dSgjelinek 
7440b5de56dSgjelinek 	if (zfs_create(zfs_name, ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0 ||
7450b5de56dSgjelinek 	    (zhp = zfs_open(zfs_name, ZFS_TYPE_ANY)) == NULL) {
7460b5de56dSgjelinek 		zfs_set_error_handler(NULL);
7470b5de56dSgjelinek 		return;
7480b5de56dSgjelinek 	}
7490b5de56dSgjelinek 
7500b5de56dSgjelinek 	if (zfs_mount(zhp, NULL, 0) != 0) {
7510b5de56dSgjelinek 		(void) zfs_destroy(zhp);
7520b5de56dSgjelinek 	} else if (zfs_prop_set(zhp, ZFS_PROP_SHARENFS, "off") != 0) {
7530b5de56dSgjelinek 		(void) fprintf(stderr, gettext("file system %s successfully "
7540b5de56dSgjelinek 		    "created,\nbut could not turn off the 'sharenfs' "
7550b5de56dSgjelinek 		    "property\n"), zfs_name);
7560b5de56dSgjelinek 	} else {
7570b5de56dSgjelinek 		if (chmod(zonepath, S_IRWXU) != 0) {
7580b5de56dSgjelinek 			(void) fprintf(stderr, gettext("file system %s "
7590b5de56dSgjelinek 			    "successfully created, but chmod %o failed: %s\n"),
7600b5de56dSgjelinek 			    zfs_name, S_IRWXU, strerror(errno));
7610b5de56dSgjelinek 			(void) destroy_zfs(zonepath);
7620b5de56dSgjelinek 		} else {
7630b5de56dSgjelinek 			(void) printf(gettext("A ZFS file system has been "
7640b5de56dSgjelinek 			    "created for this zone.\n"));
7650b5de56dSgjelinek 		}
7660b5de56dSgjelinek 	}
7670b5de56dSgjelinek 
7680b5de56dSgjelinek 	zfs_set_error_handler(NULL);
7690b5de56dSgjelinek 	zfs_close(zhp);
7700b5de56dSgjelinek }
7710b5de56dSgjelinek 
7720b5de56dSgjelinek /*
7730b5de56dSgjelinek  * If the zonepath is a ZFS file system, attempt to destroy it.  We return Z_OK
7740b5de56dSgjelinek  * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
7750b5de56dSgjelinek  * which means the caller should clean up the zonepath in the traditional
7760b5de56dSgjelinek  * way.
7770b5de56dSgjelinek  */
7780b5de56dSgjelinek int
7790b5de56dSgjelinek destroy_zfs(char *zonepath)
7800b5de56dSgjelinek {
7810b5de56dSgjelinek 	zfs_handle_t	*zhp;
7820b5de56dSgjelinek 	boolean_t	is_clone = B_FALSE;
7830b5de56dSgjelinek 	char		origin[ZFS_MAXPROPLEN];
7840b5de56dSgjelinek 
7850b5de56dSgjelinek 	zfs_set_error_handler(noop_err_handler);
7860b5de56dSgjelinek 
7870b5de56dSgjelinek 	if ((zhp = mount2zhandle(zonepath)) == NULL) {
7880b5de56dSgjelinek 		zfs_set_error_handler(NULL);
7890b5de56dSgjelinek 		return (Z_ERR);
7900b5de56dSgjelinek 	}
7910b5de56dSgjelinek 
7920b5de56dSgjelinek 	/*
7930b5de56dSgjelinek 	 * We can't destroy the file system if it has dependents.
7940b5de56dSgjelinek 	 */
7950b5de56dSgjelinek 	if (zfs_iter_dependents(zhp, has_dependent, NULL) != 0 ||
7960b5de56dSgjelinek 	    zfs_unmount(zhp, NULL, 0) != 0) {
7970b5de56dSgjelinek 		zfs_close(zhp);
7980b5de56dSgjelinek 		zfs_set_error_handler(NULL);
7990b5de56dSgjelinek 		return (Z_ERR);
8000b5de56dSgjelinek 	}
8010b5de56dSgjelinek 
8020b5de56dSgjelinek 	/*
8030b5de56dSgjelinek 	 * This might be a clone.  Try to get the snapshot so we can attempt
8040b5de56dSgjelinek 	 * to destroy that as well.
8050b5de56dSgjelinek 	 */
8060b5de56dSgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
8070b5de56dSgjelinek 	    NULL, 0, FALSE) == 0)
8080b5de56dSgjelinek 		is_clone = B_TRUE;
8090b5de56dSgjelinek 
8100b5de56dSgjelinek 	zfs_set_error_handler(NULL);
8110b5de56dSgjelinek 	if (zfs_destroy(zhp) != 0) {
8120b5de56dSgjelinek 		/*
8130b5de56dSgjelinek 		 * If the destroy fails for some reason, try to remount
8140b5de56dSgjelinek 		 * the file system so that we can use "rm -rf" to clean up
8150b5de56dSgjelinek 		 * instead.
8160b5de56dSgjelinek 		 */
8170b5de56dSgjelinek 		(void) zfs_mount(zhp, NULL, 0);
8180b5de56dSgjelinek 		zfs_close(zhp);
8190b5de56dSgjelinek 		return (Z_ERR);
8200b5de56dSgjelinek 	}
8210b5de56dSgjelinek 	zfs_set_error_handler(noop_err_handler);
8220b5de56dSgjelinek 
8230b5de56dSgjelinek 	(void) printf(gettext("The ZFS file system for this zone has been "
8240b5de56dSgjelinek 	    "destroyed.\n"));
8250b5de56dSgjelinek 
8260b5de56dSgjelinek 	if (is_clone) {
8270b5de56dSgjelinek 		zfs_handle_t	*ohp;
8280b5de56dSgjelinek 
8290b5de56dSgjelinek 		/*
8300b5de56dSgjelinek 		 * Try to clean up the snapshot that the clone was taken from.
8310b5de56dSgjelinek 		 */
8320b5de56dSgjelinek 		if ((ohp = zfs_open(origin, ZFS_TYPE_SNAPSHOT)) != NULL) {
8330b5de56dSgjelinek 			if (zfs_iter_dependents(ohp, has_dependent, NULL)
8340b5de56dSgjelinek 			    == 0 && zfs_unmount(ohp, NULL, 0) == 0) {
8350b5de56dSgjelinek 				(void) zfs_destroy(ohp);
8360b5de56dSgjelinek 			}
8370b5de56dSgjelinek 			zfs_close(ohp);
8380b5de56dSgjelinek 		}
8390b5de56dSgjelinek 	}
8400b5de56dSgjelinek 
8410b5de56dSgjelinek 	zfs_close(zhp);
8420b5de56dSgjelinek 	zfs_set_error_handler(NULL);
8430b5de56dSgjelinek 	return (Z_OK);
8440b5de56dSgjelinek }
8450b5de56dSgjelinek 
8460b5de56dSgjelinek /*
8470b5de56dSgjelinek  * Return true if the path is its own zfs file system.  We determine this
8480b5de56dSgjelinek  * by stat-ing the path to see if it is zfs and stat-ing the parent to see
8490b5de56dSgjelinek  * if it is a different fs.
8500b5de56dSgjelinek  */
8510b5de56dSgjelinek boolean_t
8520b5de56dSgjelinek is_zonepath_zfs(char *zonepath)
8530b5de56dSgjelinek {
8540b5de56dSgjelinek 	int res;
8550b5de56dSgjelinek 	char *path;
8560b5de56dSgjelinek 	char *parent;
8570b5de56dSgjelinek 	struct statvfs buf1, buf2;
8580b5de56dSgjelinek 
8590b5de56dSgjelinek 	if (statvfs(zonepath, &buf1) != 0)
8600b5de56dSgjelinek 		return (B_FALSE);
8610b5de56dSgjelinek 
8620b5de56dSgjelinek 	if (strcmp(buf1.f_basetype, "zfs") != 0)
8630b5de56dSgjelinek 		return (B_FALSE);
8640b5de56dSgjelinek 
8650b5de56dSgjelinek 	if ((path = strdup(zonepath)) == NULL)
8660b5de56dSgjelinek 		return (B_FALSE);
8670b5de56dSgjelinek 
8680b5de56dSgjelinek 	parent = dirname(path);
8690b5de56dSgjelinek 	res = statvfs(parent, &buf2);
8700b5de56dSgjelinek 	free(path);
8710b5de56dSgjelinek 
8720b5de56dSgjelinek 	if (res != 0)
8730b5de56dSgjelinek 		return (B_FALSE);
8740b5de56dSgjelinek 
8750b5de56dSgjelinek 	if (buf1.f_fsid == buf2.f_fsid)
8760b5de56dSgjelinek 		return (B_FALSE);
8770b5de56dSgjelinek 
8780b5de56dSgjelinek 	return (B_TRUE);
8790b5de56dSgjelinek }
8800b5de56dSgjelinek 
8810b5de56dSgjelinek /*
8820b5de56dSgjelinek  * Implement the fast move of a ZFS file system by simply updating the
8830b5de56dSgjelinek  * mountpoint.  Since it is file system already, we don't have the
8840b5de56dSgjelinek  * issue of cross-file system copying.
8850b5de56dSgjelinek  */
8860b5de56dSgjelinek int
8870b5de56dSgjelinek move_zfs(char *zonepath, char *new_zonepath)
8880b5de56dSgjelinek {
8890b5de56dSgjelinek 	int		ret = Z_ERR;
8900b5de56dSgjelinek 	zfs_handle_t	*zhp;
8910b5de56dSgjelinek 
8920b5de56dSgjelinek 	zfs_set_error_handler(noop_err_handler);
8930b5de56dSgjelinek 
8940b5de56dSgjelinek 	if ((zhp = mount2zhandle(zonepath)) == NULL) {
8950b5de56dSgjelinek 		zfs_set_error_handler(NULL);
8960b5de56dSgjelinek 		return (Z_ERR);
8970b5de56dSgjelinek 	}
8980b5de56dSgjelinek 
8990b5de56dSgjelinek 	if (zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, new_zonepath) == 0) {
9000b5de56dSgjelinek 		/*
9010b5de56dSgjelinek 		 * Clean up the old mount point.  We ignore any failure since
9020b5de56dSgjelinek 		 * the zone is already successfully mounted on the new path.
9030b5de56dSgjelinek 		 */
9040b5de56dSgjelinek 		(void) rmdir(zonepath);
9050b5de56dSgjelinek 		ret = Z_OK;
9060b5de56dSgjelinek 	}
9070b5de56dSgjelinek 
9080b5de56dSgjelinek 	zfs_close(zhp);
9090b5de56dSgjelinek 	zfs_set_error_handler(NULL);
9100b5de56dSgjelinek 
9110b5de56dSgjelinek 	return (ret);
9120b5de56dSgjelinek }
9130b5de56dSgjelinek 
9140b5de56dSgjelinek /*
9150b5de56dSgjelinek  * Validate that the given dataset exists on the system, and that neither it nor
9160b5de56dSgjelinek  * its children are zvols.
9170b5de56dSgjelinek  *
9180b5de56dSgjelinek  * Note that we don't do anything with the 'zoned' property here.  All
9190b5de56dSgjelinek  * management is done in zoneadmd when the zone is actually rebooted.  This
9200b5de56dSgjelinek  * allows us to automatically set the zoned property even when a zone is
9210b5de56dSgjelinek  * rebooted by the administrator.
9220b5de56dSgjelinek  */
9230b5de56dSgjelinek int
9240b5de56dSgjelinek verify_datasets(zone_dochandle_t handle)
9250b5de56dSgjelinek {
9260b5de56dSgjelinek 	int return_code = Z_OK;
9270b5de56dSgjelinek 	struct zone_dstab dstab;
9280b5de56dSgjelinek 	zfs_handle_t *zhp;
9290b5de56dSgjelinek 	char propbuf[ZFS_MAXPROPLEN];
9300b5de56dSgjelinek 	char source[ZFS_MAXNAMELEN];
9310b5de56dSgjelinek 	zfs_source_t srctype;
9320b5de56dSgjelinek 
9330b5de56dSgjelinek 	if (zonecfg_setdsent(handle) != Z_OK) {
9340b5de56dSgjelinek 		/*
9350b5de56dSgjelinek 		 * TRANSLATION_NOTE
9360b5de56dSgjelinek 		 * zfs and dataset are literals that should not be translated.
9370b5de56dSgjelinek 		 */
9380b5de56dSgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs datasets: "
9390b5de56dSgjelinek 		    "unable to enumerate datasets\n"));
9400b5de56dSgjelinek 		return (Z_ERR);
9410b5de56dSgjelinek 	}
9420b5de56dSgjelinek 
9430b5de56dSgjelinek 	zfs_set_error_handler(err_handler);
9440b5de56dSgjelinek 
9450b5de56dSgjelinek 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
9460b5de56dSgjelinek 
9470b5de56dSgjelinek 		current_dataset = dstab.zone_dataset_name;
9480b5de56dSgjelinek 
9490b5de56dSgjelinek 		if ((zhp = zfs_open(dstab.zone_dataset_name,
9500b5de56dSgjelinek 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
9510b5de56dSgjelinek 			return_code = Z_ERR;
9520b5de56dSgjelinek 			continue;
9530b5de56dSgjelinek 		}
9540b5de56dSgjelinek 
9550b5de56dSgjelinek 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf,
9560b5de56dSgjelinek 		    sizeof (propbuf), &srctype, source,
9570b5de56dSgjelinek 		    sizeof (source), 0) == 0 &&
9580b5de56dSgjelinek 		    (srctype == ZFS_SRC_INHERITED)) {
9590b5de56dSgjelinek 			(void) fprintf(stderr, gettext("could not verify zfs "
9600b5de56dSgjelinek 			    "dataset %s: mountpoint cannot be inherited\n"),
9610b5de56dSgjelinek 			    dstab.zone_dataset_name);
9620b5de56dSgjelinek 			return_code = Z_ERR;
9630b5de56dSgjelinek 			zfs_close(zhp);
9640b5de56dSgjelinek 			continue;
9650b5de56dSgjelinek 		}
9660b5de56dSgjelinek 
9670b5de56dSgjelinek 		if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
9680b5de56dSgjelinek 			(void) fprintf(stderr, gettext("cannot verify zfs "
9690b5de56dSgjelinek 			    "dataset %s: volumes cannot be specified as a "
9700b5de56dSgjelinek 			    "zone dataset resource\n"),
9710b5de56dSgjelinek 			    dstab.zone_dataset_name);
9720b5de56dSgjelinek 			return_code = Z_ERR;
9730b5de56dSgjelinek 		}
9740b5de56dSgjelinek 
9750b5de56dSgjelinek 		if (zfs_iter_children(zhp, check_zvol, NULL) != 0)
9760b5de56dSgjelinek 			return_code = Z_ERR;
9770b5de56dSgjelinek 
9780b5de56dSgjelinek 		zfs_close(zhp);
9790b5de56dSgjelinek 	}
9800b5de56dSgjelinek 	(void) zonecfg_enddsent(handle);
9810b5de56dSgjelinek 	zfs_set_error_handler(NULL);
9820b5de56dSgjelinek 
9830b5de56dSgjelinek 	return (return_code);
9840b5de56dSgjelinek }
9850b5de56dSgjelinek 
9860b5de56dSgjelinek /*
9870b5de56dSgjelinek  * Verify that the ZFS dataset exists, and its mountpoint
9880b5de56dSgjelinek  * property is set to "legacy".
9890b5de56dSgjelinek  */
9900b5de56dSgjelinek int
9910b5de56dSgjelinek verify_fs_zfs(struct zone_fstab *fstab)
9920b5de56dSgjelinek {
9930b5de56dSgjelinek 	zfs_handle_t *zhp;
9940b5de56dSgjelinek 	char propbuf[ZFS_MAXPROPLEN];
9950b5de56dSgjelinek 
9960b5de56dSgjelinek 	zfs_set_error_handler(noop_err_handler);
9970b5de56dSgjelinek 
9980b5de56dSgjelinek 	if ((zhp = zfs_open(fstab->zone_fs_special, ZFS_TYPE_ANY)) == NULL) {
9990b5de56dSgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
10000b5de56dSgjelinek 		    "could not access zfs dataset '%s'\n"),
10010b5de56dSgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
10020b5de56dSgjelinek 		zfs_set_error_handler(NULL);
10030b5de56dSgjelinek 		return (Z_ERR);
10040b5de56dSgjelinek 	}
10050b5de56dSgjelinek 
10060b5de56dSgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
10070b5de56dSgjelinek 		(void) fprintf(stderr, gettext("cannot verify fs %s: "
10080b5de56dSgjelinek 		    "'%s' is not a file system\n"),
10090b5de56dSgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
10100b5de56dSgjelinek 		zfs_close(zhp);
10110b5de56dSgjelinek 		zfs_set_error_handler(NULL);
10120b5de56dSgjelinek 		return (Z_ERR);
10130b5de56dSgjelinek 	}
10140b5de56dSgjelinek 
10150b5de56dSgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
10160b5de56dSgjelinek 	    NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
10170b5de56dSgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
10180b5de56dSgjelinek 		    "zfs '%s' mountpoint is not \"legacy\"\n"),
10190b5de56dSgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
10200b5de56dSgjelinek 		zfs_close(zhp);
10210b5de56dSgjelinek 		zfs_set_error_handler(NULL);
10220b5de56dSgjelinek 		return (Z_ERR);
10230b5de56dSgjelinek 	}
10240b5de56dSgjelinek 
10250b5de56dSgjelinek 	zfs_close(zhp);
10260b5de56dSgjelinek 	zfs_set_error_handler(NULL);
10270b5de56dSgjelinek 	return (Z_OK);
10280b5de56dSgjelinek }
1029