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