xref: /onnv-gate/usr/src/cmd/zoneadm/zfs.c (revision 9023:75687de6acb4)
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*9023Sgerald.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>
461867Sgjelinek 
471867Sgjelinek #include "zoneadm.h"
481867Sgjelinek 
492082Seschrock libzfs_handle_t *g_zfs;
501867Sgjelinek 
511867Sgjelinek typedef struct zfs_mount_data {
521867Sgjelinek 	char		*match_name;
531867Sgjelinek 	zfs_handle_t	*match_handle;
541867Sgjelinek } zfs_mount_data_t;
551867Sgjelinek 
561867Sgjelinek typedef struct zfs_snapshot_data {
571867Sgjelinek 	char	*match_name;
581867Sgjelinek 	int	len;
591867Sgjelinek 	int	max;
601867Sgjelinek } zfs_snapshot_data_t;
611867Sgjelinek 
621867Sgjelinek /*
631867Sgjelinek  * A ZFS file system iterator call-back function which is used to validate
641867Sgjelinek  * datasets imported into the zone.
651867Sgjelinek  */
661867Sgjelinek /* ARGSUSED */
671867Sgjelinek static int
681867Sgjelinek check_zvol(zfs_handle_t *zhp, void *unused)
691867Sgjelinek {
701867Sgjelinek 	int ret;
711867Sgjelinek 
721867Sgjelinek 	if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
731867Sgjelinek 		/*
741867Sgjelinek 		 * TRANSLATION_NOTE
751867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
761867Sgjelinek 		 */
771867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify zfs dataset %s: "
781867Sgjelinek 		    "volumes cannot be specified as a zone dataset resource\n"),
791867Sgjelinek 		    zfs_get_name(zhp));
801867Sgjelinek 		ret = -1;
811867Sgjelinek 	} else {
821867Sgjelinek 		ret = zfs_iter_children(zhp, check_zvol, NULL);
831867Sgjelinek 	}
841867Sgjelinek 
851867Sgjelinek 	zfs_close(zhp);
861867Sgjelinek 
871867Sgjelinek 	return (ret);
881867Sgjelinek }
891867Sgjelinek 
901867Sgjelinek /*
911867Sgjelinek  * A ZFS file system iterator call-back function which returns the
921867Sgjelinek  * zfs_handle_t for a ZFS file system on the specified mount point.
931867Sgjelinek  */
941867Sgjelinek static int
951867Sgjelinek match_mountpoint(zfs_handle_t *zhp, void *data)
961867Sgjelinek {
971867Sgjelinek 	int			res;
981867Sgjelinek 	zfs_mount_data_t	*cbp;
991867Sgjelinek 	char			mp[ZFS_MAXPROPLEN];
1001867Sgjelinek 
1011867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1021867Sgjelinek 		zfs_close(zhp);
1031867Sgjelinek 		return (0);
1041867Sgjelinek 	}
1051867Sgjelinek 
1067093Sgjelinek 	/* First check if the dataset is mounted. */
1077093Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
1087093Sgjelinek 	    0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
1097093Sgjelinek 		zfs_close(zhp);
1107093Sgjelinek 		return (0);
1117093Sgjelinek 	}
1127093Sgjelinek 
1137093Sgjelinek 	/* Now check mount point. */
1141867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
1157093Sgjelinek 	    0, B_FALSE) != 0) {
1167093Sgjelinek 		zfs_close(zhp);
1177093Sgjelinek 		return (0);
1187093Sgjelinek 	}
1197093Sgjelinek 
1207093Sgjelinek 	cbp = (zfs_mount_data_t *)data;
1217093Sgjelinek 
1227093Sgjelinek 	if (strcmp(mp, "legacy") == 0) {
1237093Sgjelinek 		/* If legacy, must look in mnttab for mountpoint. */
1247093Sgjelinek 		FILE		*fp;
1257093Sgjelinek 		struct mnttab	entry;
1267093Sgjelinek 		const char	*nm;
1277093Sgjelinek 
1287093Sgjelinek 		nm = zfs_get_name(zhp);
1297093Sgjelinek 		if ((fp = fopen(MNTTAB, "r")) == NULL) {
1307093Sgjelinek 			zfs_close(zhp);
1317093Sgjelinek 			return (0);
1327093Sgjelinek 		}
1337093Sgjelinek 
1347093Sgjelinek 		while (getmntent(fp, &entry) == 0) {
1357093Sgjelinek 			if (strcmp(nm, entry.mnt_special) == 0) {
1367093Sgjelinek 				if (strcmp(entry.mnt_mountp, cbp->match_name)
1377093Sgjelinek 				    == 0) {
1387093Sgjelinek 					(void) fclose(fp);
1397093Sgjelinek 					cbp->match_handle = zhp;
1407093Sgjelinek 					return (1);
1417093Sgjelinek 				}
1427093Sgjelinek 				break;
1437093Sgjelinek 			}
1447093Sgjelinek 		}
1457093Sgjelinek 		(void) fclose(fp);
1467093Sgjelinek 
1477093Sgjelinek 	} else if (strcmp(mp, cbp->match_name) == 0) {
1481867Sgjelinek 		cbp->match_handle = zhp;
1491867Sgjelinek 		return (1);
1501867Sgjelinek 	}
1511867Sgjelinek 
1527093Sgjelinek 	/* Iterate over any nested datasets. */
1531867Sgjelinek 	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1541867Sgjelinek 	zfs_close(zhp);
1551867Sgjelinek 	return (res);
1561867Sgjelinek }
1571867Sgjelinek 
1581867Sgjelinek /*
1591867Sgjelinek  * Get ZFS handle for the specified mount point.
1601867Sgjelinek  */
1611867Sgjelinek static zfs_handle_t *
1621867Sgjelinek mount2zhandle(char *mountpoint)
1631867Sgjelinek {
1641867Sgjelinek 	zfs_mount_data_t	cb;
1651867Sgjelinek 
1661867Sgjelinek 	cb.match_name = mountpoint;
1671867Sgjelinek 	cb.match_handle = NULL;
1682082Seschrock 	(void) zfs_iter_root(g_zfs, match_mountpoint, &cb);
1691867Sgjelinek 	return (cb.match_handle);
1701867Sgjelinek }
1711867Sgjelinek 
1721867Sgjelinek /*
1731867Sgjelinek  * Check if there is already a file system (zfs or any other type) mounted on
1741867Sgjelinek  * path.
1751867Sgjelinek  */
1761867Sgjelinek static boolean_t
1771867Sgjelinek is_mountpnt(char *path)
1781867Sgjelinek {
1791867Sgjelinek 	FILE		*fp;
1801867Sgjelinek 	struct mnttab	entry;
1811867Sgjelinek 
1827093Sgjelinek 	if ((fp = fopen(MNTTAB, "r")) == NULL)
1831867Sgjelinek 		return (B_FALSE);
1841867Sgjelinek 
1851867Sgjelinek 	while (getmntent(fp, &entry) == 0) {
1861867Sgjelinek 		if (strcmp(path, entry.mnt_mountp) == 0) {
1871867Sgjelinek 			(void) fclose(fp);
1881867Sgjelinek 			return (B_TRUE);
1891867Sgjelinek 		}
1901867Sgjelinek 	}
1911867Sgjelinek 
1921867Sgjelinek 	(void) fclose(fp);
1931867Sgjelinek 	return (B_FALSE);
1941867Sgjelinek }
1951867Sgjelinek 
1961867Sgjelinek /*
1977089Sgjelinek  * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
1981867Sgjelinek  */
1991867Sgjelinek static int
2007089Sgjelinek pre_snapshot(char *presnapbuf)
2011867Sgjelinek {
2027089Sgjelinek 	int status;
2031867Sgjelinek 
2047089Sgjelinek 	/* No brand-specific handler */
2057089Sgjelinek 	if (presnapbuf[0] == '\0')
2067089Sgjelinek 		return (Z_OK);
2071867Sgjelinek 
2087089Sgjelinek 	/* Run the hook */
2097089Sgjelinek 	status = do_subproc_interactive(presnapbuf);
2107089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific presnapshot"),
2117089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2121867Sgjelinek 		return (Z_ERR);
2131867Sgjelinek 
2141867Sgjelinek 	return (Z_OK);
2151867Sgjelinek }
2161867Sgjelinek 
2171867Sgjelinek /*
2187089Sgjelinek  * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
2191867Sgjelinek  */
2201867Sgjelinek static int
2217089Sgjelinek post_snapshot(char *postsnapbuf)
2221867Sgjelinek {
2237089Sgjelinek 	int status;
2241867Sgjelinek 
2257089Sgjelinek 	/* No brand-specific handler */
2267089Sgjelinek 	if (postsnapbuf[0] == '\0')
2277089Sgjelinek 		return (Z_OK);
2287089Sgjelinek 
2297089Sgjelinek 	/* Run the hook */
2307089Sgjelinek 	status = do_subproc_interactive(postsnapbuf);
2317089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific postsnapshot"),
2327089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2331867Sgjelinek 		return (Z_ERR);
2341867Sgjelinek 
2351867Sgjelinek 	return (Z_OK);
2361867Sgjelinek }
2371867Sgjelinek 
2381867Sgjelinek /*
2391867Sgjelinek  * This is a ZFS snapshot iterator call-back function which returns the
2401867Sgjelinek  * highest number of SUNWzone snapshots that have been taken.
2411867Sgjelinek  */
2421867Sgjelinek static int
2431867Sgjelinek get_snap_max(zfs_handle_t *zhp, void *data)
2441867Sgjelinek {
2451867Sgjelinek 	int			res;
2461867Sgjelinek 	zfs_snapshot_data_t	*cbp;
2471867Sgjelinek 
2481867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2491867Sgjelinek 		zfs_close(zhp);
2501867Sgjelinek 		return (0);
2511867Sgjelinek 	}
2521867Sgjelinek 
2531867Sgjelinek 	cbp = (zfs_snapshot_data_t *)data;
2541867Sgjelinek 
2551867Sgjelinek 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2561867Sgjelinek 		char	*nump;
2571867Sgjelinek 		int	num;
2581867Sgjelinek 
2591867Sgjelinek 		nump = (char *)(zfs_get_name(zhp) + cbp->len);
2601867Sgjelinek 		num = atoi(nump);
2611867Sgjelinek 		if (num > cbp->max)
2621867Sgjelinek 			cbp->max = num;
2631867Sgjelinek 	}
2641867Sgjelinek 
2651867Sgjelinek 	res = zfs_iter_snapshots(zhp, get_snap_max, data);
2661867Sgjelinek 	zfs_close(zhp);
2671867Sgjelinek 	return (res);
2681867Sgjelinek }
2691867Sgjelinek 
2701867Sgjelinek /*
2711867Sgjelinek  * Take a ZFS snapshot to be used for cloning the zone.
2721867Sgjelinek  */
2731867Sgjelinek static int
2747089Sgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
2757089Sgjelinek     char *presnapbuf, char *postsnapbuf)
2761867Sgjelinek {
2771867Sgjelinek 	int			res;
2781867Sgjelinek 	char			template[ZFS_MAXNAMELEN];
2791867Sgjelinek 	zfs_snapshot_data_t	cb;
2801867Sgjelinek 
2811867Sgjelinek 	/*
2821867Sgjelinek 	 * First we need to figure out the next available name for the
2831867Sgjelinek 	 * zone snapshot.  Look through the list of zones snapshots for
2841867Sgjelinek 	 * this file system to determine the maximum snapshot name.
2851867Sgjelinek 	 */
2861867Sgjelinek 	if (snprintf(template, sizeof (template), "%s@SUNWzone",
2871867Sgjelinek 	    zfs_get_name(zhp)) >=  sizeof (template))
2881867Sgjelinek 		return (Z_ERR);
2891867Sgjelinek 
2901867Sgjelinek 	cb.match_name = template;
2911867Sgjelinek 	cb.len = strlen(template);
2921867Sgjelinek 	cb.max = 0;
2931867Sgjelinek 
2941867Sgjelinek 	if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0)
2951867Sgjelinek 		return (Z_ERR);
2961867Sgjelinek 
2971867Sgjelinek 	cb.max++;
2981867Sgjelinek 
2991867Sgjelinek 	if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
3001867Sgjelinek 	    zfs_get_name(zhp), cb.max) >= snap_size)
3011867Sgjelinek 		return (Z_ERR);
3021867Sgjelinek 
3037089Sgjelinek 	if (pre_snapshot(presnapbuf) != Z_OK)
3041867Sgjelinek 		return (Z_ERR);
3057265Sahrens 	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
3067089Sgjelinek 	if (post_snapshot(postsnapbuf) != Z_OK)
3071867Sgjelinek 		return (Z_ERR);
3081867Sgjelinek 
3091867Sgjelinek 	if (res != 0)
3101867Sgjelinek 		return (Z_ERR);
3111867Sgjelinek 	return (Z_OK);
3121867Sgjelinek }
3131867Sgjelinek 
3141867Sgjelinek /*
3151867Sgjelinek  * We are using an explicit snapshot from some earlier point in time so
3167089Sgjelinek  * we need to validate it.  Run the brand specific hook.
3171867Sgjelinek  */
3181867Sgjelinek static int
3197089Sgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
3201867Sgjelinek {
3217089Sgjelinek 	int status;
3227089Sgjelinek 	char cmdbuf[MAXPATHLEN];
3231867Sgjelinek 
3247089Sgjelinek 	/* No brand-specific handler */
3257089Sgjelinek 	if (validsnapbuf[0] == '\0')
3267089Sgjelinek 		return (Z_OK);
3271867Sgjelinek 
3287089Sgjelinek 	/* pass args - snapshot_name & snap_path */
3297089Sgjelinek 	if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
3307089Sgjelinek 	    snapshot_name, snap_path) >= sizeof (cmdbuf)) {
3317089Sgjelinek 		zerror("Command line too long");
3321867Sgjelinek 		return (Z_ERR);
3331867Sgjelinek 	}
3341867Sgjelinek 
3357089Sgjelinek 	/* Run the hook */
3367089Sgjelinek 	status = do_subproc_interactive(cmdbuf);
3377089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
3387089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
3397089Sgjelinek 		return (Z_ERR);
3401867Sgjelinek 
3417089Sgjelinek 	return (Z_OK);
3421867Sgjelinek }
3431867Sgjelinek 
3441867Sgjelinek /*
3451867Sgjelinek  * Remove the sw inventory file from inside this zonepath that we picked up out
3461867Sgjelinek  * of the snapshot.
3471867Sgjelinek  */
3481867Sgjelinek static int
3491867Sgjelinek clean_out_clone()
3501867Sgjelinek {
3511867Sgjelinek 	int err;
3521867Sgjelinek 	zone_dochandle_t handle;
3531867Sgjelinek 
3541867Sgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
3551867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3561867Sgjelinek 		return (Z_ERR);
3571867Sgjelinek 	}
3581867Sgjelinek 
3591867Sgjelinek 	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3601867Sgjelinek 		errno = err;
3611867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3621867Sgjelinek 		zonecfg_fini_handle(handle);
3631867Sgjelinek 		return (Z_ERR);
3641867Sgjelinek 	}
3651867Sgjelinek 
3661867Sgjelinek 	zonecfg_rm_detached(handle, B_FALSE);
3671867Sgjelinek 	zonecfg_fini_handle(handle);
3681867Sgjelinek 
3691867Sgjelinek 	return (Z_OK);
3701867Sgjelinek }
3711867Sgjelinek 
3721867Sgjelinek /*
3731867Sgjelinek  * Make a ZFS clone on zonepath from snapshot_name.
3741867Sgjelinek  */
3751867Sgjelinek static int
3761867Sgjelinek clone_snap(char *snapshot_name, char *zonepath)
3771867Sgjelinek {
3781867Sgjelinek 	int		res = Z_OK;
3791867Sgjelinek 	int		err;
3801867Sgjelinek 	zfs_handle_t	*zhp;
3811867Sgjelinek 	zfs_handle_t	*clone;
3822676Seschrock 	nvlist_t	*props = NULL;
3831867Sgjelinek 
3842082Seschrock 	if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
3851867Sgjelinek 		return (Z_NO_ENTRY);
3861867Sgjelinek 
3871867Sgjelinek 	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
3881867Sgjelinek 
3892676Seschrock 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
3902744Snn35248 	    nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
3912744Snn35248 	    "off") != 0) {
3922744Snn35248 		if (props != NULL)
3932744Snn35248 			nvlist_free(props);
3942676Seschrock 		(void) fprintf(stderr, gettext("could not create ZFS clone "
3952676Seschrock 		    "%s: out of memory\n"), zonepath);
3962676Seschrock 		return (Z_ERR);
3972676Seschrock 	}
3982676Seschrock 
3992676Seschrock 	err = zfs_clone(zhp, zonepath, props);
4001867Sgjelinek 	zfs_close(zhp);
4012676Seschrock 
4022676Seschrock 	nvlist_free(props);
4032676Seschrock 
4041867Sgjelinek 	if (err != 0)
4051867Sgjelinek 		return (Z_ERR);
4061867Sgjelinek 
4071867Sgjelinek 	/* create the mountpoint if necessary */
4085094Slling 	if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL)
4091867Sgjelinek 		return (Z_ERR);
4101867Sgjelinek 
4111867Sgjelinek 	/*
4121867Sgjelinek 	 * The clone has been created so we need to print a diagnostic
4131867Sgjelinek 	 * message if one of the following steps fails for some reason.
4141867Sgjelinek 	 */
4151867Sgjelinek 	if (zfs_mount(clone, NULL, 0) != 0) {
4161867Sgjelinek 		(void) fprintf(stderr, gettext("could not mount ZFS clone "
4171867Sgjelinek 		    "%s\n"), zfs_get_name(clone));
4181867Sgjelinek 		res = Z_ERR;
4191867Sgjelinek 
4202676Seschrock 	} else if (clean_out_clone() != Z_OK) {
4212676Seschrock 		(void) fprintf(stderr, gettext("could not remove the "
4222676Seschrock 		    "software inventory from ZFS clone %s\n"),
4232676Seschrock 		    zfs_get_name(clone));
4242676Seschrock 		res = Z_ERR;
4251867Sgjelinek 	}
4261867Sgjelinek 
4271867Sgjelinek 	zfs_close(clone);
4281867Sgjelinek 	return (res);
4291867Sgjelinek }
4301867Sgjelinek 
4311867Sgjelinek /*
4321867Sgjelinek  * This function takes a zonepath and attempts to determine what the ZFS
4331867Sgjelinek  * file system name (not mountpoint) should be for that path.  We do not
4341867Sgjelinek  * assume that zonepath is an existing directory or ZFS fs since we use
4351867Sgjelinek  * this function as part of the process of creating a new ZFS fs or clone.
4361867Sgjelinek  *
4371867Sgjelinek  * The way this works is that we look at the parent directory of the zonepath
4381867Sgjelinek  * to see if it is a ZFS fs.  If it is, we get the name of that ZFS fs and
4391867Sgjelinek  * append the last component of the zonepath to generate the ZFS name for the
4401867Sgjelinek  * zonepath.  This matches the algorithm that ZFS uses for automatically
4411867Sgjelinek  * mounting a new fs after it is created.
4421867Sgjelinek  *
4431867Sgjelinek  * Although a ZFS fs can be mounted anywhere, we don't worry about handling
4441867Sgjelinek  * all of the complexity that a user could possibly configure with arbitrary
4451867Sgjelinek  * mounts since there is no way to generate a ZFS name from a random path in
4461867Sgjelinek  * the file system.  We only try to handle the automatic mounts that ZFS does
4471867Sgjelinek  * for each file system.  ZFS restricts this so that a new fs must be created
4481867Sgjelinek  * in an existing parent ZFS fs.  It then automatically mounts the new fs
4491867Sgjelinek  * directly under the mountpoint for the parent fs using the last component
4501867Sgjelinek  * of the name as the mountpoint directory.
4511867Sgjelinek  *
4521867Sgjelinek  * For example:
4531867Sgjelinek  *    Name			Mountpoint
4541867Sgjelinek  *    space/eng/dev/test/zone1	/project1/eng/dev/test/zone1
4551867Sgjelinek  *
4561867Sgjelinek  * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
4571867Sgjelinek  * Z_ERR.
4581867Sgjelinek  */
4591867Sgjelinek static int
4601867Sgjelinek path2name(char *zonepath, char *zfs_name, int len)
4611867Sgjelinek {
4621867Sgjelinek 	int		res;
4637093Sgjelinek 	char		*bnm, *dnm, *dname, *bname;
4641867Sgjelinek 	zfs_handle_t	*zhp;
4657093Sgjelinek 	struct stat	stbuf;
4661867Sgjelinek 
4677093Sgjelinek 	/*
4687093Sgjelinek 	 * We need two tmp strings to handle paths directly in / (e.g. /foo)
4697093Sgjelinek 	 * since dirname will overwrite the first char after "/" in this case.
4707093Sgjelinek 	 */
4717093Sgjelinek 	if ((bnm = strdup(zonepath)) == NULL)
4721867Sgjelinek 		return (Z_ERR);
4731867Sgjelinek 
4747093Sgjelinek 	if ((dnm = strdup(zonepath)) == NULL) {
4757093Sgjelinek 		free(bnm);
4767093Sgjelinek 		return (Z_ERR);
4777093Sgjelinek 	}
4787093Sgjelinek 
4797093Sgjelinek 	bname = basename(bnm);
4807093Sgjelinek 	dname = dirname(dnm);
4817093Sgjelinek 
4821867Sgjelinek 	/*
4837093Sgjelinek 	 * This is a quick test to save iterating over all of the zfs datasets
4847093Sgjelinek 	 * on the system (which can be a lot).  If the parent dir is not in a
4857093Sgjelinek 	 * ZFS fs, then we're done.
4861867Sgjelinek 	 */
4877093Sgjelinek 	if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) ||
4887093Sgjelinek 	    strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) {
4897093Sgjelinek 		free(bnm);
4907093Sgjelinek 		free(dnm);
4911867Sgjelinek 		return (Z_ERR);
4927093Sgjelinek 	}
4931867Sgjelinek 
4947093Sgjelinek 	/* See if the parent directory is its own ZFS dataset. */
4957093Sgjelinek 	if ((zhp = mount2zhandle(dname)) == NULL) {
4967093Sgjelinek 		/*
4977093Sgjelinek 		 * The parent is not a ZFS dataset so we can't automatically
4987093Sgjelinek 		 * create a dataset on the given path.
4997093Sgjelinek 		 */
5007093Sgjelinek 		free(bnm);
5017093Sgjelinek 		free(dnm);
5027093Sgjelinek 		return (Z_ERR);
5037093Sgjelinek 	}
5041867Sgjelinek 
5057093Sgjelinek 	res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname);
5067093Sgjelinek 
5077093Sgjelinek 	free(bnm);
5087093Sgjelinek 	free(dnm);
5091867Sgjelinek 	zfs_close(zhp);
5101867Sgjelinek 	if (res >= len)
5111867Sgjelinek 		return (Z_ERR);
5121867Sgjelinek 
5131867Sgjelinek 	return (Z_OK);
5141867Sgjelinek }
5151867Sgjelinek 
5161867Sgjelinek /*
5171867Sgjelinek  * A ZFS file system iterator call-back function used to determine if the
5181867Sgjelinek  * file system has dependents (snapshots & clones).
5191867Sgjelinek  */
5201867Sgjelinek /* ARGSUSED */
5211867Sgjelinek static int
5221867Sgjelinek has_dependent(zfs_handle_t *zhp, void *data)
5231867Sgjelinek {
5241867Sgjelinek 	zfs_close(zhp);
5251867Sgjelinek 	return (1);
5261867Sgjelinek }
5271867Sgjelinek 
5281867Sgjelinek /*
5291867Sgjelinek  * Given a snapshot name, get the file system path where the snapshot lives.
5301867Sgjelinek  * A snapshot name is of the form fs_name@snap_name.  For example, snapshot
5311867Sgjelinek  * pl/zones/z1@SUNWzone1 would have a path of
5321867Sgjelinek  * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5331867Sgjelinek  */
5341867Sgjelinek static int
5351867Sgjelinek snap2path(char *snap_name, char *path, int len)
5361867Sgjelinek {
5371867Sgjelinek 	char		*p;
5381867Sgjelinek 	zfs_handle_t	*zhp;
5391867Sgjelinek 	char		mp[ZFS_MAXPROPLEN];
5401867Sgjelinek 
5411867Sgjelinek 	if ((p = strrchr(snap_name, '@')) == NULL)
5421867Sgjelinek 		return (Z_ERR);
5431867Sgjelinek 
5441867Sgjelinek 	/* Get the file system name from the snap_name. */
5451867Sgjelinek 	*p = '\0';
5465094Slling 	zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET);
5471867Sgjelinek 	*p = '@';
5481867Sgjelinek 	if (zhp == NULL)
5491867Sgjelinek 		return (Z_ERR);
5501867Sgjelinek 
5511867Sgjelinek 	/* Get the file system mount point. */
5521867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
5532082Seschrock 	    0, B_FALSE) != 0) {
5541867Sgjelinek 		zfs_close(zhp);
5551867Sgjelinek 		return (Z_ERR);
5561867Sgjelinek 	}
5571867Sgjelinek 	zfs_close(zhp);
5581867Sgjelinek 
5591867Sgjelinek 	p++;
5601867Sgjelinek 	if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5611867Sgjelinek 		return (Z_ERR);
5621867Sgjelinek 
5631867Sgjelinek 	return (Z_OK);
5641867Sgjelinek }
5651867Sgjelinek 
5661867Sgjelinek /*
5671867Sgjelinek  * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
5681867Sgjelinek  * possible, or by copying the data from the snapshot to the zonepath.
5691867Sgjelinek  */
5701867Sgjelinek int
5717089Sgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
5721867Sgjelinek {
5731867Sgjelinek 	int	err = Z_OK;
5741867Sgjelinek 	char	clone_name[MAXPATHLEN];
5751867Sgjelinek 	char	snap_path[MAXPATHLEN];
5761867Sgjelinek 
5771867Sgjelinek 	if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
5781867Sgjelinek 		(void) fprintf(stderr, gettext("unable to find path for %s.\n"),
5791867Sgjelinek 		    snap_name);
5801867Sgjelinek 		return (Z_ERR);
5811867Sgjelinek 	}
5821867Sgjelinek 
5837089Sgjelinek 	if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
5841867Sgjelinek 		return (Z_NO_ENTRY);
5851867Sgjelinek 
5861867Sgjelinek 	/*
5871867Sgjelinek 	 * The zonepath cannot be ZFS cloned, try to copy the data from
5881867Sgjelinek 	 * within the snapshot to the zonepath.
5891867Sgjelinek 	 */
5901867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
5911867Sgjelinek 		if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
5921867Sgjelinek 			if (clean_out_clone() != Z_OK)
5931867Sgjelinek 				(void) fprintf(stderr,
5941867Sgjelinek 				    gettext("could not remove the "
5951867Sgjelinek 				    "software inventory from %s\n"), zonepath);
5961867Sgjelinek 
5971867Sgjelinek 		return (err);
5981867Sgjelinek 	}
5991867Sgjelinek 
6001867Sgjelinek 	if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
6011867Sgjelinek 		if (err != Z_NO_ENTRY) {
6021867Sgjelinek 			/*
6031867Sgjelinek 			 * Cloning the snapshot failed.  Fall back to trying
6041867Sgjelinek 			 * to install the zone by copying from the snapshot.
6051867Sgjelinek 			 */
6061867Sgjelinek 			if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
6071867Sgjelinek 				if (clean_out_clone() != Z_OK)
6081867Sgjelinek 					(void) fprintf(stderr,
6091867Sgjelinek 					    gettext("could not remove the "
6101867Sgjelinek 					    "software inventory from %s\n"),
6111867Sgjelinek 					    zonepath);
6121867Sgjelinek 		} else {
6131867Sgjelinek 			/*
6141867Sgjelinek 			 * The snapshot is unusable for some reason so restore
6151867Sgjelinek 			 * the zone state to configured since we were unable to
6161867Sgjelinek 			 * actually do anything about getting the zone
6171867Sgjelinek 			 * installed.
6181867Sgjelinek 			 */
6191867Sgjelinek 			int tmp;
6201867Sgjelinek 
6211867Sgjelinek 			if ((tmp = zone_set_state(target_zone,
6221867Sgjelinek 			    ZONE_STATE_CONFIGURED)) != Z_OK) {
6231867Sgjelinek 				errno = tmp;
6241867Sgjelinek 				zperror2(target_zone,
6251867Sgjelinek 				    gettext("could not set state"));
6261867Sgjelinek 			}
6271867Sgjelinek 		}
6281867Sgjelinek 	}
6291867Sgjelinek 
6301867Sgjelinek 	return (err);
6311867Sgjelinek }
6321867Sgjelinek 
6331867Sgjelinek /*
6341867Sgjelinek  * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
6351867Sgjelinek  */
6361867Sgjelinek int
6377089Sgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
6387089Sgjelinek     char *postsnapbuf)
6391867Sgjelinek {
6401867Sgjelinek 	zfs_handle_t	*zhp;
6411867Sgjelinek 	char		clone_name[MAXPATHLEN];
6421867Sgjelinek 	char		snap_name[MAXPATHLEN];
6431867Sgjelinek 
6441867Sgjelinek 	/*
6451867Sgjelinek 	 * Try to get a zfs handle for the source_zonepath.  If this fails
6461867Sgjelinek 	 * the source_zonepath is not ZFS so return an error.
6471867Sgjelinek 	 */
6481867Sgjelinek 	if ((zhp = mount2zhandle(source_zonepath)) == NULL)
6491867Sgjelinek 		return (Z_ERR);
6501867Sgjelinek 
6511867Sgjelinek 	/*
6521867Sgjelinek 	 * Check if there is a file system already mounted on zonepath.  If so,
6531867Sgjelinek 	 * we can't clone to the path so we should fall back to copying.
6541867Sgjelinek 	 */
6551867Sgjelinek 	if (is_mountpnt(zonepath)) {
6561867Sgjelinek 		zfs_close(zhp);
6571867Sgjelinek 		(void) fprintf(stderr,
6581867Sgjelinek 		    gettext("A file system is already mounted on %s,\n"
6591867Sgjelinek 		    "preventing use of a ZFS clone.\n"), zonepath);
6601867Sgjelinek 		return (Z_ERR);
6611867Sgjelinek 	}
6621867Sgjelinek 
6631867Sgjelinek 	/*
6641867Sgjelinek 	 * Instead of using path2name to get the clone name from the zonepath,
6651867Sgjelinek 	 * we could generate a name from the source zone ZFS name.  However,
6661867Sgjelinek 	 * this would mean we would create the clone under the ZFS fs of the
6671867Sgjelinek 	 * source instead of what the zonepath says.  For example,
6681867Sgjelinek 	 *
6691867Sgjelinek 	 * source_zonepath		zonepath
6701867Sgjelinek 	 * /pl/zones/dev/z1		/pl/zones/deploy/z2
6711867Sgjelinek 	 *
6721867Sgjelinek 	 * We don't want the clone to be under "dev", we want it under
6731867Sgjelinek 	 * "deploy", so that we can leverage the normal attribute inheritance
6741867Sgjelinek 	 * that ZFS provides in the fs hierarchy.
6751867Sgjelinek 	 */
6761867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
6771867Sgjelinek 		zfs_close(zhp);
6781867Sgjelinek 		return (Z_ERR);
6791867Sgjelinek 	}
6801867Sgjelinek 
6817089Sgjelinek 	if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf,
6827089Sgjelinek 	    postsnapbuf) != Z_OK) {
6831867Sgjelinek 		zfs_close(zhp);
6841867Sgjelinek 		return (Z_ERR);
6851867Sgjelinek 	}
6861867Sgjelinek 	zfs_close(zhp);
6871867Sgjelinek 
6883686Sgjelinek 	if (clone_snap(snap_name, clone_name) != Z_OK) {
6893686Sgjelinek 		/* Clean up the snapshot we just took. */
6903686Sgjelinek 		if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT))
6913686Sgjelinek 		    != NULL) {
6923686Sgjelinek 			if (zfs_unmount(zhp, NULL, 0) == 0)
6933686Sgjelinek 				(void) zfs_destroy(zhp);
6943686Sgjelinek 			zfs_close(zhp);
6953686Sgjelinek 		}
6963686Sgjelinek 
6971867Sgjelinek 		return (Z_ERR);
6983686Sgjelinek 	}
6991867Sgjelinek 
7001867Sgjelinek 	(void) printf(gettext("Instead of copying, a ZFS clone has been "
7011867Sgjelinek 	    "created for this zone.\n"));
7021867Sgjelinek 
7031867Sgjelinek 	return (Z_OK);
7041867Sgjelinek }
7051867Sgjelinek 
7061867Sgjelinek /*
7071867Sgjelinek  * Attempt to create a ZFS file system for the specified zonepath.
7081867Sgjelinek  * We either will successfully create a ZFS file system and get it mounted
7091867Sgjelinek  * on the zonepath or we don't.  The caller doesn't care since a regular
7101867Sgjelinek  * directory is used for the zonepath if no ZFS file system is mounted there.
7111867Sgjelinek  */
7121867Sgjelinek void
7131867Sgjelinek create_zfs_zonepath(char *zonepath)
7141867Sgjelinek {
7151867Sgjelinek 	zfs_handle_t	*zhp;
7161867Sgjelinek 	char		zfs_name[MAXPATHLEN];
7172676Seschrock 	nvlist_t	*props = NULL;
7181867Sgjelinek 
7191867Sgjelinek 	if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
7201867Sgjelinek 		return;
7211867Sgjelinek 
722*9023Sgerald.jelinek@sun.com 	/* Check if the dataset already exists. */
723*9023Sgerald.jelinek@sun.com 	if ((zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) != NULL) {
724*9023Sgerald.jelinek@sun.com 		zfs_close(zhp);
725*9023Sgerald.jelinek@sun.com 		return;
726*9023Sgerald.jelinek@sun.com 	}
727*9023Sgerald.jelinek@sun.com 
7282676Seschrock 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
7292744Snn35248 	    nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
7302744Snn35248 	    "off") != 0) {
7312744Snn35248 		if (props != NULL)
7322744Snn35248 			nvlist_free(props);
7332676Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
7342676Seschrock 		    "out of memory\n"), zfs_name);
7352676Seschrock 	}
7362676Seschrock 
7372676Seschrock 	if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 ||
7385094Slling 	    (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) {
7392082Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
7402082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
7412676Seschrock 		nvlist_free(props);
7421867Sgjelinek 		return;
7431867Sgjelinek 	}
7441867Sgjelinek 
7452676Seschrock 	nvlist_free(props);
7462676Seschrock 
7471867Sgjelinek 	if (zfs_mount(zhp, NULL, 0) != 0) {
7482082Seschrock 		(void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: "
7492082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
7501867Sgjelinek 		(void) zfs_destroy(zhp);
7511867Sgjelinek 	} else {
7521867Sgjelinek 		if (chmod(zonepath, S_IRWXU) != 0) {
7531867Sgjelinek 			(void) fprintf(stderr, gettext("file system %s "
7541867Sgjelinek 			    "successfully created, but chmod %o failed: %s\n"),
7551867Sgjelinek 			    zfs_name, S_IRWXU, strerror(errno));
7561867Sgjelinek 			(void) destroy_zfs(zonepath);
7571867Sgjelinek 		} else {
7581867Sgjelinek 			(void) printf(gettext("A ZFS file system has been "
7591867Sgjelinek 			    "created for this zone.\n"));
7601867Sgjelinek 		}
7611867Sgjelinek 	}
7621867Sgjelinek 
7631867Sgjelinek 	zfs_close(zhp);
7641867Sgjelinek }
7651867Sgjelinek 
7661867Sgjelinek /*
7671867Sgjelinek  * If the zonepath is a ZFS file system, attempt to destroy it.  We return Z_OK
7681867Sgjelinek  * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
7691867Sgjelinek  * which means the caller should clean up the zonepath in the traditional
7701867Sgjelinek  * way.
7711867Sgjelinek  */
7721867Sgjelinek int
7731867Sgjelinek destroy_zfs(char *zonepath)
7741867Sgjelinek {
7751867Sgjelinek 	zfs_handle_t	*zhp;
7761867Sgjelinek 	boolean_t	is_clone = B_FALSE;
7771867Sgjelinek 	char		origin[ZFS_MAXPROPLEN];
7781867Sgjelinek 
7792082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
7801867Sgjelinek 		return (Z_ERR);
7811867Sgjelinek 
7821867Sgjelinek 	/*
7831867Sgjelinek 	 * We can't destroy the file system if it has dependents.
7841867Sgjelinek 	 */
7852474Seschrock 	if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0 ||
7861867Sgjelinek 	    zfs_unmount(zhp, NULL, 0) != 0) {
7871867Sgjelinek 		zfs_close(zhp);
7881867Sgjelinek 		return (Z_ERR);
7891867Sgjelinek 	}
7901867Sgjelinek 
7911867Sgjelinek 	/*
7921867Sgjelinek 	 * This might be a clone.  Try to get the snapshot so we can attempt
7931867Sgjelinek 	 * to destroy that as well.
7941867Sgjelinek 	 */
7951867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
7962082Seschrock 	    NULL, 0, B_FALSE) == 0)
7971867Sgjelinek 		is_clone = B_TRUE;
7981867Sgjelinek 
7991867Sgjelinek 	if (zfs_destroy(zhp) != 0) {
8001867Sgjelinek 		/*
8011867Sgjelinek 		 * If the destroy fails for some reason, try to remount
8021867Sgjelinek 		 * the file system so that we can use "rm -rf" to clean up
8031867Sgjelinek 		 * instead.
8041867Sgjelinek 		 */
8051867Sgjelinek 		(void) zfs_mount(zhp, NULL, 0);
8061867Sgjelinek 		zfs_close(zhp);
8071867Sgjelinek 		return (Z_ERR);
8081867Sgjelinek 	}
8091867Sgjelinek 
8103686Sgjelinek 	/*
8113686Sgjelinek 	 * If the zone has ever been moved then the mountpoint dir will not be
8123686Sgjelinek 	 * cleaned up by the zfs_destroy().  To handle this case try to clean
8133686Sgjelinek 	 * it up now but don't worry if it fails, that will be normal.
8143686Sgjelinek 	 */
8153686Sgjelinek 	(void) rmdir(zonepath);
8163686Sgjelinek 
8171867Sgjelinek 	(void) printf(gettext("The ZFS file system for this zone has been "
8181867Sgjelinek 	    "destroyed.\n"));
8191867Sgjelinek 
8201867Sgjelinek 	if (is_clone) {
8211867Sgjelinek 		zfs_handle_t	*ohp;
8221867Sgjelinek 
8231867Sgjelinek 		/*
8241867Sgjelinek 		 * Try to clean up the snapshot that the clone was taken from.
8251867Sgjelinek 		 */
8262082Seschrock 		if ((ohp = zfs_open(g_zfs, origin,
8272082Seschrock 		    ZFS_TYPE_SNAPSHOT)) != NULL) {
8282474Seschrock 			if (zfs_iter_dependents(ohp, B_TRUE, has_dependent,
8292474Seschrock 			    NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0)
8301867Sgjelinek 				(void) zfs_destroy(ohp);
8311867Sgjelinek 			zfs_close(ohp);
8321867Sgjelinek 		}
8331867Sgjelinek 	}
8341867Sgjelinek 
8351867Sgjelinek 	zfs_close(zhp);
8361867Sgjelinek 	return (Z_OK);
8371867Sgjelinek }
8381867Sgjelinek 
8391867Sgjelinek /*
8401867Sgjelinek  * Return true if the path is its own zfs file system.  We determine this
8411867Sgjelinek  * by stat-ing the path to see if it is zfs and stat-ing the parent to see
8421867Sgjelinek  * if it is a different fs.
8431867Sgjelinek  */
8441867Sgjelinek boolean_t
8451867Sgjelinek is_zonepath_zfs(char *zonepath)
8461867Sgjelinek {
8471867Sgjelinek 	int res;
8481867Sgjelinek 	char *path;
8491867Sgjelinek 	char *parent;
8502267Sdp 	struct statvfs64 buf1, buf2;
8511867Sgjelinek 
8522267Sdp 	if (statvfs64(zonepath, &buf1) != 0)
8531867Sgjelinek 		return (B_FALSE);
8541867Sgjelinek 
8551867Sgjelinek 	if (strcmp(buf1.f_basetype, "zfs") != 0)
8561867Sgjelinek 		return (B_FALSE);
8571867Sgjelinek 
8581867Sgjelinek 	if ((path = strdup(zonepath)) == NULL)
8591867Sgjelinek 		return (B_FALSE);
8601867Sgjelinek 
8611867Sgjelinek 	parent = dirname(path);
8622267Sdp 	res = statvfs64(parent, &buf2);
8631867Sgjelinek 	free(path);
8641867Sgjelinek 
8651867Sgjelinek 	if (res != 0)
8661867Sgjelinek 		return (B_FALSE);
8671867Sgjelinek 
8681867Sgjelinek 	if (buf1.f_fsid == buf2.f_fsid)
8691867Sgjelinek 		return (B_FALSE);
8701867Sgjelinek 
8711867Sgjelinek 	return (B_TRUE);
8721867Sgjelinek }
8731867Sgjelinek 
8741867Sgjelinek /*
8751867Sgjelinek  * Implement the fast move of a ZFS file system by simply updating the
8761867Sgjelinek  * mountpoint.  Since it is file system already, we don't have the
8771867Sgjelinek  * issue of cross-file system copying.
8781867Sgjelinek  */
8791867Sgjelinek int
8801867Sgjelinek move_zfs(char *zonepath, char *new_zonepath)
8811867Sgjelinek {
8821867Sgjelinek 	int		ret = Z_ERR;
8831867Sgjelinek 	zfs_handle_t	*zhp;
8841867Sgjelinek 
8852082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
8861867Sgjelinek 		return (Z_ERR);
8871867Sgjelinek 
8882676Seschrock 	if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
8892676Seschrock 	    new_zonepath) == 0) {
8901867Sgjelinek 		/*
8911867Sgjelinek 		 * Clean up the old mount point.  We ignore any failure since
8921867Sgjelinek 		 * the zone is already successfully mounted on the new path.
8931867Sgjelinek 		 */
8941867Sgjelinek 		(void) rmdir(zonepath);
8951867Sgjelinek 		ret = Z_OK;
8961867Sgjelinek 	}
8971867Sgjelinek 
8981867Sgjelinek 	zfs_close(zhp);
8991867Sgjelinek 
9001867Sgjelinek 	return (ret);
9011867Sgjelinek }
9021867Sgjelinek 
9031867Sgjelinek /*
9041867Sgjelinek  * Validate that the given dataset exists on the system, and that neither it nor
9051867Sgjelinek  * its children are zvols.
9061867Sgjelinek  *
9071867Sgjelinek  * Note that we don't do anything with the 'zoned' property here.  All
9081867Sgjelinek  * management is done in zoneadmd when the zone is actually rebooted.  This
9091867Sgjelinek  * allows us to automatically set the zoned property even when a zone is
9101867Sgjelinek  * rebooted by the administrator.
9111867Sgjelinek  */
9121867Sgjelinek int
9131867Sgjelinek verify_datasets(zone_dochandle_t handle)
9141867Sgjelinek {
9151867Sgjelinek 	int return_code = Z_OK;
9161867Sgjelinek 	struct zone_dstab dstab;
9171867Sgjelinek 	zfs_handle_t *zhp;
9181867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
9191867Sgjelinek 	char source[ZFS_MAXNAMELEN];
9205094Slling 	zprop_source_t srctype;
9211867Sgjelinek 
9221867Sgjelinek 	if (zonecfg_setdsent(handle) != Z_OK) {
9231867Sgjelinek 		/*
9241867Sgjelinek 		 * TRANSLATION_NOTE
9251867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
9261867Sgjelinek 		 */
9271867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs datasets: "
9281867Sgjelinek 		    "unable to enumerate datasets\n"));
9291867Sgjelinek 		return (Z_ERR);
9301867Sgjelinek 	}
9311867Sgjelinek 
9321867Sgjelinek 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
9331867Sgjelinek 
9342082Seschrock 		if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name,
9351867Sgjelinek 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
9362082Seschrock 			(void) fprintf(stderr, gettext("could not verify zfs "
9372082Seschrock 			    "dataset %s: %s\n"), dstab.zone_dataset_name,
9382082Seschrock 			    libzfs_error_description(g_zfs));
9391867Sgjelinek 			return_code = Z_ERR;
9401867Sgjelinek 			continue;
9411867Sgjelinek 		}
9421867Sgjelinek 
9431867Sgjelinek 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf,
9441867Sgjelinek 		    sizeof (propbuf), &srctype, source,
9451867Sgjelinek 		    sizeof (source), 0) == 0 &&
9465094Slling 		    (srctype == ZPROP_SRC_INHERITED)) {
9471867Sgjelinek 			(void) fprintf(stderr, gettext("could not verify zfs "
9481867Sgjelinek 			    "dataset %s: mountpoint cannot be inherited\n"),
9491867Sgjelinek 			    dstab.zone_dataset_name);
9501867Sgjelinek 			return_code = Z_ERR;
9511867Sgjelinek 			zfs_close(zhp);
9521867Sgjelinek 			continue;
9531867Sgjelinek 		}
9541867Sgjelinek 
9551867Sgjelinek 		if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
9561867Sgjelinek 			(void) fprintf(stderr, gettext("cannot verify zfs "
9571867Sgjelinek 			    "dataset %s: volumes cannot be specified as a "
9581867Sgjelinek 			    "zone dataset resource\n"),
9591867Sgjelinek 			    dstab.zone_dataset_name);
9601867Sgjelinek 			return_code = Z_ERR;
9611867Sgjelinek 		}
9621867Sgjelinek 
9631867Sgjelinek 		if (zfs_iter_children(zhp, check_zvol, NULL) != 0)
9641867Sgjelinek 			return_code = Z_ERR;
9651867Sgjelinek 
9661867Sgjelinek 		zfs_close(zhp);
9671867Sgjelinek 	}
9681867Sgjelinek 	(void) zonecfg_enddsent(handle);
9691867Sgjelinek 
9701867Sgjelinek 	return (return_code);
9711867Sgjelinek }
9721867Sgjelinek 
9731867Sgjelinek /*
9741867Sgjelinek  * Verify that the ZFS dataset exists, and its mountpoint
9751867Sgjelinek  * property is set to "legacy".
9761867Sgjelinek  */
9771867Sgjelinek int
9781867Sgjelinek verify_fs_zfs(struct zone_fstab *fstab)
9791867Sgjelinek {
9801867Sgjelinek 	zfs_handle_t *zhp;
9811867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
9821867Sgjelinek 
9832082Seschrock 	if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special,
9845094Slling 	    ZFS_TYPE_DATASET)) == NULL) {
9851867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
9861867Sgjelinek 		    "could not access zfs dataset '%s'\n"),
9871867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
9881867Sgjelinek 		return (Z_ERR);
9891867Sgjelinek 	}
9901867Sgjelinek 
9911867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
9921867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify fs %s: "
9931867Sgjelinek 		    "'%s' is not a file system\n"),
9941867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
9951867Sgjelinek 		zfs_close(zhp);
9961867Sgjelinek 		return (Z_ERR);
9971867Sgjelinek 	}
9981867Sgjelinek 
9991867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
10001867Sgjelinek 	    NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
10011867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
10021867Sgjelinek 		    "zfs '%s' mountpoint is not \"legacy\"\n"),
10031867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
10041867Sgjelinek 		zfs_close(zhp);
10051867Sgjelinek 		return (Z_ERR);
10061867Sgjelinek 	}
10071867Sgjelinek 
10081867Sgjelinek 	zfs_close(zhp);
10091867Sgjelinek 	return (Z_OK);
10101867Sgjelinek }
10112082Seschrock 
10122082Seschrock int
10132082Seschrock init_zfs(void)
10142082Seschrock {
10152082Seschrock 	if ((g_zfs = libzfs_init()) == NULL) {
10162082Seschrock 		(void) fprintf(stderr, gettext("failed to initialize ZFS "
10172082Seschrock 		    "library\n"));
10182082Seschrock 		return (Z_ERR);
10192082Seschrock 	}
10202082Seschrock 
10212082Seschrock 	return (Z_OK);
10222082Seschrock }
1023