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*7089Sgjelinek * 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> 471867Sgjelinek 481867Sgjelinek #include "zoneadm.h" 491867Sgjelinek 502082Seschrock libzfs_handle_t *g_zfs; 511867Sgjelinek 521867Sgjelinek typedef struct zfs_mount_data { 531867Sgjelinek char *match_name; 541867Sgjelinek zfs_handle_t *match_handle; 551867Sgjelinek } zfs_mount_data_t; 561867Sgjelinek 571867Sgjelinek typedef struct zfs_snapshot_data { 581867Sgjelinek char *match_name; 591867Sgjelinek int len; 601867Sgjelinek int max; 611867Sgjelinek } zfs_snapshot_data_t; 621867Sgjelinek 631867Sgjelinek /* 641867Sgjelinek * A ZFS file system iterator call-back function which is used to validate 651867Sgjelinek * datasets imported into the zone. 661867Sgjelinek */ 671867Sgjelinek /* ARGSUSED */ 681867Sgjelinek static int 691867Sgjelinek check_zvol(zfs_handle_t *zhp, void *unused) 701867Sgjelinek { 711867Sgjelinek int ret; 721867Sgjelinek 731867Sgjelinek if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 741867Sgjelinek /* 751867Sgjelinek * TRANSLATION_NOTE 761867Sgjelinek * zfs and dataset are literals that should not be translated. 771867Sgjelinek */ 781867Sgjelinek (void) fprintf(stderr, gettext("cannot verify zfs dataset %s: " 791867Sgjelinek "volumes cannot be specified as a zone dataset resource\n"), 801867Sgjelinek zfs_get_name(zhp)); 811867Sgjelinek ret = -1; 821867Sgjelinek } else { 831867Sgjelinek ret = zfs_iter_children(zhp, check_zvol, NULL); 841867Sgjelinek } 851867Sgjelinek 861867Sgjelinek zfs_close(zhp); 871867Sgjelinek 881867Sgjelinek return (ret); 891867Sgjelinek } 901867Sgjelinek 911867Sgjelinek /* 921867Sgjelinek * A ZFS file system iterator call-back function which returns the 931867Sgjelinek * zfs_handle_t for a ZFS file system on the specified mount point. 941867Sgjelinek */ 951867Sgjelinek static int 961867Sgjelinek match_mountpoint(zfs_handle_t *zhp, void *data) 971867Sgjelinek { 981867Sgjelinek int res; 991867Sgjelinek zfs_mount_data_t *cbp; 1001867Sgjelinek char mp[ZFS_MAXPROPLEN]; 1011867Sgjelinek 1021867Sgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 1031867Sgjelinek zfs_close(zhp); 1041867Sgjelinek return (0); 1051867Sgjelinek } 1061867Sgjelinek 1071867Sgjelinek cbp = (zfs_mount_data_t *)data; 1081867Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 1092082Seschrock 0, B_FALSE) == 0 && strcmp(mp, cbp->match_name) == 0) { 1101867Sgjelinek cbp->match_handle = zhp; 1111867Sgjelinek return (1); 1121867Sgjelinek } 1131867Sgjelinek 1141867Sgjelinek res = zfs_iter_filesystems(zhp, match_mountpoint, data); 1151867Sgjelinek zfs_close(zhp); 1161867Sgjelinek return (res); 1171867Sgjelinek } 1181867Sgjelinek 1191867Sgjelinek /* 1201867Sgjelinek * Get ZFS handle for the specified mount point. 1211867Sgjelinek */ 1221867Sgjelinek static zfs_handle_t * 1231867Sgjelinek mount2zhandle(char *mountpoint) 1241867Sgjelinek { 1251867Sgjelinek zfs_mount_data_t cb; 1261867Sgjelinek 1271867Sgjelinek cb.match_name = mountpoint; 1281867Sgjelinek cb.match_handle = NULL; 1292082Seschrock (void) zfs_iter_root(g_zfs, match_mountpoint, &cb); 1301867Sgjelinek return (cb.match_handle); 1311867Sgjelinek } 1321867Sgjelinek 1331867Sgjelinek /* 1341867Sgjelinek * Check if there is already a file system (zfs or any other type) mounted on 1351867Sgjelinek * path. 1361867Sgjelinek */ 1371867Sgjelinek static boolean_t 1381867Sgjelinek is_mountpnt(char *path) 1391867Sgjelinek { 1401867Sgjelinek FILE *fp; 1411867Sgjelinek struct mnttab entry; 1421867Sgjelinek 1431867Sgjelinek if ((fp = fopen("/etc/mnttab", "r")) == NULL) 1441867Sgjelinek return (B_FALSE); 1451867Sgjelinek 1461867Sgjelinek while (getmntent(fp, &entry) == 0) { 1471867Sgjelinek if (strcmp(path, entry.mnt_mountp) == 0) { 1481867Sgjelinek (void) fclose(fp); 1491867Sgjelinek return (B_TRUE); 1501867Sgjelinek } 1511867Sgjelinek } 1521867Sgjelinek 1531867Sgjelinek (void) fclose(fp); 1541867Sgjelinek return (B_FALSE); 1551867Sgjelinek } 1561867Sgjelinek 1571867Sgjelinek /* 158*7089Sgjelinek * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone. 1591867Sgjelinek */ 1601867Sgjelinek static int 161*7089Sgjelinek pre_snapshot(char *presnapbuf) 1621867Sgjelinek { 163*7089Sgjelinek int status; 1641867Sgjelinek 165*7089Sgjelinek /* No brand-specific handler */ 166*7089Sgjelinek if (presnapbuf[0] == '\0') 167*7089Sgjelinek return (Z_OK); 1681867Sgjelinek 169*7089Sgjelinek /* Run the hook */ 170*7089Sgjelinek status = do_subproc_interactive(presnapbuf); 171*7089Sgjelinek if ((status = subproc_status(gettext("brand-specific presnapshot"), 172*7089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) 1731867Sgjelinek return (Z_ERR); 1741867Sgjelinek 1751867Sgjelinek return (Z_OK); 1761867Sgjelinek } 1771867Sgjelinek 1781867Sgjelinek /* 179*7089Sgjelinek * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone. 1801867Sgjelinek */ 1811867Sgjelinek static int 182*7089Sgjelinek post_snapshot(char *postsnapbuf) 1831867Sgjelinek { 184*7089Sgjelinek int status; 1851867Sgjelinek 186*7089Sgjelinek /* No brand-specific handler */ 187*7089Sgjelinek if (postsnapbuf[0] == '\0') 188*7089Sgjelinek return (Z_OK); 189*7089Sgjelinek 190*7089Sgjelinek /* Run the hook */ 191*7089Sgjelinek status = do_subproc_interactive(postsnapbuf); 192*7089Sgjelinek if ((status = subproc_status(gettext("brand-specific postsnapshot"), 193*7089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) 1941867Sgjelinek return (Z_ERR); 1951867Sgjelinek 1961867Sgjelinek return (Z_OK); 1971867Sgjelinek } 1981867Sgjelinek 1991867Sgjelinek /* 2001867Sgjelinek * This is a ZFS snapshot iterator call-back function which returns the 2011867Sgjelinek * highest number of SUNWzone snapshots that have been taken. 2021867Sgjelinek */ 2031867Sgjelinek static int 2041867Sgjelinek get_snap_max(zfs_handle_t *zhp, void *data) 2051867Sgjelinek { 2061867Sgjelinek int res; 2071867Sgjelinek zfs_snapshot_data_t *cbp; 2081867Sgjelinek 2091867Sgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { 2101867Sgjelinek zfs_close(zhp); 2111867Sgjelinek return (0); 2121867Sgjelinek } 2131867Sgjelinek 2141867Sgjelinek cbp = (zfs_snapshot_data_t *)data; 2151867Sgjelinek 2161867Sgjelinek if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) { 2171867Sgjelinek char *nump; 2181867Sgjelinek int num; 2191867Sgjelinek 2201867Sgjelinek nump = (char *)(zfs_get_name(zhp) + cbp->len); 2211867Sgjelinek num = atoi(nump); 2221867Sgjelinek if (num > cbp->max) 2231867Sgjelinek cbp->max = num; 2241867Sgjelinek } 2251867Sgjelinek 2261867Sgjelinek res = zfs_iter_snapshots(zhp, get_snap_max, data); 2271867Sgjelinek zfs_close(zhp); 2281867Sgjelinek return (res); 2291867Sgjelinek } 2301867Sgjelinek 2311867Sgjelinek /* 2321867Sgjelinek * Take a ZFS snapshot to be used for cloning the zone. 2331867Sgjelinek */ 2341867Sgjelinek static int 235*7089Sgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size, 236*7089Sgjelinek char *presnapbuf, char *postsnapbuf) 2371867Sgjelinek { 2381867Sgjelinek int res; 2391867Sgjelinek char template[ZFS_MAXNAMELEN]; 2401867Sgjelinek zfs_snapshot_data_t cb; 2411867Sgjelinek 2421867Sgjelinek /* 2431867Sgjelinek * First we need to figure out the next available name for the 2441867Sgjelinek * zone snapshot. Look through the list of zones snapshots for 2451867Sgjelinek * this file system to determine the maximum snapshot name. 2461867Sgjelinek */ 2471867Sgjelinek if (snprintf(template, sizeof (template), "%s@SUNWzone", 2481867Sgjelinek zfs_get_name(zhp)) >= sizeof (template)) 2491867Sgjelinek return (Z_ERR); 2501867Sgjelinek 2511867Sgjelinek cb.match_name = template; 2521867Sgjelinek cb.len = strlen(template); 2531867Sgjelinek cb.max = 0; 2541867Sgjelinek 2551867Sgjelinek if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0) 2561867Sgjelinek return (Z_ERR); 2571867Sgjelinek 2581867Sgjelinek cb.max++; 2591867Sgjelinek 2601867Sgjelinek if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d", 2611867Sgjelinek zfs_get_name(zhp), cb.max) >= snap_size) 2621867Sgjelinek return (Z_ERR); 2631867Sgjelinek 264*7089Sgjelinek if (pre_snapshot(presnapbuf) != Z_OK) 2651867Sgjelinek return (Z_ERR); 2662199Sahrens res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE); 267*7089Sgjelinek if (post_snapshot(postsnapbuf) != Z_OK) 2681867Sgjelinek return (Z_ERR); 2691867Sgjelinek 2701867Sgjelinek if (res != 0) 2711867Sgjelinek return (Z_ERR); 2721867Sgjelinek return (Z_OK); 2731867Sgjelinek } 2741867Sgjelinek 2751867Sgjelinek /* 2761867Sgjelinek * We are using an explicit snapshot from some earlier point in time so 277*7089Sgjelinek * we need to validate it. Run the brand specific hook. 2781867Sgjelinek */ 2791867Sgjelinek static int 280*7089Sgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf) 2811867Sgjelinek { 282*7089Sgjelinek int status; 283*7089Sgjelinek char cmdbuf[MAXPATHLEN]; 2841867Sgjelinek 285*7089Sgjelinek /* No brand-specific handler */ 286*7089Sgjelinek if (validsnapbuf[0] == '\0') 287*7089Sgjelinek return (Z_OK); 2881867Sgjelinek 289*7089Sgjelinek /* pass args - snapshot_name & snap_path */ 290*7089Sgjelinek if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf, 291*7089Sgjelinek snapshot_name, snap_path) >= sizeof (cmdbuf)) { 292*7089Sgjelinek zerror("Command line too long"); 2931867Sgjelinek return (Z_ERR); 2941867Sgjelinek } 2951867Sgjelinek 296*7089Sgjelinek /* Run the hook */ 297*7089Sgjelinek status = do_subproc_interactive(cmdbuf); 298*7089Sgjelinek if ((status = subproc_status(gettext("brand-specific validatesnapshot"), 299*7089Sgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) 300*7089Sgjelinek return (Z_ERR); 3011867Sgjelinek 302*7089Sgjelinek return (Z_OK); 3031867Sgjelinek } 3041867Sgjelinek 3051867Sgjelinek /* 3061867Sgjelinek * Remove the sw inventory file from inside this zonepath that we picked up out 3071867Sgjelinek * of the snapshot. 3081867Sgjelinek */ 3091867Sgjelinek static int 3101867Sgjelinek clean_out_clone() 3111867Sgjelinek { 3121867Sgjelinek int err; 3131867Sgjelinek zone_dochandle_t handle; 3141867Sgjelinek 3151867Sgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 3161867Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 3171867Sgjelinek return (Z_ERR); 3181867Sgjelinek } 3191867Sgjelinek 3201867Sgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 3211867Sgjelinek errno = err; 3221867Sgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 3231867Sgjelinek zonecfg_fini_handle(handle); 3241867Sgjelinek return (Z_ERR); 3251867Sgjelinek } 3261867Sgjelinek 3271867Sgjelinek zonecfg_rm_detached(handle, B_FALSE); 3281867Sgjelinek zonecfg_fini_handle(handle); 3291867Sgjelinek 3301867Sgjelinek return (Z_OK); 3311867Sgjelinek } 3321867Sgjelinek 3331867Sgjelinek /* 3341867Sgjelinek * Make a ZFS clone on zonepath from snapshot_name. 3351867Sgjelinek */ 3361867Sgjelinek static int 3371867Sgjelinek clone_snap(char *snapshot_name, char *zonepath) 3381867Sgjelinek { 3391867Sgjelinek int res = Z_OK; 3401867Sgjelinek int err; 3411867Sgjelinek zfs_handle_t *zhp; 3421867Sgjelinek zfs_handle_t *clone; 3432676Seschrock nvlist_t *props = NULL; 3441867Sgjelinek 3452082Seschrock if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL) 3461867Sgjelinek return (Z_NO_ENTRY); 3471867Sgjelinek 3481867Sgjelinek (void) printf(gettext("Cloning snapshot %s\n"), snapshot_name); 3491867Sgjelinek 3502676Seschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 || 3512744Snn35248 nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS), 3522744Snn35248 "off") != 0) { 3532744Snn35248 if (props != NULL) 3542744Snn35248 nvlist_free(props); 3552676Seschrock (void) fprintf(stderr, gettext("could not create ZFS clone " 3562676Seschrock "%s: out of memory\n"), zonepath); 3572676Seschrock return (Z_ERR); 3582676Seschrock } 3592676Seschrock 3602676Seschrock err = zfs_clone(zhp, zonepath, props); 3611867Sgjelinek zfs_close(zhp); 3622676Seschrock 3632676Seschrock nvlist_free(props); 3642676Seschrock 3651867Sgjelinek if (err != 0) 3661867Sgjelinek return (Z_ERR); 3671867Sgjelinek 3681867Sgjelinek /* create the mountpoint if necessary */ 3695094Slling if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL) 3701867Sgjelinek return (Z_ERR); 3711867Sgjelinek 3721867Sgjelinek /* 3731867Sgjelinek * The clone has been created so we need to print a diagnostic 3741867Sgjelinek * message if one of the following steps fails for some reason. 3751867Sgjelinek */ 3761867Sgjelinek if (zfs_mount(clone, NULL, 0) != 0) { 3771867Sgjelinek (void) fprintf(stderr, gettext("could not mount ZFS clone " 3781867Sgjelinek "%s\n"), zfs_get_name(clone)); 3791867Sgjelinek res = Z_ERR; 3801867Sgjelinek 3812676Seschrock } else if (clean_out_clone() != Z_OK) { 3822676Seschrock (void) fprintf(stderr, gettext("could not remove the " 3832676Seschrock "software inventory from ZFS clone %s\n"), 3842676Seschrock zfs_get_name(clone)); 3852676Seschrock res = Z_ERR; 3861867Sgjelinek } 3871867Sgjelinek 3881867Sgjelinek zfs_close(clone); 3891867Sgjelinek return (res); 3901867Sgjelinek } 3911867Sgjelinek 3921867Sgjelinek /* 3931867Sgjelinek * This function takes a zonepath and attempts to determine what the ZFS 3941867Sgjelinek * file system name (not mountpoint) should be for that path. We do not 3951867Sgjelinek * assume that zonepath is an existing directory or ZFS fs since we use 3961867Sgjelinek * this function as part of the process of creating a new ZFS fs or clone. 3971867Sgjelinek * 3981867Sgjelinek * The way this works is that we look at the parent directory of the zonepath 3991867Sgjelinek * to see if it is a ZFS fs. If it is, we get the name of that ZFS fs and 4001867Sgjelinek * append the last component of the zonepath to generate the ZFS name for the 4011867Sgjelinek * zonepath. This matches the algorithm that ZFS uses for automatically 4021867Sgjelinek * mounting a new fs after it is created. 4031867Sgjelinek * 4041867Sgjelinek * Although a ZFS fs can be mounted anywhere, we don't worry about handling 4051867Sgjelinek * all of the complexity that a user could possibly configure with arbitrary 4061867Sgjelinek * mounts since there is no way to generate a ZFS name from a random path in 4071867Sgjelinek * the file system. We only try to handle the automatic mounts that ZFS does 4081867Sgjelinek * for each file system. ZFS restricts this so that a new fs must be created 4091867Sgjelinek * in an existing parent ZFS fs. It then automatically mounts the new fs 4101867Sgjelinek * directly under the mountpoint for the parent fs using the last component 4111867Sgjelinek * of the name as the mountpoint directory. 4121867Sgjelinek * 4131867Sgjelinek * For example: 4141867Sgjelinek * Name Mountpoint 4151867Sgjelinek * space/eng/dev/test/zone1 /project1/eng/dev/test/zone1 4161867Sgjelinek * 4171867Sgjelinek * Return Z_OK if the path mapped to a ZFS file system name, otherwise return 4181867Sgjelinek * Z_ERR. 4191867Sgjelinek */ 4201867Sgjelinek static int 4211867Sgjelinek path2name(char *zonepath, char *zfs_name, int len) 4221867Sgjelinek { 4231867Sgjelinek int res; 4241867Sgjelinek char *p; 4251867Sgjelinek zfs_handle_t *zhp; 4261867Sgjelinek 4271867Sgjelinek if ((p = strrchr(zonepath, '/')) == NULL) 4281867Sgjelinek return (Z_ERR); 4291867Sgjelinek 4301867Sgjelinek /* 4311867Sgjelinek * If the parent directory is not its own ZFS fs, then we can't 4321867Sgjelinek * automatically create a new ZFS fs at the 'zonepath' mountpoint 4331867Sgjelinek * so return an error. 4341867Sgjelinek */ 4351867Sgjelinek *p = '\0'; 4361867Sgjelinek zhp = mount2zhandle(zonepath); 4371867Sgjelinek *p = '/'; 4381867Sgjelinek if (zhp == NULL) 4391867Sgjelinek return (Z_ERR); 4401867Sgjelinek 4411867Sgjelinek res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), p + 1); 4421867Sgjelinek 4431867Sgjelinek zfs_close(zhp); 4441867Sgjelinek if (res >= len) 4451867Sgjelinek return (Z_ERR); 4461867Sgjelinek 4471867Sgjelinek return (Z_OK); 4481867Sgjelinek } 4491867Sgjelinek 4501867Sgjelinek /* 4511867Sgjelinek * A ZFS file system iterator call-back function used to determine if the 4521867Sgjelinek * file system has dependents (snapshots & clones). 4531867Sgjelinek */ 4541867Sgjelinek /* ARGSUSED */ 4551867Sgjelinek static int 4561867Sgjelinek has_dependent(zfs_handle_t *zhp, void *data) 4571867Sgjelinek { 4581867Sgjelinek zfs_close(zhp); 4591867Sgjelinek return (1); 4601867Sgjelinek } 4611867Sgjelinek 4621867Sgjelinek /* 4631867Sgjelinek * Given a snapshot name, get the file system path where the snapshot lives. 4641867Sgjelinek * A snapshot name is of the form fs_name@snap_name. For example, snapshot 4651867Sgjelinek * pl/zones/z1@SUNWzone1 would have a path of 4661867Sgjelinek * /pl/zones/z1/.zfs/snapshot/SUNWzone1. 4671867Sgjelinek */ 4681867Sgjelinek static int 4691867Sgjelinek snap2path(char *snap_name, char *path, int len) 4701867Sgjelinek { 4711867Sgjelinek char *p; 4721867Sgjelinek zfs_handle_t *zhp; 4731867Sgjelinek char mp[ZFS_MAXPROPLEN]; 4741867Sgjelinek 4751867Sgjelinek if ((p = strrchr(snap_name, '@')) == NULL) 4761867Sgjelinek return (Z_ERR); 4771867Sgjelinek 4781867Sgjelinek /* Get the file system name from the snap_name. */ 4791867Sgjelinek *p = '\0'; 4805094Slling zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET); 4811867Sgjelinek *p = '@'; 4821867Sgjelinek if (zhp == NULL) 4831867Sgjelinek return (Z_ERR); 4841867Sgjelinek 4851867Sgjelinek /* Get the file system mount point. */ 4861867Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 4872082Seschrock 0, B_FALSE) != 0) { 4881867Sgjelinek zfs_close(zhp); 4891867Sgjelinek return (Z_ERR); 4901867Sgjelinek } 4911867Sgjelinek zfs_close(zhp); 4921867Sgjelinek 4931867Sgjelinek p++; 4941867Sgjelinek if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len) 4951867Sgjelinek return (Z_ERR); 4961867Sgjelinek 4971867Sgjelinek return (Z_OK); 4981867Sgjelinek } 4991867Sgjelinek 5001867Sgjelinek /* 5011867Sgjelinek * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if 5021867Sgjelinek * possible, or by copying the data from the snapshot to the zonepath. 5031867Sgjelinek */ 5041867Sgjelinek int 505*7089Sgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap) 5061867Sgjelinek { 5071867Sgjelinek int err = Z_OK; 5081867Sgjelinek char clone_name[MAXPATHLEN]; 5091867Sgjelinek char snap_path[MAXPATHLEN]; 5101867Sgjelinek 5111867Sgjelinek if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) { 5121867Sgjelinek (void) fprintf(stderr, gettext("unable to find path for %s.\n"), 5131867Sgjelinek snap_name); 5141867Sgjelinek return (Z_ERR); 5151867Sgjelinek } 5161867Sgjelinek 517*7089Sgjelinek if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK) 5181867Sgjelinek return (Z_NO_ENTRY); 5191867Sgjelinek 5201867Sgjelinek /* 5211867Sgjelinek * The zonepath cannot be ZFS cloned, try to copy the data from 5221867Sgjelinek * within the snapshot to the zonepath. 5231867Sgjelinek */ 5241867Sgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) { 5251867Sgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK) 5261867Sgjelinek if (clean_out_clone() != Z_OK) 5271867Sgjelinek (void) fprintf(stderr, 5281867Sgjelinek gettext("could not remove the " 5291867Sgjelinek "software inventory from %s\n"), zonepath); 5301867Sgjelinek 5311867Sgjelinek return (err); 5321867Sgjelinek } 5331867Sgjelinek 5341867Sgjelinek if ((err = clone_snap(snap_name, clone_name)) != Z_OK) { 5351867Sgjelinek if (err != Z_NO_ENTRY) { 5361867Sgjelinek /* 5371867Sgjelinek * Cloning the snapshot failed. Fall back to trying 5381867Sgjelinek * to install the zone by copying from the snapshot. 5391867Sgjelinek */ 5401867Sgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK) 5411867Sgjelinek if (clean_out_clone() != Z_OK) 5421867Sgjelinek (void) fprintf(stderr, 5431867Sgjelinek gettext("could not remove the " 5441867Sgjelinek "software inventory from %s\n"), 5451867Sgjelinek zonepath); 5461867Sgjelinek } else { 5471867Sgjelinek /* 5481867Sgjelinek * The snapshot is unusable for some reason so restore 5491867Sgjelinek * the zone state to configured since we were unable to 5501867Sgjelinek * actually do anything about getting the zone 5511867Sgjelinek * installed. 5521867Sgjelinek */ 5531867Sgjelinek int tmp; 5541867Sgjelinek 5551867Sgjelinek if ((tmp = zone_set_state(target_zone, 5561867Sgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 5571867Sgjelinek errno = tmp; 5581867Sgjelinek zperror2(target_zone, 5591867Sgjelinek gettext("could not set state")); 5601867Sgjelinek } 5611867Sgjelinek } 5621867Sgjelinek } 5631867Sgjelinek 5641867Sgjelinek return (err); 5651867Sgjelinek } 5661867Sgjelinek 5671867Sgjelinek /* 5681867Sgjelinek * Attempt to clone a source_zone to a target zonepath by using a ZFS clone. 5691867Sgjelinek */ 5701867Sgjelinek int 571*7089Sgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf, 572*7089Sgjelinek char *postsnapbuf) 5731867Sgjelinek { 5741867Sgjelinek zfs_handle_t *zhp; 5751867Sgjelinek char clone_name[MAXPATHLEN]; 5761867Sgjelinek char snap_name[MAXPATHLEN]; 5771867Sgjelinek 5781867Sgjelinek /* 5791867Sgjelinek * Try to get a zfs handle for the source_zonepath. If this fails 5801867Sgjelinek * the source_zonepath is not ZFS so return an error. 5811867Sgjelinek */ 5821867Sgjelinek if ((zhp = mount2zhandle(source_zonepath)) == NULL) 5831867Sgjelinek return (Z_ERR); 5841867Sgjelinek 5851867Sgjelinek /* 5861867Sgjelinek * Check if there is a file system already mounted on zonepath. If so, 5871867Sgjelinek * we can't clone to the path so we should fall back to copying. 5881867Sgjelinek */ 5891867Sgjelinek if (is_mountpnt(zonepath)) { 5901867Sgjelinek zfs_close(zhp); 5911867Sgjelinek (void) fprintf(stderr, 5921867Sgjelinek gettext("A file system is already mounted on %s,\n" 5931867Sgjelinek "preventing use of a ZFS clone.\n"), zonepath); 5941867Sgjelinek return (Z_ERR); 5951867Sgjelinek } 5961867Sgjelinek 5971867Sgjelinek /* 5981867Sgjelinek * Instead of using path2name to get the clone name from the zonepath, 5991867Sgjelinek * we could generate a name from the source zone ZFS name. However, 6001867Sgjelinek * this would mean we would create the clone under the ZFS fs of the 6011867Sgjelinek * source instead of what the zonepath says. For example, 6021867Sgjelinek * 6031867Sgjelinek * source_zonepath zonepath 6041867Sgjelinek * /pl/zones/dev/z1 /pl/zones/deploy/z2 6051867Sgjelinek * 6061867Sgjelinek * We don't want the clone to be under "dev", we want it under 6071867Sgjelinek * "deploy", so that we can leverage the normal attribute inheritance 6081867Sgjelinek * that ZFS provides in the fs hierarchy. 6091867Sgjelinek */ 6101867Sgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) { 6111867Sgjelinek zfs_close(zhp); 6121867Sgjelinek return (Z_ERR); 6131867Sgjelinek } 6141867Sgjelinek 615*7089Sgjelinek if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf, 616*7089Sgjelinek postsnapbuf) != Z_OK) { 6171867Sgjelinek zfs_close(zhp); 6181867Sgjelinek return (Z_ERR); 6191867Sgjelinek } 6201867Sgjelinek zfs_close(zhp); 6211867Sgjelinek 6223686Sgjelinek if (clone_snap(snap_name, clone_name) != Z_OK) { 6233686Sgjelinek /* Clean up the snapshot we just took. */ 6243686Sgjelinek if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT)) 6253686Sgjelinek != NULL) { 6263686Sgjelinek if (zfs_unmount(zhp, NULL, 0) == 0) 6273686Sgjelinek (void) zfs_destroy(zhp); 6283686Sgjelinek zfs_close(zhp); 6293686Sgjelinek } 6303686Sgjelinek 6311867Sgjelinek return (Z_ERR); 6323686Sgjelinek } 6331867Sgjelinek 6341867Sgjelinek (void) printf(gettext("Instead of copying, a ZFS clone has been " 6351867Sgjelinek "created for this zone.\n")); 6361867Sgjelinek 6371867Sgjelinek return (Z_OK); 6381867Sgjelinek } 6391867Sgjelinek 6401867Sgjelinek /* 6411867Sgjelinek * Attempt to create a ZFS file system for the specified zonepath. 6421867Sgjelinek * We either will successfully create a ZFS file system and get it mounted 6431867Sgjelinek * on the zonepath or we don't. The caller doesn't care since a regular 6441867Sgjelinek * directory is used for the zonepath if no ZFS file system is mounted there. 6451867Sgjelinek */ 6461867Sgjelinek void 6471867Sgjelinek create_zfs_zonepath(char *zonepath) 6481867Sgjelinek { 6491867Sgjelinek zfs_handle_t *zhp; 6501867Sgjelinek char zfs_name[MAXPATHLEN]; 6512676Seschrock nvlist_t *props = NULL; 6521867Sgjelinek 6531867Sgjelinek if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK) 6541867Sgjelinek return; 6551867Sgjelinek 6562676Seschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 || 6572744Snn35248 nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS), 6582744Snn35248 "off") != 0) { 6592744Snn35248 if (props != NULL) 6602744Snn35248 nvlist_free(props); 6612676Seschrock (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: " 6622676Seschrock "out of memory\n"), zfs_name); 6632676Seschrock } 6642676Seschrock 6652676Seschrock if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 || 6665094Slling (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) { 6672082Seschrock (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: " 6682082Seschrock "%s\n"), zfs_name, libzfs_error_description(g_zfs)); 6692676Seschrock nvlist_free(props); 6701867Sgjelinek return; 6711867Sgjelinek } 6721867Sgjelinek 6732676Seschrock nvlist_free(props); 6742676Seschrock 6751867Sgjelinek if (zfs_mount(zhp, NULL, 0) != 0) { 6762082Seschrock (void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: " 6772082Seschrock "%s\n"), zfs_name, libzfs_error_description(g_zfs)); 6781867Sgjelinek (void) zfs_destroy(zhp); 6791867Sgjelinek } else { 6801867Sgjelinek if (chmod(zonepath, S_IRWXU) != 0) { 6811867Sgjelinek (void) fprintf(stderr, gettext("file system %s " 6821867Sgjelinek "successfully created, but chmod %o failed: %s\n"), 6831867Sgjelinek zfs_name, S_IRWXU, strerror(errno)); 6841867Sgjelinek (void) destroy_zfs(zonepath); 6851867Sgjelinek } else { 6861867Sgjelinek (void) printf(gettext("A ZFS file system has been " 6871867Sgjelinek "created for this zone.\n")); 6881867Sgjelinek } 6891867Sgjelinek } 6901867Sgjelinek 6911867Sgjelinek zfs_close(zhp); 6921867Sgjelinek } 6931867Sgjelinek 6941867Sgjelinek /* 6951867Sgjelinek * If the zonepath is a ZFS file system, attempt to destroy it. We return Z_OK 6961867Sgjelinek * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR 6971867Sgjelinek * which means the caller should clean up the zonepath in the traditional 6981867Sgjelinek * way. 6991867Sgjelinek */ 7001867Sgjelinek int 7011867Sgjelinek destroy_zfs(char *zonepath) 7021867Sgjelinek { 7031867Sgjelinek zfs_handle_t *zhp; 7041867Sgjelinek boolean_t is_clone = B_FALSE; 7051867Sgjelinek char origin[ZFS_MAXPROPLEN]; 7061867Sgjelinek 7072082Seschrock if ((zhp = mount2zhandle(zonepath)) == NULL) 7081867Sgjelinek return (Z_ERR); 7091867Sgjelinek 7101867Sgjelinek /* 7111867Sgjelinek * We can't destroy the file system if it has dependents. 7121867Sgjelinek */ 7132474Seschrock if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0 || 7141867Sgjelinek zfs_unmount(zhp, NULL, 0) != 0) { 7151867Sgjelinek zfs_close(zhp); 7161867Sgjelinek return (Z_ERR); 7171867Sgjelinek } 7181867Sgjelinek 7191867Sgjelinek /* 7201867Sgjelinek * This might be a clone. Try to get the snapshot so we can attempt 7211867Sgjelinek * to destroy that as well. 7221867Sgjelinek */ 7231867Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, 7242082Seschrock NULL, 0, B_FALSE) == 0) 7251867Sgjelinek is_clone = B_TRUE; 7261867Sgjelinek 7271867Sgjelinek if (zfs_destroy(zhp) != 0) { 7281867Sgjelinek /* 7291867Sgjelinek * If the destroy fails for some reason, try to remount 7301867Sgjelinek * the file system so that we can use "rm -rf" to clean up 7311867Sgjelinek * instead. 7321867Sgjelinek */ 7331867Sgjelinek (void) zfs_mount(zhp, NULL, 0); 7341867Sgjelinek zfs_close(zhp); 7351867Sgjelinek return (Z_ERR); 7361867Sgjelinek } 7371867Sgjelinek 7383686Sgjelinek /* 7393686Sgjelinek * If the zone has ever been moved then the mountpoint dir will not be 7403686Sgjelinek * cleaned up by the zfs_destroy(). To handle this case try to clean 7413686Sgjelinek * it up now but don't worry if it fails, that will be normal. 7423686Sgjelinek */ 7433686Sgjelinek (void) rmdir(zonepath); 7443686Sgjelinek 7451867Sgjelinek (void) printf(gettext("The ZFS file system for this zone has been " 7461867Sgjelinek "destroyed.\n")); 7471867Sgjelinek 7481867Sgjelinek if (is_clone) { 7491867Sgjelinek zfs_handle_t *ohp; 7501867Sgjelinek 7511867Sgjelinek /* 7521867Sgjelinek * Try to clean up the snapshot that the clone was taken from. 7531867Sgjelinek */ 7542082Seschrock if ((ohp = zfs_open(g_zfs, origin, 7552082Seschrock ZFS_TYPE_SNAPSHOT)) != NULL) { 7562474Seschrock if (zfs_iter_dependents(ohp, B_TRUE, has_dependent, 7572474Seschrock NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0) 7581867Sgjelinek (void) zfs_destroy(ohp); 7591867Sgjelinek zfs_close(ohp); 7601867Sgjelinek } 7611867Sgjelinek } 7621867Sgjelinek 7631867Sgjelinek zfs_close(zhp); 7641867Sgjelinek return (Z_OK); 7651867Sgjelinek } 7661867Sgjelinek 7671867Sgjelinek /* 7681867Sgjelinek * Return true if the path is its own zfs file system. We determine this 7691867Sgjelinek * by stat-ing the path to see if it is zfs and stat-ing the parent to see 7701867Sgjelinek * if it is a different fs. 7711867Sgjelinek */ 7721867Sgjelinek boolean_t 7731867Sgjelinek is_zonepath_zfs(char *zonepath) 7741867Sgjelinek { 7751867Sgjelinek int res; 7761867Sgjelinek char *path; 7771867Sgjelinek char *parent; 7782267Sdp struct statvfs64 buf1, buf2; 7791867Sgjelinek 7802267Sdp if (statvfs64(zonepath, &buf1) != 0) 7811867Sgjelinek return (B_FALSE); 7821867Sgjelinek 7831867Sgjelinek if (strcmp(buf1.f_basetype, "zfs") != 0) 7841867Sgjelinek return (B_FALSE); 7851867Sgjelinek 7861867Sgjelinek if ((path = strdup(zonepath)) == NULL) 7871867Sgjelinek return (B_FALSE); 7881867Sgjelinek 7891867Sgjelinek parent = dirname(path); 7902267Sdp res = statvfs64(parent, &buf2); 7911867Sgjelinek free(path); 7921867Sgjelinek 7931867Sgjelinek if (res != 0) 7941867Sgjelinek return (B_FALSE); 7951867Sgjelinek 7961867Sgjelinek if (buf1.f_fsid == buf2.f_fsid) 7971867Sgjelinek return (B_FALSE); 7981867Sgjelinek 7991867Sgjelinek return (B_TRUE); 8001867Sgjelinek } 8011867Sgjelinek 8021867Sgjelinek /* 8031867Sgjelinek * Implement the fast move of a ZFS file system by simply updating the 8041867Sgjelinek * mountpoint. Since it is file system already, we don't have the 8051867Sgjelinek * issue of cross-file system copying. 8061867Sgjelinek */ 8071867Sgjelinek int 8081867Sgjelinek move_zfs(char *zonepath, char *new_zonepath) 8091867Sgjelinek { 8101867Sgjelinek int ret = Z_ERR; 8111867Sgjelinek zfs_handle_t *zhp; 8121867Sgjelinek 8132082Seschrock if ((zhp = mount2zhandle(zonepath)) == NULL) 8141867Sgjelinek return (Z_ERR); 8151867Sgjelinek 8162676Seschrock if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 8172676Seschrock new_zonepath) == 0) { 8181867Sgjelinek /* 8191867Sgjelinek * Clean up the old mount point. We ignore any failure since 8201867Sgjelinek * the zone is already successfully mounted on the new path. 8211867Sgjelinek */ 8221867Sgjelinek (void) rmdir(zonepath); 8231867Sgjelinek ret = Z_OK; 8241867Sgjelinek } 8251867Sgjelinek 8261867Sgjelinek zfs_close(zhp); 8271867Sgjelinek 8281867Sgjelinek return (ret); 8291867Sgjelinek } 8301867Sgjelinek 8311867Sgjelinek /* 8321867Sgjelinek * Validate that the given dataset exists on the system, and that neither it nor 8331867Sgjelinek * its children are zvols. 8341867Sgjelinek * 8351867Sgjelinek * Note that we don't do anything with the 'zoned' property here. All 8361867Sgjelinek * management is done in zoneadmd when the zone is actually rebooted. This 8371867Sgjelinek * allows us to automatically set the zoned property even when a zone is 8381867Sgjelinek * rebooted by the administrator. 8391867Sgjelinek */ 8401867Sgjelinek int 8411867Sgjelinek verify_datasets(zone_dochandle_t handle) 8421867Sgjelinek { 8431867Sgjelinek int return_code = Z_OK; 8441867Sgjelinek struct zone_dstab dstab; 8451867Sgjelinek zfs_handle_t *zhp; 8461867Sgjelinek char propbuf[ZFS_MAXPROPLEN]; 8471867Sgjelinek char source[ZFS_MAXNAMELEN]; 8485094Slling zprop_source_t srctype; 8491867Sgjelinek 8501867Sgjelinek if (zonecfg_setdsent(handle) != Z_OK) { 8511867Sgjelinek /* 8521867Sgjelinek * TRANSLATION_NOTE 8531867Sgjelinek * zfs and dataset are literals that should not be translated. 8541867Sgjelinek */ 8551867Sgjelinek (void) fprintf(stderr, gettext("could not verify zfs datasets: " 8561867Sgjelinek "unable to enumerate datasets\n")); 8571867Sgjelinek return (Z_ERR); 8581867Sgjelinek } 8591867Sgjelinek 8601867Sgjelinek while (zonecfg_getdsent(handle, &dstab) == Z_OK) { 8611867Sgjelinek 8622082Seschrock if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name, 8631867Sgjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) { 8642082Seschrock (void) fprintf(stderr, gettext("could not verify zfs " 8652082Seschrock "dataset %s: %s\n"), dstab.zone_dataset_name, 8662082Seschrock libzfs_error_description(g_zfs)); 8671867Sgjelinek return_code = Z_ERR; 8681867Sgjelinek continue; 8691867Sgjelinek } 8701867Sgjelinek 8711867Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, 8721867Sgjelinek sizeof (propbuf), &srctype, source, 8731867Sgjelinek sizeof (source), 0) == 0 && 8745094Slling (srctype == ZPROP_SRC_INHERITED)) { 8751867Sgjelinek (void) fprintf(stderr, gettext("could not verify zfs " 8761867Sgjelinek "dataset %s: mountpoint cannot be inherited\n"), 8771867Sgjelinek dstab.zone_dataset_name); 8781867Sgjelinek return_code = Z_ERR; 8791867Sgjelinek zfs_close(zhp); 8801867Sgjelinek continue; 8811867Sgjelinek } 8821867Sgjelinek 8831867Sgjelinek if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 8841867Sgjelinek (void) fprintf(stderr, gettext("cannot verify zfs " 8851867Sgjelinek "dataset %s: volumes cannot be specified as a " 8861867Sgjelinek "zone dataset resource\n"), 8871867Sgjelinek dstab.zone_dataset_name); 8881867Sgjelinek return_code = Z_ERR; 8891867Sgjelinek } 8901867Sgjelinek 8911867Sgjelinek if (zfs_iter_children(zhp, check_zvol, NULL) != 0) 8921867Sgjelinek return_code = Z_ERR; 8931867Sgjelinek 8941867Sgjelinek zfs_close(zhp); 8951867Sgjelinek } 8961867Sgjelinek (void) zonecfg_enddsent(handle); 8971867Sgjelinek 8981867Sgjelinek return (return_code); 8991867Sgjelinek } 9001867Sgjelinek 9011867Sgjelinek /* 9021867Sgjelinek * Verify that the ZFS dataset exists, and its mountpoint 9031867Sgjelinek * property is set to "legacy". 9041867Sgjelinek */ 9051867Sgjelinek int 9061867Sgjelinek verify_fs_zfs(struct zone_fstab *fstab) 9071867Sgjelinek { 9081867Sgjelinek zfs_handle_t *zhp; 9091867Sgjelinek char propbuf[ZFS_MAXPROPLEN]; 9101867Sgjelinek 9112082Seschrock if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special, 9125094Slling ZFS_TYPE_DATASET)) == NULL) { 9131867Sgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: " 9141867Sgjelinek "could not access zfs dataset '%s'\n"), 9151867Sgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 9161867Sgjelinek return (Z_ERR); 9171867Sgjelinek } 9181867Sgjelinek 9191867Sgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 9201867Sgjelinek (void) fprintf(stderr, gettext("cannot verify fs %s: " 9211867Sgjelinek "'%s' is not a file system\n"), 9221867Sgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 9231867Sgjelinek zfs_close(zhp); 9241867Sgjelinek return (Z_ERR); 9251867Sgjelinek } 9261867Sgjelinek 9271867Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf), 9281867Sgjelinek NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) { 9291867Sgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: " 9301867Sgjelinek "zfs '%s' mountpoint is not \"legacy\"\n"), 9311867Sgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 9321867Sgjelinek zfs_close(zhp); 9331867Sgjelinek return (Z_ERR); 9341867Sgjelinek } 9351867Sgjelinek 9361867Sgjelinek zfs_close(zhp); 9371867Sgjelinek return (Z_OK); 9381867Sgjelinek } 9392082Seschrock 9402082Seschrock int 9412082Seschrock init_zfs(void) 9422082Seschrock { 9432082Seschrock if ((g_zfs = libzfs_init()) == NULL) { 9442082Seschrock (void) fprintf(stderr, gettext("failed to initialize ZFS " 9452082Seschrock "library\n")); 9462082Seschrock return (Z_ERR); 9472082Seschrock } 9482082Seschrock 9492082Seschrock return (Z_OK); 9502082Seschrock } 951