xref: /onnv-gate/usr/src/cmd/zoneadm/zfs.c (revision 9386:8fc9dca843a1)
11867Sgjelinek /*
21867Sgjelinek  * CDDL HEADER START
31867Sgjelinek  *
41867Sgjelinek  * The contents of this file are subject to the terms of the
51867Sgjelinek  * Common Development and Distribution License (the "License").
61867Sgjelinek  * You may not use this file except in compliance with the License.
71867Sgjelinek  *
81867Sgjelinek  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91867Sgjelinek  * or http://www.opensolaris.org/os/licensing.
101867Sgjelinek  * See the License for the specific language governing permissions
111867Sgjelinek  * and limitations under the License.
121867Sgjelinek  *
131867Sgjelinek  * When distributing Covered Code, include this CDDL HEADER in each
141867Sgjelinek  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151867Sgjelinek  * If applicable, add the following below this CDDL HEADER, with the
161867Sgjelinek  * fields enclosed by brackets "[]" replaced with your own identifying
171867Sgjelinek  * information: Portions Copyright [yyyy] [name of copyright owner]
181867Sgjelinek  *
191867Sgjelinek  * CDDL HEADER END
201867Sgjelinek  */
211867Sgjelinek 
221867Sgjelinek /*
239023Sgerald.jelinek@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241867Sgjelinek  * Use is subject to license terms.
251867Sgjelinek  */
261867Sgjelinek 
271867Sgjelinek /*
281867Sgjelinek  * This file contains the functions used to support the ZFS integration
291867Sgjelinek  * with zones.  This includes validation (e.g. zonecfg dataset), cloning,
301867Sgjelinek  * file system creation and destruction.
311867Sgjelinek  */
321867Sgjelinek 
331867Sgjelinek #include <stdio.h>
341867Sgjelinek #include <errno.h>
351867Sgjelinek #include <unistd.h>
361867Sgjelinek #include <string.h>
371867Sgjelinek #include <locale.h>
381867Sgjelinek #include <libintl.h>
391867Sgjelinek #include <sys/stat.h>
401867Sgjelinek #include <sys/statvfs.h>
411867Sgjelinek #include <libgen.h>
421867Sgjelinek #include <libzonecfg.h>
431867Sgjelinek #include <sys/mnttab.h>
441867Sgjelinek #include <libzfs.h>
457093Sgjelinek #include <sys/mntent.h>
46*9386Sgerald.jelinek@sun.com #include <values.h>
471867Sgjelinek 
481867Sgjelinek #include "zoneadm.h"
491867Sgjelinek 
502082Seschrock libzfs_handle_t *g_zfs;
511867Sgjelinek 
521867Sgjelinek typedef struct zfs_mount_data {
531867Sgjelinek 	char		*match_name;
541867Sgjelinek 	zfs_handle_t	*match_handle;
551867Sgjelinek } zfs_mount_data_t;
561867Sgjelinek 
571867Sgjelinek typedef struct zfs_snapshot_data {
58*9386Sgerald.jelinek@sun.com 	char	*match_name;	/* zonename@SUNWzone */
59*9386Sgerald.jelinek@sun.com 	int	len;		/* strlen of match_name */
60*9386Sgerald.jelinek@sun.com 	int	max;		/* highest digit appended to snap name */
61*9386Sgerald.jelinek@sun.com 	int	num;		/* number of snapshots to rename */
62*9386Sgerald.jelinek@sun.com 	int	cntr;		/* counter for renaming snapshots */
631867Sgjelinek } zfs_snapshot_data_t;
641867Sgjelinek 
65*9386Sgerald.jelinek@sun.com typedef struct clone_data {
66*9386Sgerald.jelinek@sun.com 	zfs_handle_t	*clone_zhp;	/* clone dataset to promote */
67*9386Sgerald.jelinek@sun.com 	time_t		origin_creation; /* snapshot creation time of clone */
68*9386Sgerald.jelinek@sun.com 	const char	*snapshot;	/* snapshot of dataset being demoted */
69*9386Sgerald.jelinek@sun.com } clone_data_t;
70*9386Sgerald.jelinek@sun.com 
711867Sgjelinek /*
721867Sgjelinek  * A ZFS file system iterator call-back function which is used to validate
731867Sgjelinek  * datasets imported into the zone.
741867Sgjelinek  */
751867Sgjelinek /* ARGSUSED */
761867Sgjelinek static int
771867Sgjelinek check_zvol(zfs_handle_t *zhp, void *unused)
781867Sgjelinek {
791867Sgjelinek 	int ret;
801867Sgjelinek 
811867Sgjelinek 	if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
821867Sgjelinek 		/*
831867Sgjelinek 		 * TRANSLATION_NOTE
841867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
851867Sgjelinek 		 */
861867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify zfs dataset %s: "
871867Sgjelinek 		    "volumes cannot be specified as a zone dataset resource\n"),
881867Sgjelinek 		    zfs_get_name(zhp));
891867Sgjelinek 		ret = -1;
901867Sgjelinek 	} else {
911867Sgjelinek 		ret = zfs_iter_children(zhp, check_zvol, NULL);
921867Sgjelinek 	}
931867Sgjelinek 
941867Sgjelinek 	zfs_close(zhp);
951867Sgjelinek 
961867Sgjelinek 	return (ret);
971867Sgjelinek }
981867Sgjelinek 
991867Sgjelinek /*
1001867Sgjelinek  * A ZFS file system iterator call-back function which returns the
1011867Sgjelinek  * zfs_handle_t for a ZFS file system on the specified mount point.
1021867Sgjelinek  */
1031867Sgjelinek static int
1041867Sgjelinek match_mountpoint(zfs_handle_t *zhp, void *data)
1051867Sgjelinek {
1061867Sgjelinek 	int			res;
1071867Sgjelinek 	zfs_mount_data_t	*cbp;
1081867Sgjelinek 	char			mp[ZFS_MAXPROPLEN];
1091867Sgjelinek 
1101867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1111867Sgjelinek 		zfs_close(zhp);
1121867Sgjelinek 		return (0);
1131867Sgjelinek 	}
1141867Sgjelinek 
1157093Sgjelinek 	/* First check if the dataset is mounted. */
1167093Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
1177093Sgjelinek 	    0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
1187093Sgjelinek 		zfs_close(zhp);
1197093Sgjelinek 		return (0);
1207093Sgjelinek 	}
1217093Sgjelinek 
1227093Sgjelinek 	/* Now check mount point. */
1231867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
1247093Sgjelinek 	    0, B_FALSE) != 0) {
1257093Sgjelinek 		zfs_close(zhp);
1267093Sgjelinek 		return (0);
1277093Sgjelinek 	}
1287093Sgjelinek 
1297093Sgjelinek 	cbp = (zfs_mount_data_t *)data;
1307093Sgjelinek 
1317093Sgjelinek 	if (strcmp(mp, "legacy") == 0) {
1327093Sgjelinek 		/* If legacy, must look in mnttab for mountpoint. */
1337093Sgjelinek 		FILE		*fp;
1347093Sgjelinek 		struct mnttab	entry;
1357093Sgjelinek 		const char	*nm;
1367093Sgjelinek 
1377093Sgjelinek 		nm = zfs_get_name(zhp);
1387093Sgjelinek 		if ((fp = fopen(MNTTAB, "r")) == NULL) {
1397093Sgjelinek 			zfs_close(zhp);
1407093Sgjelinek 			return (0);
1417093Sgjelinek 		}
1427093Sgjelinek 
1437093Sgjelinek 		while (getmntent(fp, &entry) == 0) {
1447093Sgjelinek 			if (strcmp(nm, entry.mnt_special) == 0) {
1457093Sgjelinek 				if (strcmp(entry.mnt_mountp, cbp->match_name)
1467093Sgjelinek 				    == 0) {
1477093Sgjelinek 					(void) fclose(fp);
1487093Sgjelinek 					cbp->match_handle = zhp;
1497093Sgjelinek 					return (1);
1507093Sgjelinek 				}
1517093Sgjelinek 				break;
1527093Sgjelinek 			}
1537093Sgjelinek 		}
1547093Sgjelinek 		(void) fclose(fp);
1557093Sgjelinek 
1567093Sgjelinek 	} else if (strcmp(mp, cbp->match_name) == 0) {
1571867Sgjelinek 		cbp->match_handle = zhp;
1581867Sgjelinek 		return (1);
1591867Sgjelinek 	}
1601867Sgjelinek 
1617093Sgjelinek 	/* Iterate over any nested datasets. */
1621867Sgjelinek 	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1631867Sgjelinek 	zfs_close(zhp);
1641867Sgjelinek 	return (res);
1651867Sgjelinek }
1661867Sgjelinek 
1671867Sgjelinek /*
1681867Sgjelinek  * Get ZFS handle for the specified mount point.
1691867Sgjelinek  */
1701867Sgjelinek static zfs_handle_t *
1711867Sgjelinek mount2zhandle(char *mountpoint)
1721867Sgjelinek {
1731867Sgjelinek 	zfs_mount_data_t	cb;
1741867Sgjelinek 
1751867Sgjelinek 	cb.match_name = mountpoint;
1761867Sgjelinek 	cb.match_handle = NULL;
1772082Seschrock 	(void) zfs_iter_root(g_zfs, match_mountpoint, &cb);
1781867Sgjelinek 	return (cb.match_handle);
1791867Sgjelinek }
1801867Sgjelinek 
1811867Sgjelinek /*
1821867Sgjelinek  * Check if there is already a file system (zfs or any other type) mounted on
1831867Sgjelinek  * path.
1841867Sgjelinek  */
1851867Sgjelinek static boolean_t
1861867Sgjelinek is_mountpnt(char *path)
1871867Sgjelinek {
1881867Sgjelinek 	FILE		*fp;
1891867Sgjelinek 	struct mnttab	entry;
1901867Sgjelinek 
1917093Sgjelinek 	if ((fp = fopen(MNTTAB, "r")) == NULL)
1921867Sgjelinek 		return (B_FALSE);
1931867Sgjelinek 
1941867Sgjelinek 	while (getmntent(fp, &entry) == 0) {
1951867Sgjelinek 		if (strcmp(path, entry.mnt_mountp) == 0) {
1961867Sgjelinek 			(void) fclose(fp);
1971867Sgjelinek 			return (B_TRUE);
1981867Sgjelinek 		}
1991867Sgjelinek 	}
2001867Sgjelinek 
2011867Sgjelinek 	(void) fclose(fp);
2021867Sgjelinek 	return (B_FALSE);
2031867Sgjelinek }
2041867Sgjelinek 
2051867Sgjelinek /*
2067089Sgjelinek  * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
2071867Sgjelinek  */
2081867Sgjelinek static int
2097089Sgjelinek pre_snapshot(char *presnapbuf)
2101867Sgjelinek {
2117089Sgjelinek 	int status;
2121867Sgjelinek 
2137089Sgjelinek 	/* No brand-specific handler */
2147089Sgjelinek 	if (presnapbuf[0] == '\0')
2157089Sgjelinek 		return (Z_OK);
2161867Sgjelinek 
2177089Sgjelinek 	/* Run the hook */
2189310Sgerald.jelinek@sun.com 	status = do_subproc(presnapbuf);
2197089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific presnapshot"),
2207089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2211867Sgjelinek 		return (Z_ERR);
2221867Sgjelinek 
2231867Sgjelinek 	return (Z_OK);
2241867Sgjelinek }
2251867Sgjelinek 
2261867Sgjelinek /*
2277089Sgjelinek  * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
2281867Sgjelinek  */
2291867Sgjelinek static int
2307089Sgjelinek post_snapshot(char *postsnapbuf)
2311867Sgjelinek {
2327089Sgjelinek 	int status;
2331867Sgjelinek 
2347089Sgjelinek 	/* No brand-specific handler */
2357089Sgjelinek 	if (postsnapbuf[0] == '\0')
2367089Sgjelinek 		return (Z_OK);
2377089Sgjelinek 
2387089Sgjelinek 	/* Run the hook */
2399310Sgerald.jelinek@sun.com 	status = do_subproc(postsnapbuf);
2407089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific postsnapshot"),
2417089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2421867Sgjelinek 		return (Z_ERR);
2431867Sgjelinek 
2441867Sgjelinek 	return (Z_OK);
2451867Sgjelinek }
2461867Sgjelinek 
2471867Sgjelinek /*
2481867Sgjelinek  * This is a ZFS snapshot iterator call-back function which returns the
2491867Sgjelinek  * highest number of SUNWzone snapshots that have been taken.
2501867Sgjelinek  */
2511867Sgjelinek static int
2521867Sgjelinek get_snap_max(zfs_handle_t *zhp, void *data)
2531867Sgjelinek {
2541867Sgjelinek 	int			res;
2551867Sgjelinek 	zfs_snapshot_data_t	*cbp;
2561867Sgjelinek 
2571867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2581867Sgjelinek 		zfs_close(zhp);
2591867Sgjelinek 		return (0);
2601867Sgjelinek 	}
2611867Sgjelinek 
2621867Sgjelinek 	cbp = (zfs_snapshot_data_t *)data;
2631867Sgjelinek 
2641867Sgjelinek 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2651867Sgjelinek 		char	*nump;
2661867Sgjelinek 		int	num;
2671867Sgjelinek 
268*9386Sgerald.jelinek@sun.com 		cbp->num++;
2691867Sgjelinek 		nump = (char *)(zfs_get_name(zhp) + cbp->len);
2701867Sgjelinek 		num = atoi(nump);
2711867Sgjelinek 		if (num > cbp->max)
2721867Sgjelinek 			cbp->max = num;
2731867Sgjelinek 	}
2741867Sgjelinek 
2751867Sgjelinek 	res = zfs_iter_snapshots(zhp, get_snap_max, data);
2761867Sgjelinek 	zfs_close(zhp);
2771867Sgjelinek 	return (res);
2781867Sgjelinek }
2791867Sgjelinek 
2801867Sgjelinek /*
2811867Sgjelinek  * Take a ZFS snapshot to be used for cloning the zone.
2821867Sgjelinek  */
2831867Sgjelinek static int
2847089Sgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
2857089Sgjelinek     char *presnapbuf, char *postsnapbuf)
2861867Sgjelinek {
2871867Sgjelinek 	int			res;
2881867Sgjelinek 	char			template[ZFS_MAXNAMELEN];
2891867Sgjelinek 	zfs_snapshot_data_t	cb;
2901867Sgjelinek 
2911867Sgjelinek 	/*
2921867Sgjelinek 	 * First we need to figure out the next available name for the
2931867Sgjelinek 	 * zone snapshot.  Look through the list of zones snapshots for
2941867Sgjelinek 	 * this file system to determine the maximum snapshot name.
2951867Sgjelinek 	 */
2961867Sgjelinek 	if (snprintf(template, sizeof (template), "%s@SUNWzone",
2971867Sgjelinek 	    zfs_get_name(zhp)) >=  sizeof (template))
2981867Sgjelinek 		return (Z_ERR);
2991867Sgjelinek 
3001867Sgjelinek 	cb.match_name = template;
3011867Sgjelinek 	cb.len = strlen(template);
3021867Sgjelinek 	cb.max = 0;
3031867Sgjelinek 
3041867Sgjelinek 	if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0)
3051867Sgjelinek 		return (Z_ERR);
3061867Sgjelinek 
3071867Sgjelinek 	cb.max++;
3081867Sgjelinek 
3091867Sgjelinek 	if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
3101867Sgjelinek 	    zfs_get_name(zhp), cb.max) >= snap_size)
3111867Sgjelinek 		return (Z_ERR);
3121867Sgjelinek 
3137089Sgjelinek 	if (pre_snapshot(presnapbuf) != Z_OK)
3141867Sgjelinek 		return (Z_ERR);
3157265Sahrens 	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
3167089Sgjelinek 	if (post_snapshot(postsnapbuf) != Z_OK)
3171867Sgjelinek 		return (Z_ERR);
3181867Sgjelinek 
3191867Sgjelinek 	if (res != 0)
3201867Sgjelinek 		return (Z_ERR);
3211867Sgjelinek 	return (Z_OK);
3221867Sgjelinek }
3231867Sgjelinek 
3241867Sgjelinek /*
3251867Sgjelinek  * We are using an explicit snapshot from some earlier point in time so
3267089Sgjelinek  * we need to validate it.  Run the brand specific hook.
3271867Sgjelinek  */
3281867Sgjelinek static int
3297089Sgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
3301867Sgjelinek {
3317089Sgjelinek 	int status;
3327089Sgjelinek 	char cmdbuf[MAXPATHLEN];
3331867Sgjelinek 
3347089Sgjelinek 	/* No brand-specific handler */
3357089Sgjelinek 	if (validsnapbuf[0] == '\0')
3367089Sgjelinek 		return (Z_OK);
3371867Sgjelinek 
3387089Sgjelinek 	/* pass args - snapshot_name & snap_path */
3397089Sgjelinek 	if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
3407089Sgjelinek 	    snapshot_name, snap_path) >= sizeof (cmdbuf)) {
3417089Sgjelinek 		zerror("Command line too long");
3421867Sgjelinek 		return (Z_ERR);
3431867Sgjelinek 	}
3441867Sgjelinek 
3457089Sgjelinek 	/* Run the hook */
3469310Sgerald.jelinek@sun.com 	status = do_subproc(cmdbuf);
3477089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
3487089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
3497089Sgjelinek 		return (Z_ERR);
3501867Sgjelinek 
3517089Sgjelinek 	return (Z_OK);
3521867Sgjelinek }
3531867Sgjelinek 
3541867Sgjelinek /*
3551867Sgjelinek  * Remove the sw inventory file from inside this zonepath that we picked up out
3561867Sgjelinek  * of the snapshot.
3571867Sgjelinek  */
3581867Sgjelinek static int
3591867Sgjelinek clean_out_clone()
3601867Sgjelinek {
3611867Sgjelinek 	int err;
3621867Sgjelinek 	zone_dochandle_t handle;
3631867Sgjelinek 
3641867Sgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
3651867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3661867Sgjelinek 		return (Z_ERR);
3671867Sgjelinek 	}
3681867Sgjelinek 
3691867Sgjelinek 	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3701867Sgjelinek 		errno = err;
3711867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3721867Sgjelinek 		zonecfg_fini_handle(handle);
3731867Sgjelinek 		return (Z_ERR);
3741867Sgjelinek 	}
3751867Sgjelinek 
3761867Sgjelinek 	zonecfg_rm_detached(handle, B_FALSE);
3771867Sgjelinek 	zonecfg_fini_handle(handle);
3781867Sgjelinek 
3791867Sgjelinek 	return (Z_OK);
3801867Sgjelinek }
3811867Sgjelinek 
3821867Sgjelinek /*
3831867Sgjelinek  * Make a ZFS clone on zonepath from snapshot_name.
3841867Sgjelinek  */
3851867Sgjelinek static int
3861867Sgjelinek clone_snap(char *snapshot_name, char *zonepath)
3871867Sgjelinek {
3881867Sgjelinek 	int		res = Z_OK;
3891867Sgjelinek 	int		err;
3901867Sgjelinek 	zfs_handle_t	*zhp;
3911867Sgjelinek 	zfs_handle_t	*clone;
3922676Seschrock 	nvlist_t	*props = NULL;
3931867Sgjelinek 
3942082Seschrock 	if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
3951867Sgjelinek 		return (Z_NO_ENTRY);
3961867Sgjelinek 
3971867Sgjelinek 	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
3981867Sgjelinek 
3992676Seschrock 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
4002744Snn35248 	    nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
4012744Snn35248 	    "off") != 0) {
4022744Snn35248 		if (props != NULL)
4032744Snn35248 			nvlist_free(props);
4042676Seschrock 		(void) fprintf(stderr, gettext("could not create ZFS clone "
4052676Seschrock 		    "%s: out of memory\n"), zonepath);
4062676Seschrock 		return (Z_ERR);
4072676Seschrock 	}
4082676Seschrock 
4092676Seschrock 	err = zfs_clone(zhp, zonepath, props);
4101867Sgjelinek 	zfs_close(zhp);
4112676Seschrock 
4122676Seschrock 	nvlist_free(props);
4132676Seschrock 
4141867Sgjelinek 	if (err != 0)
4151867Sgjelinek 		return (Z_ERR);
4161867Sgjelinek 
4171867Sgjelinek 	/* create the mountpoint if necessary */
4185094Slling 	if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL)
4191867Sgjelinek 		return (Z_ERR);
4201867Sgjelinek 
4211867Sgjelinek 	/*
4221867Sgjelinek 	 * The clone has been created so we need to print a diagnostic
4231867Sgjelinek 	 * message if one of the following steps fails for some reason.
4241867Sgjelinek 	 */
4251867Sgjelinek 	if (zfs_mount(clone, NULL, 0) != 0) {
4261867Sgjelinek 		(void) fprintf(stderr, gettext("could not mount ZFS clone "
4271867Sgjelinek 		    "%s\n"), zfs_get_name(clone));
4281867Sgjelinek 		res = Z_ERR;
4291867Sgjelinek 
4302676Seschrock 	} else if (clean_out_clone() != Z_OK) {
4312676Seschrock 		(void) fprintf(stderr, gettext("could not remove the "
4322676Seschrock 		    "software inventory from ZFS clone %s\n"),
4332676Seschrock 		    zfs_get_name(clone));
4342676Seschrock 		res = Z_ERR;
4351867Sgjelinek 	}
4361867Sgjelinek 
4371867Sgjelinek 	zfs_close(clone);
4381867Sgjelinek 	return (res);
4391867Sgjelinek }
4401867Sgjelinek 
4411867Sgjelinek /*
4421867Sgjelinek  * This function takes a zonepath and attempts to determine what the ZFS
4431867Sgjelinek  * file system name (not mountpoint) should be for that path.  We do not
4441867Sgjelinek  * assume that zonepath is an existing directory or ZFS fs since we use
4451867Sgjelinek  * this function as part of the process of creating a new ZFS fs or clone.
4461867Sgjelinek  *
4471867Sgjelinek  * The way this works is that we look at the parent directory of the zonepath
4481867Sgjelinek  * to see if it is a ZFS fs.  If it is, we get the name of that ZFS fs and
4491867Sgjelinek  * append the last component of the zonepath to generate the ZFS name for the
4501867Sgjelinek  * zonepath.  This matches the algorithm that ZFS uses for automatically
4511867Sgjelinek  * mounting a new fs after it is created.
4521867Sgjelinek  *
4531867Sgjelinek  * Although a ZFS fs can be mounted anywhere, we don't worry about handling
4541867Sgjelinek  * all of the complexity that a user could possibly configure with arbitrary
4551867Sgjelinek  * mounts since there is no way to generate a ZFS name from a random path in
4561867Sgjelinek  * the file system.  We only try to handle the automatic mounts that ZFS does
4571867Sgjelinek  * for each file system.  ZFS restricts this so that a new fs must be created
4581867Sgjelinek  * in an existing parent ZFS fs.  It then automatically mounts the new fs
4591867Sgjelinek  * directly under the mountpoint for the parent fs using the last component
4601867Sgjelinek  * of the name as the mountpoint directory.
4611867Sgjelinek  *
4621867Sgjelinek  * For example:
4631867Sgjelinek  *    Name			Mountpoint
4641867Sgjelinek  *    space/eng/dev/test/zone1	/project1/eng/dev/test/zone1
4651867Sgjelinek  *
4661867Sgjelinek  * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
4671867Sgjelinek  * Z_ERR.
4681867Sgjelinek  */
4691867Sgjelinek static int
4701867Sgjelinek path2name(char *zonepath, char *zfs_name, int len)
4711867Sgjelinek {
4721867Sgjelinek 	int		res;
4737093Sgjelinek 	char		*bnm, *dnm, *dname, *bname;
4741867Sgjelinek 	zfs_handle_t	*zhp;
4757093Sgjelinek 	struct stat	stbuf;
4761867Sgjelinek 
4777093Sgjelinek 	/*
4787093Sgjelinek 	 * We need two tmp strings to handle paths directly in / (e.g. /foo)
4797093Sgjelinek 	 * since dirname will overwrite the first char after "/" in this case.
4807093Sgjelinek 	 */
4817093Sgjelinek 	if ((bnm = strdup(zonepath)) == NULL)
4821867Sgjelinek 		return (Z_ERR);
4831867Sgjelinek 
4847093Sgjelinek 	if ((dnm = strdup(zonepath)) == NULL) {
4857093Sgjelinek 		free(bnm);
4867093Sgjelinek 		return (Z_ERR);
4877093Sgjelinek 	}
4887093Sgjelinek 
4897093Sgjelinek 	bname = basename(bnm);
4907093Sgjelinek 	dname = dirname(dnm);
4917093Sgjelinek 
4921867Sgjelinek 	/*
4937093Sgjelinek 	 * This is a quick test to save iterating over all of the zfs datasets
4947093Sgjelinek 	 * on the system (which can be a lot).  If the parent dir is not in a
4957093Sgjelinek 	 * ZFS fs, then we're done.
4961867Sgjelinek 	 */
4977093Sgjelinek 	if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) ||
4987093Sgjelinek 	    strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) {
4997093Sgjelinek 		free(bnm);
5007093Sgjelinek 		free(dnm);
5011867Sgjelinek 		return (Z_ERR);
5027093Sgjelinek 	}
5031867Sgjelinek 
5047093Sgjelinek 	/* See if the parent directory is its own ZFS dataset. */
5057093Sgjelinek 	if ((zhp = mount2zhandle(dname)) == NULL) {
5067093Sgjelinek 		/*
5077093Sgjelinek 		 * The parent is not a ZFS dataset so we can't automatically
5087093Sgjelinek 		 * create a dataset on the given path.
5097093Sgjelinek 		 */
5107093Sgjelinek 		free(bnm);
5117093Sgjelinek 		free(dnm);
5127093Sgjelinek 		return (Z_ERR);
5137093Sgjelinek 	}
5141867Sgjelinek 
5157093Sgjelinek 	res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname);
5167093Sgjelinek 
5177093Sgjelinek 	free(bnm);
5187093Sgjelinek 	free(dnm);
5191867Sgjelinek 	zfs_close(zhp);
5201867Sgjelinek 	if (res >= len)
5211867Sgjelinek 		return (Z_ERR);
5221867Sgjelinek 
5231867Sgjelinek 	return (Z_OK);
5241867Sgjelinek }
5251867Sgjelinek 
5261867Sgjelinek /*
5271867Sgjelinek  * A ZFS file system iterator call-back function used to determine if the
5281867Sgjelinek  * file system has dependents (snapshots & clones).
5291867Sgjelinek  */
5301867Sgjelinek /* ARGSUSED */
5311867Sgjelinek static int
5321867Sgjelinek has_dependent(zfs_handle_t *zhp, void *data)
5331867Sgjelinek {
5341867Sgjelinek 	zfs_close(zhp);
5351867Sgjelinek 	return (1);
5361867Sgjelinek }
5371867Sgjelinek 
5381867Sgjelinek /*
5391867Sgjelinek  * Given a snapshot name, get the file system path where the snapshot lives.
5401867Sgjelinek  * A snapshot name is of the form fs_name@snap_name.  For example, snapshot
5411867Sgjelinek  * pl/zones/z1@SUNWzone1 would have a path of
5421867Sgjelinek  * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5431867Sgjelinek  */
5441867Sgjelinek static int
5451867Sgjelinek snap2path(char *snap_name, char *path, int len)
5461867Sgjelinek {
5471867Sgjelinek 	char		*p;
5481867Sgjelinek 	zfs_handle_t	*zhp;
5491867Sgjelinek 	char		mp[ZFS_MAXPROPLEN];
5501867Sgjelinek 
5511867Sgjelinek 	if ((p = strrchr(snap_name, '@')) == NULL)
5521867Sgjelinek 		return (Z_ERR);
5531867Sgjelinek 
5541867Sgjelinek 	/* Get the file system name from the snap_name. */
5551867Sgjelinek 	*p = '\0';
5565094Slling 	zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET);
5571867Sgjelinek 	*p = '@';
5581867Sgjelinek 	if (zhp == NULL)
5591867Sgjelinek 		return (Z_ERR);
5601867Sgjelinek 
5611867Sgjelinek 	/* Get the file system mount point. */
5621867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
5632082Seschrock 	    0, B_FALSE) != 0) {
5641867Sgjelinek 		zfs_close(zhp);
5651867Sgjelinek 		return (Z_ERR);
5661867Sgjelinek 	}
5671867Sgjelinek 	zfs_close(zhp);
5681867Sgjelinek 
5691867Sgjelinek 	p++;
5701867Sgjelinek 	if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5711867Sgjelinek 		return (Z_ERR);
5721867Sgjelinek 
5731867Sgjelinek 	return (Z_OK);
5741867Sgjelinek }
5751867Sgjelinek 
5761867Sgjelinek /*
577*9386Sgerald.jelinek@sun.com  * This callback function is used to iterate through a snapshot's dependencies
578*9386Sgerald.jelinek@sun.com  * to find a filesystem that is a direct clone of the snapshot being iterated.
579*9386Sgerald.jelinek@sun.com  */
580*9386Sgerald.jelinek@sun.com static int
581*9386Sgerald.jelinek@sun.com get_direct_clone(zfs_handle_t *zhp, void *data)
582*9386Sgerald.jelinek@sun.com {
583*9386Sgerald.jelinek@sun.com 	clone_data_t	*cd = data;
584*9386Sgerald.jelinek@sun.com 	char		origin[ZFS_MAXNAMELEN];
585*9386Sgerald.jelinek@sun.com 	char		ds_path[ZFS_MAXNAMELEN];
586*9386Sgerald.jelinek@sun.com 
587*9386Sgerald.jelinek@sun.com 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
588*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
589*9386Sgerald.jelinek@sun.com 		return (0);
590*9386Sgerald.jelinek@sun.com 	}
591*9386Sgerald.jelinek@sun.com 
592*9386Sgerald.jelinek@sun.com 	(void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
593*9386Sgerald.jelinek@sun.com 
594*9386Sgerald.jelinek@sun.com 	/* Make sure this is a direct clone of the snapshot we're iterating. */
595*9386Sgerald.jelinek@sun.com 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
596*9386Sgerald.jelinek@sun.com 	    NULL, 0, B_FALSE) != 0 || strcmp(origin, cd->snapshot) != 0) {
597*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
598*9386Sgerald.jelinek@sun.com 		return (0);
599*9386Sgerald.jelinek@sun.com 	}
600*9386Sgerald.jelinek@sun.com 
601*9386Sgerald.jelinek@sun.com 	if (cd->clone_zhp != NULL)
602*9386Sgerald.jelinek@sun.com 		zfs_close(cd->clone_zhp);
603*9386Sgerald.jelinek@sun.com 
604*9386Sgerald.jelinek@sun.com 	cd->clone_zhp = zhp;
605*9386Sgerald.jelinek@sun.com 	return (1);
606*9386Sgerald.jelinek@sun.com }
607*9386Sgerald.jelinek@sun.com 
608*9386Sgerald.jelinek@sun.com /*
609*9386Sgerald.jelinek@sun.com  * A ZFS file system iterator call-back function used to determine the clone
610*9386Sgerald.jelinek@sun.com  * to promote.  This function finds the youngest (i.e. last one taken) snapshot
611*9386Sgerald.jelinek@sun.com  * that has a clone.  If found, it returns a reference to that clone in the
612*9386Sgerald.jelinek@sun.com  * callback data.
613*9386Sgerald.jelinek@sun.com  */
614*9386Sgerald.jelinek@sun.com static int
615*9386Sgerald.jelinek@sun.com find_clone(zfs_handle_t *zhp, void *data)
616*9386Sgerald.jelinek@sun.com {
617*9386Sgerald.jelinek@sun.com 	clone_data_t	*cd = data;
618*9386Sgerald.jelinek@sun.com 	time_t		snap_creation;
619*9386Sgerald.jelinek@sun.com 	int		zret = 0;
620*9386Sgerald.jelinek@sun.com 
621*9386Sgerald.jelinek@sun.com 	/* If snapshot has no clones, skip it */
622*9386Sgerald.jelinek@sun.com 	if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) {
623*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
624*9386Sgerald.jelinek@sun.com 		return (0);
625*9386Sgerald.jelinek@sun.com 	}
626*9386Sgerald.jelinek@sun.com 
627*9386Sgerald.jelinek@sun.com 	cd->snapshot = zfs_get_name(zhp);
628*9386Sgerald.jelinek@sun.com 
629*9386Sgerald.jelinek@sun.com 	/* Get the creation time of this snapshot */
630*9386Sgerald.jelinek@sun.com 	snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
631*9386Sgerald.jelinek@sun.com 
632*9386Sgerald.jelinek@sun.com 	/*
633*9386Sgerald.jelinek@sun.com 	 * If this snapshot's creation time is greater than (i.e. younger than)
634*9386Sgerald.jelinek@sun.com 	 * the current youngest snapshot found, iterate this snapshot to
635*9386Sgerald.jelinek@sun.com 	 * get the right clone.
636*9386Sgerald.jelinek@sun.com 	 */
637*9386Sgerald.jelinek@sun.com 	if (snap_creation >= cd->origin_creation) {
638*9386Sgerald.jelinek@sun.com 		/*
639*9386Sgerald.jelinek@sun.com 		 * Iterate the dependents of this snapshot to find a clone
640*9386Sgerald.jelinek@sun.com 		 * that's a direct dependent.
641*9386Sgerald.jelinek@sun.com 		 */
642*9386Sgerald.jelinek@sun.com 		if ((zret = zfs_iter_dependents(zhp, B_FALSE, get_direct_clone,
643*9386Sgerald.jelinek@sun.com 		    cd)) == -1) {
644*9386Sgerald.jelinek@sun.com 			zfs_close(zhp);
645*9386Sgerald.jelinek@sun.com 			return (1);
646*9386Sgerald.jelinek@sun.com 		} else if (zret == 1) {
647*9386Sgerald.jelinek@sun.com 			/*
648*9386Sgerald.jelinek@sun.com 			 * Found a clone, update the origin_creation time
649*9386Sgerald.jelinek@sun.com 			 * in the callback data.
650*9386Sgerald.jelinek@sun.com 			 */
651*9386Sgerald.jelinek@sun.com 			cd->origin_creation = snap_creation;
652*9386Sgerald.jelinek@sun.com 		}
653*9386Sgerald.jelinek@sun.com 	}
654*9386Sgerald.jelinek@sun.com 
655*9386Sgerald.jelinek@sun.com 	zfs_close(zhp);
656*9386Sgerald.jelinek@sun.com 	return (0);
657*9386Sgerald.jelinek@sun.com }
658*9386Sgerald.jelinek@sun.com 
659*9386Sgerald.jelinek@sun.com /*
660*9386Sgerald.jelinek@sun.com  * A ZFS file system iterator call-back function used to remove standalone
661*9386Sgerald.jelinek@sun.com  * snapshots.
662*9386Sgerald.jelinek@sun.com  */
663*9386Sgerald.jelinek@sun.com /* ARGSUSED */
664*9386Sgerald.jelinek@sun.com static int
665*9386Sgerald.jelinek@sun.com rm_snap(zfs_handle_t *zhp, void *data)
666*9386Sgerald.jelinek@sun.com {
667*9386Sgerald.jelinek@sun.com 	/* If snapshot has clones, something is wrong */
668*9386Sgerald.jelinek@sun.com 	if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) {
669*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
670*9386Sgerald.jelinek@sun.com 		return (1);
671*9386Sgerald.jelinek@sun.com 	}
672*9386Sgerald.jelinek@sun.com 
673*9386Sgerald.jelinek@sun.com 	if (zfs_unmount(zhp, NULL, 0) == 0) {
674*9386Sgerald.jelinek@sun.com 		(void) zfs_destroy(zhp);
675*9386Sgerald.jelinek@sun.com 	}
676*9386Sgerald.jelinek@sun.com 
677*9386Sgerald.jelinek@sun.com 	zfs_close(zhp);
678*9386Sgerald.jelinek@sun.com 	return (0);
679*9386Sgerald.jelinek@sun.com }
680*9386Sgerald.jelinek@sun.com 
681*9386Sgerald.jelinek@sun.com /*
682*9386Sgerald.jelinek@sun.com  * A ZFS snapshot iterator call-back function which renames snapshots.
683*9386Sgerald.jelinek@sun.com  */
684*9386Sgerald.jelinek@sun.com static int
685*9386Sgerald.jelinek@sun.com rename_snap(zfs_handle_t *zhp, void *data)
686*9386Sgerald.jelinek@sun.com {
687*9386Sgerald.jelinek@sun.com 	int			res;
688*9386Sgerald.jelinek@sun.com 	zfs_snapshot_data_t	*cbp;
689*9386Sgerald.jelinek@sun.com 	char			template[ZFS_MAXNAMELEN];
690*9386Sgerald.jelinek@sun.com 
691*9386Sgerald.jelinek@sun.com 	cbp = (zfs_snapshot_data_t *)data;
692*9386Sgerald.jelinek@sun.com 
693*9386Sgerald.jelinek@sun.com 	/*
694*9386Sgerald.jelinek@sun.com 	 * When renaming snapshots with the iterator, the iterator can see
695*9386Sgerald.jelinek@sun.com 	 * the same snapshot after we've renamed up in the namespace.  To
696*9386Sgerald.jelinek@sun.com 	 * prevent this we check the count for the number of snapshots we have
697*9386Sgerald.jelinek@sun.com 	 * to rename and stop at that point.
698*9386Sgerald.jelinek@sun.com 	 */
699*9386Sgerald.jelinek@sun.com 	if (cbp->cntr >= cbp->num) {
700*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
701*9386Sgerald.jelinek@sun.com 		return (0);
702*9386Sgerald.jelinek@sun.com 	}
703*9386Sgerald.jelinek@sun.com 
704*9386Sgerald.jelinek@sun.com 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
705*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
706*9386Sgerald.jelinek@sun.com 		return (0);
707*9386Sgerald.jelinek@sun.com 	}
708*9386Sgerald.jelinek@sun.com 
709*9386Sgerald.jelinek@sun.com 	/* Only rename the snapshots we automatically generate when we clone. */
710*9386Sgerald.jelinek@sun.com 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) != 0) {
711*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
712*9386Sgerald.jelinek@sun.com 		return (0);
713*9386Sgerald.jelinek@sun.com 	}
714*9386Sgerald.jelinek@sun.com 
715*9386Sgerald.jelinek@sun.com 	(void) snprintf(template, sizeof (template), "%s%d", cbp->match_name,
716*9386Sgerald.jelinek@sun.com 	    cbp->max++);
717*9386Sgerald.jelinek@sun.com 
718*9386Sgerald.jelinek@sun.com 	res = (zfs_rename(zhp, template, B_FALSE) != 0);
719*9386Sgerald.jelinek@sun.com 	if (res != 0)
720*9386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("failed to rename snapshot %s "
721*9386Sgerald.jelinek@sun.com 		    "to %s: %s\n"), zfs_get_name(zhp), template,
722*9386Sgerald.jelinek@sun.com 		    libzfs_error_description(g_zfs));
723*9386Sgerald.jelinek@sun.com 
724*9386Sgerald.jelinek@sun.com 	cbp->cntr++;
725*9386Sgerald.jelinek@sun.com 
726*9386Sgerald.jelinek@sun.com 	zfs_close(zhp);
727*9386Sgerald.jelinek@sun.com 	return (res);
728*9386Sgerald.jelinek@sun.com }
729*9386Sgerald.jelinek@sun.com 
730*9386Sgerald.jelinek@sun.com /*
731*9386Sgerald.jelinek@sun.com  * Rename the source dataset's snapshots that are automatically generated when
732*9386Sgerald.jelinek@sun.com  * we clone a zone so that there won't be a name collision when we promote the
733*9386Sgerald.jelinek@sun.com  * cloned dataset.  Once the snapshots have been renamed, then promote the
734*9386Sgerald.jelinek@sun.com  * clone.
735*9386Sgerald.jelinek@sun.com  *
736*9386Sgerald.jelinek@sun.com  * The snapshot rename process gets the highest number on the snapshot names
737*9386Sgerald.jelinek@sun.com  * (the format is zonename@SUNWzoneXX where XX are digits) on both the source
738*9386Sgerald.jelinek@sun.com  * and clone datasets, then renames the source dataset snapshots starting at
739*9386Sgerald.jelinek@sun.com  * the next number.
740*9386Sgerald.jelinek@sun.com  */
741*9386Sgerald.jelinek@sun.com static int
742*9386Sgerald.jelinek@sun.com promote_clone(zfs_handle_t *src_zhp, zfs_handle_t *cln_zhp)
743*9386Sgerald.jelinek@sun.com {
744*9386Sgerald.jelinek@sun.com 	zfs_snapshot_data_t	sd;
745*9386Sgerald.jelinek@sun.com 	char			nm[ZFS_MAXNAMELEN];
746*9386Sgerald.jelinek@sun.com 	char			template[ZFS_MAXNAMELEN];
747*9386Sgerald.jelinek@sun.com 
748*9386Sgerald.jelinek@sun.com 	(void) strlcpy(nm, zfs_get_name(cln_zhp), sizeof (nm));
749*9386Sgerald.jelinek@sun.com 	/*
750*9386Sgerald.jelinek@sun.com 	 * Start by getting the clone's snapshot max which we use
751*9386Sgerald.jelinek@sun.com 	 * during the rename of the original dataset's snapshots.
752*9386Sgerald.jelinek@sun.com 	 */
753*9386Sgerald.jelinek@sun.com 	(void) snprintf(template, sizeof (template), "%s@SUNWzone", nm);
754*9386Sgerald.jelinek@sun.com 	sd.match_name = template;
755*9386Sgerald.jelinek@sun.com 	sd.len = strlen(template);
756*9386Sgerald.jelinek@sun.com 	sd.max = 0;
757*9386Sgerald.jelinek@sun.com 
758*9386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(cln_zhp, get_snap_max, &sd) != 0)
759*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
760*9386Sgerald.jelinek@sun.com 
761*9386Sgerald.jelinek@sun.com 	/*
762*9386Sgerald.jelinek@sun.com 	 * Now make sure the source's snapshot max is at least as high as
763*9386Sgerald.jelinek@sun.com 	 * the clone's snapshot max.
764*9386Sgerald.jelinek@sun.com 	 */
765*9386Sgerald.jelinek@sun.com 	(void) snprintf(template, sizeof (template), "%s@SUNWzone",
766*9386Sgerald.jelinek@sun.com 	    zfs_get_name(src_zhp));
767*9386Sgerald.jelinek@sun.com 	sd.match_name = template;
768*9386Sgerald.jelinek@sun.com 	sd.len = strlen(template);
769*9386Sgerald.jelinek@sun.com 	sd.num = 0;
770*9386Sgerald.jelinek@sun.com 
771*9386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(src_zhp, get_snap_max, &sd) != 0)
772*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
773*9386Sgerald.jelinek@sun.com 
774*9386Sgerald.jelinek@sun.com 	/*
775*9386Sgerald.jelinek@sun.com 	 * Now rename the source dataset's snapshots so there's no
776*9386Sgerald.jelinek@sun.com 	 * conflict when we promote the clone.
777*9386Sgerald.jelinek@sun.com 	 */
778*9386Sgerald.jelinek@sun.com 	sd.max++;
779*9386Sgerald.jelinek@sun.com 	sd.cntr = 0;
780*9386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(src_zhp, rename_snap, &sd) != 0)
781*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
782*9386Sgerald.jelinek@sun.com 
783*9386Sgerald.jelinek@sun.com 	/* close and reopen the clone dataset to get the latest info */
784*9386Sgerald.jelinek@sun.com 	zfs_close(cln_zhp);
785*9386Sgerald.jelinek@sun.com 	if ((cln_zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
786*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
787*9386Sgerald.jelinek@sun.com 
788*9386Sgerald.jelinek@sun.com 	if (zfs_promote(cln_zhp) != 0) {
789*9386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("failed to promote %s: %s\n"),
790*9386Sgerald.jelinek@sun.com 		    nm, libzfs_error_description(g_zfs));
791*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
792*9386Sgerald.jelinek@sun.com 	}
793*9386Sgerald.jelinek@sun.com 
794*9386Sgerald.jelinek@sun.com 	zfs_close(cln_zhp);
795*9386Sgerald.jelinek@sun.com 	return (Z_OK);
796*9386Sgerald.jelinek@sun.com }
797*9386Sgerald.jelinek@sun.com 
798*9386Sgerald.jelinek@sun.com /*
799*9386Sgerald.jelinek@sun.com  * Promote the youngest clone.  That clone will then become the origin of all
800*9386Sgerald.jelinek@sun.com  * of the other clones that were hanging off of the source dataset.
801*9386Sgerald.jelinek@sun.com  */
802*9386Sgerald.jelinek@sun.com int
803*9386Sgerald.jelinek@sun.com promote_all_clones(zfs_handle_t *zhp)
804*9386Sgerald.jelinek@sun.com {
805*9386Sgerald.jelinek@sun.com 	clone_data_t	cd;
806*9386Sgerald.jelinek@sun.com 	char		nm[ZFS_MAXNAMELEN];
807*9386Sgerald.jelinek@sun.com 
808*9386Sgerald.jelinek@sun.com 	cd.clone_zhp = NULL;
809*9386Sgerald.jelinek@sun.com 	cd.origin_creation = 0;
810*9386Sgerald.jelinek@sun.com 	cd.snapshot = NULL;
811*9386Sgerald.jelinek@sun.com 
812*9386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(zhp, find_clone, &cd) != 0) {
813*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
814*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
815*9386Sgerald.jelinek@sun.com 	}
816*9386Sgerald.jelinek@sun.com 
817*9386Sgerald.jelinek@sun.com 	/* Nothing to promote. */
818*9386Sgerald.jelinek@sun.com 	if (cd.clone_zhp == NULL)
819*9386Sgerald.jelinek@sun.com 		return (Z_OK);
820*9386Sgerald.jelinek@sun.com 
821*9386Sgerald.jelinek@sun.com 	/* Found the youngest clone to promote.  Promote it. */
822*9386Sgerald.jelinek@sun.com 	if (promote_clone(zhp, cd.clone_zhp) != 0) {
823*9386Sgerald.jelinek@sun.com 		zfs_close(cd.clone_zhp);
824*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
825*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
826*9386Sgerald.jelinek@sun.com 	}
827*9386Sgerald.jelinek@sun.com 
828*9386Sgerald.jelinek@sun.com 	/* close and reopen the main dataset to get the latest info */
829*9386Sgerald.jelinek@sun.com 	(void) strlcpy(nm, zfs_get_name(zhp), sizeof (nm));
830*9386Sgerald.jelinek@sun.com 	zfs_close(zhp);
831*9386Sgerald.jelinek@sun.com 	if ((zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
832*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
833*9386Sgerald.jelinek@sun.com 
834*9386Sgerald.jelinek@sun.com 	return (Z_OK);
835*9386Sgerald.jelinek@sun.com }
836*9386Sgerald.jelinek@sun.com 
837*9386Sgerald.jelinek@sun.com /*
8381867Sgjelinek  * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
8391867Sgjelinek  * possible, or by copying the data from the snapshot to the zonepath.
8401867Sgjelinek  */
8411867Sgjelinek int
8427089Sgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
8431867Sgjelinek {
8441867Sgjelinek 	int	err = Z_OK;
8451867Sgjelinek 	char	clone_name[MAXPATHLEN];
8461867Sgjelinek 	char	snap_path[MAXPATHLEN];
8471867Sgjelinek 
8481867Sgjelinek 	if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
8491867Sgjelinek 		(void) fprintf(stderr, gettext("unable to find path for %s.\n"),
8501867Sgjelinek 		    snap_name);
8511867Sgjelinek 		return (Z_ERR);
8521867Sgjelinek 	}
8531867Sgjelinek 
8547089Sgjelinek 	if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
8551867Sgjelinek 		return (Z_NO_ENTRY);
8561867Sgjelinek 
8571867Sgjelinek 	/*
8581867Sgjelinek 	 * The zonepath cannot be ZFS cloned, try to copy the data from
8591867Sgjelinek 	 * within the snapshot to the zonepath.
8601867Sgjelinek 	 */
8611867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
8621867Sgjelinek 		if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8631867Sgjelinek 			if (clean_out_clone() != Z_OK)
8641867Sgjelinek 				(void) fprintf(stderr,
8651867Sgjelinek 				    gettext("could not remove the "
8661867Sgjelinek 				    "software inventory from %s\n"), zonepath);
8671867Sgjelinek 
8681867Sgjelinek 		return (err);
8691867Sgjelinek 	}
8701867Sgjelinek 
8711867Sgjelinek 	if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
8721867Sgjelinek 		if (err != Z_NO_ENTRY) {
8731867Sgjelinek 			/*
8741867Sgjelinek 			 * Cloning the snapshot failed.  Fall back to trying
8751867Sgjelinek 			 * to install the zone by copying from the snapshot.
8761867Sgjelinek 			 */
8771867Sgjelinek 			if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8781867Sgjelinek 				if (clean_out_clone() != Z_OK)
8791867Sgjelinek 					(void) fprintf(stderr,
8801867Sgjelinek 					    gettext("could not remove the "
8811867Sgjelinek 					    "software inventory from %s\n"),
8821867Sgjelinek 					    zonepath);
8831867Sgjelinek 		} else {
8841867Sgjelinek 			/*
8851867Sgjelinek 			 * The snapshot is unusable for some reason so restore
8861867Sgjelinek 			 * the zone state to configured since we were unable to
8871867Sgjelinek 			 * actually do anything about getting the zone
8881867Sgjelinek 			 * installed.
8891867Sgjelinek 			 */
8901867Sgjelinek 			int tmp;
8911867Sgjelinek 
8921867Sgjelinek 			if ((tmp = zone_set_state(target_zone,
8931867Sgjelinek 			    ZONE_STATE_CONFIGURED)) != Z_OK) {
8941867Sgjelinek 				errno = tmp;
8951867Sgjelinek 				zperror2(target_zone,
8961867Sgjelinek 				    gettext("could not set state"));
8971867Sgjelinek 			}
8981867Sgjelinek 		}
8991867Sgjelinek 	}
9001867Sgjelinek 
9011867Sgjelinek 	return (err);
9021867Sgjelinek }
9031867Sgjelinek 
9041867Sgjelinek /*
9051867Sgjelinek  * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
9061867Sgjelinek  */
9071867Sgjelinek int
9087089Sgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
9097089Sgjelinek     char *postsnapbuf)
9101867Sgjelinek {
9111867Sgjelinek 	zfs_handle_t	*zhp;
9121867Sgjelinek 	char		clone_name[MAXPATHLEN];
9131867Sgjelinek 	char		snap_name[MAXPATHLEN];
9141867Sgjelinek 
9151867Sgjelinek 	/*
9161867Sgjelinek 	 * Try to get a zfs handle for the source_zonepath.  If this fails
9171867Sgjelinek 	 * the source_zonepath is not ZFS so return an error.
9181867Sgjelinek 	 */
9191867Sgjelinek 	if ((zhp = mount2zhandle(source_zonepath)) == NULL)
9201867Sgjelinek 		return (Z_ERR);
9211867Sgjelinek 
9221867Sgjelinek 	/*
9231867Sgjelinek 	 * Check if there is a file system already mounted on zonepath.  If so,
9241867Sgjelinek 	 * we can't clone to the path so we should fall back to copying.
9251867Sgjelinek 	 */
9261867Sgjelinek 	if (is_mountpnt(zonepath)) {
9271867Sgjelinek 		zfs_close(zhp);
9281867Sgjelinek 		(void) fprintf(stderr,
9291867Sgjelinek 		    gettext("A file system is already mounted on %s,\n"
9301867Sgjelinek 		    "preventing use of a ZFS clone.\n"), zonepath);
9311867Sgjelinek 		return (Z_ERR);
9321867Sgjelinek 	}
9331867Sgjelinek 
9341867Sgjelinek 	/*
9351867Sgjelinek 	 * Instead of using path2name to get the clone name from the zonepath,
9361867Sgjelinek 	 * we could generate a name from the source zone ZFS name.  However,
9371867Sgjelinek 	 * this would mean we would create the clone under the ZFS fs of the
9381867Sgjelinek 	 * source instead of what the zonepath says.  For example,
9391867Sgjelinek 	 *
9401867Sgjelinek 	 * source_zonepath		zonepath
9411867Sgjelinek 	 * /pl/zones/dev/z1		/pl/zones/deploy/z2
9421867Sgjelinek 	 *
9431867Sgjelinek 	 * We don't want the clone to be under "dev", we want it under
9441867Sgjelinek 	 * "deploy", so that we can leverage the normal attribute inheritance
9451867Sgjelinek 	 * that ZFS provides in the fs hierarchy.
9461867Sgjelinek 	 */
9471867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
9481867Sgjelinek 		zfs_close(zhp);
9491867Sgjelinek 		return (Z_ERR);
9501867Sgjelinek 	}
9511867Sgjelinek 
9527089Sgjelinek 	if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf,
9537089Sgjelinek 	    postsnapbuf) != Z_OK) {
9541867Sgjelinek 		zfs_close(zhp);
9551867Sgjelinek 		return (Z_ERR);
9561867Sgjelinek 	}
9571867Sgjelinek 	zfs_close(zhp);
9581867Sgjelinek 
9593686Sgjelinek 	if (clone_snap(snap_name, clone_name) != Z_OK) {
9603686Sgjelinek 		/* Clean up the snapshot we just took. */
9613686Sgjelinek 		if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT))
9623686Sgjelinek 		    != NULL) {
9633686Sgjelinek 			if (zfs_unmount(zhp, NULL, 0) == 0)
9643686Sgjelinek 				(void) zfs_destroy(zhp);
9653686Sgjelinek 			zfs_close(zhp);
9663686Sgjelinek 		}
9673686Sgjelinek 
9681867Sgjelinek 		return (Z_ERR);
9693686Sgjelinek 	}
9701867Sgjelinek 
9711867Sgjelinek 	(void) printf(gettext("Instead of copying, a ZFS clone has been "
9721867Sgjelinek 	    "created for this zone.\n"));
9731867Sgjelinek 
9741867Sgjelinek 	return (Z_OK);
9751867Sgjelinek }
9761867Sgjelinek 
9771867Sgjelinek /*
9781867Sgjelinek  * Attempt to create a ZFS file system for the specified zonepath.
9791867Sgjelinek  * We either will successfully create a ZFS file system and get it mounted
9801867Sgjelinek  * on the zonepath or we don't.  The caller doesn't care since a regular
9811867Sgjelinek  * directory is used for the zonepath if no ZFS file system is mounted there.
9821867Sgjelinek  */
9831867Sgjelinek void
9841867Sgjelinek create_zfs_zonepath(char *zonepath)
9851867Sgjelinek {
9861867Sgjelinek 	zfs_handle_t	*zhp;
9871867Sgjelinek 	char		zfs_name[MAXPATHLEN];
9882676Seschrock 	nvlist_t	*props = NULL;
9891867Sgjelinek 
9901867Sgjelinek 	if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
9911867Sgjelinek 		return;
9921867Sgjelinek 
9939023Sgerald.jelinek@sun.com 	/* Check if the dataset already exists. */
9949023Sgerald.jelinek@sun.com 	if ((zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) != NULL) {
9959023Sgerald.jelinek@sun.com 		zfs_close(zhp);
9969023Sgerald.jelinek@sun.com 		return;
9979023Sgerald.jelinek@sun.com 	}
9989023Sgerald.jelinek@sun.com 
9992676Seschrock 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
10002744Snn35248 	    nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
10012744Snn35248 	    "off") != 0) {
10022744Snn35248 		if (props != NULL)
10032744Snn35248 			nvlist_free(props);
10042676Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
10052676Seschrock 		    "out of memory\n"), zfs_name);
10062676Seschrock 	}
10072676Seschrock 
10082676Seschrock 	if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 ||
10095094Slling 	    (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) {
10102082Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
10112082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
10122676Seschrock 		nvlist_free(props);
10131867Sgjelinek 		return;
10141867Sgjelinek 	}
10151867Sgjelinek 
10162676Seschrock 	nvlist_free(props);
10172676Seschrock 
10181867Sgjelinek 	if (zfs_mount(zhp, NULL, 0) != 0) {
10192082Seschrock 		(void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: "
10202082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
10211867Sgjelinek 		(void) zfs_destroy(zhp);
10221867Sgjelinek 	} else {
10231867Sgjelinek 		if (chmod(zonepath, S_IRWXU) != 0) {
10241867Sgjelinek 			(void) fprintf(stderr, gettext("file system %s "
10251867Sgjelinek 			    "successfully created, but chmod %o failed: %s\n"),
10261867Sgjelinek 			    zfs_name, S_IRWXU, strerror(errno));
10271867Sgjelinek 			(void) destroy_zfs(zonepath);
10281867Sgjelinek 		} else {
10291867Sgjelinek 			(void) printf(gettext("A ZFS file system has been "
10301867Sgjelinek 			    "created for this zone.\n"));
10311867Sgjelinek 		}
10321867Sgjelinek 	}
10331867Sgjelinek 
10341867Sgjelinek 	zfs_close(zhp);
10351867Sgjelinek }
10361867Sgjelinek 
10371867Sgjelinek /*
10381867Sgjelinek  * If the zonepath is a ZFS file system, attempt to destroy it.  We return Z_OK
10391867Sgjelinek  * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
10401867Sgjelinek  * which means the caller should clean up the zonepath in the traditional
10411867Sgjelinek  * way.
10421867Sgjelinek  */
10431867Sgjelinek int
10441867Sgjelinek destroy_zfs(char *zonepath)
10451867Sgjelinek {
10461867Sgjelinek 	zfs_handle_t	*zhp;
10471867Sgjelinek 	boolean_t	is_clone = B_FALSE;
10481867Sgjelinek 	char		origin[ZFS_MAXPROPLEN];
10491867Sgjelinek 
10502082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
10511867Sgjelinek 		return (Z_ERR);
10521867Sgjelinek 
1053*9386Sgerald.jelinek@sun.com 	if (promote_all_clones(zhp) != 0)
1054*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
1055*9386Sgerald.jelinek@sun.com 
1056*9386Sgerald.jelinek@sun.com 	/* Now cleanup any snapshots remaining. */
1057*9386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(zhp, rm_snap, NULL) != 0) {
1058*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
1059*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
1060*9386Sgerald.jelinek@sun.com 	}
1061*9386Sgerald.jelinek@sun.com 
10621867Sgjelinek 	/*
1063*9386Sgerald.jelinek@sun.com 	 * We can't destroy the file system if it has still has dependents.
1064*9386Sgerald.jelinek@sun.com 	 * There shouldn't be any at this point, but we'll double check.
10651867Sgjelinek 	 */
1066*9386Sgerald.jelinek@sun.com 	if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0) {
1067*9386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("zfs destroy %s failed: the "
1068*9386Sgerald.jelinek@sun.com 		    "dataset still has dependents\n"), zfs_get_name(zhp));
10691867Sgjelinek 		zfs_close(zhp);
10701867Sgjelinek 		return (Z_ERR);
10711867Sgjelinek 	}
10721867Sgjelinek 
10731867Sgjelinek 	/*
10741867Sgjelinek 	 * This might be a clone.  Try to get the snapshot so we can attempt
10751867Sgjelinek 	 * to destroy that as well.
10761867Sgjelinek 	 */
10771867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
10782082Seschrock 	    NULL, 0, B_FALSE) == 0)
10791867Sgjelinek 		is_clone = B_TRUE;
10801867Sgjelinek 
1081*9386Sgerald.jelinek@sun.com 	if (zfs_unmount(zhp, NULL, 0) != 0) {
1082*9386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("zfs unmount %s failed: %s\n"),
1083*9386Sgerald.jelinek@sun.com 		    zfs_get_name(zhp), libzfs_error_description(g_zfs));
1084*9386Sgerald.jelinek@sun.com 		zfs_close(zhp);
1085*9386Sgerald.jelinek@sun.com 		return (Z_ERR);
1086*9386Sgerald.jelinek@sun.com 	}
1087*9386Sgerald.jelinek@sun.com 
10881867Sgjelinek 	if (zfs_destroy(zhp) != 0) {
10891867Sgjelinek 		/*
10901867Sgjelinek 		 * If the destroy fails for some reason, try to remount
10911867Sgjelinek 		 * the file system so that we can use "rm -rf" to clean up
10921867Sgjelinek 		 * instead.
10931867Sgjelinek 		 */
1094*9386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("zfs destroy %s failed: %s\n"),
1095*9386Sgerald.jelinek@sun.com 		    zfs_get_name(zhp), libzfs_error_description(g_zfs));
10961867Sgjelinek 		(void) zfs_mount(zhp, NULL, 0);
10971867Sgjelinek 		zfs_close(zhp);
10981867Sgjelinek 		return (Z_ERR);
10991867Sgjelinek 	}
11001867Sgjelinek 
11013686Sgjelinek 	/*
11023686Sgjelinek 	 * If the zone has ever been moved then the mountpoint dir will not be
11033686Sgjelinek 	 * cleaned up by the zfs_destroy().  To handle this case try to clean
11043686Sgjelinek 	 * it up now but don't worry if it fails, that will be normal.
11053686Sgjelinek 	 */
11063686Sgjelinek 	(void) rmdir(zonepath);
11073686Sgjelinek 
11081867Sgjelinek 	(void) printf(gettext("The ZFS file system for this zone has been "
11091867Sgjelinek 	    "destroyed.\n"));
11101867Sgjelinek 
11111867Sgjelinek 	if (is_clone) {
11121867Sgjelinek 		zfs_handle_t	*ohp;
11131867Sgjelinek 
11141867Sgjelinek 		/*
11151867Sgjelinek 		 * Try to clean up the snapshot that the clone was taken from.
11161867Sgjelinek 		 */
11172082Seschrock 		if ((ohp = zfs_open(g_zfs, origin,
11182082Seschrock 		    ZFS_TYPE_SNAPSHOT)) != NULL) {
11192474Seschrock 			if (zfs_iter_dependents(ohp, B_TRUE, has_dependent,
11202474Seschrock 			    NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0)
11211867Sgjelinek 				(void) zfs_destroy(ohp);
11221867Sgjelinek 			zfs_close(ohp);
11231867Sgjelinek 		}
11241867Sgjelinek 	}
11251867Sgjelinek 
11261867Sgjelinek 	zfs_close(zhp);
11271867Sgjelinek 	return (Z_OK);
11281867Sgjelinek }
11291867Sgjelinek 
11301867Sgjelinek /*
11311867Sgjelinek  * Return true if the path is its own zfs file system.  We determine this
11321867Sgjelinek  * by stat-ing the path to see if it is zfs and stat-ing the parent to see
11331867Sgjelinek  * if it is a different fs.
11341867Sgjelinek  */
11351867Sgjelinek boolean_t
11361867Sgjelinek is_zonepath_zfs(char *zonepath)
11371867Sgjelinek {
11381867Sgjelinek 	int res;
11391867Sgjelinek 	char *path;
11401867Sgjelinek 	char *parent;
11412267Sdp 	struct statvfs64 buf1, buf2;
11421867Sgjelinek 
11432267Sdp 	if (statvfs64(zonepath, &buf1) != 0)
11441867Sgjelinek 		return (B_FALSE);
11451867Sgjelinek 
11461867Sgjelinek 	if (strcmp(buf1.f_basetype, "zfs") != 0)
11471867Sgjelinek 		return (B_FALSE);
11481867Sgjelinek 
11491867Sgjelinek 	if ((path = strdup(zonepath)) == NULL)
11501867Sgjelinek 		return (B_FALSE);
11511867Sgjelinek 
11521867Sgjelinek 	parent = dirname(path);
11532267Sdp 	res = statvfs64(parent, &buf2);
11541867Sgjelinek 	free(path);
11551867Sgjelinek 
11561867Sgjelinek 	if (res != 0)
11571867Sgjelinek 		return (B_FALSE);
11581867Sgjelinek 
11591867Sgjelinek 	if (buf1.f_fsid == buf2.f_fsid)
11601867Sgjelinek 		return (B_FALSE);
11611867Sgjelinek 
11621867Sgjelinek 	return (B_TRUE);
11631867Sgjelinek }
11641867Sgjelinek 
11651867Sgjelinek /*
11661867Sgjelinek  * Implement the fast move of a ZFS file system by simply updating the
11671867Sgjelinek  * mountpoint.  Since it is file system already, we don't have the
11681867Sgjelinek  * issue of cross-file system copying.
11691867Sgjelinek  */
11701867Sgjelinek int
11711867Sgjelinek move_zfs(char *zonepath, char *new_zonepath)
11721867Sgjelinek {
11731867Sgjelinek 	int		ret = Z_ERR;
11741867Sgjelinek 	zfs_handle_t	*zhp;
11751867Sgjelinek 
11762082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
11771867Sgjelinek 		return (Z_ERR);
11781867Sgjelinek 
11792676Seschrock 	if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
11802676Seschrock 	    new_zonepath) == 0) {
11811867Sgjelinek 		/*
11821867Sgjelinek 		 * Clean up the old mount point.  We ignore any failure since
11831867Sgjelinek 		 * the zone is already successfully mounted on the new path.
11841867Sgjelinek 		 */
11851867Sgjelinek 		(void) rmdir(zonepath);
11861867Sgjelinek 		ret = Z_OK;
11871867Sgjelinek 	}
11881867Sgjelinek 
11891867Sgjelinek 	zfs_close(zhp);
11901867Sgjelinek 
11911867Sgjelinek 	return (ret);
11921867Sgjelinek }
11931867Sgjelinek 
11941867Sgjelinek /*
11951867Sgjelinek  * Validate that the given dataset exists on the system, and that neither it nor
11961867Sgjelinek  * its children are zvols.
11971867Sgjelinek  *
11981867Sgjelinek  * Note that we don't do anything with the 'zoned' property here.  All
11991867Sgjelinek  * management is done in zoneadmd when the zone is actually rebooted.  This
12001867Sgjelinek  * allows us to automatically set the zoned property even when a zone is
12011867Sgjelinek  * rebooted by the administrator.
12021867Sgjelinek  */
12031867Sgjelinek int
12041867Sgjelinek verify_datasets(zone_dochandle_t handle)
12051867Sgjelinek {
12061867Sgjelinek 	int return_code = Z_OK;
12071867Sgjelinek 	struct zone_dstab dstab;
12081867Sgjelinek 	zfs_handle_t *zhp;
12091867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
12101867Sgjelinek 	char source[ZFS_MAXNAMELEN];
12115094Slling 	zprop_source_t srctype;
12121867Sgjelinek 
12131867Sgjelinek 	if (zonecfg_setdsent(handle) != Z_OK) {
12141867Sgjelinek 		/*
12151867Sgjelinek 		 * TRANSLATION_NOTE
12161867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
12171867Sgjelinek 		 */
12181867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs datasets: "
12191867Sgjelinek 		    "unable to enumerate datasets\n"));
12201867Sgjelinek 		return (Z_ERR);
12211867Sgjelinek 	}
12221867Sgjelinek 
12231867Sgjelinek 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
12241867Sgjelinek 
12252082Seschrock 		if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name,
12261867Sgjelinek 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
12272082Seschrock 			(void) fprintf(stderr, gettext("could not verify zfs "
12282082Seschrock 			    "dataset %s: %s\n"), dstab.zone_dataset_name,
12292082Seschrock 			    libzfs_error_description(g_zfs));
12301867Sgjelinek 			return_code = Z_ERR;
12311867Sgjelinek 			continue;
12321867Sgjelinek 		}
12331867Sgjelinek 
12341867Sgjelinek 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf,
12351867Sgjelinek 		    sizeof (propbuf), &srctype, source,
12361867Sgjelinek 		    sizeof (source), 0) == 0 &&
12375094Slling 		    (srctype == ZPROP_SRC_INHERITED)) {
12381867Sgjelinek 			(void) fprintf(stderr, gettext("could not verify zfs "
12391867Sgjelinek 			    "dataset %s: mountpoint cannot be inherited\n"),
12401867Sgjelinek 			    dstab.zone_dataset_name);
12411867Sgjelinek 			return_code = Z_ERR;
12421867Sgjelinek 			zfs_close(zhp);
12431867Sgjelinek 			continue;
12441867Sgjelinek 		}
12451867Sgjelinek 
12461867Sgjelinek 		if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
12471867Sgjelinek 			(void) fprintf(stderr, gettext("cannot verify zfs "
12481867Sgjelinek 			    "dataset %s: volumes cannot be specified as a "
12491867Sgjelinek 			    "zone dataset resource\n"),
12501867Sgjelinek 			    dstab.zone_dataset_name);
12511867Sgjelinek 			return_code = Z_ERR;
12521867Sgjelinek 		}
12531867Sgjelinek 
12541867Sgjelinek 		if (zfs_iter_children(zhp, check_zvol, NULL) != 0)
12551867Sgjelinek 			return_code = Z_ERR;
12561867Sgjelinek 
12571867Sgjelinek 		zfs_close(zhp);
12581867Sgjelinek 	}
12591867Sgjelinek 	(void) zonecfg_enddsent(handle);
12601867Sgjelinek 
12611867Sgjelinek 	return (return_code);
12621867Sgjelinek }
12631867Sgjelinek 
12641867Sgjelinek /*
12651867Sgjelinek  * Verify that the ZFS dataset exists, and its mountpoint
12661867Sgjelinek  * property is set to "legacy".
12671867Sgjelinek  */
12681867Sgjelinek int
12691867Sgjelinek verify_fs_zfs(struct zone_fstab *fstab)
12701867Sgjelinek {
12711867Sgjelinek 	zfs_handle_t *zhp;
12721867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
12731867Sgjelinek 
12742082Seschrock 	if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special,
12755094Slling 	    ZFS_TYPE_DATASET)) == NULL) {
12761867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
12771867Sgjelinek 		    "could not access zfs dataset '%s'\n"),
12781867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
12791867Sgjelinek 		return (Z_ERR);
12801867Sgjelinek 	}
12811867Sgjelinek 
12821867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
12831867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify fs %s: "
12841867Sgjelinek 		    "'%s' is not a file system\n"),
12851867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
12861867Sgjelinek 		zfs_close(zhp);
12871867Sgjelinek 		return (Z_ERR);
12881867Sgjelinek 	}
12891867Sgjelinek 
12901867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
12911867Sgjelinek 	    NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
12921867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
12931867Sgjelinek 		    "zfs '%s' mountpoint is not \"legacy\"\n"),
12941867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
12951867Sgjelinek 		zfs_close(zhp);
12961867Sgjelinek 		return (Z_ERR);
12971867Sgjelinek 	}
12981867Sgjelinek 
12991867Sgjelinek 	zfs_close(zhp);
13001867Sgjelinek 	return (Z_OK);
13011867Sgjelinek }
13022082Seschrock 
13032082Seschrock int
13042082Seschrock init_zfs(void)
13052082Seschrock {
13062082Seschrock 	if ((g_zfs = libzfs_init()) == NULL) {
13072082Seschrock 		(void) fprintf(stderr, gettext("failed to initialize ZFS "
13082082Seschrock 		    "library\n"));
13092082Seschrock 		return (Z_ERR);
13102082Seschrock 	}
13112082Seschrock 
13122082Seschrock 	return (Z_OK);
13132082Seschrock }
1314