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