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 /* 23d1f855d7S * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 240b5de56dSgjelinek * Use is subject to license terms. 250b5de56dSgjelinek */ 260b5de56dSgjelinek 270b5de56dSgjelinek /* 280b5de56dSgjelinek * This file contains the functions used to support the ZFS integration 290b5de56dSgjelinek * with zones. This includes validation (e.g. zonecfg dataset), cloning, 300b5de56dSgjelinek * file system creation and destruction. 310b5de56dSgjelinek */ 320b5de56dSgjelinek 330b5de56dSgjelinek #include <stdio.h> 340b5de56dSgjelinek #include <errno.h> 350b5de56dSgjelinek #include <unistd.h> 360b5de56dSgjelinek #include <string.h> 370b5de56dSgjelinek #include <locale.h> 380b5de56dSgjelinek #include <libintl.h> 390b5de56dSgjelinek #include <sys/stat.h> 400b5de56dSgjelinek #include <sys/statvfs.h> 410b5de56dSgjelinek #include <libgen.h> 420b5de56dSgjelinek #include <libzonecfg.h> 430b5de56dSgjelinek #include <sys/mnttab.h> 440b5de56dSgjelinek #include <libzfs.h> 4511506c41Sgjelinek #include <sys/mntent.h> 46286822ddS #include <values.h> 47*0094b373Sjv227347 #include <strings.h> 48*0094b373Sjv227347 #include <assert.h> 490b5de56dSgjelinek 500b5de56dSgjelinek #include "zoneadm.h" 510b5de56dSgjelinek 5299653d4eSeschrock libzfs_handle_t *g_zfs; 530b5de56dSgjelinek 540b5de56dSgjelinek typedef struct zfs_mount_data { 550b5de56dSgjelinek char *match_name; 560b5de56dSgjelinek zfs_handle_t *match_handle; 570b5de56dSgjelinek } zfs_mount_data_t; 580b5de56dSgjelinek 590b5de56dSgjelinek typedef struct zfs_snapshot_data { 60286822ddS char *match_name; /* zonename@SUNWzone */ 61286822ddS int len; /* strlen of match_name */ 62286822ddS int max; /* highest digit appended to snap name */ 63286822ddS int num; /* number of snapshots to rename */ 64286822ddS int cntr; /* counter for renaming snapshots */ 650b5de56dSgjelinek } zfs_snapshot_data_t; 660b5de56dSgjelinek 67286822ddS typedef struct clone_data { 68286822ddS zfs_handle_t *clone_zhp; /* clone dataset to promote */ 69286822ddS time_t origin_creation; /* snapshot creation time of clone */ 70286822ddS const char *snapshot; /* snapshot of dataset being demoted */ 71286822ddS } clone_data_t; 72286822ddS 730b5de56dSgjelinek /* 740b5de56dSgjelinek * A ZFS file system iterator call-back function which is used to validate 750b5de56dSgjelinek * datasets imported into the zone. 760b5de56dSgjelinek */ 770b5de56dSgjelinek /* ARGSUSED */ 780b5de56dSgjelinek static int 790b5de56dSgjelinek check_zvol(zfs_handle_t *zhp, void *unused) 800b5de56dSgjelinek { 810b5de56dSgjelinek int ret; 820b5de56dSgjelinek 830b5de56dSgjelinek if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 840b5de56dSgjelinek /* 850b5de56dSgjelinek * TRANSLATION_NOTE 860b5de56dSgjelinek * zfs and dataset are literals that should not be translated. 870b5de56dSgjelinek */ 880b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify zfs dataset %s: " 890b5de56dSgjelinek "volumes cannot be specified as a zone dataset resource\n"), 900b5de56dSgjelinek zfs_get_name(zhp)); 910b5de56dSgjelinek ret = -1; 920b5de56dSgjelinek } else { 930b5de56dSgjelinek ret = zfs_iter_children(zhp, check_zvol, NULL); 940b5de56dSgjelinek } 950b5de56dSgjelinek 960b5de56dSgjelinek zfs_close(zhp); 970b5de56dSgjelinek 980b5de56dSgjelinek return (ret); 990b5de56dSgjelinek } 1000b5de56dSgjelinek 1010b5de56dSgjelinek /* 1020b5de56dSgjelinek * A ZFS file system iterator call-back function which returns the 1030b5de56dSgjelinek * zfs_handle_t for a ZFS file system on the specified mount point. 1040b5de56dSgjelinek */ 1050b5de56dSgjelinek static int 1060b5de56dSgjelinek match_mountpoint(zfs_handle_t *zhp, void *data) 1070b5de56dSgjelinek { 1080b5de56dSgjelinek int res; 1090b5de56dSgjelinek zfs_mount_data_t *cbp; 1100b5de56dSgjelinek char mp[ZFS_MAXPROPLEN]; 1110b5de56dSgjelinek 1120b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 1130b5de56dSgjelinek zfs_close(zhp); 1140b5de56dSgjelinek return (0); 1150b5de56dSgjelinek } 1160b5de56dSgjelinek 11711506c41Sgjelinek /* First check if the dataset is mounted. */ 11811506c41Sgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTED, mp, sizeof (mp), NULL, NULL, 11911506c41Sgjelinek 0, B_FALSE) != 0 || strcmp(mp, "no") == 0) { 12011506c41Sgjelinek zfs_close(zhp); 12111506c41Sgjelinek return (0); 12211506c41Sgjelinek } 12311506c41Sgjelinek 12411506c41Sgjelinek /* Now check mount point. */ 1250b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 12611506c41Sgjelinek 0, B_FALSE) != 0) { 12711506c41Sgjelinek zfs_close(zhp); 12811506c41Sgjelinek return (0); 12911506c41Sgjelinek } 13011506c41Sgjelinek 13111506c41Sgjelinek cbp = (zfs_mount_data_t *)data; 13211506c41Sgjelinek 13311506c41Sgjelinek if (strcmp(mp, "legacy") == 0) { 13411506c41Sgjelinek /* If legacy, must look in mnttab for mountpoint. */ 13511506c41Sgjelinek FILE *fp; 13611506c41Sgjelinek struct mnttab entry; 13711506c41Sgjelinek const char *nm; 13811506c41Sgjelinek 13911506c41Sgjelinek nm = zfs_get_name(zhp); 14011506c41Sgjelinek if ((fp = fopen(MNTTAB, "r")) == NULL) { 14111506c41Sgjelinek zfs_close(zhp); 14211506c41Sgjelinek return (0); 14311506c41Sgjelinek } 14411506c41Sgjelinek 14511506c41Sgjelinek while (getmntent(fp, &entry) == 0) { 14611506c41Sgjelinek if (strcmp(nm, entry.mnt_special) == 0) { 14711506c41Sgjelinek if (strcmp(entry.mnt_mountp, cbp->match_name) 14811506c41Sgjelinek == 0) { 14911506c41Sgjelinek (void) fclose(fp); 15011506c41Sgjelinek cbp->match_handle = zhp; 15111506c41Sgjelinek return (1); 15211506c41Sgjelinek } 15311506c41Sgjelinek break; 15411506c41Sgjelinek } 15511506c41Sgjelinek } 15611506c41Sgjelinek (void) fclose(fp); 15711506c41Sgjelinek 15811506c41Sgjelinek } else if (strcmp(mp, cbp->match_name) == 0) { 1590b5de56dSgjelinek cbp->match_handle = zhp; 1600b5de56dSgjelinek return (1); 1610b5de56dSgjelinek } 1620b5de56dSgjelinek 16311506c41Sgjelinek /* Iterate over any nested datasets. */ 1640b5de56dSgjelinek res = zfs_iter_filesystems(zhp, match_mountpoint, data); 1650b5de56dSgjelinek zfs_close(zhp); 1660b5de56dSgjelinek return (res); 1670b5de56dSgjelinek } 1680b5de56dSgjelinek 1690b5de56dSgjelinek /* 1700b5de56dSgjelinek * Get ZFS handle for the specified mount point. 1710b5de56dSgjelinek */ 1720b5de56dSgjelinek static zfs_handle_t * 1730b5de56dSgjelinek mount2zhandle(char *mountpoint) 1740b5de56dSgjelinek { 1750b5de56dSgjelinek zfs_mount_data_t cb; 1760b5de56dSgjelinek 1770b5de56dSgjelinek cb.match_name = mountpoint; 1780b5de56dSgjelinek cb.match_handle = NULL; 17999653d4eSeschrock (void) zfs_iter_root(g_zfs, match_mountpoint, &cb); 1800b5de56dSgjelinek return (cb.match_handle); 1810b5de56dSgjelinek } 1820b5de56dSgjelinek 1830b5de56dSgjelinek /* 1840b5de56dSgjelinek * Check if there is already a file system (zfs or any other type) mounted on 1850b5de56dSgjelinek * path. 1860b5de56dSgjelinek */ 1870b5de56dSgjelinek static boolean_t 1880b5de56dSgjelinek is_mountpnt(char *path) 1890b5de56dSgjelinek { 1900b5de56dSgjelinek FILE *fp; 1910b5de56dSgjelinek struct mnttab entry; 1920b5de56dSgjelinek 19311506c41Sgjelinek if ((fp = fopen(MNTTAB, "r")) == NULL) 1940b5de56dSgjelinek return (B_FALSE); 1950b5de56dSgjelinek 1960b5de56dSgjelinek while (getmntent(fp, &entry) == 0) { 1970b5de56dSgjelinek if (strcmp(path, entry.mnt_mountp) == 0) { 1980b5de56dSgjelinek (void) fclose(fp); 1990b5de56dSgjelinek return (B_TRUE); 2000b5de56dSgjelinek } 2010b5de56dSgjelinek } 2020b5de56dSgjelinek 2030b5de56dSgjelinek (void) fclose(fp); 2040b5de56dSgjelinek return (B_FALSE); 2050b5de56dSgjelinek } 2060b5de56dSgjelinek 2070b5de56dSgjelinek /* 208ff17c8bfSgjelinek * Run the brand's pre-snapshot hook before we take a ZFS snapshot of the zone. 2090b5de56dSgjelinek */ 2100b5de56dSgjelinek static int 211ff17c8bfSgjelinek pre_snapshot(char *presnapbuf) 2120b5de56dSgjelinek { 213ff17c8bfSgjelinek int status; 2140b5de56dSgjelinek 215ff17c8bfSgjelinek /* No brand-specific handler */ 216ff17c8bfSgjelinek if (presnapbuf[0] == '\0') 217ff17c8bfSgjelinek return (Z_OK); 218ff17c8bfSgjelinek 219ff17c8bfSgjelinek /* Run the hook */ 220c75cc341S status = do_subproc(presnapbuf); 221ff17c8bfSgjelinek if ((status = subproc_status(gettext("brand-specific presnapshot"), 222ff17c8bfSgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) 2230b5de56dSgjelinek return (Z_ERR); 2240b5de56dSgjelinek 2250b5de56dSgjelinek return (Z_OK); 2260b5de56dSgjelinek } 2270b5de56dSgjelinek 2280b5de56dSgjelinek /* 229ff17c8bfSgjelinek * Run the brand's post-snapshot hook after we take a ZFS snapshot of the zone. 2300b5de56dSgjelinek */ 2310b5de56dSgjelinek static int 232ff17c8bfSgjelinek post_snapshot(char *postsnapbuf) 2330b5de56dSgjelinek { 234ff17c8bfSgjelinek int status; 2350b5de56dSgjelinek 236ff17c8bfSgjelinek /* No brand-specific handler */ 237ff17c8bfSgjelinek if (postsnapbuf[0] == '\0') 238ff17c8bfSgjelinek return (Z_OK); 239ff17c8bfSgjelinek 240ff17c8bfSgjelinek /* Run the hook */ 241c75cc341S status = do_subproc(postsnapbuf); 242ff17c8bfSgjelinek if ((status = subproc_status(gettext("brand-specific postsnapshot"), 243ff17c8bfSgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) 2440b5de56dSgjelinek return (Z_ERR); 2450b5de56dSgjelinek 2460b5de56dSgjelinek return (Z_OK); 2470b5de56dSgjelinek } 2480b5de56dSgjelinek 2490b5de56dSgjelinek /* 2500b5de56dSgjelinek * This is a ZFS snapshot iterator call-back function which returns the 2510b5de56dSgjelinek * highest number of SUNWzone snapshots that have been taken. 2520b5de56dSgjelinek */ 2530b5de56dSgjelinek static int 2540b5de56dSgjelinek get_snap_max(zfs_handle_t *zhp, void *data) 2550b5de56dSgjelinek { 2560b5de56dSgjelinek int res; 2570b5de56dSgjelinek zfs_snapshot_data_t *cbp; 2580b5de56dSgjelinek 2590b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { 2600b5de56dSgjelinek zfs_close(zhp); 2610b5de56dSgjelinek return (0); 2620b5de56dSgjelinek } 2630b5de56dSgjelinek 2640b5de56dSgjelinek cbp = (zfs_snapshot_data_t *)data; 2650b5de56dSgjelinek 2660b5de56dSgjelinek if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) { 2670b5de56dSgjelinek char *nump; 2680b5de56dSgjelinek int num; 2690b5de56dSgjelinek 270286822ddS cbp->num++; 2710b5de56dSgjelinek nump = (char *)(zfs_get_name(zhp) + cbp->len); 2720b5de56dSgjelinek num = atoi(nump); 2730b5de56dSgjelinek if (num > cbp->max) 2740b5de56dSgjelinek cbp->max = num; 2750b5de56dSgjelinek } 2760b5de56dSgjelinek 2770b5de56dSgjelinek res = zfs_iter_snapshots(zhp, get_snap_max, data); 2780b5de56dSgjelinek zfs_close(zhp); 2790b5de56dSgjelinek return (res); 2800b5de56dSgjelinek } 2810b5de56dSgjelinek 2820b5de56dSgjelinek /* 2830b5de56dSgjelinek * Take a ZFS snapshot to be used for cloning the zone. 2840b5de56dSgjelinek */ 2850b5de56dSgjelinek static int 286ff17c8bfSgjelinek take_snapshot(zfs_handle_t *zhp, char *snapshot_name, int snap_size, 287ff17c8bfSgjelinek char *presnapbuf, char *postsnapbuf) 2880b5de56dSgjelinek { 2890b5de56dSgjelinek int res; 2900b5de56dSgjelinek char template[ZFS_MAXNAMELEN]; 2910b5de56dSgjelinek zfs_snapshot_data_t cb; 2920b5de56dSgjelinek 2930b5de56dSgjelinek /* 2940b5de56dSgjelinek * First we need to figure out the next available name for the 2950b5de56dSgjelinek * zone snapshot. Look through the list of zones snapshots for 2960b5de56dSgjelinek * this file system to determine the maximum snapshot name. 2970b5de56dSgjelinek */ 2980b5de56dSgjelinek if (snprintf(template, sizeof (template), "%s@SUNWzone", 2990b5de56dSgjelinek zfs_get_name(zhp)) >= sizeof (template)) 3000b5de56dSgjelinek return (Z_ERR); 3010b5de56dSgjelinek 3020b5de56dSgjelinek cb.match_name = template; 3030b5de56dSgjelinek cb.len = strlen(template); 3040b5de56dSgjelinek cb.max = 0; 3050b5de56dSgjelinek 3060b5de56dSgjelinek if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0) 3070b5de56dSgjelinek return (Z_ERR); 3080b5de56dSgjelinek 3090b5de56dSgjelinek cb.max++; 3100b5de56dSgjelinek 3110b5de56dSgjelinek if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d", 3120b5de56dSgjelinek zfs_get_name(zhp), cb.max) >= snap_size) 3130b5de56dSgjelinek return (Z_ERR); 3140b5de56dSgjelinek 315ff17c8bfSgjelinek if (pre_snapshot(presnapbuf) != Z_OK) 3160b5de56dSgjelinek return (Z_ERR); 317bb0ade09Sahrens res = zfs_snapshot(g_zfs, snapshot_name, B_FALSE, NULL); 318ff17c8bfSgjelinek if (post_snapshot(postsnapbuf) != Z_OK) 3190b5de56dSgjelinek return (Z_ERR); 3200b5de56dSgjelinek 3210b5de56dSgjelinek if (res != 0) 3220b5de56dSgjelinek return (Z_ERR); 3230b5de56dSgjelinek return (Z_OK); 3240b5de56dSgjelinek } 3250b5de56dSgjelinek 3260b5de56dSgjelinek /* 3270b5de56dSgjelinek * We are using an explicit snapshot from some earlier point in time so 328ff17c8bfSgjelinek * we need to validate it. Run the brand specific hook. 3290b5de56dSgjelinek */ 3300b5de56dSgjelinek static int 331ff17c8bfSgjelinek validate_snapshot(char *snapshot_name, char *snap_path, char *validsnapbuf) 3320b5de56dSgjelinek { 333ff17c8bfSgjelinek int status; 334ff17c8bfSgjelinek char cmdbuf[MAXPATHLEN]; 3350b5de56dSgjelinek 336ff17c8bfSgjelinek /* No brand-specific handler */ 337ff17c8bfSgjelinek if (validsnapbuf[0] == '\0') 338ff17c8bfSgjelinek return (Z_OK); 339ff17c8bfSgjelinek 340ff17c8bfSgjelinek /* pass args - snapshot_name & snap_path */ 341ff17c8bfSgjelinek if (snprintf(cmdbuf, sizeof (cmdbuf), "%s %s %s", validsnapbuf, 342ff17c8bfSgjelinek snapshot_name, snap_path) >= sizeof (cmdbuf)) { 343ff17c8bfSgjelinek zerror("Command line too long"); 3440b5de56dSgjelinek return (Z_ERR); 3450b5de56dSgjelinek } 3460b5de56dSgjelinek 347ff17c8bfSgjelinek /* Run the hook */ 348c75cc341S status = do_subproc(cmdbuf); 349ff17c8bfSgjelinek if ((status = subproc_status(gettext("brand-specific validatesnapshot"), 350ff17c8bfSgjelinek status, B_FALSE)) != ZONE_SUBPROC_OK) 3510b5de56dSgjelinek return (Z_ERR); 3520b5de56dSgjelinek 353ff17c8bfSgjelinek return (Z_OK); 3540b5de56dSgjelinek } 3550b5de56dSgjelinek 3560b5de56dSgjelinek /* 3570b5de56dSgjelinek * Remove the sw inventory file from inside this zonepath that we picked up out 3580b5de56dSgjelinek * of the snapshot. 3590b5de56dSgjelinek */ 3600b5de56dSgjelinek static int 3610b5de56dSgjelinek clean_out_clone() 3620b5de56dSgjelinek { 3630b5de56dSgjelinek int err; 3640b5de56dSgjelinek zone_dochandle_t handle; 3650b5de56dSgjelinek 3660b5de56dSgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 3670b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 3680b5de56dSgjelinek return (Z_ERR); 3690b5de56dSgjelinek } 3700b5de56dSgjelinek 3710b5de56dSgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 3720b5de56dSgjelinek errno = err; 3730b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 3740b5de56dSgjelinek zonecfg_fini_handle(handle); 3750b5de56dSgjelinek return (Z_ERR); 3760b5de56dSgjelinek } 3770b5de56dSgjelinek 3780b5de56dSgjelinek zonecfg_rm_detached(handle, B_FALSE); 3790b5de56dSgjelinek zonecfg_fini_handle(handle); 3800b5de56dSgjelinek 3810b5de56dSgjelinek return (Z_OK); 3820b5de56dSgjelinek } 3830b5de56dSgjelinek 3840b5de56dSgjelinek /* 3850b5de56dSgjelinek * Make a ZFS clone on zonepath from snapshot_name. 3860b5de56dSgjelinek */ 3870b5de56dSgjelinek static int 3880b5de56dSgjelinek clone_snap(char *snapshot_name, char *zonepath) 3890b5de56dSgjelinek { 3900b5de56dSgjelinek int res = Z_OK; 3910b5de56dSgjelinek int err; 3920b5de56dSgjelinek zfs_handle_t *zhp; 3930b5de56dSgjelinek zfs_handle_t *clone; 394e9dbad6fSeschrock nvlist_t *props = NULL; 3950b5de56dSgjelinek 39699653d4eSeschrock if ((zhp = zfs_open(g_zfs, snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL) 3970b5de56dSgjelinek return (Z_NO_ENTRY); 3980b5de56dSgjelinek 3990b5de56dSgjelinek (void) printf(gettext("Cloning snapshot %s\n"), snapshot_name); 4000b5de56dSgjelinek 401e9dbad6fSeschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 || 4025f8e1617Snn35248 nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS), 4035f8e1617Snn35248 "off") != 0) { 4045f8e1617Snn35248 if (props != NULL) 405e9dbad6fSeschrock nvlist_free(props); 406e9dbad6fSeschrock (void) fprintf(stderr, gettext("could not create ZFS clone " 407e9dbad6fSeschrock "%s: out of memory\n"), zonepath); 408e9dbad6fSeschrock return (Z_ERR); 409e9dbad6fSeschrock } 410e9dbad6fSeschrock 411e9dbad6fSeschrock err = zfs_clone(zhp, zonepath, props); 4120b5de56dSgjelinek zfs_close(zhp); 413e9dbad6fSeschrock 414e9dbad6fSeschrock nvlist_free(props); 415e9dbad6fSeschrock 4160b5de56dSgjelinek if (err != 0) 4170b5de56dSgjelinek return (Z_ERR); 4180b5de56dSgjelinek 4190b5de56dSgjelinek /* create the mountpoint if necessary */ 420990b4856Slling if ((clone = zfs_open(g_zfs, zonepath, ZFS_TYPE_DATASET)) == NULL) 4210b5de56dSgjelinek return (Z_ERR); 4220b5de56dSgjelinek 4230b5de56dSgjelinek /* 4240b5de56dSgjelinek * The clone has been created so we need to print a diagnostic 4250b5de56dSgjelinek * message if one of the following steps fails for some reason. 4260b5de56dSgjelinek */ 4270b5de56dSgjelinek if (zfs_mount(clone, NULL, 0) != 0) { 4280b5de56dSgjelinek (void) fprintf(stderr, gettext("could not mount ZFS clone " 4290b5de56dSgjelinek "%s\n"), zfs_get_name(clone)); 4300b5de56dSgjelinek res = Z_ERR; 4310b5de56dSgjelinek 432e9dbad6fSeschrock } else if (clean_out_clone() != Z_OK) { 4330b5de56dSgjelinek (void) fprintf(stderr, gettext("could not remove the " 4340b5de56dSgjelinek "software inventory from ZFS clone %s\n"), 4350b5de56dSgjelinek zfs_get_name(clone)); 4360b5de56dSgjelinek res = Z_ERR; 4370b5de56dSgjelinek } 4380b5de56dSgjelinek 4390b5de56dSgjelinek zfs_close(clone); 4400b5de56dSgjelinek return (res); 4410b5de56dSgjelinek } 4420b5de56dSgjelinek 4430b5de56dSgjelinek /* 4440b5de56dSgjelinek * This function takes a zonepath and attempts to determine what the ZFS 4450b5de56dSgjelinek * file system name (not mountpoint) should be for that path. We do not 4460b5de56dSgjelinek * assume that zonepath is an existing directory or ZFS fs since we use 4470b5de56dSgjelinek * this function as part of the process of creating a new ZFS fs or clone. 4480b5de56dSgjelinek * 4490b5de56dSgjelinek * The way this works is that we look at the parent directory of the zonepath 4500b5de56dSgjelinek * to see if it is a ZFS fs. If it is, we get the name of that ZFS fs and 4510b5de56dSgjelinek * append the last component of the zonepath to generate the ZFS name for the 4520b5de56dSgjelinek * zonepath. This matches the algorithm that ZFS uses for automatically 4530b5de56dSgjelinek * mounting a new fs after it is created. 4540b5de56dSgjelinek * 4550b5de56dSgjelinek * Although a ZFS fs can be mounted anywhere, we don't worry about handling 4560b5de56dSgjelinek * all of the complexity that a user could possibly configure with arbitrary 4570b5de56dSgjelinek * mounts since there is no way to generate a ZFS name from a random path in 4580b5de56dSgjelinek * the file system. We only try to handle the automatic mounts that ZFS does 4590b5de56dSgjelinek * for each file system. ZFS restricts this so that a new fs must be created 4600b5de56dSgjelinek * in an existing parent ZFS fs. It then automatically mounts the new fs 4610b5de56dSgjelinek * directly under the mountpoint for the parent fs using the last component 4620b5de56dSgjelinek * of the name as the mountpoint directory. 4630b5de56dSgjelinek * 4640b5de56dSgjelinek * For example: 4650b5de56dSgjelinek * Name Mountpoint 4660b5de56dSgjelinek * space/eng/dev/test/zone1 /project1/eng/dev/test/zone1 4670b5de56dSgjelinek * 4680b5de56dSgjelinek * Return Z_OK if the path mapped to a ZFS file system name, otherwise return 4690b5de56dSgjelinek * Z_ERR. 4700b5de56dSgjelinek */ 4710b5de56dSgjelinek static int 4720b5de56dSgjelinek path2name(char *zonepath, char *zfs_name, int len) 4730b5de56dSgjelinek { 4740b5de56dSgjelinek int res; 47511506c41Sgjelinek char *bnm, *dnm, *dname, *bname; 4760b5de56dSgjelinek zfs_handle_t *zhp; 47711506c41Sgjelinek struct stat stbuf; 4780b5de56dSgjelinek 4790b5de56dSgjelinek /* 48011506c41Sgjelinek * We need two tmp strings to handle paths directly in / (e.g. /foo) 48111506c41Sgjelinek * since dirname will overwrite the first char after "/" in this case. 4820b5de56dSgjelinek */ 48311506c41Sgjelinek if ((bnm = strdup(zonepath)) == NULL) 4840b5de56dSgjelinek return (Z_ERR); 4850b5de56dSgjelinek 48611506c41Sgjelinek if ((dnm = strdup(zonepath)) == NULL) { 48711506c41Sgjelinek free(bnm); 48811506c41Sgjelinek return (Z_ERR); 48911506c41Sgjelinek } 4900b5de56dSgjelinek 49111506c41Sgjelinek bname = basename(bnm); 49211506c41Sgjelinek dname = dirname(dnm); 49311506c41Sgjelinek 49411506c41Sgjelinek /* 49511506c41Sgjelinek * This is a quick test to save iterating over all of the zfs datasets 49611506c41Sgjelinek * on the system (which can be a lot). If the parent dir is not in a 49711506c41Sgjelinek * ZFS fs, then we're done. 49811506c41Sgjelinek */ 49911506c41Sgjelinek if (stat(dname, &stbuf) != 0 || !S_ISDIR(stbuf.st_mode) || 50011506c41Sgjelinek strcmp(stbuf.st_fstype, MNTTYPE_ZFS) != 0) { 50111506c41Sgjelinek free(bnm); 50211506c41Sgjelinek free(dnm); 50311506c41Sgjelinek return (Z_ERR); 50411506c41Sgjelinek } 50511506c41Sgjelinek 50611506c41Sgjelinek /* See if the parent directory is its own ZFS dataset. */ 50711506c41Sgjelinek if ((zhp = mount2zhandle(dname)) == NULL) { 50811506c41Sgjelinek /* 50911506c41Sgjelinek * The parent is not a ZFS dataset so we can't automatically 51011506c41Sgjelinek * create a dataset on the given path. 51111506c41Sgjelinek */ 51211506c41Sgjelinek free(bnm); 51311506c41Sgjelinek free(dnm); 51411506c41Sgjelinek return (Z_ERR); 51511506c41Sgjelinek } 51611506c41Sgjelinek 51711506c41Sgjelinek res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), bname); 51811506c41Sgjelinek 51911506c41Sgjelinek free(bnm); 52011506c41Sgjelinek free(dnm); 5210b5de56dSgjelinek zfs_close(zhp); 5220b5de56dSgjelinek if (res >= len) 5230b5de56dSgjelinek return (Z_ERR); 5240b5de56dSgjelinek 5250b5de56dSgjelinek return (Z_OK); 5260b5de56dSgjelinek } 5270b5de56dSgjelinek 5280b5de56dSgjelinek /* 5290b5de56dSgjelinek * A ZFS file system iterator call-back function used to determine if the 5300b5de56dSgjelinek * file system has dependents (snapshots & clones). 5310b5de56dSgjelinek */ 5320b5de56dSgjelinek /* ARGSUSED */ 5330b5de56dSgjelinek static int 5340b5de56dSgjelinek has_dependent(zfs_handle_t *zhp, void *data) 5350b5de56dSgjelinek { 5360b5de56dSgjelinek zfs_close(zhp); 5370b5de56dSgjelinek return (1); 5380b5de56dSgjelinek } 5390b5de56dSgjelinek 5400b5de56dSgjelinek /* 5410b5de56dSgjelinek * Given a snapshot name, get the file system path where the snapshot lives. 5420b5de56dSgjelinek * A snapshot name is of the form fs_name@snap_name. For example, snapshot 5430b5de56dSgjelinek * pl/zones/z1@SUNWzone1 would have a path of 5440b5de56dSgjelinek * /pl/zones/z1/.zfs/snapshot/SUNWzone1. 5450b5de56dSgjelinek */ 5460b5de56dSgjelinek static int 5470b5de56dSgjelinek snap2path(char *snap_name, char *path, int len) 5480b5de56dSgjelinek { 5490b5de56dSgjelinek char *p; 5500b5de56dSgjelinek zfs_handle_t *zhp; 5510b5de56dSgjelinek char mp[ZFS_MAXPROPLEN]; 5520b5de56dSgjelinek 5530b5de56dSgjelinek if ((p = strrchr(snap_name, '@')) == NULL) 5540b5de56dSgjelinek return (Z_ERR); 5550b5de56dSgjelinek 5560b5de56dSgjelinek /* Get the file system name from the snap_name. */ 5570b5de56dSgjelinek *p = '\0'; 558990b4856Slling zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_DATASET); 5590b5de56dSgjelinek *p = '@'; 5600b5de56dSgjelinek if (zhp == NULL) 5610b5de56dSgjelinek return (Z_ERR); 5620b5de56dSgjelinek 5630b5de56dSgjelinek /* Get the file system mount point. */ 5640b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 56599653d4eSeschrock 0, B_FALSE) != 0) { 5660b5de56dSgjelinek zfs_close(zhp); 5670b5de56dSgjelinek return (Z_ERR); 5680b5de56dSgjelinek } 5690b5de56dSgjelinek zfs_close(zhp); 5700b5de56dSgjelinek 5710b5de56dSgjelinek p++; 5720b5de56dSgjelinek if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len) 5730b5de56dSgjelinek return (Z_ERR); 5740b5de56dSgjelinek 5750b5de56dSgjelinek return (Z_OK); 5760b5de56dSgjelinek } 5770b5de56dSgjelinek 5780b5de56dSgjelinek /* 579286822ddS * This callback function is used to iterate through a snapshot's dependencies 580286822ddS * to find a filesystem that is a direct clone of the snapshot being iterated. 581286822ddS */ 582286822ddS static int 583286822ddS get_direct_clone(zfs_handle_t *zhp, void *data) 584286822ddS { 585286822ddS clone_data_t *cd = data; 586286822ddS char origin[ZFS_MAXNAMELEN]; 587286822ddS char ds_path[ZFS_MAXNAMELEN]; 588286822ddS 589286822ddS if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 590286822ddS zfs_close(zhp); 591286822ddS return (0); 592286822ddS } 593286822ddS 594286822ddS (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path)); 595286822ddS 596286822ddS /* Make sure this is a direct clone of the snapshot we're iterating. */ 597286822ddS if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, 598286822ddS NULL, 0, B_FALSE) != 0 || strcmp(origin, cd->snapshot) != 0) { 599286822ddS zfs_close(zhp); 600286822ddS return (0); 601286822ddS } 602286822ddS 603286822ddS if (cd->clone_zhp != NULL) 604286822ddS zfs_close(cd->clone_zhp); 605286822ddS 606286822ddS cd->clone_zhp = zhp; 607286822ddS return (1); 608286822ddS } 609286822ddS 610286822ddS /* 611286822ddS * A ZFS file system iterator call-back function used to determine the clone 612286822ddS * to promote. This function finds the youngest (i.e. last one taken) snapshot 613286822ddS * that has a clone. If found, it returns a reference to that clone in the 614286822ddS * callback data. 615286822ddS */ 616286822ddS static int 617286822ddS find_clone(zfs_handle_t *zhp, void *data) 618286822ddS { 619286822ddS clone_data_t *cd = data; 620286822ddS time_t snap_creation; 621286822ddS int zret = 0; 622286822ddS 623286822ddS /* If snapshot has no clones, skip it */ 624286822ddS if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) { 625286822ddS zfs_close(zhp); 626286822ddS return (0); 627286822ddS } 628286822ddS 629286822ddS cd->snapshot = zfs_get_name(zhp); 630286822ddS 631286822ddS /* Get the creation time of this snapshot */ 632286822ddS snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); 633286822ddS 634286822ddS /* 635286822ddS * If this snapshot's creation time is greater than (i.e. younger than) 636286822ddS * the current youngest snapshot found, iterate this snapshot to 637286822ddS * get the right clone. 638286822ddS */ 639286822ddS if (snap_creation >= cd->origin_creation) { 640286822ddS /* 641286822ddS * Iterate the dependents of this snapshot to find a clone 642286822ddS * that's a direct dependent. 643286822ddS */ 644286822ddS if ((zret = zfs_iter_dependents(zhp, B_FALSE, get_direct_clone, 645286822ddS cd)) == -1) { 646286822ddS zfs_close(zhp); 647286822ddS return (1); 648286822ddS } else if (zret == 1) { 649286822ddS /* 650286822ddS * Found a clone, update the origin_creation time 651286822ddS * in the callback data. 652286822ddS */ 653286822ddS cd->origin_creation = snap_creation; 654286822ddS } 655286822ddS } 656286822ddS 657286822ddS zfs_close(zhp); 658286822ddS return (0); 659286822ddS } 660286822ddS 661286822ddS /* 662286822ddS * A ZFS file system iterator call-back function used to remove standalone 663286822ddS * snapshots. 664286822ddS */ 665286822ddS /* ARGSUSED */ 666286822ddS static int 667286822ddS rm_snap(zfs_handle_t *zhp, void *data) 668286822ddS { 669286822ddS /* If snapshot has clones, something is wrong */ 670286822ddS if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) { 671286822ddS zfs_close(zhp); 672286822ddS return (1); 673286822ddS } 674286822ddS 675286822ddS if (zfs_unmount(zhp, NULL, 0) == 0) { 676842727c2SChris Kirby (void) zfs_destroy(zhp, B_FALSE); 677286822ddS } 678286822ddS 679286822ddS zfs_close(zhp); 680286822ddS return (0); 681286822ddS } 682286822ddS 683286822ddS /* 684286822ddS * A ZFS snapshot iterator call-back function which renames snapshots. 685286822ddS */ 686286822ddS static int 687286822ddS rename_snap(zfs_handle_t *zhp, void *data) 688286822ddS { 689286822ddS int res; 690286822ddS zfs_snapshot_data_t *cbp; 691286822ddS char template[ZFS_MAXNAMELEN]; 692286822ddS 693286822ddS cbp = (zfs_snapshot_data_t *)data; 694286822ddS 695286822ddS /* 696286822ddS * When renaming snapshots with the iterator, the iterator can see 697286822ddS * the same snapshot after we've renamed up in the namespace. To 698286822ddS * prevent this we check the count for the number of snapshots we have 699286822ddS * to rename and stop at that point. 700286822ddS */ 701286822ddS if (cbp->cntr >= cbp->num) { 702286822ddS zfs_close(zhp); 703286822ddS return (0); 704286822ddS } 705286822ddS 706286822ddS if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { 707286822ddS zfs_close(zhp); 708286822ddS return (0); 709286822ddS } 710286822ddS 711286822ddS /* Only rename the snapshots we automatically generate when we clone. */ 712286822ddS if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) != 0) { 713286822ddS zfs_close(zhp); 714286822ddS return (0); 715286822ddS } 716286822ddS 717286822ddS (void) snprintf(template, sizeof (template), "%s%d", cbp->match_name, 718286822ddS cbp->max++); 719286822ddS 720286822ddS res = (zfs_rename(zhp, template, B_FALSE) != 0); 721286822ddS if (res != 0) 722286822ddS (void) fprintf(stderr, gettext("failed to rename snapshot %s " 723286822ddS "to %s: %s\n"), zfs_get_name(zhp), template, 724286822ddS libzfs_error_description(g_zfs)); 725286822ddS 726286822ddS cbp->cntr++; 727286822ddS 728286822ddS zfs_close(zhp); 729286822ddS return (res); 730286822ddS } 731286822ddS 732286822ddS /* 733286822ddS * Rename the source dataset's snapshots that are automatically generated when 734286822ddS * we clone a zone so that there won't be a name collision when we promote the 735286822ddS * cloned dataset. Once the snapshots have been renamed, then promote the 736286822ddS * clone. 737286822ddS * 738286822ddS * The snapshot rename process gets the highest number on the snapshot names 739286822ddS * (the format is zonename@SUNWzoneXX where XX are digits) on both the source 740286822ddS * and clone datasets, then renames the source dataset snapshots starting at 741286822ddS * the next number. 742286822ddS */ 743286822ddS static int 744286822ddS promote_clone(zfs_handle_t *src_zhp, zfs_handle_t *cln_zhp) 745286822ddS { 746286822ddS zfs_snapshot_data_t sd; 747286822ddS char nm[ZFS_MAXNAMELEN]; 748286822ddS char template[ZFS_MAXNAMELEN]; 749286822ddS 750286822ddS (void) strlcpy(nm, zfs_get_name(cln_zhp), sizeof (nm)); 751286822ddS /* 752286822ddS * Start by getting the clone's snapshot max which we use 753286822ddS * during the rename of the original dataset's snapshots. 754286822ddS */ 755286822ddS (void) snprintf(template, sizeof (template), "%s@SUNWzone", nm); 756286822ddS sd.match_name = template; 757286822ddS sd.len = strlen(template); 758286822ddS sd.max = 0; 759286822ddS 760286822ddS if (zfs_iter_snapshots(cln_zhp, get_snap_max, &sd) != 0) 761286822ddS return (Z_ERR); 762286822ddS 763286822ddS /* 764286822ddS * Now make sure the source's snapshot max is at least as high as 765286822ddS * the clone's snapshot max. 766286822ddS */ 767286822ddS (void) snprintf(template, sizeof (template), "%s@SUNWzone", 768286822ddS zfs_get_name(src_zhp)); 769286822ddS sd.match_name = template; 770286822ddS sd.len = strlen(template); 771286822ddS sd.num = 0; 772286822ddS 773286822ddS if (zfs_iter_snapshots(src_zhp, get_snap_max, &sd) != 0) 774286822ddS return (Z_ERR); 775286822ddS 776286822ddS /* 777286822ddS * Now rename the source dataset's snapshots so there's no 778286822ddS * conflict when we promote the clone. 779286822ddS */ 780286822ddS sd.max++; 781286822ddS sd.cntr = 0; 782286822ddS if (zfs_iter_snapshots(src_zhp, rename_snap, &sd) != 0) 783286822ddS return (Z_ERR); 784286822ddS 785286822ddS /* close and reopen the clone dataset to get the latest info */ 786286822ddS zfs_close(cln_zhp); 787286822ddS if ((cln_zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL) 788286822ddS return (Z_ERR); 789286822ddS 790286822ddS if (zfs_promote(cln_zhp) != 0) { 791286822ddS (void) fprintf(stderr, gettext("failed to promote %s: %s\n"), 792286822ddS nm, libzfs_error_description(g_zfs)); 793286822ddS return (Z_ERR); 794286822ddS } 795286822ddS 796286822ddS zfs_close(cln_zhp); 797286822ddS return (Z_OK); 798286822ddS } 799286822ddS 800286822ddS /* 801286822ddS * Promote the youngest clone. That clone will then become the origin of all 802286822ddS * of the other clones that were hanging off of the source dataset. 803286822ddS */ 804286822ddS int 805286822ddS promote_all_clones(zfs_handle_t *zhp) 806286822ddS { 807286822ddS clone_data_t cd; 808286822ddS char nm[ZFS_MAXNAMELEN]; 809286822ddS 810286822ddS cd.clone_zhp = NULL; 811286822ddS cd.origin_creation = 0; 812286822ddS cd.snapshot = NULL; 813286822ddS 814286822ddS if (zfs_iter_snapshots(zhp, find_clone, &cd) != 0) { 815286822ddS zfs_close(zhp); 816286822ddS return (Z_ERR); 817286822ddS } 818286822ddS 819286822ddS /* Nothing to promote. */ 820286822ddS if (cd.clone_zhp == NULL) 821286822ddS return (Z_OK); 822286822ddS 823286822ddS /* Found the youngest clone to promote. Promote it. */ 824286822ddS if (promote_clone(zhp, cd.clone_zhp) != 0) { 825286822ddS zfs_close(cd.clone_zhp); 826286822ddS zfs_close(zhp); 827286822ddS return (Z_ERR); 828286822ddS } 829286822ddS 830286822ddS /* close and reopen the main dataset to get the latest info */ 831286822ddS (void) strlcpy(nm, zfs_get_name(zhp), sizeof (nm)); 832286822ddS zfs_close(zhp); 833286822ddS if ((zhp = zfs_open(g_zfs, nm, ZFS_TYPE_FILESYSTEM)) == NULL) 834286822ddS return (Z_ERR); 835286822ddS 836286822ddS return (Z_OK); 837286822ddS } 838286822ddS 839286822ddS /* 8400b5de56dSgjelinek * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if 8410b5de56dSgjelinek * possible, or by copying the data from the snapshot to the zonepath. 8420b5de56dSgjelinek */ 8430b5de56dSgjelinek int 844ff17c8bfSgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath, char *validatesnap) 8450b5de56dSgjelinek { 8460b5de56dSgjelinek int err = Z_OK; 8470b5de56dSgjelinek char clone_name[MAXPATHLEN]; 8480b5de56dSgjelinek char snap_path[MAXPATHLEN]; 8490b5de56dSgjelinek 8500b5de56dSgjelinek if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) { 8510b5de56dSgjelinek (void) fprintf(stderr, gettext("unable to find path for %s.\n"), 8520b5de56dSgjelinek snap_name); 8530b5de56dSgjelinek return (Z_ERR); 8540b5de56dSgjelinek } 8550b5de56dSgjelinek 856ff17c8bfSgjelinek if (validate_snapshot(snap_name, snap_path, validatesnap) != Z_OK) 8570b5de56dSgjelinek return (Z_NO_ENTRY); 8580b5de56dSgjelinek 8590b5de56dSgjelinek /* 8600b5de56dSgjelinek * The zonepath cannot be ZFS cloned, try to copy the data from 8610b5de56dSgjelinek * within the snapshot to the zonepath. 8620b5de56dSgjelinek */ 8630b5de56dSgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) { 8640b5de56dSgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK) 8650b5de56dSgjelinek if (clean_out_clone() != Z_OK) 8660b5de56dSgjelinek (void) fprintf(stderr, 8670b5de56dSgjelinek gettext("could not remove the " 8680b5de56dSgjelinek "software inventory from %s\n"), zonepath); 8690b5de56dSgjelinek 8700b5de56dSgjelinek return (err); 8710b5de56dSgjelinek } 8720b5de56dSgjelinek 8730b5de56dSgjelinek if ((err = clone_snap(snap_name, clone_name)) != Z_OK) { 8740b5de56dSgjelinek if (err != Z_NO_ENTRY) { 8750b5de56dSgjelinek /* 8760b5de56dSgjelinek * Cloning the snapshot failed. Fall back to trying 8770b5de56dSgjelinek * to install the zone by copying from the snapshot. 8780b5de56dSgjelinek */ 8790b5de56dSgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK) 8800b5de56dSgjelinek if (clean_out_clone() != Z_OK) 8810b5de56dSgjelinek (void) fprintf(stderr, 8820b5de56dSgjelinek gettext("could not remove the " 8830b5de56dSgjelinek "software inventory from %s\n"), 8840b5de56dSgjelinek zonepath); 8850b5de56dSgjelinek } else { 8860b5de56dSgjelinek /* 8870b5de56dSgjelinek * The snapshot is unusable for some reason so restore 8880b5de56dSgjelinek * the zone state to configured since we were unable to 8890b5de56dSgjelinek * actually do anything about getting the zone 8900b5de56dSgjelinek * installed. 8910b5de56dSgjelinek */ 8920b5de56dSgjelinek int tmp; 8930b5de56dSgjelinek 8940b5de56dSgjelinek if ((tmp = zone_set_state(target_zone, 8950b5de56dSgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 8960b5de56dSgjelinek errno = tmp; 8970b5de56dSgjelinek zperror2(target_zone, 8980b5de56dSgjelinek gettext("could not set state")); 8990b5de56dSgjelinek } 9000b5de56dSgjelinek } 9010b5de56dSgjelinek } 9020b5de56dSgjelinek 9030b5de56dSgjelinek return (err); 9040b5de56dSgjelinek } 9050b5de56dSgjelinek 9060b5de56dSgjelinek /* 9070b5de56dSgjelinek * Attempt to clone a source_zone to a target zonepath by using a ZFS clone. 9080b5de56dSgjelinek */ 9090b5de56dSgjelinek int 910ff17c8bfSgjelinek clone_zfs(char *source_zonepath, char *zonepath, char *presnapbuf, 911ff17c8bfSgjelinek char *postsnapbuf) 9120b5de56dSgjelinek { 9130b5de56dSgjelinek zfs_handle_t *zhp; 9140b5de56dSgjelinek char clone_name[MAXPATHLEN]; 9150b5de56dSgjelinek char snap_name[MAXPATHLEN]; 9160b5de56dSgjelinek 9170b5de56dSgjelinek /* 9180b5de56dSgjelinek * Try to get a zfs handle for the source_zonepath. If this fails 9190b5de56dSgjelinek * the source_zonepath is not ZFS so return an error. 9200b5de56dSgjelinek */ 9210b5de56dSgjelinek if ((zhp = mount2zhandle(source_zonepath)) == NULL) 9220b5de56dSgjelinek return (Z_ERR); 9230b5de56dSgjelinek 9240b5de56dSgjelinek /* 9250b5de56dSgjelinek * Check if there is a file system already mounted on zonepath. If so, 9260b5de56dSgjelinek * we can't clone to the path so we should fall back to copying. 9270b5de56dSgjelinek */ 9280b5de56dSgjelinek if (is_mountpnt(zonepath)) { 9290b5de56dSgjelinek zfs_close(zhp); 9300b5de56dSgjelinek (void) fprintf(stderr, 9310b5de56dSgjelinek gettext("A file system is already mounted on %s,\n" 9320b5de56dSgjelinek "preventing use of a ZFS clone.\n"), zonepath); 9330b5de56dSgjelinek return (Z_ERR); 9340b5de56dSgjelinek } 9350b5de56dSgjelinek 9360b5de56dSgjelinek /* 9370b5de56dSgjelinek * Instead of using path2name to get the clone name from the zonepath, 9380b5de56dSgjelinek * we could generate a name from the source zone ZFS name. However, 9390b5de56dSgjelinek * this would mean we would create the clone under the ZFS fs of the 9400b5de56dSgjelinek * source instead of what the zonepath says. For example, 9410b5de56dSgjelinek * 9420b5de56dSgjelinek * source_zonepath zonepath 9430b5de56dSgjelinek * /pl/zones/dev/z1 /pl/zones/deploy/z2 9440b5de56dSgjelinek * 9450b5de56dSgjelinek * We don't want the clone to be under "dev", we want it under 9460b5de56dSgjelinek * "deploy", so that we can leverage the normal attribute inheritance 9470b5de56dSgjelinek * that ZFS provides in the fs hierarchy. 9480b5de56dSgjelinek */ 9490b5de56dSgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) { 9500b5de56dSgjelinek zfs_close(zhp); 9510b5de56dSgjelinek return (Z_ERR); 9520b5de56dSgjelinek } 9530b5de56dSgjelinek 954ff17c8bfSgjelinek if (take_snapshot(zhp, snap_name, sizeof (snap_name), presnapbuf, 955ff17c8bfSgjelinek postsnapbuf) != Z_OK) { 9560b5de56dSgjelinek zfs_close(zhp); 9570b5de56dSgjelinek return (Z_ERR); 9580b5de56dSgjelinek } 9590b5de56dSgjelinek zfs_close(zhp); 9600b5de56dSgjelinek 961d9e728a2Sgjelinek if (clone_snap(snap_name, clone_name) != Z_OK) { 962d9e728a2Sgjelinek /* Clean up the snapshot we just took. */ 963d9e728a2Sgjelinek if ((zhp = zfs_open(g_zfs, snap_name, ZFS_TYPE_SNAPSHOT)) 964d9e728a2Sgjelinek != NULL) { 965d9e728a2Sgjelinek if (zfs_unmount(zhp, NULL, 0) == 0) 966842727c2SChris Kirby (void) zfs_destroy(zhp, B_FALSE); 967d9e728a2Sgjelinek zfs_close(zhp); 968d9e728a2Sgjelinek } 969d9e728a2Sgjelinek 9700b5de56dSgjelinek return (Z_ERR); 971d9e728a2Sgjelinek } 9720b5de56dSgjelinek 9730b5de56dSgjelinek (void) printf(gettext("Instead of copying, a ZFS clone has been " 9740b5de56dSgjelinek "created for this zone.\n")); 9750b5de56dSgjelinek 9760b5de56dSgjelinek return (Z_OK); 9770b5de56dSgjelinek } 9780b5de56dSgjelinek 9790b5de56dSgjelinek /* 9800b5de56dSgjelinek * Attempt to create a ZFS file system for the specified zonepath. 9810b5de56dSgjelinek * We either will successfully create a ZFS file system and get it mounted 9820b5de56dSgjelinek * on the zonepath or we don't. The caller doesn't care since a regular 9830b5de56dSgjelinek * directory is used for the zonepath if no ZFS file system is mounted there. 9840b5de56dSgjelinek */ 9850b5de56dSgjelinek void 9860b5de56dSgjelinek create_zfs_zonepath(char *zonepath) 9870b5de56dSgjelinek { 9880b5de56dSgjelinek zfs_handle_t *zhp; 9890b5de56dSgjelinek char zfs_name[MAXPATHLEN]; 990e9dbad6fSeschrock nvlist_t *props = NULL; 9910b5de56dSgjelinek 9920b5de56dSgjelinek if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK) 9930b5de56dSgjelinek return; 9940b5de56dSgjelinek 995d1f855d7S /* Check if the dataset already exists. */ 996d1f855d7S if ((zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) != NULL) { 997d1f855d7S zfs_close(zhp); 998d1f855d7S return; 999d1f855d7S } 1000d1f855d7S 1001e9dbad6fSeschrock if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0 || 10025f8e1617Snn35248 nvlist_add_string(props, zfs_prop_to_name(ZFS_PROP_SHARENFS), 10035f8e1617Snn35248 "off") != 0) { 10045f8e1617Snn35248 if (props != NULL) 1005e9dbad6fSeschrock nvlist_free(props); 1006e9dbad6fSeschrock (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: " 1007e9dbad6fSeschrock "out of memory\n"), zfs_name); 1008e9dbad6fSeschrock } 1009e9dbad6fSeschrock 1010e9dbad6fSeschrock if (zfs_create(g_zfs, zfs_name, ZFS_TYPE_FILESYSTEM, props) != 0 || 1011990b4856Slling (zhp = zfs_open(g_zfs, zfs_name, ZFS_TYPE_DATASET)) == NULL) { 101299653d4eSeschrock (void) fprintf(stderr, gettext("cannot create ZFS dataset %s: " 101399653d4eSeschrock "%s\n"), zfs_name, libzfs_error_description(g_zfs)); 1014e9dbad6fSeschrock nvlist_free(props); 10150b5de56dSgjelinek return; 10160b5de56dSgjelinek } 10170b5de56dSgjelinek 1018e9dbad6fSeschrock nvlist_free(props); 1019e9dbad6fSeschrock 10200b5de56dSgjelinek if (zfs_mount(zhp, NULL, 0) != 0) { 102199653d4eSeschrock (void) fprintf(stderr, gettext("cannot mount ZFS dataset %s: " 102299653d4eSeschrock "%s\n"), zfs_name, libzfs_error_description(g_zfs)); 1023842727c2SChris Kirby (void) zfs_destroy(zhp, B_FALSE); 10240b5de56dSgjelinek } else { 10250b5de56dSgjelinek if (chmod(zonepath, S_IRWXU) != 0) { 10260b5de56dSgjelinek (void) fprintf(stderr, gettext("file system %s " 10270b5de56dSgjelinek "successfully created, but chmod %o failed: %s\n"), 10280b5de56dSgjelinek zfs_name, S_IRWXU, strerror(errno)); 10290b5de56dSgjelinek (void) destroy_zfs(zonepath); 10300b5de56dSgjelinek } else { 10310b5de56dSgjelinek (void) printf(gettext("A ZFS file system has been " 10320b5de56dSgjelinek "created for this zone.\n")); 10330b5de56dSgjelinek } 10340b5de56dSgjelinek } 10350b5de56dSgjelinek 10360b5de56dSgjelinek zfs_close(zhp); 10370b5de56dSgjelinek } 10380b5de56dSgjelinek 10390b5de56dSgjelinek /* 10400b5de56dSgjelinek * If the zonepath is a ZFS file system, attempt to destroy it. We return Z_OK 10410b5de56dSgjelinek * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR 10420b5de56dSgjelinek * which means the caller should clean up the zonepath in the traditional 10430b5de56dSgjelinek * way. 10440b5de56dSgjelinek */ 10450b5de56dSgjelinek int 10460b5de56dSgjelinek destroy_zfs(char *zonepath) 10470b5de56dSgjelinek { 10480b5de56dSgjelinek zfs_handle_t *zhp; 10490b5de56dSgjelinek boolean_t is_clone = B_FALSE; 10500b5de56dSgjelinek char origin[ZFS_MAXPROPLEN]; 10510b5de56dSgjelinek 105299653d4eSeschrock if ((zhp = mount2zhandle(zonepath)) == NULL) 10530b5de56dSgjelinek return (Z_ERR); 10540b5de56dSgjelinek 1055286822ddS if (promote_all_clones(zhp) != 0) 1056286822ddS return (Z_ERR); 1057286822ddS 1058286822ddS /* Now cleanup any snapshots remaining. */ 1059286822ddS if (zfs_iter_snapshots(zhp, rm_snap, NULL) != 0) { 1060286822ddS zfs_close(zhp); 1061286822ddS return (Z_ERR); 1062286822ddS } 1063286822ddS 10640b5de56dSgjelinek /* 1065286822ddS * We can't destroy the file system if it has still has dependents. 1066286822ddS * There shouldn't be any at this point, but we'll double check. 10670b5de56dSgjelinek */ 1068286822ddS if (zfs_iter_dependents(zhp, B_TRUE, has_dependent, NULL) != 0) { 1069286822ddS (void) fprintf(stderr, gettext("zfs destroy %s failed: the " 1070286822ddS "dataset still has dependents\n"), zfs_get_name(zhp)); 10710b5de56dSgjelinek zfs_close(zhp); 10720b5de56dSgjelinek return (Z_ERR); 10730b5de56dSgjelinek } 10740b5de56dSgjelinek 10750b5de56dSgjelinek /* 10760b5de56dSgjelinek * This might be a clone. Try to get the snapshot so we can attempt 10770b5de56dSgjelinek * to destroy that as well. 10780b5de56dSgjelinek */ 10790b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, 108099653d4eSeschrock NULL, 0, B_FALSE) == 0) 10810b5de56dSgjelinek is_clone = B_TRUE; 10820b5de56dSgjelinek 1083286822ddS if (zfs_unmount(zhp, NULL, 0) != 0) { 1084286822ddS (void) fprintf(stderr, gettext("zfs unmount %s failed: %s\n"), 1085286822ddS zfs_get_name(zhp), libzfs_error_description(g_zfs)); 1086286822ddS zfs_close(zhp); 1087286822ddS return (Z_ERR); 1088286822ddS } 1089286822ddS 1090842727c2SChris Kirby if (zfs_destroy(zhp, B_FALSE) != 0) { 10910b5de56dSgjelinek /* 10920b5de56dSgjelinek * If the destroy fails for some reason, try to remount 10930b5de56dSgjelinek * the file system so that we can use "rm -rf" to clean up 10940b5de56dSgjelinek * instead. 10950b5de56dSgjelinek */ 1096286822ddS (void) fprintf(stderr, gettext("zfs destroy %s failed: %s\n"), 1097286822ddS zfs_get_name(zhp), libzfs_error_description(g_zfs)); 10980b5de56dSgjelinek (void) zfs_mount(zhp, NULL, 0); 10990b5de56dSgjelinek zfs_close(zhp); 11000b5de56dSgjelinek return (Z_ERR); 11010b5de56dSgjelinek } 11020b5de56dSgjelinek 1103d9e728a2Sgjelinek /* 1104d9e728a2Sgjelinek * If the zone has ever been moved then the mountpoint dir will not be 1105d9e728a2Sgjelinek * cleaned up by the zfs_destroy(). To handle this case try to clean 1106d9e728a2Sgjelinek * it up now but don't worry if it fails, that will be normal. 1107d9e728a2Sgjelinek */ 1108d9e728a2Sgjelinek (void) rmdir(zonepath); 1109d9e728a2Sgjelinek 11100b5de56dSgjelinek (void) printf(gettext("The ZFS file system for this zone has been " 11110b5de56dSgjelinek "destroyed.\n")); 11120b5de56dSgjelinek 11130b5de56dSgjelinek if (is_clone) { 11140b5de56dSgjelinek zfs_handle_t *ohp; 11150b5de56dSgjelinek 11160b5de56dSgjelinek /* 11170b5de56dSgjelinek * Try to clean up the snapshot that the clone was taken from. 11180b5de56dSgjelinek */ 111999653d4eSeschrock if ((ohp = zfs_open(g_zfs, origin, 112099653d4eSeschrock ZFS_TYPE_SNAPSHOT)) != NULL) { 11213bb79becSeschrock if (zfs_iter_dependents(ohp, B_TRUE, has_dependent, 11223bb79becSeschrock NULL) == 0 && zfs_unmount(ohp, NULL, 0) == 0) 1123842727c2SChris Kirby (void) zfs_destroy(ohp, B_FALSE); 11240b5de56dSgjelinek zfs_close(ohp); 11250b5de56dSgjelinek } 11260b5de56dSgjelinek } 11270b5de56dSgjelinek 11280b5de56dSgjelinek zfs_close(zhp); 11290b5de56dSgjelinek return (Z_OK); 11300b5de56dSgjelinek } 11310b5de56dSgjelinek 11320b5de56dSgjelinek /* 11330b5de56dSgjelinek * Return true if the path is its own zfs file system. We determine this 11340b5de56dSgjelinek * by stat-ing the path to see if it is zfs and stat-ing the parent to see 11350b5de56dSgjelinek * if it is a different fs. 11360b5de56dSgjelinek */ 11370b5de56dSgjelinek boolean_t 11380b5de56dSgjelinek is_zonepath_zfs(char *zonepath) 11390b5de56dSgjelinek { 11400b5de56dSgjelinek int res; 11410b5de56dSgjelinek char *path; 11420b5de56dSgjelinek char *parent; 11433f2f09c1Sdp struct statvfs64 buf1, buf2; 11440b5de56dSgjelinek 11453f2f09c1Sdp if (statvfs64(zonepath, &buf1) != 0) 11460b5de56dSgjelinek return (B_FALSE); 11470b5de56dSgjelinek 11480b5de56dSgjelinek if (strcmp(buf1.f_basetype, "zfs") != 0) 11490b5de56dSgjelinek return (B_FALSE); 11500b5de56dSgjelinek 11510b5de56dSgjelinek if ((path = strdup(zonepath)) == NULL) 11520b5de56dSgjelinek return (B_FALSE); 11530b5de56dSgjelinek 11540b5de56dSgjelinek parent = dirname(path); 11553f2f09c1Sdp res = statvfs64(parent, &buf2); 11560b5de56dSgjelinek free(path); 11570b5de56dSgjelinek 11580b5de56dSgjelinek if (res != 0) 11590b5de56dSgjelinek return (B_FALSE); 11600b5de56dSgjelinek 11610b5de56dSgjelinek if (buf1.f_fsid == buf2.f_fsid) 11620b5de56dSgjelinek return (B_FALSE); 11630b5de56dSgjelinek 11640b5de56dSgjelinek return (B_TRUE); 11650b5de56dSgjelinek } 11660b5de56dSgjelinek 11670b5de56dSgjelinek /* 11680b5de56dSgjelinek * Implement the fast move of a ZFS file system by simply updating the 11690b5de56dSgjelinek * mountpoint. Since it is file system already, we don't have the 11700b5de56dSgjelinek * issue of cross-file system copying. 11710b5de56dSgjelinek */ 11720b5de56dSgjelinek int 11730b5de56dSgjelinek move_zfs(char *zonepath, char *new_zonepath) 11740b5de56dSgjelinek { 11750b5de56dSgjelinek int ret = Z_ERR; 11760b5de56dSgjelinek zfs_handle_t *zhp; 11770b5de56dSgjelinek 117899653d4eSeschrock if ((zhp = mount2zhandle(zonepath)) == NULL) 11790b5de56dSgjelinek return (Z_ERR); 11800b5de56dSgjelinek 1181e9dbad6fSeschrock if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1182e9dbad6fSeschrock new_zonepath) == 0) { 11830b5de56dSgjelinek /* 11840b5de56dSgjelinek * Clean up the old mount point. We ignore any failure since 11850b5de56dSgjelinek * the zone is already successfully mounted on the new path. 11860b5de56dSgjelinek */ 11870b5de56dSgjelinek (void) rmdir(zonepath); 11880b5de56dSgjelinek ret = Z_OK; 11890b5de56dSgjelinek } 11900b5de56dSgjelinek 11910b5de56dSgjelinek zfs_close(zhp); 11920b5de56dSgjelinek 11930b5de56dSgjelinek return (ret); 11940b5de56dSgjelinek } 11950b5de56dSgjelinek 11960b5de56dSgjelinek /* 11970b5de56dSgjelinek * Validate that the given dataset exists on the system, and that neither it nor 11980b5de56dSgjelinek * its children are zvols. 11990b5de56dSgjelinek * 12000b5de56dSgjelinek * Note that we don't do anything with the 'zoned' property here. All 12010b5de56dSgjelinek * management is done in zoneadmd when the zone is actually rebooted. This 12020b5de56dSgjelinek * allows us to automatically set the zoned property even when a zone is 12030b5de56dSgjelinek * rebooted by the administrator. 12040b5de56dSgjelinek */ 12050b5de56dSgjelinek int 12060b5de56dSgjelinek verify_datasets(zone_dochandle_t handle) 12070b5de56dSgjelinek { 12080b5de56dSgjelinek int return_code = Z_OK; 12090b5de56dSgjelinek struct zone_dstab dstab; 12100b5de56dSgjelinek zfs_handle_t *zhp; 12110b5de56dSgjelinek char propbuf[ZFS_MAXPROPLEN]; 12120b5de56dSgjelinek char source[ZFS_MAXNAMELEN]; 1213990b4856Slling zprop_source_t srctype; 12140b5de56dSgjelinek 12150b5de56dSgjelinek if (zonecfg_setdsent(handle) != Z_OK) { 12160b5de56dSgjelinek /* 12170b5de56dSgjelinek * TRANSLATION_NOTE 12180b5de56dSgjelinek * zfs and dataset are literals that should not be translated. 12190b5de56dSgjelinek */ 12200b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs datasets: " 12210b5de56dSgjelinek "unable to enumerate datasets\n")); 12220b5de56dSgjelinek return (Z_ERR); 12230b5de56dSgjelinek } 12240b5de56dSgjelinek 12250b5de56dSgjelinek while (zonecfg_getdsent(handle, &dstab) == Z_OK) { 12260b5de56dSgjelinek 122799653d4eSeschrock if ((zhp = zfs_open(g_zfs, dstab.zone_dataset_name, 12280b5de56dSgjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) { 122999653d4eSeschrock (void) fprintf(stderr, gettext("could not verify zfs " 123099653d4eSeschrock "dataset %s: %s\n"), dstab.zone_dataset_name, 123199653d4eSeschrock libzfs_error_description(g_zfs)); 12320b5de56dSgjelinek return_code = Z_ERR; 12330b5de56dSgjelinek continue; 12340b5de56dSgjelinek } 12350b5de56dSgjelinek 12360b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, 12370b5de56dSgjelinek sizeof (propbuf), &srctype, source, 12380b5de56dSgjelinek sizeof (source), 0) == 0 && 1239990b4856Slling (srctype == ZPROP_SRC_INHERITED)) { 12400b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs " 12410b5de56dSgjelinek "dataset %s: mountpoint cannot be inherited\n"), 12420b5de56dSgjelinek dstab.zone_dataset_name); 12430b5de56dSgjelinek return_code = Z_ERR; 12440b5de56dSgjelinek zfs_close(zhp); 12450b5de56dSgjelinek continue; 12460b5de56dSgjelinek } 12470b5de56dSgjelinek 12480b5de56dSgjelinek if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 12490b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify zfs " 12500b5de56dSgjelinek "dataset %s: volumes cannot be specified as a " 12510b5de56dSgjelinek "zone dataset resource\n"), 12520b5de56dSgjelinek dstab.zone_dataset_name); 12530b5de56dSgjelinek return_code = Z_ERR; 12540b5de56dSgjelinek } 12550b5de56dSgjelinek 12560b5de56dSgjelinek if (zfs_iter_children(zhp, check_zvol, NULL) != 0) 12570b5de56dSgjelinek return_code = Z_ERR; 12580b5de56dSgjelinek 12590b5de56dSgjelinek zfs_close(zhp); 12600b5de56dSgjelinek } 12610b5de56dSgjelinek (void) zonecfg_enddsent(handle); 12620b5de56dSgjelinek 12630b5de56dSgjelinek return (return_code); 12640b5de56dSgjelinek } 12650b5de56dSgjelinek 12660b5de56dSgjelinek /* 12670b5de56dSgjelinek * Verify that the ZFS dataset exists, and its mountpoint 12680b5de56dSgjelinek * property is set to "legacy". 12690b5de56dSgjelinek */ 12700b5de56dSgjelinek int 12710b5de56dSgjelinek verify_fs_zfs(struct zone_fstab *fstab) 12720b5de56dSgjelinek { 12730b5de56dSgjelinek zfs_handle_t *zhp; 12740b5de56dSgjelinek char propbuf[ZFS_MAXPROPLEN]; 12750b5de56dSgjelinek 127699653d4eSeschrock if ((zhp = zfs_open(g_zfs, fstab->zone_fs_special, 1277990b4856Slling ZFS_TYPE_DATASET)) == NULL) { 12780b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: " 12790b5de56dSgjelinek "could not access zfs dataset '%s'\n"), 12800b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 12810b5de56dSgjelinek return (Z_ERR); 12820b5de56dSgjelinek } 12830b5de56dSgjelinek 12840b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 12850b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify fs %s: " 12860b5de56dSgjelinek "'%s' is not a file system\n"), 12870b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 12880b5de56dSgjelinek zfs_close(zhp); 12890b5de56dSgjelinek return (Z_ERR); 12900b5de56dSgjelinek } 12910b5de56dSgjelinek 12920b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf), 12930b5de56dSgjelinek NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) { 12940b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: " 12950b5de56dSgjelinek "zfs '%s' mountpoint is not \"legacy\"\n"), 12960b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 12970b5de56dSgjelinek zfs_close(zhp); 12980b5de56dSgjelinek return (Z_ERR); 12990b5de56dSgjelinek } 13000b5de56dSgjelinek 13010b5de56dSgjelinek zfs_close(zhp); 130299653d4eSeschrock return (Z_OK); 130399653d4eSeschrock } 130499653d4eSeschrock 1305*0094b373Sjv227347 /* 1306*0094b373Sjv227347 * Destroy the specified mnttab structure that was created by mnttab_dup(). 1307*0094b373Sjv227347 * NOTE: The structure's mnt_time field isn't freed. 1308*0094b373Sjv227347 */ 1309*0094b373Sjv227347 static void 1310*0094b373Sjv227347 mnttab_destroy(struct mnttab *tabp) 1311*0094b373Sjv227347 { 1312*0094b373Sjv227347 assert(tabp != NULL); 1313*0094b373Sjv227347 1314*0094b373Sjv227347 free(tabp->mnt_mountp); 1315*0094b373Sjv227347 free(tabp->mnt_special); 1316*0094b373Sjv227347 free(tabp->mnt_fstype); 1317*0094b373Sjv227347 free(tabp->mnt_mntopts); 1318*0094b373Sjv227347 free(tabp); 1319*0094b373Sjv227347 } 1320*0094b373Sjv227347 1321*0094b373Sjv227347 /* 1322*0094b373Sjv227347 * Duplicate the specified mnttab structure. The mnt_mountp and mnt_time 1323*0094b373Sjv227347 * fields aren't duplicated. This function returns a pointer to the new mnttab 1324*0094b373Sjv227347 * structure or NULL if an error occurred. If an error occurs, then this 1325*0094b373Sjv227347 * function sets errno to reflect the error. mnttab structures created by 1326*0094b373Sjv227347 * this function should be destroyed via mnttab_destroy(). 1327*0094b373Sjv227347 */ 1328*0094b373Sjv227347 static struct mnttab * 1329*0094b373Sjv227347 mnttab_dup(const struct mnttab *srcp) 1330*0094b373Sjv227347 { 1331*0094b373Sjv227347 struct mnttab *retval; 1332*0094b373Sjv227347 1333*0094b373Sjv227347 assert(srcp != NULL); 1334*0094b373Sjv227347 1335*0094b373Sjv227347 retval = (struct mnttab *)calloc(1, sizeof (*retval)); 1336*0094b373Sjv227347 if (retval == NULL) { 1337*0094b373Sjv227347 errno = ENOMEM; 1338*0094b373Sjv227347 return (NULL); 1339*0094b373Sjv227347 } 1340*0094b373Sjv227347 if (srcp->mnt_special != NULL) { 1341*0094b373Sjv227347 retval->mnt_special = strdup(srcp->mnt_special); 1342*0094b373Sjv227347 if (retval->mnt_special == NULL) 1343*0094b373Sjv227347 goto err; 1344*0094b373Sjv227347 } 1345*0094b373Sjv227347 if (srcp->mnt_fstype != NULL) { 1346*0094b373Sjv227347 retval->mnt_fstype = strdup(srcp->mnt_fstype); 1347*0094b373Sjv227347 if (retval->mnt_fstype == NULL) 1348*0094b373Sjv227347 goto err; 1349*0094b373Sjv227347 } 1350*0094b373Sjv227347 retval->mnt_mntopts = (char *)malloc(MAX_MNTOPT_STR * sizeof (char)); 1351*0094b373Sjv227347 if (retval->mnt_mntopts == NULL) 1352*0094b373Sjv227347 goto err; 1353*0094b373Sjv227347 if (srcp->mnt_mntopts != NULL) { 1354*0094b373Sjv227347 if (strlcpy(retval->mnt_mntopts, srcp->mnt_mntopts, 1355*0094b373Sjv227347 MAX_MNTOPT_STR * sizeof (char)) >= MAX_MNTOPT_STR * 1356*0094b373Sjv227347 sizeof (char)) { 1357*0094b373Sjv227347 mnttab_destroy(retval); 1358*0094b373Sjv227347 errno = EOVERFLOW; /* similar to mount(2) behavior */ 1359*0094b373Sjv227347 return (NULL); 1360*0094b373Sjv227347 } 1361*0094b373Sjv227347 } else { 1362*0094b373Sjv227347 retval->mnt_mntopts[0] = '\0'; 1363*0094b373Sjv227347 } 1364*0094b373Sjv227347 return (retval); 1365*0094b373Sjv227347 1366*0094b373Sjv227347 err: 1367*0094b373Sjv227347 mnttab_destroy(retval); 1368*0094b373Sjv227347 errno = ENOMEM; 1369*0094b373Sjv227347 return (NULL); 1370*0094b373Sjv227347 } 1371*0094b373Sjv227347 1372*0094b373Sjv227347 /* 1373*0094b373Sjv227347 * Determine whether the specified ZFS dataset's mountpoint property is set 1374*0094b373Sjv227347 * to "legacy". If the specified dataset does not have a legacy mountpoint, 1375*0094b373Sjv227347 * then the string pointer to which the mountpoint argument points is assigned 1376*0094b373Sjv227347 * a dynamically-allocated string containing the dataset's mountpoint 1377*0094b373Sjv227347 * property. If the dataset's mountpoint property is "legacy" or a libzfs 1378*0094b373Sjv227347 * error occurs, then the string pointer to which the mountpoint argument 1379*0094b373Sjv227347 * points isn't modified. 1380*0094b373Sjv227347 * 1381*0094b373Sjv227347 * This function returns B_TRUE if it doesn't encounter any fatal errors. 1382*0094b373Sjv227347 * It returns B_FALSE if it encounters a fatal error and sets errno to the 1383*0094b373Sjv227347 * appropriate error code. 1384*0094b373Sjv227347 */ 1385*0094b373Sjv227347 static boolean_t 1386*0094b373Sjv227347 get_zfs_non_legacy_mountpoint(const char *dataset_name, char **mountpoint) 1387*0094b373Sjv227347 { 1388*0094b373Sjv227347 zfs_handle_t *zhp; 1389*0094b373Sjv227347 char propbuf[ZFS_MAXPROPLEN]; 1390*0094b373Sjv227347 1391*0094b373Sjv227347 assert(dataset_name != NULL); 1392*0094b373Sjv227347 assert(mountpoint != NULL); 1393*0094b373Sjv227347 1394*0094b373Sjv227347 if ((zhp = zfs_open(g_zfs, dataset_name, ZFS_TYPE_DATASET)) == NULL) { 1395*0094b373Sjv227347 errno = EINVAL; 1396*0094b373Sjv227347 return (B_FALSE); 1397*0094b373Sjv227347 } 1398*0094b373Sjv227347 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf), 1399*0094b373Sjv227347 NULL, NULL, 0, 0) != 0) { 1400*0094b373Sjv227347 zfs_close(zhp); 1401*0094b373Sjv227347 errno = EINVAL; 1402*0094b373Sjv227347 return (B_FALSE); 1403*0094b373Sjv227347 } 1404*0094b373Sjv227347 zfs_close(zhp); 1405*0094b373Sjv227347 if (strcmp(propbuf, "legacy") != 0) { 1406*0094b373Sjv227347 if ((*mountpoint = strdup(propbuf)) == NULL) { 1407*0094b373Sjv227347 errno = ENOMEM; 1408*0094b373Sjv227347 return (B_FALSE); 1409*0094b373Sjv227347 } 1410*0094b373Sjv227347 } 1411*0094b373Sjv227347 return (B_TRUE); 1412*0094b373Sjv227347 } 1413*0094b373Sjv227347 1414*0094b373Sjv227347 1415*0094b373Sjv227347 /* 1416*0094b373Sjv227347 * This zonecfg_find_mounts() callback records information about mounts of 1417*0094b373Sjv227347 * interest in a zonepath. It also tallies the number of zone 1418*0094b373Sjv227347 * root overlay mounts and the number of unexpected mounts found. 1419*0094b373Sjv227347 * This function outputs errors using zerror() if it finds unexpected 1420*0094b373Sjv227347 * mounts. cookiep should point to an initialized zone_mounts_t structure. 1421*0094b373Sjv227347 * 1422*0094b373Sjv227347 * This function returns zero on success and a nonzero value on failure. 1423*0094b373Sjv227347 */ 1424*0094b373Sjv227347 static int 1425*0094b373Sjv227347 zone_mounts_cb(const struct mnttab *mountp, void *cookiep) 1426*0094b373Sjv227347 { 1427*0094b373Sjv227347 zone_mounts_t *mounts; 1428*0094b373Sjv227347 const char *zone_mount_dir; 1429*0094b373Sjv227347 1430*0094b373Sjv227347 assert(mountp != NULL); 1431*0094b373Sjv227347 assert(cookiep != NULL); 1432*0094b373Sjv227347 1433*0094b373Sjv227347 mounts = (zone_mounts_t *)cookiep; 1434*0094b373Sjv227347 zone_mount_dir = mountp->mnt_mountp + mounts->zonepath_len; 1435*0094b373Sjv227347 if (strcmp(zone_mount_dir, "/root") == 0) { 1436*0094b373Sjv227347 /* 1437*0094b373Sjv227347 * Check for an overlay mount. If we already detected a /root 1438*0094b373Sjv227347 * mount, then the current mount must be an overlay mount. 1439*0094b373Sjv227347 */ 1440*0094b373Sjv227347 if (mounts->root_mnttab != NULL) { 1441*0094b373Sjv227347 mounts->num_root_overlay_mounts++; 1442*0094b373Sjv227347 return (0); 1443*0094b373Sjv227347 } 1444*0094b373Sjv227347 1445*0094b373Sjv227347 /* 1446*0094b373Sjv227347 * Store the root mount's mnttab information in the 1447*0094b373Sjv227347 * zone_mounts_t structure for future use. 1448*0094b373Sjv227347 */ 1449*0094b373Sjv227347 if ((mounts->root_mnttab = mnttab_dup(mountp)) == NULL) { 1450*0094b373Sjv227347 zperror(cmd_to_str(CMD_MOVE), B_FALSE); 1451*0094b373Sjv227347 return (-1); 1452*0094b373Sjv227347 } 1453*0094b373Sjv227347 1454*0094b373Sjv227347 /* 1455*0094b373Sjv227347 * Determine if the filesystem is a ZFS filesystem with a 1456*0094b373Sjv227347 * non-legacy mountpoint. If it is, then set the root 1457*0094b373Sjv227347 * filesystem's mnttab's mnt_mountp field to a non-NULL 1458*0094b373Sjv227347 * value, which will serve as a flag to indicate this special 1459*0094b373Sjv227347 * condition. 1460*0094b373Sjv227347 */ 1461*0094b373Sjv227347 if (strcmp(mountp->mnt_fstype, MNTTYPE_ZFS) == 0 && 1462*0094b373Sjv227347 get_zfs_non_legacy_mountpoint(mountp->mnt_special, 1463*0094b373Sjv227347 &mounts->root_mnttab->mnt_mountp) != B_TRUE) { 1464*0094b373Sjv227347 zperror(cmd_to_str(CMD_MOVE), B_FALSE); 1465*0094b373Sjv227347 return (-1); 1466*0094b373Sjv227347 } 1467*0094b373Sjv227347 } else { 1468*0094b373Sjv227347 /* 1469*0094b373Sjv227347 * An unexpected mount was found. Notify the user. 1470*0094b373Sjv227347 */ 1471*0094b373Sjv227347 if (mounts->num_unexpected_mounts == 0) 1472*0094b373Sjv227347 zerror(gettext("These file systems are mounted on " 1473*0094b373Sjv227347 "subdirectories of %s.\n"), mounts->zonepath); 1474*0094b373Sjv227347 mounts->num_unexpected_mounts++; 1475*0094b373Sjv227347 (void) zfm_print(mountp, NULL); 1476*0094b373Sjv227347 } 1477*0094b373Sjv227347 return (0); 1478*0094b373Sjv227347 } 1479*0094b373Sjv227347 1480*0094b373Sjv227347 /* 1481*0094b373Sjv227347 * Initialize the specified zone_mounts_t structure for the given zonepath. 1482*0094b373Sjv227347 * If this function succeeds, it returns zero and the specified zone_mounts_t 1483*0094b373Sjv227347 * structure contains information about mounts in the specified zonepath. 1484*0094b373Sjv227347 * The function returns a nonzero value if it fails. The zone_mounts_t 1485*0094b373Sjv227347 * structure doesn't need be destroyed via zone_mounts_destroy() if this 1486*0094b373Sjv227347 * function fails. 1487*0094b373Sjv227347 */ 1488*0094b373Sjv227347 int 1489*0094b373Sjv227347 zone_mounts_init(zone_mounts_t *mounts, const char *zonepath) 1490*0094b373Sjv227347 { 1491*0094b373Sjv227347 assert(mounts != NULL); 1492*0094b373Sjv227347 assert(zonepath != NULL); 1493*0094b373Sjv227347 1494*0094b373Sjv227347 bzero(mounts, sizeof (*mounts)); 1495*0094b373Sjv227347 if ((mounts->zonepath = strdup(zonepath)) == NULL) { 1496*0094b373Sjv227347 zerror(gettext("the process ran out of memory while checking " 1497*0094b373Sjv227347 "for mounts in zonepath %s."), zonepath); 1498*0094b373Sjv227347 return (-1); 1499*0094b373Sjv227347 } 1500*0094b373Sjv227347 mounts->zonepath_len = strlen(zonepath); 1501*0094b373Sjv227347 if (zonecfg_find_mounts((char *)zonepath, zone_mounts_cb, mounts) == 1502*0094b373Sjv227347 -1) { 1503*0094b373Sjv227347 zerror(gettext("an error occurred while checking for mounts " 1504*0094b373Sjv227347 "in zonepath %s."), zonepath); 1505*0094b373Sjv227347 zone_mounts_destroy(mounts); 1506*0094b373Sjv227347 return (-1); 1507*0094b373Sjv227347 } 1508*0094b373Sjv227347 return (0); 1509*0094b373Sjv227347 } 1510*0094b373Sjv227347 1511*0094b373Sjv227347 /* 1512*0094b373Sjv227347 * Destroy the memory used by the specified zone_mounts_t structure's fields. 1513*0094b373Sjv227347 * This function doesn't free the memory occupied by the structure itself 1514*0094b373Sjv227347 * (i.e., it doesn't free the parameter). 1515*0094b373Sjv227347 */ 1516*0094b373Sjv227347 void 1517*0094b373Sjv227347 zone_mounts_destroy(zone_mounts_t *mounts) 1518*0094b373Sjv227347 { 1519*0094b373Sjv227347 assert(mounts != NULL); 1520*0094b373Sjv227347 1521*0094b373Sjv227347 free(mounts->zonepath); 1522*0094b373Sjv227347 if (mounts->root_mnttab != NULL) 1523*0094b373Sjv227347 mnttab_destroy(mounts->root_mnttab); 1524*0094b373Sjv227347 } 1525*0094b373Sjv227347 1526*0094b373Sjv227347 /* 1527*0094b373Sjv227347 * Mount a moving zone's root filesystem (if it had a root filesystem mount 1528*0094b373Sjv227347 * prior to the move) using the specified zonepath. mounts should refer to 1529*0094b373Sjv227347 * the zone_mounts_t structure describing the zone's mount information. 1530*0094b373Sjv227347 * 1531*0094b373Sjv227347 * This function returns zero if the mount succeeds and a nonzero value 1532*0094b373Sjv227347 * if it doesn't. 1533*0094b373Sjv227347 */ 1534*0094b373Sjv227347 int 1535*0094b373Sjv227347 zone_mount_rootfs(zone_mounts_t *mounts, const char *zonepath) 1536*0094b373Sjv227347 { 1537*0094b373Sjv227347 char zoneroot[MAXPATHLEN]; 1538*0094b373Sjv227347 struct mnttab *mtab; 1539*0094b373Sjv227347 int flags; 1540*0094b373Sjv227347 1541*0094b373Sjv227347 assert(mounts != NULL); 1542*0094b373Sjv227347 assert(zonepath != NULL); 1543*0094b373Sjv227347 1544*0094b373Sjv227347 /* 1545*0094b373Sjv227347 * If there isn't a root filesystem, then don't do anything. 1546*0094b373Sjv227347 */ 1547*0094b373Sjv227347 mtab = mounts->root_mnttab; 1548*0094b373Sjv227347 if (mtab == NULL) 1549*0094b373Sjv227347 return (0); 1550*0094b373Sjv227347 1551*0094b373Sjv227347 /* 1552*0094b373Sjv227347 * Determine the root filesystem's new mountpoint. 1553*0094b373Sjv227347 */ 1554*0094b373Sjv227347 if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >= 1555*0094b373Sjv227347 sizeof (zoneroot)) { 1556*0094b373Sjv227347 zerror(gettext("Zonepath %s is too long.\n"), zonepath); 1557*0094b373Sjv227347 return (-1); 1558*0094b373Sjv227347 } 1559*0094b373Sjv227347 1560*0094b373Sjv227347 /* 1561*0094b373Sjv227347 * If the root filesystem is a non-legacy ZFS filesystem (i.e., if it's 1562*0094b373Sjv227347 * mnt_mountp field is non-NULL), then make the filesystem's new 1563*0094b373Sjv227347 * mount point its mountpoint property and mount the filesystem. 1564*0094b373Sjv227347 */ 1565*0094b373Sjv227347 if (mtab->mnt_mountp != NULL) { 1566*0094b373Sjv227347 zfs_handle_t *zhp; 1567*0094b373Sjv227347 1568*0094b373Sjv227347 if ((zhp = zfs_open(g_zfs, mtab->mnt_special, 1569*0094b373Sjv227347 ZFS_TYPE_DATASET)) == NULL) { 1570*0094b373Sjv227347 zerror(gettext("could not get ZFS handle for the zone's" 1571*0094b373Sjv227347 " root filesystem")); 1572*0094b373Sjv227347 return (-1); 1573*0094b373Sjv227347 } 1574*0094b373Sjv227347 if (zfs_prop_set(zhp, zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1575*0094b373Sjv227347 zoneroot) != 0) { 1576*0094b373Sjv227347 zerror(gettext("could not modify zone's root " 1577*0094b373Sjv227347 "filesystem's mountpoint property")); 1578*0094b373Sjv227347 zfs_close(zhp); 1579*0094b373Sjv227347 return (-1); 1580*0094b373Sjv227347 } 1581*0094b373Sjv227347 if (zfs_mount(zhp, mtab->mnt_mntopts, 0) != 0) { 1582*0094b373Sjv227347 zerror(gettext("unable to mount zone root %s: %s"), 1583*0094b373Sjv227347 zoneroot, libzfs_error_description(g_zfs)); 1584*0094b373Sjv227347 if (zfs_prop_set(zhp, 1585*0094b373Sjv227347 zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 1586*0094b373Sjv227347 mtab->mnt_mountp) != 0) 1587*0094b373Sjv227347 zerror(gettext("unable to restore zone's root " 1588*0094b373Sjv227347 "filesystem's mountpoint property")); 1589*0094b373Sjv227347 zfs_close(zhp); 1590*0094b373Sjv227347 return (-1); 1591*0094b373Sjv227347 } 1592*0094b373Sjv227347 zfs_close(zhp); 1593*0094b373Sjv227347 return (0); 1594*0094b373Sjv227347 } 1595*0094b373Sjv227347 1596*0094b373Sjv227347 /* 1597*0094b373Sjv227347 * The root filesystem is either a legacy-mounted ZFS filesystem or 1598*0094b373Sjv227347 * a non-ZFS filesystem. Use mount(2) to mount the root filesystem. 1599*0094b373Sjv227347 */ 1600*0094b373Sjv227347 if (mtab->mnt_mntopts != NULL) 1601*0094b373Sjv227347 flags = MS_OPTIONSTR; 1602*0094b373Sjv227347 else 1603*0094b373Sjv227347 flags = 0; 1604*0094b373Sjv227347 if (mount(mtab->mnt_special, zoneroot, flags, mtab->mnt_fstype, NULL, 0, 1605*0094b373Sjv227347 mtab->mnt_mntopts, MAX_MNTOPT_STR * sizeof (char)) != 0) { 1606*0094b373Sjv227347 flags = errno; 1607*0094b373Sjv227347 zerror(gettext("unable to mount zone root %s: %s"), zoneroot, 1608*0094b373Sjv227347 strerror(flags)); 1609*0094b373Sjv227347 return (-1); 1610*0094b373Sjv227347 } 1611*0094b373Sjv227347 return (0); 1612*0094b373Sjv227347 } 1613*0094b373Sjv227347 1614*0094b373Sjv227347 /* 1615*0094b373Sjv227347 * Unmount a moving zone's root filesystem (if such a mount exists) using the 1616*0094b373Sjv227347 * specified zonepath. mounts should refer to the zone_mounts_t structure 1617*0094b373Sjv227347 * describing the zone's mount information. If force is B_TRUE, then if the 1618*0094b373Sjv227347 * unmount fails, then the function will try to forcibly unmount the zone's root 1619*0094b373Sjv227347 * filesystem. 1620*0094b373Sjv227347 * 1621*0094b373Sjv227347 * This function returns zero if the unmount (forced or otherwise) succeeds; 1622*0094b373Sjv227347 * otherwise, it returns a nonzero value. 1623*0094b373Sjv227347 */ 1624*0094b373Sjv227347 int 1625*0094b373Sjv227347 zone_unmount_rootfs(zone_mounts_t *mounts, const char *zonepath, 1626*0094b373Sjv227347 boolean_t force) 1627*0094b373Sjv227347 { 1628*0094b373Sjv227347 char zoneroot[MAXPATHLEN]; 1629*0094b373Sjv227347 struct mnttab *mtab; 1630*0094b373Sjv227347 int err; 1631*0094b373Sjv227347 1632*0094b373Sjv227347 assert(mounts != NULL); 1633*0094b373Sjv227347 assert(zonepath != NULL); 1634*0094b373Sjv227347 1635*0094b373Sjv227347 /* 1636*0094b373Sjv227347 * If there isn't a root filesystem, then don't do anything. 1637*0094b373Sjv227347 */ 1638*0094b373Sjv227347 mtab = mounts->root_mnttab; 1639*0094b373Sjv227347 if (mtab == NULL) 1640*0094b373Sjv227347 return (0); 1641*0094b373Sjv227347 1642*0094b373Sjv227347 /* 1643*0094b373Sjv227347 * Determine the root filesystem's mountpoint. 1644*0094b373Sjv227347 */ 1645*0094b373Sjv227347 if (snprintf(zoneroot, sizeof (zoneroot), "%s/root", zonepath) >= 1646*0094b373Sjv227347 sizeof (zoneroot)) { 1647*0094b373Sjv227347 zerror(gettext("Zonepath %s is too long.\n"), zonepath); 1648*0094b373Sjv227347 return (-1); 1649*0094b373Sjv227347 } 1650*0094b373Sjv227347 1651*0094b373Sjv227347 /* 1652*0094b373Sjv227347 * If the root filesystem is a non-legacy ZFS fileystem, then unmount 1653*0094b373Sjv227347 * the filesystem via libzfs. 1654*0094b373Sjv227347 */ 1655*0094b373Sjv227347 if (mtab->mnt_mountp != NULL) { 1656*0094b373Sjv227347 zfs_handle_t *zhp; 1657*0094b373Sjv227347 1658*0094b373Sjv227347 if ((zhp = zfs_open(g_zfs, mtab->mnt_special, 1659*0094b373Sjv227347 ZFS_TYPE_DATASET)) == NULL) { 1660*0094b373Sjv227347 zerror(gettext("could not get ZFS handle for the zone's" 1661*0094b373Sjv227347 " root filesystem")); 1662*0094b373Sjv227347 return (-1); 1663*0094b373Sjv227347 } 1664*0094b373Sjv227347 if (zfs_unmount(zhp, zoneroot, 0) != 0) { 1665*0094b373Sjv227347 if (force && zfs_unmount(zhp, zoneroot, MS_FORCE) == 1666*0094b373Sjv227347 0) { 1667*0094b373Sjv227347 zfs_close(zhp); 1668*0094b373Sjv227347 return (0); 1669*0094b373Sjv227347 } 1670*0094b373Sjv227347 zerror(gettext("unable to unmount zone root %s: %s"), 1671*0094b373Sjv227347 zoneroot, libzfs_error_description(g_zfs)); 1672*0094b373Sjv227347 zfs_close(zhp); 1673*0094b373Sjv227347 return (-1); 1674*0094b373Sjv227347 } 1675*0094b373Sjv227347 zfs_close(zhp); 1676*0094b373Sjv227347 return (0); 1677*0094b373Sjv227347 } 1678*0094b373Sjv227347 1679*0094b373Sjv227347 /* 1680*0094b373Sjv227347 * Use umount(2) to unmount the root filesystem. If this fails, then 1681*0094b373Sjv227347 * forcibly unmount it if the force flag is set. 1682*0094b373Sjv227347 */ 1683*0094b373Sjv227347 if (umount(zoneroot) != 0) { 1684*0094b373Sjv227347 if (force && umount2(zoneroot, MS_FORCE) == 0) 1685*0094b373Sjv227347 return (0); 1686*0094b373Sjv227347 err = errno; 1687*0094b373Sjv227347 zerror(gettext("unable to unmount zone root %s: %s"), zoneroot, 1688*0094b373Sjv227347 strerror(err)); 1689*0094b373Sjv227347 return (-1); 1690*0094b373Sjv227347 } 1691*0094b373Sjv227347 return (0); 1692*0094b373Sjv227347 } 1693*0094b373Sjv227347 169499653d4eSeschrock int 169599653d4eSeschrock init_zfs(void) 169699653d4eSeschrock { 169799653d4eSeschrock if ((g_zfs = libzfs_init()) == NULL) { 169899653d4eSeschrock (void) fprintf(stderr, gettext("failed to initialize ZFS " 169999653d4eSeschrock "library\n")); 170099653d4eSeschrock return (Z_ERR); 170199653d4eSeschrock } 170299653d4eSeschrock 17030b5de56dSgjelinek return (Z_OK); 17040b5de56dSgjelinek } 1705