xref: /onnv-gate/usr/src/cmd/zoneadm/zfs.c (revision 12781:4f82f6b1fb30)
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 /*
23*12781SFrank.Batschulat@Sun.COM  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
241867Sgjelinek  */
251867Sgjelinek 
261867Sgjelinek /*
271867Sgjelinek  * This file contains the functions used to support the ZFS integration
281867Sgjelinek  * with zones.  This includes validation (e.g. zonecfg dataset), cloning,
291867Sgjelinek  * file system creation and destruction.
301867Sgjelinek  */
311867Sgjelinek 
321867Sgjelinek #include <stdio.h>
331867Sgjelinek #include <errno.h>
341867Sgjelinek #include <unistd.h>
351867Sgjelinek #include <string.h>
361867Sgjelinek #include <locale.h>
371867Sgjelinek #include <libintl.h>
381867Sgjelinek #include <sys/stat.h>
391867Sgjelinek #include <sys/statvfs.h>
401867Sgjelinek #include <libgen.h>
411867Sgjelinek #include <libzonecfg.h>
421867Sgjelinek #include <sys/mnttab.h>
431867Sgjelinek #include <libzfs.h>
447093Sgjelinek #include <sys/mntent.h>
459386Sgerald.jelinek@sun.com #include <values.h>
4611276SJordan.Vaughan@Sun.com #include <strings.h>
4711276SJordan.Vaughan@Sun.com #include <assert.h>
481867Sgjelinek 
491867Sgjelinek #include "zoneadm.h"
501867Sgjelinek 
512082Seschrock libzfs_handle_t *g_zfs;
521867Sgjelinek 
531867Sgjelinek typedef struct zfs_mount_data {
541867Sgjelinek 	char		*match_name;
551867Sgjelinek 	zfs_handle_t	*match_handle;
561867Sgjelinek } zfs_mount_data_t;
571867Sgjelinek 
581867Sgjelinek typedef struct zfs_snapshot_data {
599386Sgerald.jelinek@sun.com 	char	*match_name;	/* zonename@SUNWzone */
609386Sgerald.jelinek@sun.com 	int	len;		/* strlen of match_name */
619386Sgerald.jelinek@sun.com 	int	max;		/* highest digit appended to snap name */
629386Sgerald.jelinek@sun.com 	int	num;		/* number of snapshots to rename */
639386Sgerald.jelinek@sun.com 	int	cntr;		/* counter for renaming snapshots */
641867Sgjelinek } zfs_snapshot_data_t;
651867Sgjelinek 
669386Sgerald.jelinek@sun.com typedef struct clone_data {
679386Sgerald.jelinek@sun.com 	zfs_handle_t	*clone_zhp;	/* clone dataset to promote */
689386Sgerald.jelinek@sun.com 	time_t		origin_creation; /* snapshot creation time of clone */
699386Sgerald.jelinek@sun.com 	const char	*snapshot;	/* snapshot of dataset being demoted */
709386Sgerald.jelinek@sun.com } clone_data_t;
719386Sgerald.jelinek@sun.com 
721867Sgjelinek /*
731867Sgjelinek  * A ZFS file system iterator call-back function which is used to validate
741867Sgjelinek  * datasets imported into the zone.
751867Sgjelinek  */
761867Sgjelinek /* ARGSUSED */
771867Sgjelinek static int
check_zvol(zfs_handle_t * zhp,void * unused)781867Sgjelinek check_zvol(zfs_handle_t *zhp, void *unused)
791867Sgjelinek {
801867Sgjelinek 	int ret;
811867Sgjelinek 
821867Sgjelinek 	if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
831867Sgjelinek 		/*
841867Sgjelinek 		 * TRANSLATION_NOTE
851867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
861867Sgjelinek 		 */
871867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify zfs dataset %s: "
881867Sgjelinek 		    "volumes cannot be specified as a zone dataset resource\n"),
891867Sgjelinek 		    zfs_get_name(zhp));
901867Sgjelinek 		ret = -1;
911867Sgjelinek 	} else {
921867Sgjelinek 		ret = zfs_iter_children(zhp, check_zvol, NULL);
931867Sgjelinek 	}
941867Sgjelinek 
951867Sgjelinek 	zfs_close(zhp);
961867Sgjelinek 
971867Sgjelinek 	return (ret);
981867Sgjelinek }
991867Sgjelinek 
1001867Sgjelinek /*
1011867Sgjelinek  * A ZFS file system iterator call-back function which returns the
1021867Sgjelinek  * zfs_handle_t for a ZFS file system on the specified mount point.
1031867Sgjelinek  */
1041867Sgjelinek static int
match_mountpoint(zfs_handle_t * zhp,void * data)1051867Sgjelinek match_mountpoint(zfs_handle_t *zhp, void *data)
1061867Sgjelinek {
1071867Sgjelinek 	int			res;
1081867Sgjelinek 	zfs_mount_data_t	*cbp;
1091867Sgjelinek 	char			mp[ZFS_MAXPROPLEN];
1101867Sgjelinek 
1111867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1121867Sgjelinek 		zfs_close(zhp);
1131867Sgjelinek 		return (0);
1141867Sgjelinek 	}
1151867Sgjelinek 
1167093Sgjelinek 	/* First check if the dataset is mounted. */
1177093Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
1187093Sgjelinek 	    0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
1197093Sgjelinek 		zfs_close(zhp);
1207093Sgjelinek 		return (0);
1217093Sgjelinek 	}
1227093Sgjelinek 
1237093Sgjelinek 	/* Now check mount point. */
1241867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
1257093Sgjelinek 	    0, B_FALSE) != 0) {
1267093Sgjelinek 		zfs_close(zhp);
1277093Sgjelinek 		return (0);
1287093Sgjelinek 	}
1297093Sgjelinek 
1307093Sgjelinek 	cbp = (zfs_mount_data_t *)data;
1317093Sgjelinek 
1327093Sgjelinek 	if (strcmp(mp, "legacy") == 0) {
1337093Sgjelinek 		/* If legacy, must look in mnttab for mountpoint. */
1347093Sgjelinek 		FILE		*fp;
1357093Sgjelinek 		struct mnttab	entry;
1367093Sgjelinek 		const char	*nm;
1377093Sgjelinek 
1387093Sgjelinek 		nm = zfs_get_name(zhp);
1397093Sgjelinek 		if ((fp = fopen(MNTTAB, "r")) == NULL) {
1407093Sgjelinek 			zfs_close(zhp);
1417093Sgjelinek 			return (0);
1427093Sgjelinek 		}
1437093Sgjelinek 
1447093Sgjelinek 		while (getmntent(fp, &entry) == 0) {
1457093Sgjelinek 			if (strcmp(nm, entry.mnt_special) == 0) {
1467093Sgjelinek 				if (strcmp(entry.mnt_mountp, cbp->match_name)
1477093Sgjelinek 				    == 0) {
1487093Sgjelinek 					(void) fclose(fp);
1497093Sgjelinek 					cbp->match_handle = zhp;
1507093Sgjelinek 					return (1);
1517093Sgjelinek 				}
1527093Sgjelinek 				break;
1537093Sgjelinek 			}
1547093Sgjelinek 		}
1557093Sgjelinek 		(void) fclose(fp);
1567093Sgjelinek 
1577093Sgjelinek 	} else if (strcmp(mp, cbp->match_name) == 0) {
1581867Sgjelinek 		cbp->match_handle = zhp;
1591867Sgjelinek 		return (1);
1601867Sgjelinek 	}
1611867Sgjelinek 
1627093Sgjelinek 	/* Iterate over any nested datasets. */
1631867Sgjelinek 	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1641867Sgjelinek 	zfs_close(zhp);
1651867Sgjelinek 	return (res);
1661867Sgjelinek }
1671867Sgjelinek 
1681867Sgjelinek /*
1691867Sgjelinek  * Get ZFS handle for the specified mount point.
1701867Sgjelinek  */
1711867Sgjelinek static zfs_handle_t *
mount2zhandle(char * mountpoint)1721867Sgjelinek mount2zhandle(char *mountpoint)
1731867Sgjelinek {
1741867Sgjelinek 	zfs_mount_data_t	cb;
1751867Sgjelinek 
1761867Sgjelinek 	cb.match_name = mountpoint;
1771867Sgjelinek 	cb.match_handle = NULL;
1782082Seschrock 	(void) zfs_iter_root(g_zfs, match_mountpoint, &cb);
1791867Sgjelinek 	return (cb.match_handle);
1801867Sgjelinek }
1811867Sgjelinek 
1821867Sgjelinek /*
1831867Sgjelinek  * Check if there is already a file system (zfs or any other type) mounted on
1841867Sgjelinek  * path.
1851867Sgjelinek  */
1861867Sgjelinek static boolean_t
is_mountpnt(char * path)1871867Sgjelinek is_mountpnt(char *path)
1881867Sgjelinek {
1891867Sgjelinek 	FILE		*fp;
1901867Sgjelinek 	struct mnttab	entry;
1911867Sgjelinek 
1927093Sgjelinek 	if ((fp = fopen(MNTTAB, "r")) == NULL)
1931867Sgjelinek 		return (B_FALSE);
1941867Sgjelinek 
1951867Sgjelinek 	while (getmntent(fp, &entry) == 0) {
1961867Sgjelinek 		if (strcmp(path, entry.mnt_mountp) == 0) {
1971867Sgjelinek 			(void) fclose(fp);
1981867Sgjelinek 			return (B_TRUE);
1991867Sgjelinek 		}
2001867Sgjelinek 	}
2011867Sgjelinek 
2021867Sgjelinek 	(void) fclose(fp);
2031867Sgjelinek 	return (B_FALSE);
2041867Sgjelinek }
2051867Sgjelinek 
2061867Sgjelinek /*
2077089Sgjelinek  * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
2081867Sgjelinek  */
2091867Sgjelinek static int
pre_snapshot(char * presnapbuf)2107089Sgjelinek pre_snapshot(char *presnapbuf)
2111867Sgjelinek {
2127089Sgjelinek 	int status;
2131867Sgjelinek 
2147089Sgjelinek 	/* No brand-specific handler */
2157089Sgjelinek 	if (presnapbuf[0] == '\0')
2167089Sgjelinek 		return (Z_OK);
2171867Sgjelinek 
2187089Sgjelinek 	/* Run the hook */
2199310Sgerald.jelinek@sun.com 	status = do_subproc(presnapbuf);
2207089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific presnapshot"),
2217089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2221867Sgjelinek 		return (Z_ERR);
2231867Sgjelinek 
2241867Sgjelinek 	return (Z_OK);
2251867Sgjelinek }
2261867Sgjelinek 
2271867Sgjelinek /*
2287089Sgjelinek  * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
2291867Sgjelinek  */
2301867Sgjelinek static int
post_snapshot(char * postsnapbuf)2317089Sgjelinek post_snapshot(char *postsnapbuf)
2321867Sgjelinek {
2337089Sgjelinek 	int status;
2341867Sgjelinek 
2357089Sgjelinek 	/* No brand-specific handler */
2367089Sgjelinek 	if (postsnapbuf[0] == '\0')
2377089Sgjelinek 		return (Z_OK);
2387089Sgjelinek 
2397089Sgjelinek 	/* Run the hook */
2409310Sgerald.jelinek@sun.com 	status = do_subproc(postsnapbuf);
2417089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific postsnapshot"),
2427089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2431867Sgjelinek 		return (Z_ERR);
2441867Sgjelinek 
2451867Sgjelinek 	return (Z_OK);
2461867Sgjelinek }
2471867Sgjelinek 
2481867Sgjelinek /*
2491867Sgjelinek  * This is a ZFS snapshot iterator call-back function which returns the
2501867Sgjelinek  * highest number of SUNWzone snapshots that have been taken.
2511867Sgjelinek  */
2521867Sgjelinek static int
get_snap_max(zfs_handle_t * zhp,void * data)2531867Sgjelinek get_snap_max(zfs_handle_t *zhp, void *data)
2541867Sgjelinek {
2551867Sgjelinek 	int			res;
2561867Sgjelinek 	zfs_snapshot_data_t	*cbp;
2571867Sgjelinek 
2581867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2591867Sgjelinek 		zfs_close(zhp);
2601867Sgjelinek 		return (0);
2611867Sgjelinek 	}
2621867Sgjelinek 
2631867Sgjelinek 	cbp = (zfs_snapshot_data_t *)data;
2641867Sgjelinek 
2651867Sgjelinek 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2661867Sgjelinek 		char	*nump;
2671867Sgjelinek 		int	num;
2681867Sgjelinek 
2699386Sgerald.jelinek@sun.com 		cbp->num++;
2701867Sgjelinek 		nump = (char *)(zfs_get_name(zhp) + cbp->len);
2711867Sgjelinek 		num = atoi(nump);
2721867Sgjelinek 		if (num > cbp->max)
2731867Sgjelinek 			cbp->max = num;
2741867Sgjelinek 	}
2751867Sgjelinek 
2761867Sgjelinek 	res = zfs_iter_snapshots(zhp, get_snap_max, data);
2771867Sgjelinek 	zfs_close(zhp);
2781867Sgjelinek 	return (res);
2791867Sgjelinek }
2801867Sgjelinek 
2811867Sgjelinek /*
2821867Sgjelinek  * Take a ZFS snapshot to be used for cloning the zone.
2831867Sgjelinek  */
2841867Sgjelinek static int
take_snapshot(zfs_handle_t * zhp,char * snapshot_name,int snap_size,char * presnapbuf,char * postsnapbuf)2857089Sgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
2867089Sgjelinek     char *presnapbuf, char *postsnapbuf)
2871867Sgjelinek {
2881867Sgjelinek 	int			res;
2891867Sgjelinek 	char			template[ZFS_MAXNAMELEN];
2901867Sgjelinek 	zfs_snapshot_data_t	cb;
2911867Sgjelinek 
2921867Sgjelinek 	/*
2931867Sgjelinek 	 * First we need to figure out the next available name for the
2941867Sgjelinek 	 * zone snapshot.  Look through the list of zones snapshots for
2951867Sgjelinek 	 * this file system to determine the maximum snapshot name.
2961867Sgjelinek 	 */
2971867Sgjelinek 	if (snprintf(template, sizeof (template), "%s@SUNWzone",
2981867Sgjelinek 	    zfs_get_name(zhp)) >=  sizeof (template))
2991867Sgjelinek 		return (Z_ERR);
3001867Sgjelinek 
3011867Sgjelinek 	cb.match_name = template;
3021867Sgjelinek 	cb.len = strlen(template);
3031867Sgjelinek 	cb.max = 0;
3041867Sgjelinek 
3051867Sgjelinek 	if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0)
3061867Sgjelinek 		return (Z_ERR);
3071867Sgjelinek 
3081867Sgjelinek 	cb.max++;
3091867Sgjelinek 
3101867Sgjelinek 	if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
3111867Sgjelinek 	    zfs_get_name(zhp), cb.max) >= snap_size)
3121867Sgjelinek 		return (Z_ERR);
3131867Sgjelinek 
3147089Sgjelinek 	if (pre_snapshot(presnapbuf) != Z_OK)
3151867Sgjelinek 		return (Z_ERR);
3167265Sahrens 	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
3177089Sgjelinek 	if (post_snapshot(postsnapbuf) != Z_OK)
3181867Sgjelinek 		return (Z_ERR);
3191867Sgjelinek 
3201867Sgjelinek 	if (res != 0)
3211867Sgjelinek 		return (Z_ERR);
3221867Sgjelinek 	return (Z_OK);
3231867Sgjelinek }
3241867Sgjelinek 
3251867Sgjelinek /*
3261867Sgjelinek  * We are using an explicit snapshot from some earlier point in time so
3277089Sgjelinek  * we need to validate it.  Run the brand specific hook.
3281867Sgjelinek  */
3291867Sgjelinek static int
validate_snapshot(char * snapshot_name,char * snap_path,char * validsnapbuf)3307089Sgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
3311867Sgjelinek {
3327089Sgjelinek 	int status;
3337089Sgjelinek 	char cmdbuf[MAXPATHLEN];
3341867Sgjelinek 
3357089Sgjelinek 	/* No brand-specific handler */
3367089Sgjelinek 	if (validsnapbuf[0] == '\0')
3377089Sgjelinek 		return (Z_OK);
3381867Sgjelinek 
3397089Sgjelinek 	/* pass args - snapshot_name & snap_path */
3407089Sgjelinek 	if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
3417089Sgjelinek 	    snapshot_name, snap_path) >= sizeof (cmdbuf)) {
3427089Sgjelinek 		zerror("Command line too long");
3431867Sgjelinek 		return (Z_ERR);
3441867Sgjelinek 	}
3451867Sgjelinek 
3467089Sgjelinek 	/* Run the hook */
3479310Sgerald.jelinek@sun.com 	status = do_subproc(cmdbuf);
3487089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
3497089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
3507089Sgjelinek 		return (Z_ERR);
3511867Sgjelinek 
3527089Sgjelinek 	return (Z_OK);
3531867Sgjelinek }
3541867Sgjelinek 
3551867Sgjelinek /*
3561867Sgjelinek  * Remove the sw inventory file from inside this zonepath that we picked up out
3571867Sgjelinek  * of the snapshot.
3581867Sgjelinek  */
3591867Sgjelinek static int
clean_out_clone()3601867Sgjelinek clean_out_clone()
3611867Sgjelinek {
3621867Sgjelinek 	int err;
3631867Sgjelinek 	zone_dochandle_t handle;
3641867Sgjelinek 
3651867Sgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
3661867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3671867Sgjelinek 		return (Z_ERR);
3681867Sgjelinek 	}
3691867Sgjelinek 
3701867Sgjelinek 	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3711867Sgjelinek 		errno = err;
3721867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3731867Sgjelinek 		zonecfg_fini_handle(handle);
3741867Sgjelinek 		return (Z_ERR);
3751867Sgjelinek 	}
3761867Sgjelinek 
3771867Sgjelinek 	zonecfg_rm_detached(handle, B_FALSE);
3781867Sgjelinek 	zonecfg_fini_handle(handle);
3791867Sgjelinek 
3801867Sgjelinek 	return (Z_OK);
3811867Sgjelinek }
3821867Sgjelinek 
3831867Sgjelinek /*
3841867Sgjelinek  * Make a ZFS clone on zonepath from snapshot_name.
3851867Sgjelinek  */
3861867Sgjelinek static int
clone_snap(char * snapshot_name,char * zonepath)3871867Sgjelinek clone_snap(char *snapshot_name, char *zonepath)
3881867Sgjelinek {
3891867Sgjelinek 	int		res = Z_OK;
3901867Sgjelinek 	int		err;
3911867Sgjelinek 	zfs_handle_t	*zhp;
3921867Sgjelinek 	zfs_handle_t	*clone;
3932676Seschrock 	nvlist_t	*props = NULL;
3941867Sgjelinek 
3952082Seschrock 	if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
3961867Sgjelinek 		return (Z_NO_ENTRY);
3971867Sgjelinek 
3981867Sgjelinek 	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
3991867Sgjelinek 
400*12781SFrank.Batschulat@Sun.COM 	/*
401*12781SFrank.Batschulat@Sun.COM 	 * We turn off zfs SHARENFS and SHARESMB properties on the
402*12781SFrank.Batschulat@Sun.COM 	 * zoneroot dataset in order to prevent the GZ from sharing
403*12781SFrank.Batschulat@Sun.COM 	 * NGZ data by accident.
404*12781SFrank.Batschulat@Sun.COM 	 */
405*12781SFrank.Batschulat@Sun.COM 	if ((nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) ||
406*12781SFrank.Batschulat@Sun.COM 	    (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
407*12781SFrank.Batschulat@Sun.COM 	    "off") != 0) ||
408*12781SFrank.Batschulat@Sun.COM 	    (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARESMB),
409*12781SFrank.Batschulat@Sun.COM 	    "off") != 0)) {
4102744Snn35248 		if (props != NULL)
4112744Snn35248 			nvlist_free(props);
4122676Seschrock 		(void) fprintf(stderr, gettext("could not create ZFS clone "
4132676Seschrock 		    "%s: out of memory\n"), zonepath);
4142676Seschrock 		return (Z_ERR);
4152676Seschrock 	}
4162676Seschrock 
4172676Seschrock 	err = zfs_clone(zhp, zonepath, props);
4181867Sgjelinek 	zfs_close(zhp);
4192676Seschrock 
4202676Seschrock 	nvlist_free(props);
4212676Seschrock 
4221867Sgjelinek 	if (err != 0)
4231867Sgjelinek 		return (Z_ERR);
4241867Sgjelinek 
4251867Sgjelinek 	/* create the mountpoint if necessary */
4265094Slling 	if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL)
4271867Sgjelinek 		return (Z_ERR);
4281867Sgjelinek 
4291867Sgjelinek 	/*
4301867Sgjelinek 	 * The clone has been created so we need to print a diagnostic
4311867Sgjelinek 	 * message if one of the following steps fails for some reason.
4321867Sgjelinek 	 */
4331867Sgjelinek 	if (zfs_mount(clone, NULL, 0) != 0) {
4341867Sgjelinek 		(void) fprintf(stderr, gettext("could not mount ZFS clone "
4351867Sgjelinek 		    "%s\n"), zfs_get_name(clone));
4361867Sgjelinek 		res = Z_ERR;
4371867Sgjelinek 
4382676Seschrock 	} else if (clean_out_clone() != Z_OK) {
4392676Seschrock 		(void) fprintf(stderr, gettext("could not remove the "
4402676Seschrock 		    "software inventory from ZFS clone %s\n"),
4412676Seschrock 		    zfs_get_name(clone));
4422676Seschrock 		res = Z_ERR;
4431867Sgjelinek 	}
4441867Sgjelinek 
4451867Sgjelinek 	zfs_close(clone);
4461867Sgjelinek 	return (res);
4471867Sgjelinek }
4481867Sgjelinek 
4491867Sgjelinek /*
4501867Sgjelinek  * This function takes a zonepath and attempts to determine what the ZFS
4511867Sgjelinek  * file system name (not mountpoint) should be for that path.  We do not
4521867Sgjelinek  * assume that zonepath is an existing directory or ZFS fs since we use
4531867Sgjelinek  * this function as part of the process of creating a new ZFS fs or clone.
4541867Sgjelinek  *
4551867Sgjelinek  * The way this works is that we look at the parent directory of the zonepath
4561867Sgjelinek  * to see if it is a ZFS fs.  If it is, we get the name of that ZFS fs and
4571867Sgjelinek  * append the last component of the zonepath to generate the ZFS name for the
4581867Sgjelinek  * zonepath.  This matches the algorithm that ZFS uses for automatically
4591867Sgjelinek  * mounting a new fs after it is created.
4601867Sgjelinek  *
4611867Sgjelinek  * Although a ZFS fs can be mounted anywhere, we don't worry about handling
4621867Sgjelinek  * all of the complexity that a user could possibly configure with arbitrary
4631867Sgjelinek  * mounts since there is no way to generate a ZFS name from a random path in
4641867Sgjelinek  * the file system.  We only try to handle the automatic mounts that ZFS does
4651867Sgjelinek  * for each file system.  ZFS restricts this so that a new fs must be created
4661867Sgjelinek  * in an existing parent ZFS fs.  It then automatically mounts the new fs
4671867Sgjelinek  * directly under the mountpoint for the parent fs using the last component
4681867Sgjelinek  * of the name as the mountpoint directory.
4691867Sgjelinek  *
4701867Sgjelinek  * For example:
4711867Sgjelinek  *    Name			Mountpoint
4721867Sgjelinek  *    space/eng/dev/test/zone1	/project1/eng/dev/test/zone1
4731867Sgjelinek  *
4741867Sgjelinek  * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
4751867Sgjelinek  * Z_ERR.
4761867Sgjelinek  */
4771867Sgjelinek static int
path2name(char * zonepath,char * zfs_name,int len)4781867Sgjelinek path2name(char *zonepath, char *zfs_name, int len)
4791867Sgjelinek {
4801867Sgjelinek 	int		res;
4817093Sgjelinek 	char		*bnm, *dnm, *dname, *bname;
4821867Sgjelinek 	zfs_handle_t	*zhp;
4837093Sgjelinek 	struct stat	stbuf;
4841867Sgjelinek 
4857093Sgjelinek 	/*
4867093Sgjelinek 	 * We need two tmp strings to handle paths directly in / (e.g. /foo)
4877093Sgjelinek 	 * since dirname will overwrite the first char after "/" in this case.
4887093Sgjelinek 	 */
4897093Sgjelinek 	if ((bnm = strdup(zonepath)) == NULL)
4901867Sgjelinek 		return (Z_ERR);
4911867Sgjelinek 
4927093Sgjelinek 	if ((dnm = strdup(zonepath)) == NULL) {
4937093Sgjelinek 		free(bnm);
4947093Sgjelinek 		return (Z_ERR);
4957093Sgjelinek 	}
4967093Sgjelinek 
4977093Sgjelinek 	bname = basename(bnm);
4987093Sgjelinek 	dname = dirname(dnm);
4997093Sgjelinek 
5001867Sgjelinek 	/*
5017093Sgjelinek 	 * This is a quick test to save iterating over all of the zfs datasets
5027093Sgjelinek 	 * on the system (which can be a lot).  If the parent dir is not in a
5037093Sgjelinek 	 * ZFS fs, then we're done.
5041867Sgjelinek 	 */
5057093Sgjelinek 	if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) ||
5067093Sgjelinek 	    strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) {
5077093Sgjelinek 		free(bnm);
5087093Sgjelinek 		free(dnm);
5091867Sgjelinek 		return (Z_ERR);
5107093Sgjelinek 	}
5111867Sgjelinek 
5127093Sgjelinek 	/* See if the parent directory is its own ZFS dataset. */
5137093Sgjelinek 	if ((zhp = mount2zhandle(dname)) == NULL) {
5147093Sgjelinek 		/*
5157093Sgjelinek 		 * The parent is not a ZFS dataset so we can't automatically
5167093Sgjelinek 		 * create a dataset on the given path.
5177093Sgjelinek 		 */
5187093Sgjelinek 		free(bnm);
5197093Sgjelinek 		free(dnm);
5207093Sgjelinek 		return (Z_ERR);
5217093Sgjelinek 	}
5221867Sgjelinek 
5237093Sgjelinek 	res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname);
5247093Sgjelinek 
5257093Sgjelinek 	free(bnm);
5267093Sgjelinek 	free(dnm);
5271867Sgjelinek 	zfs_close(zhp);
5281867Sgjelinek 	if (res >= len)
5291867Sgjelinek 		return (Z_ERR);
5301867Sgjelinek 
5311867Sgjelinek 	return (Z_OK);
5321867Sgjelinek }
5331867Sgjelinek 
5341867Sgjelinek /*
5351867Sgjelinek  * A ZFS file system iterator call-back function used to determine if the
5361867Sgjelinek  * file system has dependents (snapshots & clones).
5371867Sgjelinek  */
5381867Sgjelinek /* ARGSUSED */
5391867Sgjelinek static int
has_dependent(zfs_handle_t * zhp,void * data)5401867Sgjelinek has_dependent(zfs_handle_t *zhp, void *data)
5411867Sgjelinek {
5421867Sgjelinek 	zfs_close(zhp);
5431867Sgjelinek 	return (1);
5441867Sgjelinek }
5451867Sgjelinek 
5461867Sgjelinek /*
5471867Sgjelinek  * Given a snapshot name, get the file system path where the snapshot lives.
5481867Sgjelinek  * A snapshot name is of the form fs_name@snap_name.  For example, snapshot
5491867Sgjelinek  * pl/zones/z1@SUNWzone1 would have a path of
5501867Sgjelinek  * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5511867Sgjelinek  */
5521867Sgjelinek static int
snap2path(char * snap_name,char * path,int len)5531867Sgjelinek snap2path(char *snap_name, char *path, int len)
5541867Sgjelinek {
5551867Sgjelinek 	char		*p;
5561867Sgjelinek 	zfs_handle_t	*zhp;
5571867Sgjelinek 	char		mp[ZFS_MAXPROPLEN];
5581867Sgjelinek 
5591867Sgjelinek 	if ((p = strrchr(snap_name, '@')) == NULL)
5601867Sgjelinek 		return (Z_ERR);
5611867Sgjelinek 
5621867Sgjelinek 	/* Get the file system name from the snap_name. */
5631867Sgjelinek 	*p = '\0';
5645094Slling 	zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET);
5651867Sgjelinek 	*p = '@';
5661867Sgjelinek 	if (zhp == NULL)
5671867Sgjelinek 		return (Z_ERR);
5681867Sgjelinek 
5691867Sgjelinek 	/* Get the file system mount point. */
5701867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
5712082Seschrock 	    0, B_FALSE) != 0) {
5721867Sgjelinek 		zfs_close(zhp);
5731867Sgjelinek 		return (Z_ERR);
5741867Sgjelinek 	}
5751867Sgjelinek 	zfs_close(zhp);
5761867Sgjelinek 
5771867Sgjelinek 	p++;
5781867Sgjelinek 	if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5791867Sgjelinek 		return (Z_ERR);
5801867Sgjelinek 
5811867Sgjelinek 	return (Z_OK);
5821867Sgjelinek }
5831867Sgjelinek 
5841867Sgjelinek /*
5859386Sgerald.jelinek@sun.com  * This callback function is used to iterate through a snapshot's dependencies
5869386Sgerald.jelinek@sun.com  * to find a filesystem that is a direct clone of the snapshot being iterated.
5879386Sgerald.jelinek@sun.com  */
5889386Sgerald.jelinek@sun.com static int
get_direct_clone(zfs_handle_t * zhp,void * data)5899386Sgerald.jelinek@sun.com get_direct_clone(zfs_handle_t *zhp, void *data)
5909386Sgerald.jelinek@sun.com {
5919386Sgerald.jelinek@sun.com 	clone_data_t	*cd = data;
5929386Sgerald.jelinek@sun.com 	char		origin[ZFS_MAXNAMELEN];
5939386Sgerald.jelinek@sun.com 	char		ds_path[ZFS_MAXNAMELEN];
5949386Sgerald.jelinek@sun.com 
5959386Sgerald.jelinek@sun.com 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
5969386Sgerald.jelinek@sun.com 		zfs_close(zhp);
5979386Sgerald.jelinek@sun.com 		return (0);
5989386Sgerald.jelinek@sun.com 	}
5999386Sgerald.jelinek@sun.com 
6009386Sgerald.jelinek@sun.com 	(void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
6019386Sgerald.jelinek@sun.com 
6029386Sgerald.jelinek@sun.com 	/* Make sure this is a direct clone of the snapshot we're iterating. */
6039386Sgerald.jelinek@sun.com 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
6049386Sgerald.jelinek@sun.com 	    NULL, 0, B_FALSE) != 0 || strcmp(origin, cd->snapshot) != 0) {
6059386Sgerald.jelinek@sun.com 		zfs_close(zhp);
6069386Sgerald.jelinek@sun.com 		return (0);
6079386Sgerald.jelinek@sun.com 	}
6089386Sgerald.jelinek@sun.com 
6099386Sgerald.jelinek@sun.com 	if (cd->clone_zhp != NULL)
6109386Sgerald.jelinek@sun.com 		zfs_close(cd->clone_zhp);
6119386Sgerald.jelinek@sun.com 
6129386Sgerald.jelinek@sun.com 	cd->clone_zhp = zhp;
6139386Sgerald.jelinek@sun.com 	return (1);
6149386Sgerald.jelinek@sun.com }
6159386Sgerald.jelinek@sun.com 
6169386Sgerald.jelinek@sun.com /*
6179386Sgerald.jelinek@sun.com  * A ZFS file system iterator call-back function used to determine the clone
6189386Sgerald.jelinek@sun.com  * to promote.  This function finds the youngest (i.e. last one taken) snapshot
6199386Sgerald.jelinek@sun.com  * that has a clone.  If found, it returns a reference to that clone in the
6209386Sgerald.jelinek@sun.com  * callback data.
6219386Sgerald.jelinek@sun.com  */
6229386Sgerald.jelinek@sun.com static int
find_clone(zfs_handle_t * zhp,void * data)6239386Sgerald.jelinek@sun.com find_clone(zfs_handle_t *zhp, void *data)
6249386Sgerald.jelinek@sun.com {
6259386Sgerald.jelinek@sun.com 	clone_data_t	*cd = data;
6269386Sgerald.jelinek@sun.com 	time_t		snap_creation;
6279386Sgerald.jelinek@sun.com 	int		zret = 0;
6289386Sgerald.jelinek@sun.com 
6299386Sgerald.jelinek@sun.com 	/* If snapshot has no clones, skip it */
6309386Sgerald.jelinek@sun.com 	if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) {
6319386Sgerald.jelinek@sun.com 		zfs_close(zhp);
6329386Sgerald.jelinek@sun.com 		return (0);
6339386Sgerald.jelinek@sun.com 	}
6349386Sgerald.jelinek@sun.com 
6359386Sgerald.jelinek@sun.com 	cd->snapshot = zfs_get_name(zhp);
6369386Sgerald.jelinek@sun.com 
6379386Sgerald.jelinek@sun.com 	/* Get the creation time of this snapshot */
6389386Sgerald.jelinek@sun.com 	snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
6399386Sgerald.jelinek@sun.com 
6409386Sgerald.jelinek@sun.com 	/*
6419386Sgerald.jelinek@sun.com 	 * If this snapshot's creation time is greater than (i.e. younger than)
6429386Sgerald.jelinek@sun.com 	 * the current youngest snapshot found, iterate this snapshot to
6439386Sgerald.jelinek@sun.com 	 * get the right clone.
6449386Sgerald.jelinek@sun.com 	 */
6459386Sgerald.jelinek@sun.com 	if (snap_creation >= cd->origin_creation) {
6469386Sgerald.jelinek@sun.com 		/*
6479386Sgerald.jelinek@sun.com 		 * Iterate the dependents of this snapshot to find a clone
6489386Sgerald.jelinek@sun.com 		 * that's a direct dependent.
6499386Sgerald.jelinek@sun.com 		 */
6509386Sgerald.jelinek@sun.com 		if ((zret = zfs_iter_dependents(zhp, B_FALSE, get_direct_clone,
6519386Sgerald.jelinek@sun.com 		    cd)) == -1) {
6529386Sgerald.jelinek@sun.com 			zfs_close(zhp);
6539386Sgerald.jelinek@sun.com 			return (1);
6549386Sgerald.jelinek@sun.com 		} else if (zret == 1) {
6559386Sgerald.jelinek@sun.com 			/*
6569386Sgerald.jelinek@sun.com 			 * Found a clone, update the origin_creation time
6579386Sgerald.jelinek@sun.com 			 * in the callback data.
6589386Sgerald.jelinek@sun.com 			 */
6599386Sgerald.jelinek@sun.com 			cd->origin_creation = snap_creation;
6609386Sgerald.jelinek@sun.com 		}
6619386Sgerald.jelinek@sun.com 	}
6629386Sgerald.jelinek@sun.com 
6639386Sgerald.jelinek@sun.com 	zfs_close(zhp);
6649386Sgerald.jelinek@sun.com 	return (0);
6659386Sgerald.jelinek@sun.com }
6669386Sgerald.jelinek@sun.com 
6679386Sgerald.jelinek@sun.com /*
6689386Sgerald.jelinek@sun.com  * A ZFS file system iterator call-back function used to remove standalone
6699386Sgerald.jelinek@sun.com  * snapshots.
6709386Sgerald.jelinek@sun.com  */
6719386Sgerald.jelinek@sun.com /* ARGSUSED */
6729386Sgerald.jelinek@sun.com static int
rm_snap(zfs_handle_t * zhp,void * data)6739386Sgerald.jelinek@sun.com rm_snap(zfs_handle_t *zhp, void *data)
6749386Sgerald.jelinek@sun.com {
6759386Sgerald.jelinek@sun.com 	/* If snapshot has clones, something is wrong */
6769386Sgerald.jelinek@sun.com 	if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) {
6779386Sgerald.jelinek@sun.com 		zfs_close(zhp);
6789386Sgerald.jelinek@sun.com 		return (1);
6799386Sgerald.jelinek@sun.com 	}
6809386Sgerald.jelinek@sun.com 
6819386Sgerald.jelinek@sun.com 	if (zfs_unmount(zhp, NULL, 0) == 0) {
68210242Schris.kirby@sun.com 		(void) zfs_destroy(zhp, B_FALSE);
6839386Sgerald.jelinek@sun.com 	}
6849386Sgerald.jelinek@sun.com 
6859386Sgerald.jelinek@sun.com 	zfs_close(zhp);
6869386Sgerald.jelinek@sun.com 	return (0);
6879386Sgerald.jelinek@sun.com }
6889386Sgerald.jelinek@sun.com 
6899386Sgerald.jelinek@sun.com /*
6909386Sgerald.jelinek@sun.com  * A ZFS snapshot iterator call-back function which renames snapshots.
6919386Sgerald.jelinek@sun.com  */
6929386Sgerald.jelinek@sun.com static int
rename_snap(zfs_handle_t * zhp,void * data)6939386Sgerald.jelinek@sun.com rename_snap(zfs_handle_t *zhp, void *data)
6949386Sgerald.jelinek@sun.com {
6959386Sgerald.jelinek@sun.com 	int			res;
6969386Sgerald.jelinek@sun.com 	zfs_snapshot_data_t	*cbp;
6979386Sgerald.jelinek@sun.com 	char			template[ZFS_MAXNAMELEN];
6989386Sgerald.jelinek@sun.com 
6999386Sgerald.jelinek@sun.com 	cbp = (zfs_snapshot_data_t *)data;
7009386Sgerald.jelinek@sun.com 
7019386Sgerald.jelinek@sun.com 	/*
7029386Sgerald.jelinek@sun.com 	 * When renaming snapshots with the iterator, the iterator can see
7039386Sgerald.jelinek@sun.com 	 * the same snapshot after we've renamed up in the namespace.  To
7049386Sgerald.jelinek@sun.com 	 * prevent this we check the count for the number of snapshots we have
7059386Sgerald.jelinek@sun.com 	 * to rename and stop at that point.
7069386Sgerald.jelinek@sun.com 	 */
7079386Sgerald.jelinek@sun.com 	if (cbp->cntr >= cbp->num) {
7089386Sgerald.jelinek@sun.com 		zfs_close(zhp);
7099386Sgerald.jelinek@sun.com 		return (0);
7109386Sgerald.jelinek@sun.com 	}
7119386Sgerald.jelinek@sun.com 
7129386Sgerald.jelinek@sun.com 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
7139386Sgerald.jelinek@sun.com 		zfs_close(zhp);
7149386Sgerald.jelinek@sun.com 		return (0);
7159386Sgerald.jelinek@sun.com 	}
7169386Sgerald.jelinek@sun.com 
7179386Sgerald.jelinek@sun.com 	/* Only rename the snapshots we automatically generate when we clone. */
7189386Sgerald.jelinek@sun.com 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) != 0) {
7199386Sgerald.jelinek@sun.com 		zfs_close(zhp);
7209386Sgerald.jelinek@sun.com 		return (0);
7219386Sgerald.jelinek@sun.com 	}
7229386Sgerald.jelinek@sun.com 
7239386Sgerald.jelinek@sun.com 	(void) snprintf(template, sizeof (template), "%s%d", cbp->match_name,
7249386Sgerald.jelinek@sun.com 	    cbp->max++);
7259386Sgerald.jelinek@sun.com 
7269386Sgerald.jelinek@sun.com 	res = (zfs_rename(zhp, template, B_FALSE) != 0);
7279386Sgerald.jelinek@sun.com 	if (res != 0)
7289386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("failed to rename snapshot %s "
7299386Sgerald.jelinek@sun.com 		    "to %s: %s\n"), zfs_get_name(zhp), template,
7309386Sgerald.jelinek@sun.com 		    libzfs_error_description(g_zfs));
7319386Sgerald.jelinek@sun.com 
7329386Sgerald.jelinek@sun.com 	cbp->cntr++;
7339386Sgerald.jelinek@sun.com 
7349386Sgerald.jelinek@sun.com 	zfs_close(zhp);
7359386Sgerald.jelinek@sun.com 	return (res);
7369386Sgerald.jelinek@sun.com }
7379386Sgerald.jelinek@sun.com 
7389386Sgerald.jelinek@sun.com /*
7399386Sgerald.jelinek@sun.com  * Rename the source dataset's snapshots that are automatically generated when
7409386Sgerald.jelinek@sun.com  * we clone a zone so that there won't be a name collision when we promote the
7419386Sgerald.jelinek@sun.com  * cloned dataset.  Once the snapshots have been renamed, then promote the
7429386Sgerald.jelinek@sun.com  * clone.
7439386Sgerald.jelinek@sun.com  *
7449386Sgerald.jelinek@sun.com  * The snapshot rename process gets the highest number on the snapshot names
7459386Sgerald.jelinek@sun.com  * (the format is zonename@SUNWzoneXX where XX are digits) on both the source
7469386Sgerald.jelinek@sun.com  * and clone datasets, then renames the source dataset snapshots starting at
7479386Sgerald.jelinek@sun.com  * the next number.
7489386Sgerald.jelinek@sun.com  */
7499386Sgerald.jelinek@sun.com static int
promote_clone(zfs_handle_t * src_zhp,zfs_handle_t * cln_zhp)7509386Sgerald.jelinek@sun.com promote_clone(zfs_handle_t *src_zhp, zfs_handle_t *cln_zhp)
7519386Sgerald.jelinek@sun.com {
7529386Sgerald.jelinek@sun.com 	zfs_snapshot_data_t	sd;
7539386Sgerald.jelinek@sun.com 	char			nm[ZFS_MAXNAMELEN];
7549386Sgerald.jelinek@sun.com 	char			template[ZFS_MAXNAMELEN];
7559386Sgerald.jelinek@sun.com 
7569386Sgerald.jelinek@sun.com 	(void) strlcpy(nm, zfs_get_name(cln_zhp), sizeof (nm));
7579386Sgerald.jelinek@sun.com 	/*
7589386Sgerald.jelinek@sun.com 	 * Start by getting the clone's snapshot max which we use
7599386Sgerald.jelinek@sun.com 	 * during the rename of the original dataset's snapshots.
7609386Sgerald.jelinek@sun.com 	 */
7619386Sgerald.jelinek@sun.com 	(void) snprintf(template, sizeof (template), "%s@SUNWzone", nm);
7629386Sgerald.jelinek@sun.com 	sd.match_name = template;
7639386Sgerald.jelinek@sun.com 	sd.len = strlen(template);
7649386Sgerald.jelinek@sun.com 	sd.max = 0;
7659386Sgerald.jelinek@sun.com 
7669386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(cln_zhp, get_snap_max, &sd) != 0)
7679386Sgerald.jelinek@sun.com 		return (Z_ERR);
7689386Sgerald.jelinek@sun.com 
7699386Sgerald.jelinek@sun.com 	/*
7709386Sgerald.jelinek@sun.com 	 * Now make sure the source's snapshot max is at least as high as
7719386Sgerald.jelinek@sun.com 	 * the clone's snapshot max.
7729386Sgerald.jelinek@sun.com 	 */
7739386Sgerald.jelinek@sun.com 	(void) snprintf(template, sizeof (template), "%s@SUNWzone",
7749386Sgerald.jelinek@sun.com 	    zfs_get_name(src_zhp));
7759386Sgerald.jelinek@sun.com 	sd.match_name = template;
7769386Sgerald.jelinek@sun.com 	sd.len = strlen(template);
7779386Sgerald.jelinek@sun.com 	sd.num = 0;
7789386Sgerald.jelinek@sun.com 
7799386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(src_zhp, get_snap_max, &sd) != 0)
7809386Sgerald.jelinek@sun.com 		return (Z_ERR);
7819386Sgerald.jelinek@sun.com 
7829386Sgerald.jelinek@sun.com 	/*
7839386Sgerald.jelinek@sun.com 	 * Now rename the source dataset's snapshots so there's no
7849386Sgerald.jelinek@sun.com 	 * conflict when we promote the clone.
7859386Sgerald.jelinek@sun.com 	 */
7869386Sgerald.jelinek@sun.com 	sd.max++;
7879386Sgerald.jelinek@sun.com 	sd.cntr = 0;
7889386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(src_zhp, rename_snap, &sd) != 0)
7899386Sgerald.jelinek@sun.com 		return (Z_ERR);
7909386Sgerald.jelinek@sun.com 
7919386Sgerald.jelinek@sun.com 	/* close and reopen the clone dataset to get the latest info */
7929386Sgerald.jelinek@sun.com 	zfs_close(cln_zhp);
7939386Sgerald.jelinek@sun.com 	if ((cln_zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
7949386Sgerald.jelinek@sun.com 		return (Z_ERR);
7959386Sgerald.jelinek@sun.com 
7969386Sgerald.jelinek@sun.com 	if (zfs_promote(cln_zhp) != 0) {
7979386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("failed to promote %s: %s\n"),
7989386Sgerald.jelinek@sun.com 		    nm, libzfs_error_description(g_zfs));
7999386Sgerald.jelinek@sun.com 		return (Z_ERR);
8009386Sgerald.jelinek@sun.com 	}
8019386Sgerald.jelinek@sun.com 
8029386Sgerald.jelinek@sun.com 	zfs_close(cln_zhp);
8039386Sgerald.jelinek@sun.com 	return (Z_OK);
8049386Sgerald.jelinek@sun.com }
8059386Sgerald.jelinek@sun.com 
8069386Sgerald.jelinek@sun.com /*
8079386Sgerald.jelinek@sun.com  * Promote the youngest clone.  That clone will then become the origin of all
8089386Sgerald.jelinek@sun.com  * of the other clones that were hanging off of the source dataset.
8099386Sgerald.jelinek@sun.com  */
8109386Sgerald.jelinek@sun.com int
promote_all_clones(zfs_handle_t * zhp)8119386Sgerald.jelinek@sun.com promote_all_clones(zfs_handle_t *zhp)
8129386Sgerald.jelinek@sun.com {
8139386Sgerald.jelinek@sun.com 	clone_data_t	cd;
8149386Sgerald.jelinek@sun.com 	char		nm[ZFS_MAXNAMELEN];
8159386Sgerald.jelinek@sun.com 
8169386Sgerald.jelinek@sun.com 	cd.clone_zhp = NULL;
8179386Sgerald.jelinek@sun.com 	cd.origin_creation = 0;
8189386Sgerald.jelinek@sun.com 	cd.snapshot = NULL;
8199386Sgerald.jelinek@sun.com 
8209386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(zhp, find_clone, &cd) != 0) {
8219386Sgerald.jelinek@sun.com 		zfs_close(zhp);
8229386Sgerald.jelinek@sun.com 		return (Z_ERR);
8239386Sgerald.jelinek@sun.com 	}
8249386Sgerald.jelinek@sun.com 
8259386Sgerald.jelinek@sun.com 	/* Nothing to promote. */
8269386Sgerald.jelinek@sun.com 	if (cd.clone_zhp == NULL)
8279386Sgerald.jelinek@sun.com 		return (Z_OK);
8289386Sgerald.jelinek@sun.com 
8299386Sgerald.jelinek@sun.com 	/* Found the youngest clone to promote.  Promote it. */
8309386Sgerald.jelinek@sun.com 	if (promote_clone(zhp, cd.clone_zhp) != 0) {
8319386Sgerald.jelinek@sun.com 		zfs_close(cd.clone_zhp);
8329386Sgerald.jelinek@sun.com 		zfs_close(zhp);
8339386Sgerald.jelinek@sun.com 		return (Z_ERR);
8349386Sgerald.jelinek@sun.com 	}
8359386Sgerald.jelinek@sun.com 
8369386Sgerald.jelinek@sun.com 	/* close and reopen the main dataset to get the latest info */
8379386Sgerald.jelinek@sun.com 	(void) strlcpy(nm, zfs_get_name(zhp), sizeof (nm));
8389386Sgerald.jelinek@sun.com 	zfs_close(zhp);
8399386Sgerald.jelinek@sun.com 	if ((zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
8409386Sgerald.jelinek@sun.com 		return (Z_ERR);
8419386Sgerald.jelinek@sun.com 
8429386Sgerald.jelinek@sun.com 	return (Z_OK);
8439386Sgerald.jelinek@sun.com }
8449386Sgerald.jelinek@sun.com 
8459386Sgerald.jelinek@sun.com /*
8461867Sgjelinek  * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
8471867Sgjelinek  * possible, or by copying the data from the snapshot to the zonepath.
8481867Sgjelinek  */
8491867Sgjelinek int
clone_snapshot_zfs(char * snap_name,char * zonepath,char * validatesnap)8507089Sgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
8511867Sgjelinek {
8521867Sgjelinek 	int	err = Z_OK;
8531867Sgjelinek 	char	clone_name[MAXPATHLEN];
8541867Sgjelinek 	char	snap_path[MAXPATHLEN];
8551867Sgjelinek 
8561867Sgjelinek 	if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
8571867Sgjelinek 		(void) fprintf(stderr, gettext("unable to find path for %s.\n"),
8581867Sgjelinek 		    snap_name);
8591867Sgjelinek 		return (Z_ERR);
8601867Sgjelinek 	}
8611867Sgjelinek 
8627089Sgjelinek 	if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
8631867Sgjelinek 		return (Z_NO_ENTRY);
8641867Sgjelinek 
8651867Sgjelinek 	/*
8661867Sgjelinek 	 * The zonepath cannot be ZFS cloned, try to copy the data from
8671867Sgjelinek 	 * within the snapshot to the zonepath.
8681867Sgjelinek 	 */
8691867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
8701867Sgjelinek 		if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8711867Sgjelinek 			if (clean_out_clone() != Z_OK)
8721867Sgjelinek 				(void) fprintf(stderr,
8731867Sgjelinek 				    gettext("could not remove the "
8741867Sgjelinek 				    "software inventory from %s\n"), zonepath);
8751867Sgjelinek 
8761867Sgjelinek 		return (err);
8771867Sgjelinek 	}
8781867Sgjelinek 
8791867Sgjelinek 	if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
8801867Sgjelinek 		if (err != Z_NO_ENTRY) {
8811867Sgjelinek 			/*
8821867Sgjelinek 			 * Cloning the snapshot failed.  Fall back to trying
8831867Sgjelinek 			 * to install the zone by copying from the snapshot.
8841867Sgjelinek 			 */
8851867Sgjelinek 			if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8861867Sgjelinek 				if (clean_out_clone() != Z_OK)
8871867Sgjelinek 					(void) fprintf(stderr,
8881867Sgjelinek 					    gettext("could not remove the "
8891867Sgjelinek 					    "software inventory from %s\n"),
8901867Sgjelinek 					    zonepath);
8911867Sgjelinek 		} else {
8921867Sgjelinek 			/*
8931867Sgjelinek 			 * The snapshot is unusable for some reason so restore
8941867Sgjelinek 			 * the zone state to configured since we were unable to
8951867Sgjelinek 			 * actually do anything about getting the zone
8961867Sgjelinek 			 * installed.
8971867Sgjelinek 			 */
8981867Sgjelinek 			int tmp;
8991867Sgjelinek 
9001867Sgjelinek 			if ((tmp = zone_set_state(target_zone,
9011867Sgjelinek 			    ZONE_STATE_CONFIGURED)) != Z_OK) {
9021867Sgjelinek 				errno = tmp;
9031867Sgjelinek 				zperror2(target_zone,
9041867Sgjelinek 				    gettext("could not set state"));
9051867Sgjelinek 			}
9061867Sgjelinek 		}
9071867Sgjelinek 	}
9081867Sgjelinek 
9091867Sgjelinek 	return (err);
9101867Sgjelinek }
9111867Sgjelinek 
9121867Sgjelinek /*
9131867Sgjelinek  * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
9141867Sgjelinek  */
9151867Sgjelinek int
clone_zfs(char * source_zonepath,char * zonepath,char * presnapbuf,char * postsnapbuf)9167089Sgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
9177089Sgjelinek     char *postsnapbuf)
9181867Sgjelinek {
9191867Sgjelinek 	zfs_handle_t	*zhp;
9201867Sgjelinek 	char		clone_name[MAXPATHLEN];
9211867Sgjelinek 	char		snap_name[MAXPATHLEN];
9221867Sgjelinek 
9231867Sgjelinek 	/*
9241867Sgjelinek 	 * Try to get a zfs handle for the source_zonepath.  If this fails
9251867Sgjelinek 	 * the source_zonepath is not ZFS so return an error.
9261867Sgjelinek 	 */
9271867Sgjelinek 	if ((zhp = mount2zhandle(source_zonepath)) == NULL)
9281867Sgjelinek 		return (Z_ERR);
9291867Sgjelinek 
9301867Sgjelinek 	/*
9311867Sgjelinek 	 * Check if there is a file system already mounted on zonepath.  If so,
9321867Sgjelinek 	 * we can't clone to the path so we should fall back to copying.
9331867Sgjelinek 	 */
9341867Sgjelinek 	if (is_mountpnt(zonepath)) {
9351867Sgjelinek 		zfs_close(zhp);
9361867Sgjelinek 		(void) fprintf(stderr,
9371867Sgjelinek 		    gettext("A file system is already mounted on %s,\n"
9381867Sgjelinek 		    "preventing use of a ZFS clone.\n"), zonepath);
9391867Sgjelinek 		return (Z_ERR);
9401867Sgjelinek 	}
9411867Sgjelinek 
9421867Sgjelinek 	/*
9431867Sgjelinek 	 * Instead of using path2name to get the clone name from the zonepath,
9441867Sgjelinek 	 * we could generate a name from the source zone ZFS name.  However,
9451867Sgjelinek 	 * this would mean we would create the clone under the ZFS fs of the
9461867Sgjelinek 	 * source instead of what the zonepath says.  For example,
9471867Sgjelinek 	 *
9481867Sgjelinek 	 * source_zonepath		zonepath
9491867Sgjelinek 	 * /pl/zones/dev/z1		/pl/zones/deploy/z2
9501867Sgjelinek 	 *
9511867Sgjelinek 	 * We don't want the clone to be under "dev", we want it under
9521867Sgjelinek 	 * "deploy", so that we can leverage the normal attribute inheritance
9531867Sgjelinek 	 * that ZFS provides in the fs hierarchy.
9541867Sgjelinek 	 */
9551867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
9561867Sgjelinek 		zfs_close(zhp);
9571867Sgjelinek 		return (Z_ERR);
9581867Sgjelinek 	}
9591867Sgjelinek 
9607089Sgjelinek 	if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf,
9617089Sgjelinek 	    postsnapbuf) != Z_OK) {
9621867Sgjelinek 		zfs_close(zhp);
9631867Sgjelinek 		return (Z_ERR);
9641867Sgjelinek 	}
9651867Sgjelinek 	zfs_close(zhp);
9661867Sgjelinek 
9673686Sgjelinek 	if (clone_snap(snap_name, clone_name) != Z_OK) {
9683686Sgjelinek 		/* Clean up the snapshot we just took. */
9693686Sgjelinek 		if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT))
9703686Sgjelinek 		    != NULL) {
9713686Sgjelinek 			if (zfs_unmount(zhp, NULL, 0) == 0)
97210242Schris.kirby@sun.com 				(void) zfs_destroy(zhp, B_FALSE);
9733686Sgjelinek 			zfs_close(zhp);
9743686Sgjelinek 		}
9753686Sgjelinek 
9761867Sgjelinek 		return (Z_ERR);
9773686Sgjelinek 	}
9781867Sgjelinek 
9791867Sgjelinek 	(void) printf(gettext("Instead of copying, a ZFS clone has been "
9801867Sgjelinek 	    "created for this zone.\n"));
9811867Sgjelinek 
9821867Sgjelinek 	return (Z_OK);
9831867Sgjelinek }
9841867Sgjelinek 
9851867Sgjelinek /*
9861867Sgjelinek  * Attempt to create a ZFS file system for the specified zonepath.
9871867Sgjelinek  * We either will successfully create a ZFS file system and get it mounted
9881867Sgjelinek  * on the zonepath or we don't.  The caller doesn't care since a regular
9891867Sgjelinek  * directory is used for the zonepath if no ZFS file system is mounted there.
9901867Sgjelinek  */
9911867Sgjelinek void
create_zfs_zonepath(char * zonepath)9921867Sgjelinek create_zfs_zonepath(char *zonepath)
9931867Sgjelinek {
9941867Sgjelinek 	zfs_handle_t	*zhp;
9951867Sgjelinek 	char		zfs_name[MAXPATHLEN];
9962676Seschrock 	nvlist_t	*props = NULL;
9971867Sgjelinek 
9981867Sgjelinek 	if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
9991867Sgjelinek 		return;
10001867Sgjelinek 
10019023Sgerald.jelinek@sun.com 	/* Check if the dataset already exists. */
10029023Sgerald.jelinek@sun.com 	if ((zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) != NULL) {
10039023Sgerald.jelinek@sun.com 		zfs_close(zhp);
10049023Sgerald.jelinek@sun.com 		return;
10059023Sgerald.jelinek@sun.com 	}
10069023Sgerald.jelinek@sun.com 
1007*12781SFrank.Batschulat@Sun.COM 	/*
1008*12781SFrank.Batschulat@Sun.COM 	 * We turn off zfs SHARENFS and SHARESMB properties on the
1009*12781SFrank.Batschulat@Sun.COM 	 * zoneroot dataset in order to prevent the GZ from sharing
1010*12781SFrank.Batschulat@Sun.COM 	 * NGZ data by accident.
1011*12781SFrank.Batschulat@Sun.COM 	 */
1012*12781SFrank.Batschulat@Sun.COM 	if ((nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) ||
1013*12781SFrank.Batschulat@Sun.COM 	    (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
1014*12781SFrank.Batschulat@Sun.COM 	    "off") != 0) ||
1015*12781SFrank.Batschulat@Sun.COM 	    (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARESMB),
1016*12781SFrank.Batschulat@Sun.COM 	    "off") != 0)) {
10172744Snn35248 		if (props != NULL)
10182744Snn35248 			nvlist_free(props);
10192676Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
10202676Seschrock 		    "out of memory\n"), zfs_name);
10212676Seschrock 	}
10222676Seschrock 
10232676Seschrock 	if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 ||
10245094Slling 	    (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) {
10252082Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
10262082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
10272676Seschrock 		nvlist_free(props);
10281867Sgjelinek 		return;
10291867Sgjelinek 	}
10301867Sgjelinek 
10312676Seschrock 	nvlist_free(props);
10322676Seschrock 
10331867Sgjelinek 	if (zfs_mount(zhp, NULL, 0) != 0) {
10342082Seschrock 		(void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: "
10352082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
103610242Schris.kirby@sun.com 		(void) zfs_destroy(zhp, B_FALSE);
10371867Sgjelinek 	} else {
10381867Sgjelinek 		if (chmod(zonepath, S_IRWXU) != 0) {
10391867Sgjelinek 			(void) fprintf(stderr, gettext("file system %s "
10401867Sgjelinek 			    "successfully created, but chmod %o failed: %s\n"),
10411867Sgjelinek 			    zfs_name, S_IRWXU, strerror(errno));
10421867Sgjelinek 			(void) destroy_zfs(zonepath);
10431867Sgjelinek 		} else {
10441867Sgjelinek 			(void) printf(gettext("A ZFS file system has been "
10451867Sgjelinek 			    "created for this zone.\n"));
10461867Sgjelinek 		}
10471867Sgjelinek 	}
10481867Sgjelinek 
10491867Sgjelinek 	zfs_close(zhp);
10501867Sgjelinek }
10511867Sgjelinek 
10521867Sgjelinek /*
10531867Sgjelinek  * If the zonepath is a ZFS file system, attempt to destroy it.  We return Z_OK
10541867Sgjelinek  * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
10551867Sgjelinek  * which means the caller should clean up the zonepath in the traditional
10561867Sgjelinek  * way.
10571867Sgjelinek  */
10581867Sgjelinek int
destroy_zfs(char * zonepath)10591867Sgjelinek destroy_zfs(char *zonepath)
10601867Sgjelinek {
10611867Sgjelinek 	zfs_handle_t	*zhp;
10621867Sgjelinek 	boolean_t	is_clone = B_FALSE;
10631867Sgjelinek 	char		origin[ZFS_MAXPROPLEN];
10641867Sgjelinek 
10652082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
10661867Sgjelinek 		return (Z_ERR);
10671867Sgjelinek 
10689386Sgerald.jelinek@sun.com 	if (promote_all_clones(zhp) != 0)
10699386Sgerald.jelinek@sun.com 		return (Z_ERR);
10709386Sgerald.jelinek@sun.com 
10719386Sgerald.jelinek@sun.com 	/* Now cleanup any snapshots remaining. */
10729386Sgerald.jelinek@sun.com 	if (zfs_iter_snapshots(zhp, rm_snap, NULL) != 0) {
10739386Sgerald.jelinek@sun.com 		zfs_close(zhp);
10749386Sgerald.jelinek@sun.com 		return (Z_ERR);
10759386Sgerald.jelinek@sun.com 	}
10769386Sgerald.jelinek@sun.com 
10771867Sgjelinek 	/*
10789386Sgerald.jelinek@sun.com 	 * We can't destroy the file system if it has still has dependents.
10799386Sgerald.jelinek@sun.com 	 * There shouldn't be any at this point, but we'll double check.
10801867Sgjelinek 	 */
10819386Sgerald.jelinek@sun.com 	if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0) {
10829386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("zfs destroy %s failed: the "
10839386Sgerald.jelinek@sun.com 		    "dataset still has dependents\n"), zfs_get_name(zhp));
10841867Sgjelinek 		zfs_close(zhp);
10851867Sgjelinek 		return (Z_ERR);
10861867Sgjelinek 	}
10871867Sgjelinek 
10881867Sgjelinek 	/*
10891867Sgjelinek 	 * This might be a clone.  Try to get the snapshot so we can attempt
10901867Sgjelinek 	 * to destroy that as well.
10911867Sgjelinek 	 */
10921867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
10932082Seschrock 	    NULL, 0, B_FALSE) == 0)
10941867Sgjelinek 		is_clone = B_TRUE;
10951867Sgjelinek 
10969386Sgerald.jelinek@sun.com 	if (zfs_unmount(zhp, NULL, 0) != 0) {
10979386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("zfs unmount %s failed: %s\n"),
10989386Sgerald.jelinek@sun.com 		    zfs_get_name(zhp), libzfs_error_description(g_zfs));
10999386Sgerald.jelinek@sun.com 		zfs_close(zhp);
11009386Sgerald.jelinek@sun.com 		return (Z_ERR);
11019386Sgerald.jelinek@sun.com 	}
11029386Sgerald.jelinek@sun.com 
110310242Schris.kirby@sun.com 	if (zfs_destroy(zhp, B_FALSE) != 0) {
11041867Sgjelinek 		/*
11051867Sgjelinek 		 * If the destroy fails for some reason, try to remount
11061867Sgjelinek 		 * the file system so that we can use "rm -rf" to clean up
11071867Sgjelinek 		 * instead.
11081867Sgjelinek 		 */
11099386Sgerald.jelinek@sun.com 		(void) fprintf(stderr, gettext("zfs destroy %s failed: %s\n"),
11109386Sgerald.jelinek@sun.com 		    zfs_get_name(zhp), libzfs_error_description(g_zfs));
11111867Sgjelinek 		(void) zfs_mount(zhp, NULL, 0);
11121867Sgjelinek 		zfs_close(zhp);
11131867Sgjelinek 		return (Z_ERR);
11141867Sgjelinek 	}
11151867Sgjelinek 
11163686Sgjelinek 	/*
11173686Sgjelinek 	 * If the zone has ever been moved then the mountpoint dir will not be
11183686Sgjelinek 	 * cleaned up by the zfs_destroy().  To handle this case try to clean
11193686Sgjelinek 	 * it up now but don't worry if it fails, that will be normal.
11203686Sgjelinek 	 */
11213686Sgjelinek 	(void) rmdir(zonepath);
11223686Sgjelinek 
11231867Sgjelinek 	(void) printf(gettext("The ZFS file system for this zone has been "
11241867Sgjelinek 	    "destroyed.\n"));
11251867Sgjelinek 
11261867Sgjelinek 	if (is_clone) {
11271867Sgjelinek 		zfs_handle_t	*ohp;
11281867Sgjelinek 
11291867Sgjelinek 		/*
11301867Sgjelinek 		 * Try to clean up the snapshot that the clone was taken from.
11311867Sgjelinek 		 */
11322082Seschrock 		if ((ohp = zfs_open(g_zfs, origin,
11332082Seschrock 		    ZFS_TYPE_SNAPSHOT)) != NULL) {
11342474Seschrock 			if (zfs_iter_dependents(ohp, B_TRUE, has_dependent,
11352474Seschrock 			    NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0)
113610242Schris.kirby@sun.com 				(void) zfs_destroy(ohp, B_FALSE);
11371867Sgjelinek 			zfs_close(ohp);
11381867Sgjelinek 		}
11391867Sgjelinek 	}
11401867Sgjelinek 
11411867Sgjelinek 	zfs_close(zhp);
11421867Sgjelinek 	return (Z_OK);
11431867Sgjelinek }
11441867Sgjelinek 
11451867Sgjelinek /*
11461867Sgjelinek  * Return true if the path is its own zfs file system.  We determine this
11471867Sgjelinek  * by stat-ing the path to see if it is zfs and stat-ing the parent to see
11481867Sgjelinek  * if it is a different fs.
11491867Sgjelinek  */
11501867Sgjelinek boolean_t
is_zonepath_zfs(char * zonepath)11511867Sgjelinek is_zonepath_zfs(char *zonepath)
11521867Sgjelinek {
11531867Sgjelinek 	int res;
11541867Sgjelinek 	char *path;
11551867Sgjelinek 	char *parent;
11562267Sdp 	struct statvfs64 buf1, buf2;
11571867Sgjelinek 
11582267Sdp 	if (statvfs64(zonepath, &buf1) != 0)
11591867Sgjelinek 		return (B_FALSE);
11601867Sgjelinek 
11611867Sgjelinek 	if (strcmp(buf1.f_basetype, "zfs") != 0)
11621867Sgjelinek 		return (B_FALSE);
11631867Sgjelinek 
11641867Sgjelinek 	if ((path = strdup(zonepath)) == NULL)
11651867Sgjelinek 		return (B_FALSE);
11661867Sgjelinek 
11671867Sgjelinek 	parent = dirname(path);
11682267Sdp 	res = statvfs64(parent, &buf2);
11691867Sgjelinek 	free(path);
11701867Sgjelinek 
11711867Sgjelinek 	if (res != 0)
11721867Sgjelinek 		return (B_FALSE);
11731867Sgjelinek 
11741867Sgjelinek 	if (buf1.f_fsid == buf2.f_fsid)
11751867Sgjelinek 		return (B_FALSE);
11761867Sgjelinek 
11771867Sgjelinek 	return (B_TRUE);
11781867Sgjelinek }
11791867Sgjelinek 
11801867Sgjelinek /*
11811867Sgjelinek  * Implement the fast move of a ZFS file system by simply updating the
11821867Sgjelinek  * mountpoint.  Since it is file system already, we don't have the
11831867Sgjelinek  * issue of cross-file system copying.
11841867Sgjelinek  */
11851867Sgjelinek int
move_zfs(char * zonepath,char * new_zonepath)11861867Sgjelinek move_zfs(char *zonepath, char *new_zonepath)
11871867Sgjelinek {
11881867Sgjelinek 	int		ret = Z_ERR;
11891867Sgjelinek 	zfs_handle_t	*zhp;
11901867Sgjelinek 
11912082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
11921867Sgjelinek 		return (Z_ERR);
11931867Sgjelinek 
11942676Seschrock 	if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
11952676Seschrock 	    new_zonepath) == 0) {
11961867Sgjelinek 		/*
11971867Sgjelinek 		 * Clean up the old mount point.  We ignore any failure since
11981867Sgjelinek 		 * the zone is already successfully mounted on the new path.
11991867Sgjelinek 		 */
12001867Sgjelinek 		(void) rmdir(zonepath);
12011867Sgjelinek 		ret = Z_OK;
12021867Sgjelinek 	}
12031867Sgjelinek 
12041867Sgjelinek 	zfs_close(zhp);
12051867Sgjelinek 
12061867Sgjelinek 	return (ret);
12071867Sgjelinek }
12081867Sgjelinek 
12091867Sgjelinek /*
12101867Sgjelinek  * Validate that the given dataset exists on the system, and that neither it nor
12111867Sgjelinek  * its children are zvols.
12121867Sgjelinek  *
12131867Sgjelinek  * Note that we don't do anything with the 'zoned' property here.  All
12141867Sgjelinek  * management is done in zoneadmd when the zone is actually rebooted.  This
12151867Sgjelinek  * allows us to automatically set the zoned property even when a zone is
12161867Sgjelinek  * rebooted by the administrator.
12171867Sgjelinek  */
12181867Sgjelinek int
verify_datasets(zone_dochandle_t handle)12191867Sgjelinek verify_datasets(zone_dochandle_t handle)
12201867Sgjelinek {
12211867Sgjelinek 	int return_code = Z_OK;
12221867Sgjelinek 	struct zone_dstab dstab;
12231867Sgjelinek 	zfs_handle_t *zhp;
12241867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
12251867Sgjelinek 	char source[ZFS_MAXNAMELEN];
12265094Slling 	zprop_source_t srctype;
12271867Sgjelinek 
12281867Sgjelinek 	if (zonecfg_setdsent(handle) != Z_OK) {
12291867Sgjelinek 		/*
12301867Sgjelinek 		 * TRANSLATION_NOTE
12311867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
12321867Sgjelinek 		 */
12331867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs datasets: "
12341867Sgjelinek 		    "unable to enumerate datasets\n"));
12351867Sgjelinek 		return (Z_ERR);
12361867Sgjelinek 	}
12371867Sgjelinek 
12381867Sgjelinek 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
12391867Sgjelinek 
12402082Seschrock 		if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name,
12411867Sgjelinek 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
12422082Seschrock 			(void) fprintf(stderr, gettext("could not verify zfs "
12432082Seschrock 			    "dataset %s: %s\n"), dstab.zone_dataset_name,
12442082Seschrock 			    libzfs_error_description(g_zfs));
12451867Sgjelinek 			return_code = Z_ERR;
12461867Sgjelinek 			continue;
12471867Sgjelinek 		}
12481867Sgjelinek 
12491867Sgjelinek 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf,
12501867Sgjelinek 		    sizeof (propbuf), &srctype, source,
12511867Sgjelinek 		    sizeof (source), 0) == 0 &&
12525094Slling 		    (srctype == ZPROP_SRC_INHERITED)) {
12531867Sgjelinek 			(void) fprintf(stderr, gettext("could not verify zfs "
12541867Sgjelinek 			    "dataset %s: mountpoint cannot be inherited\n"),
12551867Sgjelinek 			    dstab.zone_dataset_name);
12561867Sgjelinek 			return_code = Z_ERR;
12571867Sgjelinek 			zfs_close(zhp);
12581867Sgjelinek 			continue;
12591867Sgjelinek 		}
12601867Sgjelinek 
12611867Sgjelinek 		if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
12621867Sgjelinek 			(void) fprintf(stderr, gettext("cannot verify zfs "
12631867Sgjelinek 			    "dataset %s: volumes cannot be specified as a "
12641867Sgjelinek 			    "zone dataset resource\n"),
12651867Sgjelinek 			    dstab.zone_dataset_name);
12661867Sgjelinek 			return_code = Z_ERR;
12671867Sgjelinek 		}
12681867Sgjelinek 
12691867Sgjelinek 		if (zfs_iter_children(zhp, check_zvol, NULL) != 0)
12701867Sgjelinek 			return_code = Z_ERR;
12711867Sgjelinek 
12721867Sgjelinek 		zfs_close(zhp);
12731867Sgjelinek 	}
12741867Sgjelinek 	(void) zonecfg_enddsent(handle);
12751867Sgjelinek 
12761867Sgjelinek 	return (return_code);
12771867Sgjelinek }
12781867Sgjelinek 
12791867Sgjelinek /*
12801867Sgjelinek  * Verify that the ZFS dataset exists, and its mountpoint
12811867Sgjelinek  * property is set to "legacy".
12821867Sgjelinek  */
12831867Sgjelinek int
verify_fs_zfs(struct zone_fstab * fstab)12841867Sgjelinek verify_fs_zfs(struct zone_fstab *fstab)
12851867Sgjelinek {
12861867Sgjelinek 	zfs_handle_t *zhp;
12871867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
12881867Sgjelinek 
12892082Seschrock 	if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special,
12905094Slling 	    ZFS_TYPE_DATASET)) == NULL) {
12911867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
12921867Sgjelinek 		    "could not access zfs dataset '%s'\n"),
12931867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
12941867Sgjelinek 		return (Z_ERR);
12951867Sgjelinek 	}
12961867Sgjelinek 
12971867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
12981867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify fs %s: "
12991867Sgjelinek 		    "'%s' is not a file system\n"),
13001867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
13011867Sgjelinek 		zfs_close(zhp);
13021867Sgjelinek 		return (Z_ERR);
13031867Sgjelinek 	}
13041867Sgjelinek 
13051867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
13061867Sgjelinek 	    NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
13071867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
13081867Sgjelinek 		    "zfs '%s' mountpoint is not \"legacy\"\n"),
13091867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
13101867Sgjelinek 		zfs_close(zhp);
13111867Sgjelinek 		return (Z_ERR);
13121867Sgjelinek 	}
13131867Sgjelinek 
13141867Sgjelinek 	zfs_close(zhp);
13151867Sgjelinek 	return (Z_OK);
13161867Sgjelinek }
13172082Seschrock 
131811276SJordan.Vaughan@Sun.com /*
131911276SJordan.Vaughan@Sun.com  * Destroy the specified mnttab structure that was created by mnttab_dup().
132011276SJordan.Vaughan@Sun.com  * NOTE: The structure's mnt_time field isn't freed.
132111276SJordan.Vaughan@Sun.com  */
132211276SJordan.Vaughan@Sun.com static void
mnttab_destroy(struct mnttab * tabp)132311276SJordan.Vaughan@Sun.com mnttab_destroy(struct mnttab *tabp)
132411276SJordan.Vaughan@Sun.com {
132511276SJordan.Vaughan@Sun.com 	assert(tabp != NULL);
132611276SJordan.Vaughan@Sun.com 
132711276SJordan.Vaughan@Sun.com 	free(tabp->mnt_mountp);
132811276SJordan.Vaughan@Sun.com 	free(tabp->mnt_special);
132911276SJordan.Vaughan@Sun.com 	free(tabp->mnt_fstype);
133011276SJordan.Vaughan@Sun.com 	free(tabp->mnt_mntopts);
133111276SJordan.Vaughan@Sun.com 	free(tabp);
133211276SJordan.Vaughan@Sun.com }
133311276SJordan.Vaughan@Sun.com 
133411276SJordan.Vaughan@Sun.com /*
133511276SJordan.Vaughan@Sun.com  * Duplicate the specified mnttab structure.  The mnt_mountp and mnt_time
133611276SJordan.Vaughan@Sun.com  * fields aren't duplicated.  This function returns a pointer to the new mnttab
133711276SJordan.Vaughan@Sun.com  * structure or NULL if an error occurred.  If an error occurs, then this
133811276SJordan.Vaughan@Sun.com  * function sets errno to reflect the error.  mnttab structures created by
133911276SJordan.Vaughan@Sun.com  * this function should be destroyed via mnttab_destroy().
134011276SJordan.Vaughan@Sun.com  */
134111276SJordan.Vaughan@Sun.com static struct mnttab *
mnttab_dup(const struct mnttab * srcp)134211276SJordan.Vaughan@Sun.com mnttab_dup(const struct mnttab *srcp)
134311276SJordan.Vaughan@Sun.com {
134411276SJordan.Vaughan@Sun.com 	struct mnttab *retval;
134511276SJordan.Vaughan@Sun.com 
134611276SJordan.Vaughan@Sun.com 	assert(srcp != NULL);
134711276SJordan.Vaughan@Sun.com 
134811276SJordan.Vaughan@Sun.com 	retval = (struct mnttab *)calloc(1, sizeof (*retval));
134911276SJordan.Vaughan@Sun.com 	if (retval == NULL) {
135011276SJordan.Vaughan@Sun.com 		errno = ENOMEM;
135111276SJordan.Vaughan@Sun.com 		return (NULL);
135211276SJordan.Vaughan@Sun.com 	}
135311276SJordan.Vaughan@Sun.com 	if (srcp->mnt_special != NULL) {
135411276SJordan.Vaughan@Sun.com 		retval->mnt_special = strdup(srcp->mnt_special);
135511276SJordan.Vaughan@Sun.com 		if (retval->mnt_special == NULL)
135611276SJordan.Vaughan@Sun.com 			goto err;
135711276SJordan.Vaughan@Sun.com 	}
135811276SJordan.Vaughan@Sun.com 	if (srcp->mnt_fstype != NULL) {
135911276SJordan.Vaughan@Sun.com 		retval->mnt_fstype = strdup(srcp->mnt_fstype);
136011276SJordan.Vaughan@Sun.com 		if (retval->mnt_fstype == NULL)
136111276SJordan.Vaughan@Sun.com 			goto err;
136211276SJordan.Vaughan@Sun.com 	}
136311276SJordan.Vaughan@Sun.com 	retval->mnt_mntopts = (char *)malloc(MAX_MNTOPT_STR * sizeof (char));
136411276SJordan.Vaughan@Sun.com 	if (retval->mnt_mntopts == NULL)
136511276SJordan.Vaughan@Sun.com 		goto err;
136611276SJordan.Vaughan@Sun.com 	if (srcp->mnt_mntopts != NULL) {
136711276SJordan.Vaughan@Sun.com 		if (strlcpy(retval->mnt_mntopts, srcp->mnt_mntopts,
136811276SJordan.Vaughan@Sun.com 		    MAX_MNTOPT_STR * sizeof (char)) >= MAX_MNTOPT_STR *
136911276SJordan.Vaughan@Sun.com 		    sizeof (char)) {
137011276SJordan.Vaughan@Sun.com 			mnttab_destroy(retval);
137111276SJordan.Vaughan@Sun.com 			errno = EOVERFLOW; /* similar to mount(2) behavior */
137211276SJordan.Vaughan@Sun.com 			return (NULL);
137311276SJordan.Vaughan@Sun.com 		}
137411276SJordan.Vaughan@Sun.com 	} else {
137511276SJordan.Vaughan@Sun.com 		retval->mnt_mntopts[0] = '\0';
137611276SJordan.Vaughan@Sun.com 	}
137711276SJordan.Vaughan@Sun.com 	return (retval);
137811276SJordan.Vaughan@Sun.com 
137911276SJordan.Vaughan@Sun.com err:
138011276SJordan.Vaughan@Sun.com 	mnttab_destroy(retval);
138111276SJordan.Vaughan@Sun.com 	errno = ENOMEM;
138211276SJordan.Vaughan@Sun.com 	return (NULL);
138311276SJordan.Vaughan@Sun.com }
138411276SJordan.Vaughan@Sun.com 
138511276SJordan.Vaughan@Sun.com /*
138611276SJordan.Vaughan@Sun.com  * Determine whether the specified ZFS dataset's mountpoint property is set
138711276SJordan.Vaughan@Sun.com  * to "legacy".  If the specified dataset does not have a legacy mountpoint,
138811276SJordan.Vaughan@Sun.com  * then the string pointer to which the mountpoint argument points is assigned
138911276SJordan.Vaughan@Sun.com  * a dynamically-allocated string containing the dataset's mountpoint
139011276SJordan.Vaughan@Sun.com  * property.  If the dataset's mountpoint property is "legacy" or a libzfs
139111276SJordan.Vaughan@Sun.com  * error occurs, then the string pointer to which the mountpoint argument
139211276SJordan.Vaughan@Sun.com  * points isn't modified.
139311276SJordan.Vaughan@Sun.com  *
139411276SJordan.Vaughan@Sun.com  * This function returns B_TRUE if it doesn't encounter any fatal errors.
139511276SJordan.Vaughan@Sun.com  * It returns B_FALSE if it encounters a fatal error and sets errno to the
139611276SJordan.Vaughan@Sun.com  * appropriate error code.
139711276SJordan.Vaughan@Sun.com  */
139811276SJordan.Vaughan@Sun.com static boolean_t
get_zfs_non_legacy_mountpoint(const char * dataset_name,char ** mountpoint)139911276SJordan.Vaughan@Sun.com get_zfs_non_legacy_mountpoint(const char *dataset_name, char **mountpoint)
140011276SJordan.Vaughan@Sun.com {
140111276SJordan.Vaughan@Sun.com 	zfs_handle_t *zhp;
140211276SJordan.Vaughan@Sun.com 	char propbuf[ZFS_MAXPROPLEN];
140311276SJordan.Vaughan@Sun.com 
140411276SJordan.Vaughan@Sun.com 	assert(dataset_name != NULL);
140511276SJordan.Vaughan@Sun.com 	assert(mountpoint != NULL);
140611276SJordan.Vaughan@Sun.com 
140711276SJordan.Vaughan@Sun.com 	if ((zhp = zfs_open(g_zfs, dataset_name, ZFS_TYPE_DATASET)) == NULL) {
140811276SJordan.Vaughan@Sun.com 		errno = EINVAL;
140911276SJordan.Vaughan@Sun.com 		return (B_FALSE);
141011276SJordan.Vaughan@Sun.com 	}
141111276SJordan.Vaughan@Sun.com 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
141211276SJordan.Vaughan@Sun.com 	    NULL, NULL, 0, 0) != 0) {
141311276SJordan.Vaughan@Sun.com 		zfs_close(zhp);
141411276SJordan.Vaughan@Sun.com 		errno = EINVAL;
141511276SJordan.Vaughan@Sun.com 		return (B_FALSE);
141611276SJordan.Vaughan@Sun.com 	}
141711276SJordan.Vaughan@Sun.com 	zfs_close(zhp);
141811276SJordan.Vaughan@Sun.com 	if (strcmp(propbuf, "legacy") != 0) {
141911276SJordan.Vaughan@Sun.com 		if ((*mountpoint = strdup(propbuf)) == NULL) {
142011276SJordan.Vaughan@Sun.com 			errno = ENOMEM;
142111276SJordan.Vaughan@Sun.com 			return (B_FALSE);
142211276SJordan.Vaughan@Sun.com 		}
142311276SJordan.Vaughan@Sun.com 	}
142411276SJordan.Vaughan@Sun.com 	return (B_TRUE);
142511276SJordan.Vaughan@Sun.com }
142611276SJordan.Vaughan@Sun.com 
142711276SJordan.Vaughan@Sun.com 
142811276SJordan.Vaughan@Sun.com /*
142911276SJordan.Vaughan@Sun.com  * This zonecfg_find_mounts() callback records information about mounts of
143011276SJordan.Vaughan@Sun.com  * interest in a zonepath.  It also tallies the number of zone
143111276SJordan.Vaughan@Sun.com  * root overlay mounts and the number of unexpected mounts found.
143211276SJordan.Vaughan@Sun.com  * This function outputs errors using zerror() if it finds unexpected
143311276SJordan.Vaughan@Sun.com  * mounts.  cookiep should point to an initialized zone_mounts_t structure.
143411276SJordan.Vaughan@Sun.com  *
143511276SJordan.Vaughan@Sun.com  * This function returns zero on success and a nonzero value on failure.
143611276SJordan.Vaughan@Sun.com  */
143711276SJordan.Vaughan@Sun.com static int
zone_mounts_cb(const struct mnttab * mountp,void * cookiep)143811276SJordan.Vaughan@Sun.com zone_mounts_cb(const struct mnttab *mountp, void *cookiep)
143911276SJordan.Vaughan@Sun.com {
144011276SJordan.Vaughan@Sun.com 	zone_mounts_t *mounts;
144111276SJordan.Vaughan@Sun.com 	const char *zone_mount_dir;
144211276SJordan.Vaughan@Sun.com 
144311276SJordan.Vaughan@Sun.com 	assert(mountp != NULL);
144411276SJordan.Vaughan@Sun.com 	assert(cookiep != NULL);
144511276SJordan.Vaughan@Sun.com 
144611276SJordan.Vaughan@Sun.com 	mounts = (zone_mounts_t *)cookiep;
144711276SJordan.Vaughan@Sun.com 	zone_mount_dir = mountp->mnt_mountp + mounts->zonepath_len;
144811276SJordan.Vaughan@Sun.com 	if (strcmp(zone_mount_dir, "/root") == 0) {
144911276SJordan.Vaughan@Sun.com 		/*
145011276SJordan.Vaughan@Sun.com 		 * Check for an overlay mount.  If we already detected a /root
145111276SJordan.Vaughan@Sun.com 		 * mount, then the current mount must be an overlay mount.
145211276SJordan.Vaughan@Sun.com 		 */
145311276SJordan.Vaughan@Sun.com 		if (mounts->root_mnttab != NULL) {
145411276SJordan.Vaughan@Sun.com 			mounts->num_root_overlay_mounts++;
145511276SJordan.Vaughan@Sun.com 			return (0);
145611276SJordan.Vaughan@Sun.com 		}
145711276SJordan.Vaughan@Sun.com 
145811276SJordan.Vaughan@Sun.com 		/*
145911276SJordan.Vaughan@Sun.com 		 * Store the root mount's mnttab information in the
146011276SJordan.Vaughan@Sun.com 		 * zone_mounts_t structure for future use.
146111276SJordan.Vaughan@Sun.com 		 */
146211276SJordan.Vaughan@Sun.com 		if ((mounts->root_mnttab = mnttab_dup(mountp)) == NULL) {
146311276SJordan.Vaughan@Sun.com 			zperror(cmd_to_str(CMD_MOVE), B_FALSE);
146411276SJordan.Vaughan@Sun.com 			return (-1);
146511276SJordan.Vaughan@Sun.com 		}
146611276SJordan.Vaughan@Sun.com 
146711276SJordan.Vaughan@Sun.com 		/*
146811276SJordan.Vaughan@Sun.com 		 * Determine if the filesystem is a ZFS filesystem with a
146911276SJordan.Vaughan@Sun.com 		 * non-legacy mountpoint.  If it is, then set the root
147011276SJordan.Vaughan@Sun.com 		 * filesystem's mnttab's mnt_mountp field to a non-NULL
147111276SJordan.Vaughan@Sun.com 		 * value, which will serve as a flag to indicate this special
147211276SJordan.Vaughan@Sun.com 		 * condition.
147311276SJordan.Vaughan@Sun.com 		 */
147411276SJordan.Vaughan@Sun.com 		if (strcmp(mountp->mnt_fstype, MNTTYPE_ZFS) == 0 &&
147511276SJordan.Vaughan@Sun.com 		    get_zfs_non_legacy_mountpoint(mountp->mnt_special,
147611276SJordan.Vaughan@Sun.com 		    &mounts->root_mnttab->mnt_mountp) != B_TRUE) {
147711276SJordan.Vaughan@Sun.com 			zperror(cmd_to_str(CMD_MOVE), B_FALSE);
147811276SJordan.Vaughan@Sun.com 			return (-1);
147911276SJordan.Vaughan@Sun.com 		}
148011276SJordan.Vaughan@Sun.com 	} else {
148111276SJordan.Vaughan@Sun.com 		/*
148211276SJordan.Vaughan@Sun.com 		 * An unexpected mount was found.  Notify the user.
148311276SJordan.Vaughan@Sun.com 		 */
148411276SJordan.Vaughan@Sun.com 		if (mounts->num_unexpected_mounts == 0)
148511276SJordan.Vaughan@Sun.com 			zerror(gettext("These file systems are mounted on "
148611276SJordan.Vaughan@Sun.com 			    "subdirectories of %s.\n"), mounts->zonepath);
148711276SJordan.Vaughan@Sun.com 		mounts->num_unexpected_mounts++;
148811276SJordan.Vaughan@Sun.com 		(void) zfm_print(mountp, NULL);
148911276SJordan.Vaughan@Sun.com 	}
149011276SJordan.Vaughan@Sun.com 	return (0);
149111276SJordan.Vaughan@Sun.com }
149211276SJordan.Vaughan@Sun.com 
149311276SJordan.Vaughan@Sun.com /*
149411276SJordan.Vaughan@Sun.com  * Initialize the specified zone_mounts_t structure for the given zonepath.
149511276SJordan.Vaughan@Sun.com  * If this function succeeds, it returns zero and the specified zone_mounts_t
149611276SJordan.Vaughan@Sun.com  * structure contains information about mounts in the specified zonepath.
149711276SJordan.Vaughan@Sun.com  * The function returns a nonzero value if it fails.  The zone_mounts_t
149811276SJordan.Vaughan@Sun.com  * structure doesn't need be destroyed via zone_mounts_destroy() if this
149911276SJordan.Vaughan@Sun.com  * function fails.
150011276SJordan.Vaughan@Sun.com  */
150111276SJordan.Vaughan@Sun.com int
zone_mounts_init(zone_mounts_t * mounts,const char * zonepath)150211276SJordan.Vaughan@Sun.com zone_mounts_init(zone_mounts_t *mounts, const char *zonepath)
150311276SJordan.Vaughan@Sun.com {
150411276SJordan.Vaughan@Sun.com 	assert(mounts != NULL);
150511276SJordan.Vaughan@Sun.com 	assert(zonepath != NULL);
150611276SJordan.Vaughan@Sun.com 
150711276SJordan.Vaughan@Sun.com 	bzero(mounts, sizeof (*mounts));
150811276SJordan.Vaughan@Sun.com 	if ((mounts->zonepath = strdup(zonepath)) == NULL) {
150911276SJordan.Vaughan@Sun.com 		zerror(gettext("the process ran out of memory while checking "
151011276SJordan.Vaughan@Sun.com 		    "for mounts in zonepath %s."), zonepath);
151111276SJordan.Vaughan@Sun.com 		return (-1);
151211276SJordan.Vaughan@Sun.com 	}
151311276SJordan.Vaughan@Sun.com 	mounts->zonepath_len = strlen(zonepath);
151411276SJordan.Vaughan@Sun.com 	if (zonecfg_find_mounts((char *)zonepath, zone_mounts_cb, mounts) ==
151511276SJordan.Vaughan@Sun.com 	    -1) {
151611276SJordan.Vaughan@Sun.com 		zerror(gettext("an error occurred while checking for mounts "
151711276SJordan.Vaughan@Sun.com 		    "in zonepath %s."), zonepath);
151811276SJordan.Vaughan@Sun.com 		zone_mounts_destroy(mounts);
151911276SJordan.Vaughan@Sun.com 		return (-1);
152011276SJordan.Vaughan@Sun.com 	}
152111276SJordan.Vaughan@Sun.com 	return (0);
152211276SJordan.Vaughan@Sun.com }
152311276SJordan.Vaughan@Sun.com 
152411276SJordan.Vaughan@Sun.com /*
152511276SJordan.Vaughan@Sun.com  * Destroy the memory used by the specified zone_mounts_t structure's fields.
152611276SJordan.Vaughan@Sun.com  * This function doesn't free the memory occupied by the structure itself
152711276SJordan.Vaughan@Sun.com  * (i.e., it doesn't free the parameter).
152811276SJordan.Vaughan@Sun.com  */
152911276SJordan.Vaughan@Sun.com void
zone_mounts_destroy(zone_mounts_t * mounts)153011276SJordan.Vaughan@Sun.com zone_mounts_destroy(zone_mounts_t *mounts)
153111276SJordan.Vaughan@Sun.com {
153211276SJordan.Vaughan@Sun.com 	assert(mounts != NULL);
153311276SJordan.Vaughan@Sun.com 
153411276SJordan.Vaughan@Sun.com 	free(mounts->zonepath);
153511276SJordan.Vaughan@Sun.com 	if (mounts->root_mnttab != NULL)
153611276SJordan.Vaughan@Sun.com 		mnttab_destroy(mounts->root_mnttab);
153711276SJordan.Vaughan@Sun.com }
153811276SJordan.Vaughan@Sun.com 
153911276SJordan.Vaughan@Sun.com /*
154011276SJordan.Vaughan@Sun.com  * Mount a moving zone's root filesystem (if it had a root filesystem mount
154111276SJordan.Vaughan@Sun.com  * prior to the move) using the specified zonepath.  mounts should refer to
154211276SJordan.Vaughan@Sun.com  * the zone_mounts_t structure describing the zone's mount information.
154311276SJordan.Vaughan@Sun.com  *
154411276SJordan.Vaughan@Sun.com  * This function returns zero if the mount succeeds and a nonzero value
154511276SJordan.Vaughan@Sun.com  * if it doesn't.
154611276SJordan.Vaughan@Sun.com  */
154711276SJordan.Vaughan@Sun.com int
zone_mount_rootfs(zone_mounts_t * mounts,const char * zonepath)154811276SJordan.Vaughan@Sun.com zone_mount_rootfs(zone_mounts_t *mounts, const char *zonepath)
154911276SJordan.Vaughan@Sun.com {
155011276SJordan.Vaughan@Sun.com 	char zoneroot[MAXPATHLEN];
155111276SJordan.Vaughan@Sun.com 	struct mnttab *mtab;
155211276SJordan.Vaughan@Sun.com 	int flags;
155311276SJordan.Vaughan@Sun.com 
155411276SJordan.Vaughan@Sun.com 	assert(mounts != NULL);
155511276SJordan.Vaughan@Sun.com 	assert(zonepath != NULL);
155611276SJordan.Vaughan@Sun.com 
155711276SJordan.Vaughan@Sun.com 	/*
155811276SJordan.Vaughan@Sun.com 	 * If there isn't a root filesystem, then don't do anything.
155911276SJordan.Vaughan@Sun.com 	 */
156011276SJordan.Vaughan@Sun.com 	mtab = mounts->root_mnttab;
156111276SJordan.Vaughan@Sun.com 	if (mtab == NULL)
156211276SJordan.Vaughan@Sun.com 		return (0);
156311276SJordan.Vaughan@Sun.com 
156411276SJordan.Vaughan@Sun.com 	/*
156511276SJordan.Vaughan@Sun.com 	 * Determine the root filesystem's new mountpoint.
156611276SJordan.Vaughan@Sun.com 	 */
156711276SJordan.Vaughan@Sun.com 	if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >=
156811276SJordan.Vaughan@Sun.com 	    sizeof (zoneroot)) {
156911276SJordan.Vaughan@Sun.com 		zerror(gettext("Zonepath %s is too long.\n"), zonepath);
157011276SJordan.Vaughan@Sun.com 		return (-1);
157111276SJordan.Vaughan@Sun.com 	}
157211276SJordan.Vaughan@Sun.com 
157311276SJordan.Vaughan@Sun.com 	/*
157411276SJordan.Vaughan@Sun.com 	 * If the root filesystem is a non-legacy ZFS filesystem (i.e., if it's
157511276SJordan.Vaughan@Sun.com 	 * mnt_mountp field is non-NULL), then make the filesystem's new
157611276SJordan.Vaughan@Sun.com 	 * mount point its mountpoint property and mount the filesystem.
157711276SJordan.Vaughan@Sun.com 	 */
157811276SJordan.Vaughan@Sun.com 	if (mtab->mnt_mountp != NULL) {
157911276SJordan.Vaughan@Sun.com 		zfs_handle_t *zhp;
158011276SJordan.Vaughan@Sun.com 
158111276SJordan.Vaughan@Sun.com 		if ((zhp = zfs_open(g_zfs, mtab->mnt_special,
158211276SJordan.Vaughan@Sun.com 		    ZFS_TYPE_DATASET)) == NULL) {
158311276SJordan.Vaughan@Sun.com 			zerror(gettext("could not get ZFS handle for the zone's"
158411276SJordan.Vaughan@Sun.com 			    " root filesystem"));
158511276SJordan.Vaughan@Sun.com 			return (-1);
158611276SJordan.Vaughan@Sun.com 		}
158711276SJordan.Vaughan@Sun.com 		if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
158811276SJordan.Vaughan@Sun.com 		    zoneroot) != 0) {
158911276SJordan.Vaughan@Sun.com 			zerror(gettext("could not modify zone's root "
159011276SJordan.Vaughan@Sun.com 			    "filesystem's mountpoint property"));
159111276SJordan.Vaughan@Sun.com 			zfs_close(zhp);
159211276SJordan.Vaughan@Sun.com 			return (-1);
159311276SJordan.Vaughan@Sun.com 		}
159411276SJordan.Vaughan@Sun.com 		if (zfs_mount(zhp, mtab->mnt_mntopts, 0) != 0) {
159511276SJordan.Vaughan@Sun.com 			zerror(gettext("unable to mount zone root %s: %s"),
159611276SJordan.Vaughan@Sun.com 			    zoneroot, libzfs_error_description(g_zfs));
159711276SJordan.Vaughan@Sun.com 			if (zfs_prop_set(zhp,
159811276SJordan.Vaughan@Sun.com 			    zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
159911276SJordan.Vaughan@Sun.com 			    mtab->mnt_mountp) != 0)
160011276SJordan.Vaughan@Sun.com 				zerror(gettext("unable to restore zone's root "
160111276SJordan.Vaughan@Sun.com 				    "filesystem's mountpoint property"));
160211276SJordan.Vaughan@Sun.com 			zfs_close(zhp);
160311276SJordan.Vaughan@Sun.com 			return (-1);
160411276SJordan.Vaughan@Sun.com 		}
160511276SJordan.Vaughan@Sun.com 		zfs_close(zhp);
160611276SJordan.Vaughan@Sun.com 		return (0);
160711276SJordan.Vaughan@Sun.com 	}
160811276SJordan.Vaughan@Sun.com 
160911276SJordan.Vaughan@Sun.com 	/*
161011276SJordan.Vaughan@Sun.com 	 * The root filesystem is either a legacy-mounted ZFS filesystem or
161111276SJordan.Vaughan@Sun.com 	 * a non-ZFS filesystem.  Use mount(2) to mount the root filesystem.
161211276SJordan.Vaughan@Sun.com 	 */
161311276SJordan.Vaughan@Sun.com 	if (mtab->mnt_mntopts != NULL)
161411276SJordan.Vaughan@Sun.com 		flags = MS_OPTIONSTR;
161511276SJordan.Vaughan@Sun.com 	else
161611276SJordan.Vaughan@Sun.com 		flags = 0;
161711276SJordan.Vaughan@Sun.com 	if (mount(mtab->mnt_special, zoneroot, flags, mtab->mnt_fstype, NULL, 0,
161811276SJordan.Vaughan@Sun.com 	    mtab->mnt_mntopts, MAX_MNTOPT_STR * sizeof (char)) != 0) {
161911276SJordan.Vaughan@Sun.com 		flags = errno;
162011276SJordan.Vaughan@Sun.com 		zerror(gettext("unable to mount zone root %s: %s"), zoneroot,
162111276SJordan.Vaughan@Sun.com 		    strerror(flags));
162211276SJordan.Vaughan@Sun.com 		return (-1);
162311276SJordan.Vaughan@Sun.com 	}
162411276SJordan.Vaughan@Sun.com 	return (0);
162511276SJordan.Vaughan@Sun.com }
162611276SJordan.Vaughan@Sun.com 
162711276SJordan.Vaughan@Sun.com /*
162811276SJordan.Vaughan@Sun.com  * Unmount a moving zone's root filesystem (if such a mount exists) using the
162911276SJordan.Vaughan@Sun.com  * specified zonepath.  mounts should refer to the zone_mounts_t structure
163011276SJordan.Vaughan@Sun.com  * describing the zone's mount information.  If force is B_TRUE, then if the
163111276SJordan.Vaughan@Sun.com  * unmount fails, then the function will try to forcibly unmount the zone's root
163211276SJordan.Vaughan@Sun.com  * filesystem.
163311276SJordan.Vaughan@Sun.com  *
163411276SJordan.Vaughan@Sun.com  * This function returns zero if the unmount (forced or otherwise) succeeds;
163511276SJordan.Vaughan@Sun.com  * otherwise, it returns a nonzero value.
163611276SJordan.Vaughan@Sun.com  */
163711276SJordan.Vaughan@Sun.com int
zone_unmount_rootfs(zone_mounts_t * mounts,const char * zonepath,boolean_t force)163811276SJordan.Vaughan@Sun.com zone_unmount_rootfs(zone_mounts_t *mounts, const char *zonepath,
163911276SJordan.Vaughan@Sun.com     boolean_t force)
164011276SJordan.Vaughan@Sun.com {
164111276SJordan.Vaughan@Sun.com 	char zoneroot[MAXPATHLEN];
164211276SJordan.Vaughan@Sun.com 	struct mnttab *mtab;
164311276SJordan.Vaughan@Sun.com 	int err;
164411276SJordan.Vaughan@Sun.com 
164511276SJordan.Vaughan@Sun.com 	assert(mounts != NULL);
164611276SJordan.Vaughan@Sun.com 	assert(zonepath != NULL);
164711276SJordan.Vaughan@Sun.com 
164811276SJordan.Vaughan@Sun.com 	/*
164911276SJordan.Vaughan@Sun.com 	 * If there isn't a root filesystem, then don't do anything.
165011276SJordan.Vaughan@Sun.com 	 */
165111276SJordan.Vaughan@Sun.com 	mtab = mounts->root_mnttab;
165211276SJordan.Vaughan@Sun.com 	if (mtab == NULL)
165311276SJordan.Vaughan@Sun.com 		return (0);
165411276SJordan.Vaughan@Sun.com 
165511276SJordan.Vaughan@Sun.com 	/*
165611276SJordan.Vaughan@Sun.com 	 * Determine the root filesystem's mountpoint.
165711276SJordan.Vaughan@Sun.com 	 */
165811276SJordan.Vaughan@Sun.com 	if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >=
165911276SJordan.Vaughan@Sun.com 	    sizeof (zoneroot)) {
166011276SJordan.Vaughan@Sun.com 		zerror(gettext("Zonepath %s is too long.\n"), zonepath);
166111276SJordan.Vaughan@Sun.com 		return (-1);
166211276SJordan.Vaughan@Sun.com 	}
166311276SJordan.Vaughan@Sun.com 
166411276SJordan.Vaughan@Sun.com 	/*
166511276SJordan.Vaughan@Sun.com 	 * If the root filesystem is a non-legacy ZFS fileystem, then unmount
166611276SJordan.Vaughan@Sun.com 	 * the filesystem via libzfs.
166711276SJordan.Vaughan@Sun.com 	 */
166811276SJordan.Vaughan@Sun.com 	if (mtab->mnt_mountp != NULL) {
166911276SJordan.Vaughan@Sun.com 		zfs_handle_t *zhp;
167011276SJordan.Vaughan@Sun.com 
167111276SJordan.Vaughan@Sun.com 		if ((zhp = zfs_open(g_zfs, mtab->mnt_special,
167211276SJordan.Vaughan@Sun.com 		    ZFS_TYPE_DATASET)) == NULL) {
167311276SJordan.Vaughan@Sun.com 			zerror(gettext("could not get ZFS handle for the zone's"
167411276SJordan.Vaughan@Sun.com 			    " root filesystem"));
167511276SJordan.Vaughan@Sun.com 			return (-1);
167611276SJordan.Vaughan@Sun.com 		}
167711276SJordan.Vaughan@Sun.com 		if (zfs_unmount(zhp, zoneroot, 0) != 0) {
167811276SJordan.Vaughan@Sun.com 			if (force && zfs_unmount(zhp, zoneroot, MS_FORCE) ==
167911276SJordan.Vaughan@Sun.com 			    0) {
168011276SJordan.Vaughan@Sun.com 				zfs_close(zhp);
168111276SJordan.Vaughan@Sun.com 				return (0);
168211276SJordan.Vaughan@Sun.com 			}
168311276SJordan.Vaughan@Sun.com 			zerror(gettext("unable to unmount zone root %s: %s"),
168411276SJordan.Vaughan@Sun.com 			    zoneroot, libzfs_error_description(g_zfs));
168511276SJordan.Vaughan@Sun.com 			zfs_close(zhp);
168611276SJordan.Vaughan@Sun.com 			return (-1);
168711276SJordan.Vaughan@Sun.com 		}
168811276SJordan.Vaughan@Sun.com 		zfs_close(zhp);
168911276SJordan.Vaughan@Sun.com 		return (0);
169011276SJordan.Vaughan@Sun.com 	}
169111276SJordan.Vaughan@Sun.com 
169211276SJordan.Vaughan@Sun.com 	/*
169311276SJordan.Vaughan@Sun.com 	 * Use umount(2) to unmount the root filesystem.  If this fails, then
169411276SJordan.Vaughan@Sun.com 	 * forcibly unmount it if the force flag is set.
169511276SJordan.Vaughan@Sun.com 	 */
169611276SJordan.Vaughan@Sun.com 	if (umount(zoneroot) != 0) {
169711276SJordan.Vaughan@Sun.com 		if (force && umount2(zoneroot, MS_FORCE) == 0)
169811276SJordan.Vaughan@Sun.com 			return (0);
169911276SJordan.Vaughan@Sun.com 		err = errno;
170011276SJordan.Vaughan@Sun.com 		zerror(gettext("unable to unmount zone root %s: %s"), zoneroot,
170111276SJordan.Vaughan@Sun.com 		    strerror(err));
170211276SJordan.Vaughan@Sun.com 		return (-1);
170311276SJordan.Vaughan@Sun.com 	}
170411276SJordan.Vaughan@Sun.com 	return (0);
170511276SJordan.Vaughan@Sun.com }
170611276SJordan.Vaughan@Sun.com 
17072082Seschrock int
init_zfs(void)17082082Seschrock init_zfs(void)
17092082Seschrock {
17102082Seschrock 	if ((g_zfs = libzfs_init()) == NULL) {
17112082Seschrock 		(void) fprintf(stderr, gettext("failed to initialize ZFS "
17122082Seschrock 		    "library\n"));
17132082Seschrock 		return (Z_ERR);
17142082Seschrock 	}
17152082Seschrock 
17162082Seschrock 	return (Z_OK);
17172082Seschrock }
1718