1*0b5de56dSgjelinek /* 2*0b5de56dSgjelinek * CDDL HEADER START 3*0b5de56dSgjelinek * 4*0b5de56dSgjelinek * The contents of this file are subject to the terms of the 5*0b5de56dSgjelinek * Common Development and Distribution License (the "License"). 6*0b5de56dSgjelinek * You may not use this file except in compliance with the License. 7*0b5de56dSgjelinek * 8*0b5de56dSgjelinek * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*0b5de56dSgjelinek * or http://www.opensolaris.org/os/licensing. 10*0b5de56dSgjelinek * See the License for the specific language governing permissions 11*0b5de56dSgjelinek * and limitations under the License. 12*0b5de56dSgjelinek * 13*0b5de56dSgjelinek * When distributing Covered Code, include this CDDL HEADER in each 14*0b5de56dSgjelinek * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*0b5de56dSgjelinek * If applicable, add the following below this CDDL HEADER, with the 16*0b5de56dSgjelinek * fields enclosed by brackets "[]" replaced with your own identifying 17*0b5de56dSgjelinek * information: Portions Copyright [yyyy] [name of copyright owner] 18*0b5de56dSgjelinek * 19*0b5de56dSgjelinek * CDDL HEADER END 20*0b5de56dSgjelinek */ 21*0b5de56dSgjelinek 22*0b5de56dSgjelinek /* 23*0b5de56dSgjelinek * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*0b5de56dSgjelinek * Use is subject to license terms. 25*0b5de56dSgjelinek */ 26*0b5de56dSgjelinek 27*0b5de56dSgjelinek #pragma ident "%Z%%M% %I% %E% SMI" 28*0b5de56dSgjelinek 29*0b5de56dSgjelinek /* 30*0b5de56dSgjelinek * This file contains the functions used to support the ZFS integration 31*0b5de56dSgjelinek * with zones. This includes validation (e.g. zonecfg dataset), cloning, 32*0b5de56dSgjelinek * file system creation and destruction. 33*0b5de56dSgjelinek */ 34*0b5de56dSgjelinek 35*0b5de56dSgjelinek #include <stdio.h> 36*0b5de56dSgjelinek #include <errno.h> 37*0b5de56dSgjelinek #include <unistd.h> 38*0b5de56dSgjelinek #include <string.h> 39*0b5de56dSgjelinek #include <locale.h> 40*0b5de56dSgjelinek #include <libintl.h> 41*0b5de56dSgjelinek #include <sys/stat.h> 42*0b5de56dSgjelinek #include <sys/statvfs.h> 43*0b5de56dSgjelinek #include <libgen.h> 44*0b5de56dSgjelinek #include <libzonecfg.h> 45*0b5de56dSgjelinek #include <sys/mnttab.h> 46*0b5de56dSgjelinek #include <libzfs.h> 47*0b5de56dSgjelinek 48*0b5de56dSgjelinek #include "zoneadm.h" 49*0b5de56dSgjelinek 50*0b5de56dSgjelinek static const char *current_dataset; 51*0b5de56dSgjelinek 52*0b5de56dSgjelinek typedef struct zfs_mount_data { 53*0b5de56dSgjelinek char *match_name; 54*0b5de56dSgjelinek zfs_handle_t *match_handle; 55*0b5de56dSgjelinek } zfs_mount_data_t; 56*0b5de56dSgjelinek 57*0b5de56dSgjelinek typedef struct zfs_snapshot_data { 58*0b5de56dSgjelinek char *match_name; 59*0b5de56dSgjelinek int len; 60*0b5de56dSgjelinek int max; 61*0b5de56dSgjelinek } zfs_snapshot_data_t; 62*0b5de56dSgjelinek 63*0b5de56dSgjelinek /* 64*0b5de56dSgjelinek * ZFS error handler to do nothing - do not print the libzfs error messages. 65*0b5de56dSgjelinek */ 66*0b5de56dSgjelinek /* ARGSUSED */ 67*0b5de56dSgjelinek static void 68*0b5de56dSgjelinek noop_err_handler(const char *fmt, va_list ap) 69*0b5de56dSgjelinek { 70*0b5de56dSgjelinek } 71*0b5de56dSgjelinek 72*0b5de56dSgjelinek /* 73*0b5de56dSgjelinek * Custom error handler for errors incurred as part of verifying datasets. We 74*0b5de56dSgjelinek * want to trim off the leading 'cannot open ...' to create a better error 75*0b5de56dSgjelinek * message. The only other way this can fail is if we fail to set the 'zoned' 76*0b5de56dSgjelinek * property. In this case we just pass the error on verbatim. 77*0b5de56dSgjelinek */ 78*0b5de56dSgjelinek static void 79*0b5de56dSgjelinek err_handler(const char *fmt, va_list ap) 80*0b5de56dSgjelinek { 81*0b5de56dSgjelinek char buf[1024]; 82*0b5de56dSgjelinek 83*0b5de56dSgjelinek (void) vsnprintf(buf, sizeof (buf), fmt, ap); 84*0b5de56dSgjelinek 85*0b5de56dSgjelinek if (strncmp(gettext("cannot open "), buf, 86*0b5de56dSgjelinek strlen(gettext("cannot open "))) == 0) 87*0b5de56dSgjelinek /* 88*0b5de56dSgjelinek * TRANSLATION_NOTE 89*0b5de56dSgjelinek * zfs and dataset are literals that should not be translated. 90*0b5de56dSgjelinek */ 91*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs " 92*0b5de56dSgjelinek "dataset %s%s\n"), current_dataset, strchr(buf, ':')); 93*0b5de56dSgjelinek else 94*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs dataset " 95*0b5de56dSgjelinek "%s: %s\n"), current_dataset, buf); 96*0b5de56dSgjelinek } 97*0b5de56dSgjelinek 98*0b5de56dSgjelinek /* 99*0b5de56dSgjelinek * A ZFS file system iterator call-back function which is used to validate 100*0b5de56dSgjelinek * datasets imported into the zone. 101*0b5de56dSgjelinek */ 102*0b5de56dSgjelinek /* ARGSUSED */ 103*0b5de56dSgjelinek static int 104*0b5de56dSgjelinek check_zvol(zfs_handle_t *zhp, void *unused) 105*0b5de56dSgjelinek { 106*0b5de56dSgjelinek int ret; 107*0b5de56dSgjelinek 108*0b5de56dSgjelinek if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 109*0b5de56dSgjelinek /* 110*0b5de56dSgjelinek * TRANSLATION_NOTE 111*0b5de56dSgjelinek * zfs and dataset are literals that should not be translated. 112*0b5de56dSgjelinek */ 113*0b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify zfs dataset %s: " 114*0b5de56dSgjelinek "volumes cannot be specified as a zone dataset resource\n"), 115*0b5de56dSgjelinek zfs_get_name(zhp)); 116*0b5de56dSgjelinek ret = -1; 117*0b5de56dSgjelinek } else { 118*0b5de56dSgjelinek ret = zfs_iter_children(zhp, check_zvol, NULL); 119*0b5de56dSgjelinek } 120*0b5de56dSgjelinek 121*0b5de56dSgjelinek zfs_close(zhp); 122*0b5de56dSgjelinek 123*0b5de56dSgjelinek return (ret); 124*0b5de56dSgjelinek } 125*0b5de56dSgjelinek 126*0b5de56dSgjelinek /* 127*0b5de56dSgjelinek * A ZFS file system iterator call-back function which returns the 128*0b5de56dSgjelinek * zfs_handle_t for a ZFS file system on the specified mount point. 129*0b5de56dSgjelinek */ 130*0b5de56dSgjelinek static int 131*0b5de56dSgjelinek match_mountpoint(zfs_handle_t *zhp, void *data) 132*0b5de56dSgjelinek { 133*0b5de56dSgjelinek int res; 134*0b5de56dSgjelinek zfs_mount_data_t *cbp; 135*0b5de56dSgjelinek char mp[ZFS_MAXPROPLEN]; 136*0b5de56dSgjelinek 137*0b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 138*0b5de56dSgjelinek zfs_close(zhp); 139*0b5de56dSgjelinek return (0); 140*0b5de56dSgjelinek } 141*0b5de56dSgjelinek 142*0b5de56dSgjelinek cbp = (zfs_mount_data_t *)data; 143*0b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 144*0b5de56dSgjelinek 0, FALSE) == 0 && strcmp(mp, cbp->match_name) == 0) { 145*0b5de56dSgjelinek cbp->match_handle = zhp; 146*0b5de56dSgjelinek return (1); 147*0b5de56dSgjelinek } 148*0b5de56dSgjelinek 149*0b5de56dSgjelinek res = zfs_iter_filesystems(zhp, match_mountpoint, data); 150*0b5de56dSgjelinek zfs_close(zhp); 151*0b5de56dSgjelinek return (res); 152*0b5de56dSgjelinek } 153*0b5de56dSgjelinek 154*0b5de56dSgjelinek /* 155*0b5de56dSgjelinek * Get ZFS handle for the specified mount point. 156*0b5de56dSgjelinek */ 157*0b5de56dSgjelinek static zfs_handle_t * 158*0b5de56dSgjelinek mount2zhandle(char *mountpoint) 159*0b5de56dSgjelinek { 160*0b5de56dSgjelinek zfs_mount_data_t cb; 161*0b5de56dSgjelinek 162*0b5de56dSgjelinek cb.match_name = mountpoint; 163*0b5de56dSgjelinek cb.match_handle = NULL; 164*0b5de56dSgjelinek (void) zfs_iter_root(match_mountpoint, &cb); 165*0b5de56dSgjelinek return (cb.match_handle); 166*0b5de56dSgjelinek } 167*0b5de56dSgjelinek 168*0b5de56dSgjelinek /* 169*0b5de56dSgjelinek * Check if there is already a file system (zfs or any other type) mounted on 170*0b5de56dSgjelinek * path. 171*0b5de56dSgjelinek */ 172*0b5de56dSgjelinek static boolean_t 173*0b5de56dSgjelinek is_mountpnt(char *path) 174*0b5de56dSgjelinek { 175*0b5de56dSgjelinek FILE *fp; 176*0b5de56dSgjelinek struct mnttab entry; 177*0b5de56dSgjelinek 178*0b5de56dSgjelinek if ((fp = fopen("/etc/mnttab", "r")) == NULL) 179*0b5de56dSgjelinek return (B_FALSE); 180*0b5de56dSgjelinek 181*0b5de56dSgjelinek while (getmntent(fp, &entry) == 0) { 182*0b5de56dSgjelinek if (strcmp(path, entry.mnt_mountp) == 0) { 183*0b5de56dSgjelinek (void) fclose(fp); 184*0b5de56dSgjelinek return (B_TRUE); 185*0b5de56dSgjelinek } 186*0b5de56dSgjelinek } 187*0b5de56dSgjelinek 188*0b5de56dSgjelinek (void) fclose(fp); 189*0b5de56dSgjelinek return (B_FALSE); 190*0b5de56dSgjelinek } 191*0b5de56dSgjelinek 192*0b5de56dSgjelinek /* 193*0b5de56dSgjelinek * Perform any necessary housekeeping tasks we need to do before we take 194*0b5de56dSgjelinek * a ZFS snapshot of the zone. What this really entails is that we are 195*0b5de56dSgjelinek * taking a sw inventory of the source zone, like we do when we detach, 196*0b5de56dSgjelinek * so that there is the XML manifest in the snapshot. We use that to 197*0b5de56dSgjelinek * validate the snapshot if it is the source of a clone at some later time. 198*0b5de56dSgjelinek */ 199*0b5de56dSgjelinek static int 200*0b5de56dSgjelinek pre_snapshot(char *source_zone) 201*0b5de56dSgjelinek { 202*0b5de56dSgjelinek int err; 203*0b5de56dSgjelinek zone_dochandle_t handle; 204*0b5de56dSgjelinek 205*0b5de56dSgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 206*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 207*0b5de56dSgjelinek return (Z_ERR); 208*0b5de56dSgjelinek } 209*0b5de56dSgjelinek 210*0b5de56dSgjelinek if ((err = zonecfg_get_handle(source_zone, handle)) != Z_OK) { 211*0b5de56dSgjelinek errno = err; 212*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 213*0b5de56dSgjelinek zonecfg_fini_handle(handle); 214*0b5de56dSgjelinek return (Z_ERR); 215*0b5de56dSgjelinek } 216*0b5de56dSgjelinek 217*0b5de56dSgjelinek if ((err = zonecfg_get_detach_info(handle, B_TRUE)) != Z_OK) { 218*0b5de56dSgjelinek errno = err; 219*0b5de56dSgjelinek zperror(gettext("getting the software version information " 220*0b5de56dSgjelinek "failed"), B_TRUE); 221*0b5de56dSgjelinek zonecfg_fini_handle(handle); 222*0b5de56dSgjelinek return (Z_ERR); 223*0b5de56dSgjelinek } 224*0b5de56dSgjelinek 225*0b5de56dSgjelinek if ((err = zonecfg_detach_save(handle)) != Z_OK) { 226*0b5de56dSgjelinek errno = err; 227*0b5de56dSgjelinek zperror(gettext("saving the software version manifest failed"), 228*0b5de56dSgjelinek B_TRUE); 229*0b5de56dSgjelinek zonecfg_fini_handle(handle); 230*0b5de56dSgjelinek return (Z_ERR); 231*0b5de56dSgjelinek } 232*0b5de56dSgjelinek 233*0b5de56dSgjelinek zonecfg_fini_handle(handle); 234*0b5de56dSgjelinek return (Z_OK); 235*0b5de56dSgjelinek } 236*0b5de56dSgjelinek 237*0b5de56dSgjelinek /* 238*0b5de56dSgjelinek * Perform any necessary housekeeping tasks we need to do after we take 239*0b5de56dSgjelinek * a ZFS snapshot of the zone. What this really entails is removing the 240*0b5de56dSgjelinek * sw inventory XML file from the zone. It is still in the snapshot where 241*0b5de56dSgjelinek * we want it, but we don't want it in the source zone itself. 242*0b5de56dSgjelinek */ 243*0b5de56dSgjelinek static int 244*0b5de56dSgjelinek post_snapshot(char *source_zone) 245*0b5de56dSgjelinek { 246*0b5de56dSgjelinek int err; 247*0b5de56dSgjelinek zone_dochandle_t handle; 248*0b5de56dSgjelinek 249*0b5de56dSgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 250*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 251*0b5de56dSgjelinek return (Z_ERR); 252*0b5de56dSgjelinek } 253*0b5de56dSgjelinek 254*0b5de56dSgjelinek if ((err = zonecfg_get_handle(source_zone, handle)) != Z_OK) { 255*0b5de56dSgjelinek errno = err; 256*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 257*0b5de56dSgjelinek zonecfg_fini_handle(handle); 258*0b5de56dSgjelinek return (Z_ERR); 259*0b5de56dSgjelinek } 260*0b5de56dSgjelinek 261*0b5de56dSgjelinek zonecfg_rm_detached(handle, B_FALSE); 262*0b5de56dSgjelinek zonecfg_fini_handle(handle); 263*0b5de56dSgjelinek 264*0b5de56dSgjelinek return (Z_OK); 265*0b5de56dSgjelinek } 266*0b5de56dSgjelinek 267*0b5de56dSgjelinek /* 268*0b5de56dSgjelinek * This is a ZFS snapshot iterator call-back function which returns the 269*0b5de56dSgjelinek * highest number of SUNWzone snapshots that have been taken. 270*0b5de56dSgjelinek */ 271*0b5de56dSgjelinek static int 272*0b5de56dSgjelinek get_snap_max(zfs_handle_t *zhp, void *data) 273*0b5de56dSgjelinek { 274*0b5de56dSgjelinek int res; 275*0b5de56dSgjelinek zfs_snapshot_data_t *cbp; 276*0b5de56dSgjelinek 277*0b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) { 278*0b5de56dSgjelinek zfs_close(zhp); 279*0b5de56dSgjelinek return (0); 280*0b5de56dSgjelinek } 281*0b5de56dSgjelinek 282*0b5de56dSgjelinek cbp = (zfs_snapshot_data_t *)data; 283*0b5de56dSgjelinek 284*0b5de56dSgjelinek if (strncmp(zfs_get_name(zhp), cbp->match_name, cbp->len) == 0) { 285*0b5de56dSgjelinek char *nump; 286*0b5de56dSgjelinek int num; 287*0b5de56dSgjelinek 288*0b5de56dSgjelinek nump = (char *)(zfs_get_name(zhp) + cbp->len); 289*0b5de56dSgjelinek num = atoi(nump); 290*0b5de56dSgjelinek if (num > cbp->max) 291*0b5de56dSgjelinek cbp->max = num; 292*0b5de56dSgjelinek } 293*0b5de56dSgjelinek 294*0b5de56dSgjelinek res = zfs_iter_snapshots(zhp, get_snap_max, data); 295*0b5de56dSgjelinek zfs_close(zhp); 296*0b5de56dSgjelinek return (res); 297*0b5de56dSgjelinek } 298*0b5de56dSgjelinek 299*0b5de56dSgjelinek /* 300*0b5de56dSgjelinek * Take a ZFS snapshot to be used for cloning the zone. 301*0b5de56dSgjelinek */ 302*0b5de56dSgjelinek static int 303*0b5de56dSgjelinek take_snapshot(char *source_zone, zfs_handle_t *zhp, char *snapshot_name, 304*0b5de56dSgjelinek int snap_size) 305*0b5de56dSgjelinek { 306*0b5de56dSgjelinek int res; 307*0b5de56dSgjelinek char template[ZFS_MAXNAMELEN]; 308*0b5de56dSgjelinek zfs_snapshot_data_t cb; 309*0b5de56dSgjelinek 310*0b5de56dSgjelinek /* 311*0b5de56dSgjelinek * First we need to figure out the next available name for the 312*0b5de56dSgjelinek * zone snapshot. Look through the list of zones snapshots for 313*0b5de56dSgjelinek * this file system to determine the maximum snapshot name. 314*0b5de56dSgjelinek */ 315*0b5de56dSgjelinek if (snprintf(template, sizeof (template), "%s@SUNWzone", 316*0b5de56dSgjelinek zfs_get_name(zhp)) >= sizeof (template)) 317*0b5de56dSgjelinek return (Z_ERR); 318*0b5de56dSgjelinek 319*0b5de56dSgjelinek cb.match_name = template; 320*0b5de56dSgjelinek cb.len = strlen(template); 321*0b5de56dSgjelinek cb.max = 0; 322*0b5de56dSgjelinek 323*0b5de56dSgjelinek if (zfs_iter_snapshots(zhp, get_snap_max, &cb) != 0) 324*0b5de56dSgjelinek return (Z_ERR); 325*0b5de56dSgjelinek 326*0b5de56dSgjelinek cb.max++; 327*0b5de56dSgjelinek 328*0b5de56dSgjelinek if (snprintf(snapshot_name, snap_size, "%s@SUNWzone%d", 329*0b5de56dSgjelinek zfs_get_name(zhp), cb.max) >= snap_size) 330*0b5de56dSgjelinek return (Z_ERR); 331*0b5de56dSgjelinek 332*0b5de56dSgjelinek if (pre_snapshot(source_zone) != Z_OK) 333*0b5de56dSgjelinek return (Z_ERR); 334*0b5de56dSgjelinek res = zfs_snapshot(snapshot_name); 335*0b5de56dSgjelinek if (post_snapshot(source_zone) != Z_OK) 336*0b5de56dSgjelinek return (Z_ERR); 337*0b5de56dSgjelinek 338*0b5de56dSgjelinek if (res != 0) 339*0b5de56dSgjelinek return (Z_ERR); 340*0b5de56dSgjelinek return (Z_OK); 341*0b5de56dSgjelinek } 342*0b5de56dSgjelinek 343*0b5de56dSgjelinek /* 344*0b5de56dSgjelinek * We are using an explicit snapshot from some earlier point in time so 345*0b5de56dSgjelinek * we need to validate it. This involves checking the sw inventory that 346*0b5de56dSgjelinek * we took when we made the snapshot to verify that the current sw config 347*0b5de56dSgjelinek * on the host is still valid to run a zone made from this snapshot. 348*0b5de56dSgjelinek */ 349*0b5de56dSgjelinek static int 350*0b5de56dSgjelinek validate_snapshot(char *snapshot_name, char *snap_path) 351*0b5de56dSgjelinek { 352*0b5de56dSgjelinek int err; 353*0b5de56dSgjelinek zone_dochandle_t handle; 354*0b5de56dSgjelinek zone_dochandle_t athandle = NULL; 355*0b5de56dSgjelinek 356*0b5de56dSgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 357*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 358*0b5de56dSgjelinek return (Z_ERR); 359*0b5de56dSgjelinek } 360*0b5de56dSgjelinek 361*0b5de56dSgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 362*0b5de56dSgjelinek errno = err; 363*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 364*0b5de56dSgjelinek zonecfg_fini_handle(handle); 365*0b5de56dSgjelinek return (Z_ERR); 366*0b5de56dSgjelinek } 367*0b5de56dSgjelinek 368*0b5de56dSgjelinek if ((athandle = zonecfg_init_handle()) == NULL) { 369*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 370*0b5de56dSgjelinek goto done; 371*0b5de56dSgjelinek } 372*0b5de56dSgjelinek 373*0b5de56dSgjelinek if ((err = zonecfg_get_attach_handle(snap_path, target_zone, B_TRUE, 374*0b5de56dSgjelinek athandle)) != Z_OK) { 375*0b5de56dSgjelinek if (err == Z_NO_ZONE) 376*0b5de56dSgjelinek (void) fprintf(stderr, gettext("snapshot %s was not " 377*0b5de56dSgjelinek "taken\n\tby a 'zoneadm clone' command. It can " 378*0b5de56dSgjelinek "not be used to clone zones.\n"), snapshot_name); 379*0b5de56dSgjelinek else 380*0b5de56dSgjelinek (void) fprintf(stderr, gettext("snapshot %s is " 381*0b5de56dSgjelinek "out-dated\n\tIt can no longer be used to clone " 382*0b5de56dSgjelinek "zones on this system.\n"), snapshot_name); 383*0b5de56dSgjelinek goto done; 384*0b5de56dSgjelinek } 385*0b5de56dSgjelinek 386*0b5de56dSgjelinek /* Get the detach information for the locally defined zone. */ 387*0b5de56dSgjelinek if ((err = zonecfg_get_detach_info(handle, B_FALSE)) != Z_OK) { 388*0b5de56dSgjelinek errno = err; 389*0b5de56dSgjelinek zperror(gettext("getting the attach information failed"), 390*0b5de56dSgjelinek B_TRUE); 391*0b5de56dSgjelinek goto done; 392*0b5de56dSgjelinek } 393*0b5de56dSgjelinek 394*0b5de56dSgjelinek if ((err = sw_cmp(handle, athandle, SW_CMP_SILENT)) != Z_OK) 395*0b5de56dSgjelinek (void) fprintf(stderr, gettext("snapshot %s is out-dated\n\t" 396*0b5de56dSgjelinek "It can no longer be used to clone zones on this " 397*0b5de56dSgjelinek "system.\n"), snapshot_name); 398*0b5de56dSgjelinek 399*0b5de56dSgjelinek done: 400*0b5de56dSgjelinek zonecfg_fini_handle(handle); 401*0b5de56dSgjelinek if (athandle != NULL) 402*0b5de56dSgjelinek zonecfg_fini_handle(athandle); 403*0b5de56dSgjelinek 404*0b5de56dSgjelinek return (err); 405*0b5de56dSgjelinek } 406*0b5de56dSgjelinek 407*0b5de56dSgjelinek /* 408*0b5de56dSgjelinek * Remove the sw inventory file from inside this zonepath that we picked up out 409*0b5de56dSgjelinek * of the snapshot. 410*0b5de56dSgjelinek */ 411*0b5de56dSgjelinek static int 412*0b5de56dSgjelinek clean_out_clone() 413*0b5de56dSgjelinek { 414*0b5de56dSgjelinek int err; 415*0b5de56dSgjelinek zone_dochandle_t handle; 416*0b5de56dSgjelinek 417*0b5de56dSgjelinek if ((handle = zonecfg_init_handle()) == NULL) { 418*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 419*0b5de56dSgjelinek return (Z_ERR); 420*0b5de56dSgjelinek } 421*0b5de56dSgjelinek 422*0b5de56dSgjelinek if ((err = zonecfg_get_handle(target_zone, handle)) != Z_OK) { 423*0b5de56dSgjelinek errno = err; 424*0b5de56dSgjelinek zperror(cmd_to_str(CMD_CLONE), B_TRUE); 425*0b5de56dSgjelinek zonecfg_fini_handle(handle); 426*0b5de56dSgjelinek return (Z_ERR); 427*0b5de56dSgjelinek } 428*0b5de56dSgjelinek 429*0b5de56dSgjelinek zonecfg_rm_detached(handle, B_FALSE); 430*0b5de56dSgjelinek zonecfg_fini_handle(handle); 431*0b5de56dSgjelinek 432*0b5de56dSgjelinek return (Z_OK); 433*0b5de56dSgjelinek } 434*0b5de56dSgjelinek 435*0b5de56dSgjelinek /* 436*0b5de56dSgjelinek * Make a ZFS clone on zonepath from snapshot_name. 437*0b5de56dSgjelinek */ 438*0b5de56dSgjelinek static int 439*0b5de56dSgjelinek clone_snap(char *snapshot_name, char *zonepath) 440*0b5de56dSgjelinek { 441*0b5de56dSgjelinek int res = Z_OK; 442*0b5de56dSgjelinek int err; 443*0b5de56dSgjelinek zfs_handle_t *zhp; 444*0b5de56dSgjelinek zfs_handle_t *clone; 445*0b5de56dSgjelinek 446*0b5de56dSgjelinek if ((zhp = zfs_open(snapshot_name, ZFS_TYPE_SNAPSHOT)) == NULL) 447*0b5de56dSgjelinek return (Z_NO_ENTRY); 448*0b5de56dSgjelinek 449*0b5de56dSgjelinek (void) printf(gettext("Cloning snapshot %s\n"), snapshot_name); 450*0b5de56dSgjelinek 451*0b5de56dSgjelinek err = zfs_clone(zhp, zonepath); 452*0b5de56dSgjelinek zfs_close(zhp); 453*0b5de56dSgjelinek if (err != 0) 454*0b5de56dSgjelinek return (Z_ERR); 455*0b5de56dSgjelinek 456*0b5de56dSgjelinek /* create the mountpoint if necessary */ 457*0b5de56dSgjelinek if ((clone = zfs_open(zonepath, ZFS_TYPE_ANY)) == NULL) 458*0b5de56dSgjelinek return (Z_ERR); 459*0b5de56dSgjelinek 460*0b5de56dSgjelinek /* 461*0b5de56dSgjelinek * The clone has been created so we need to print a diagnostic 462*0b5de56dSgjelinek * message if one of the following steps fails for some reason. 463*0b5de56dSgjelinek */ 464*0b5de56dSgjelinek if (zfs_mount(clone, NULL, 0) != 0) { 465*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not mount ZFS clone " 466*0b5de56dSgjelinek "%s\n"), zfs_get_name(clone)); 467*0b5de56dSgjelinek res = Z_ERR; 468*0b5de56dSgjelinek 469*0b5de56dSgjelinek } else { 470*0b5de56dSgjelinek if (zfs_prop_set(clone, ZFS_PROP_SHARENFS, "off") != 0) { 471*0b5de56dSgjelinek /* we won't consider this a failure */ 472*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not turn off the " 473*0b5de56dSgjelinek "'sharenfs' property on ZFS clone %s\n"), 474*0b5de56dSgjelinek zfs_get_name(clone)); 475*0b5de56dSgjelinek } 476*0b5de56dSgjelinek 477*0b5de56dSgjelinek if (clean_out_clone() != Z_OK) { 478*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not remove the " 479*0b5de56dSgjelinek "software inventory from ZFS clone %s\n"), 480*0b5de56dSgjelinek zfs_get_name(clone)); 481*0b5de56dSgjelinek res = Z_ERR; 482*0b5de56dSgjelinek } 483*0b5de56dSgjelinek } 484*0b5de56dSgjelinek 485*0b5de56dSgjelinek zfs_close(clone); 486*0b5de56dSgjelinek return (res); 487*0b5de56dSgjelinek } 488*0b5de56dSgjelinek 489*0b5de56dSgjelinek /* 490*0b5de56dSgjelinek * This function takes a zonepath and attempts to determine what the ZFS 491*0b5de56dSgjelinek * file system name (not mountpoint) should be for that path. We do not 492*0b5de56dSgjelinek * assume that zonepath is an existing directory or ZFS fs since we use 493*0b5de56dSgjelinek * this function as part of the process of creating a new ZFS fs or clone. 494*0b5de56dSgjelinek * 495*0b5de56dSgjelinek * The way this works is that we look at the parent directory of the zonepath 496*0b5de56dSgjelinek * to see if it is a ZFS fs. If it is, we get the name of that ZFS fs and 497*0b5de56dSgjelinek * append the last component of the zonepath to generate the ZFS name for the 498*0b5de56dSgjelinek * zonepath. This matches the algorithm that ZFS uses for automatically 499*0b5de56dSgjelinek * mounting a new fs after it is created. 500*0b5de56dSgjelinek * 501*0b5de56dSgjelinek * Although a ZFS fs can be mounted anywhere, we don't worry about handling 502*0b5de56dSgjelinek * all of the complexity that a user could possibly configure with arbitrary 503*0b5de56dSgjelinek * mounts since there is no way to generate a ZFS name from a random path in 504*0b5de56dSgjelinek * the file system. We only try to handle the automatic mounts that ZFS does 505*0b5de56dSgjelinek * for each file system. ZFS restricts this so that a new fs must be created 506*0b5de56dSgjelinek * in an existing parent ZFS fs. It then automatically mounts the new fs 507*0b5de56dSgjelinek * directly under the mountpoint for the parent fs using the last component 508*0b5de56dSgjelinek * of the name as the mountpoint directory. 509*0b5de56dSgjelinek * 510*0b5de56dSgjelinek * For example: 511*0b5de56dSgjelinek * Name Mountpoint 512*0b5de56dSgjelinek * space/eng/dev/test/zone1 /project1/eng/dev/test/zone1 513*0b5de56dSgjelinek * 514*0b5de56dSgjelinek * Return Z_OK if the path mapped to a ZFS file system name, otherwise return 515*0b5de56dSgjelinek * Z_ERR. 516*0b5de56dSgjelinek */ 517*0b5de56dSgjelinek static int 518*0b5de56dSgjelinek path2name(char *zonepath, char *zfs_name, int len) 519*0b5de56dSgjelinek { 520*0b5de56dSgjelinek int res; 521*0b5de56dSgjelinek char *p; 522*0b5de56dSgjelinek zfs_handle_t *zhp; 523*0b5de56dSgjelinek 524*0b5de56dSgjelinek if ((p = strrchr(zonepath, '/')) == NULL) 525*0b5de56dSgjelinek return (Z_ERR); 526*0b5de56dSgjelinek 527*0b5de56dSgjelinek /* 528*0b5de56dSgjelinek * If the parent directory is not its own ZFS fs, then we can't 529*0b5de56dSgjelinek * automatically create a new ZFS fs at the 'zonepath' mountpoint 530*0b5de56dSgjelinek * so return an error. 531*0b5de56dSgjelinek */ 532*0b5de56dSgjelinek *p = '\0'; 533*0b5de56dSgjelinek zhp = mount2zhandle(zonepath); 534*0b5de56dSgjelinek *p = '/'; 535*0b5de56dSgjelinek if (zhp == NULL) 536*0b5de56dSgjelinek return (Z_ERR); 537*0b5de56dSgjelinek 538*0b5de56dSgjelinek res = snprintf(zfs_name, len, "%s/%s", zfs_get_name(zhp), p + 1); 539*0b5de56dSgjelinek 540*0b5de56dSgjelinek zfs_close(zhp); 541*0b5de56dSgjelinek if (res >= len) 542*0b5de56dSgjelinek return (Z_ERR); 543*0b5de56dSgjelinek 544*0b5de56dSgjelinek return (Z_OK); 545*0b5de56dSgjelinek } 546*0b5de56dSgjelinek 547*0b5de56dSgjelinek /* 548*0b5de56dSgjelinek * A ZFS file system iterator call-back function used to determine if the 549*0b5de56dSgjelinek * file system has dependents (snapshots & clones). 550*0b5de56dSgjelinek */ 551*0b5de56dSgjelinek /* ARGSUSED */ 552*0b5de56dSgjelinek static int 553*0b5de56dSgjelinek has_dependent(zfs_handle_t *zhp, void *data) 554*0b5de56dSgjelinek { 555*0b5de56dSgjelinek zfs_close(zhp); 556*0b5de56dSgjelinek return (1); 557*0b5de56dSgjelinek } 558*0b5de56dSgjelinek 559*0b5de56dSgjelinek /* 560*0b5de56dSgjelinek * Given a snapshot name, get the file system path where the snapshot lives. 561*0b5de56dSgjelinek * A snapshot name is of the form fs_name@snap_name. For example, snapshot 562*0b5de56dSgjelinek * pl/zones/z1@SUNWzone1 would have a path of 563*0b5de56dSgjelinek * /pl/zones/z1/.zfs/snapshot/SUNWzone1. 564*0b5de56dSgjelinek */ 565*0b5de56dSgjelinek static int 566*0b5de56dSgjelinek snap2path(char *snap_name, char *path, int len) 567*0b5de56dSgjelinek { 568*0b5de56dSgjelinek char *p; 569*0b5de56dSgjelinek zfs_handle_t *zhp; 570*0b5de56dSgjelinek char mp[ZFS_MAXPROPLEN]; 571*0b5de56dSgjelinek 572*0b5de56dSgjelinek if ((p = strrchr(snap_name, '@')) == NULL) 573*0b5de56dSgjelinek return (Z_ERR); 574*0b5de56dSgjelinek 575*0b5de56dSgjelinek /* Get the file system name from the snap_name. */ 576*0b5de56dSgjelinek *p = '\0'; 577*0b5de56dSgjelinek zhp = zfs_open(snap_name, ZFS_TYPE_ANY); 578*0b5de56dSgjelinek *p = '@'; 579*0b5de56dSgjelinek if (zhp == NULL) 580*0b5de56dSgjelinek return (Z_ERR); 581*0b5de56dSgjelinek 582*0b5de56dSgjelinek /* Get the file system mount point. */ 583*0b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mp, sizeof (mp), NULL, NULL, 584*0b5de56dSgjelinek 0, FALSE) != 0) { 585*0b5de56dSgjelinek zfs_close(zhp); 586*0b5de56dSgjelinek return (Z_ERR); 587*0b5de56dSgjelinek } 588*0b5de56dSgjelinek zfs_close(zhp); 589*0b5de56dSgjelinek 590*0b5de56dSgjelinek p++; 591*0b5de56dSgjelinek if (snprintf(path, len, "%s/.zfs/snapshot/%s", mp, p) >= len) 592*0b5de56dSgjelinek return (Z_ERR); 593*0b5de56dSgjelinek 594*0b5de56dSgjelinek return (Z_OK); 595*0b5de56dSgjelinek } 596*0b5de56dSgjelinek 597*0b5de56dSgjelinek /* 598*0b5de56dSgjelinek * Clone a pre-existing ZFS snapshot, either by making a direct ZFS clone, if 599*0b5de56dSgjelinek * possible, or by copying the data from the snapshot to the zonepath. 600*0b5de56dSgjelinek */ 601*0b5de56dSgjelinek int 602*0b5de56dSgjelinek clone_snapshot_zfs(char *snap_name, char *zonepath) 603*0b5de56dSgjelinek { 604*0b5de56dSgjelinek int err = Z_OK; 605*0b5de56dSgjelinek char clone_name[MAXPATHLEN]; 606*0b5de56dSgjelinek char snap_path[MAXPATHLEN]; 607*0b5de56dSgjelinek 608*0b5de56dSgjelinek if (snap2path(snap_name, snap_path, sizeof (snap_path)) != Z_OK) { 609*0b5de56dSgjelinek (void) fprintf(stderr, gettext("unable to find path for %s.\n"), 610*0b5de56dSgjelinek snap_name); 611*0b5de56dSgjelinek return (Z_ERR); 612*0b5de56dSgjelinek } 613*0b5de56dSgjelinek 614*0b5de56dSgjelinek if (validate_snapshot(snap_name, snap_path) != Z_OK) 615*0b5de56dSgjelinek return (Z_NO_ENTRY); 616*0b5de56dSgjelinek 617*0b5de56dSgjelinek /* 618*0b5de56dSgjelinek * The zonepath cannot be ZFS cloned, try to copy the data from 619*0b5de56dSgjelinek * within the snapshot to the zonepath. 620*0b5de56dSgjelinek */ 621*0b5de56dSgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) { 622*0b5de56dSgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK) 623*0b5de56dSgjelinek if (clean_out_clone() != Z_OK) 624*0b5de56dSgjelinek (void) fprintf(stderr, 625*0b5de56dSgjelinek gettext("could not remove the " 626*0b5de56dSgjelinek "software inventory from %s\n"), zonepath); 627*0b5de56dSgjelinek 628*0b5de56dSgjelinek return (err); 629*0b5de56dSgjelinek } 630*0b5de56dSgjelinek 631*0b5de56dSgjelinek if ((err = clone_snap(snap_name, clone_name)) != Z_OK) { 632*0b5de56dSgjelinek if (err != Z_NO_ENTRY) { 633*0b5de56dSgjelinek /* 634*0b5de56dSgjelinek * Cloning the snapshot failed. Fall back to trying 635*0b5de56dSgjelinek * to install the zone by copying from the snapshot. 636*0b5de56dSgjelinek */ 637*0b5de56dSgjelinek if ((err = clone_copy(snap_path, zonepath)) == Z_OK) 638*0b5de56dSgjelinek if (clean_out_clone() != Z_OK) 639*0b5de56dSgjelinek (void) fprintf(stderr, 640*0b5de56dSgjelinek gettext("could not remove the " 641*0b5de56dSgjelinek "software inventory from %s\n"), 642*0b5de56dSgjelinek zonepath); 643*0b5de56dSgjelinek } else { 644*0b5de56dSgjelinek /* 645*0b5de56dSgjelinek * The snapshot is unusable for some reason so restore 646*0b5de56dSgjelinek * the zone state to configured since we were unable to 647*0b5de56dSgjelinek * actually do anything about getting the zone 648*0b5de56dSgjelinek * installed. 649*0b5de56dSgjelinek */ 650*0b5de56dSgjelinek int tmp; 651*0b5de56dSgjelinek 652*0b5de56dSgjelinek if ((tmp = zone_set_state(target_zone, 653*0b5de56dSgjelinek ZONE_STATE_CONFIGURED)) != Z_OK) { 654*0b5de56dSgjelinek errno = tmp; 655*0b5de56dSgjelinek zperror2(target_zone, 656*0b5de56dSgjelinek gettext("could not set state")); 657*0b5de56dSgjelinek } 658*0b5de56dSgjelinek } 659*0b5de56dSgjelinek } 660*0b5de56dSgjelinek 661*0b5de56dSgjelinek return (err); 662*0b5de56dSgjelinek } 663*0b5de56dSgjelinek 664*0b5de56dSgjelinek /* 665*0b5de56dSgjelinek * Attempt to clone a source_zone to a target zonepath by using a ZFS clone. 666*0b5de56dSgjelinek */ 667*0b5de56dSgjelinek int 668*0b5de56dSgjelinek clone_zfs(char *source_zone, char *source_zonepath, char *zonepath) 669*0b5de56dSgjelinek { 670*0b5de56dSgjelinek zfs_handle_t *zhp; 671*0b5de56dSgjelinek char clone_name[MAXPATHLEN]; 672*0b5de56dSgjelinek char snap_name[MAXPATHLEN]; 673*0b5de56dSgjelinek 674*0b5de56dSgjelinek /* 675*0b5de56dSgjelinek * Try to get a zfs handle for the source_zonepath. If this fails 676*0b5de56dSgjelinek * the source_zonepath is not ZFS so return an error. 677*0b5de56dSgjelinek */ 678*0b5de56dSgjelinek if ((zhp = mount2zhandle(source_zonepath)) == NULL) 679*0b5de56dSgjelinek return (Z_ERR); 680*0b5de56dSgjelinek 681*0b5de56dSgjelinek /* 682*0b5de56dSgjelinek * Check if there is a file system already mounted on zonepath. If so, 683*0b5de56dSgjelinek * we can't clone to the path so we should fall back to copying. 684*0b5de56dSgjelinek */ 685*0b5de56dSgjelinek if (is_mountpnt(zonepath)) { 686*0b5de56dSgjelinek zfs_close(zhp); 687*0b5de56dSgjelinek (void) fprintf(stderr, 688*0b5de56dSgjelinek gettext("A file system is already mounted on %s,\n" 689*0b5de56dSgjelinek "preventing use of a ZFS clone.\n"), zonepath); 690*0b5de56dSgjelinek return (Z_ERR); 691*0b5de56dSgjelinek } 692*0b5de56dSgjelinek 693*0b5de56dSgjelinek /* 694*0b5de56dSgjelinek * Instead of using path2name to get the clone name from the zonepath, 695*0b5de56dSgjelinek * we could generate a name from the source zone ZFS name. However, 696*0b5de56dSgjelinek * this would mean we would create the clone under the ZFS fs of the 697*0b5de56dSgjelinek * source instead of what the zonepath says. For example, 698*0b5de56dSgjelinek * 699*0b5de56dSgjelinek * source_zonepath zonepath 700*0b5de56dSgjelinek * /pl/zones/dev/z1 /pl/zones/deploy/z2 701*0b5de56dSgjelinek * 702*0b5de56dSgjelinek * We don't want the clone to be under "dev", we want it under 703*0b5de56dSgjelinek * "deploy", so that we can leverage the normal attribute inheritance 704*0b5de56dSgjelinek * that ZFS provides in the fs hierarchy. 705*0b5de56dSgjelinek */ 706*0b5de56dSgjelinek if (path2name(zonepath, clone_name, sizeof (clone_name)) != Z_OK) { 707*0b5de56dSgjelinek zfs_close(zhp); 708*0b5de56dSgjelinek return (Z_ERR); 709*0b5de56dSgjelinek } 710*0b5de56dSgjelinek 711*0b5de56dSgjelinek if (take_snapshot(source_zone, zhp, snap_name, sizeof (snap_name)) 712*0b5de56dSgjelinek != Z_OK) { 713*0b5de56dSgjelinek zfs_close(zhp); 714*0b5de56dSgjelinek return (Z_ERR); 715*0b5de56dSgjelinek } 716*0b5de56dSgjelinek zfs_close(zhp); 717*0b5de56dSgjelinek 718*0b5de56dSgjelinek if (clone_snap(snap_name, clone_name) != Z_OK) 719*0b5de56dSgjelinek return (Z_ERR); 720*0b5de56dSgjelinek 721*0b5de56dSgjelinek (void) printf(gettext("Instead of copying, a ZFS clone has been " 722*0b5de56dSgjelinek "created for this zone.\n")); 723*0b5de56dSgjelinek 724*0b5de56dSgjelinek return (Z_OK); 725*0b5de56dSgjelinek } 726*0b5de56dSgjelinek 727*0b5de56dSgjelinek /* 728*0b5de56dSgjelinek * Attempt to create a ZFS file system for the specified zonepath. 729*0b5de56dSgjelinek * We either will successfully create a ZFS file system and get it mounted 730*0b5de56dSgjelinek * on the zonepath or we don't. The caller doesn't care since a regular 731*0b5de56dSgjelinek * directory is used for the zonepath if no ZFS file system is mounted there. 732*0b5de56dSgjelinek */ 733*0b5de56dSgjelinek void 734*0b5de56dSgjelinek create_zfs_zonepath(char *zonepath) 735*0b5de56dSgjelinek { 736*0b5de56dSgjelinek zfs_handle_t *zhp; 737*0b5de56dSgjelinek char zfs_name[MAXPATHLEN]; 738*0b5de56dSgjelinek 739*0b5de56dSgjelinek if (path2name(zonepath, zfs_name, sizeof (zfs_name)) != Z_OK) 740*0b5de56dSgjelinek return; 741*0b5de56dSgjelinek 742*0b5de56dSgjelinek zfs_set_error_handler(noop_err_handler); 743*0b5de56dSgjelinek 744*0b5de56dSgjelinek if (zfs_create(zfs_name, ZFS_TYPE_FILESYSTEM, NULL, NULL) != 0 || 745*0b5de56dSgjelinek (zhp = zfs_open(zfs_name, ZFS_TYPE_ANY)) == NULL) { 746*0b5de56dSgjelinek zfs_set_error_handler(NULL); 747*0b5de56dSgjelinek return; 748*0b5de56dSgjelinek } 749*0b5de56dSgjelinek 750*0b5de56dSgjelinek if (zfs_mount(zhp, NULL, 0) != 0) { 751*0b5de56dSgjelinek (void) zfs_destroy(zhp); 752*0b5de56dSgjelinek } else if (zfs_prop_set(zhp, ZFS_PROP_SHARENFS, "off") != 0) { 753*0b5de56dSgjelinek (void) fprintf(stderr, gettext("file system %s successfully " 754*0b5de56dSgjelinek "created,\nbut could not turn off the 'sharenfs' " 755*0b5de56dSgjelinek "property\n"), zfs_name); 756*0b5de56dSgjelinek } else { 757*0b5de56dSgjelinek if (chmod(zonepath, S_IRWXU) != 0) { 758*0b5de56dSgjelinek (void) fprintf(stderr, gettext("file system %s " 759*0b5de56dSgjelinek "successfully created, but chmod %o failed: %s\n"), 760*0b5de56dSgjelinek zfs_name, S_IRWXU, strerror(errno)); 761*0b5de56dSgjelinek (void) destroy_zfs(zonepath); 762*0b5de56dSgjelinek } else { 763*0b5de56dSgjelinek (void) printf(gettext("A ZFS file system has been " 764*0b5de56dSgjelinek "created for this zone.\n")); 765*0b5de56dSgjelinek } 766*0b5de56dSgjelinek } 767*0b5de56dSgjelinek 768*0b5de56dSgjelinek zfs_set_error_handler(NULL); 769*0b5de56dSgjelinek zfs_close(zhp); 770*0b5de56dSgjelinek } 771*0b5de56dSgjelinek 772*0b5de56dSgjelinek /* 773*0b5de56dSgjelinek * If the zonepath is a ZFS file system, attempt to destroy it. We return Z_OK 774*0b5de56dSgjelinek * if we were able to zfs_destroy the zonepath, otherwise we return Z_ERR 775*0b5de56dSgjelinek * which means the caller should clean up the zonepath in the traditional 776*0b5de56dSgjelinek * way. 777*0b5de56dSgjelinek */ 778*0b5de56dSgjelinek int 779*0b5de56dSgjelinek destroy_zfs(char *zonepath) 780*0b5de56dSgjelinek { 781*0b5de56dSgjelinek zfs_handle_t *zhp; 782*0b5de56dSgjelinek boolean_t is_clone = B_FALSE; 783*0b5de56dSgjelinek char origin[ZFS_MAXPROPLEN]; 784*0b5de56dSgjelinek 785*0b5de56dSgjelinek zfs_set_error_handler(noop_err_handler); 786*0b5de56dSgjelinek 787*0b5de56dSgjelinek if ((zhp = mount2zhandle(zonepath)) == NULL) { 788*0b5de56dSgjelinek zfs_set_error_handler(NULL); 789*0b5de56dSgjelinek return (Z_ERR); 790*0b5de56dSgjelinek } 791*0b5de56dSgjelinek 792*0b5de56dSgjelinek /* 793*0b5de56dSgjelinek * We can't destroy the file system if it has dependents. 794*0b5de56dSgjelinek */ 795*0b5de56dSgjelinek if (zfs_iter_dependents(zhp, has_dependent, NULL) != 0 || 796*0b5de56dSgjelinek zfs_unmount(zhp, NULL, 0) != 0) { 797*0b5de56dSgjelinek zfs_close(zhp); 798*0b5de56dSgjelinek zfs_set_error_handler(NULL); 799*0b5de56dSgjelinek return (Z_ERR); 800*0b5de56dSgjelinek } 801*0b5de56dSgjelinek 802*0b5de56dSgjelinek /* 803*0b5de56dSgjelinek * This might be a clone. Try to get the snapshot so we can attempt 804*0b5de56dSgjelinek * to destroy that as well. 805*0b5de56dSgjelinek */ 806*0b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, 807*0b5de56dSgjelinek NULL, 0, FALSE) == 0) 808*0b5de56dSgjelinek is_clone = B_TRUE; 809*0b5de56dSgjelinek 810*0b5de56dSgjelinek zfs_set_error_handler(NULL); 811*0b5de56dSgjelinek if (zfs_destroy(zhp) != 0) { 812*0b5de56dSgjelinek /* 813*0b5de56dSgjelinek * If the destroy fails for some reason, try to remount 814*0b5de56dSgjelinek * the file system so that we can use "rm -rf" to clean up 815*0b5de56dSgjelinek * instead. 816*0b5de56dSgjelinek */ 817*0b5de56dSgjelinek (void) zfs_mount(zhp, NULL, 0); 818*0b5de56dSgjelinek zfs_close(zhp); 819*0b5de56dSgjelinek return (Z_ERR); 820*0b5de56dSgjelinek } 821*0b5de56dSgjelinek zfs_set_error_handler(noop_err_handler); 822*0b5de56dSgjelinek 823*0b5de56dSgjelinek (void) printf(gettext("The ZFS file system for this zone has been " 824*0b5de56dSgjelinek "destroyed.\n")); 825*0b5de56dSgjelinek 826*0b5de56dSgjelinek if (is_clone) { 827*0b5de56dSgjelinek zfs_handle_t *ohp; 828*0b5de56dSgjelinek 829*0b5de56dSgjelinek /* 830*0b5de56dSgjelinek * Try to clean up the snapshot that the clone was taken from. 831*0b5de56dSgjelinek */ 832*0b5de56dSgjelinek if ((ohp = zfs_open(origin, ZFS_TYPE_SNAPSHOT)) != NULL) { 833*0b5de56dSgjelinek if (zfs_iter_dependents(ohp, has_dependent, NULL) 834*0b5de56dSgjelinek == 0 && zfs_unmount(ohp, NULL, 0) == 0) { 835*0b5de56dSgjelinek (void) zfs_destroy(ohp); 836*0b5de56dSgjelinek } 837*0b5de56dSgjelinek zfs_close(ohp); 838*0b5de56dSgjelinek } 839*0b5de56dSgjelinek } 840*0b5de56dSgjelinek 841*0b5de56dSgjelinek zfs_close(zhp); 842*0b5de56dSgjelinek zfs_set_error_handler(NULL); 843*0b5de56dSgjelinek return (Z_OK); 844*0b5de56dSgjelinek } 845*0b5de56dSgjelinek 846*0b5de56dSgjelinek /* 847*0b5de56dSgjelinek * Return true if the path is its own zfs file system. We determine this 848*0b5de56dSgjelinek * by stat-ing the path to see if it is zfs and stat-ing the parent to see 849*0b5de56dSgjelinek * if it is a different fs. 850*0b5de56dSgjelinek */ 851*0b5de56dSgjelinek boolean_t 852*0b5de56dSgjelinek is_zonepath_zfs(char *zonepath) 853*0b5de56dSgjelinek { 854*0b5de56dSgjelinek int res; 855*0b5de56dSgjelinek char *path; 856*0b5de56dSgjelinek char *parent; 857*0b5de56dSgjelinek struct statvfs buf1, buf2; 858*0b5de56dSgjelinek 859*0b5de56dSgjelinek if (statvfs(zonepath, &buf1) != 0) 860*0b5de56dSgjelinek return (B_FALSE); 861*0b5de56dSgjelinek 862*0b5de56dSgjelinek if (strcmp(buf1.f_basetype, "zfs") != 0) 863*0b5de56dSgjelinek return (B_FALSE); 864*0b5de56dSgjelinek 865*0b5de56dSgjelinek if ((path = strdup(zonepath)) == NULL) 866*0b5de56dSgjelinek return (B_FALSE); 867*0b5de56dSgjelinek 868*0b5de56dSgjelinek parent = dirname(path); 869*0b5de56dSgjelinek res = statvfs(parent, &buf2); 870*0b5de56dSgjelinek free(path); 871*0b5de56dSgjelinek 872*0b5de56dSgjelinek if (res != 0) 873*0b5de56dSgjelinek return (B_FALSE); 874*0b5de56dSgjelinek 875*0b5de56dSgjelinek if (buf1.f_fsid == buf2.f_fsid) 876*0b5de56dSgjelinek return (B_FALSE); 877*0b5de56dSgjelinek 878*0b5de56dSgjelinek return (B_TRUE); 879*0b5de56dSgjelinek } 880*0b5de56dSgjelinek 881*0b5de56dSgjelinek /* 882*0b5de56dSgjelinek * Implement the fast move of a ZFS file system by simply updating the 883*0b5de56dSgjelinek * mountpoint. Since it is file system already, we don't have the 884*0b5de56dSgjelinek * issue of cross-file system copying. 885*0b5de56dSgjelinek */ 886*0b5de56dSgjelinek int 887*0b5de56dSgjelinek move_zfs(char *zonepath, char *new_zonepath) 888*0b5de56dSgjelinek { 889*0b5de56dSgjelinek int ret = Z_ERR; 890*0b5de56dSgjelinek zfs_handle_t *zhp; 891*0b5de56dSgjelinek 892*0b5de56dSgjelinek zfs_set_error_handler(noop_err_handler); 893*0b5de56dSgjelinek 894*0b5de56dSgjelinek if ((zhp = mount2zhandle(zonepath)) == NULL) { 895*0b5de56dSgjelinek zfs_set_error_handler(NULL); 896*0b5de56dSgjelinek return (Z_ERR); 897*0b5de56dSgjelinek } 898*0b5de56dSgjelinek 899*0b5de56dSgjelinek if (zfs_prop_set(zhp, ZFS_PROP_MOUNTPOINT, new_zonepath) == 0) { 900*0b5de56dSgjelinek /* 901*0b5de56dSgjelinek * Clean up the old mount point. We ignore any failure since 902*0b5de56dSgjelinek * the zone is already successfully mounted on the new path. 903*0b5de56dSgjelinek */ 904*0b5de56dSgjelinek (void) rmdir(zonepath); 905*0b5de56dSgjelinek ret = Z_OK; 906*0b5de56dSgjelinek } 907*0b5de56dSgjelinek 908*0b5de56dSgjelinek zfs_close(zhp); 909*0b5de56dSgjelinek zfs_set_error_handler(NULL); 910*0b5de56dSgjelinek 911*0b5de56dSgjelinek return (ret); 912*0b5de56dSgjelinek } 913*0b5de56dSgjelinek 914*0b5de56dSgjelinek /* 915*0b5de56dSgjelinek * Validate that the given dataset exists on the system, and that neither it nor 916*0b5de56dSgjelinek * its children are zvols. 917*0b5de56dSgjelinek * 918*0b5de56dSgjelinek * Note that we don't do anything with the 'zoned' property here. All 919*0b5de56dSgjelinek * management is done in zoneadmd when the zone is actually rebooted. This 920*0b5de56dSgjelinek * allows us to automatically set the zoned property even when a zone is 921*0b5de56dSgjelinek * rebooted by the administrator. 922*0b5de56dSgjelinek */ 923*0b5de56dSgjelinek int 924*0b5de56dSgjelinek verify_datasets(zone_dochandle_t handle) 925*0b5de56dSgjelinek { 926*0b5de56dSgjelinek int return_code = Z_OK; 927*0b5de56dSgjelinek struct zone_dstab dstab; 928*0b5de56dSgjelinek zfs_handle_t *zhp; 929*0b5de56dSgjelinek char propbuf[ZFS_MAXPROPLEN]; 930*0b5de56dSgjelinek char source[ZFS_MAXNAMELEN]; 931*0b5de56dSgjelinek zfs_source_t srctype; 932*0b5de56dSgjelinek 933*0b5de56dSgjelinek if (zonecfg_setdsent(handle) != Z_OK) { 934*0b5de56dSgjelinek /* 935*0b5de56dSgjelinek * TRANSLATION_NOTE 936*0b5de56dSgjelinek * zfs and dataset are literals that should not be translated. 937*0b5de56dSgjelinek */ 938*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs datasets: " 939*0b5de56dSgjelinek "unable to enumerate datasets\n")); 940*0b5de56dSgjelinek return (Z_ERR); 941*0b5de56dSgjelinek } 942*0b5de56dSgjelinek 943*0b5de56dSgjelinek zfs_set_error_handler(err_handler); 944*0b5de56dSgjelinek 945*0b5de56dSgjelinek while (zonecfg_getdsent(handle, &dstab) == Z_OK) { 946*0b5de56dSgjelinek 947*0b5de56dSgjelinek current_dataset = dstab.zone_dataset_name; 948*0b5de56dSgjelinek 949*0b5de56dSgjelinek if ((zhp = zfs_open(dstab.zone_dataset_name, 950*0b5de56dSgjelinek ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME)) == NULL) { 951*0b5de56dSgjelinek return_code = Z_ERR; 952*0b5de56dSgjelinek continue; 953*0b5de56dSgjelinek } 954*0b5de56dSgjelinek 955*0b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, 956*0b5de56dSgjelinek sizeof (propbuf), &srctype, source, 957*0b5de56dSgjelinek sizeof (source), 0) == 0 && 958*0b5de56dSgjelinek (srctype == ZFS_SRC_INHERITED)) { 959*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify zfs " 960*0b5de56dSgjelinek "dataset %s: mountpoint cannot be inherited\n"), 961*0b5de56dSgjelinek dstab.zone_dataset_name); 962*0b5de56dSgjelinek return_code = Z_ERR; 963*0b5de56dSgjelinek zfs_close(zhp); 964*0b5de56dSgjelinek continue; 965*0b5de56dSgjelinek } 966*0b5de56dSgjelinek 967*0b5de56dSgjelinek if (zfs_get_type(zhp) == ZFS_TYPE_VOLUME) { 968*0b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify zfs " 969*0b5de56dSgjelinek "dataset %s: volumes cannot be specified as a " 970*0b5de56dSgjelinek "zone dataset resource\n"), 971*0b5de56dSgjelinek dstab.zone_dataset_name); 972*0b5de56dSgjelinek return_code = Z_ERR; 973*0b5de56dSgjelinek } 974*0b5de56dSgjelinek 975*0b5de56dSgjelinek if (zfs_iter_children(zhp, check_zvol, NULL) != 0) 976*0b5de56dSgjelinek return_code = Z_ERR; 977*0b5de56dSgjelinek 978*0b5de56dSgjelinek zfs_close(zhp); 979*0b5de56dSgjelinek } 980*0b5de56dSgjelinek (void) zonecfg_enddsent(handle); 981*0b5de56dSgjelinek zfs_set_error_handler(NULL); 982*0b5de56dSgjelinek 983*0b5de56dSgjelinek return (return_code); 984*0b5de56dSgjelinek } 985*0b5de56dSgjelinek 986*0b5de56dSgjelinek /* 987*0b5de56dSgjelinek * Verify that the ZFS dataset exists, and its mountpoint 988*0b5de56dSgjelinek * property is set to "legacy". 989*0b5de56dSgjelinek */ 990*0b5de56dSgjelinek int 991*0b5de56dSgjelinek verify_fs_zfs(struct zone_fstab *fstab) 992*0b5de56dSgjelinek { 993*0b5de56dSgjelinek zfs_handle_t *zhp; 994*0b5de56dSgjelinek char propbuf[ZFS_MAXPROPLEN]; 995*0b5de56dSgjelinek 996*0b5de56dSgjelinek zfs_set_error_handler(noop_err_handler); 997*0b5de56dSgjelinek 998*0b5de56dSgjelinek if ((zhp = zfs_open(fstab->zone_fs_special, ZFS_TYPE_ANY)) == NULL) { 999*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: " 1000*0b5de56dSgjelinek "could not access zfs dataset '%s'\n"), 1001*0b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 1002*0b5de56dSgjelinek zfs_set_error_handler(NULL); 1003*0b5de56dSgjelinek return (Z_ERR); 1004*0b5de56dSgjelinek } 1005*0b5de56dSgjelinek 1006*0b5de56dSgjelinek if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 1007*0b5de56dSgjelinek (void) fprintf(stderr, gettext("cannot verify fs %s: " 1008*0b5de56dSgjelinek "'%s' is not a file system\n"), 1009*0b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 1010*0b5de56dSgjelinek zfs_close(zhp); 1011*0b5de56dSgjelinek zfs_set_error_handler(NULL); 1012*0b5de56dSgjelinek return (Z_ERR); 1013*0b5de56dSgjelinek } 1014*0b5de56dSgjelinek 1015*0b5de56dSgjelinek if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, propbuf, sizeof (propbuf), 1016*0b5de56dSgjelinek NULL, NULL, 0, 0) != 0 || strcmp(propbuf, "legacy") != 0) { 1017*0b5de56dSgjelinek (void) fprintf(stderr, gettext("could not verify fs %s: " 1018*0b5de56dSgjelinek "zfs '%s' mountpoint is not \"legacy\"\n"), 1019*0b5de56dSgjelinek fstab->zone_fs_dir, fstab->zone_fs_special); 1020*0b5de56dSgjelinek zfs_close(zhp); 1021*0b5de56dSgjelinek zfs_set_error_handler(NULL); 1022*0b5de56dSgjelinek return (Z_ERR); 1023*0b5de56dSgjelinek } 1024*0b5de56dSgjelinek 1025*0b5de56dSgjelinek zfs_close(zhp); 1026*0b5de56dSgjelinek zfs_set_error_handler(NULL); 1027*0b5de56dSgjelinek return (Z_OK); 1028*0b5de56dSgjelinek } 1029