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 /*
232b6c28b8Sbatschul * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24*40a5c998SMatthew Ahrens * Copyright (c) 2012, 2015 by Delphix. All rights reserved.
25dd9c3b29SJerry Jelinek * Copyright (c) 2012, Joyent, Inc. All rights reserved.
260b5de56dSgjelinek */
270b5de56dSgjelinek
280b5de56dSgjelinek /*
290b5de56dSgjelinek * This file contains the functions used to support the ZFS integration
300b5de56dSgjelinek * with zones. This includes validation (e.g. zonecfg dataset), cloning,
310b5de56dSgjelinek * file system creation and destruction.
320b5de56dSgjelinek */
330b5de56dSgjelinek
340b5de56dSgjelinek #include <stdio.h>
350b5de56dSgjelinek #include <errno.h>
360b5de56dSgjelinek #include <unistd.h>
370b5de56dSgjelinek #include <string.h>
380b5de56dSgjelinek #include <locale.h>
390b5de56dSgjelinek #include <libintl.h>
400b5de56dSgjelinek #include <sys/stat.h>
410b5de56dSgjelinek #include <sys/statvfs.h>
420b5de56dSgjelinek #include <libgen.h>
430b5de56dSgjelinek #include <libzonecfg.h>
440b5de56dSgjelinek #include <sys/mnttab.h>
450b5de56dSgjelinek #include <libzfs.h>
4611506c41Sgjelinek #include <sys/mntent.h>
47286822ddS #include <values.h>
480094b373Sjv227347 #include <strings.h>
490094b373Sjv227347 #include <assert.h>
500b5de56dSgjelinek
510b5de56dSgjelinek #include "zoneadm.h"
520b5de56dSgjelinek
5399653d4eSeschrock libzfs_handle_t *g_zfs;
540b5de56dSgjelinek
550b5de56dSgjelinek typedef struct zfs_mount_data {
560b5de56dSgjelinek char *match_name;
570b5de56dSgjelinek zfs_handle_t *match_handle;
580b5de56dSgjelinek } zfs_mount_data_t;
590b5de56dSgjelinek
600b5de56dSgjelinek typedef struct zfs_snapshot_data {
61286822ddS char *match_name; /* zonename@SUNWzone */
62286822ddS int len; /* strlen of match_name */
63286822ddS int max; /* highest digit appended to snap name */
64286822ddS int num; /* number of snapshots to rename */
65286822ddS int cntr; /* counter for renaming snapshots */
660b5de56dSgjelinek } zfs_snapshot_data_t;
670b5de56dSgjelinek
68286822ddS typedef struct clone_data {
69286822ddS zfs_handle_t *clone_zhp; /* clone dataset to promote */
70286822ddS time_t origin_creation; /* snapshot creation time of clone */
71286822ddS const char *snapshot; /* snapshot of dataset being demoted */
72286822ddS } clone_data_t;
73286822ddS
740b5de56dSgjelinek /*
750b5de56dSgjelinek * A ZFS file system iterator call-back function which returns the
760b5de56dSgjelinek * zfs_handle_t for a ZFS file system on the specified mount point.
770b5de56dSgjelinek */
780b5de56dSgjelinek static int
match_mountpoint(zfs_handle_t * zhp,void * data)790b5de56dSgjelinek match_mountpoint(zfs_handle_t *zhp, void *data)
800b5de56dSgjelinek {
810b5de56dSgjelinek int res;
820b5de56dSgjelinek zfs_mount_data_t *cbp;
830b5de56dSgjelinek char mp[ZFS_MAXPROPLEN];
840b5de56dSgjelinek
850b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
860b5de56dSgjelinek zfs_close(zhp);
870b5de56dSgjelinek return (0);
880b5de56dSgjelinek }
890b5de56dSgjelinek
9011506c41Sgjelinek /* First check if the dataset is mounted. */
9111506c41Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL,
9211506c41Sgjelinek 0, B_FALSE) != 0 || strcmp(mp, "no") == 0) {
9311506c41Sgjelinek zfs_close(zhp);
9411506c41Sgjelinek return (0);
9511506c41Sgjelinek }
9611506c41Sgjelinek
9711506c41Sgjelinek /* Now check mount point. */
980b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
9911506c41Sgjelinek 0, B_FALSE) != 0) {
10011506c41Sgjelinek zfs_close(zhp);
10111506c41Sgjelinek return (0);
10211506c41Sgjelinek }
10311506c41Sgjelinek
10411506c41Sgjelinek cbp = (zfs_mount_data_t *)data;
10511506c41Sgjelinek
10611506c41Sgjelinek if (strcmp(mp, "legacy") == 0) {
10711506c41Sgjelinek /* If legacy, must look in mnttab for mountpoint. */
10811506c41Sgjelinek FILE *fp;
10911506c41Sgjelinek struct mnttab entry;
11011506c41Sgjelinek const char *nm;
11111506c41Sgjelinek
11211506c41Sgjelinek nm = zfs_get_name(zhp);
11311506c41Sgjelinek if ((fp = fopen(MNTTAB, "r")) == NULL) {
11411506c41Sgjelinek zfs_close(zhp);
11511506c41Sgjelinek return (0);
11611506c41Sgjelinek }
11711506c41Sgjelinek
11811506c41Sgjelinek while (getmntent(fp, &entry) == 0) {
11911506c41Sgjelinek if (strcmp(nm, entry.mnt_special) == 0) {
12011506c41Sgjelinek if (strcmp(entry.mnt_mountp, cbp->match_name)
12111506c41Sgjelinek == 0) {
12211506c41Sgjelinek (void) fclose(fp);
12311506c41Sgjelinek cbp->match_handle = zhp;
12411506c41Sgjelinek return (1);
12511506c41Sgjelinek }
12611506c41Sgjelinek break;
12711506c41Sgjelinek }
12811506c41Sgjelinek }
12911506c41Sgjelinek (void) fclose(fp);
13011506c41Sgjelinek
13111506c41Sgjelinek } else if (strcmp(mp, cbp->match_name) == 0) {
1320b5de56dSgjelinek cbp->match_handle = zhp;
1330b5de56dSgjelinek return (1);
1340b5de56dSgjelinek }
1350b5de56dSgjelinek
13611506c41Sgjelinek /* Iterate over any nested datasets. */
1370b5de56dSgjelinek res = zfs_iter_filesystems(zhp, match_mountpoint, data);
1380b5de56dSgjelinek zfs_close(zhp);
1390b5de56dSgjelinek return (res);
1400b5de56dSgjelinek }
1410b5de56dSgjelinek
1420b5de56dSgjelinek /*
1430b5de56dSgjelinek * Get ZFS handle for the specified mount point.
1440b5de56dSgjelinek */
1450b5de56dSgjelinek static zfs_handle_t *
mount2zhandle(char * mountpoint)1460b5de56dSgjelinek mount2zhandle(char *mountpoint)
1470b5de56dSgjelinek {
1480b5de56dSgjelinek zfs_mount_data_t cb;
1490b5de56dSgjelinek
1500b5de56dSgjelinek cb.match_name = mountpoint;
1510b5de56dSgjelinek cb.match_handle = NULL;
15299653d4eSeschrock (void) zfs_iter_root(g_zfs, match_mountpoint, &cb);
1530b5de56dSgjelinek return (cb.match_handle);
1540b5de56dSgjelinek }
1550b5de56dSgjelinek
1560b5de56dSgjelinek /*
1570b5de56dSgjelinek * Check if there is already a file system (zfs or any other type) mounted on
1580b5de56dSgjelinek * path.
1590b5de56dSgjelinek */
1600b5de56dSgjelinek static boolean_t
is_mountpnt(char * path)1610b5de56dSgjelinek is_mountpnt(char *path)
1620b5de56dSgjelinek {
1630b5de56dSgjelinek FILE *fp;
1640b5de56dSgjelinek struct mnttab entry;
1650b5de56dSgjelinek
16611506c41Sgjelinek if ((fp = fopen(MNTTAB, "r")) == NULL)
1670b5de56dSgjelinek return (B_FALSE);
1680b5de56dSgjelinek
1690b5de56dSgjelinek while (getmntent(fp, &entry) == 0) {
1700b5de56dSgjelinek if (strcmp(path, entry.mnt_mountp) == 0) {
1710b5de56dSgjelinek (void) fclose(fp);
1720b5de56dSgjelinek return (B_TRUE);
1730b5de56dSgjelinek }
1740b5de56dSgjelinek }
1750b5de56dSgjelinek
1760b5de56dSgjelinek (void) fclose(fp);
1770b5de56dSgjelinek return (B_FALSE);
1780b5de56dSgjelinek }
1790b5de56dSgjelinek
1800b5de56dSgjelinek /*
181ff17c8bfSgjelinek * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone.
1820b5de56dSgjelinek */
1830b5de56dSgjelinek static int
pre_snapshot(char * presnapbuf)184ff17c8bfSgjelinek pre_snapshot(char *presnapbuf)
1850b5de56dSgjelinek {
186ff17c8bfSgjelinek int status;
1870b5de56dSgjelinek
188ff17c8bfSgjelinek /* No brand-specific handler */
189ff17c8bfSgjelinek if (presnapbuf[0] == '\0')
190ff17c8bfSgjelinek return (Z_OK);
191ff17c8bfSgjelinek
192ff17c8bfSgjelinek /* Run the hook */
193c75cc341S status = do_subproc(presnapbuf);
194ff17c8bfSgjelinek if ((status = subproc_status(gettext("brand-specific presnapshot"),
195ff17c8bfSgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK)
1960b5de56dSgjelinek return (Z_ERR);
1970b5de56dSgjelinek
1980b5de56dSgjelinek return (Z_OK);
1990b5de56dSgjelinek }
2000b5de56dSgjelinek
2010b5de56dSgjelinek /*
202ff17c8bfSgjelinek * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone.
2030b5de56dSgjelinek */
2040b5de56dSgjelinek static int
post_snapshot(char * postsnapbuf)205ff17c8bfSgjelinek post_snapshot(char *postsnapbuf)
2060b5de56dSgjelinek {
207ff17c8bfSgjelinek int status;
2080b5de56dSgjelinek
209ff17c8bfSgjelinek /* No brand-specific handler */
210ff17c8bfSgjelinek if (postsnapbuf[0] == '\0')
211ff17c8bfSgjelinek return (Z_OK);
212ff17c8bfSgjelinek
213ff17c8bfSgjelinek /* Run the hook */
214c75cc341S status = do_subproc(postsnapbuf);
215ff17c8bfSgjelinek if ((status = subproc_status(gettext("brand-specific postsnapshot"),
216ff17c8bfSgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK)
2170b5de56dSgjelinek return (Z_ERR);
2180b5de56dSgjelinek
2190b5de56dSgjelinek return (Z_OK);
2200b5de56dSgjelinek }
2210b5de56dSgjelinek
2220b5de56dSgjelinek /*
2230b5de56dSgjelinek * This is a ZFS snapshot iterator call-back function which returns the
2240b5de56dSgjelinek * highest number of SUNWzone snapshots that have been taken.
2250b5de56dSgjelinek */
2260b5de56dSgjelinek static int
get_snap_max(zfs_handle_t * zhp,void * data)2270b5de56dSgjelinek get_snap_max(zfs_handle_t *zhp, void *data)
2280b5de56dSgjelinek {
2290b5de56dSgjelinek int res;
2300b5de56dSgjelinek zfs_snapshot_data_t *cbp;
2310b5de56dSgjelinek
2320b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
2330b5de56dSgjelinek zfs_close(zhp);
2340b5de56dSgjelinek return (0);
2350b5de56dSgjelinek }
2360b5de56dSgjelinek
2370b5de56dSgjelinek cbp = (zfs_snapshot_data_t *)data;
2380b5de56dSgjelinek
2390b5de56dSgjelinek if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) {
2400b5de56dSgjelinek char *nump;
2410b5de56dSgjelinek int num;
2420b5de56dSgjelinek
243286822ddS cbp->num++;
2440b5de56dSgjelinek nump = (char *)(zfs_get_name(zhp) + cbp->len);
2450b5de56dSgjelinek num = atoi(nump);
2460b5de56dSgjelinek if (num > cbp->max)
2470b5de56dSgjelinek cbp->max = num;
2480b5de56dSgjelinek }
2490b5de56dSgjelinek
2500b5de56dSgjelinek res = zfs_iter_snapshots(zhp, get_snap_max, data);
2510b5de56dSgjelinek zfs_close(zhp);
2520b5de56dSgjelinek return (res);
2530b5de56dSgjelinek }
2540b5de56dSgjelinek
2550b5de56dSgjelinek /*
2560b5de56dSgjelinek * Take a ZFS snapshot to be used for cloning the zone.
2570b5de56dSgjelinek */
2580b5de56dSgjelinek static int
take_snapshot(zfs_handle_t * zhp,char * snapshot_name,int snap_size,char * presnapbuf,char * postsnapbuf)259ff17c8bfSgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size,
260ff17c8bfSgjelinek char *presnapbuf, char *postsnapbuf)
2610b5de56dSgjelinek {
2620b5de56dSgjelinek int res;
263*40a5c998SMatthew Ahrens char template[ZFS_MAX_DATASET_NAME_LEN];
2640b5de56dSgjelinek zfs_snapshot_data_t cb;
2650b5de56dSgjelinek
2660b5de56dSgjelinek /*
2670b5de56dSgjelinek * First we need to figure out the next available name for the
2680b5de56dSgjelinek * zone snapshot. Look through the list of zones snapshots for
2690b5de56dSgjelinek * this file system to determine the maximum snapshot name.
2700b5de56dSgjelinek */
2710b5de56dSgjelinek if (snprintf(template, sizeof (template), "%s@SUNWzone",
2720b5de56dSgjelinek zfs_get_name(zhp)) >= sizeof (template))
2730b5de56dSgjelinek return (Z_ERR);
2740b5de56dSgjelinek
2750b5de56dSgjelinek cb.match_name = template;
2760b5de56dSgjelinek cb.len = strlen(template);
2770b5de56dSgjelinek cb.max = 0;
2780b5de56dSgjelinek
2790b5de56dSgjelinek if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0)
2800b5de56dSgjelinek return (Z_ERR);
2810b5de56dSgjelinek
2820b5de56dSgjelinek cb.max++;
2830b5de56dSgjelinek
2840b5de56dSgjelinek if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d",
2850b5de56dSgjelinek zfs_get_name(zhp), cb.max) >= snap_size)
2860b5de56dSgjelinek return (Z_ERR);
2870b5de56dSgjelinek
288ff17c8bfSgjelinek if (pre_snapshot(presnapbuf) != Z_OK)
2890b5de56dSgjelinek return (Z_ERR);
290bb0ade09Sahrens res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL);
291ff17c8bfSgjelinek if (post_snapshot(postsnapbuf) != Z_OK)
2920b5de56dSgjelinek return (Z_ERR);
2930b5de56dSgjelinek
2940b5de56dSgjelinek if (res != 0)
2950b5de56dSgjelinek return (Z_ERR);
2960b5de56dSgjelinek return (Z_OK);
2970b5de56dSgjelinek }
2980b5de56dSgjelinek
2990b5de56dSgjelinek /*
3000b5de56dSgjelinek * We are using an explicit snapshot from some earlier point in time so
301ff17c8bfSgjelinek * we need to validate it. Run the brand specific hook.
3020b5de56dSgjelinek */
3030b5de56dSgjelinek static int
validate_snapshot(char * snapshot_name,char * snap_path,char * validsnapbuf)304ff17c8bfSgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf)
3050b5de56dSgjelinek {
306ff17c8bfSgjelinek int status;
307ff17c8bfSgjelinek char cmdbuf[MAXPATHLEN];
3080b5de56dSgjelinek
309ff17c8bfSgjelinek /* No brand-specific handler */
310ff17c8bfSgjelinek if (validsnapbuf[0] == '\0')
311ff17c8bfSgjelinek return (Z_OK);
312ff17c8bfSgjelinek
313ff17c8bfSgjelinek /* pass args - snapshot_name & snap_path */
314ff17c8bfSgjelinek if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf,
315ff17c8bfSgjelinek snapshot_name, snap_path) >= sizeof (cmdbuf)) {
316ff17c8bfSgjelinek zerror("Command line too long");
3170b5de56dSgjelinek return (Z_ERR);
3180b5de56dSgjelinek }
3190b5de56dSgjelinek
320ff17c8bfSgjelinek /* Run the hook */
321c75cc341S status = do_subproc(cmdbuf);
322ff17c8bfSgjelinek if ((status = subproc_status(gettext("brand-specific validatesnapshot"),
323ff17c8bfSgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK)
3240b5de56dSgjelinek return (Z_ERR);
3250b5de56dSgjelinek
326ff17c8bfSgjelinek return (Z_OK);
3270b5de56dSgjelinek }
3280b5de56dSgjelinek
3290b5de56dSgjelinek /*
3300b5de56dSgjelinek * Remove the sw inventory file from inside this zonepath that we picked up out
3310b5de56dSgjelinek * of the snapshot.
3320b5de56dSgjelinek */
3330b5de56dSgjelinek static int
clean_out_clone()3340b5de56dSgjelinek clean_out_clone()
3350b5de56dSgjelinek {
3360b5de56dSgjelinek int err;
3370b5de56dSgjelinek zone_dochandle_t handle;
3380b5de56dSgjelinek
3390b5de56dSgjelinek if ((handle = zonecfg_init_handle()) == NULL) {
3400b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3410b5de56dSgjelinek return (Z_ERR);
3420b5de56dSgjelinek }
3430b5de56dSgjelinek
3440b5de56dSgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) {
3450b5de56dSgjelinek errno = err;
3460b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE);
3470b5de56dSgjelinek zonecfg_fini_handle(handle);
3480b5de56dSgjelinek return (Z_ERR);
3490b5de56dSgjelinek }
3500b5de56dSgjelinek
3510b5de56dSgjelinek zonecfg_rm_detached(handle, B_FALSE);
3520b5de56dSgjelinek zonecfg_fini_handle(handle);
3530b5de56dSgjelinek
3540b5de56dSgjelinek return (Z_OK);
3550b5de56dSgjelinek }
3560b5de56dSgjelinek
3570b5de56dSgjelinek /*
3580b5de56dSgjelinek * Make a ZFS clone on zonepath from snapshot_name.
3590b5de56dSgjelinek */
3600b5de56dSgjelinek static int
clone_snap(char * snapshot_name,char * zonepath)3610b5de56dSgjelinek clone_snap(char *snapshot_name, char *zonepath)
3620b5de56dSgjelinek {
3630b5de56dSgjelinek int res = Z_OK;
3640b5de56dSgjelinek int err;
3650b5de56dSgjelinek zfs_handle_t *zhp;
3660b5de56dSgjelinek zfs_handle_t *clone;
367e9dbad6fSeschrock nvlist_t *props = NULL;
3680b5de56dSgjelinek
36999653d4eSeschrock if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL)
3700b5de56dSgjelinek return (Z_NO_ENTRY);
3710b5de56dSgjelinek
3720b5de56dSgjelinek (void) printf(gettext("Cloning snapshot %s\n"), snapshot_name);
3730b5de56dSgjelinek
3742b6c28b8Sbatschul /*
3752b6c28b8Sbatschul * We turn off zfs SHARENFS and SHARESMB properties on the
3762b6c28b8Sbatschul * zoneroot dataset in order to prevent the GZ from sharing
3772b6c28b8Sbatschul * NGZ data by accident.
3782b6c28b8Sbatschul */
3792b6c28b8Sbatschul if ((nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) ||
3802b6c28b8Sbatschul (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
3812b6c28b8Sbatschul "off") != 0) ||
3822b6c28b8Sbatschul (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARESMB),
3832b6c28b8Sbatschul "off") != 0)) {
384e9dbad6fSeschrock nvlist_free(props);
385e9dbad6fSeschrock (void) fprintf(stderr, gettext("could not create ZFS clone "
386e9dbad6fSeschrock "%s: out of memory\n"), zonepath);
387e9dbad6fSeschrock return (Z_ERR);
388e9dbad6fSeschrock }
389e9dbad6fSeschrock
390e9dbad6fSeschrock err = zfs_clone(zhp, zonepath, props);
3910b5de56dSgjelinek zfs_close(zhp);
392e9dbad6fSeschrock
393e9dbad6fSeschrock nvlist_free(props);
394e9dbad6fSeschrock
3950b5de56dSgjelinek if (err != 0)
3960b5de56dSgjelinek return (Z_ERR);
3970b5de56dSgjelinek
3980b5de56dSgjelinek /* create the mountpoint if necessary */
399990b4856Slling if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL)
4000b5de56dSgjelinek return (Z_ERR);
4010b5de56dSgjelinek
4020b5de56dSgjelinek /*
4030b5de56dSgjelinek * The clone has been created so we need to print a diagnostic
4040b5de56dSgjelinek * message if one of the following steps fails for some reason.
4050b5de56dSgjelinek */
4060b5de56dSgjelinek if (zfs_mount(clone, NULL, 0) != 0) {
4070b5de56dSgjelinek (void) fprintf(stderr, gettext("could not mount ZFS clone "
4080b5de56dSgjelinek "%s\n"), zfs_get_name(clone));
4090b5de56dSgjelinek res = Z_ERR;
4100b5de56dSgjelinek
411e9dbad6fSeschrock } else if (clean_out_clone() != Z_OK) {
4120b5de56dSgjelinek (void) fprintf(stderr, gettext("could not remove the "
4130b5de56dSgjelinek "software inventory from ZFS clone %s\n"),
4140b5de56dSgjelinek zfs_get_name(clone));
4150b5de56dSgjelinek res = Z_ERR;
4160b5de56dSgjelinek }
4170b5de56dSgjelinek
4180b5de56dSgjelinek zfs_close(clone);
4190b5de56dSgjelinek return (res);
4200b5de56dSgjelinek }
4210b5de56dSgjelinek
4220b5de56dSgjelinek /*
4230b5de56dSgjelinek * This function takes a zonepath and attempts to determine what the ZFS
4240b5de56dSgjelinek * file system name (not mountpoint) should be for that path. We do not
4250b5de56dSgjelinek * assume that zonepath is an existing directory or ZFS fs since we use
4260b5de56dSgjelinek * this function as part of the process of creating a new ZFS fs or clone.
4270b5de56dSgjelinek *
4280b5de56dSgjelinek * The way this works is that we look at the parent directory of the zonepath
4290b5de56dSgjelinek * to see if it is a ZFS fs. If it is, we get the name of that ZFS fs and
4300b5de56dSgjelinek * append the last component of the zonepath to generate the ZFS name for the
4310b5de56dSgjelinek * zonepath. This matches the algorithm that ZFS uses for automatically
4320b5de56dSgjelinek * mounting a new fs after it is created.
4330b5de56dSgjelinek *
4340b5de56dSgjelinek * Although a ZFS fs can be mounted anywhere, we don't worry about handling
4350b5de56dSgjelinek * all of the complexity that a user could possibly configure with arbitrary
4360b5de56dSgjelinek * mounts since there is no way to generate a ZFS name from a random path in
4370b5de56dSgjelinek * the file system. We only try to handle the automatic mounts that ZFS does
4380b5de56dSgjelinek * for each file system. ZFS restricts this so that a new fs must be created
4390b5de56dSgjelinek * in an existing parent ZFS fs. It then automatically mounts the new fs
4400b5de56dSgjelinek * directly under the mountpoint for the parent fs using the last component
4410b5de56dSgjelinek * of the name as the mountpoint directory.
4420b5de56dSgjelinek *
4430b5de56dSgjelinek * For example:
4440b5de56dSgjelinek * Name Mountpoint
4450b5de56dSgjelinek * space/eng/dev/test/zone1 /project1/eng/dev/test/zone1
4460b5de56dSgjelinek *
4470b5de56dSgjelinek * Return Z_OK if the path mapped to a ZFS file system name, otherwise return
4480b5de56dSgjelinek * Z_ERR.
4490b5de56dSgjelinek */
4500b5de56dSgjelinek static int
path2name(char * zonepath,char * zfs_name,int len)4510b5de56dSgjelinek path2name(char *zonepath, char *zfs_name, int len)
4520b5de56dSgjelinek {
4530b5de56dSgjelinek int res;
45411506c41Sgjelinek char *bnm, *dnm, *dname, *bname;
4550b5de56dSgjelinek zfs_handle_t *zhp;
45611506c41Sgjelinek struct stat stbuf;
4570b5de56dSgjelinek
4580b5de56dSgjelinek /*
45911506c41Sgjelinek * We need two tmp strings to handle paths directly in / (e.g. /foo)
46011506c41Sgjelinek * since dirname will overwrite the first char after "/" in this case.
4610b5de56dSgjelinek */
46211506c41Sgjelinek if ((bnm = strdup(zonepath)) == NULL)
4630b5de56dSgjelinek return (Z_ERR);
4640b5de56dSgjelinek
46511506c41Sgjelinek if ((dnm = strdup(zonepath)) == NULL) {
46611506c41Sgjelinek free(bnm);
46711506c41Sgjelinek return (Z_ERR);
46811506c41Sgjelinek }
4690b5de56dSgjelinek
47011506c41Sgjelinek bname = basename(bnm);
47111506c41Sgjelinek dname = dirname(dnm);
47211506c41Sgjelinek
47311506c41Sgjelinek /*
47411506c41Sgjelinek * This is a quick test to save iterating over all of the zfs datasets
47511506c41Sgjelinek * on the system (which can be a lot). If the parent dir is not in a
47611506c41Sgjelinek * ZFS fs, then we're done.
47711506c41Sgjelinek */
47811506c41Sgjelinek if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) ||
47911506c41Sgjelinek strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) {
48011506c41Sgjelinek free(bnm);
48111506c41Sgjelinek free(dnm);
48211506c41Sgjelinek return (Z_ERR);
48311506c41Sgjelinek }
48411506c41Sgjelinek
48511506c41Sgjelinek /* See if the parent directory is its own ZFS dataset. */
48611506c41Sgjelinek if ((zhp = mount2zhandle(dname)) == NULL) {
48711506c41Sgjelinek /*
48811506c41Sgjelinek * The parent is not a ZFS dataset so we can't automatically
48911506c41Sgjelinek * create a dataset on the given path.
49011506c41Sgjelinek */
49111506c41Sgjelinek free(bnm);
49211506c41Sgjelinek free(dnm);
49311506c41Sgjelinek return (Z_ERR);
49411506c41Sgjelinek }
49511506c41Sgjelinek
49611506c41Sgjelinek res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname);
49711506c41Sgjelinek
49811506c41Sgjelinek free(bnm);
49911506c41Sgjelinek free(dnm);
5000b5de56dSgjelinek zfs_close(zhp);
5010b5de56dSgjelinek if (res >= len)
5020b5de56dSgjelinek return (Z_ERR);
5030b5de56dSgjelinek
5040b5de56dSgjelinek return (Z_OK);
5050b5de56dSgjelinek }
5060b5de56dSgjelinek
5070b5de56dSgjelinek /*
5080b5de56dSgjelinek * A ZFS file system iterator call-back function used to determine if the
5090b5de56dSgjelinek * file system has dependents (snapshots & clones).
5100b5de56dSgjelinek */
5110b5de56dSgjelinek /* ARGSUSED */
5120b5de56dSgjelinek static int
has_dependent(zfs_handle_t * zhp,void * data)5130b5de56dSgjelinek has_dependent(zfs_handle_t *zhp, void *data)
5140b5de56dSgjelinek {
5150b5de56dSgjelinek zfs_close(zhp);
5160b5de56dSgjelinek return (1);
5170b5de56dSgjelinek }
5180b5de56dSgjelinek
5190b5de56dSgjelinek /*
5200b5de56dSgjelinek * Given a snapshot name, get the file system path where the snapshot lives.
5210b5de56dSgjelinek * A snapshot name is of the form fs_name@snap_name. For example, snapshot
5220b5de56dSgjelinek * pl/zones/z1@SUNWzone1 would have a path of
5230b5de56dSgjelinek * /pl/zones/z1/.zfs/snapshot/SUNWzone1.
5240b5de56dSgjelinek */
5250b5de56dSgjelinek static int
snap2path(char * snap_name,char * path,int len)5260b5de56dSgjelinek snap2path(char *snap_name, char *path, int len)
5270b5de56dSgjelinek {
5280b5de56dSgjelinek char *p;
5290b5de56dSgjelinek zfs_handle_t *zhp;
5300b5de56dSgjelinek char mp[ZFS_MAXPROPLEN];
5310b5de56dSgjelinek
5320b5de56dSgjelinek if ((p = strrchr(snap_name, '@')) == NULL)
5330b5de56dSgjelinek return (Z_ERR);
5340b5de56dSgjelinek
5350b5de56dSgjelinek /* Get the file system name from the snap_name. */
5360b5de56dSgjelinek *p = '\0';
537990b4856Slling zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET);
5380b5de56dSgjelinek *p = '@';
5390b5de56dSgjelinek if (zhp == NULL)
5400b5de56dSgjelinek return (Z_ERR);
5410b5de56dSgjelinek
5420b5de56dSgjelinek /* Get the file system mount point. */
5430b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL,
54499653d4eSeschrock 0, B_FALSE) != 0) {
5450b5de56dSgjelinek zfs_close(zhp);
5460b5de56dSgjelinek return (Z_ERR);
5470b5de56dSgjelinek }
5480b5de56dSgjelinek zfs_close(zhp);
5490b5de56dSgjelinek
5500b5de56dSgjelinek p++;
5510b5de56dSgjelinek if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len)
5520b5de56dSgjelinek return (Z_ERR);
5530b5de56dSgjelinek
5540b5de56dSgjelinek return (Z_OK);
5550b5de56dSgjelinek }
5560b5de56dSgjelinek
5570b5de56dSgjelinek /*
558286822ddS * This callback function is used to iterate through a snapshot's dependencies
559286822ddS * to find a filesystem that is a direct clone of the snapshot being iterated.
560286822ddS */
561286822ddS static int
get_direct_clone(zfs_handle_t * zhp,void * data)562286822ddS get_direct_clone(zfs_handle_t *zhp, void *data)
563286822ddS {
564286822ddS clone_data_t *cd = data;
565*40a5c998SMatthew Ahrens char origin[ZFS_MAX_DATASET_NAME_LEN];
566*40a5c998SMatthew Ahrens char ds_path[ZFS_MAX_DATASET_NAME_LEN];
567286822ddS
568286822ddS if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
569286822ddS zfs_close(zhp);
570286822ddS return (0);
571286822ddS }
572286822ddS
573286822ddS (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path));
574286822ddS
575286822ddS /* Make sure this is a direct clone of the snapshot we're iterating. */
576286822ddS if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
577286822ddS NULL, 0, B_FALSE) != 0 || strcmp(origin, cd->snapshot) != 0) {
578286822ddS zfs_close(zhp);
579286822ddS return (0);
580286822ddS }
581286822ddS
582286822ddS if (cd->clone_zhp != NULL)
583286822ddS zfs_close(cd->clone_zhp);
584286822ddS
585286822ddS cd->clone_zhp = zhp;
586286822ddS return (1);
587286822ddS }
588286822ddS
589286822ddS /*
590286822ddS * A ZFS file system iterator call-back function used to determine the clone
591286822ddS * to promote. This function finds the youngest (i.e. last one taken) snapshot
592286822ddS * that has a clone. If found, it returns a reference to that clone in the
593286822ddS * callback data.
594286822ddS */
595286822ddS static int
find_clone(zfs_handle_t * zhp,void * data)596286822ddS find_clone(zfs_handle_t *zhp, void *data)
597286822ddS {
598286822ddS clone_data_t *cd = data;
599286822ddS time_t snap_creation;
600286822ddS int zret = 0;
601286822ddS
602286822ddS /* If snapshot has no clones, skip it */
603286822ddS if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) {
604286822ddS zfs_close(zhp);
605286822ddS return (0);
606286822ddS }
607286822ddS
608286822ddS cd->snapshot = zfs_get_name(zhp);
609286822ddS
610286822ddS /* Get the creation time of this snapshot */
611286822ddS snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION);
612286822ddS
613286822ddS /*
614286822ddS * If this snapshot's creation time is greater than (i.e. younger than)
615286822ddS * the current youngest snapshot found, iterate this snapshot to
616286822ddS * get the right clone.
617286822ddS */
618286822ddS if (snap_creation >= cd->origin_creation) {
619286822ddS /*
620286822ddS * Iterate the dependents of this snapshot to find a clone
621286822ddS * that's a direct dependent.
622286822ddS */
623286822ddS if ((zret = zfs_iter_dependents(zhp, B_FALSE, get_direct_clone,
624286822ddS cd)) == -1) {
625286822ddS zfs_close(zhp);
626286822ddS return (1);
627286822ddS } else if (zret == 1) {
628286822ddS /*
629286822ddS * Found a clone, update the origin_creation time
630286822ddS * in the callback data.
631286822ddS */
632286822ddS cd->origin_creation = snap_creation;
633286822ddS }
634286822ddS }
635286822ddS
636286822ddS zfs_close(zhp);
637286822ddS return (0);
638286822ddS }
639286822ddS
640286822ddS /*
641286822ddS * A ZFS file system iterator call-back function used to remove standalone
642286822ddS * snapshots.
643286822ddS */
644286822ddS /* ARGSUSED */
645286822ddS static int
rm_snap(zfs_handle_t * zhp,void * data)646286822ddS rm_snap(zfs_handle_t *zhp, void *data)
647286822ddS {
648286822ddS /* If snapshot has clones, something is wrong */
649286822ddS if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) {
650286822ddS zfs_close(zhp);
651286822ddS return (1);
652286822ddS }
653286822ddS
654286822ddS if (zfs_unmount(zhp, NULL, 0) == 0) {
655842727c2SChris Kirby (void) zfs_destroy(zhp, B_FALSE);
656286822ddS }
657286822ddS
658286822ddS zfs_close(zhp);
659286822ddS return (0);
660286822ddS }
661286822ddS
662286822ddS /*
663286822ddS * A ZFS snapshot iterator call-back function which renames snapshots.
664286822ddS */
665286822ddS static int
rename_snap(zfs_handle_t * zhp,void * data)666286822ddS rename_snap(zfs_handle_t *zhp, void *data)
667286822ddS {
668286822ddS int res;
669286822ddS zfs_snapshot_data_t *cbp;
670*40a5c998SMatthew Ahrens char template[ZFS_MAX_DATASET_NAME_LEN];
671286822ddS
672286822ddS cbp = (zfs_snapshot_data_t *)data;
673286822ddS
674286822ddS /*
675286822ddS * When renaming snapshots with the iterator, the iterator can see
676286822ddS * the same snapshot after we've renamed up in the namespace. To
677286822ddS * prevent this we check the count for the number of snapshots we have
678286822ddS * to rename and stop at that point.
679286822ddS */
680286822ddS if (cbp->cntr >= cbp->num) {
681286822ddS zfs_close(zhp);
682286822ddS return (0);
683286822ddS }
684286822ddS
685286822ddS if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) {
686286822ddS zfs_close(zhp);
687286822ddS return (0);
688286822ddS }
689286822ddS
690286822ddS /* Only rename the snapshots we automatically generate when we clone. */
691286822ddS if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) != 0) {
692286822ddS zfs_close(zhp);
693286822ddS return (0);
694286822ddS }
695286822ddS
696286822ddS (void) snprintf(template, sizeof (template), "%s%d", cbp->match_name,
697286822ddS cbp->max++);
698286822ddS
6996a9cb0eaSEric Schrock res = (zfs_rename(zhp, template, B_FALSE, B_FALSE) != 0);
700286822ddS if (res != 0)
701286822ddS (void) fprintf(stderr, gettext("failed to rename snapshot %s "
702286822ddS "to %s: %s\n"), zfs_get_name(zhp), template,
703286822ddS libzfs_error_description(g_zfs));
704286822ddS
705286822ddS cbp->cntr++;
706286822ddS
707286822ddS zfs_close(zhp);
708286822ddS return (res);
709286822ddS }
710286822ddS
711286822ddS /*
712286822ddS * Rename the source dataset's snapshots that are automatically generated when
713286822ddS * we clone a zone so that there won't be a name collision when we promote the
714286822ddS * cloned dataset. Once the snapshots have been renamed, then promote the
715286822ddS * clone.
716286822ddS *
717286822ddS * The snapshot rename process gets the highest number on the snapshot names
718286822ddS * (the format is zonename@SUNWzoneXX where XX are digits) on both the source
719286822ddS * and clone datasets, then renames the source dataset snapshots starting at
720286822ddS * the next number.
721286822ddS */
722286822ddS static int
promote_clone(zfs_handle_t * src_zhp,zfs_handle_t * cln_zhp)723286822ddS promote_clone(zfs_handle_t *src_zhp, zfs_handle_t *cln_zhp)
724286822ddS {
725286822ddS zfs_snapshot_data_t sd;
726*40a5c998SMatthew Ahrens char nm[ZFS_MAX_DATASET_NAME_LEN];
727*40a5c998SMatthew Ahrens char template[ZFS_MAX_DATASET_NAME_LEN];
728286822ddS
729286822ddS (void) strlcpy(nm, zfs_get_name(cln_zhp), sizeof (nm));
730286822ddS /*
731286822ddS * Start by getting the clone's snapshot max which we use
732286822ddS * during the rename of the original dataset's snapshots.
733286822ddS */
734286822ddS (void) snprintf(template, sizeof (template), "%s@SUNWzone", nm);
735286822ddS sd.match_name = template;
736286822ddS sd.len = strlen(template);
737286822ddS sd.max = 0;
738286822ddS
739286822ddS if (zfs_iter_snapshots(cln_zhp, get_snap_max, &sd) != 0)
740286822ddS return (Z_ERR);
741286822ddS
742286822ddS /*
743286822ddS * Now make sure the source's snapshot max is at least as high as
744286822ddS * the clone's snapshot max.
745286822ddS */
746286822ddS (void) snprintf(template, sizeof (template), "%s@SUNWzone",
747286822ddS zfs_get_name(src_zhp));
748286822ddS sd.match_name = template;
749286822ddS sd.len = strlen(template);
750286822ddS sd.num = 0;
751286822ddS
752286822ddS if (zfs_iter_snapshots(src_zhp, get_snap_max, &sd) != 0)
753286822ddS return (Z_ERR);
754286822ddS
755286822ddS /*
756286822ddS * Now rename the source dataset's snapshots so there's no
757286822ddS * conflict when we promote the clone.
758286822ddS */
759286822ddS sd.max++;
760286822ddS sd.cntr = 0;
761286822ddS if (zfs_iter_snapshots(src_zhp, rename_snap, &sd) != 0)
762286822ddS return (Z_ERR);
763286822ddS
764286822ddS /* close and reopen the clone dataset to get the latest info */
765286822ddS zfs_close(cln_zhp);
766286822ddS if ((cln_zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
767286822ddS return (Z_ERR);
768286822ddS
769286822ddS if (zfs_promote(cln_zhp) != 0) {
770286822ddS (void) fprintf(stderr, gettext("failed to promote %s: %s\n"),
771286822ddS nm, libzfs_error_description(g_zfs));
772286822ddS return (Z_ERR);
773286822ddS }
774286822ddS
775286822ddS zfs_close(cln_zhp);
776286822ddS return (Z_OK);
777286822ddS }
778286822ddS
779286822ddS /*
780286822ddS * Promote the youngest clone. That clone will then become the origin of all
781286822ddS * of the other clones that were hanging off of the source dataset.
782286822ddS */
783286822ddS int
promote_all_clones(zfs_handle_t * zhp)784286822ddS promote_all_clones(zfs_handle_t *zhp)
785286822ddS {
786286822ddS clone_data_t cd;
787*40a5c998SMatthew Ahrens char nm[ZFS_MAX_DATASET_NAME_LEN];
788286822ddS
789286822ddS cd.clone_zhp = NULL;
790286822ddS cd.origin_creation = 0;
791286822ddS cd.snapshot = NULL;
792286822ddS
793286822ddS if (zfs_iter_snapshots(zhp, find_clone, &cd) != 0) {
794286822ddS zfs_close(zhp);
795286822ddS return (Z_ERR);
796286822ddS }
797286822ddS
798286822ddS /* Nothing to promote. */
799286822ddS if (cd.clone_zhp == NULL)
800286822ddS return (Z_OK);
801286822ddS
802286822ddS /* Found the youngest clone to promote. Promote it. */
803286822ddS if (promote_clone(zhp, cd.clone_zhp) != 0) {
804286822ddS zfs_close(cd.clone_zhp);
805286822ddS zfs_close(zhp);
806286822ddS return (Z_ERR);
807286822ddS }
808286822ddS
809286822ddS /* close and reopen the main dataset to get the latest info */
810286822ddS (void) strlcpy(nm, zfs_get_name(zhp), sizeof (nm));
811286822ddS zfs_close(zhp);
812286822ddS if ((zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL)
813286822ddS return (Z_ERR);
814286822ddS
815286822ddS return (Z_OK);
816286822ddS }
817286822ddS
818286822ddS /*
8190b5de56dSgjelinek * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if
8200b5de56dSgjelinek * possible, or by copying the data from the snapshot to the zonepath.
8210b5de56dSgjelinek */
8220b5de56dSgjelinek int
clone_snapshot_zfs(char * snap_name,char * zonepath,char * validatesnap)823ff17c8bfSgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap)
8240b5de56dSgjelinek {
8250b5de56dSgjelinek int err = Z_OK;
8260b5de56dSgjelinek char clone_name[MAXPATHLEN];
8270b5de56dSgjelinek char snap_path[MAXPATHLEN];
8280b5de56dSgjelinek
8290b5de56dSgjelinek if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) {
8300b5de56dSgjelinek (void) fprintf(stderr, gettext("unable to find path for %s.\n"),
8310b5de56dSgjelinek snap_name);
8320b5de56dSgjelinek return (Z_ERR);
8330b5de56dSgjelinek }
8340b5de56dSgjelinek
835ff17c8bfSgjelinek if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK)
8360b5de56dSgjelinek return (Z_NO_ENTRY);
8370b5de56dSgjelinek
8380b5de56dSgjelinek /*
8390b5de56dSgjelinek * The zonepath cannot be ZFS cloned, try to copy the data from
8400b5de56dSgjelinek * within the snapshot to the zonepath.
8410b5de56dSgjelinek */
8420b5de56dSgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
8430b5de56dSgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8440b5de56dSgjelinek if (clean_out_clone() != Z_OK)
8450b5de56dSgjelinek (void) fprintf(stderr,
8460b5de56dSgjelinek gettext("could not remove the "
8470b5de56dSgjelinek "software inventory from %s\n"), zonepath);
8480b5de56dSgjelinek
8490b5de56dSgjelinek return (err);
8500b5de56dSgjelinek }
8510b5de56dSgjelinek
8520b5de56dSgjelinek if ((err = clone_snap(snap_name, clone_name)) != Z_OK) {
8530b5de56dSgjelinek if (err != Z_NO_ENTRY) {
8540b5de56dSgjelinek /*
8550b5de56dSgjelinek * Cloning the snapshot failed. Fall back to trying
8560b5de56dSgjelinek * to install the zone by copying from the snapshot.
8570b5de56dSgjelinek */
8580b5de56dSgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK)
8590b5de56dSgjelinek if (clean_out_clone() != Z_OK)
8600b5de56dSgjelinek (void) fprintf(stderr,
8610b5de56dSgjelinek gettext("could not remove the "
8620b5de56dSgjelinek "software inventory from %s\n"),
8630b5de56dSgjelinek zonepath);
8640b5de56dSgjelinek } else {
8650b5de56dSgjelinek /*
8660b5de56dSgjelinek * The snapshot is unusable for some reason so restore
8670b5de56dSgjelinek * the zone state to configured since we were unable to
8680b5de56dSgjelinek * actually do anything about getting the zone
8690b5de56dSgjelinek * installed.
8700b5de56dSgjelinek */
8710b5de56dSgjelinek int tmp;
8720b5de56dSgjelinek
8730b5de56dSgjelinek if ((tmp = zone_set_state(target_zone,
8740b5de56dSgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) {
8750b5de56dSgjelinek errno = tmp;
8760b5de56dSgjelinek zperror2(target_zone,
8770b5de56dSgjelinek gettext("could not set state"));
8780b5de56dSgjelinek }
8790b5de56dSgjelinek }
8800b5de56dSgjelinek }
8810b5de56dSgjelinek
8820b5de56dSgjelinek return (err);
8830b5de56dSgjelinek }
8840b5de56dSgjelinek
8850b5de56dSgjelinek /*
8860b5de56dSgjelinek * Attempt to clone a source_zone to a target zonepath by using a ZFS clone.
8870b5de56dSgjelinek */
8880b5de56dSgjelinek int
clone_zfs(char * source_zonepath,char * zonepath,char * presnapbuf,char * postsnapbuf)889ff17c8bfSgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf,
890ff17c8bfSgjelinek char *postsnapbuf)
8910b5de56dSgjelinek {
8920b5de56dSgjelinek zfs_handle_t *zhp;
8930b5de56dSgjelinek char clone_name[MAXPATHLEN];
8940b5de56dSgjelinek char snap_name[MAXPATHLEN];
8950b5de56dSgjelinek
8960b5de56dSgjelinek /*
8970b5de56dSgjelinek * Try to get a zfs handle for the source_zonepath. If this fails
8980b5de56dSgjelinek * the source_zonepath is not ZFS so return an error.
8990b5de56dSgjelinek */
9000b5de56dSgjelinek if ((zhp = mount2zhandle(source_zonepath)) == NULL)
9010b5de56dSgjelinek return (Z_ERR);
9020b5de56dSgjelinek
9030b5de56dSgjelinek /*
9040b5de56dSgjelinek * Check if there is a file system already mounted on zonepath. If so,
9050b5de56dSgjelinek * we can't clone to the path so we should fall back to copying.
9060b5de56dSgjelinek */
9070b5de56dSgjelinek if (is_mountpnt(zonepath)) {
9080b5de56dSgjelinek zfs_close(zhp);
9090b5de56dSgjelinek (void) fprintf(stderr,
9100b5de56dSgjelinek gettext("A file system is already mounted on %s,\n"
9110b5de56dSgjelinek "preventing use of a ZFS clone.\n"), zonepath);
9120b5de56dSgjelinek return (Z_ERR);
9130b5de56dSgjelinek }
9140b5de56dSgjelinek
9150b5de56dSgjelinek /*
9160b5de56dSgjelinek * Instead of using path2name to get the clone name from the zonepath,
9170b5de56dSgjelinek * we could generate a name from the source zone ZFS name. However,
9180b5de56dSgjelinek * this would mean we would create the clone under the ZFS fs of the
9190b5de56dSgjelinek * source instead of what the zonepath says. For example,
9200b5de56dSgjelinek *
9210b5de56dSgjelinek * source_zonepath zonepath
9220b5de56dSgjelinek * /pl/zones/dev/z1 /pl/zones/deploy/z2
9230b5de56dSgjelinek *
9240b5de56dSgjelinek * We don't want the clone to be under "dev", we want it under
9250b5de56dSgjelinek * "deploy", so that we can leverage the normal attribute inheritance
9260b5de56dSgjelinek * that ZFS provides in the fs hierarchy.
9270b5de56dSgjelinek */
9280b5de56dSgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) {
9290b5de56dSgjelinek zfs_close(zhp);
9300b5de56dSgjelinek return (Z_ERR);
9310b5de56dSgjelinek }
9320b5de56dSgjelinek
933ff17c8bfSgjelinek if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf,
934ff17c8bfSgjelinek postsnapbuf) != Z_OK) {
9350b5de56dSgjelinek zfs_close(zhp);
9360b5de56dSgjelinek return (Z_ERR);
9370b5de56dSgjelinek }
9380b5de56dSgjelinek zfs_close(zhp);
9390b5de56dSgjelinek
940d9e728a2Sgjelinek if (clone_snap(snap_name, clone_name) != Z_OK) {
941d9e728a2Sgjelinek /* Clean up the snapshot we just took. */
942d9e728a2Sgjelinek if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT))
943d9e728a2Sgjelinek != NULL) {
944d9e728a2Sgjelinek if (zfs_unmount(zhp, NULL, 0) == 0)
945842727c2SChris Kirby (void) zfs_destroy(zhp, B_FALSE);
946d9e728a2Sgjelinek zfs_close(zhp);
947d9e728a2Sgjelinek }
948d9e728a2Sgjelinek
9490b5de56dSgjelinek return (Z_ERR);
950d9e728a2Sgjelinek }
9510b5de56dSgjelinek
9520b5de56dSgjelinek (void) printf(gettext("Instead of copying, a ZFS clone has been "
9530b5de56dSgjelinek "created for this zone.\n"));
9540b5de56dSgjelinek
9550b5de56dSgjelinek return (Z_OK);
9560b5de56dSgjelinek }
9570b5de56dSgjelinek
9580b5de56dSgjelinek /*
9590b5de56dSgjelinek * Attempt to create a ZFS file system for the specified zonepath.
9600b5de56dSgjelinek * We either will successfully create a ZFS file system and get it mounted
9610b5de56dSgjelinek * on the zonepath or we don't. The caller doesn't care since a regular
9620b5de56dSgjelinek * directory is used for the zonepath if no ZFS file system is mounted there.
9630b5de56dSgjelinek */
9640b5de56dSgjelinek void
create_zfs_zonepath(char * zonepath)9650b5de56dSgjelinek create_zfs_zonepath(char *zonepath)
9660b5de56dSgjelinek {
9670b5de56dSgjelinek zfs_handle_t *zhp;
9680b5de56dSgjelinek char zfs_name[MAXPATHLEN];
969e9dbad6fSeschrock nvlist_t *props = NULL;
9700b5de56dSgjelinek
9710b5de56dSgjelinek if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK)
9720b5de56dSgjelinek return;
9730b5de56dSgjelinek
974d1f855d7S /* Check if the dataset already exists. */
975d1f855d7S if ((zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) != NULL) {
976d1f855d7S zfs_close(zhp);
977d1f855d7S return;
978d1f855d7S }
979d1f855d7S
9802b6c28b8Sbatschul /*
9812b6c28b8Sbatschul * We turn off zfs SHARENFS and SHARESMB properties on the
9822b6c28b8Sbatschul * zoneroot dataset in order to prevent the GZ from sharing
9832b6c28b8Sbatschul * NGZ data by accident.
9842b6c28b8Sbatschul */
9852b6c28b8Sbatschul if ((nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) ||
9862b6c28b8Sbatschul (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS),
9872b6c28b8Sbatschul "off") != 0) ||
9882b6c28b8Sbatschul (nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARESMB),
9892b6c28b8Sbatschul "off") != 0)) {
990e9dbad6fSeschrock nvlist_free(props);
991e9dbad6fSeschrock (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
992e9dbad6fSeschrock "out of memory\n"), zfs_name);
993e9dbad6fSeschrock }
994e9dbad6fSeschrock
995e9dbad6fSeschrock if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 ||
996990b4856Slling (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) {
99799653d4eSeschrock (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: "
99899653d4eSeschrock "%s\n"), zfs_name, libzfs_error_description(g_zfs));
999e9dbad6fSeschrock nvlist_free(props);
10000b5de56dSgjelinek return;
10010b5de56dSgjelinek }
10020b5de56dSgjelinek
1003e9dbad6fSeschrock nvlist_free(props);
1004e9dbad6fSeschrock
10050b5de56dSgjelinek if (zfs_mount(zhp, NULL, 0) != 0) {
100699653d4eSeschrock (void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: "
100799653d4eSeschrock "%s\n"), zfs_name, libzfs_error_description(g_zfs));
1008842727c2SChris Kirby (void) zfs_destroy(zhp, B_FALSE);
10090b5de56dSgjelinek } else {
10100b5de56dSgjelinek if (chmod(zonepath, S_IRWXU) != 0) {
10110b5de56dSgjelinek (void) fprintf(stderr, gettext("file system %s "
10120b5de56dSgjelinek "successfully created, but chmod %o failed: %s\n"),
10130b5de56dSgjelinek zfs_name, S_IRWXU, strerror(errno));
10140b5de56dSgjelinek (void) destroy_zfs(zonepath);
10150b5de56dSgjelinek } else {
10160b5de56dSgjelinek (void) printf(gettext("A ZFS file system has been "
10170b5de56dSgjelinek "created for this zone.\n"));
10180b5de56dSgjelinek }
10190b5de56dSgjelinek }
10200b5de56dSgjelinek
10210b5de56dSgjelinek zfs_close(zhp);
10220b5de56dSgjelinek }
10230b5de56dSgjelinek
10240b5de56dSgjelinek /*
10250b5de56dSgjelinek * If the zonepath is a ZFS file system, attempt to destroy it. We return Z_OK
10260b5de56dSgjelinek * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR
10270b5de56dSgjelinek * which means the caller should clean up the zonepath in the traditional
10280b5de56dSgjelinek * way.
10290b5de56dSgjelinek */
10300b5de56dSgjelinek int
destroy_zfs(char * zonepath)10310b5de56dSgjelinek destroy_zfs(char *zonepath)
10320b5de56dSgjelinek {
10330b5de56dSgjelinek zfs_handle_t *zhp;
10340b5de56dSgjelinek boolean_t is_clone = B_FALSE;
10350b5de56dSgjelinek char origin[ZFS_MAXPROPLEN];
10360b5de56dSgjelinek
103799653d4eSeschrock if ((zhp = mount2zhandle(zonepath)) == NULL)
10380b5de56dSgjelinek return (Z_ERR);
10390b5de56dSgjelinek
1040286822ddS if (promote_all_clones(zhp) != 0)
1041286822ddS return (Z_ERR);
1042286822ddS
1043286822ddS /* Now cleanup any snapshots remaining. */
1044286822ddS if (zfs_iter_snapshots(zhp, rm_snap, NULL) != 0) {
1045286822ddS zfs_close(zhp);
1046286822ddS return (Z_ERR);
1047286822ddS }
1048286822ddS
10490b5de56dSgjelinek /*
1050286822ddS * We can't destroy the file system if it has still has dependents.
1051286822ddS * There shouldn't be any at this point, but we'll double check.
10520b5de56dSgjelinek */
1053286822ddS if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0) {
1054286822ddS (void) fprintf(stderr, gettext("zfs destroy %s failed: the "
1055286822ddS "dataset still has dependents\n"), zfs_get_name(zhp));
10560b5de56dSgjelinek zfs_close(zhp);
10570b5de56dSgjelinek return (Z_ERR);
10580b5de56dSgjelinek }
10590b5de56dSgjelinek
10600b5de56dSgjelinek /*
10610b5de56dSgjelinek * This might be a clone. Try to get the snapshot so we can attempt
10620b5de56dSgjelinek * to destroy that as well.
10630b5de56dSgjelinek */
10640b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL,
106599653d4eSeschrock NULL, 0, B_FALSE) == 0)
10660b5de56dSgjelinek is_clone = B_TRUE;
10670b5de56dSgjelinek
1068286822ddS if (zfs_unmount(zhp, NULL, 0) != 0) {
1069286822ddS (void) fprintf(stderr, gettext("zfs unmount %s failed: %s\n"),
1070286822ddS zfs_get_name(zhp), libzfs_error_description(g_zfs));
1071286822ddS zfs_close(zhp);
1072286822ddS return (Z_ERR);
1073286822ddS }
1074286822ddS
1075842727c2SChris Kirby if (zfs_destroy(zhp, B_FALSE) != 0) {
10760b5de56dSgjelinek /*
10770b5de56dSgjelinek * If the destroy fails for some reason, try to remount
10780b5de56dSgjelinek * the file system so that we can use "rm -rf" to clean up
10790b5de56dSgjelinek * instead.
10800b5de56dSgjelinek */
1081286822ddS (void) fprintf(stderr, gettext("zfs destroy %s failed: %s\n"),
1082286822ddS zfs_get_name(zhp), libzfs_error_description(g_zfs));
10830b5de56dSgjelinek (void) zfs_mount(zhp, NULL, 0);
10840b5de56dSgjelinek zfs_close(zhp);
10850b5de56dSgjelinek return (Z_ERR);
10860b5de56dSgjelinek }
10870b5de56dSgjelinek
1088d9e728a2Sgjelinek /*
1089d9e728a2Sgjelinek * If the zone has ever been moved then the mountpoint dir will not be
1090d9e728a2Sgjelinek * cleaned up by the zfs_destroy(). To handle this case try to clean
1091d9e728a2Sgjelinek * it up now but don't worry if it fails, that will be normal.
1092d9e728a2Sgjelinek */
1093d9e728a2Sgjelinek (void) rmdir(zonepath);
1094d9e728a2Sgjelinek
10950b5de56dSgjelinek (void) printf(gettext("The ZFS file system for this zone has been "
10960b5de56dSgjelinek "destroyed.\n"));
10970b5de56dSgjelinek
10980b5de56dSgjelinek if (is_clone) {
10990b5de56dSgjelinek zfs_handle_t *ohp;
11000b5de56dSgjelinek
11010b5de56dSgjelinek /*
11020b5de56dSgjelinek * Try to clean up the snapshot that the clone was taken from.
11030b5de56dSgjelinek */
110499653d4eSeschrock if ((ohp = zfs_open(g_zfs, origin,
110599653d4eSeschrock ZFS_TYPE_SNAPSHOT)) != NULL) {
11063bb79becSeschrock if (zfs_iter_dependents(ohp, B_TRUE, has_dependent,
11073bb79becSeschrock NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0)
1108842727c2SChris Kirby (void) zfs_destroy(ohp, B_FALSE);
11090b5de56dSgjelinek zfs_close(ohp);
11100b5de56dSgjelinek }
11110b5de56dSgjelinek }
11120b5de56dSgjelinek
11130b5de56dSgjelinek zfs_close(zhp);
11140b5de56dSgjelinek return (Z_OK);
11150b5de56dSgjelinek }
11160b5de56dSgjelinek
11170b5de56dSgjelinek /*
11180b5de56dSgjelinek * Return true if the path is its own zfs file system. We determine this
11190b5de56dSgjelinek * by stat-ing the path to see if it is zfs and stat-ing the parent to see
11200b5de56dSgjelinek * if it is a different fs.
11210b5de56dSgjelinek */
11220b5de56dSgjelinek boolean_t
is_zonepath_zfs(char * zonepath)11230b5de56dSgjelinek is_zonepath_zfs(char *zonepath)
11240b5de56dSgjelinek {
11250b5de56dSgjelinek int res;
11260b5de56dSgjelinek char *path;
11270b5de56dSgjelinek char *parent;
11283f2f09c1Sdp struct statvfs64 buf1, buf2;
11290b5de56dSgjelinek
11303f2f09c1Sdp if (statvfs64(zonepath, &buf1) != 0)
11310b5de56dSgjelinek return (B_FALSE);
11320b5de56dSgjelinek
11330b5de56dSgjelinek if (strcmp(buf1.f_basetype, "zfs") != 0)
11340b5de56dSgjelinek return (B_FALSE);
11350b5de56dSgjelinek
11360b5de56dSgjelinek if ((path = strdup(zonepath)) == NULL)
11370b5de56dSgjelinek return (B_FALSE);
11380b5de56dSgjelinek
11390b5de56dSgjelinek parent = dirname(path);
11403f2f09c1Sdp res = statvfs64(parent, &buf2);
11410b5de56dSgjelinek free(path);
11420b5de56dSgjelinek
11430b5de56dSgjelinek if (res != 0)
11440b5de56dSgjelinek return (B_FALSE);
11450b5de56dSgjelinek
11460b5de56dSgjelinek if (buf1.f_fsid == buf2.f_fsid)
11470b5de56dSgjelinek return (B_FALSE);
11480b5de56dSgjelinek
11490b5de56dSgjelinek return (B_TRUE);
11500b5de56dSgjelinek }
11510b5de56dSgjelinek
11520b5de56dSgjelinek /*
11530b5de56dSgjelinek * Implement the fast move of a ZFS file system by simply updating the
11540b5de56dSgjelinek * mountpoint. Since it is file system already, we don't have the
11550b5de56dSgjelinek * issue of cross-file system copying.
11560b5de56dSgjelinek */
11570b5de56dSgjelinek int
move_zfs(char * zonepath,char * new_zonepath)11580b5de56dSgjelinek move_zfs(char *zonepath, char *new_zonepath)
11590b5de56dSgjelinek {
11600b5de56dSgjelinek int ret = Z_ERR;
11610b5de56dSgjelinek zfs_handle_t *zhp;
11620b5de56dSgjelinek
116399653d4eSeschrock if ((zhp = mount2zhandle(zonepath)) == NULL)
11640b5de56dSgjelinek return (Z_ERR);
11650b5de56dSgjelinek
1166e9dbad6fSeschrock if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
1167e9dbad6fSeschrock new_zonepath) == 0) {
11680b5de56dSgjelinek /*
11690b5de56dSgjelinek * Clean up the old mount point. We ignore any failure since
11700b5de56dSgjelinek * the zone is already successfully mounted on the new path.
11710b5de56dSgjelinek */
11720b5de56dSgjelinek (void) rmdir(zonepath);
11730b5de56dSgjelinek ret = Z_OK;
11740b5de56dSgjelinek }
11750b5de56dSgjelinek
11760b5de56dSgjelinek zfs_close(zhp);
11770b5de56dSgjelinek
11780b5de56dSgjelinek return (ret);
11790b5de56dSgjelinek }
11800b5de56dSgjelinek
11810b5de56dSgjelinek /*
11820b5de56dSgjelinek * Validate that the given dataset exists on the system, and that neither it nor
11830b5de56dSgjelinek * its children are zvols.
11840b5de56dSgjelinek *
11850b5de56dSgjelinek * Note that we don't do anything with the 'zoned' property here. All
11860b5de56dSgjelinek * management is done in zoneadmd when the zone is actually rebooted. This
11870b5de56dSgjelinek * allows us to automatically set the zoned property even when a zone is
11880b5de56dSgjelinek * rebooted by the administrator.
11890b5de56dSgjelinek */
11900b5de56dSgjelinek int
verify_datasets(zone_dochandle_t handle)11910b5de56dSgjelinek verify_datasets(zone_dochandle_t handle)
11920b5de56dSgjelinek {
11930b5de56dSgjelinek int return_code = Z_OK;
11940b5de56dSgjelinek struct zone_dstab dstab;
11950b5de56dSgjelinek zfs_handle_t *zhp;
11960b5de56dSgjelinek char propbuf[ZFS_MAXPROPLEN];
1197*40a5c998SMatthew Ahrens char source[ZFS_MAX_DATASET_NAME_LEN];
1198990b4856Slling zprop_source_t srctype;
11990b5de56dSgjelinek
12000b5de56dSgjelinek if (zonecfg_setdsent(handle) != Z_OK) {
12010b5de56dSgjelinek /*
12020b5de56dSgjelinek * TRANSLATION_NOTE
12030b5de56dSgjelinek * zfs and dataset are literals that should not be translated.
12040b5de56dSgjelinek */
12050b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs datasets: "
12060b5de56dSgjelinek "unable to enumerate datasets\n"));
12070b5de56dSgjelinek return (Z_ERR);
12080b5de56dSgjelinek }
12090b5de56dSgjelinek
12100b5de56dSgjelinek while (zonecfg_getdsent(handle, &dstab) == Z_OK) {
12110b5de56dSgjelinek
121299653d4eSeschrock if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name,
12130b5de56dSgjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) {
121499653d4eSeschrock (void) fprintf(stderr, gettext("could not verify zfs "
121599653d4eSeschrock "dataset %s: %s\n"), dstab.zone_dataset_name,
121699653d4eSeschrock libzfs_error_description(g_zfs));
12170b5de56dSgjelinek return_code = Z_ERR;
12180b5de56dSgjelinek continue;
12190b5de56dSgjelinek }
12200b5de56dSgjelinek
12210b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf,
12220b5de56dSgjelinek sizeof (propbuf), &srctype, source,
12230b5de56dSgjelinek sizeof (source), 0) == 0 &&
1224990b4856Slling (srctype == ZPROP_SRC_INHERITED)) {
12250b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs "
12260b5de56dSgjelinek "dataset %s: mountpoint cannot be inherited\n"),
12270b5de56dSgjelinek dstab.zone_dataset_name);
12280b5de56dSgjelinek return_code = Z_ERR;
12290b5de56dSgjelinek zfs_close(zhp);
12300b5de56dSgjelinek continue;
12310b5de56dSgjelinek }
12320b5de56dSgjelinek
12330b5de56dSgjelinek zfs_close(zhp);
12340b5de56dSgjelinek }
12350b5de56dSgjelinek (void) zonecfg_enddsent(handle);
12360b5de56dSgjelinek
12370b5de56dSgjelinek return (return_code);
12380b5de56dSgjelinek }
12390b5de56dSgjelinek
12400b5de56dSgjelinek /*
12410b5de56dSgjelinek * Verify that the ZFS dataset exists, and its mountpoint
12420b5de56dSgjelinek * property is set to "legacy".
12430b5de56dSgjelinek */
12440b5de56dSgjelinek int
verify_fs_zfs(struct zone_fstab * fstab)12450b5de56dSgjelinek verify_fs_zfs(struct zone_fstab *fstab)
12460b5de56dSgjelinek {
12470b5de56dSgjelinek zfs_handle_t *zhp;
12480b5de56dSgjelinek char propbuf[ZFS_MAXPROPLEN];
12490b5de56dSgjelinek
125099653d4eSeschrock if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special,
1251990b4856Slling ZFS_TYPE_DATASET)) == NULL) {
12520b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: "
12530b5de56dSgjelinek "could not access zfs dataset '%s'\n"),
12540b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special);
12550b5de56dSgjelinek return (Z_ERR);
12560b5de56dSgjelinek }
12570b5de56dSgjelinek
12580b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) {
12590b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify fs %s: "
12600b5de56dSgjelinek "'%s' is not a file system\n"),
12610b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special);
12620b5de56dSgjelinek zfs_close(zhp);
12630b5de56dSgjelinek return (Z_ERR);
12640b5de56dSgjelinek }
12650b5de56dSgjelinek
12660b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
12670b5de56dSgjelinek NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) {
12680b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: "
12690b5de56dSgjelinek "zfs '%s' mountpoint is not \"legacy\"\n"),
12700b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special);
12710b5de56dSgjelinek zfs_close(zhp);
12720b5de56dSgjelinek return (Z_ERR);
12730b5de56dSgjelinek }
12740b5de56dSgjelinek
12750b5de56dSgjelinek zfs_close(zhp);
127699653d4eSeschrock return (Z_OK);
127799653d4eSeschrock }
127899653d4eSeschrock
12790094b373Sjv227347 /*
12800094b373Sjv227347 * Destroy the specified mnttab structure that was created by mnttab_dup().
12810094b373Sjv227347 * NOTE: The structure's mnt_time field isn't freed.
12820094b373Sjv227347 */
12830094b373Sjv227347 static void
mnttab_destroy(struct mnttab * tabp)12840094b373Sjv227347 mnttab_destroy(struct mnttab *tabp)
12850094b373Sjv227347 {
12860094b373Sjv227347 assert(tabp != NULL);
12870094b373Sjv227347
12880094b373Sjv227347 free(tabp->mnt_mountp);
12890094b373Sjv227347 free(tabp->mnt_special);
12900094b373Sjv227347 free(tabp->mnt_fstype);
12910094b373Sjv227347 free(tabp->mnt_mntopts);
12920094b373Sjv227347 free(tabp);
12930094b373Sjv227347 }
12940094b373Sjv227347
12950094b373Sjv227347 /*
12960094b373Sjv227347 * Duplicate the specified mnttab structure. The mnt_mountp and mnt_time
12970094b373Sjv227347 * fields aren't duplicated. This function returns a pointer to the new mnttab
12980094b373Sjv227347 * structure or NULL if an error occurred. If an error occurs, then this
12990094b373Sjv227347 * function sets errno to reflect the error. mnttab structures created by
13000094b373Sjv227347 * this function should be destroyed via mnttab_destroy().
13010094b373Sjv227347 */
13020094b373Sjv227347 static struct mnttab *
mnttab_dup(const struct mnttab * srcp)13030094b373Sjv227347 mnttab_dup(const struct mnttab *srcp)
13040094b373Sjv227347 {
13050094b373Sjv227347 struct mnttab *retval;
13060094b373Sjv227347
13070094b373Sjv227347 assert(srcp != NULL);
13080094b373Sjv227347
13090094b373Sjv227347 retval = (struct mnttab *)calloc(1, sizeof (*retval));
13100094b373Sjv227347 if (retval == NULL) {
13110094b373Sjv227347 errno = ENOMEM;
13120094b373Sjv227347 return (NULL);
13130094b373Sjv227347 }
13140094b373Sjv227347 if (srcp->mnt_special != NULL) {
13150094b373Sjv227347 retval->mnt_special = strdup(srcp->mnt_special);
13160094b373Sjv227347 if (retval->mnt_special == NULL)
13170094b373Sjv227347 goto err;
13180094b373Sjv227347 }
13190094b373Sjv227347 if (srcp->mnt_fstype != NULL) {
13200094b373Sjv227347 retval->mnt_fstype = strdup(srcp->mnt_fstype);
13210094b373Sjv227347 if (retval->mnt_fstype == NULL)
13220094b373Sjv227347 goto err;
13230094b373Sjv227347 }
13240094b373Sjv227347 retval->mnt_mntopts = (char *)malloc(MAX_MNTOPT_STR * sizeof (char));
13250094b373Sjv227347 if (retval->mnt_mntopts == NULL)
13260094b373Sjv227347 goto err;
13270094b373Sjv227347 if (srcp->mnt_mntopts != NULL) {
13280094b373Sjv227347 if (strlcpy(retval->mnt_mntopts, srcp->mnt_mntopts,
13290094b373Sjv227347 MAX_MNTOPT_STR * sizeof (char)) >= MAX_MNTOPT_STR *
13300094b373Sjv227347 sizeof (char)) {
13310094b373Sjv227347 mnttab_destroy(retval);
13320094b373Sjv227347 errno = EOVERFLOW; /* similar to mount(2) behavior */
13330094b373Sjv227347 return (NULL);
13340094b373Sjv227347 }
13350094b373Sjv227347 } else {
13360094b373Sjv227347 retval->mnt_mntopts[0] = '\0';
13370094b373Sjv227347 }
13380094b373Sjv227347 return (retval);
13390094b373Sjv227347
13400094b373Sjv227347 err:
13410094b373Sjv227347 mnttab_destroy(retval);
13420094b373Sjv227347 errno = ENOMEM;
13430094b373Sjv227347 return (NULL);
13440094b373Sjv227347 }
13450094b373Sjv227347
13460094b373Sjv227347 /*
13470094b373Sjv227347 * Determine whether the specified ZFS dataset's mountpoint property is set
13480094b373Sjv227347 * to "legacy". If the specified dataset does not have a legacy mountpoint,
13490094b373Sjv227347 * then the string pointer to which the mountpoint argument points is assigned
13500094b373Sjv227347 * a dynamically-allocated string containing the dataset's mountpoint
13510094b373Sjv227347 * property. If the dataset's mountpoint property is "legacy" or a libzfs
13520094b373Sjv227347 * error occurs, then the string pointer to which the mountpoint argument
13530094b373Sjv227347 * points isn't modified.
13540094b373Sjv227347 *
13550094b373Sjv227347 * This function returns B_TRUE if it doesn't encounter any fatal errors.
13560094b373Sjv227347 * It returns B_FALSE if it encounters a fatal error and sets errno to the
13570094b373Sjv227347 * appropriate error code.
13580094b373Sjv227347 */
13590094b373Sjv227347 static boolean_t
get_zfs_non_legacy_mountpoint(const char * dataset_name,char ** mountpoint)13600094b373Sjv227347 get_zfs_non_legacy_mountpoint(const char *dataset_name, char **mountpoint)
13610094b373Sjv227347 {
13620094b373Sjv227347 zfs_handle_t *zhp;
13630094b373Sjv227347 char propbuf[ZFS_MAXPROPLEN];
13640094b373Sjv227347
13650094b373Sjv227347 assert(dataset_name != NULL);
13660094b373Sjv227347 assert(mountpoint != NULL);
13670094b373Sjv227347
13680094b373Sjv227347 if ((zhp = zfs_open(g_zfs, dataset_name, ZFS_TYPE_DATASET)) == NULL) {
13690094b373Sjv227347 errno = EINVAL;
13700094b373Sjv227347 return (B_FALSE);
13710094b373Sjv227347 }
13720094b373Sjv227347 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf),
13730094b373Sjv227347 NULL, NULL, 0, 0) != 0) {
13740094b373Sjv227347 zfs_close(zhp);
13750094b373Sjv227347 errno = EINVAL;
13760094b373Sjv227347 return (B_FALSE);
13770094b373Sjv227347 }
13780094b373Sjv227347 zfs_close(zhp);
13790094b373Sjv227347 if (strcmp(propbuf, "legacy") != 0) {
13800094b373Sjv227347 if ((*mountpoint = strdup(propbuf)) == NULL) {
13810094b373Sjv227347 errno = ENOMEM;
13820094b373Sjv227347 return (B_FALSE);
13830094b373Sjv227347 }
13840094b373Sjv227347 }
13850094b373Sjv227347 return (B_TRUE);
13860094b373Sjv227347 }
13870094b373Sjv227347
13880094b373Sjv227347
13890094b373Sjv227347 /*
13900094b373Sjv227347 * This zonecfg_find_mounts() callback records information about mounts of
13910094b373Sjv227347 * interest in a zonepath. It also tallies the number of zone
13920094b373Sjv227347 * root overlay mounts and the number of unexpected mounts found.
13930094b373Sjv227347 * This function outputs errors using zerror() if it finds unexpected
13940094b373Sjv227347 * mounts. cookiep should point to an initialized zone_mounts_t structure.
13950094b373Sjv227347 *
13960094b373Sjv227347 * This function returns zero on success and a nonzero value on failure.
13970094b373Sjv227347 */
13980094b373Sjv227347 static int
zone_mounts_cb(const struct mnttab * mountp,void * cookiep)13990094b373Sjv227347 zone_mounts_cb(const struct mnttab *mountp, void *cookiep)
14000094b373Sjv227347 {
14010094b373Sjv227347 zone_mounts_t *mounts;
14020094b373Sjv227347 const char *zone_mount_dir;
14030094b373Sjv227347
14040094b373Sjv227347 assert(mountp != NULL);
14050094b373Sjv227347 assert(cookiep != NULL);
14060094b373Sjv227347
14070094b373Sjv227347 mounts = (zone_mounts_t *)cookiep;
14080094b373Sjv227347 zone_mount_dir = mountp->mnt_mountp + mounts->zonepath_len;
14090094b373Sjv227347 if (strcmp(zone_mount_dir, "/root") == 0) {
14100094b373Sjv227347 /*
14110094b373Sjv227347 * Check for an overlay mount. If we already detected a /root
14120094b373Sjv227347 * mount, then the current mount must be an overlay mount.
14130094b373Sjv227347 */
14140094b373Sjv227347 if (mounts->root_mnttab != NULL) {
14150094b373Sjv227347 mounts->num_root_overlay_mounts++;
14160094b373Sjv227347 return (0);
14170094b373Sjv227347 }
14180094b373Sjv227347
14190094b373Sjv227347 /*
14200094b373Sjv227347 * Store the root mount's mnttab information in the
14210094b373Sjv227347 * zone_mounts_t structure for future use.
14220094b373Sjv227347 */
14230094b373Sjv227347 if ((mounts->root_mnttab = mnttab_dup(mountp)) == NULL) {
14240094b373Sjv227347 zperror(cmd_to_str(CMD_MOVE), B_FALSE);
14250094b373Sjv227347 return (-1);
14260094b373Sjv227347 }
14270094b373Sjv227347
14280094b373Sjv227347 /*
14290094b373Sjv227347 * Determine if the filesystem is a ZFS filesystem with a
14300094b373Sjv227347 * non-legacy mountpoint. If it is, then set the root
14310094b373Sjv227347 * filesystem's mnttab's mnt_mountp field to a non-NULL
14320094b373Sjv227347 * value, which will serve as a flag to indicate this special
14330094b373Sjv227347 * condition.
14340094b373Sjv227347 */
14350094b373Sjv227347 if (strcmp(mountp->mnt_fstype, MNTTYPE_ZFS) == 0 &&
14360094b373Sjv227347 get_zfs_non_legacy_mountpoint(mountp->mnt_special,
14370094b373Sjv227347 &mounts->root_mnttab->mnt_mountp) != B_TRUE) {
14380094b373Sjv227347 zperror(cmd_to_str(CMD_MOVE), B_FALSE);
14390094b373Sjv227347 return (-1);
14400094b373Sjv227347 }
14410094b373Sjv227347 } else {
14420094b373Sjv227347 /*
14430094b373Sjv227347 * An unexpected mount was found. Notify the user.
14440094b373Sjv227347 */
14450094b373Sjv227347 if (mounts->num_unexpected_mounts == 0)
14460094b373Sjv227347 zerror(gettext("These file systems are mounted on "
14470094b373Sjv227347 "subdirectories of %s.\n"), mounts->zonepath);
14480094b373Sjv227347 mounts->num_unexpected_mounts++;
14490094b373Sjv227347 (void) zfm_print(mountp, NULL);
14500094b373Sjv227347 }
14510094b373Sjv227347 return (0);
14520094b373Sjv227347 }
14530094b373Sjv227347
14540094b373Sjv227347 /*
14550094b373Sjv227347 * Initialize the specified zone_mounts_t structure for the given zonepath.
14560094b373Sjv227347 * If this function succeeds, it returns zero and the specified zone_mounts_t
14570094b373Sjv227347 * structure contains information about mounts in the specified zonepath.
14580094b373Sjv227347 * The function returns a nonzero value if it fails. The zone_mounts_t
14590094b373Sjv227347 * structure doesn't need be destroyed via zone_mounts_destroy() if this
14600094b373Sjv227347 * function fails.
14610094b373Sjv227347 */
14620094b373Sjv227347 int
zone_mounts_init(zone_mounts_t * mounts,const char * zonepath)14630094b373Sjv227347 zone_mounts_init(zone_mounts_t *mounts, const char *zonepath)
14640094b373Sjv227347 {
14650094b373Sjv227347 assert(mounts != NULL);
14660094b373Sjv227347 assert(zonepath != NULL);
14670094b373Sjv227347
14680094b373Sjv227347 bzero(mounts, sizeof (*mounts));
14690094b373Sjv227347 if ((mounts->zonepath = strdup(zonepath)) == NULL) {
14700094b373Sjv227347 zerror(gettext("the process ran out of memory while checking "
14710094b373Sjv227347 "for mounts in zonepath %s."), zonepath);
14720094b373Sjv227347 return (-1);
14730094b373Sjv227347 }
14740094b373Sjv227347 mounts->zonepath_len = strlen(zonepath);
14750094b373Sjv227347 if (zonecfg_find_mounts((char *)zonepath, zone_mounts_cb, mounts) ==
14760094b373Sjv227347 -1) {
14770094b373Sjv227347 zerror(gettext("an error occurred while checking for mounts "
14780094b373Sjv227347 "in zonepath %s."), zonepath);
14790094b373Sjv227347 zone_mounts_destroy(mounts);
14800094b373Sjv227347 return (-1);
14810094b373Sjv227347 }
14820094b373Sjv227347 return (0);
14830094b373Sjv227347 }
14840094b373Sjv227347
14850094b373Sjv227347 /*
14860094b373Sjv227347 * Destroy the memory used by the specified zone_mounts_t structure's fields.
14870094b373Sjv227347 * This function doesn't free the memory occupied by the structure itself
14880094b373Sjv227347 * (i.e., it doesn't free the parameter).
14890094b373Sjv227347 */
14900094b373Sjv227347 void
zone_mounts_destroy(zone_mounts_t * mounts)14910094b373Sjv227347 zone_mounts_destroy(zone_mounts_t *mounts)
14920094b373Sjv227347 {
14930094b373Sjv227347 assert(mounts != NULL);
14940094b373Sjv227347
14950094b373Sjv227347 free(mounts->zonepath);
14960094b373Sjv227347 if (mounts->root_mnttab != NULL)
14970094b373Sjv227347 mnttab_destroy(mounts->root_mnttab);
14980094b373Sjv227347 }
14990094b373Sjv227347
15000094b373Sjv227347 /*
15010094b373Sjv227347 * Mount a moving zone's root filesystem (if it had a root filesystem mount
15020094b373Sjv227347 * prior to the move) using the specified zonepath. mounts should refer to
15030094b373Sjv227347 * the zone_mounts_t structure describing the zone's mount information.
15040094b373Sjv227347 *
15050094b373Sjv227347 * This function returns zero if the mount succeeds and a nonzero value
15060094b373Sjv227347 * if it doesn't.
15070094b373Sjv227347 */
15080094b373Sjv227347 int
zone_mount_rootfs(zone_mounts_t * mounts,const char * zonepath)15090094b373Sjv227347 zone_mount_rootfs(zone_mounts_t *mounts, const char *zonepath)
15100094b373Sjv227347 {
15110094b373Sjv227347 char zoneroot[MAXPATHLEN];
15120094b373Sjv227347 struct mnttab *mtab;
15130094b373Sjv227347 int flags;
15140094b373Sjv227347
15150094b373Sjv227347 assert(mounts != NULL);
15160094b373Sjv227347 assert(zonepath != NULL);
15170094b373Sjv227347
15180094b373Sjv227347 /*
15190094b373Sjv227347 * If there isn't a root filesystem, then don't do anything.
15200094b373Sjv227347 */
15210094b373Sjv227347 mtab = mounts->root_mnttab;
15220094b373Sjv227347 if (mtab == NULL)
15230094b373Sjv227347 return (0);
15240094b373Sjv227347
15250094b373Sjv227347 /*
15260094b373Sjv227347 * Determine the root filesystem's new mountpoint.
15270094b373Sjv227347 */
15280094b373Sjv227347 if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >=
15290094b373Sjv227347 sizeof (zoneroot)) {
15300094b373Sjv227347 zerror(gettext("Zonepath %s is too long.\n"), zonepath);
15310094b373Sjv227347 return (-1);
15320094b373Sjv227347 }
15330094b373Sjv227347
15340094b373Sjv227347 /*
15350094b373Sjv227347 * If the root filesystem is a non-legacy ZFS filesystem (i.e., if it's
15360094b373Sjv227347 * mnt_mountp field is non-NULL), then make the filesystem's new
15370094b373Sjv227347 * mount point its mountpoint property and mount the filesystem.
15380094b373Sjv227347 */
15390094b373Sjv227347 if (mtab->mnt_mountp != NULL) {
15400094b373Sjv227347 zfs_handle_t *zhp;
15410094b373Sjv227347
15420094b373Sjv227347 if ((zhp = zfs_open(g_zfs, mtab->mnt_special,
15430094b373Sjv227347 ZFS_TYPE_DATASET)) == NULL) {
15440094b373Sjv227347 zerror(gettext("could not get ZFS handle for the zone's"
15450094b373Sjv227347 " root filesystem"));
15460094b373Sjv227347 return (-1);
15470094b373Sjv227347 }
15480094b373Sjv227347 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
15490094b373Sjv227347 zoneroot) != 0) {
15500094b373Sjv227347 zerror(gettext("could not modify zone's root "
15510094b373Sjv227347 "filesystem's mountpoint property"));
15520094b373Sjv227347 zfs_close(zhp);
15530094b373Sjv227347 return (-1);
15540094b373Sjv227347 }
15550094b373Sjv227347 if (zfs_mount(zhp, mtab->mnt_mntopts, 0) != 0) {
15560094b373Sjv227347 zerror(gettext("unable to mount zone root %s: %s"),
15570094b373Sjv227347 zoneroot, libzfs_error_description(g_zfs));
15580094b373Sjv227347 if (zfs_prop_set(zhp,
15590094b373Sjv227347 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT),
15600094b373Sjv227347 mtab->mnt_mountp) != 0)
15610094b373Sjv227347 zerror(gettext("unable to restore zone's root "
15620094b373Sjv227347 "filesystem's mountpoint property"));
15630094b373Sjv227347 zfs_close(zhp);
15640094b373Sjv227347 return (-1);
15650094b373Sjv227347 }
15660094b373Sjv227347 zfs_close(zhp);
15670094b373Sjv227347 return (0);
15680094b373Sjv227347 }
15690094b373Sjv227347
15700094b373Sjv227347 /*
15710094b373Sjv227347 * The root filesystem is either a legacy-mounted ZFS filesystem or
15720094b373Sjv227347 * a non-ZFS filesystem. Use mount(2) to mount the root filesystem.
15730094b373Sjv227347 */
15740094b373Sjv227347 if (mtab->mnt_mntopts != NULL)
15750094b373Sjv227347 flags = MS_OPTIONSTR;
15760094b373Sjv227347 else
15770094b373Sjv227347 flags = 0;
15780094b373Sjv227347 if (mount(mtab->mnt_special, zoneroot, flags, mtab->mnt_fstype, NULL, 0,
15790094b373Sjv227347 mtab->mnt_mntopts, MAX_MNTOPT_STR * sizeof (char)) != 0) {
15800094b373Sjv227347 flags = errno;
15810094b373Sjv227347 zerror(gettext("unable to mount zone root %s: %s"), zoneroot,
15820094b373Sjv227347 strerror(flags));
15830094b373Sjv227347 return (-1);
15840094b373Sjv227347 }
15850094b373Sjv227347 return (0);
15860094b373Sjv227347 }
15870094b373Sjv227347
15880094b373Sjv227347 /*
15890094b373Sjv227347 * Unmount a moving zone's root filesystem (if such a mount exists) using the
15900094b373Sjv227347 * specified zonepath. mounts should refer to the zone_mounts_t structure
15910094b373Sjv227347 * describing the zone's mount information. If force is B_TRUE, then if the
15920094b373Sjv227347 * unmount fails, then the function will try to forcibly unmount the zone's root
15930094b373Sjv227347 * filesystem.
15940094b373Sjv227347 *
15950094b373Sjv227347 * This function returns zero if the unmount (forced or otherwise) succeeds;
15960094b373Sjv227347 * otherwise, it returns a nonzero value.
15970094b373Sjv227347 */
15980094b373Sjv227347 int
zone_unmount_rootfs(zone_mounts_t * mounts,const char * zonepath,boolean_t force)15990094b373Sjv227347 zone_unmount_rootfs(zone_mounts_t *mounts, const char *zonepath,
16000094b373Sjv227347 boolean_t force)
16010094b373Sjv227347 {
16020094b373Sjv227347 char zoneroot[MAXPATHLEN];
16030094b373Sjv227347 struct mnttab *mtab;
16040094b373Sjv227347 int err;
16050094b373Sjv227347
16060094b373Sjv227347 assert(mounts != NULL);
16070094b373Sjv227347 assert(zonepath != NULL);
16080094b373Sjv227347
16090094b373Sjv227347 /*
16100094b373Sjv227347 * If there isn't a root filesystem, then don't do anything.
16110094b373Sjv227347 */
16120094b373Sjv227347 mtab = mounts->root_mnttab;
16130094b373Sjv227347 if (mtab == NULL)
16140094b373Sjv227347 return (0);
16150094b373Sjv227347
16160094b373Sjv227347 /*
16170094b373Sjv227347 * Determine the root filesystem's mountpoint.
16180094b373Sjv227347 */
16190094b373Sjv227347 if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >=
16200094b373Sjv227347 sizeof (zoneroot)) {
16210094b373Sjv227347 zerror(gettext("Zonepath %s is too long.\n"), zonepath);
16220094b373Sjv227347 return (-1);
16230094b373Sjv227347 }
16240094b373Sjv227347
16250094b373Sjv227347 /*
16260094b373Sjv227347 * If the root filesystem is a non-legacy ZFS fileystem, then unmount
16270094b373Sjv227347 * the filesystem via libzfs.
16280094b373Sjv227347 */
16290094b373Sjv227347 if (mtab->mnt_mountp != NULL) {
16300094b373Sjv227347 zfs_handle_t *zhp;
16310094b373Sjv227347
16320094b373Sjv227347 if ((zhp = zfs_open(g_zfs, mtab->mnt_special,
16330094b373Sjv227347 ZFS_TYPE_DATASET)) == NULL) {
16340094b373Sjv227347 zerror(gettext("could not get ZFS handle for the zone's"
16350094b373Sjv227347 " root filesystem"));
16360094b373Sjv227347 return (-1);
16370094b373Sjv227347 }
16380094b373Sjv227347 if (zfs_unmount(zhp, zoneroot, 0) != 0) {
16390094b373Sjv227347 if (force && zfs_unmount(zhp, zoneroot, MS_FORCE) ==
16400094b373Sjv227347 0) {
16410094b373Sjv227347 zfs_close(zhp);
16420094b373Sjv227347 return (0);
16430094b373Sjv227347 }
16440094b373Sjv227347 zerror(gettext("unable to unmount zone root %s: %s"),
16450094b373Sjv227347 zoneroot, libzfs_error_description(g_zfs));
16460094b373Sjv227347 zfs_close(zhp);
16470094b373Sjv227347 return (-1);
16480094b373Sjv227347 }
16490094b373Sjv227347 zfs_close(zhp);
16500094b373Sjv227347 return (0);
16510094b373Sjv227347 }
16520094b373Sjv227347
16530094b373Sjv227347 /*
16540094b373Sjv227347 * Use umount(2) to unmount the root filesystem. If this fails, then
16550094b373Sjv227347 * forcibly unmount it if the force flag is set.
16560094b373Sjv227347 */
16570094b373Sjv227347 if (umount(zoneroot) != 0) {
16580094b373Sjv227347 if (force && umount2(zoneroot, MS_FORCE) == 0)
16590094b373Sjv227347 return (0);
16600094b373Sjv227347 err = errno;
16610094b373Sjv227347 zerror(gettext("unable to unmount zone root %s: %s"), zoneroot,
16620094b373Sjv227347 strerror(err));
16630094b373Sjv227347 return (-1);
16640094b373Sjv227347 }
16650094b373Sjv227347 return (0);
16660094b373Sjv227347 }
16670094b373Sjv227347
166899653d4eSeschrock int
init_zfs(void)166999653d4eSeschrock init_zfs(void)
167099653d4eSeschrock {
167199653d4eSeschrock if ((g_zfs = libzfs_init()) == NULL) {
167299653d4eSeschrock (void) fprintf(stderr, gettext("failed to initialize ZFS "
167399653d4eSeschrock "library\n"));
167499653d4eSeschrock return (Z_ERR);
167599653d4eSeschrock }
167699653d4eSeschrock
16770b5de56dSgjelinek return (Z_OK);
16780b5de56dSgjelinek }
1679