xref: /onnv-gate/usr/src/cmd/zoneadm/zfs.c (revision 7093:03f0b895f95e)
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 /*
237089Sgjelinek  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241867Sgjelinek  * Use is subject to license terms.
251867Sgjelinek  */
261867Sgjelinek 
271867Sgjelinek #pragma ident	"%Z%%M%	%I%	%E% SMI"
281867Sgjelinek 
291867Sgjelinek /*
301867Sgjelinek  * This file contains the functions used to support the ZFS integration
311867Sgjelinek  * with zones.  This includes validation (e.g. zonecfg dataset), cloning,
321867Sgjelinek  * file system creation and destruction.
331867Sgjelinek  */
341867Sgjelinek 
351867Sgjelinek #include <stdio.h>
361867Sgjelinek #include <errno.h>
371867Sgjelinek #include <unistd.h>
381867Sgjelinek #include <string.h>
391867Sgjelinek #include <locale.h>
401867Sgjelinek #include <libintl.h>
411867Sgjelinek #include <sys/stat.h>
421867Sgjelinek #include <sys/statvfs.h>
431867Sgjelinek #include <libgen.h>
441867Sgjelinek #include <libzonecfg.h>
451867Sgjelinek #include <sys/mnttab.h>
461867Sgjelinek #include <libzfs.h>
47*7093Sgjelinek #include <sys/mntent.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 {
591867Sgjelinek 	char	*match_name;
601867Sgjelinek 	int	len;
611867Sgjelinek 	int	max;
621867Sgjelinek } zfs_snapshot_data_t;
631867Sgjelinek 
641867Sgjelinek /*
651867Sgjelinek  * A ZFS file system iterator call-back function which is used to validate
661867Sgjelinek  * datasets imported into the zone.
671867Sgjelinek  */
681867Sgjelinek /* ARGSUSED */
691867Sgjelinek static int
701867Sgjelinek check_zvol(zfs_handle_t *zhp, void *unused)
711867Sgjelinek {
721867Sgjelinek 	int ret;
731867Sgjelinek 
741867Sgjelinek 	if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
751867Sgjelinek 		/*
761867Sgjelinek 		 * TRANSLATION_NOTE
771867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
781867Sgjelinek 		 */
791867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify zfs dataset %s: "
801867Sgjelinek 		    "volumes cannot be specified as a zone dataset resource\n"),
811867Sgjelinek 		    zfs_get_name(zhp));
821867Sgjelinek 		ret = -1;
831867Sgjelinek 	} else {
841867Sgjelinek 		ret = zfs_iter_children(zhp, check_zvol, NULL);
851867Sgjelinek 	}
861867Sgjelinek 
871867Sgjelinek 	zfs_close(zhp);
881867Sgjelinek 
891867Sgjelinek 	return (ret);
901867Sgjelinek }
911867Sgjelinek 
921867Sgjelinek /*
931867Sgjelinek  * A ZFS file system iterator call-back function which returns the
941867Sgjelinek  * zfs_handle_t for a ZFS file system on the specified mount point.
951867Sgjelinek  */
961867Sgjelinek static int
971867Sgjelinek match_mountpoint(zfs_handle_t *zhp, void *data)
981867Sgjelinek {
991867Sgjelinek 	int			res;
1001867Sgjelinek 	zfs_mount_data_t	*cbp;
1011867Sgjelinek 	char			mp[ZFS_MAXPROPLEN];
1021867Sgjelinek 
1031867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
1041867Sgjelinek 		zfs_close(zhp);
1051867Sgjelinek 		return (0);
1061867Sgjelinek 	}
1071867Sgjelinek 
108*7093Sgjelinek 	/* First check if the dataset is mounted. */
109*7093Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
110*7093Sgjelinek 	    0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
111*7093Sgjelinek 		zfs_close(zhp);
112*7093Sgjelinek 		return (0);
113*7093Sgjelinek 	}
114*7093Sgjelinek 
115*7093Sgjelinek 	/* Now check mount point. */
1161867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
117*7093Sgjelinek 	    0, B_FALSE) != 0) {
118*7093Sgjelinek 		zfs_close(zhp);
119*7093Sgjelinek 		return (0);
120*7093Sgjelinek 	}
121*7093Sgjelinek 
122*7093Sgjelinek 	cbp = (zfs_mount_data_t *)data;
123*7093Sgjelinek 
124*7093Sgjelinek 	if (strcmp(mp, "legacy") == 0) {
125*7093Sgjelinek 		/* If legacy, must look in mnttab for mountpoint. */
126*7093Sgjelinek 		FILE		*fp;
127*7093Sgjelinek 		struct mnttab	entry;
128*7093Sgjelinek 		const char	*nm;
129*7093Sgjelinek 
130*7093Sgjelinek 		nm = zfs_get_name(zhp);
131*7093Sgjelinek 		if ((fp = fopen(MNTTAB, "r")) == NULL) {
132*7093Sgjelinek 			zfs_close(zhp);
133*7093Sgjelinek 			return (0);
134*7093Sgjelinek 		}
135*7093Sgjelinek 
136*7093Sgjelinek 		while (getmntent(fp, &entry) == 0) {
137*7093Sgjelinek 			if (strcmp(nm, entry.mnt_special) == 0) {
138*7093Sgjelinek 				if (strcmp(entry.mnt_mountp, cbp->match_name)
139*7093Sgjelinek 				    == 0) {
140*7093Sgjelinek 					(void) fclose(fp);
141*7093Sgjelinek 					cbp->match_handle = zhp;
142*7093Sgjelinek 					return (1);
143*7093Sgjelinek 				}
144*7093Sgjelinek 				break;
145*7093Sgjelinek 			}
146*7093Sgjelinek 		}
147*7093Sgjelinek 		(void) fclose(fp);
148*7093Sgjelinek 
149*7093Sgjelinek 	} else if (strcmp(mp, cbp->match_name) == 0) {
1501867Sgjelinek 		cbp->match_handle = zhp;
1511867Sgjelinek 		return (1);
1521867Sgjelinek 	}
1531867Sgjelinek 
154*7093Sgjelinek 	/* Iterate over any nested datasets. */
1551867Sgjelinek 	res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1561867Sgjelinek 	zfs_close(zhp);
1571867Sgjelinek 	return (res);
1581867Sgjelinek }
1591867Sgjelinek 
1601867Sgjelinek /*
1611867Sgjelinek  * Get ZFS handle for the specified mount point.
1621867Sgjelinek  */
1631867Sgjelinek static zfs_handle_t *
1641867Sgjelinek mount2zhandle(char *mountpoint)
1651867Sgjelinek {
1661867Sgjelinek 	zfs_mount_data_t	cb;
1671867Sgjelinek 
1681867Sgjelinek 	cb.match_name = mountpoint;
1691867Sgjelinek 	cb.match_handle = NULL;
1702082Seschrock 	(void) zfs_iter_root(g_zfs, match_mountpoint, &cb);
1711867Sgjelinek 	return (cb.match_handle);
1721867Sgjelinek }
1731867Sgjelinek 
1741867Sgjelinek /*
1751867Sgjelinek  * Check if there is already a file system (zfs or any other type) mounted on
1761867Sgjelinek  * path.
1771867Sgjelinek  */
1781867Sgjelinek static boolean_t
1791867Sgjelinek is_mountpnt(char *path)
1801867Sgjelinek {
1811867Sgjelinek 	FILE		*fp;
1821867Sgjelinek 	struct mnttab	entry;
1831867Sgjelinek 
184*7093Sgjelinek 	if ((fp = fopen(MNTTAB, "r")) == NULL)
1851867Sgjelinek 		return (B_FALSE);
1861867Sgjelinek 
1871867Sgjelinek 	while (getmntent(fp, &entry) == 0) {
1881867Sgjelinek 		if (strcmp(path, entry.mnt_mountp) == 0) {
1891867Sgjelinek 			(void) fclose(fp);
1901867Sgjelinek 			return (B_TRUE);
1911867Sgjelinek 		}
1921867Sgjelinek 	}
1931867Sgjelinek 
1941867Sgjelinek 	(void) fclose(fp);
1951867Sgjelinek 	return (B_FALSE);
1961867Sgjelinek }
1971867Sgjelinek 
1981867Sgjelinek /*
1997089Sgjelinek  * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
2001867Sgjelinek  */
2011867Sgjelinek static int
2027089Sgjelinek pre_snapshot(char *presnapbuf)
2031867Sgjelinek {
2047089Sgjelinek 	int status;
2051867Sgjelinek 
2067089Sgjelinek 	/* No brand-specific handler */
2077089Sgjelinek 	if (presnapbuf[0] == '\0')
2087089Sgjelinek 		return (Z_OK);
2091867Sgjelinek 
2107089Sgjelinek 	/* Run the hook */
2117089Sgjelinek 	status = do_subproc_interactive(presnapbuf);
2127089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific presnapshot"),
2137089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2141867Sgjelinek 		return (Z_ERR);
2151867Sgjelinek 
2161867Sgjelinek 	return (Z_OK);
2171867Sgjelinek }
2181867Sgjelinek 
2191867Sgjelinek /*
2207089Sgjelinek  * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
2211867Sgjelinek  */
2221867Sgjelinek static int
2237089Sgjelinek post_snapshot(char *postsnapbuf)
2241867Sgjelinek {
2257089Sgjelinek 	int status;
2261867Sgjelinek 
2277089Sgjelinek 	/* No brand-specific handler */
2287089Sgjelinek 	if (postsnapbuf[0] == '\0')
2297089Sgjelinek 		return (Z_OK);
2307089Sgjelinek 
2317089Sgjelinek 	/* Run the hook */
2327089Sgjelinek 	status = do_subproc_interactive(postsnapbuf);
2337089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific postsnapshot"),
2347089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
2351867Sgjelinek 		return (Z_ERR);
2361867Sgjelinek 
2371867Sgjelinek 	return (Z_OK);
2381867Sgjelinek }
2391867Sgjelinek 
2401867Sgjelinek /*
2411867Sgjelinek  * This is a ZFS snapshot iterator call-back function which returns the
2421867Sgjelinek  * highest number of SUNWzone snapshots that have been taken.
2431867Sgjelinek  */
2441867Sgjelinek static int
2451867Sgjelinek get_snap_max(zfs_handle_t *zhp, void *data)
2461867Sgjelinek {
2471867Sgjelinek 	int			res;
2481867Sgjelinek 	zfs_snapshot_data_t	*cbp;
2491867Sgjelinek 
2501867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2511867Sgjelinek 		zfs_close(zhp);
2521867Sgjelinek 		return (0);
2531867Sgjelinek 	}
2541867Sgjelinek 
2551867Sgjelinek 	cbp = (zfs_snapshot_data_t *)data;
2561867Sgjelinek 
2571867Sgjelinek 	if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2581867Sgjelinek 		char	*nump;
2591867Sgjelinek 		int	num;
2601867Sgjelinek 
2611867Sgjelinek 		nump = (char *)(zfs_get_name(zhp) + cbp->len);
2621867Sgjelinek 		num = atoi(nump);
2631867Sgjelinek 		if (num > cbp->max)
2641867Sgjelinek 			cbp->max = num;
2651867Sgjelinek 	}
2661867Sgjelinek 
2671867Sgjelinek 	res = zfs_iter_snapshots(zhp, get_snap_max, data);
2681867Sgjelinek 	zfs_close(zhp);
2691867Sgjelinek 	return (res);
2701867Sgjelinek }
2711867Sgjelinek 
2721867Sgjelinek /*
2731867Sgjelinek  * Take a ZFS snapshot to be used for cloning the zone.
2741867Sgjelinek  */
2751867Sgjelinek static int
2767089Sgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
2777089Sgjelinek     char *presnapbuf, char *postsnapbuf)
2781867Sgjelinek {
2791867Sgjelinek 	int			res;
2801867Sgjelinek 	char			template[ZFS_MAXNAMELEN];
2811867Sgjelinek 	zfs_snapshot_data_t	cb;
2821867Sgjelinek 
2831867Sgjelinek 	/*
2841867Sgjelinek 	 * First we need to figure out the next available name for the
2851867Sgjelinek 	 * zone snapshot.  Look through the list of zones snapshots for
2861867Sgjelinek 	 * this file system to determine the maximum snapshot name.
2871867Sgjelinek 	 */
2881867Sgjelinek 	if (snprintf(template, sizeof (template), "%s@SUNWzone",
2891867Sgjelinek 	    zfs_get_name(zhp)) >=  sizeof (template))
2901867Sgjelinek 		return (Z_ERR);
2911867Sgjelinek 
2921867Sgjelinek 	cb.match_name = template;
2931867Sgjelinek 	cb.len = strlen(template);
2941867Sgjelinek 	cb.max = 0;
2951867Sgjelinek 
2961867Sgjelinek 	if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0)
2971867Sgjelinek 		return (Z_ERR);
2981867Sgjelinek 
2991867Sgjelinek 	cb.max++;
3001867Sgjelinek 
3011867Sgjelinek 	if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
3021867Sgjelinek 	    zfs_get_name(zhp), cb.max) >= snap_size)
3031867Sgjelinek 		return (Z_ERR);
3041867Sgjelinek 
3057089Sgjelinek 	if (pre_snapshot(presnapbuf) != Z_OK)
3061867Sgjelinek 		return (Z_ERR);
3072199Sahrens 	res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE);
3087089Sgjelinek 	if (post_snapshot(postsnapbuf) != Z_OK)
3091867Sgjelinek 		return (Z_ERR);
3101867Sgjelinek 
3111867Sgjelinek 	if (res != 0)
3121867Sgjelinek 		return (Z_ERR);
3131867Sgjelinek 	return (Z_OK);
3141867Sgjelinek }
3151867Sgjelinek 
3161867Sgjelinek /*
3171867Sgjelinek  * We are using an explicit snapshot from some earlier point in time so
3187089Sgjelinek  * we need to validate it.  Run the brand specific hook.
3191867Sgjelinek  */
3201867Sgjelinek static int
3217089Sgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
3221867Sgjelinek {
3237089Sgjelinek 	int status;
3247089Sgjelinek 	char cmdbuf[MAXPATHLEN];
3251867Sgjelinek 
3267089Sgjelinek 	/* No brand-specific handler */
3277089Sgjelinek 	if (validsnapbuf[0] == '\0')
3287089Sgjelinek 		return (Z_OK);
3291867Sgjelinek 
3307089Sgjelinek 	/* pass args - snapshot_name & snap_path */
3317089Sgjelinek 	if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
3327089Sgjelinek 	    snapshot_name, snap_path) >= sizeof (cmdbuf)) {
3337089Sgjelinek 		zerror("Command line too long");
3341867Sgjelinek 		return (Z_ERR);
3351867Sgjelinek 	}
3361867Sgjelinek 
3377089Sgjelinek 	/* Run the hook */
3387089Sgjelinek 	status = do_subproc_interactive(cmdbuf);
3397089Sgjelinek 	if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
3407089Sgjelinek 	    status, B_FALSE)) != ZONE_SUBPROC_OK)
3417089Sgjelinek 		return (Z_ERR);
3421867Sgjelinek 
3437089Sgjelinek 	return (Z_OK);
3441867Sgjelinek }
3451867Sgjelinek 
3461867Sgjelinek /*
3471867Sgjelinek  * Remove the sw inventory file from inside this zonepath that we picked up out
3481867Sgjelinek  * of the snapshot.
3491867Sgjelinek  */
3501867Sgjelinek static int
3511867Sgjelinek clean_out_clone()
3521867Sgjelinek {
3531867Sgjelinek 	int err;
3541867Sgjelinek 	zone_dochandle_t handle;
3551867Sgjelinek 
3561867Sgjelinek 	if ((handle = zonecfg_init_handle()) == NULL) {
3571867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3581867Sgjelinek 		return (Z_ERR);
3591867Sgjelinek 	}
3601867Sgjelinek 
3611867Sgjelinek 	if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3621867Sgjelinek 		errno = err;
3631867Sgjelinek 		zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3641867Sgjelinek 		zonecfg_fini_handle(handle);
3651867Sgjelinek 		return (Z_ERR);
3661867Sgjelinek 	}
3671867Sgjelinek 
3681867Sgjelinek 	zonecfg_rm_detached(handle, B_FALSE);
3691867Sgjelinek 	zonecfg_fini_handle(handle);
3701867Sgjelinek 
3711867Sgjelinek 	return (Z_OK);
3721867Sgjelinek }
3731867Sgjelinek 
3741867Sgjelinek /*
3751867Sgjelinek  * Make a ZFS clone on zonepath from snapshot_name.
3761867Sgjelinek  */
3771867Sgjelinek static int
3781867Sgjelinek clone_snap(char *snapshot_name, char *zonepath)
3791867Sgjelinek {
3801867Sgjelinek 	int		res = Z_OK;
3811867Sgjelinek 	int		err;
3821867Sgjelinek 	zfs_handle_t	*zhp;
3831867Sgjelinek 	zfs_handle_t	*clone;
3842676Seschrock 	nvlist_t	*props = NULL;
3851867Sgjelinek 
3862082Seschrock 	if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
3871867Sgjelinek 		return (Z_NO_ENTRY);
3881867Sgjelinek 
3891867Sgjelinek 	(void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
3901867Sgjelinek 
3912676Seschrock 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
3922744Snn35248 	    nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
3932744Snn35248 	    "off") != 0) {
3942744Snn35248 		if (props != NULL)
3952744Snn35248 			nvlist_free(props);
3962676Seschrock 		(void) fprintf(stderr, gettext("could not create ZFS clone "
3972676Seschrock 		    "%s: out of memory\n"), zonepath);
3982676Seschrock 		return (Z_ERR);
3992676Seschrock 	}
4002676Seschrock 
4012676Seschrock 	err = zfs_clone(zhp, zonepath, props);
4021867Sgjelinek 	zfs_close(zhp);
4032676Seschrock 
4042676Seschrock 	nvlist_free(props);
4052676Seschrock 
4061867Sgjelinek 	if (err != 0)
4071867Sgjelinek 		return (Z_ERR);
4081867Sgjelinek 
4091867Sgjelinek 	/* create the mountpoint if necessary */
4105094Slling 	if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL)
4111867Sgjelinek 		return (Z_ERR);
4121867Sgjelinek 
4131867Sgjelinek 	/*
4141867Sgjelinek 	 * The clone has been created so we need to print a diagnostic
4151867Sgjelinek 	 * message if one of the following steps fails for some reason.
4161867Sgjelinek 	 */
4171867Sgjelinek 	if (zfs_mount(clone, NULL, 0) != 0) {
4181867Sgjelinek 		(void) fprintf(stderr, gettext("could not mount ZFS clone "
4191867Sgjelinek 		    "%s\n"), zfs_get_name(clone));
4201867Sgjelinek 		res = Z_ERR;
4211867Sgjelinek 
4222676Seschrock 	} else if (clean_out_clone() != Z_OK) {
4232676Seschrock 		(void) fprintf(stderr, gettext("could not remove the "
4242676Seschrock 		    "software inventory from ZFS clone %s\n"),
4252676Seschrock 		    zfs_get_name(clone));
4262676Seschrock 		res = Z_ERR;
4271867Sgjelinek 	}
4281867Sgjelinek 
4291867Sgjelinek 	zfs_close(clone);
4301867Sgjelinek 	return (res);
4311867Sgjelinek }
4321867Sgjelinek 
4331867Sgjelinek /*
4341867Sgjelinek  * This function takes a zonepath and attempts to determine what the ZFS
4351867Sgjelinek  * file system name (not mountpoint) should be for that path.  We do not
4361867Sgjelinek  * assume that zonepath is an existing directory or ZFS fs since we use
4371867Sgjelinek  * this function as part of the process of creating a new ZFS fs or clone.
4381867Sgjelinek  *
4391867Sgjelinek  * The way this works is that we look at the parent directory of the zonepath
4401867Sgjelinek  * to see if it is a ZFS fs.  If it is, we get the name of that ZFS fs and
4411867Sgjelinek  * append the last component of the zonepath to generate the ZFS name for the
4421867Sgjelinek  * zonepath.  This matches the algorithm that ZFS uses for automatically
4431867Sgjelinek  * mounting a new fs after it is created.
4441867Sgjelinek  *
4451867Sgjelinek  * Although a ZFS fs can be mounted anywhere, we don't worry about handling
4461867Sgjelinek  * all of the complexity that a user could possibly configure with arbitrary
4471867Sgjelinek  * mounts since there is no way to generate a ZFS name from a random path in
4481867Sgjelinek  * the file system.  We only try to handle the automatic mounts that ZFS does
4491867Sgjelinek  * for each file system.  ZFS restricts this so that a new fs must be created
4501867Sgjelinek  * in an existing parent ZFS fs.  It then automatically mounts the new fs
4511867Sgjelinek  * directly under the mountpoint for the parent fs using the last component
4521867Sgjelinek  * of the name as the mountpoint directory.
4531867Sgjelinek  *
4541867Sgjelinek  * For example:
4551867Sgjelinek  *    Name			Mountpoint
4561867Sgjelinek  *    space/eng/dev/test/zone1	/project1/eng/dev/test/zone1
4571867Sgjelinek  *
4581867Sgjelinek  * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
4591867Sgjelinek  * Z_ERR.
4601867Sgjelinek  */
4611867Sgjelinek static int
4621867Sgjelinek path2name(char *zonepath, char *zfs_name, int len)
4631867Sgjelinek {
4641867Sgjelinek 	int		res;
465*7093Sgjelinek 	char		*bnm, *dnm, *dname, *bname;
4661867Sgjelinek 	zfs_handle_t	*zhp;
467*7093Sgjelinek 	struct stat	stbuf;
4681867Sgjelinek 
469*7093Sgjelinek 	/*
470*7093Sgjelinek 	 * We need two tmp strings to handle paths directly in / (e.g. /foo)
471*7093Sgjelinek 	 * since dirname will overwrite the first char after "/" in this case.
472*7093Sgjelinek 	 */
473*7093Sgjelinek 	if ((bnm = strdup(zonepath)) == NULL)
4741867Sgjelinek 		return (Z_ERR);
4751867Sgjelinek 
476*7093Sgjelinek 	if ((dnm = strdup(zonepath)) == NULL) {
477*7093Sgjelinek 		free(bnm);
478*7093Sgjelinek 		return (Z_ERR);
479*7093Sgjelinek 	}
480*7093Sgjelinek 
481*7093Sgjelinek 	bname = basename(bnm);
482*7093Sgjelinek 	dname = dirname(dnm);
483*7093Sgjelinek 
4841867Sgjelinek 	/*
485*7093Sgjelinek 	 * This is a quick test to save iterating over all of the zfs datasets
486*7093Sgjelinek 	 * on the system (which can be a lot).  If the parent dir is not in a
487*7093Sgjelinek 	 * ZFS fs, then we're done.
4881867Sgjelinek 	 */
489*7093Sgjelinek 	if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) ||
490*7093Sgjelinek 	    strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) {
491*7093Sgjelinek 		free(bnm);
492*7093Sgjelinek 		free(dnm);
4931867Sgjelinek 		return (Z_ERR);
494*7093Sgjelinek 	}
4951867Sgjelinek 
496*7093Sgjelinek 	/* See if the parent directory is its own ZFS dataset. */
497*7093Sgjelinek 	if ((zhp = mount2zhandle(dname)) == NULL) {
498*7093Sgjelinek 		/*
499*7093Sgjelinek 		 * The parent is not a ZFS dataset so we can't automatically
500*7093Sgjelinek 		 * create a dataset on the given path.
501*7093Sgjelinek 		 */
502*7093Sgjelinek 		free(bnm);
503*7093Sgjelinek 		free(dnm);
504*7093Sgjelinek 		return (Z_ERR);
505*7093Sgjelinek 	}
5061867Sgjelinek 
507*7093Sgjelinek 	res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname);
508*7093Sgjelinek 
509*7093Sgjelinek 	free(bnm);
510*7093Sgjelinek 	free(dnm);
5111867Sgjelinek 	zfs_close(zhp);
5121867Sgjelinek 	if (res >= len)
5131867Sgjelinek 		return (Z_ERR);
5141867Sgjelinek 
5151867Sgjelinek 	return (Z_OK);
5161867Sgjelinek }
5171867Sgjelinek 
5181867Sgjelinek /*
5191867Sgjelinek  * A ZFS file system iterator call-back function used to determine if the
5201867Sgjelinek  * file system has dependents (snapshots & clones).
5211867Sgjelinek  */
5221867Sgjelinek /* ARGSUSED */
5231867Sgjelinek static int
5241867Sgjelinek has_dependent(zfs_handle_t *zhp, void *data)
5251867Sgjelinek {
5261867Sgjelinek 	zfs_close(zhp);
5271867Sgjelinek 	return (1);
5281867Sgjelinek }
5291867Sgjelinek 
5301867Sgjelinek /*
5311867Sgjelinek  * Given a snapshot name, get the file system path where the snapshot lives.
5321867Sgjelinek  * A snapshot name is of the form fs_name@snap_name.  For example, snapshot
5331867Sgjelinek  * pl/zones/z1@SUNWzone1 would have a path of
5341867Sgjelinek  * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5351867Sgjelinek  */
5361867Sgjelinek static int
5371867Sgjelinek snap2path(char *snap_name, char *path, int len)
5381867Sgjelinek {
5391867Sgjelinek 	char		*p;
5401867Sgjelinek 	zfs_handle_t	*zhp;
5411867Sgjelinek 	char		mp[ZFS_MAXPROPLEN];
5421867Sgjelinek 
5431867Sgjelinek 	if ((p = strrchr(snap_name, '@')) == NULL)
5441867Sgjelinek 		return (Z_ERR);
5451867Sgjelinek 
5461867Sgjelinek 	/* Get the file system name from the snap_name. */
5471867Sgjelinek 	*p = '\0';
5485094Slling 	zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET);
5491867Sgjelinek 	*p = '@';
5501867Sgjelinek 	if (zhp == NULL)
5511867Sgjelinek 		return (Z_ERR);
5521867Sgjelinek 
5531867Sgjelinek 	/* Get the file system mount point. */
5541867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
5552082Seschrock 	    0, B_FALSE) != 0) {
5561867Sgjelinek 		zfs_close(zhp);
5571867Sgjelinek 		return (Z_ERR);
5581867Sgjelinek 	}
5591867Sgjelinek 	zfs_close(zhp);
5601867Sgjelinek 
5611867Sgjelinek 	p++;
5621867Sgjelinek 	if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5631867Sgjelinek 		return (Z_ERR);
5641867Sgjelinek 
5651867Sgjelinek 	return (Z_OK);
5661867Sgjelinek }
5671867Sgjelinek 
5681867Sgjelinek /*
5691867Sgjelinek  * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
5701867Sgjelinek  * possible, or by copying the data from the snapshot to the zonepath.
5711867Sgjelinek  */
5721867Sgjelinek int
5737089Sgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
5741867Sgjelinek {
5751867Sgjelinek 	int	err = Z_OK;
5761867Sgjelinek 	char	clone_name[MAXPATHLEN];
5771867Sgjelinek 	char	snap_path[MAXPATHLEN];
5781867Sgjelinek 
5791867Sgjelinek 	if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
5801867Sgjelinek 		(void) fprintf(stderr, gettext("unable to find path for %s.\n"),
5811867Sgjelinek 		    snap_name);
5821867Sgjelinek 		return (Z_ERR);
5831867Sgjelinek 	}
5841867Sgjelinek 
5857089Sgjelinek 	if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
5861867Sgjelinek 		return (Z_NO_ENTRY);
5871867Sgjelinek 
5881867Sgjelinek 	/*
5891867Sgjelinek 	 * The zonepath cannot be ZFS cloned, try to copy the data from
5901867Sgjelinek 	 * within the snapshot to the zonepath.
5911867Sgjelinek 	 */
5921867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
5931867Sgjelinek 		if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
5941867Sgjelinek 			if (clean_out_clone() != Z_OK)
5951867Sgjelinek 				(void) fprintf(stderr,
5961867Sgjelinek 				    gettext("could not remove the "
5971867Sgjelinek 				    "software inventory from %s\n"), zonepath);
5981867Sgjelinek 
5991867Sgjelinek 		return (err);
6001867Sgjelinek 	}
6011867Sgjelinek 
6021867Sgjelinek 	if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
6031867Sgjelinek 		if (err != Z_NO_ENTRY) {
6041867Sgjelinek 			/*
6051867Sgjelinek 			 * Cloning the snapshot failed.  Fall back to trying
6061867Sgjelinek 			 * to install the zone by copying from the snapshot.
6071867Sgjelinek 			 */
6081867Sgjelinek 			if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
6091867Sgjelinek 				if (clean_out_clone() != Z_OK)
6101867Sgjelinek 					(void) fprintf(stderr,
6111867Sgjelinek 					    gettext("could not remove the "
6121867Sgjelinek 					    "software inventory from %s\n"),
6131867Sgjelinek 					    zonepath);
6141867Sgjelinek 		} else {
6151867Sgjelinek 			/*
6161867Sgjelinek 			 * The snapshot is unusable for some reason so restore
6171867Sgjelinek 			 * the zone state to configured since we were unable to
6181867Sgjelinek 			 * actually do anything about getting the zone
6191867Sgjelinek 			 * installed.
6201867Sgjelinek 			 */
6211867Sgjelinek 			int tmp;
6221867Sgjelinek 
6231867Sgjelinek 			if ((tmp = zone_set_state(target_zone,
6241867Sgjelinek 			    ZONE_STATE_CONFIGURED)) != Z_OK) {
6251867Sgjelinek 				errno = tmp;
6261867Sgjelinek 				zperror2(target_zone,
6271867Sgjelinek 				    gettext("could not set state"));
6281867Sgjelinek 			}
6291867Sgjelinek 		}
6301867Sgjelinek 	}
6311867Sgjelinek 
6321867Sgjelinek 	return (err);
6331867Sgjelinek }
6341867Sgjelinek 
6351867Sgjelinek /*
6361867Sgjelinek  * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
6371867Sgjelinek  */
6381867Sgjelinek int
6397089Sgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
6407089Sgjelinek     char *postsnapbuf)
6411867Sgjelinek {
6421867Sgjelinek 	zfs_handle_t	*zhp;
6431867Sgjelinek 	char		clone_name[MAXPATHLEN];
6441867Sgjelinek 	char		snap_name[MAXPATHLEN];
6451867Sgjelinek 
6461867Sgjelinek 	/*
6471867Sgjelinek 	 * Try to get a zfs handle for the source_zonepath.  If this fails
6481867Sgjelinek 	 * the source_zonepath is not ZFS so return an error.
6491867Sgjelinek 	 */
6501867Sgjelinek 	if ((zhp = mount2zhandle(source_zonepath)) == NULL)
6511867Sgjelinek 		return (Z_ERR);
6521867Sgjelinek 
6531867Sgjelinek 	/*
6541867Sgjelinek 	 * Check if there is a file system already mounted on zonepath.  If so,
6551867Sgjelinek 	 * we can't clone to the path so we should fall back to copying.
6561867Sgjelinek 	 */
6571867Sgjelinek 	if (is_mountpnt(zonepath)) {
6581867Sgjelinek 		zfs_close(zhp);
6591867Sgjelinek 		(void) fprintf(stderr,
6601867Sgjelinek 		    gettext("A file system is already mounted on %s,\n"
6611867Sgjelinek 		    "preventing use of a ZFS clone.\n"), zonepath);
6621867Sgjelinek 		return (Z_ERR);
6631867Sgjelinek 	}
6641867Sgjelinek 
6651867Sgjelinek 	/*
6661867Sgjelinek 	 * Instead of using path2name to get the clone name from the zonepath,
6671867Sgjelinek 	 * we could generate a name from the source zone ZFS name.  However,
6681867Sgjelinek 	 * this would mean we would create the clone under the ZFS fs of the
6691867Sgjelinek 	 * source instead of what the zonepath says.  For example,
6701867Sgjelinek 	 *
6711867Sgjelinek 	 * source_zonepath		zonepath
6721867Sgjelinek 	 * /pl/zones/dev/z1		/pl/zones/deploy/z2
6731867Sgjelinek 	 *
6741867Sgjelinek 	 * We don't want the clone to be under "dev", we want it under
6751867Sgjelinek 	 * "deploy", so that we can leverage the normal attribute inheritance
6761867Sgjelinek 	 * that ZFS provides in the fs hierarchy.
6771867Sgjelinek 	 */
6781867Sgjelinek 	if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
6791867Sgjelinek 		zfs_close(zhp);
6801867Sgjelinek 		return (Z_ERR);
6811867Sgjelinek 	}
6821867Sgjelinek 
6837089Sgjelinek 	if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf,
6847089Sgjelinek 	    postsnapbuf) != Z_OK) {
6851867Sgjelinek 		zfs_close(zhp);
6861867Sgjelinek 		return (Z_ERR);
6871867Sgjelinek 	}
6881867Sgjelinek 	zfs_close(zhp);
6891867Sgjelinek 
6903686Sgjelinek 	if (clone_snap(snap_name, clone_name) != Z_OK) {
6913686Sgjelinek 		/* Clean up the snapshot we just took. */
6923686Sgjelinek 		if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT))
6933686Sgjelinek 		    != NULL) {
6943686Sgjelinek 			if (zfs_unmount(zhp, NULL, 0) == 0)
6953686Sgjelinek 				(void) zfs_destroy(zhp);
6963686Sgjelinek 			zfs_close(zhp);
6973686Sgjelinek 		}
6983686Sgjelinek 
6991867Sgjelinek 		return (Z_ERR);
7003686Sgjelinek 	}
7011867Sgjelinek 
7021867Sgjelinek 	(void) printf(gettext("Instead of copying, a ZFS clone has been "
7031867Sgjelinek 	    "created for this zone.\n"));
7041867Sgjelinek 
7051867Sgjelinek 	return (Z_OK);
7061867Sgjelinek }
7071867Sgjelinek 
7081867Sgjelinek /*
7091867Sgjelinek  * Attempt to create a ZFS file system for the specified zonepath.
7101867Sgjelinek  * We either will successfully create a ZFS file system and get it mounted
7111867Sgjelinek  * on the zonepath or we don't.  The caller doesn't care since a regular
7121867Sgjelinek  * directory is used for the zonepath if no ZFS file system is mounted there.
7131867Sgjelinek  */
7141867Sgjelinek void
7151867Sgjelinek create_zfs_zonepath(char *zonepath)
7161867Sgjelinek {
7171867Sgjelinek 	zfs_handle_t	*zhp;
7181867Sgjelinek 	char		zfs_name[MAXPATHLEN];
7192676Seschrock 	nvlist_t	*props = NULL;
7201867Sgjelinek 
7211867Sgjelinek 	if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
7221867Sgjelinek 		return;
7231867Sgjelinek 
7242676Seschrock 	if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 ||
7252744Snn35248 	    nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
7262744Snn35248 	    "off") != 0) {
7272744Snn35248 		if (props != NULL)
7282744Snn35248 			nvlist_free(props);
7292676Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
7302676Seschrock 		    "out of memory\n"), zfs_name);
7312676Seschrock 	}
7322676Seschrock 
7332676Seschrock 	if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 ||
7345094Slling 	    (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) {
7352082Seschrock 		(void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
7362082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
7372676Seschrock 		nvlist_free(props);
7381867Sgjelinek 		return;
7391867Sgjelinek 	}
7401867Sgjelinek 
7412676Seschrock 	nvlist_free(props);
7422676Seschrock 
7431867Sgjelinek 	if (zfs_mount(zhp, NULL, 0) != 0) {
7442082Seschrock 		(void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: "
7452082Seschrock 		    "%s\n"), zfs_name, libzfs_error_description(g_zfs));
7461867Sgjelinek 		(void) zfs_destroy(zhp);
7471867Sgjelinek 	} else {
7481867Sgjelinek 		if (chmod(zonepath, S_IRWXU) != 0) {
7491867Sgjelinek 			(void) fprintf(stderr, gettext("file system %s "
7501867Sgjelinek 			    "successfully created, but chmod %o failed: %s\n"),
7511867Sgjelinek 			    zfs_name, S_IRWXU, strerror(errno));
7521867Sgjelinek 			(void) destroy_zfs(zonepath);
7531867Sgjelinek 		} else {
7541867Sgjelinek 			(void) printf(gettext("A ZFS file system has been "
7551867Sgjelinek 			    "created for this zone.\n"));
7561867Sgjelinek 		}
7571867Sgjelinek 	}
7581867Sgjelinek 
7591867Sgjelinek 	zfs_close(zhp);
7601867Sgjelinek }
7611867Sgjelinek 
7621867Sgjelinek /*
7631867Sgjelinek  * If the zonepath is a ZFS file system, attempt to destroy it.  We return Z_OK
7641867Sgjelinek  * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
7651867Sgjelinek  * which means the caller should clean up the zonepath in the traditional
7661867Sgjelinek  * way.
7671867Sgjelinek  */
7681867Sgjelinek int
7691867Sgjelinek destroy_zfs(char *zonepath)
7701867Sgjelinek {
7711867Sgjelinek 	zfs_handle_t	*zhp;
7721867Sgjelinek 	boolean_t	is_clone = B_FALSE;
7731867Sgjelinek 	char		origin[ZFS_MAXPROPLEN];
7741867Sgjelinek 
7752082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
7761867Sgjelinek 		return (Z_ERR);
7771867Sgjelinek 
7781867Sgjelinek 	/*
7791867Sgjelinek 	 * We can't destroy the file system if it has dependents.
7801867Sgjelinek 	 */
7812474Seschrock 	if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0 ||
7821867Sgjelinek 	    zfs_unmount(zhp, NULL, 0) != 0) {
7831867Sgjelinek 		zfs_close(zhp);
7841867Sgjelinek 		return (Z_ERR);
7851867Sgjelinek 	}
7861867Sgjelinek 
7871867Sgjelinek 	/*
7881867Sgjelinek 	 * This might be a clone.  Try to get the snapshot so we can attempt
7891867Sgjelinek 	 * to destroy that as well.
7901867Sgjelinek 	 */
7911867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
7922082Seschrock 	    NULL, 0, B_FALSE) == 0)
7931867Sgjelinek 		is_clone = B_TRUE;
7941867Sgjelinek 
7951867Sgjelinek 	if (zfs_destroy(zhp) != 0) {
7961867Sgjelinek 		/*
7971867Sgjelinek 		 * If the destroy fails for some reason, try to remount
7981867Sgjelinek 		 * the file system so that we can use "rm -rf" to clean up
7991867Sgjelinek 		 * instead.
8001867Sgjelinek 		 */
8011867Sgjelinek 		(void) zfs_mount(zhp, NULL, 0);
8021867Sgjelinek 		zfs_close(zhp);
8031867Sgjelinek 		return (Z_ERR);
8041867Sgjelinek 	}
8051867Sgjelinek 
8063686Sgjelinek 	/*
8073686Sgjelinek 	 * If the zone has ever been moved then the mountpoint dir will not be
8083686Sgjelinek 	 * cleaned up by the zfs_destroy().  To handle this case try to clean
8093686Sgjelinek 	 * it up now but don't worry if it fails, that will be normal.
8103686Sgjelinek 	 */
8113686Sgjelinek 	(void) rmdir(zonepath);
8123686Sgjelinek 
8131867Sgjelinek 	(void) printf(gettext("The ZFS file system for this zone has been "
8141867Sgjelinek 	    "destroyed.\n"));
8151867Sgjelinek 
8161867Sgjelinek 	if (is_clone) {
8171867Sgjelinek 		zfs_handle_t	*ohp;
8181867Sgjelinek 
8191867Sgjelinek 		/*
8201867Sgjelinek 		 * Try to clean up the snapshot that the clone was taken from.
8211867Sgjelinek 		 */
8222082Seschrock 		if ((ohp = zfs_open(g_zfs, origin,
8232082Seschrock 		    ZFS_TYPE_SNAPSHOT)) != NULL) {
8242474Seschrock 			if (zfs_iter_dependents(ohp, B_TRUE, has_dependent,
8252474Seschrock 			    NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0)
8261867Sgjelinek 				(void) zfs_destroy(ohp);
8271867Sgjelinek 			zfs_close(ohp);
8281867Sgjelinek 		}
8291867Sgjelinek 	}
8301867Sgjelinek 
8311867Sgjelinek 	zfs_close(zhp);
8321867Sgjelinek 	return (Z_OK);
8331867Sgjelinek }
8341867Sgjelinek 
8351867Sgjelinek /*
8361867Sgjelinek  * Return true if the path is its own zfs file system.  We determine this
8371867Sgjelinek  * by stat-ing the path to see if it is zfs and stat-ing the parent to see
8381867Sgjelinek  * if it is a different fs.
8391867Sgjelinek  */
8401867Sgjelinek boolean_t
8411867Sgjelinek is_zonepath_zfs(char *zonepath)
8421867Sgjelinek {
8431867Sgjelinek 	int res;
8441867Sgjelinek 	char *path;
8451867Sgjelinek 	char *parent;
8462267Sdp 	struct statvfs64 buf1, buf2;
8471867Sgjelinek 
8482267Sdp 	if (statvfs64(zonepath, &buf1) != 0)
8491867Sgjelinek 		return (B_FALSE);
8501867Sgjelinek 
8511867Sgjelinek 	if (strcmp(buf1.f_basetype, "zfs") != 0)
8521867Sgjelinek 		return (B_FALSE);
8531867Sgjelinek 
8541867Sgjelinek 	if ((path = strdup(zonepath)) == NULL)
8551867Sgjelinek 		return (B_FALSE);
8561867Sgjelinek 
8571867Sgjelinek 	parent = dirname(path);
8582267Sdp 	res = statvfs64(parent, &buf2);
8591867Sgjelinek 	free(path);
8601867Sgjelinek 
8611867Sgjelinek 	if (res != 0)
8621867Sgjelinek 		return (B_FALSE);
8631867Sgjelinek 
8641867Sgjelinek 	if (buf1.f_fsid == buf2.f_fsid)
8651867Sgjelinek 		return (B_FALSE);
8661867Sgjelinek 
8671867Sgjelinek 	return (B_TRUE);
8681867Sgjelinek }
8691867Sgjelinek 
8701867Sgjelinek /*
8711867Sgjelinek  * Implement the fast move of a ZFS file system by simply updating the
8721867Sgjelinek  * mountpoint.  Since it is file system already, we don't have the
8731867Sgjelinek  * issue of cross-file system copying.
8741867Sgjelinek  */
8751867Sgjelinek int
8761867Sgjelinek move_zfs(char *zonepath, char *new_zonepath)
8771867Sgjelinek {
8781867Sgjelinek 	int		ret = Z_ERR;
8791867Sgjelinek 	zfs_handle_t	*zhp;
8801867Sgjelinek 
8812082Seschrock 	if ((zhp = mount2zhandle(zonepath)) == NULL)
8821867Sgjelinek 		return (Z_ERR);
8831867Sgjelinek 
8842676Seschrock 	if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
8852676Seschrock 	    new_zonepath) == 0) {
8861867Sgjelinek 		/*
8871867Sgjelinek 		 * Clean up the old mount point.  We ignore any failure since
8881867Sgjelinek 		 * the zone is already successfully mounted on the new path.
8891867Sgjelinek 		 */
8901867Sgjelinek 		(void) rmdir(zonepath);
8911867Sgjelinek 		ret = Z_OK;
8921867Sgjelinek 	}
8931867Sgjelinek 
8941867Sgjelinek 	zfs_close(zhp);
8951867Sgjelinek 
8961867Sgjelinek 	return (ret);
8971867Sgjelinek }
8981867Sgjelinek 
8991867Sgjelinek /*
9001867Sgjelinek  * Validate that the given dataset exists on the system, and that neither it nor
9011867Sgjelinek  * its children are zvols.
9021867Sgjelinek  *
9031867Sgjelinek  * Note that we don't do anything with the 'zoned' property here.  All
9041867Sgjelinek  * management is done in zoneadmd when the zone is actually rebooted.  This
9051867Sgjelinek  * allows us to automatically set the zoned property even when a zone is
9061867Sgjelinek  * rebooted by the administrator.
9071867Sgjelinek  */
9081867Sgjelinek int
9091867Sgjelinek verify_datasets(zone_dochandle_t handle)
9101867Sgjelinek {
9111867Sgjelinek 	int return_code = Z_OK;
9121867Sgjelinek 	struct zone_dstab dstab;
9131867Sgjelinek 	zfs_handle_t *zhp;
9141867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
9151867Sgjelinek 	char source[ZFS_MAXNAMELEN];
9165094Slling 	zprop_source_t srctype;
9171867Sgjelinek 
9181867Sgjelinek 	if (zonecfg_setdsent(handle) != Z_OK) {
9191867Sgjelinek 		/*
9201867Sgjelinek 		 * TRANSLATION_NOTE
9211867Sgjelinek 		 * zfs and dataset are literals that should not be translated.
9221867Sgjelinek 		 */
9231867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify zfs datasets: "
9241867Sgjelinek 		    "unable to enumerate datasets\n"));
9251867Sgjelinek 		return (Z_ERR);
9261867Sgjelinek 	}
9271867Sgjelinek 
9281867Sgjelinek 	while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
9291867Sgjelinek 
9302082Seschrock 		if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name,
9311867Sgjelinek 		    ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
9322082Seschrock 			(void) fprintf(stderr, gettext("could not verify zfs "
9332082Seschrock 			    "dataset %s: %s\n"), dstab.zone_dataset_name,
9342082Seschrock 			    libzfs_error_description(g_zfs));
9351867Sgjelinek 			return_code = Z_ERR;
9361867Sgjelinek 			continue;
9371867Sgjelinek 		}
9381867Sgjelinek 
9391867Sgjelinek 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf,
9401867Sgjelinek 		    sizeof (propbuf), &srctype, source,
9411867Sgjelinek 		    sizeof (source), 0) == 0 &&
9425094Slling 		    (srctype == ZPROP_SRC_INHERITED)) {
9431867Sgjelinek 			(void) fprintf(stderr, gettext("could not verify zfs "
9441867Sgjelinek 			    "dataset %s: mountpoint cannot be inherited\n"),
9451867Sgjelinek 			    dstab.zone_dataset_name);
9461867Sgjelinek 			return_code = Z_ERR;
9471867Sgjelinek 			zfs_close(zhp);
9481867Sgjelinek 			continue;
9491867Sgjelinek 		}
9501867Sgjelinek 
9511867Sgjelinek 		if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) {
9521867Sgjelinek 			(void) fprintf(stderr, gettext("cannot verify zfs "
9531867Sgjelinek 			    "dataset %s: volumes cannot be specified as a "
9541867Sgjelinek 			    "zone dataset resource\n"),
9551867Sgjelinek 			    dstab.zone_dataset_name);
9561867Sgjelinek 			return_code = Z_ERR;
9571867Sgjelinek 		}
9581867Sgjelinek 
9591867Sgjelinek 		if (zfs_iter_children(zhp, check_zvol, NULL) != 0)
9601867Sgjelinek 			return_code = Z_ERR;
9611867Sgjelinek 
9621867Sgjelinek 		zfs_close(zhp);
9631867Sgjelinek 	}
9641867Sgjelinek 	(void) zonecfg_enddsent(handle);
9651867Sgjelinek 
9661867Sgjelinek 	return (return_code);
9671867Sgjelinek }
9681867Sgjelinek 
9691867Sgjelinek /*
9701867Sgjelinek  * Verify that the ZFS dataset exists, and its mountpoint
9711867Sgjelinek  * property is set to "legacy".
9721867Sgjelinek  */
9731867Sgjelinek int
9741867Sgjelinek verify_fs_zfs(struct zone_fstab *fstab)
9751867Sgjelinek {
9761867Sgjelinek 	zfs_handle_t *zhp;
9771867Sgjelinek 	char propbuf[ZFS_MAXPROPLEN];
9781867Sgjelinek 
9792082Seschrock 	if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special,
9805094Slling 	    ZFS_TYPE_DATASET)) == NULL) {
9811867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
9821867Sgjelinek 		    "could not access zfs dataset '%s'\n"),
9831867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
9841867Sgjelinek 		return (Z_ERR);
9851867Sgjelinek 	}
9861867Sgjelinek 
9871867Sgjelinek 	if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
9881867Sgjelinek 		(void) fprintf(stderr, gettext("cannot verify fs %s: "
9891867Sgjelinek 		    "'%s' is not a file system\n"),
9901867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
9911867Sgjelinek 		zfs_close(zhp);
9921867Sgjelinek 		return (Z_ERR);
9931867Sgjelinek 	}
9941867Sgjelinek 
9951867Sgjelinek 	if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
9961867Sgjelinek 	    NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
9971867Sgjelinek 		(void) fprintf(stderr, gettext("could not verify fs %s: "
9981867Sgjelinek 		    "zfs '%s' mountpoint is not \"legacy\"\n"),
9991867Sgjelinek 		    fstab->zone_fs_dir, fstab->zone_fs_special);
10001867Sgjelinek 		zfs_close(zhp);
10011867Sgjelinek 		return (Z_ERR);
10021867Sgjelinek 	}
10031867Sgjelinek 
10041867Sgjelinek 	zfs_close(zhp);
10051867Sgjelinek 	return (Z_OK);
10061867Sgjelinek }
10072082Seschrock 
10082082Seschrock int
10092082Seschrock init_zfs(void)
10102082Seschrock {
10112082Seschrock 	if ((g_zfs = libzfs_init()) == NULL) {
10122082Seschrock 		(void) fprintf(stderr, gettext("failed to initialize ZFS "
10132082Seschrock 		    "library\n"));
10142082Seschrock 		return (Z_ERR);
10152082Seschrock 	}
10162082Seschrock 
10172082Seschrock 	return (Z_OK);
10182082Seschrock }
1019