1*13013Sglenn.lagasse@oracle.com /* 2*13013Sglenn.lagasse@oracle.com * CDDL HEADER START 3*13013Sglenn.lagasse@oracle.com * 4*13013Sglenn.lagasse@oracle.com * The contents of this file are subject to the terms of the 5*13013Sglenn.lagasse@oracle.com * Common Development and Distribution License (the "License"). 6*13013Sglenn.lagasse@oracle.com * You may not use this file except in compliance with the License. 7*13013Sglenn.lagasse@oracle.com * 8*13013Sglenn.lagasse@oracle.com * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*13013Sglenn.lagasse@oracle.com * or http://www.opensolaris.org/os/licensing. 10*13013Sglenn.lagasse@oracle.com * See the License for the specific language governing permissions 11*13013Sglenn.lagasse@oracle.com * and limitations under the License. 12*13013Sglenn.lagasse@oracle.com * 13*13013Sglenn.lagasse@oracle.com * When distributing Covered Code, include this CDDL HEADER in each 14*13013Sglenn.lagasse@oracle.com * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*13013Sglenn.lagasse@oracle.com * If applicable, add the following below this CDDL HEADER, with the 16*13013Sglenn.lagasse@oracle.com * fields enclosed by brackets "[]" replaced with your own identifying 17*13013Sglenn.lagasse@oracle.com * information: Portions Copyright [yyyy] [name of copyright owner] 18*13013Sglenn.lagasse@oracle.com * 19*13013Sglenn.lagasse@oracle.com * CDDL HEADER END 20*13013Sglenn.lagasse@oracle.com */ 21*13013Sglenn.lagasse@oracle.com 22*13013Sglenn.lagasse@oracle.com /* 23*13013Sglenn.lagasse@oracle.com * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24*13013Sglenn.lagasse@oracle.com */ 25*13013Sglenn.lagasse@oracle.com 26*13013Sglenn.lagasse@oracle.com /* 27*13013Sglenn.lagasse@oracle.com * System includes 28*13013Sglenn.lagasse@oracle.com */ 29*13013Sglenn.lagasse@oracle.com 30*13013Sglenn.lagasse@oracle.com #include <assert.h> 31*13013Sglenn.lagasse@oracle.com #include <ctype.h> 32*13013Sglenn.lagasse@oracle.com #include <errno.h> 33*13013Sglenn.lagasse@oracle.com #include <libgen.h> 34*13013Sglenn.lagasse@oracle.com #include <libintl.h> 35*13013Sglenn.lagasse@oracle.com #include <libnvpair.h> 36*13013Sglenn.lagasse@oracle.com #include <libzfs.h> 37*13013Sglenn.lagasse@oracle.com #include <stdio.h> 38*13013Sglenn.lagasse@oracle.com #include <stdlib.h> 39*13013Sglenn.lagasse@oracle.com #include <string.h> 40*13013Sglenn.lagasse@oracle.com #include <sys/mnttab.h> 41*13013Sglenn.lagasse@oracle.com #include <sys/mount.h> 42*13013Sglenn.lagasse@oracle.com #include <sys/stat.h> 43*13013Sglenn.lagasse@oracle.com #include <sys/types.h> 44*13013Sglenn.lagasse@oracle.com #include <sys/wait.h> 45*13013Sglenn.lagasse@oracle.com #include <unistd.h> 46*13013Sglenn.lagasse@oracle.com 47*13013Sglenn.lagasse@oracle.com #include <libbe.h> 48*13013Sglenn.lagasse@oracle.com #include <libbe_priv.h> 49*13013Sglenn.lagasse@oracle.com 50*13013Sglenn.lagasse@oracle.com /* Library wide variables */ 51*13013Sglenn.lagasse@oracle.com libzfs_handle_t *g_zfs = NULL; 52*13013Sglenn.lagasse@oracle.com 53*13013Sglenn.lagasse@oracle.com /* Private function prototypes */ 54*13013Sglenn.lagasse@oracle.com static int _be_destroy(const char *, be_destroy_data_t *); 55*13013Sglenn.lagasse@oracle.com static int be_destroy_zones(char *, char *, be_destroy_data_t *); 56*13013Sglenn.lagasse@oracle.com static int be_destroy_zone_roots(char *, be_destroy_data_t *); 57*13013Sglenn.lagasse@oracle.com static int be_destroy_zone_roots_callback(zfs_handle_t *, void *); 58*13013Sglenn.lagasse@oracle.com static int be_copy_zones(char *, char *, char *); 59*13013Sglenn.lagasse@oracle.com static int be_clone_fs_callback(zfs_handle_t *, void *); 60*13013Sglenn.lagasse@oracle.com static int be_destroy_callback(zfs_handle_t *, void *); 61*13013Sglenn.lagasse@oracle.com static int be_send_fs_callback(zfs_handle_t *, void *); 62*13013Sglenn.lagasse@oracle.com static int be_demote_callback(zfs_handle_t *, void *); 63*13013Sglenn.lagasse@oracle.com static int be_demote_find_clone_callback(zfs_handle_t *, void *); 64*13013Sglenn.lagasse@oracle.com static int be_demote_get_one_clone(zfs_handle_t *, void *); 65*13013Sglenn.lagasse@oracle.com static int be_get_snap(char *, char **); 66*13013Sglenn.lagasse@oracle.com static int be_prep_clone_send_fs(zfs_handle_t *, be_transaction_data_t *, 67*13013Sglenn.lagasse@oracle.com char *, int); 68*13013Sglenn.lagasse@oracle.com static boolean_t be_create_container_ds(char *); 69*13013Sglenn.lagasse@oracle.com static char *be_get_zone_be_name(char *root_ds, char *container_ds); 70*13013Sglenn.lagasse@oracle.com static int be_zone_root_exists_callback(zfs_handle_t *, void *); 71*13013Sglenn.lagasse@oracle.com 72*13013Sglenn.lagasse@oracle.com /* ******************************************************************** */ 73*13013Sglenn.lagasse@oracle.com /* Public Functions */ 74*13013Sglenn.lagasse@oracle.com /* ******************************************************************** */ 75*13013Sglenn.lagasse@oracle.com 76*13013Sglenn.lagasse@oracle.com /* 77*13013Sglenn.lagasse@oracle.com * Function: be_init 78*13013Sglenn.lagasse@oracle.com * Description: Creates the initial datasets for a BE and leaves them 79*13013Sglenn.lagasse@oracle.com * unpopulated. The resultant BE can be mounted but can't 80*13013Sglenn.lagasse@oracle.com * yet be activated or booted. 81*13013Sglenn.lagasse@oracle.com * Parameters: 82*13013Sglenn.lagasse@oracle.com * be_attrs - pointer to nvlist_t of attributes being passed in. 83*13013Sglenn.lagasse@oracle.com * The following attributes are used by this function: 84*13013Sglenn.lagasse@oracle.com * 85*13013Sglenn.lagasse@oracle.com * BE_ATTR_NEW_BE_NAME *required 86*13013Sglenn.lagasse@oracle.com * BE_ATTR_NEW_BE_POOL *required 87*13013Sglenn.lagasse@oracle.com * BE_ATTR_ZFS_PROPERTIES *optional 88*13013Sglenn.lagasse@oracle.com * BE_ATTR_FS_NAMES *optional 89*13013Sglenn.lagasse@oracle.com * BE_ATTR_FS_NUM *optional 90*13013Sglenn.lagasse@oracle.com * BE_ATTR_SHARED_FS_NAMES *optional 91*13013Sglenn.lagasse@oracle.com * BE_ATTR_SHARED_FS_NUM *optional 92*13013Sglenn.lagasse@oracle.com * Return: 93*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 94*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 95*13013Sglenn.lagasse@oracle.com * Scope: 96*13013Sglenn.lagasse@oracle.com * Public 97*13013Sglenn.lagasse@oracle.com */ 98*13013Sglenn.lagasse@oracle.com int 99*13013Sglenn.lagasse@oracle.com be_init(nvlist_t *be_attrs) 100*13013Sglenn.lagasse@oracle.com { 101*13013Sglenn.lagasse@oracle.com be_transaction_data_t bt = { 0 }; 102*13013Sglenn.lagasse@oracle.com zpool_handle_t *zlp; 103*13013Sglenn.lagasse@oracle.com nvlist_t *zfs_props = NULL; 104*13013Sglenn.lagasse@oracle.com char nbe_root_ds[MAXPATHLEN]; 105*13013Sglenn.lagasse@oracle.com char child_fs[MAXPATHLEN]; 106*13013Sglenn.lagasse@oracle.com char **fs_names = NULL; 107*13013Sglenn.lagasse@oracle.com char **shared_fs_names = NULL; 108*13013Sglenn.lagasse@oracle.com uint16_t fs_num = 0; 109*13013Sglenn.lagasse@oracle.com uint16_t shared_fs_num = 0; 110*13013Sglenn.lagasse@oracle.com int nelem; 111*13013Sglenn.lagasse@oracle.com int i; 112*13013Sglenn.lagasse@oracle.com int zret = 0, ret = BE_SUCCESS; 113*13013Sglenn.lagasse@oracle.com 114*13013Sglenn.lagasse@oracle.com /* Initialize libzfs handle */ 115*13013Sglenn.lagasse@oracle.com if (!be_zfs_init()) 116*13013Sglenn.lagasse@oracle.com return (BE_ERR_INIT); 117*13013Sglenn.lagasse@oracle.com 118*13013Sglenn.lagasse@oracle.com /* Get new BE name */ 119*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name) 120*13013Sglenn.lagasse@oracle.com != 0) { 121*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to lookup " 122*13013Sglenn.lagasse@oracle.com "BE_ATTR_NEW_BE_NAME attribute\n")); 123*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 124*13013Sglenn.lagasse@oracle.com } 125*13013Sglenn.lagasse@oracle.com 126*13013Sglenn.lagasse@oracle.com /* Validate new BE name */ 127*13013Sglenn.lagasse@oracle.com if (!be_valid_be_name(bt.nbe_name)) { 128*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: invalid BE name %s\n"), 129*13013Sglenn.lagasse@oracle.com bt.nbe_name); 130*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 131*13013Sglenn.lagasse@oracle.com } 132*13013Sglenn.lagasse@oracle.com 133*13013Sglenn.lagasse@oracle.com /* Get zpool name */ 134*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_POOL, &bt.nbe_zpool) 135*13013Sglenn.lagasse@oracle.com != 0) { 136*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to lookup " 137*13013Sglenn.lagasse@oracle.com "BE_ATTR_NEW_BE_POOL attribute\n")); 138*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 139*13013Sglenn.lagasse@oracle.com } 140*13013Sglenn.lagasse@oracle.com 141*13013Sglenn.lagasse@oracle.com /* Get file system attributes */ 142*13013Sglenn.lagasse@oracle.com nelem = 0; 143*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, 0, 144*13013Sglenn.lagasse@oracle.com BE_ATTR_FS_NUM, DATA_TYPE_UINT16, &fs_num, 145*13013Sglenn.lagasse@oracle.com BE_ATTR_FS_NAMES, DATA_TYPE_STRING_ARRAY, &fs_names, &nelem, 146*13013Sglenn.lagasse@oracle.com NULL) != 0) { 147*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to lookup fs " 148*13013Sglenn.lagasse@oracle.com "attributes\n")); 149*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 150*13013Sglenn.lagasse@oracle.com } 151*13013Sglenn.lagasse@oracle.com if (nelem != fs_num) { 152*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: size of FS_NAMES array (%d) " 153*13013Sglenn.lagasse@oracle.com "does not match FS_NUM (%d)\n"), nelem, fs_num); 154*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 155*13013Sglenn.lagasse@oracle.com } 156*13013Sglenn.lagasse@oracle.com 157*13013Sglenn.lagasse@oracle.com /* Get shared file system attributes */ 158*13013Sglenn.lagasse@oracle.com nelem = 0; 159*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 160*13013Sglenn.lagasse@oracle.com BE_ATTR_SHARED_FS_NUM, DATA_TYPE_UINT16, &shared_fs_num, 161*13013Sglenn.lagasse@oracle.com BE_ATTR_SHARED_FS_NAMES, DATA_TYPE_STRING_ARRAY, &shared_fs_names, 162*13013Sglenn.lagasse@oracle.com &nelem, NULL) != 0) { 163*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to lookup " 164*13013Sglenn.lagasse@oracle.com "shared fs attributes\n")); 165*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 166*13013Sglenn.lagasse@oracle.com } 167*13013Sglenn.lagasse@oracle.com if (nelem != shared_fs_num) { 168*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: size of SHARED_FS_NAMES " 169*13013Sglenn.lagasse@oracle.com "array does not match SHARED_FS_NUM\n")); 170*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 171*13013Sglenn.lagasse@oracle.com } 172*13013Sglenn.lagasse@oracle.com 173*13013Sglenn.lagasse@oracle.com /* Verify that nbe_zpool exists */ 174*13013Sglenn.lagasse@oracle.com if ((zlp = zpool_open(g_zfs, bt.nbe_zpool)) == NULL) { 175*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to " 176*13013Sglenn.lagasse@oracle.com "find existing zpool (%s): %s\n"), bt.nbe_zpool, 177*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 178*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 179*13013Sglenn.lagasse@oracle.com } 180*13013Sglenn.lagasse@oracle.com zpool_close(zlp); 181*13013Sglenn.lagasse@oracle.com 182*13013Sglenn.lagasse@oracle.com /* 183*13013Sglenn.lagasse@oracle.com * Verify BE container dataset in nbe_zpool exists. 184*13013Sglenn.lagasse@oracle.com * If not, create it. 185*13013Sglenn.lagasse@oracle.com */ 186*13013Sglenn.lagasse@oracle.com if (!be_create_container_ds(bt.nbe_zpool)) 187*13013Sglenn.lagasse@oracle.com return (BE_ERR_CREATDS); 188*13013Sglenn.lagasse@oracle.com 189*13013Sglenn.lagasse@oracle.com /* 190*13013Sglenn.lagasse@oracle.com * Verify that nbe_name doesn't already exist in some pool. 191*13013Sglenn.lagasse@oracle.com */ 192*13013Sglenn.lagasse@oracle.com if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) > 0) { 193*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: BE (%s) already exists\n"), 194*13013Sglenn.lagasse@oracle.com bt.nbe_name); 195*13013Sglenn.lagasse@oracle.com return (BE_ERR_BE_EXISTS); 196*13013Sglenn.lagasse@oracle.com } else if (zret < 0) { 197*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: zpool_iter failed: %s\n"), 198*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 199*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 200*13013Sglenn.lagasse@oracle.com } 201*13013Sglenn.lagasse@oracle.com 202*13013Sglenn.lagasse@oracle.com /* Generate string for BE's root dataset */ 203*13013Sglenn.lagasse@oracle.com be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds, 204*13013Sglenn.lagasse@oracle.com sizeof (nbe_root_ds)); 205*13013Sglenn.lagasse@oracle.com 206*13013Sglenn.lagasse@oracle.com /* 207*13013Sglenn.lagasse@oracle.com * Create property list for new BE root dataset. If some 208*13013Sglenn.lagasse@oracle.com * zfs properties were already provided by the caller, dup 209*13013Sglenn.lagasse@oracle.com * that list. Otherwise initialize a new property list. 210*13013Sglenn.lagasse@oracle.com */ 211*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 212*13013Sglenn.lagasse@oracle.com BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL) 213*13013Sglenn.lagasse@oracle.com != 0) { 214*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to lookup " 215*13013Sglenn.lagasse@oracle.com "BE_ATTR_ZFS_PROPERTIES attribute\n")); 216*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 217*13013Sglenn.lagasse@oracle.com } 218*13013Sglenn.lagasse@oracle.com if (zfs_props != NULL) { 219*13013Sglenn.lagasse@oracle.com /* Make sure its a unique nvlist */ 220*13013Sglenn.lagasse@oracle.com if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) && 221*13013Sglenn.lagasse@oracle.com !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) { 222*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: ZFS property list " 223*13013Sglenn.lagasse@oracle.com "not unique\n")); 224*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 225*13013Sglenn.lagasse@oracle.com } 226*13013Sglenn.lagasse@oracle.com 227*13013Sglenn.lagasse@oracle.com /* Dup the list */ 228*13013Sglenn.lagasse@oracle.com if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) { 229*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to dup ZFS " 230*13013Sglenn.lagasse@oracle.com "property list\n")); 231*13013Sglenn.lagasse@oracle.com return (BE_ERR_NOMEM); 232*13013Sglenn.lagasse@oracle.com } 233*13013Sglenn.lagasse@oracle.com } else { 234*13013Sglenn.lagasse@oracle.com /* Initialize new nvlist */ 235*13013Sglenn.lagasse@oracle.com if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { 236*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: internal " 237*13013Sglenn.lagasse@oracle.com "error: out of memory\n")); 238*13013Sglenn.lagasse@oracle.com return (BE_ERR_NOMEM); 239*13013Sglenn.lagasse@oracle.com } 240*13013Sglenn.lagasse@oracle.com } 241*13013Sglenn.lagasse@oracle.com 242*13013Sglenn.lagasse@oracle.com /* Set the mountpoint property for the root dataset */ 243*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(bt.nbe_zfs_props, 244*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), "/") != 0) { 245*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: internal error " 246*13013Sglenn.lagasse@oracle.com "out of memory\n")); 247*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NOMEM; 248*13013Sglenn.lagasse@oracle.com goto done; 249*13013Sglenn.lagasse@oracle.com } 250*13013Sglenn.lagasse@oracle.com 251*13013Sglenn.lagasse@oracle.com /* Set the 'canmount' property */ 252*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(bt.nbe_zfs_props, 253*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) { 254*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: internal error " 255*13013Sglenn.lagasse@oracle.com "out of memory\n")); 256*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NOMEM; 257*13013Sglenn.lagasse@oracle.com goto done; 258*13013Sglenn.lagasse@oracle.com } 259*13013Sglenn.lagasse@oracle.com 260*13013Sglenn.lagasse@oracle.com /* Create BE root dataset for the new BE */ 261*13013Sglenn.lagasse@oracle.com if (zfs_create(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM, 262*13013Sglenn.lagasse@oracle.com bt.nbe_zfs_props) != 0) { 263*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to " 264*13013Sglenn.lagasse@oracle.com "create BE root dataset (%s): %s\n"), nbe_root_ds, 265*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 266*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 267*13013Sglenn.lagasse@oracle.com goto done; 268*13013Sglenn.lagasse@oracle.com } 269*13013Sglenn.lagasse@oracle.com 270*13013Sglenn.lagasse@oracle.com /* Set UUID for new BE */ 271*13013Sglenn.lagasse@oracle.com if ((ret = be_set_uuid(nbe_root_ds)) != BE_SUCCESS) { 272*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to " 273*13013Sglenn.lagasse@oracle.com "set uuid for new BE\n")); 274*13013Sglenn.lagasse@oracle.com } 275*13013Sglenn.lagasse@oracle.com 276*13013Sglenn.lagasse@oracle.com /* 277*13013Sglenn.lagasse@oracle.com * Clear the mountpoint property so that the non-shared 278*13013Sglenn.lagasse@oracle.com * file systems created below inherit their mountpoints. 279*13013Sglenn.lagasse@oracle.com */ 280*13013Sglenn.lagasse@oracle.com (void) nvlist_remove(bt.nbe_zfs_props, 281*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), DATA_TYPE_STRING); 282*13013Sglenn.lagasse@oracle.com 283*13013Sglenn.lagasse@oracle.com /* Create the new BE's non-shared file systems */ 284*13013Sglenn.lagasse@oracle.com for (i = 0; i < fs_num && fs_names[i]; i++) { 285*13013Sglenn.lagasse@oracle.com /* 286*13013Sglenn.lagasse@oracle.com * If fs == "/", skip it; 287*13013Sglenn.lagasse@oracle.com * we already created the root dataset 288*13013Sglenn.lagasse@oracle.com */ 289*13013Sglenn.lagasse@oracle.com if (strcmp(fs_names[i], "/") == 0) 290*13013Sglenn.lagasse@oracle.com continue; 291*13013Sglenn.lagasse@oracle.com 292*13013Sglenn.lagasse@oracle.com /* Generate string for file system */ 293*13013Sglenn.lagasse@oracle.com (void) snprintf(child_fs, sizeof (child_fs), "%s%s", 294*13013Sglenn.lagasse@oracle.com nbe_root_ds, fs_names[i]); 295*13013Sglenn.lagasse@oracle.com 296*13013Sglenn.lagasse@oracle.com /* Create file system */ 297*13013Sglenn.lagasse@oracle.com if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM, 298*13013Sglenn.lagasse@oracle.com bt.nbe_zfs_props) != 0) { 299*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to create " 300*13013Sglenn.lagasse@oracle.com "BE's child dataset (%s): %s\n"), child_fs, 301*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 302*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 303*13013Sglenn.lagasse@oracle.com goto done; 304*13013Sglenn.lagasse@oracle.com } 305*13013Sglenn.lagasse@oracle.com } 306*13013Sglenn.lagasse@oracle.com 307*13013Sglenn.lagasse@oracle.com /* Create the new BE's shared file systems */ 308*13013Sglenn.lagasse@oracle.com if (shared_fs_num > 0) { 309*13013Sglenn.lagasse@oracle.com nvlist_t *props = NULL; 310*13013Sglenn.lagasse@oracle.com 311*13013Sglenn.lagasse@oracle.com if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 312*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: nvlist_alloc failed\n")); 313*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NOMEM; 314*13013Sglenn.lagasse@oracle.com goto done; 315*13013Sglenn.lagasse@oracle.com } 316*13013Sglenn.lagasse@oracle.com 317*13013Sglenn.lagasse@oracle.com for (i = 0; i < shared_fs_num; i++) { 318*13013Sglenn.lagasse@oracle.com /* Generate string for shared file system */ 319*13013Sglenn.lagasse@oracle.com (void) snprintf(child_fs, sizeof (child_fs), "%s%s", 320*13013Sglenn.lagasse@oracle.com bt.nbe_zpool, shared_fs_names[i]); 321*13013Sglenn.lagasse@oracle.com 322*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(props, 323*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 324*13013Sglenn.lagasse@oracle.com shared_fs_names[i]) != 0) { 325*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: " 326*13013Sglenn.lagasse@oracle.com "internal error: out of memory\n")); 327*13013Sglenn.lagasse@oracle.com nvlist_free(props); 328*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NOMEM; 329*13013Sglenn.lagasse@oracle.com goto done; 330*13013Sglenn.lagasse@oracle.com } 331*13013Sglenn.lagasse@oracle.com 332*13013Sglenn.lagasse@oracle.com /* Create file system if it doesn't already exist */ 333*13013Sglenn.lagasse@oracle.com if (zfs_dataset_exists(g_zfs, child_fs, 334*13013Sglenn.lagasse@oracle.com ZFS_TYPE_FILESYSTEM)) { 335*13013Sglenn.lagasse@oracle.com continue; 336*13013Sglenn.lagasse@oracle.com } 337*13013Sglenn.lagasse@oracle.com if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM, 338*13013Sglenn.lagasse@oracle.com props) != 0) { 339*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_init: failed to " 340*13013Sglenn.lagasse@oracle.com "create BE's shared dataset (%s): %s\n"), 341*13013Sglenn.lagasse@oracle.com child_fs, libzfs_error_description(g_zfs)); 342*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 343*13013Sglenn.lagasse@oracle.com nvlist_free(props); 344*13013Sglenn.lagasse@oracle.com goto done; 345*13013Sglenn.lagasse@oracle.com } 346*13013Sglenn.lagasse@oracle.com } 347*13013Sglenn.lagasse@oracle.com 348*13013Sglenn.lagasse@oracle.com nvlist_free(props); 349*13013Sglenn.lagasse@oracle.com } 350*13013Sglenn.lagasse@oracle.com 351*13013Sglenn.lagasse@oracle.com done: 352*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 353*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 354*13013Sglenn.lagasse@oracle.com 355*13013Sglenn.lagasse@oracle.com be_zfs_fini(); 356*13013Sglenn.lagasse@oracle.com 357*13013Sglenn.lagasse@oracle.com return (ret); 358*13013Sglenn.lagasse@oracle.com } 359*13013Sglenn.lagasse@oracle.com 360*13013Sglenn.lagasse@oracle.com /* 361*13013Sglenn.lagasse@oracle.com * Function: be_destroy 362*13013Sglenn.lagasse@oracle.com * Description: Destroy a BE and all of its children datasets, snapshots and 363*13013Sglenn.lagasse@oracle.com * zones that belong to the parent BE. 364*13013Sglenn.lagasse@oracle.com * Parameters: 365*13013Sglenn.lagasse@oracle.com * be_attrs - pointer to nvlist_t of attributes being passed in. 366*13013Sglenn.lagasse@oracle.com * The following attributes are used by this function: 367*13013Sglenn.lagasse@oracle.com * 368*13013Sglenn.lagasse@oracle.com * BE_ATTR_ORIG_BE_NAME *required 369*13013Sglenn.lagasse@oracle.com * BE_ATTR_DESTROY_FLAGS *optional 370*13013Sglenn.lagasse@oracle.com * Return: 371*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 372*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 373*13013Sglenn.lagasse@oracle.com * Scope: 374*13013Sglenn.lagasse@oracle.com * Public 375*13013Sglenn.lagasse@oracle.com */ 376*13013Sglenn.lagasse@oracle.com int 377*13013Sglenn.lagasse@oracle.com be_destroy(nvlist_t *be_attrs) 378*13013Sglenn.lagasse@oracle.com { 379*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp = NULL; 380*13013Sglenn.lagasse@oracle.com be_transaction_data_t bt = { 0 }; 381*13013Sglenn.lagasse@oracle.com be_transaction_data_t cur_bt = { 0 }; 382*13013Sglenn.lagasse@oracle.com be_destroy_data_t dd = { 0 }; 383*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 384*13013Sglenn.lagasse@oracle.com uint16_t flags = 0; 385*13013Sglenn.lagasse@oracle.com int zret; 386*13013Sglenn.lagasse@oracle.com char obe_root_ds[MAXPATHLEN]; 387*13013Sglenn.lagasse@oracle.com char *mp = NULL; 388*13013Sglenn.lagasse@oracle.com 389*13013Sglenn.lagasse@oracle.com /* Initialize libzfs handle */ 390*13013Sglenn.lagasse@oracle.com if (!be_zfs_init()) 391*13013Sglenn.lagasse@oracle.com return (BE_ERR_INIT); 392*13013Sglenn.lagasse@oracle.com 393*13013Sglenn.lagasse@oracle.com /* Get name of BE to delete */ 394*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name) 395*13013Sglenn.lagasse@oracle.com != 0) { 396*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to lookup " 397*13013Sglenn.lagasse@oracle.com "BE_ATTR_ORIG_BE_NAME attribute\n")); 398*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 399*13013Sglenn.lagasse@oracle.com } 400*13013Sglenn.lagasse@oracle.com 401*13013Sglenn.lagasse@oracle.com /* 402*13013Sglenn.lagasse@oracle.com * Validate BE name. If valid, then check that the original BE is not 403*13013Sglenn.lagasse@oracle.com * the active BE. If it is the 'active' BE then return an error code 404*13013Sglenn.lagasse@oracle.com * since we can't destroy the active BE. 405*13013Sglenn.lagasse@oracle.com */ 406*13013Sglenn.lagasse@oracle.com if (!be_valid_be_name(bt.obe_name)) { 407*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: invalid BE name %s\n"), 408*13013Sglenn.lagasse@oracle.com bt.obe_name); 409*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 410*13013Sglenn.lagasse@oracle.com } else if (bt.obe_name != NULL) { 411*13013Sglenn.lagasse@oracle.com if ((ret = be_find_current_be(&cur_bt)) != BE_SUCCESS) { 412*13013Sglenn.lagasse@oracle.com return (ret); 413*13013Sglenn.lagasse@oracle.com } 414*13013Sglenn.lagasse@oracle.com if (strcmp(cur_bt.obe_name, bt.obe_name) == 0) { 415*13013Sglenn.lagasse@oracle.com return (BE_ERR_DESTROY_CURR_BE); 416*13013Sglenn.lagasse@oracle.com } 417*13013Sglenn.lagasse@oracle.com } 418*13013Sglenn.lagasse@oracle.com 419*13013Sglenn.lagasse@oracle.com /* Get destroy flags if provided */ 420*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 421*13013Sglenn.lagasse@oracle.com BE_ATTR_DESTROY_FLAGS, DATA_TYPE_UINT16, &flags, NULL) 422*13013Sglenn.lagasse@oracle.com != 0) { 423*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to lookup " 424*13013Sglenn.lagasse@oracle.com "BE_ATTR_DESTROY_FLAGS attribute\n")); 425*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 426*13013Sglenn.lagasse@oracle.com } 427*13013Sglenn.lagasse@oracle.com 428*13013Sglenn.lagasse@oracle.com dd.destroy_snaps = flags & BE_DESTROY_FLAG_SNAPSHOTS; 429*13013Sglenn.lagasse@oracle.com dd.force_unmount = flags & BE_DESTROY_FLAG_FORCE_UNMOUNT; 430*13013Sglenn.lagasse@oracle.com 431*13013Sglenn.lagasse@oracle.com /* Find which zpool obe_name lives in */ 432*13013Sglenn.lagasse@oracle.com if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 433*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to find zpool " 434*13013Sglenn.lagasse@oracle.com "for BE (%s)\n"), bt.obe_name); 435*13013Sglenn.lagasse@oracle.com return (BE_ERR_BE_NOENT); 436*13013Sglenn.lagasse@oracle.com } else if (zret < 0) { 437*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: zpool_iter failed: %s\n"), 438*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 439*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 440*13013Sglenn.lagasse@oracle.com } 441*13013Sglenn.lagasse@oracle.com 442*13013Sglenn.lagasse@oracle.com /* Generate string for obe_name's root dataset */ 443*13013Sglenn.lagasse@oracle.com be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 444*13013Sglenn.lagasse@oracle.com sizeof (obe_root_ds)); 445*13013Sglenn.lagasse@oracle.com bt.obe_root_ds = obe_root_ds; 446*13013Sglenn.lagasse@oracle.com 447*13013Sglenn.lagasse@oracle.com /* 448*13013Sglenn.lagasse@oracle.com * Detect if the BE to destroy has the 'active on boot' property set. 449*13013Sglenn.lagasse@oracle.com * If so, set the 'active on boot' property on the the 'active' BE. 450*13013Sglenn.lagasse@oracle.com */ 451*13013Sglenn.lagasse@oracle.com if (be_is_active_on_boot(bt.obe_name)) { 452*13013Sglenn.lagasse@oracle.com if ((ret = be_activate_current_be()) != BE_SUCCESS) { 453*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 454*13013Sglenn.lagasse@oracle.com "make the current BE 'active on boot'\n")); 455*13013Sglenn.lagasse@oracle.com return (ret); 456*13013Sglenn.lagasse@oracle.com } 457*13013Sglenn.lagasse@oracle.com } 458*13013Sglenn.lagasse@oracle.com 459*13013Sglenn.lagasse@oracle.com /* Get handle to BE's root dataset */ 460*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 461*13013Sglenn.lagasse@oracle.com NULL) { 462*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 463*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 464*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 465*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 466*13013Sglenn.lagasse@oracle.com } 467*13013Sglenn.lagasse@oracle.com 468*13013Sglenn.lagasse@oracle.com /* Get the UUID of the global BE */ 469*13013Sglenn.lagasse@oracle.com if (be_get_uuid(zfs_get_name(zhp), &dd.gz_be_uuid) != BE_SUCCESS) { 470*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: BE has no UUID (%s)\n"), 471*13013Sglenn.lagasse@oracle.com zfs_get_name(zhp)); 472*13013Sglenn.lagasse@oracle.com } 473*13013Sglenn.lagasse@oracle.com 474*13013Sglenn.lagasse@oracle.com /* 475*13013Sglenn.lagasse@oracle.com * If the global BE is mounted, make sure we've been given the 476*13013Sglenn.lagasse@oracle.com * flag to forcibly unmount it. 477*13013Sglenn.lagasse@oracle.com */ 478*13013Sglenn.lagasse@oracle.com if (zfs_is_mounted(zhp, &mp)) { 479*13013Sglenn.lagasse@oracle.com if (!(dd.force_unmount)) { 480*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: " 481*13013Sglenn.lagasse@oracle.com "%s is currently mounted at %s, cannot destroy\n"), 482*13013Sglenn.lagasse@oracle.com bt.obe_name, mp != NULL ? mp : "<unknown>"); 483*13013Sglenn.lagasse@oracle.com 484*13013Sglenn.lagasse@oracle.com free(mp); 485*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 486*13013Sglenn.lagasse@oracle.com return (BE_ERR_MOUNTED); 487*13013Sglenn.lagasse@oracle.com } 488*13013Sglenn.lagasse@oracle.com free(mp); 489*13013Sglenn.lagasse@oracle.com } 490*13013Sglenn.lagasse@oracle.com 491*13013Sglenn.lagasse@oracle.com /* 492*13013Sglenn.lagasse@oracle.com * Destroy the non-global zone BE's if we are in the global zone 493*13013Sglenn.lagasse@oracle.com * and there is a UUID associated with the global zone BE 494*13013Sglenn.lagasse@oracle.com */ 495*13013Sglenn.lagasse@oracle.com if (getzoneid() == GLOBAL_ZONEID && !uuid_is_null(dd.gz_be_uuid)) { 496*13013Sglenn.lagasse@oracle.com if ((ret = be_destroy_zones(bt.obe_name, bt.obe_root_ds, &dd)) 497*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 498*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 499*13013Sglenn.lagasse@oracle.com "destroy one or more zones for BE %s\n"), 500*13013Sglenn.lagasse@oracle.com bt.obe_name); 501*13013Sglenn.lagasse@oracle.com goto done; 502*13013Sglenn.lagasse@oracle.com } 503*13013Sglenn.lagasse@oracle.com } 504*13013Sglenn.lagasse@oracle.com 505*13013Sglenn.lagasse@oracle.com /* Unmount the BE if it was mounted */ 506*13013Sglenn.lagasse@oracle.com if (zfs_is_mounted(zhp, NULL)) { 507*13013Sglenn.lagasse@oracle.com if ((ret = _be_unmount(bt.obe_name, BE_UNMOUNT_FLAG_FORCE)) 508*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 509*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: " 510*13013Sglenn.lagasse@oracle.com "failed to unmount %s\n"), bt.obe_name); 511*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 512*13013Sglenn.lagasse@oracle.com return (ret); 513*13013Sglenn.lagasse@oracle.com } 514*13013Sglenn.lagasse@oracle.com } 515*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 516*13013Sglenn.lagasse@oracle.com 517*13013Sglenn.lagasse@oracle.com /* Destroy this BE */ 518*13013Sglenn.lagasse@oracle.com if ((ret = _be_destroy((const char *)bt.obe_root_ds, &dd)) 519*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 520*13013Sglenn.lagasse@oracle.com goto done; 521*13013Sglenn.lagasse@oracle.com } 522*13013Sglenn.lagasse@oracle.com 523*13013Sglenn.lagasse@oracle.com /* Remove BE's entry from the boot menu */ 524*13013Sglenn.lagasse@oracle.com if (getzoneid() == GLOBAL_ZONEID) { 525*13013Sglenn.lagasse@oracle.com if ((ret = be_remove_menu(bt.obe_name, bt.obe_zpool, NULL)) 526*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 527*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 528*13013Sglenn.lagasse@oracle.com "remove BE %s from the boot menu\n"), 529*13013Sglenn.lagasse@oracle.com bt.obe_root_ds); 530*13013Sglenn.lagasse@oracle.com goto done; 531*13013Sglenn.lagasse@oracle.com } 532*13013Sglenn.lagasse@oracle.com } 533*13013Sglenn.lagasse@oracle.com 534*13013Sglenn.lagasse@oracle.com done: 535*13013Sglenn.lagasse@oracle.com be_zfs_fini(); 536*13013Sglenn.lagasse@oracle.com 537*13013Sglenn.lagasse@oracle.com return (ret); 538*13013Sglenn.lagasse@oracle.com } 539*13013Sglenn.lagasse@oracle.com 540*13013Sglenn.lagasse@oracle.com /* 541*13013Sglenn.lagasse@oracle.com * Function: be_copy 542*13013Sglenn.lagasse@oracle.com * Description: This function makes a copy of an existing BE. If the original 543*13013Sglenn.lagasse@oracle.com * BE and the new BE are in the same pool, it uses zfs cloning to 544*13013Sglenn.lagasse@oracle.com * create the new BE, otherwise it does a physical copy. 545*13013Sglenn.lagasse@oracle.com * If the original BE name isn't provided, it uses the currently 546*13013Sglenn.lagasse@oracle.com * booted BE. If the new BE name isn't provided, it creates an 547*13013Sglenn.lagasse@oracle.com * auto named BE and returns that name to the caller. 548*13013Sglenn.lagasse@oracle.com * Parameters: 549*13013Sglenn.lagasse@oracle.com * be_attrs - pointer to nvlist_t of attributes being passed in. 550*13013Sglenn.lagasse@oracle.com * The following attributes are used by this function: 551*13013Sglenn.lagasse@oracle.com * 552*13013Sglenn.lagasse@oracle.com * BE_ATTR_ORIG_BE_NAME *optional 553*13013Sglenn.lagasse@oracle.com * BE_ATTR_SNAP_NAME *optional 554*13013Sglenn.lagasse@oracle.com * BE_ATTR_NEW_BE_NAME *optional 555*13013Sglenn.lagasse@oracle.com * BE_ATTR_NEW_BE_POOL *optional 556*13013Sglenn.lagasse@oracle.com * BE_ATTR_NEW_BE_DESC *optional 557*13013Sglenn.lagasse@oracle.com * BE_ATTR_ZFS_PROPERTIES *optional 558*13013Sglenn.lagasse@oracle.com * BE_ATTR_POLICY *optional 559*13013Sglenn.lagasse@oracle.com * 560*13013Sglenn.lagasse@oracle.com * If the BE_ATTR_NEW_BE_NAME was not passed in, upon 561*13013Sglenn.lagasse@oracle.com * successful BE creation, the following attribute values 562*13013Sglenn.lagasse@oracle.com * will be returned to the caller by setting them in the 563*13013Sglenn.lagasse@oracle.com * be_attrs parameter passed in: 564*13013Sglenn.lagasse@oracle.com * 565*13013Sglenn.lagasse@oracle.com * BE_ATTR_SNAP_NAME 566*13013Sglenn.lagasse@oracle.com * BE_ATTR_NEW_BE_NAME 567*13013Sglenn.lagasse@oracle.com * Return: 568*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 569*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 570*13013Sglenn.lagasse@oracle.com * Scope: 571*13013Sglenn.lagasse@oracle.com * Public 572*13013Sglenn.lagasse@oracle.com */ 573*13013Sglenn.lagasse@oracle.com int 574*13013Sglenn.lagasse@oracle.com be_copy(nvlist_t *be_attrs) 575*13013Sglenn.lagasse@oracle.com { 576*13013Sglenn.lagasse@oracle.com be_transaction_data_t bt = { 0 }; 577*13013Sglenn.lagasse@oracle.com be_fs_list_data_t fld = { 0 }; 578*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp = NULL; 579*13013Sglenn.lagasse@oracle.com nvlist_t *zfs_props = NULL; 580*13013Sglenn.lagasse@oracle.com uuid_t uu = { 0 }; 581*13013Sglenn.lagasse@oracle.com char obe_root_ds[MAXPATHLEN]; 582*13013Sglenn.lagasse@oracle.com char nbe_root_ds[MAXPATHLEN]; 583*13013Sglenn.lagasse@oracle.com char ss[MAXPATHLEN]; 584*13013Sglenn.lagasse@oracle.com char *new_mp = NULL; 585*13013Sglenn.lagasse@oracle.com boolean_t autoname = B_FALSE; 586*13013Sglenn.lagasse@oracle.com boolean_t be_created = B_FALSE; 587*13013Sglenn.lagasse@oracle.com int i; 588*13013Sglenn.lagasse@oracle.com int zret; 589*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 590*13013Sglenn.lagasse@oracle.com 591*13013Sglenn.lagasse@oracle.com /* Initialize libzfs handle */ 592*13013Sglenn.lagasse@oracle.com if (!be_zfs_init()) 593*13013Sglenn.lagasse@oracle.com return (BE_ERR_INIT); 594*13013Sglenn.lagasse@oracle.com 595*13013Sglenn.lagasse@oracle.com /* Get original BE name */ 596*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 597*13013Sglenn.lagasse@oracle.com BE_ATTR_ORIG_BE_NAME, DATA_TYPE_STRING, &bt.obe_name, NULL) != 0) { 598*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 599*13013Sglenn.lagasse@oracle.com "BE_ATTR_ORIG_BE_NAME attribute\n")); 600*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 601*13013Sglenn.lagasse@oracle.com } 602*13013Sglenn.lagasse@oracle.com 603*13013Sglenn.lagasse@oracle.com /* If original BE name not provided, use current BE */ 604*13013Sglenn.lagasse@oracle.com if (bt.obe_name == NULL) { 605*13013Sglenn.lagasse@oracle.com if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) { 606*13013Sglenn.lagasse@oracle.com return (ret); 607*13013Sglenn.lagasse@oracle.com } 608*13013Sglenn.lagasse@oracle.com } else { 609*13013Sglenn.lagasse@oracle.com /* Validate original BE name */ 610*13013Sglenn.lagasse@oracle.com if (!be_valid_be_name(bt.obe_name)) { 611*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 612*13013Sglenn.lagasse@oracle.com "invalid BE name %s\n"), bt.obe_name); 613*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 614*13013Sglenn.lagasse@oracle.com } 615*13013Sglenn.lagasse@oracle.com } 616*13013Sglenn.lagasse@oracle.com 617*13013Sglenn.lagasse@oracle.com /* Find which zpool obe_name lives in */ 618*13013Sglenn.lagasse@oracle.com if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 619*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 620*13013Sglenn.lagasse@oracle.com "find zpool for BE (%s)\n"), bt.obe_name); 621*13013Sglenn.lagasse@oracle.com return (BE_ERR_BE_NOENT); 622*13013Sglenn.lagasse@oracle.com } else if (zret < 0) { 623*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 624*13013Sglenn.lagasse@oracle.com "zpool_iter failed: %s\n"), 625*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 626*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 627*13013Sglenn.lagasse@oracle.com } 628*13013Sglenn.lagasse@oracle.com 629*13013Sglenn.lagasse@oracle.com /* Get snapshot name of original BE if one was provided */ 630*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 631*13013Sglenn.lagasse@oracle.com BE_ATTR_SNAP_NAME, DATA_TYPE_STRING, &bt.obe_snap_name, NULL) 632*13013Sglenn.lagasse@oracle.com != 0) { 633*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 634*13013Sglenn.lagasse@oracle.com "BE_ATTR_SNAP_NAME attribute\n")); 635*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 636*13013Sglenn.lagasse@oracle.com } 637*13013Sglenn.lagasse@oracle.com 638*13013Sglenn.lagasse@oracle.com /* Get new BE name */ 639*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 640*13013Sglenn.lagasse@oracle.com BE_ATTR_NEW_BE_NAME, DATA_TYPE_STRING, &bt.nbe_name, NULL) 641*13013Sglenn.lagasse@oracle.com != 0) { 642*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 643*13013Sglenn.lagasse@oracle.com "BE_ATTR_NEW_BE_NAME attribute\n")); 644*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 645*13013Sglenn.lagasse@oracle.com } 646*13013Sglenn.lagasse@oracle.com 647*13013Sglenn.lagasse@oracle.com /* Get zpool name to create new BE in */ 648*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 649*13013Sglenn.lagasse@oracle.com BE_ATTR_NEW_BE_POOL, DATA_TYPE_STRING, &bt.nbe_zpool, NULL) != 0) { 650*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 651*13013Sglenn.lagasse@oracle.com "BE_ATTR_NEW_BE_POOL attribute\n")); 652*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 653*13013Sglenn.lagasse@oracle.com } 654*13013Sglenn.lagasse@oracle.com 655*13013Sglenn.lagasse@oracle.com /* Get new BE's description if one was provided */ 656*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 657*13013Sglenn.lagasse@oracle.com BE_ATTR_NEW_BE_DESC, DATA_TYPE_STRING, &bt.nbe_desc, NULL) != 0) { 658*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 659*13013Sglenn.lagasse@oracle.com "BE_ATTR_NEW_BE_DESC attribute\n")); 660*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 661*13013Sglenn.lagasse@oracle.com } 662*13013Sglenn.lagasse@oracle.com 663*13013Sglenn.lagasse@oracle.com /* Get BE policy to create this snapshot under */ 664*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 665*13013Sglenn.lagasse@oracle.com BE_ATTR_POLICY, DATA_TYPE_STRING, &bt.policy, NULL) != 0) { 666*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 667*13013Sglenn.lagasse@oracle.com "BE_ATTR_POLICY attribute\n")); 668*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 669*13013Sglenn.lagasse@oracle.com } 670*13013Sglenn.lagasse@oracle.com 671*13013Sglenn.lagasse@oracle.com /* 672*13013Sglenn.lagasse@oracle.com * Create property list for new BE root dataset. If some 673*13013Sglenn.lagasse@oracle.com * zfs properties were already provided by the caller, dup 674*13013Sglenn.lagasse@oracle.com * that list. Otherwise initialize a new property list. 675*13013Sglenn.lagasse@oracle.com */ 676*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 677*13013Sglenn.lagasse@oracle.com BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL) 678*13013Sglenn.lagasse@oracle.com != 0) { 679*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to lookup " 680*13013Sglenn.lagasse@oracle.com "BE_ATTR_ZFS_PROPERTIES attribute\n")); 681*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 682*13013Sglenn.lagasse@oracle.com } 683*13013Sglenn.lagasse@oracle.com if (zfs_props != NULL) { 684*13013Sglenn.lagasse@oracle.com /* Make sure its a unique nvlist */ 685*13013Sglenn.lagasse@oracle.com if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) && 686*13013Sglenn.lagasse@oracle.com !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) { 687*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: ZFS property list " 688*13013Sglenn.lagasse@oracle.com "not unique\n")); 689*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 690*13013Sglenn.lagasse@oracle.com } 691*13013Sglenn.lagasse@oracle.com 692*13013Sglenn.lagasse@oracle.com /* Dup the list */ 693*13013Sglenn.lagasse@oracle.com if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) { 694*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 695*13013Sglenn.lagasse@oracle.com "failed to dup ZFS property list\n")); 696*13013Sglenn.lagasse@oracle.com return (BE_ERR_NOMEM); 697*13013Sglenn.lagasse@oracle.com } 698*13013Sglenn.lagasse@oracle.com } else { 699*13013Sglenn.lagasse@oracle.com /* Initialize new nvlist */ 700*13013Sglenn.lagasse@oracle.com if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { 701*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: internal " 702*13013Sglenn.lagasse@oracle.com "error: out of memory\n")); 703*13013Sglenn.lagasse@oracle.com return (BE_ERR_NOMEM); 704*13013Sglenn.lagasse@oracle.com } 705*13013Sglenn.lagasse@oracle.com } 706*13013Sglenn.lagasse@oracle.com 707*13013Sglenn.lagasse@oracle.com /* 708*13013Sglenn.lagasse@oracle.com * If new BE name provided, validate the BE name and then verify 709*13013Sglenn.lagasse@oracle.com * that new BE name doesn't already exist in some pool. 710*13013Sglenn.lagasse@oracle.com */ 711*13013Sglenn.lagasse@oracle.com if (bt.nbe_name) { 712*13013Sglenn.lagasse@oracle.com /* Validate original BE name */ 713*13013Sglenn.lagasse@oracle.com if (!be_valid_be_name(bt.nbe_name)) { 714*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 715*13013Sglenn.lagasse@oracle.com "invalid BE name %s\n"), bt.nbe_name); 716*13013Sglenn.lagasse@oracle.com ret = BE_ERR_INVAL; 717*13013Sglenn.lagasse@oracle.com goto done; 718*13013Sglenn.lagasse@oracle.com } 719*13013Sglenn.lagasse@oracle.com 720*13013Sglenn.lagasse@oracle.com /* Verify it doesn't already exist */ 721*13013Sglenn.lagasse@oracle.com if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) 722*13013Sglenn.lagasse@oracle.com > 0) { 723*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: BE (%s) already " 724*13013Sglenn.lagasse@oracle.com "exists\n"), bt.nbe_name); 725*13013Sglenn.lagasse@oracle.com ret = BE_ERR_BE_EXISTS; 726*13013Sglenn.lagasse@oracle.com goto done; 727*13013Sglenn.lagasse@oracle.com } else if (zret < 0) { 728*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: zpool_iter failed: " 729*13013Sglenn.lagasse@oracle.com "%s\n"), libzfs_error_description(g_zfs)); 730*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 731*13013Sglenn.lagasse@oracle.com goto done; 732*13013Sglenn.lagasse@oracle.com } 733*13013Sglenn.lagasse@oracle.com } else { 734*13013Sglenn.lagasse@oracle.com /* 735*13013Sglenn.lagasse@oracle.com * If an auto named BE is desired, it must be in the same 736*13013Sglenn.lagasse@oracle.com * pool is the original BE. 737*13013Sglenn.lagasse@oracle.com */ 738*13013Sglenn.lagasse@oracle.com if (bt.nbe_zpool != NULL) { 739*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: cannot specify pool " 740*13013Sglenn.lagasse@oracle.com "name when creating an auto named BE\n")); 741*13013Sglenn.lagasse@oracle.com ret = BE_ERR_INVAL; 742*13013Sglenn.lagasse@oracle.com goto done; 743*13013Sglenn.lagasse@oracle.com } 744*13013Sglenn.lagasse@oracle.com 745*13013Sglenn.lagasse@oracle.com /* 746*13013Sglenn.lagasse@oracle.com * Generate auto named BE 747*13013Sglenn.lagasse@oracle.com */ 748*13013Sglenn.lagasse@oracle.com if ((bt.nbe_name = be_auto_be_name(bt.obe_name)) 749*13013Sglenn.lagasse@oracle.com == NULL) { 750*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 751*13013Sglenn.lagasse@oracle.com "failed to generate auto BE name\n")); 752*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 753*13013Sglenn.lagasse@oracle.com goto done; 754*13013Sglenn.lagasse@oracle.com } 755*13013Sglenn.lagasse@oracle.com 756*13013Sglenn.lagasse@oracle.com autoname = B_TRUE; 757*13013Sglenn.lagasse@oracle.com } 758*13013Sglenn.lagasse@oracle.com 759*13013Sglenn.lagasse@oracle.com /* 760*13013Sglenn.lagasse@oracle.com * If zpool name to create new BE in is not provided, 761*13013Sglenn.lagasse@oracle.com * create new BE in original BE's pool. 762*13013Sglenn.lagasse@oracle.com */ 763*13013Sglenn.lagasse@oracle.com if (bt.nbe_zpool == NULL) { 764*13013Sglenn.lagasse@oracle.com bt.nbe_zpool = bt.obe_zpool; 765*13013Sglenn.lagasse@oracle.com } 766*13013Sglenn.lagasse@oracle.com 767*13013Sglenn.lagasse@oracle.com /* Get root dataset names for obe_name and nbe_name */ 768*13013Sglenn.lagasse@oracle.com be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 769*13013Sglenn.lagasse@oracle.com sizeof (obe_root_ds)); 770*13013Sglenn.lagasse@oracle.com be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds, 771*13013Sglenn.lagasse@oracle.com sizeof (nbe_root_ds)); 772*13013Sglenn.lagasse@oracle.com 773*13013Sglenn.lagasse@oracle.com bt.obe_root_ds = obe_root_ds; 774*13013Sglenn.lagasse@oracle.com bt.nbe_root_ds = nbe_root_ds; 775*13013Sglenn.lagasse@oracle.com 776*13013Sglenn.lagasse@oracle.com /* 777*13013Sglenn.lagasse@oracle.com * If an existing snapshot name has been provided to create from, 778*13013Sglenn.lagasse@oracle.com * verify that it exists for the original BE's root dataset. 779*13013Sglenn.lagasse@oracle.com */ 780*13013Sglenn.lagasse@oracle.com if (bt.obe_snap_name != NULL) { 781*13013Sglenn.lagasse@oracle.com 782*13013Sglenn.lagasse@oracle.com /* Generate dataset name for snapshot to use. */ 783*13013Sglenn.lagasse@oracle.com (void) snprintf(ss, sizeof (ss), "%s@%s", bt.obe_root_ds, 784*13013Sglenn.lagasse@oracle.com bt.obe_snap_name); 785*13013Sglenn.lagasse@oracle.com 786*13013Sglenn.lagasse@oracle.com /* Verify snapshot exists */ 787*13013Sglenn.lagasse@oracle.com if (!zfs_dataset_exists(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) { 788*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 789*13013Sglenn.lagasse@oracle.com "snapshot does not exist (%s): %s\n"), ss, 790*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 791*13013Sglenn.lagasse@oracle.com ret = BE_ERR_SS_NOENT; 792*13013Sglenn.lagasse@oracle.com goto done; 793*13013Sglenn.lagasse@oracle.com } 794*13013Sglenn.lagasse@oracle.com } else { 795*13013Sglenn.lagasse@oracle.com /* 796*13013Sglenn.lagasse@oracle.com * Else snapshot name was not provided, generate an 797*13013Sglenn.lagasse@oracle.com * auto named snapshot to use as its origin. 798*13013Sglenn.lagasse@oracle.com */ 799*13013Sglenn.lagasse@oracle.com if ((ret = _be_create_snapshot(bt.obe_name, 800*13013Sglenn.lagasse@oracle.com &bt.obe_snap_name, bt.policy)) != BE_SUCCESS) { 801*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 802*13013Sglenn.lagasse@oracle.com "failed to create auto named snapshot\n")); 803*13013Sglenn.lagasse@oracle.com goto done; 804*13013Sglenn.lagasse@oracle.com } 805*13013Sglenn.lagasse@oracle.com 806*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(be_attrs, BE_ATTR_SNAP_NAME, 807*13013Sglenn.lagasse@oracle.com bt.obe_snap_name) != 0) { 808*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 809*13013Sglenn.lagasse@oracle.com "failed to add snap name to be_attrs\n")); 810*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NOMEM; 811*13013Sglenn.lagasse@oracle.com goto done; 812*13013Sglenn.lagasse@oracle.com } 813*13013Sglenn.lagasse@oracle.com } 814*13013Sglenn.lagasse@oracle.com 815*13013Sglenn.lagasse@oracle.com /* Get handle to original BE's root dataset. */ 816*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) 817*13013Sglenn.lagasse@oracle.com == NULL) { 818*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 819*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 820*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 821*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 822*13013Sglenn.lagasse@oracle.com goto done; 823*13013Sglenn.lagasse@oracle.com } 824*13013Sglenn.lagasse@oracle.com 825*13013Sglenn.lagasse@oracle.com /* If original BE is currently mounted, record its altroot. */ 826*13013Sglenn.lagasse@oracle.com if (zfs_is_mounted(zhp, &bt.obe_altroot) && bt.obe_altroot == NULL) { 827*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 828*13013Sglenn.lagasse@oracle.com "get altroot of mounted BE %s: %s\n"), 829*13013Sglenn.lagasse@oracle.com bt.obe_name, libzfs_error_description(g_zfs)); 830*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 831*13013Sglenn.lagasse@oracle.com goto done; 832*13013Sglenn.lagasse@oracle.com } 833*13013Sglenn.lagasse@oracle.com 834*13013Sglenn.lagasse@oracle.com if (strcmp(bt.obe_zpool, bt.nbe_zpool) == 0) { 835*13013Sglenn.lagasse@oracle.com 836*13013Sglenn.lagasse@oracle.com /* Do clone */ 837*13013Sglenn.lagasse@oracle.com 838*13013Sglenn.lagasse@oracle.com /* 839*13013Sglenn.lagasse@oracle.com * Iterate through original BE's datasets and clone 840*13013Sglenn.lagasse@oracle.com * them to create new BE. This call will end up closing 841*13013Sglenn.lagasse@oracle.com * the zfs handle passed in whether it succeeds for fails. 842*13013Sglenn.lagasse@oracle.com */ 843*13013Sglenn.lagasse@oracle.com if ((ret = be_clone_fs_callback(zhp, &bt)) != 0) { 844*13013Sglenn.lagasse@oracle.com zhp = NULL; 845*13013Sglenn.lagasse@oracle.com /* Creating clone BE failed */ 846*13013Sglenn.lagasse@oracle.com if (!autoname || ret != BE_ERR_BE_EXISTS) { 847*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 848*13013Sglenn.lagasse@oracle.com "failed to clone new BE (%s) from " 849*13013Sglenn.lagasse@oracle.com "orig BE (%s)\n"), 850*13013Sglenn.lagasse@oracle.com bt.nbe_name, bt.obe_name); 851*13013Sglenn.lagasse@oracle.com ret = BE_ERR_CLONE; 852*13013Sglenn.lagasse@oracle.com goto done; 853*13013Sglenn.lagasse@oracle.com } 854*13013Sglenn.lagasse@oracle.com 855*13013Sglenn.lagasse@oracle.com /* 856*13013Sglenn.lagasse@oracle.com * We failed to create the new BE because a BE with 857*13013Sglenn.lagasse@oracle.com * the auto-name we generated above has since come 858*13013Sglenn.lagasse@oracle.com * into existence. Regenerate a new auto-name 859*13013Sglenn.lagasse@oracle.com * and retry. 860*13013Sglenn.lagasse@oracle.com */ 861*13013Sglenn.lagasse@oracle.com for (i = 1; i < BE_AUTO_NAME_MAX_TRY; i++) { 862*13013Sglenn.lagasse@oracle.com 863*13013Sglenn.lagasse@oracle.com /* Sleep 1 before retrying */ 864*13013Sglenn.lagasse@oracle.com (void) sleep(1); 865*13013Sglenn.lagasse@oracle.com 866*13013Sglenn.lagasse@oracle.com /* Generate new auto BE name */ 867*13013Sglenn.lagasse@oracle.com free(bt.nbe_name); 868*13013Sglenn.lagasse@oracle.com if ((bt.nbe_name = be_auto_be_name(bt.obe_name)) 869*13013Sglenn.lagasse@oracle.com == NULL) { 870*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 871*13013Sglenn.lagasse@oracle.com "failed to generate auto " 872*13013Sglenn.lagasse@oracle.com "BE name\n")); 873*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 874*13013Sglenn.lagasse@oracle.com goto done; 875*13013Sglenn.lagasse@oracle.com } 876*13013Sglenn.lagasse@oracle.com 877*13013Sglenn.lagasse@oracle.com /* 878*13013Sglenn.lagasse@oracle.com * Regenerate string for new BE's 879*13013Sglenn.lagasse@oracle.com * root dataset name 880*13013Sglenn.lagasse@oracle.com */ 881*13013Sglenn.lagasse@oracle.com be_make_root_ds(bt.nbe_zpool, bt.nbe_name, 882*13013Sglenn.lagasse@oracle.com nbe_root_ds, sizeof (nbe_root_ds)); 883*13013Sglenn.lagasse@oracle.com bt.nbe_root_ds = nbe_root_ds; 884*13013Sglenn.lagasse@oracle.com 885*13013Sglenn.lagasse@oracle.com /* 886*13013Sglenn.lagasse@oracle.com * Get handle to original BE's root dataset. 887*13013Sglenn.lagasse@oracle.com */ 888*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, 889*13013Sglenn.lagasse@oracle.com ZFS_TYPE_FILESYSTEM)) == NULL) { 890*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 891*13013Sglenn.lagasse@oracle.com "failed to open BE root dataset " 892*13013Sglenn.lagasse@oracle.com "(%s): %s\n"), bt.obe_root_ds, 893*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 894*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 895*13013Sglenn.lagasse@oracle.com goto done; 896*13013Sglenn.lagasse@oracle.com } 897*13013Sglenn.lagasse@oracle.com 898*13013Sglenn.lagasse@oracle.com /* 899*13013Sglenn.lagasse@oracle.com * Try to clone the BE again. This 900*13013Sglenn.lagasse@oracle.com * call will end up closing the zfs 901*13013Sglenn.lagasse@oracle.com * handle passed in whether it 902*13013Sglenn.lagasse@oracle.com * succeeds or fails. 903*13013Sglenn.lagasse@oracle.com */ 904*13013Sglenn.lagasse@oracle.com ret = be_clone_fs_callback(zhp, &bt); 905*13013Sglenn.lagasse@oracle.com zhp = NULL; 906*13013Sglenn.lagasse@oracle.com if (ret == 0) { 907*13013Sglenn.lagasse@oracle.com break; 908*13013Sglenn.lagasse@oracle.com } else if (ret != BE_ERR_BE_EXISTS) { 909*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 910*13013Sglenn.lagasse@oracle.com "failed to clone new BE " 911*13013Sglenn.lagasse@oracle.com "(%s) from orig BE (%s)\n"), 912*13013Sglenn.lagasse@oracle.com bt.nbe_name, bt.obe_name); 913*13013Sglenn.lagasse@oracle.com ret = BE_ERR_CLONE; 914*13013Sglenn.lagasse@oracle.com goto done; 915*13013Sglenn.lagasse@oracle.com } 916*13013Sglenn.lagasse@oracle.com } 917*13013Sglenn.lagasse@oracle.com 918*13013Sglenn.lagasse@oracle.com /* 919*13013Sglenn.lagasse@oracle.com * If we've exhausted the maximum number of 920*13013Sglenn.lagasse@oracle.com * tries, free the auto BE name and return 921*13013Sglenn.lagasse@oracle.com * error. 922*13013Sglenn.lagasse@oracle.com */ 923*13013Sglenn.lagasse@oracle.com if (i == BE_AUTO_NAME_MAX_TRY) { 924*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed " 925*13013Sglenn.lagasse@oracle.com "to create unique auto BE name\n")); 926*13013Sglenn.lagasse@oracle.com free(bt.nbe_name); 927*13013Sglenn.lagasse@oracle.com bt.nbe_name = NULL; 928*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 929*13013Sglenn.lagasse@oracle.com goto done; 930*13013Sglenn.lagasse@oracle.com } 931*13013Sglenn.lagasse@oracle.com } 932*13013Sglenn.lagasse@oracle.com zhp = NULL; 933*13013Sglenn.lagasse@oracle.com 934*13013Sglenn.lagasse@oracle.com } else { 935*13013Sglenn.lagasse@oracle.com 936*13013Sglenn.lagasse@oracle.com /* Do copy (i.e. send BE datasets via zfs_send/recv) */ 937*13013Sglenn.lagasse@oracle.com 938*13013Sglenn.lagasse@oracle.com /* 939*13013Sglenn.lagasse@oracle.com * Verify BE container dataset in nbe_zpool exists. 940*13013Sglenn.lagasse@oracle.com * If not, create it. 941*13013Sglenn.lagasse@oracle.com */ 942*13013Sglenn.lagasse@oracle.com if (!be_create_container_ds(bt.nbe_zpool)) { 943*13013Sglenn.lagasse@oracle.com ret = BE_ERR_CREATDS; 944*13013Sglenn.lagasse@oracle.com goto done; 945*13013Sglenn.lagasse@oracle.com } 946*13013Sglenn.lagasse@oracle.com 947*13013Sglenn.lagasse@oracle.com /* 948*13013Sglenn.lagasse@oracle.com * Iterate through original BE's datasets and send 949*13013Sglenn.lagasse@oracle.com * them to the other pool. This call will end up closing 950*13013Sglenn.lagasse@oracle.com * the zfs handle passed in whether it succeeds or fails. 951*13013Sglenn.lagasse@oracle.com */ 952*13013Sglenn.lagasse@oracle.com if ((ret = be_send_fs_callback(zhp, &bt)) != 0) { 953*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 954*13013Sglenn.lagasse@oracle.com "send BE (%s) to pool (%s)\n"), bt.obe_name, 955*13013Sglenn.lagasse@oracle.com bt.nbe_zpool); 956*13013Sglenn.lagasse@oracle.com ret = BE_ERR_COPY; 957*13013Sglenn.lagasse@oracle.com zhp = NULL; 958*13013Sglenn.lagasse@oracle.com goto done; 959*13013Sglenn.lagasse@oracle.com } 960*13013Sglenn.lagasse@oracle.com zhp = NULL; 961*13013Sglenn.lagasse@oracle.com } 962*13013Sglenn.lagasse@oracle.com 963*13013Sglenn.lagasse@oracle.com /* 964*13013Sglenn.lagasse@oracle.com * Set flag to note that the dataset(s) for the new BE have been 965*13013Sglenn.lagasse@oracle.com * successfully created so that if a failure happens from this point 966*13013Sglenn.lagasse@oracle.com * on, we know to cleanup these datasets. 967*13013Sglenn.lagasse@oracle.com */ 968*13013Sglenn.lagasse@oracle.com be_created = B_TRUE; 969*13013Sglenn.lagasse@oracle.com 970*13013Sglenn.lagasse@oracle.com /* 971*13013Sglenn.lagasse@oracle.com * Validate that the new BE is mountable. 972*13013Sglenn.lagasse@oracle.com * Do not attempt to mount non-global zone datasets 973*13013Sglenn.lagasse@oracle.com * since they are not cloned yet. 974*13013Sglenn.lagasse@oracle.com */ 975*13013Sglenn.lagasse@oracle.com if ((ret = _be_mount(bt.nbe_name, &new_mp, BE_MOUNT_FLAG_NO_ZONES)) 976*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 977*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 978*13013Sglenn.lagasse@oracle.com "mount newly created BE\n")); 979*13013Sglenn.lagasse@oracle.com (void) _be_unmount(bt.nbe_name, 0); 980*13013Sglenn.lagasse@oracle.com goto done; 981*13013Sglenn.lagasse@oracle.com } 982*13013Sglenn.lagasse@oracle.com 983*13013Sglenn.lagasse@oracle.com /* Set UUID for new BE */ 984*13013Sglenn.lagasse@oracle.com if (be_set_uuid(bt.nbe_root_ds) != BE_SUCCESS) { 985*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 986*13013Sglenn.lagasse@oracle.com "set uuid for new BE\n")); 987*13013Sglenn.lagasse@oracle.com } 988*13013Sglenn.lagasse@oracle.com 989*13013Sglenn.lagasse@oracle.com /* 990*13013Sglenn.lagasse@oracle.com * Process zones outside of the private BE namespace. 991*13013Sglenn.lagasse@oracle.com * This has to be done here because we need the uuid set in the 992*13013Sglenn.lagasse@oracle.com * root dataset of the new BE. The uuid is use to set the parentbe 993*13013Sglenn.lagasse@oracle.com * property for the new zones datasets. 994*13013Sglenn.lagasse@oracle.com */ 995*13013Sglenn.lagasse@oracle.com if (getzoneid() == GLOBAL_ZONEID && 996*13013Sglenn.lagasse@oracle.com be_get_uuid(bt.obe_root_ds, &uu) == BE_SUCCESS) { 997*13013Sglenn.lagasse@oracle.com if ((ret = be_copy_zones(bt.obe_name, bt.obe_root_ds, 998*13013Sglenn.lagasse@oracle.com bt.nbe_root_ds)) != BE_SUCCESS) { 999*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to process " 1000*13013Sglenn.lagasse@oracle.com "zones\n")); 1001*13013Sglenn.lagasse@oracle.com goto done; 1002*13013Sglenn.lagasse@oracle.com } 1003*13013Sglenn.lagasse@oracle.com } 1004*13013Sglenn.lagasse@oracle.com 1005*13013Sglenn.lagasse@oracle.com /* 1006*13013Sglenn.lagasse@oracle.com * Generate a list of file systems from the original BE that are 1007*13013Sglenn.lagasse@oracle.com * legacy mounted. We use this list to determine which entries in 1008*13013Sglenn.lagasse@oracle.com * vfstab we need to update for the new BE we've just created. 1009*13013Sglenn.lagasse@oracle.com */ 1010*13013Sglenn.lagasse@oracle.com if ((ret = be_get_legacy_fs(bt.obe_name, bt.obe_root_ds, NULL, NULL, 1011*13013Sglenn.lagasse@oracle.com &fld)) != BE_SUCCESS) { 1012*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1013*13013Sglenn.lagasse@oracle.com "get legacy mounted file system list for %s\n"), 1014*13013Sglenn.lagasse@oracle.com bt.obe_name); 1015*13013Sglenn.lagasse@oracle.com goto done; 1016*13013Sglenn.lagasse@oracle.com } 1017*13013Sglenn.lagasse@oracle.com 1018*13013Sglenn.lagasse@oracle.com /* 1019*13013Sglenn.lagasse@oracle.com * Update new BE's vfstab. 1020*13013Sglenn.lagasse@oracle.com */ 1021*13013Sglenn.lagasse@oracle.com if ((ret = be_update_vfstab(bt.nbe_name, bt.obe_zpool, bt.nbe_zpool, 1022*13013Sglenn.lagasse@oracle.com &fld, new_mp)) != BE_SUCCESS) { 1023*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1024*13013Sglenn.lagasse@oracle.com "update new BE's vfstab (%s)\n"), bt.nbe_name); 1025*13013Sglenn.lagasse@oracle.com goto done; 1026*13013Sglenn.lagasse@oracle.com } 1027*13013Sglenn.lagasse@oracle.com 1028*13013Sglenn.lagasse@oracle.com /* Unmount the new BE */ 1029*13013Sglenn.lagasse@oracle.com if ((ret = _be_unmount(bt.nbe_name, 0)) != BE_SUCCESS) { 1030*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1031*13013Sglenn.lagasse@oracle.com "unmount newly created BE\n")); 1032*13013Sglenn.lagasse@oracle.com goto done; 1033*13013Sglenn.lagasse@oracle.com } 1034*13013Sglenn.lagasse@oracle.com 1035*13013Sglenn.lagasse@oracle.com /* 1036*13013Sglenn.lagasse@oracle.com * Add boot menu entry for newly created clone 1037*13013Sglenn.lagasse@oracle.com */ 1038*13013Sglenn.lagasse@oracle.com if (getzoneid() == GLOBAL_ZONEID && 1039*13013Sglenn.lagasse@oracle.com (ret = be_append_menu(bt.nbe_name, bt.nbe_zpool, 1040*13013Sglenn.lagasse@oracle.com NULL, bt.obe_root_ds, bt.nbe_desc)) != BE_SUCCESS) { 1041*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1042*13013Sglenn.lagasse@oracle.com "add BE (%s) to boot menu\n"), bt.nbe_name); 1043*13013Sglenn.lagasse@oracle.com goto done; 1044*13013Sglenn.lagasse@oracle.com } 1045*13013Sglenn.lagasse@oracle.com 1046*13013Sglenn.lagasse@oracle.com /* 1047*13013Sglenn.lagasse@oracle.com * If we succeeded in creating an auto named BE, set its policy 1048*13013Sglenn.lagasse@oracle.com * type and return the auto generated name to the caller by storing 1049*13013Sglenn.lagasse@oracle.com * it in the nvlist passed in by the caller. 1050*13013Sglenn.lagasse@oracle.com */ 1051*13013Sglenn.lagasse@oracle.com if (autoname) { 1052*13013Sglenn.lagasse@oracle.com /* Get handle to new BE's root dataset. */ 1053*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, bt.nbe_root_ds, 1054*13013Sglenn.lagasse@oracle.com ZFS_TYPE_FILESYSTEM)) == NULL) { 1055*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1056*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), bt.nbe_root_ds, 1057*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1058*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1059*13013Sglenn.lagasse@oracle.com goto done; 1060*13013Sglenn.lagasse@oracle.com } 1061*13013Sglenn.lagasse@oracle.com 1062*13013Sglenn.lagasse@oracle.com /* 1063*13013Sglenn.lagasse@oracle.com * Set the policy type property into the new BE's root dataset 1064*13013Sglenn.lagasse@oracle.com */ 1065*13013Sglenn.lagasse@oracle.com if (bt.policy == NULL) { 1066*13013Sglenn.lagasse@oracle.com /* If no policy type provided, use default type */ 1067*13013Sglenn.lagasse@oracle.com bt.policy = be_default_policy(); 1068*13013Sglenn.lagasse@oracle.com } 1069*13013Sglenn.lagasse@oracle.com 1070*13013Sglenn.lagasse@oracle.com if (zfs_prop_set(zhp, BE_POLICY_PROPERTY, bt.policy) != 0) { 1071*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1072*13013Sglenn.lagasse@oracle.com "set BE policy for %s: %s\n"), bt.nbe_name, 1073*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1074*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1075*13013Sglenn.lagasse@oracle.com goto done; 1076*13013Sglenn.lagasse@oracle.com } 1077*13013Sglenn.lagasse@oracle.com 1078*13013Sglenn.lagasse@oracle.com /* 1079*13013Sglenn.lagasse@oracle.com * Return the auto generated name to the caller 1080*13013Sglenn.lagasse@oracle.com */ 1081*13013Sglenn.lagasse@oracle.com if (bt.nbe_name) { 1082*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(be_attrs, BE_ATTR_NEW_BE_NAME, 1083*13013Sglenn.lagasse@oracle.com bt.nbe_name) != 0) { 1084*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: failed to " 1085*13013Sglenn.lagasse@oracle.com "add snap name to be_attrs\n")); 1086*13013Sglenn.lagasse@oracle.com } 1087*13013Sglenn.lagasse@oracle.com } 1088*13013Sglenn.lagasse@oracle.com } 1089*13013Sglenn.lagasse@oracle.com 1090*13013Sglenn.lagasse@oracle.com done: 1091*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1092*13013Sglenn.lagasse@oracle.com be_free_fs_list(&fld); 1093*13013Sglenn.lagasse@oracle.com 1094*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 1095*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 1096*13013Sglenn.lagasse@oracle.com 1097*13013Sglenn.lagasse@oracle.com free(bt.obe_altroot); 1098*13013Sglenn.lagasse@oracle.com free(new_mp); 1099*13013Sglenn.lagasse@oracle.com 1100*13013Sglenn.lagasse@oracle.com /* 1101*13013Sglenn.lagasse@oracle.com * If a failure occurred and we already created the datasets for 1102*13013Sglenn.lagasse@oracle.com * the new boot environment, destroy them. 1103*13013Sglenn.lagasse@oracle.com */ 1104*13013Sglenn.lagasse@oracle.com if (ret != BE_SUCCESS && be_created) { 1105*13013Sglenn.lagasse@oracle.com be_destroy_data_t cdd = { 0 }; 1106*13013Sglenn.lagasse@oracle.com 1107*13013Sglenn.lagasse@oracle.com cdd.force_unmount = B_TRUE; 1108*13013Sglenn.lagasse@oracle.com 1109*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy: " 1110*13013Sglenn.lagasse@oracle.com "destroying partially created boot environment\n")); 1111*13013Sglenn.lagasse@oracle.com 1112*13013Sglenn.lagasse@oracle.com if (getzoneid() == GLOBAL_ZONEID && be_get_uuid(bt.nbe_root_ds, 1113*13013Sglenn.lagasse@oracle.com &cdd.gz_be_uuid) == 0) 1114*13013Sglenn.lagasse@oracle.com (void) be_destroy_zones(bt.nbe_name, bt.nbe_root_ds, 1115*13013Sglenn.lagasse@oracle.com &cdd); 1116*13013Sglenn.lagasse@oracle.com 1117*13013Sglenn.lagasse@oracle.com (void) _be_destroy(bt.nbe_root_ds, &cdd); 1118*13013Sglenn.lagasse@oracle.com } 1119*13013Sglenn.lagasse@oracle.com 1120*13013Sglenn.lagasse@oracle.com be_zfs_fini(); 1121*13013Sglenn.lagasse@oracle.com 1122*13013Sglenn.lagasse@oracle.com return (ret); 1123*13013Sglenn.lagasse@oracle.com } 1124*13013Sglenn.lagasse@oracle.com 1125*13013Sglenn.lagasse@oracle.com /* ******************************************************************** */ 1126*13013Sglenn.lagasse@oracle.com /* Semi-Private Functions */ 1127*13013Sglenn.lagasse@oracle.com /* ******************************************************************** */ 1128*13013Sglenn.lagasse@oracle.com 1129*13013Sglenn.lagasse@oracle.com /* 1130*13013Sglenn.lagasse@oracle.com * Function: be_find_zpool_callback 1131*13013Sglenn.lagasse@oracle.com * Description: Callback function used to find the pool that a BE lives in. 1132*13013Sglenn.lagasse@oracle.com * Parameters: 1133*13013Sglenn.lagasse@oracle.com * zlp - zpool_handle_t pointer for the current pool being 1134*13013Sglenn.lagasse@oracle.com * looked at. 1135*13013Sglenn.lagasse@oracle.com * data - be_transaction_data_t pointer providing information 1136*13013Sglenn.lagasse@oracle.com * about the BE that's being searched for. 1137*13013Sglenn.lagasse@oracle.com * This function uses the obe_name member of this 1138*13013Sglenn.lagasse@oracle.com * parameter to use as the BE name to search for. 1139*13013Sglenn.lagasse@oracle.com * Upon successfully locating the BE, it populates 1140*13013Sglenn.lagasse@oracle.com * obe_zpool with the pool name that the BE is found in. 1141*13013Sglenn.lagasse@oracle.com * Returns: 1142*13013Sglenn.lagasse@oracle.com * 1 - BE exists in this pool. 1143*13013Sglenn.lagasse@oracle.com * 0 - BE does not exist in this pool. 1144*13013Sglenn.lagasse@oracle.com * Scope: 1145*13013Sglenn.lagasse@oracle.com * Semi-private (library wide use only) 1146*13013Sglenn.lagasse@oracle.com */ 1147*13013Sglenn.lagasse@oracle.com int 1148*13013Sglenn.lagasse@oracle.com be_find_zpool_callback(zpool_handle_t *zlp, void *data) 1149*13013Sglenn.lagasse@oracle.com { 1150*13013Sglenn.lagasse@oracle.com be_transaction_data_t *bt = data; 1151*13013Sglenn.lagasse@oracle.com const char *zpool = zpool_get_name(zlp); 1152*13013Sglenn.lagasse@oracle.com char be_root_ds[MAXPATHLEN]; 1153*13013Sglenn.lagasse@oracle.com 1154*13013Sglenn.lagasse@oracle.com /* 1155*13013Sglenn.lagasse@oracle.com * Generate string for the BE's root dataset 1156*13013Sglenn.lagasse@oracle.com */ 1157*13013Sglenn.lagasse@oracle.com be_make_root_ds(zpool, bt->obe_name, be_root_ds, sizeof (be_root_ds)); 1158*13013Sglenn.lagasse@oracle.com 1159*13013Sglenn.lagasse@oracle.com /* 1160*13013Sglenn.lagasse@oracle.com * Check if dataset exists 1161*13013Sglenn.lagasse@oracle.com */ 1162*13013Sglenn.lagasse@oracle.com if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) { 1163*13013Sglenn.lagasse@oracle.com /* BE's root dataset exists in zpool */ 1164*13013Sglenn.lagasse@oracle.com bt->obe_zpool = strdup(zpool); 1165*13013Sglenn.lagasse@oracle.com zpool_close(zlp); 1166*13013Sglenn.lagasse@oracle.com return (1); 1167*13013Sglenn.lagasse@oracle.com } 1168*13013Sglenn.lagasse@oracle.com 1169*13013Sglenn.lagasse@oracle.com zpool_close(zlp); 1170*13013Sglenn.lagasse@oracle.com return (0); 1171*13013Sglenn.lagasse@oracle.com } 1172*13013Sglenn.lagasse@oracle.com 1173*13013Sglenn.lagasse@oracle.com /* 1174*13013Sglenn.lagasse@oracle.com * Function: be_exists_callback 1175*13013Sglenn.lagasse@oracle.com * Description: Callback function used to find out if a BE exists. 1176*13013Sglenn.lagasse@oracle.com * Parameters: 1177*13013Sglenn.lagasse@oracle.com * zlp - zpool_handle_t pointer to the current pool being 1178*13013Sglenn.lagasse@oracle.com * looked at. 1179*13013Sglenn.lagasse@oracle.com * data - BE name to look for. 1180*13013Sglenn.lagasse@oracle.com * Return: 1181*13013Sglenn.lagasse@oracle.com * 1 - BE exists in this pool. 1182*13013Sglenn.lagasse@oracle.com * 0 - BE does not exist in this pool. 1183*13013Sglenn.lagasse@oracle.com * Scope: 1184*13013Sglenn.lagasse@oracle.com * Semi-private (library wide use only) 1185*13013Sglenn.lagasse@oracle.com */ 1186*13013Sglenn.lagasse@oracle.com int 1187*13013Sglenn.lagasse@oracle.com be_exists_callback(zpool_handle_t *zlp, void *data) 1188*13013Sglenn.lagasse@oracle.com { 1189*13013Sglenn.lagasse@oracle.com const char *zpool = zpool_get_name(zlp); 1190*13013Sglenn.lagasse@oracle.com char *be_name = data; 1191*13013Sglenn.lagasse@oracle.com char be_root_ds[MAXPATHLEN]; 1192*13013Sglenn.lagasse@oracle.com 1193*13013Sglenn.lagasse@oracle.com /* 1194*13013Sglenn.lagasse@oracle.com * Generate string for the BE's root dataset 1195*13013Sglenn.lagasse@oracle.com */ 1196*13013Sglenn.lagasse@oracle.com be_make_root_ds(zpool, be_name, be_root_ds, sizeof (be_root_ds)); 1197*13013Sglenn.lagasse@oracle.com 1198*13013Sglenn.lagasse@oracle.com /* 1199*13013Sglenn.lagasse@oracle.com * Check if dataset exists 1200*13013Sglenn.lagasse@oracle.com */ 1201*13013Sglenn.lagasse@oracle.com if (zfs_dataset_exists(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) { 1202*13013Sglenn.lagasse@oracle.com /* BE's root dataset exists in zpool */ 1203*13013Sglenn.lagasse@oracle.com zpool_close(zlp); 1204*13013Sglenn.lagasse@oracle.com return (1); 1205*13013Sglenn.lagasse@oracle.com } 1206*13013Sglenn.lagasse@oracle.com 1207*13013Sglenn.lagasse@oracle.com zpool_close(zlp); 1208*13013Sglenn.lagasse@oracle.com return (0); 1209*13013Sglenn.lagasse@oracle.com } 1210*13013Sglenn.lagasse@oracle.com 1211*13013Sglenn.lagasse@oracle.com /* 1212*13013Sglenn.lagasse@oracle.com * Function: be_set_uuid 1213*13013Sglenn.lagasse@oracle.com * Description: This function generates a uuid, unparses it into 1214*13013Sglenn.lagasse@oracle.com * string representation, and sets that string into 1215*13013Sglenn.lagasse@oracle.com * a zfs user property for a root dataset of a BE. 1216*13013Sglenn.lagasse@oracle.com * The name of the user property used to store the 1217*13013Sglenn.lagasse@oracle.com * uuid is org.opensolaris.libbe:uuid 1218*13013Sglenn.lagasse@oracle.com * 1219*13013Sglenn.lagasse@oracle.com * Parameters: 1220*13013Sglenn.lagasse@oracle.com * root_ds - Root dataset of the BE to set a uuid on. 1221*13013Sglenn.lagasse@oracle.com * Return: 1222*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1223*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 1224*13013Sglenn.lagasse@oracle.com * Scope: 1225*13013Sglenn.lagasse@oracle.com * Semi-private (library wide ues only) 1226*13013Sglenn.lagasse@oracle.com */ 1227*13013Sglenn.lagasse@oracle.com int 1228*13013Sglenn.lagasse@oracle.com be_set_uuid(char *root_ds) 1229*13013Sglenn.lagasse@oracle.com { 1230*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp = NULL; 1231*13013Sglenn.lagasse@oracle.com uuid_t uu = { 0 }; 1232*13013Sglenn.lagasse@oracle.com char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 }; 1233*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 1234*13013Sglenn.lagasse@oracle.com 1235*13013Sglenn.lagasse@oracle.com /* Generate a UUID and unparse it into string form */ 1236*13013Sglenn.lagasse@oracle.com uuid_generate(uu); 1237*13013Sglenn.lagasse@oracle.com if (uuid_is_null(uu) != 0) { 1238*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_set_uuid: failed to " 1239*13013Sglenn.lagasse@oracle.com "generate uuid\n")); 1240*13013Sglenn.lagasse@oracle.com return (BE_ERR_GEN_UUID); 1241*13013Sglenn.lagasse@oracle.com } 1242*13013Sglenn.lagasse@oracle.com uuid_unparse(uu, uu_string); 1243*13013Sglenn.lagasse@oracle.com 1244*13013Sglenn.lagasse@oracle.com /* Get handle to the BE's root dataset. */ 1245*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { 1246*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_set_uuid: failed to " 1247*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), root_ds, 1248*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1249*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1250*13013Sglenn.lagasse@oracle.com } 1251*13013Sglenn.lagasse@oracle.com 1252*13013Sglenn.lagasse@oracle.com /* Set uuid property for the BE */ 1253*13013Sglenn.lagasse@oracle.com if (zfs_prop_set(zhp, BE_UUID_PROPERTY, uu_string) != 0) { 1254*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_set_uuid: failed to " 1255*13013Sglenn.lagasse@oracle.com "set uuid property for BE: %s\n"), 1256*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1257*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1258*13013Sglenn.lagasse@oracle.com } 1259*13013Sglenn.lagasse@oracle.com 1260*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1261*13013Sglenn.lagasse@oracle.com 1262*13013Sglenn.lagasse@oracle.com return (ret); 1263*13013Sglenn.lagasse@oracle.com } 1264*13013Sglenn.lagasse@oracle.com 1265*13013Sglenn.lagasse@oracle.com /* 1266*13013Sglenn.lagasse@oracle.com * Function: be_get_uuid 1267*13013Sglenn.lagasse@oracle.com * Description: This function gets the uuid string from a BE root 1268*13013Sglenn.lagasse@oracle.com * dataset, parses it into internal format, and returns 1269*13013Sglenn.lagasse@oracle.com * it the caller via a reference pointer passed in. 1270*13013Sglenn.lagasse@oracle.com * 1271*13013Sglenn.lagasse@oracle.com * Parameters: 1272*13013Sglenn.lagasse@oracle.com * rootds - Root dataset of the BE to get the uuid from. 1273*13013Sglenn.lagasse@oracle.com * uu - reference pointer to a uuid_t to return uuid in. 1274*13013Sglenn.lagasse@oracle.com * Return: 1275*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1276*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 1277*13013Sglenn.lagasse@oracle.com * Scope: 1278*13013Sglenn.lagasse@oracle.com * Semi-private (library wide use only) 1279*13013Sglenn.lagasse@oracle.com */ 1280*13013Sglenn.lagasse@oracle.com int 1281*13013Sglenn.lagasse@oracle.com be_get_uuid(const char *root_ds, uuid_t *uu) 1282*13013Sglenn.lagasse@oracle.com { 1283*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp = NULL; 1284*13013Sglenn.lagasse@oracle.com nvlist_t *userprops = NULL; 1285*13013Sglenn.lagasse@oracle.com nvlist_t *propname = NULL; 1286*13013Sglenn.lagasse@oracle.com char *uu_string = NULL; 1287*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 1288*13013Sglenn.lagasse@oracle.com 1289*13013Sglenn.lagasse@oracle.com /* Get handle to the BE's root dataset. */ 1290*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { 1291*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_get_uuid: failed to " 1292*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), root_ds, 1293*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1294*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1295*13013Sglenn.lagasse@oracle.com } 1296*13013Sglenn.lagasse@oracle.com 1297*13013Sglenn.lagasse@oracle.com /* Get user properties for BE's root dataset */ 1298*13013Sglenn.lagasse@oracle.com if ((userprops = zfs_get_user_props(zhp)) == NULL) { 1299*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_get_uuid: failed to " 1300*13013Sglenn.lagasse@oracle.com "get user properties for BE root dataset (%s): %s\n"), 1301*13013Sglenn.lagasse@oracle.com root_ds, libzfs_error_description(g_zfs)); 1302*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1303*13013Sglenn.lagasse@oracle.com goto done; 1304*13013Sglenn.lagasse@oracle.com } 1305*13013Sglenn.lagasse@oracle.com 1306*13013Sglenn.lagasse@oracle.com /* Get UUID string from BE's root dataset user properties */ 1307*13013Sglenn.lagasse@oracle.com if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propname) != 0 || 1308*13013Sglenn.lagasse@oracle.com nvlist_lookup_string(propname, ZPROP_VALUE, &uu_string) != 0) { 1309*13013Sglenn.lagasse@oracle.com /* 1310*13013Sglenn.lagasse@oracle.com * This probably just means that the BE is simply too old 1311*13013Sglenn.lagasse@oracle.com * to have a uuid or that we haven't created a uuid for 1312*13013Sglenn.lagasse@oracle.com * this BE yet. 1313*13013Sglenn.lagasse@oracle.com */ 1314*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_get_uuid: failed to " 1315*13013Sglenn.lagasse@oracle.com "get uuid property from BE root dataset user " 1316*13013Sglenn.lagasse@oracle.com "properties.\n")); 1317*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NO_UUID; 1318*13013Sglenn.lagasse@oracle.com goto done; 1319*13013Sglenn.lagasse@oracle.com } 1320*13013Sglenn.lagasse@oracle.com /* Parse uuid string into internal format */ 1321*13013Sglenn.lagasse@oracle.com if (uuid_parse(uu_string, *uu) != 0 || uuid_is_null(*uu)) { 1322*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_get_uuid: failed to " 1323*13013Sglenn.lagasse@oracle.com "parse uuid\n")); 1324*13013Sglenn.lagasse@oracle.com ret = BE_ERR_PARSE_UUID; 1325*13013Sglenn.lagasse@oracle.com goto done; 1326*13013Sglenn.lagasse@oracle.com } 1327*13013Sglenn.lagasse@oracle.com 1328*13013Sglenn.lagasse@oracle.com done: 1329*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1330*13013Sglenn.lagasse@oracle.com return (ret); 1331*13013Sglenn.lagasse@oracle.com } 1332*13013Sglenn.lagasse@oracle.com 1333*13013Sglenn.lagasse@oracle.com /* ******************************************************************** */ 1334*13013Sglenn.lagasse@oracle.com /* Private Functions */ 1335*13013Sglenn.lagasse@oracle.com /* ******************************************************************** */ 1336*13013Sglenn.lagasse@oracle.com 1337*13013Sglenn.lagasse@oracle.com /* 1338*13013Sglenn.lagasse@oracle.com * Function: _be_destroy 1339*13013Sglenn.lagasse@oracle.com * Description: Destroy a BE and all of its children datasets and snapshots. 1340*13013Sglenn.lagasse@oracle.com * This function is called for both global BEs and non-global BEs. 1341*13013Sglenn.lagasse@oracle.com * The root dataset of either the global BE or non-global BE to be 1342*13013Sglenn.lagasse@oracle.com * destroyed is passed in. 1343*13013Sglenn.lagasse@oracle.com * Parameters: 1344*13013Sglenn.lagasse@oracle.com * root_ds - pointer to the name of the root dataset of the 1345*13013Sglenn.lagasse@oracle.com * BE to destroy. 1346*13013Sglenn.lagasse@oracle.com * dd - pointer to a be_destroy_data_t structure. 1347*13013Sglenn.lagasse@oracle.com * 1348*13013Sglenn.lagasse@oracle.com * Return: 1349*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 1350*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1351*13013Sglenn.lagasse@oracle.com * Scope: 1352*13013Sglenn.lagasse@oracle.com * Private 1353*13013Sglenn.lagasse@oracle.com */ 1354*13013Sglenn.lagasse@oracle.com static int 1355*13013Sglenn.lagasse@oracle.com _be_destroy(const char *root_ds, be_destroy_data_t *dd) 1356*13013Sglenn.lagasse@oracle.com { 1357*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp = NULL; 1358*13013Sglenn.lagasse@oracle.com char origin[MAXPATHLEN]; 1359*13013Sglenn.lagasse@oracle.com char parent[MAXPATHLEN]; 1360*13013Sglenn.lagasse@oracle.com char *snap = NULL; 1361*13013Sglenn.lagasse@oracle.com boolean_t has_origin = B_FALSE; 1362*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 1363*13013Sglenn.lagasse@oracle.com 1364*13013Sglenn.lagasse@oracle.com /* Get handle to BE's root dataset */ 1365*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == 1366*13013Sglenn.lagasse@oracle.com NULL) { 1367*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1368*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), root_ds, 1369*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1370*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1371*13013Sglenn.lagasse@oracle.com } 1372*13013Sglenn.lagasse@oracle.com 1373*13013Sglenn.lagasse@oracle.com /* 1374*13013Sglenn.lagasse@oracle.com * Demote this BE in case it has dependent clones. This call 1375*13013Sglenn.lagasse@oracle.com * will end up closing the zfs handle passed in whether it 1376*13013Sglenn.lagasse@oracle.com * succeeds or fails. 1377*13013Sglenn.lagasse@oracle.com */ 1378*13013Sglenn.lagasse@oracle.com if (be_demote_callback(zhp, NULL) != 0) { 1379*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: " 1380*13013Sglenn.lagasse@oracle.com "failed to demote BE %s\n"), root_ds); 1381*13013Sglenn.lagasse@oracle.com return (BE_ERR_DEMOTE); 1382*13013Sglenn.lagasse@oracle.com } 1383*13013Sglenn.lagasse@oracle.com 1384*13013Sglenn.lagasse@oracle.com /* Get handle to BE's root dataset */ 1385*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) == 1386*13013Sglenn.lagasse@oracle.com NULL) { 1387*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1388*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), root_ds, 1389*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1390*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1391*13013Sglenn.lagasse@oracle.com } 1392*13013Sglenn.lagasse@oracle.com 1393*13013Sglenn.lagasse@oracle.com /* 1394*13013Sglenn.lagasse@oracle.com * Get the origin of this BE's root dataset. This will be used 1395*13013Sglenn.lagasse@oracle.com * later to destroy the snapshots originally used to create this BE. 1396*13013Sglenn.lagasse@oracle.com */ 1397*13013Sglenn.lagasse@oracle.com if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, 1398*13013Sglenn.lagasse@oracle.com NULL, 0, B_FALSE) == 0) { 1399*13013Sglenn.lagasse@oracle.com (void) strlcpy(parent, origin, sizeof (parent)); 1400*13013Sglenn.lagasse@oracle.com if (be_get_snap(parent, &snap) != BE_SUCCESS) { 1401*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1402*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1403*13013Sglenn.lagasse@oracle.com "get snapshot name from origin %s\n"), origin); 1404*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 1405*13013Sglenn.lagasse@oracle.com } 1406*13013Sglenn.lagasse@oracle.com has_origin = B_TRUE; 1407*13013Sglenn.lagasse@oracle.com } 1408*13013Sglenn.lagasse@oracle.com 1409*13013Sglenn.lagasse@oracle.com /* 1410*13013Sglenn.lagasse@oracle.com * Destroy the BE's root and its hierarchical children. This call 1411*13013Sglenn.lagasse@oracle.com * will end up closing the zfs handle passed in whether it succeeds 1412*13013Sglenn.lagasse@oracle.com * or fails. 1413*13013Sglenn.lagasse@oracle.com */ 1414*13013Sglenn.lagasse@oracle.com if (be_destroy_callback(zhp, dd) != 0) { 1415*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1416*13013Sglenn.lagasse@oracle.com "destroy BE %s\n"), root_ds); 1417*13013Sglenn.lagasse@oracle.com return (BE_ERR_DESTROY); 1418*13013Sglenn.lagasse@oracle.com } 1419*13013Sglenn.lagasse@oracle.com 1420*13013Sglenn.lagasse@oracle.com /* If BE has an origin */ 1421*13013Sglenn.lagasse@oracle.com if (has_origin) { 1422*13013Sglenn.lagasse@oracle.com 1423*13013Sglenn.lagasse@oracle.com /* 1424*13013Sglenn.lagasse@oracle.com * If origin snapshot doesn't have any other 1425*13013Sglenn.lagasse@oracle.com * dependents, delete the origin. 1426*13013Sglenn.lagasse@oracle.com */ 1427*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, origin, ZFS_TYPE_SNAPSHOT)) == 1428*13013Sglenn.lagasse@oracle.com NULL) { 1429*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1430*13013Sglenn.lagasse@oracle.com "open BE's origin (%s): %s\n"), origin, 1431*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1432*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1433*13013Sglenn.lagasse@oracle.com return (ret); 1434*13013Sglenn.lagasse@oracle.com } 1435*13013Sglenn.lagasse@oracle.com 1436*13013Sglenn.lagasse@oracle.com /* If origin has dependents, don't delete it. */ 1437*13013Sglenn.lagasse@oracle.com if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) != 0) { 1438*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1439*13013Sglenn.lagasse@oracle.com return (ret); 1440*13013Sglenn.lagasse@oracle.com } 1441*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1442*13013Sglenn.lagasse@oracle.com 1443*13013Sglenn.lagasse@oracle.com /* Get handle to BE's parent's root dataset */ 1444*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, parent, ZFS_TYPE_FILESYSTEM)) == 1445*13013Sglenn.lagasse@oracle.com NULL) { 1446*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1447*13013Sglenn.lagasse@oracle.com "open BE's parent root dataset (%s): %s\n"), parent, 1448*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1449*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1450*13013Sglenn.lagasse@oracle.com return (ret); 1451*13013Sglenn.lagasse@oracle.com } 1452*13013Sglenn.lagasse@oracle.com 1453*13013Sglenn.lagasse@oracle.com /* Destroy the snapshot origin used to create this BE. */ 1454*13013Sglenn.lagasse@oracle.com /* 1455*13013Sglenn.lagasse@oracle.com * The boolean set to B_FALSE and passed to zfs_destroy_snaps() 1456*13013Sglenn.lagasse@oracle.com * tells zfs to process and destroy the snapshots now. 1457*13013Sglenn.lagasse@oracle.com * Otherwise the call will potentially return where the 1458*13013Sglenn.lagasse@oracle.com * snapshot isn't actually destroyed yet, and ZFS is waiting 1459*13013Sglenn.lagasse@oracle.com * until all the references to the snapshot have been 1460*13013Sglenn.lagasse@oracle.com * released before actually destroying the snapshot. 1461*13013Sglenn.lagasse@oracle.com */ 1462*13013Sglenn.lagasse@oracle.com if (zfs_destroy_snaps(zhp, snap, B_FALSE) != 0) { 1463*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy: failed to " 1464*13013Sglenn.lagasse@oracle.com "destroy original snapshots used to create " 1465*13013Sglenn.lagasse@oracle.com "BE: %s\n"), libzfs_error_description(g_zfs)); 1466*13013Sglenn.lagasse@oracle.com 1467*13013Sglenn.lagasse@oracle.com /* 1468*13013Sglenn.lagasse@oracle.com * If a failure happened because a clone exists, 1469*13013Sglenn.lagasse@oracle.com * don't return a failure to the user. Above, we're 1470*13013Sglenn.lagasse@oracle.com * only checking that the root dataset's origin 1471*13013Sglenn.lagasse@oracle.com * snapshot doesn't have dependent clones, but its 1472*13013Sglenn.lagasse@oracle.com * possible that a subordinate dataset origin snapshot 1473*13013Sglenn.lagasse@oracle.com * has a clone. We really need to check for that 1474*13013Sglenn.lagasse@oracle.com * before trying to destroy the origin snapshot. 1475*13013Sglenn.lagasse@oracle.com */ 1476*13013Sglenn.lagasse@oracle.com if (libzfs_errno(g_zfs) != EZFS_EXISTS) { 1477*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1478*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1479*13013Sglenn.lagasse@oracle.com return (ret); 1480*13013Sglenn.lagasse@oracle.com } 1481*13013Sglenn.lagasse@oracle.com } 1482*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1483*13013Sglenn.lagasse@oracle.com } 1484*13013Sglenn.lagasse@oracle.com 1485*13013Sglenn.lagasse@oracle.com return (ret); 1486*13013Sglenn.lagasse@oracle.com } 1487*13013Sglenn.lagasse@oracle.com 1488*13013Sglenn.lagasse@oracle.com /* 1489*13013Sglenn.lagasse@oracle.com * Function: be_destroy_zones 1490*13013Sglenn.lagasse@oracle.com * Description: Find valid zone's and call be_destroy_zone_roots to destroy its 1491*13013Sglenn.lagasse@oracle.com * corresponding dataset and all of its children datasets 1492*13013Sglenn.lagasse@oracle.com * and snapshots. 1493*13013Sglenn.lagasse@oracle.com * Parameters: 1494*13013Sglenn.lagasse@oracle.com * be_name - name of global boot environment being destroyed 1495*13013Sglenn.lagasse@oracle.com * be_root_ds - root dataset of global boot environment being 1496*13013Sglenn.lagasse@oracle.com * destroyed. 1497*13013Sglenn.lagasse@oracle.com * dd - be_destroy_data_t pointer 1498*13013Sglenn.lagasse@oracle.com * Return: 1499*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 1500*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1501*13013Sglenn.lagasse@oracle.com * Scope: 1502*13013Sglenn.lagasse@oracle.com * Private 1503*13013Sglenn.lagasse@oracle.com * 1504*13013Sglenn.lagasse@oracle.com * NOTES - Requires that the BE being deleted has no dependent BEs. If it 1505*13013Sglenn.lagasse@oracle.com * does, the destroy will fail. 1506*13013Sglenn.lagasse@oracle.com */ 1507*13013Sglenn.lagasse@oracle.com static int 1508*13013Sglenn.lagasse@oracle.com be_destroy_zones(char *be_name, char *be_root_ds, be_destroy_data_t *dd) 1509*13013Sglenn.lagasse@oracle.com { 1510*13013Sglenn.lagasse@oracle.com int i; 1511*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 1512*13013Sglenn.lagasse@oracle.com int force_umnt = BE_UNMOUNT_FLAG_NULL; 1513*13013Sglenn.lagasse@oracle.com char *zonepath = NULL; 1514*13013Sglenn.lagasse@oracle.com char *zonename = NULL; 1515*13013Sglenn.lagasse@oracle.com char *zonepath_ds = NULL; 1516*13013Sglenn.lagasse@oracle.com char *mp = NULL; 1517*13013Sglenn.lagasse@oracle.com zoneList_t zlist = NULL; 1518*13013Sglenn.lagasse@oracle.com zoneBrandList_t *brands = NULL; 1519*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp = NULL; 1520*13013Sglenn.lagasse@oracle.com 1521*13013Sglenn.lagasse@oracle.com /* If zones are not implemented, then get out. */ 1522*13013Sglenn.lagasse@oracle.com if (!z_zones_are_implemented()) { 1523*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 1524*13013Sglenn.lagasse@oracle.com } 1525*13013Sglenn.lagasse@oracle.com 1526*13013Sglenn.lagasse@oracle.com /* Get list of supported brands */ 1527*13013Sglenn.lagasse@oracle.com if ((brands = be_get_supported_brandlist()) == NULL) { 1528*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zones: " 1529*13013Sglenn.lagasse@oracle.com "no supported brands\n")); 1530*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 1531*13013Sglenn.lagasse@oracle.com } 1532*13013Sglenn.lagasse@oracle.com 1533*13013Sglenn.lagasse@oracle.com /* Get handle to BE's root dataset */ 1534*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, be_root_ds, ZFS_TYPE_FILESYSTEM)) == 1535*13013Sglenn.lagasse@oracle.com NULL) { 1536*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zones: failed to " 1537*13013Sglenn.lagasse@oracle.com "open BE root dataset (%s): %s\n"), be_root_ds, 1538*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1539*13013Sglenn.lagasse@oracle.com z_free_brand_list(brands); 1540*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1541*13013Sglenn.lagasse@oracle.com } 1542*13013Sglenn.lagasse@oracle.com 1543*13013Sglenn.lagasse@oracle.com /* 1544*13013Sglenn.lagasse@oracle.com * If the global BE is not mounted, we must mount it here to 1545*13013Sglenn.lagasse@oracle.com * gather data about the non-global zones in it. 1546*13013Sglenn.lagasse@oracle.com */ 1547*13013Sglenn.lagasse@oracle.com if (!zfs_is_mounted(zhp, &mp)) { 1548*13013Sglenn.lagasse@oracle.com if ((ret = _be_mount(be_name, &mp, 1549*13013Sglenn.lagasse@oracle.com BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) { 1550*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zones: failed to " 1551*13013Sglenn.lagasse@oracle.com "mount the BE (%s) for zones processing.\n"), 1552*13013Sglenn.lagasse@oracle.com be_name); 1553*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1554*13013Sglenn.lagasse@oracle.com z_free_brand_list(brands); 1555*13013Sglenn.lagasse@oracle.com return (ret); 1556*13013Sglenn.lagasse@oracle.com } 1557*13013Sglenn.lagasse@oracle.com } 1558*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1559*13013Sglenn.lagasse@oracle.com 1560*13013Sglenn.lagasse@oracle.com z_set_zone_root(mp); 1561*13013Sglenn.lagasse@oracle.com free(mp); 1562*13013Sglenn.lagasse@oracle.com 1563*13013Sglenn.lagasse@oracle.com /* Get list of supported zones. */ 1564*13013Sglenn.lagasse@oracle.com if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) { 1565*13013Sglenn.lagasse@oracle.com z_free_brand_list(brands); 1566*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 1567*13013Sglenn.lagasse@oracle.com } 1568*13013Sglenn.lagasse@oracle.com 1569*13013Sglenn.lagasse@oracle.com /* Unmount the BE before destroying the zones in it. */ 1570*13013Sglenn.lagasse@oracle.com if (dd->force_unmount) 1571*13013Sglenn.lagasse@oracle.com force_umnt = BE_UNMOUNT_FLAG_FORCE; 1572*13013Sglenn.lagasse@oracle.com if ((ret = _be_unmount(be_name, force_umnt)) != BE_SUCCESS) { 1573*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zones: failed to " 1574*13013Sglenn.lagasse@oracle.com "unmount the BE (%s)\n"), be_name); 1575*13013Sglenn.lagasse@oracle.com goto done; 1576*13013Sglenn.lagasse@oracle.com } 1577*13013Sglenn.lagasse@oracle.com 1578*13013Sglenn.lagasse@oracle.com /* Iterate through the zones and destroy them. */ 1579*13013Sglenn.lagasse@oracle.com for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) { 1580*13013Sglenn.lagasse@oracle.com 1581*13013Sglenn.lagasse@oracle.com /* Skip zones that aren't at least installed */ 1582*13013Sglenn.lagasse@oracle.com if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED) 1583*13013Sglenn.lagasse@oracle.com continue; 1584*13013Sglenn.lagasse@oracle.com 1585*13013Sglenn.lagasse@oracle.com zonepath = z_zlist_get_zonepath(zlist, i); 1586*13013Sglenn.lagasse@oracle.com 1587*13013Sglenn.lagasse@oracle.com /* 1588*13013Sglenn.lagasse@oracle.com * Get the dataset of this zonepath. If its not 1589*13013Sglenn.lagasse@oracle.com * a dataset, skip it. 1590*13013Sglenn.lagasse@oracle.com */ 1591*13013Sglenn.lagasse@oracle.com if ((zonepath_ds = be_get_ds_from_dir(zonepath)) == NULL) 1592*13013Sglenn.lagasse@oracle.com continue; 1593*13013Sglenn.lagasse@oracle.com 1594*13013Sglenn.lagasse@oracle.com /* 1595*13013Sglenn.lagasse@oracle.com * Check if this zone is supported based on the 1596*13013Sglenn.lagasse@oracle.com * dataset of its zonepath. 1597*13013Sglenn.lagasse@oracle.com */ 1598*13013Sglenn.lagasse@oracle.com if (!be_zone_supported(zonepath_ds)) { 1599*13013Sglenn.lagasse@oracle.com free(zonepath_ds); 1600*13013Sglenn.lagasse@oracle.com continue; 1601*13013Sglenn.lagasse@oracle.com } 1602*13013Sglenn.lagasse@oracle.com 1603*13013Sglenn.lagasse@oracle.com /* Find the zone BE root datasets for this zone. */ 1604*13013Sglenn.lagasse@oracle.com if ((ret = be_destroy_zone_roots(zonepath_ds, dd)) 1605*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 1606*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zones: failed to " 1607*13013Sglenn.lagasse@oracle.com "find and destroy zone roots for zone %s\n"), 1608*13013Sglenn.lagasse@oracle.com zonename); 1609*13013Sglenn.lagasse@oracle.com free(zonepath_ds); 1610*13013Sglenn.lagasse@oracle.com goto done; 1611*13013Sglenn.lagasse@oracle.com } 1612*13013Sglenn.lagasse@oracle.com free(zonepath_ds); 1613*13013Sglenn.lagasse@oracle.com } 1614*13013Sglenn.lagasse@oracle.com 1615*13013Sglenn.lagasse@oracle.com done: 1616*13013Sglenn.lagasse@oracle.com z_free_brand_list(brands); 1617*13013Sglenn.lagasse@oracle.com z_free_zone_list(zlist); 1618*13013Sglenn.lagasse@oracle.com 1619*13013Sglenn.lagasse@oracle.com return (ret); 1620*13013Sglenn.lagasse@oracle.com } 1621*13013Sglenn.lagasse@oracle.com 1622*13013Sglenn.lagasse@oracle.com /* 1623*13013Sglenn.lagasse@oracle.com * Function: be_destroy_zone_roots 1624*13013Sglenn.lagasse@oracle.com * Description: This function will open the zone's root container dataset 1625*13013Sglenn.lagasse@oracle.com * and iterate the datasets within, looking for roots that 1626*13013Sglenn.lagasse@oracle.com * belong to the given global BE and destroying them. 1627*13013Sglenn.lagasse@oracle.com * If no other zone roots remain in the zone's root container 1628*13013Sglenn.lagasse@oracle.com * dataset, the function will destroy it and the zone's 1629*13013Sglenn.lagasse@oracle.com * zonepath dataset as well. 1630*13013Sglenn.lagasse@oracle.com * Parameters: 1631*13013Sglenn.lagasse@oracle.com * zonepath_ds - pointer to zone's zonepath dataset. 1632*13013Sglenn.lagasse@oracle.com * dd - pointer to a linked destroy data. 1633*13013Sglenn.lagasse@oracle.com * Returns: 1634*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 1635*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1636*13013Sglenn.lagasse@oracle.com * Scope: 1637*13013Sglenn.lagasse@oracle.com * Private 1638*13013Sglenn.lagasse@oracle.com */ 1639*13013Sglenn.lagasse@oracle.com static int 1640*13013Sglenn.lagasse@oracle.com be_destroy_zone_roots(char *zonepath_ds, be_destroy_data_t *dd) 1641*13013Sglenn.lagasse@oracle.com { 1642*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp; 1643*13013Sglenn.lagasse@oracle.com char zone_container_ds[MAXPATHLEN]; 1644*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 1645*13013Sglenn.lagasse@oracle.com 1646*13013Sglenn.lagasse@oracle.com /* Generate string for the root container dataset for this zone. */ 1647*13013Sglenn.lagasse@oracle.com be_make_container_ds(zonepath_ds, zone_container_ds, 1648*13013Sglenn.lagasse@oracle.com sizeof (zone_container_ds)); 1649*13013Sglenn.lagasse@oracle.com 1650*13013Sglenn.lagasse@oracle.com /* Get handle to this zone's root container dataset. */ 1651*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM)) 1652*13013Sglenn.lagasse@oracle.com == NULL) { 1653*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots: failed to " 1654*13013Sglenn.lagasse@oracle.com "open zone root container dataset (%s): %s\n"), 1655*13013Sglenn.lagasse@oracle.com zone_container_ds, libzfs_error_description(g_zfs)); 1656*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1657*13013Sglenn.lagasse@oracle.com } 1658*13013Sglenn.lagasse@oracle.com 1659*13013Sglenn.lagasse@oracle.com /* 1660*13013Sglenn.lagasse@oracle.com * Iterate through all of this zone's BEs, destroying the ones 1661*13013Sglenn.lagasse@oracle.com * that belong to the parent global BE. 1662*13013Sglenn.lagasse@oracle.com */ 1663*13013Sglenn.lagasse@oracle.com if ((ret = zfs_iter_filesystems(zhp, be_destroy_zone_roots_callback, 1664*13013Sglenn.lagasse@oracle.com dd)) != 0) { 1665*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots: failed to " 1666*13013Sglenn.lagasse@oracle.com "destroy zone roots under zonepath dataset %s: %s\n"), 1667*13013Sglenn.lagasse@oracle.com zonepath_ds, libzfs_error_description(g_zfs)); 1668*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1669*13013Sglenn.lagasse@oracle.com return (ret); 1670*13013Sglenn.lagasse@oracle.com } 1671*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1672*13013Sglenn.lagasse@oracle.com 1673*13013Sglenn.lagasse@oracle.com /* Get handle to this zone's root container dataset. */ 1674*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, zone_container_ds, ZFS_TYPE_FILESYSTEM)) 1675*13013Sglenn.lagasse@oracle.com == NULL) { 1676*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots: failed to " 1677*13013Sglenn.lagasse@oracle.com "open zone root container dataset (%s): %s\n"), 1678*13013Sglenn.lagasse@oracle.com zone_container_ds, libzfs_error_description(g_zfs)); 1679*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1680*13013Sglenn.lagasse@oracle.com } 1681*13013Sglenn.lagasse@oracle.com 1682*13013Sglenn.lagasse@oracle.com /* 1683*13013Sglenn.lagasse@oracle.com * If there are no more zone roots in this zone's root container, 1684*13013Sglenn.lagasse@oracle.com * dataset, destroy it and the zonepath dataset as well. 1685*13013Sglenn.lagasse@oracle.com */ 1686*13013Sglenn.lagasse@oracle.com if (zfs_iter_filesystems(zhp, be_zone_root_exists_callback, NULL) 1687*13013Sglenn.lagasse@oracle.com == 0) { 1688*13013Sglenn.lagasse@oracle.com /* Destroy the zone root container dataset */ 1689*13013Sglenn.lagasse@oracle.com if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 || 1690*13013Sglenn.lagasse@oracle.com zfs_destroy(zhp, B_FALSE) != 0) { 1691*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots: failed to " 1692*13013Sglenn.lagasse@oracle.com "destroy zone root container dataset (%s): %s\n"), 1693*13013Sglenn.lagasse@oracle.com zone_container_ds, libzfs_error_description(g_zfs)); 1694*13013Sglenn.lagasse@oracle.com goto done; 1695*13013Sglenn.lagasse@oracle.com } 1696*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1697*13013Sglenn.lagasse@oracle.com 1698*13013Sglenn.lagasse@oracle.com /* Get handle to zonepath dataset */ 1699*13013Sglenn.lagasse@oracle.com if ((zhp = zfs_open(g_zfs, zonepath_ds, ZFS_TYPE_FILESYSTEM)) 1700*13013Sglenn.lagasse@oracle.com == NULL) { 1701*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots: failed to " 1702*13013Sglenn.lagasse@oracle.com "open zonepath dataset (%s): %s\n"), 1703*13013Sglenn.lagasse@oracle.com zonepath_ds, libzfs_error_description(g_zfs)); 1704*13013Sglenn.lagasse@oracle.com goto done; 1705*13013Sglenn.lagasse@oracle.com } 1706*13013Sglenn.lagasse@oracle.com 1707*13013Sglenn.lagasse@oracle.com /* Destroy zonepath dataset */ 1708*13013Sglenn.lagasse@oracle.com if (zfs_unmount(zhp, NULL, MS_FORCE) != 0 || 1709*13013Sglenn.lagasse@oracle.com zfs_destroy(zhp, B_FALSE) != 0) { 1710*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots: " 1711*13013Sglenn.lagasse@oracle.com "failed to destroy zonepath dataest %s: %s\n"), 1712*13013Sglenn.lagasse@oracle.com zonepath_ds, libzfs_error_description(g_zfs)); 1713*13013Sglenn.lagasse@oracle.com goto done; 1714*13013Sglenn.lagasse@oracle.com } 1715*13013Sglenn.lagasse@oracle.com } 1716*13013Sglenn.lagasse@oracle.com 1717*13013Sglenn.lagasse@oracle.com done: 1718*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1719*13013Sglenn.lagasse@oracle.com return (ret); 1720*13013Sglenn.lagasse@oracle.com } 1721*13013Sglenn.lagasse@oracle.com 1722*13013Sglenn.lagasse@oracle.com /* 1723*13013Sglenn.lagasse@oracle.com * Function: be_destroy_zone_roots_callback 1724*13013Sglenn.lagasse@oracle.com * Description: This function is used as a callback to iterate over all of 1725*13013Sglenn.lagasse@oracle.com * a zone's root datasets, finding the one's that 1726*13013Sglenn.lagasse@oracle.com * correspond to the current BE. The name's 1727*13013Sglenn.lagasse@oracle.com * of the zone root datasets are then destroyed by _be_destroy(). 1728*13013Sglenn.lagasse@oracle.com * Parameters: 1729*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer to current dataset being processed 1730*13013Sglenn.lagasse@oracle.com * data - be_destroy_data_t pointer 1731*13013Sglenn.lagasse@oracle.com * Returns: 1732*13013Sglenn.lagasse@oracle.com * 0 - Success 1733*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1734*13013Sglenn.lagasse@oracle.com * Scope: 1735*13013Sglenn.lagasse@oracle.com * Private 1736*13013Sglenn.lagasse@oracle.com */ 1737*13013Sglenn.lagasse@oracle.com static int 1738*13013Sglenn.lagasse@oracle.com be_destroy_zone_roots_callback(zfs_handle_t *zhp, void *data) 1739*13013Sglenn.lagasse@oracle.com { 1740*13013Sglenn.lagasse@oracle.com be_destroy_data_t *dd = data; 1741*13013Sglenn.lagasse@oracle.com uuid_t parent_uuid = { 0 }; 1742*13013Sglenn.lagasse@oracle.com int ret = 0; 1743*13013Sglenn.lagasse@oracle.com 1744*13013Sglenn.lagasse@oracle.com if (be_zone_get_parent_uuid(zfs_get_name(zhp), &parent_uuid) 1745*13013Sglenn.lagasse@oracle.com != BE_SUCCESS) { 1746*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_roots_callback: " 1747*13013Sglenn.lagasse@oracle.com "could not get parentuuid for zone root dataset %s\n"), 1748*13013Sglenn.lagasse@oracle.com zfs_get_name(zhp)); 1749*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1750*13013Sglenn.lagasse@oracle.com return (0); 1751*13013Sglenn.lagasse@oracle.com } 1752*13013Sglenn.lagasse@oracle.com 1753*13013Sglenn.lagasse@oracle.com if (uuid_compare(dd->gz_be_uuid, parent_uuid) == 0) { 1754*13013Sglenn.lagasse@oracle.com /* 1755*13013Sglenn.lagasse@oracle.com * Found a zone root dataset belonging to the parent 1756*13013Sglenn.lagasse@oracle.com * BE being destroyed. Destroy this zone BE. 1757*13013Sglenn.lagasse@oracle.com */ 1758*13013Sglenn.lagasse@oracle.com if ((ret = _be_destroy(zfs_get_name(zhp), dd)) != BE_SUCCESS) { 1759*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_zone_root_callback: " 1760*13013Sglenn.lagasse@oracle.com "failed to destroy zone root %s\n"), 1761*13013Sglenn.lagasse@oracle.com zfs_get_name(zhp)); 1762*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1763*13013Sglenn.lagasse@oracle.com return (ret); 1764*13013Sglenn.lagasse@oracle.com } 1765*13013Sglenn.lagasse@oracle.com } 1766*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 1767*13013Sglenn.lagasse@oracle.com 1768*13013Sglenn.lagasse@oracle.com return (ret); 1769*13013Sglenn.lagasse@oracle.com } 1770*13013Sglenn.lagasse@oracle.com 1771*13013Sglenn.lagasse@oracle.com /* 1772*13013Sglenn.lagasse@oracle.com * Function: be_copy_zones 1773*13013Sglenn.lagasse@oracle.com * Description: Find valid zones and clone them to create their 1774*13013Sglenn.lagasse@oracle.com * corresponding datasets for the BE being created. 1775*13013Sglenn.lagasse@oracle.com * Parameters: 1776*13013Sglenn.lagasse@oracle.com * obe_name - name of source global BE being copied. 1777*13013Sglenn.lagasse@oracle.com * obe_root_ds - root dataset of source global BE being copied. 1778*13013Sglenn.lagasse@oracle.com * nbe_root_ds - root dataset of target global BE. 1779*13013Sglenn.lagasse@oracle.com * Return: 1780*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 1781*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 1782*13013Sglenn.lagasse@oracle.com * Scope: 1783*13013Sglenn.lagasse@oracle.com * Private 1784*13013Sglenn.lagasse@oracle.com */ 1785*13013Sglenn.lagasse@oracle.com static int 1786*13013Sglenn.lagasse@oracle.com be_copy_zones(char *obe_name, char *obe_root_ds, char *nbe_root_ds) 1787*13013Sglenn.lagasse@oracle.com { 1788*13013Sglenn.lagasse@oracle.com int i, num_retries; 1789*13013Sglenn.lagasse@oracle.com int ret = BE_SUCCESS; 1790*13013Sglenn.lagasse@oracle.com int iret = 0; 1791*13013Sglenn.lagasse@oracle.com char *zonename = NULL; 1792*13013Sglenn.lagasse@oracle.com char *zonepath = NULL; 1793*13013Sglenn.lagasse@oracle.com char *zone_be_name = NULL; 1794*13013Sglenn.lagasse@oracle.com char *temp_mntpt = NULL; 1795*13013Sglenn.lagasse@oracle.com char *new_zone_be_name = NULL; 1796*13013Sglenn.lagasse@oracle.com char zoneroot[MAXPATHLEN]; 1797*13013Sglenn.lagasse@oracle.com char zoneroot_ds[MAXPATHLEN]; 1798*13013Sglenn.lagasse@oracle.com char zone_container_ds[MAXPATHLEN]; 1799*13013Sglenn.lagasse@oracle.com char new_zoneroot_ds[MAXPATHLEN]; 1800*13013Sglenn.lagasse@oracle.com char ss[MAXPATHLEN]; 1801*13013Sglenn.lagasse@oracle.com uuid_t uu = { 0 }; 1802*13013Sglenn.lagasse@oracle.com char uu_string[UUID_PRINTABLE_STRING_LENGTH] = { 0 }; 1803*13013Sglenn.lagasse@oracle.com be_transaction_data_t bt = { 0 }; 1804*13013Sglenn.lagasse@oracle.com zfs_handle_t *obe_zhp = NULL; 1805*13013Sglenn.lagasse@oracle.com zfs_handle_t *nbe_zhp = NULL; 1806*13013Sglenn.lagasse@oracle.com zfs_handle_t *z_zhp = NULL; 1807*13013Sglenn.lagasse@oracle.com zoneList_t zlist = NULL; 1808*13013Sglenn.lagasse@oracle.com zoneBrandList_t *brands = NULL; 1809*13013Sglenn.lagasse@oracle.com boolean_t mounted_here = B_FALSE; 1810*13013Sglenn.lagasse@oracle.com char *snap_name = NULL; 1811*13013Sglenn.lagasse@oracle.com 1812*13013Sglenn.lagasse@oracle.com /* If zones are not implemented, then get out. */ 1813*13013Sglenn.lagasse@oracle.com if (!z_zones_are_implemented()) { 1814*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 1815*13013Sglenn.lagasse@oracle.com } 1816*13013Sglenn.lagasse@oracle.com 1817*13013Sglenn.lagasse@oracle.com /* Get list of supported brands */ 1818*13013Sglenn.lagasse@oracle.com if ((brands = be_get_supported_brandlist()) == NULL) { 1819*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1820*13013Sglenn.lagasse@oracle.com "no supported brands\n")); 1821*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 1822*13013Sglenn.lagasse@oracle.com } 1823*13013Sglenn.lagasse@oracle.com 1824*13013Sglenn.lagasse@oracle.com /* Get handle to origin BE's root dataset */ 1825*13013Sglenn.lagasse@oracle.com if ((obe_zhp = zfs_open(g_zfs, obe_root_ds, ZFS_TYPE_FILESYSTEM)) 1826*13013Sglenn.lagasse@oracle.com == NULL) { 1827*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: failed to open " 1828*13013Sglenn.lagasse@oracle.com "the origin BE root dataset (%s) for zones processing: " 1829*13013Sglenn.lagasse@oracle.com "%s\n"), obe_root_ds, libzfs_error_description(g_zfs)); 1830*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1831*13013Sglenn.lagasse@oracle.com } 1832*13013Sglenn.lagasse@oracle.com 1833*13013Sglenn.lagasse@oracle.com /* Get handle to newly cloned BE's root dataset */ 1834*13013Sglenn.lagasse@oracle.com if ((nbe_zhp = zfs_open(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM)) 1835*13013Sglenn.lagasse@oracle.com == NULL) { 1836*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: failed to open " 1837*13013Sglenn.lagasse@oracle.com "the new BE root dataset (%s): %s\n"), nbe_root_ds, 1838*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 1839*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(obe_zhp); 1840*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 1841*13013Sglenn.lagasse@oracle.com } 1842*13013Sglenn.lagasse@oracle.com 1843*13013Sglenn.lagasse@oracle.com /* Get the uuid of the newly cloned parent BE. */ 1844*13013Sglenn.lagasse@oracle.com if (be_get_uuid(zfs_get_name(nbe_zhp), &uu) != BE_SUCCESS) { 1845*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1846*13013Sglenn.lagasse@oracle.com "failed to get uuid for BE root " 1847*13013Sglenn.lagasse@oracle.com "dataset %s\n"), zfs_get_name(nbe_zhp)); 1848*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(nbe_zhp); 1849*13013Sglenn.lagasse@oracle.com goto done; 1850*13013Sglenn.lagasse@oracle.com } 1851*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(nbe_zhp); 1852*13013Sglenn.lagasse@oracle.com uuid_unparse(uu, uu_string); 1853*13013Sglenn.lagasse@oracle.com 1854*13013Sglenn.lagasse@oracle.com /* 1855*13013Sglenn.lagasse@oracle.com * If the origin BE is not mounted, we must mount it here to 1856*13013Sglenn.lagasse@oracle.com * gather data about the non-global zones in it. 1857*13013Sglenn.lagasse@oracle.com */ 1858*13013Sglenn.lagasse@oracle.com if (!zfs_is_mounted(obe_zhp, &temp_mntpt)) { 1859*13013Sglenn.lagasse@oracle.com if ((ret = _be_mount(obe_name, &temp_mntpt, 1860*13013Sglenn.lagasse@oracle.com BE_MOUNT_FLAG_NULL)) != BE_SUCCESS) { 1861*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: failed to " 1862*13013Sglenn.lagasse@oracle.com "mount the BE (%s) for zones procesing.\n"), 1863*13013Sglenn.lagasse@oracle.com obe_name); 1864*13013Sglenn.lagasse@oracle.com goto done; 1865*13013Sglenn.lagasse@oracle.com } 1866*13013Sglenn.lagasse@oracle.com mounted_here = B_TRUE; 1867*13013Sglenn.lagasse@oracle.com } 1868*13013Sglenn.lagasse@oracle.com 1869*13013Sglenn.lagasse@oracle.com z_set_zone_root(temp_mntpt); 1870*13013Sglenn.lagasse@oracle.com 1871*13013Sglenn.lagasse@oracle.com /* Get list of supported zones. */ 1872*13013Sglenn.lagasse@oracle.com if ((zlist = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) { 1873*13013Sglenn.lagasse@oracle.com ret = BE_SUCCESS; 1874*13013Sglenn.lagasse@oracle.com goto done; 1875*13013Sglenn.lagasse@oracle.com } 1876*13013Sglenn.lagasse@oracle.com 1877*13013Sglenn.lagasse@oracle.com for (i = 0; (zonename = z_zlist_get_zonename(zlist, i)) != NULL; i++) { 1878*13013Sglenn.lagasse@oracle.com 1879*13013Sglenn.lagasse@oracle.com be_fs_list_data_t fld = { 0 }; 1880*13013Sglenn.lagasse@oracle.com char zonepath_ds[MAXPATHLEN]; 1881*13013Sglenn.lagasse@oracle.com char *ds = NULL; 1882*13013Sglenn.lagasse@oracle.com 1883*13013Sglenn.lagasse@oracle.com /* Get zonepath of zone */ 1884*13013Sglenn.lagasse@oracle.com zonepath = z_zlist_get_zonepath(zlist, i); 1885*13013Sglenn.lagasse@oracle.com 1886*13013Sglenn.lagasse@oracle.com /* Skip zones that aren't at least installed */ 1887*13013Sglenn.lagasse@oracle.com if (z_zlist_get_current_state(zlist, i) < ZONE_STATE_INSTALLED) 1888*13013Sglenn.lagasse@oracle.com continue; 1889*13013Sglenn.lagasse@oracle.com 1890*13013Sglenn.lagasse@oracle.com /* 1891*13013Sglenn.lagasse@oracle.com * Get the dataset of this zonepath. If its not 1892*13013Sglenn.lagasse@oracle.com * a dataset, skip it. 1893*13013Sglenn.lagasse@oracle.com */ 1894*13013Sglenn.lagasse@oracle.com if ((ds = be_get_ds_from_dir(zonepath)) == NULL) 1895*13013Sglenn.lagasse@oracle.com continue; 1896*13013Sglenn.lagasse@oracle.com 1897*13013Sglenn.lagasse@oracle.com (void) strlcpy(zonepath_ds, ds, sizeof (zonepath_ds)); 1898*13013Sglenn.lagasse@oracle.com free(ds); 1899*13013Sglenn.lagasse@oracle.com ds = NULL; 1900*13013Sglenn.lagasse@oracle.com 1901*13013Sglenn.lagasse@oracle.com /* Get zoneroot directory */ 1902*13013Sglenn.lagasse@oracle.com be_make_zoneroot(zonepath, zoneroot, sizeof (zoneroot)); 1903*13013Sglenn.lagasse@oracle.com 1904*13013Sglenn.lagasse@oracle.com /* If zonepath dataset not supported, skip it. */ 1905*13013Sglenn.lagasse@oracle.com if (!be_zone_supported(zonepath_ds)) { 1906*13013Sglenn.lagasse@oracle.com continue; 1907*13013Sglenn.lagasse@oracle.com } 1908*13013Sglenn.lagasse@oracle.com 1909*13013Sglenn.lagasse@oracle.com if ((ret = be_find_active_zone_root(obe_zhp, zonepath_ds, 1910*13013Sglenn.lagasse@oracle.com zoneroot_ds, sizeof (zoneroot_ds))) != BE_SUCCESS) { 1911*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1912*13013Sglenn.lagasse@oracle.com "failed to find active zone root for zone %s " 1913*13013Sglenn.lagasse@oracle.com "in BE %s\n"), zonename, obe_name); 1914*13013Sglenn.lagasse@oracle.com goto done; 1915*13013Sglenn.lagasse@oracle.com } 1916*13013Sglenn.lagasse@oracle.com 1917*13013Sglenn.lagasse@oracle.com be_make_container_ds(zonepath_ds, zone_container_ds, 1918*13013Sglenn.lagasse@oracle.com sizeof (zone_container_ds)); 1919*13013Sglenn.lagasse@oracle.com 1920*13013Sglenn.lagasse@oracle.com if ((z_zhp = zfs_open(g_zfs, zoneroot_ds, 1921*13013Sglenn.lagasse@oracle.com ZFS_TYPE_FILESYSTEM)) == NULL) { 1922*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1923*13013Sglenn.lagasse@oracle.com "failed to open zone root dataset (%s): %s\n"), 1924*13013Sglenn.lagasse@oracle.com zoneroot_ds, libzfs_error_description(g_zfs)); 1925*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1926*13013Sglenn.lagasse@oracle.com goto done; 1927*13013Sglenn.lagasse@oracle.com } 1928*13013Sglenn.lagasse@oracle.com 1929*13013Sglenn.lagasse@oracle.com zone_be_name = 1930*13013Sglenn.lagasse@oracle.com be_get_zone_be_name(zoneroot_ds, zone_container_ds); 1931*13013Sglenn.lagasse@oracle.com 1932*13013Sglenn.lagasse@oracle.com if ((new_zone_be_name = be_auto_zone_be_name(zone_container_ds, 1933*13013Sglenn.lagasse@oracle.com zone_be_name)) == NULL) { 1934*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: failed " 1935*13013Sglenn.lagasse@oracle.com "to generate auto name for zone BE.\n")); 1936*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 1937*13013Sglenn.lagasse@oracle.com goto done; 1938*13013Sglenn.lagasse@oracle.com } 1939*13013Sglenn.lagasse@oracle.com 1940*13013Sglenn.lagasse@oracle.com if ((snap_name = be_auto_snap_name()) == NULL) { 1941*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: failed to " 1942*13013Sglenn.lagasse@oracle.com "generate snapshot name for zone BE.\n")); 1943*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 1944*13013Sglenn.lagasse@oracle.com goto done; 1945*13013Sglenn.lagasse@oracle.com } 1946*13013Sglenn.lagasse@oracle.com 1947*13013Sglenn.lagasse@oracle.com (void) snprintf(ss, sizeof (ss), "%s@%s", zoneroot_ds, 1948*13013Sglenn.lagasse@oracle.com snap_name); 1949*13013Sglenn.lagasse@oracle.com 1950*13013Sglenn.lagasse@oracle.com if (zfs_snapshot(g_zfs, ss, B_TRUE, NULL) != 0) { 1951*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1952*13013Sglenn.lagasse@oracle.com "failed to snapshot zone BE (%s): %s\n"), 1953*13013Sglenn.lagasse@oracle.com ss, libzfs_error_description(g_zfs)); 1954*13013Sglenn.lagasse@oracle.com if (libzfs_errno(g_zfs) == EZFS_EXISTS) 1955*13013Sglenn.lagasse@oracle.com ret = BE_ERR_ZONE_SS_EXISTS; 1956*13013Sglenn.lagasse@oracle.com else 1957*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 1958*13013Sglenn.lagasse@oracle.com 1959*13013Sglenn.lagasse@oracle.com goto done; 1960*13013Sglenn.lagasse@oracle.com } 1961*13013Sglenn.lagasse@oracle.com 1962*13013Sglenn.lagasse@oracle.com (void) snprintf(new_zoneroot_ds, sizeof (new_zoneroot_ds), 1963*13013Sglenn.lagasse@oracle.com "%s/%s", zone_container_ds, new_zone_be_name); 1964*13013Sglenn.lagasse@oracle.com 1965*13013Sglenn.lagasse@oracle.com bt.obe_name = zone_be_name; 1966*13013Sglenn.lagasse@oracle.com bt.obe_root_ds = zoneroot_ds; 1967*13013Sglenn.lagasse@oracle.com bt.obe_snap_name = snap_name; 1968*13013Sglenn.lagasse@oracle.com bt.obe_altroot = temp_mntpt; 1969*13013Sglenn.lagasse@oracle.com bt.nbe_name = new_zone_be_name; 1970*13013Sglenn.lagasse@oracle.com bt.nbe_root_ds = new_zoneroot_ds; 1971*13013Sglenn.lagasse@oracle.com 1972*13013Sglenn.lagasse@oracle.com if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { 1973*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1974*13013Sglenn.lagasse@oracle.com "internal error: out of memory\n")); 1975*13013Sglenn.lagasse@oracle.com ret = BE_ERR_NOMEM; 1976*13013Sglenn.lagasse@oracle.com goto done; 1977*13013Sglenn.lagasse@oracle.com } 1978*13013Sglenn.lagasse@oracle.com 1979*13013Sglenn.lagasse@oracle.com /* 1980*13013Sglenn.lagasse@oracle.com * The call to be_clone_fs_callback always closes the 1981*13013Sglenn.lagasse@oracle.com * zfs_handle so there's no need to close z_zhp. 1982*13013Sglenn.lagasse@oracle.com */ 1983*13013Sglenn.lagasse@oracle.com if ((iret = be_clone_fs_callback(z_zhp, &bt)) != 0) { 1984*13013Sglenn.lagasse@oracle.com z_zhp = NULL; 1985*13013Sglenn.lagasse@oracle.com if (iret != BE_ERR_BE_EXISTS) { 1986*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 1987*13013Sglenn.lagasse@oracle.com "failed to create zone BE clone for new " 1988*13013Sglenn.lagasse@oracle.com "zone BE %s\n"), new_zone_be_name); 1989*13013Sglenn.lagasse@oracle.com ret = iret; 1990*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 1991*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 1992*13013Sglenn.lagasse@oracle.com goto done; 1993*13013Sglenn.lagasse@oracle.com } 1994*13013Sglenn.lagasse@oracle.com /* 1995*13013Sglenn.lagasse@oracle.com * We failed to create the new zone BE because a zone 1996*13013Sglenn.lagasse@oracle.com * BE with the auto-name we generated above has since 1997*13013Sglenn.lagasse@oracle.com * come into existence. Regenerate a new auto-name 1998*13013Sglenn.lagasse@oracle.com * and retry. 1999*13013Sglenn.lagasse@oracle.com */ 2000*13013Sglenn.lagasse@oracle.com for (num_retries = 1; 2001*13013Sglenn.lagasse@oracle.com num_retries < BE_AUTO_NAME_MAX_TRY; 2002*13013Sglenn.lagasse@oracle.com num_retries++) { 2003*13013Sglenn.lagasse@oracle.com 2004*13013Sglenn.lagasse@oracle.com /* Sleep 1 before retrying */ 2005*13013Sglenn.lagasse@oracle.com (void) sleep(1); 2006*13013Sglenn.lagasse@oracle.com 2007*13013Sglenn.lagasse@oracle.com /* Generate new auto zone BE name */ 2008*13013Sglenn.lagasse@oracle.com free(new_zone_be_name); 2009*13013Sglenn.lagasse@oracle.com if ((new_zone_be_name = be_auto_zone_be_name( 2010*13013Sglenn.lagasse@oracle.com zone_container_ds, 2011*13013Sglenn.lagasse@oracle.com zone_be_name)) == NULL) { 2012*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2013*13013Sglenn.lagasse@oracle.com "failed to generate auto name " 2014*13013Sglenn.lagasse@oracle.com "for zone BE.\n")); 2015*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 2016*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 2017*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 2018*13013Sglenn.lagasse@oracle.com goto done; 2019*13013Sglenn.lagasse@oracle.com } 2020*13013Sglenn.lagasse@oracle.com 2021*13013Sglenn.lagasse@oracle.com (void) snprintf(new_zoneroot_ds, 2022*13013Sglenn.lagasse@oracle.com sizeof (new_zoneroot_ds), 2023*13013Sglenn.lagasse@oracle.com "%s/%s", zone_container_ds, 2024*13013Sglenn.lagasse@oracle.com new_zone_be_name); 2025*13013Sglenn.lagasse@oracle.com bt.nbe_name = new_zone_be_name; 2026*13013Sglenn.lagasse@oracle.com bt.nbe_root_ds = new_zoneroot_ds; 2027*13013Sglenn.lagasse@oracle.com 2028*13013Sglenn.lagasse@oracle.com /* 2029*13013Sglenn.lagasse@oracle.com * Get handle to original zone BE's root 2030*13013Sglenn.lagasse@oracle.com * dataset. 2031*13013Sglenn.lagasse@oracle.com */ 2032*13013Sglenn.lagasse@oracle.com if ((z_zhp = zfs_open(g_zfs, zoneroot_ds, 2033*13013Sglenn.lagasse@oracle.com ZFS_TYPE_FILESYSTEM)) == NULL) { 2034*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2035*13013Sglenn.lagasse@oracle.com "failed to open zone root " 2036*13013Sglenn.lagasse@oracle.com "dataset (%s): %s\n"), 2037*13013Sglenn.lagasse@oracle.com zoneroot_ds, 2038*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2039*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2040*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 2041*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 2042*13013Sglenn.lagasse@oracle.com goto done; 2043*13013Sglenn.lagasse@oracle.com } 2044*13013Sglenn.lagasse@oracle.com 2045*13013Sglenn.lagasse@oracle.com /* 2046*13013Sglenn.lagasse@oracle.com * Try to clone the zone BE again. This 2047*13013Sglenn.lagasse@oracle.com * call will end up closing the zfs 2048*13013Sglenn.lagasse@oracle.com * handle passed in whether it 2049*13013Sglenn.lagasse@oracle.com * succeeds or fails. 2050*13013Sglenn.lagasse@oracle.com */ 2051*13013Sglenn.lagasse@oracle.com iret = be_clone_fs_callback(z_zhp, &bt); 2052*13013Sglenn.lagasse@oracle.com z_zhp = NULL; 2053*13013Sglenn.lagasse@oracle.com if (iret == 0) { 2054*13013Sglenn.lagasse@oracle.com break; 2055*13013Sglenn.lagasse@oracle.com } else if (iret != BE_ERR_BE_EXISTS) { 2056*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2057*13013Sglenn.lagasse@oracle.com "failed to create zone BE clone " 2058*13013Sglenn.lagasse@oracle.com "for new zone BE %s\n"), 2059*13013Sglenn.lagasse@oracle.com new_zone_be_name); 2060*13013Sglenn.lagasse@oracle.com ret = iret; 2061*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 2062*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 2063*13013Sglenn.lagasse@oracle.com goto done; 2064*13013Sglenn.lagasse@oracle.com } 2065*13013Sglenn.lagasse@oracle.com } 2066*13013Sglenn.lagasse@oracle.com /* 2067*13013Sglenn.lagasse@oracle.com * If we've exhausted the maximum number of 2068*13013Sglenn.lagasse@oracle.com * tries, free the auto zone BE name and return 2069*13013Sglenn.lagasse@oracle.com * error. 2070*13013Sglenn.lagasse@oracle.com */ 2071*13013Sglenn.lagasse@oracle.com if (num_retries == BE_AUTO_NAME_MAX_TRY) { 2072*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: failed " 2073*13013Sglenn.lagasse@oracle.com "to create a unique auto zone BE name\n")); 2074*13013Sglenn.lagasse@oracle.com free(bt.nbe_name); 2075*13013Sglenn.lagasse@oracle.com bt.nbe_name = NULL; 2076*13013Sglenn.lagasse@oracle.com ret = BE_ERR_AUTONAME; 2077*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 2078*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 2079*13013Sglenn.lagasse@oracle.com goto done; 2080*13013Sglenn.lagasse@oracle.com } 2081*13013Sglenn.lagasse@oracle.com } 2082*13013Sglenn.lagasse@oracle.com 2083*13013Sglenn.lagasse@oracle.com if (bt.nbe_zfs_props != NULL) 2084*13013Sglenn.lagasse@oracle.com nvlist_free(bt.nbe_zfs_props); 2085*13013Sglenn.lagasse@oracle.com 2086*13013Sglenn.lagasse@oracle.com z_zhp = NULL; 2087*13013Sglenn.lagasse@oracle.com 2088*13013Sglenn.lagasse@oracle.com if ((z_zhp = zfs_open(g_zfs, new_zoneroot_ds, 2089*13013Sglenn.lagasse@oracle.com ZFS_TYPE_FILESYSTEM)) == NULL) { 2090*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2091*13013Sglenn.lagasse@oracle.com "failed to open the new zone BE root dataset " 2092*13013Sglenn.lagasse@oracle.com "(%s): %s\n"), new_zoneroot_ds, 2093*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2094*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2095*13013Sglenn.lagasse@oracle.com goto done; 2096*13013Sglenn.lagasse@oracle.com } 2097*13013Sglenn.lagasse@oracle.com 2098*13013Sglenn.lagasse@oracle.com if (zfs_prop_set(z_zhp, BE_ZONE_PARENTBE_PROPERTY, 2099*13013Sglenn.lagasse@oracle.com uu_string) != 0) { 2100*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2101*13013Sglenn.lagasse@oracle.com "failed to set parentbe property\n")); 2102*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(z_zhp); 2103*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2104*13013Sglenn.lagasse@oracle.com goto done; 2105*13013Sglenn.lagasse@oracle.com } 2106*13013Sglenn.lagasse@oracle.com 2107*13013Sglenn.lagasse@oracle.com if (zfs_prop_set(z_zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) { 2108*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2109*13013Sglenn.lagasse@oracle.com "failed to set active property\n")); 2110*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(z_zhp); 2111*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2112*13013Sglenn.lagasse@oracle.com goto done; 2113*13013Sglenn.lagasse@oracle.com } 2114*13013Sglenn.lagasse@oracle.com 2115*13013Sglenn.lagasse@oracle.com /* 2116*13013Sglenn.lagasse@oracle.com * Generate a list of file systems from the original 2117*13013Sglenn.lagasse@oracle.com * zone BE that are legacy mounted. We use this list 2118*13013Sglenn.lagasse@oracle.com * to determine which entries in the vfstab we need to 2119*13013Sglenn.lagasse@oracle.com * update for the new zone BE we've just created. 2120*13013Sglenn.lagasse@oracle.com */ 2121*13013Sglenn.lagasse@oracle.com if ((ret = be_get_legacy_fs(obe_name, obe_root_ds, 2122*13013Sglenn.lagasse@oracle.com zoneroot_ds, zoneroot, &fld)) != BE_SUCCESS) { 2123*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2124*13013Sglenn.lagasse@oracle.com "failed to get legacy mounted file system " 2125*13013Sglenn.lagasse@oracle.com "list for zone %s\n"), zonename); 2126*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(z_zhp); 2127*13013Sglenn.lagasse@oracle.com goto done; 2128*13013Sglenn.lagasse@oracle.com } 2129*13013Sglenn.lagasse@oracle.com 2130*13013Sglenn.lagasse@oracle.com /* 2131*13013Sglenn.lagasse@oracle.com * Update new zone BE's vfstab. 2132*13013Sglenn.lagasse@oracle.com */ 2133*13013Sglenn.lagasse@oracle.com if ((ret = be_update_zone_vfstab(z_zhp, bt.nbe_name, 2134*13013Sglenn.lagasse@oracle.com zonepath_ds, zonepath_ds, &fld)) != BE_SUCCESS) { 2135*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_copy_zones: " 2136*13013Sglenn.lagasse@oracle.com "failed to update new BE's vfstab (%s)\n"), 2137*13013Sglenn.lagasse@oracle.com bt.nbe_name); 2138*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(z_zhp); 2139*13013Sglenn.lagasse@oracle.com be_free_fs_list(&fld); 2140*13013Sglenn.lagasse@oracle.com goto done; 2141*13013Sglenn.lagasse@oracle.com } 2142*13013Sglenn.lagasse@oracle.com 2143*13013Sglenn.lagasse@oracle.com be_free_fs_list(&fld); 2144*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(z_zhp); 2145*13013Sglenn.lagasse@oracle.com } 2146*13013Sglenn.lagasse@oracle.com 2147*13013Sglenn.lagasse@oracle.com done: 2148*13013Sglenn.lagasse@oracle.com free(snap_name); 2149*13013Sglenn.lagasse@oracle.com if (brands != NULL) 2150*13013Sglenn.lagasse@oracle.com z_free_brand_list(brands); 2151*13013Sglenn.lagasse@oracle.com if (zlist != NULL) 2152*13013Sglenn.lagasse@oracle.com z_free_zone_list(zlist); 2153*13013Sglenn.lagasse@oracle.com 2154*13013Sglenn.lagasse@oracle.com if (mounted_here) 2155*13013Sglenn.lagasse@oracle.com (void) _be_unmount(obe_name, 0); 2156*13013Sglenn.lagasse@oracle.com 2157*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(obe_zhp); 2158*13013Sglenn.lagasse@oracle.com return (ret); 2159*13013Sglenn.lagasse@oracle.com } 2160*13013Sglenn.lagasse@oracle.com 2161*13013Sglenn.lagasse@oracle.com /* 2162*13013Sglenn.lagasse@oracle.com * Function: be_clone_fs_callback 2163*13013Sglenn.lagasse@oracle.com * Description: Callback function used to iterate through a BE's filesystems 2164*13013Sglenn.lagasse@oracle.com * to clone them for the new BE. 2165*13013Sglenn.lagasse@oracle.com * Parameters: 2166*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer for the filesystem being processed. 2167*13013Sglenn.lagasse@oracle.com * data - be_transaction_data_t pointer providing information 2168*13013Sglenn.lagasse@oracle.com * about original BE and new BE. 2169*13013Sglenn.lagasse@oracle.com * Return: 2170*13013Sglenn.lagasse@oracle.com * 0 - Success 2171*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 2172*13013Sglenn.lagasse@oracle.com * Scope: 2173*13013Sglenn.lagasse@oracle.com * Private 2174*13013Sglenn.lagasse@oracle.com */ 2175*13013Sglenn.lagasse@oracle.com static int 2176*13013Sglenn.lagasse@oracle.com be_clone_fs_callback(zfs_handle_t *zhp, void *data) 2177*13013Sglenn.lagasse@oracle.com { 2178*13013Sglenn.lagasse@oracle.com be_transaction_data_t *bt = data; 2179*13013Sglenn.lagasse@oracle.com zfs_handle_t *zhp_ss = NULL; 2180*13013Sglenn.lagasse@oracle.com char prop_buf[MAXPATHLEN]; 2181*13013Sglenn.lagasse@oracle.com char zhp_name[ZFS_MAXNAMELEN]; 2182*13013Sglenn.lagasse@oracle.com char clone_ds[MAXPATHLEN]; 2183*13013Sglenn.lagasse@oracle.com char ss[MAXPATHLEN]; 2184*13013Sglenn.lagasse@oracle.com int ret = 0; 2185*13013Sglenn.lagasse@oracle.com 2186*13013Sglenn.lagasse@oracle.com if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf, 2187*13013Sglenn.lagasse@oracle.com ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) != 0) { 2188*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_clone_fs_callback: " 2189*13013Sglenn.lagasse@oracle.com "failed to get dataset mountpoint (%s): %s\n"), 2190*13013Sglenn.lagasse@oracle.com zfs_get_name(zhp), libzfs_error_description(g_zfs)); 2191*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2192*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2193*13013Sglenn.lagasse@oracle.com return (ret); 2194*13013Sglenn.lagasse@oracle.com } 2195*13013Sglenn.lagasse@oracle.com 2196*13013Sglenn.lagasse@oracle.com if (zfs_prop_get_int(zhp, ZFS_PROP_ZONED) != 0 && 2197*13013Sglenn.lagasse@oracle.com strcmp(prop_buf, "legacy") != 0) { 2198*13013Sglenn.lagasse@oracle.com /* 2199*13013Sglenn.lagasse@oracle.com * Since zfs can't currently handle setting the 2200*13013Sglenn.lagasse@oracle.com * mountpoint for a zoned dataset we'll have to skip 2201*13013Sglenn.lagasse@oracle.com * this dataset. This is because the mountpoint is not 2202*13013Sglenn.lagasse@oracle.com * set to "legacy". 2203*13013Sglenn.lagasse@oracle.com */ 2204*13013Sglenn.lagasse@oracle.com goto zoned; 2205*13013Sglenn.lagasse@oracle.com } 2206*13013Sglenn.lagasse@oracle.com /* 2207*13013Sglenn.lagasse@oracle.com * Get a copy of the dataset name from the zfs handle 2208*13013Sglenn.lagasse@oracle.com */ 2209*13013Sglenn.lagasse@oracle.com (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name)); 2210*13013Sglenn.lagasse@oracle.com 2211*13013Sglenn.lagasse@oracle.com /* 2212*13013Sglenn.lagasse@oracle.com * Get the clone dataset name and prepare the zfs properties for it. 2213*13013Sglenn.lagasse@oracle.com */ 2214*13013Sglenn.lagasse@oracle.com if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds, 2215*13013Sglenn.lagasse@oracle.com sizeof (clone_ds))) != BE_SUCCESS) { 2216*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2217*13013Sglenn.lagasse@oracle.com return (ret); 2218*13013Sglenn.lagasse@oracle.com } 2219*13013Sglenn.lagasse@oracle.com 2220*13013Sglenn.lagasse@oracle.com /* 2221*13013Sglenn.lagasse@oracle.com * Generate the name of the snapshot to use. 2222*13013Sglenn.lagasse@oracle.com */ 2223*13013Sglenn.lagasse@oracle.com (void) snprintf(ss, sizeof (ss), "%s@%s", zhp_name, 2224*13013Sglenn.lagasse@oracle.com bt->obe_snap_name); 2225*13013Sglenn.lagasse@oracle.com 2226*13013Sglenn.lagasse@oracle.com /* 2227*13013Sglenn.lagasse@oracle.com * Get handle to snapshot. 2228*13013Sglenn.lagasse@oracle.com */ 2229*13013Sglenn.lagasse@oracle.com if ((zhp_ss = zfs_open(g_zfs, ss, ZFS_TYPE_SNAPSHOT)) == NULL) { 2230*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_clone_fs_callback: " 2231*13013Sglenn.lagasse@oracle.com "failed to get handle to snapshot (%s): %s\n"), ss, 2232*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2233*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2234*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2235*13013Sglenn.lagasse@oracle.com return (ret); 2236*13013Sglenn.lagasse@oracle.com } 2237*13013Sglenn.lagasse@oracle.com 2238*13013Sglenn.lagasse@oracle.com /* 2239*13013Sglenn.lagasse@oracle.com * Clone the dataset. 2240*13013Sglenn.lagasse@oracle.com */ 2241*13013Sglenn.lagasse@oracle.com if (zfs_clone(zhp_ss, clone_ds, bt->nbe_zfs_props) != 0) { 2242*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_clone_fs_callback: " 2243*13013Sglenn.lagasse@oracle.com "failed to create clone dataset (%s): %s\n"), 2244*13013Sglenn.lagasse@oracle.com clone_ds, libzfs_error_description(g_zfs)); 2245*13013Sglenn.lagasse@oracle.com 2246*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp_ss); 2247*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2248*13013Sglenn.lagasse@oracle.com 2249*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 2250*13013Sglenn.lagasse@oracle.com } 2251*13013Sglenn.lagasse@oracle.com 2252*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp_ss); 2253*13013Sglenn.lagasse@oracle.com 2254*13013Sglenn.lagasse@oracle.com zoned: 2255*13013Sglenn.lagasse@oracle.com /* 2256*13013Sglenn.lagasse@oracle.com * Iterate through zhp's children datasets (if any) 2257*13013Sglenn.lagasse@oracle.com * and clone them accordingly. 2258*13013Sglenn.lagasse@oracle.com */ 2259*13013Sglenn.lagasse@oracle.com if ((ret = zfs_iter_filesystems(zhp, be_clone_fs_callback, bt)) != 0) { 2260*13013Sglenn.lagasse@oracle.com /* 2261*13013Sglenn.lagasse@oracle.com * Error occurred while processing a child dataset. 2262*13013Sglenn.lagasse@oracle.com * Destroy this dataset and return error. 2263*13013Sglenn.lagasse@oracle.com */ 2264*13013Sglenn.lagasse@oracle.com zfs_handle_t *d_zhp = NULL; 2265*13013Sglenn.lagasse@oracle.com 2266*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2267*13013Sglenn.lagasse@oracle.com 2268*13013Sglenn.lagasse@oracle.com if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM)) 2269*13013Sglenn.lagasse@oracle.com == NULL) { 2270*13013Sglenn.lagasse@oracle.com return (ret); 2271*13013Sglenn.lagasse@oracle.com } 2272*13013Sglenn.lagasse@oracle.com 2273*13013Sglenn.lagasse@oracle.com (void) zfs_destroy(d_zhp, B_FALSE); 2274*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(d_zhp); 2275*13013Sglenn.lagasse@oracle.com return (ret); 2276*13013Sglenn.lagasse@oracle.com } 2277*13013Sglenn.lagasse@oracle.com 2278*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2279*13013Sglenn.lagasse@oracle.com return (0); 2280*13013Sglenn.lagasse@oracle.com } 2281*13013Sglenn.lagasse@oracle.com 2282*13013Sglenn.lagasse@oracle.com /* 2283*13013Sglenn.lagasse@oracle.com * Function: be_send_fs_callback 2284*13013Sglenn.lagasse@oracle.com * Description: Callback function used to iterate through a BE's filesystems 2285*13013Sglenn.lagasse@oracle.com * to copy them for the new BE. 2286*13013Sglenn.lagasse@oracle.com * Parameters: 2287*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer for the filesystem being processed. 2288*13013Sglenn.lagasse@oracle.com * data - be_transaction_data_t pointer providing information 2289*13013Sglenn.lagasse@oracle.com * about original BE and new BE. 2290*13013Sglenn.lagasse@oracle.com * Return: 2291*13013Sglenn.lagasse@oracle.com * 0 - Success 2292*13013Sglenn.lagasse@oracle.com * be_errnot_t - Failure 2293*13013Sglenn.lagasse@oracle.com * Scope: 2294*13013Sglenn.lagasse@oracle.com * Private 2295*13013Sglenn.lagasse@oracle.com */ 2296*13013Sglenn.lagasse@oracle.com static int 2297*13013Sglenn.lagasse@oracle.com be_send_fs_callback(zfs_handle_t *zhp, void *data) 2298*13013Sglenn.lagasse@oracle.com { 2299*13013Sglenn.lagasse@oracle.com be_transaction_data_t *bt = data; 2300*13013Sglenn.lagasse@oracle.com recvflags_t flags = { 0 }; 2301*13013Sglenn.lagasse@oracle.com char zhp_name[ZFS_MAXNAMELEN]; 2302*13013Sglenn.lagasse@oracle.com char clone_ds[MAXPATHLEN]; 2303*13013Sglenn.lagasse@oracle.com sendflags_t send_flags = { 0 }; 2304*13013Sglenn.lagasse@oracle.com int pid, status, retval; 2305*13013Sglenn.lagasse@oracle.com int srpipe[2]; 2306*13013Sglenn.lagasse@oracle.com int ret = 0; 2307*13013Sglenn.lagasse@oracle.com 2308*13013Sglenn.lagasse@oracle.com /* 2309*13013Sglenn.lagasse@oracle.com * Get a copy of the dataset name from the zfs handle 2310*13013Sglenn.lagasse@oracle.com */ 2311*13013Sglenn.lagasse@oracle.com (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name)); 2312*13013Sglenn.lagasse@oracle.com 2313*13013Sglenn.lagasse@oracle.com /* 2314*13013Sglenn.lagasse@oracle.com * Get the clone dataset name and prepare the zfs properties for it. 2315*13013Sglenn.lagasse@oracle.com */ 2316*13013Sglenn.lagasse@oracle.com if ((ret = be_prep_clone_send_fs(zhp, bt, clone_ds, 2317*13013Sglenn.lagasse@oracle.com sizeof (clone_ds))) != BE_SUCCESS) { 2318*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2319*13013Sglenn.lagasse@oracle.com return (ret); 2320*13013Sglenn.lagasse@oracle.com } 2321*13013Sglenn.lagasse@oracle.com 2322*13013Sglenn.lagasse@oracle.com /* 2323*13013Sglenn.lagasse@oracle.com * Create the new dataset. 2324*13013Sglenn.lagasse@oracle.com */ 2325*13013Sglenn.lagasse@oracle.com if (zfs_create(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM, bt->nbe_zfs_props) 2326*13013Sglenn.lagasse@oracle.com != 0) { 2327*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_send_fs_callback: " 2328*13013Sglenn.lagasse@oracle.com "failed to create new dataset '%s': %s\n"), 2329*13013Sglenn.lagasse@oracle.com clone_ds, libzfs_error_description(g_zfs)); 2330*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2331*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2332*13013Sglenn.lagasse@oracle.com return (ret); 2333*13013Sglenn.lagasse@oracle.com } 2334*13013Sglenn.lagasse@oracle.com 2335*13013Sglenn.lagasse@oracle.com /* 2336*13013Sglenn.lagasse@oracle.com * Destination file system is already created 2337*13013Sglenn.lagasse@oracle.com * hence we need to set the force flag on 2338*13013Sglenn.lagasse@oracle.com */ 2339*13013Sglenn.lagasse@oracle.com flags.force = B_TRUE; 2340*13013Sglenn.lagasse@oracle.com 2341*13013Sglenn.lagasse@oracle.com /* 2342*13013Sglenn.lagasse@oracle.com * Initiate the pipe to be used for the send and recv 2343*13013Sglenn.lagasse@oracle.com */ 2344*13013Sglenn.lagasse@oracle.com if (pipe(srpipe) != 0) { 2345*13013Sglenn.lagasse@oracle.com int err = errno; 2346*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_send_fs_callback: failed to " 2347*13013Sglenn.lagasse@oracle.com "open pipe\n")); 2348*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2349*13013Sglenn.lagasse@oracle.com return (errno_to_be_err(err)); 2350*13013Sglenn.lagasse@oracle.com } 2351*13013Sglenn.lagasse@oracle.com 2352*13013Sglenn.lagasse@oracle.com /* 2353*13013Sglenn.lagasse@oracle.com * Fork off a child to send the dataset 2354*13013Sglenn.lagasse@oracle.com */ 2355*13013Sglenn.lagasse@oracle.com if ((pid = fork()) == -1) { 2356*13013Sglenn.lagasse@oracle.com int err = errno; 2357*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_send_fs_callback: failed to fork\n")); 2358*13013Sglenn.lagasse@oracle.com (void) close(srpipe[0]); 2359*13013Sglenn.lagasse@oracle.com (void) close(srpipe[1]); 2360*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2361*13013Sglenn.lagasse@oracle.com return (errno_to_be_err(err)); 2362*13013Sglenn.lagasse@oracle.com } else if (pid == 0) { /* child process */ 2363*13013Sglenn.lagasse@oracle.com (void) close(srpipe[0]); 2364*13013Sglenn.lagasse@oracle.com 2365*13013Sglenn.lagasse@oracle.com /* Send dataset */ 2366*13013Sglenn.lagasse@oracle.com if (zfs_send(zhp, NULL, bt->obe_snap_name, send_flags, 2367*13013Sglenn.lagasse@oracle.com srpipe[1], NULL, NULL, NULL) != 0) { 2368*13013Sglenn.lagasse@oracle.com _exit(1); 2369*13013Sglenn.lagasse@oracle.com } 2370*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2371*13013Sglenn.lagasse@oracle.com 2372*13013Sglenn.lagasse@oracle.com _exit(0); 2373*13013Sglenn.lagasse@oracle.com } 2374*13013Sglenn.lagasse@oracle.com 2375*13013Sglenn.lagasse@oracle.com (void) close(srpipe[1]); 2376*13013Sglenn.lagasse@oracle.com 2377*13013Sglenn.lagasse@oracle.com /* Receive dataset */ 2378*13013Sglenn.lagasse@oracle.com if (zfs_receive(g_zfs, clone_ds, flags, srpipe[0], NULL) != 0) { 2379*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_send_fs_callback: failed to " 2380*13013Sglenn.lagasse@oracle.com "recv dataset (%s)\n"), clone_ds); 2381*13013Sglenn.lagasse@oracle.com } 2382*13013Sglenn.lagasse@oracle.com (void) close(srpipe[0]); 2383*13013Sglenn.lagasse@oracle.com 2384*13013Sglenn.lagasse@oracle.com /* wait for child to exit */ 2385*13013Sglenn.lagasse@oracle.com do { 2386*13013Sglenn.lagasse@oracle.com retval = waitpid(pid, &status, 0); 2387*13013Sglenn.lagasse@oracle.com if (retval == -1) { 2388*13013Sglenn.lagasse@oracle.com status = 0; 2389*13013Sglenn.lagasse@oracle.com } 2390*13013Sglenn.lagasse@oracle.com } while (retval != pid); 2391*13013Sglenn.lagasse@oracle.com 2392*13013Sglenn.lagasse@oracle.com if (WEXITSTATUS(status) != 0) { 2393*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_send_fs_callback: failed to " 2394*13013Sglenn.lagasse@oracle.com "send dataset (%s)\n"), zhp_name); 2395*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2396*13013Sglenn.lagasse@oracle.com return (BE_ERR_ZFS); 2397*13013Sglenn.lagasse@oracle.com } 2398*13013Sglenn.lagasse@oracle.com 2399*13013Sglenn.lagasse@oracle.com 2400*13013Sglenn.lagasse@oracle.com /* 2401*13013Sglenn.lagasse@oracle.com * Iterate through zhp's children datasets (if any) 2402*13013Sglenn.lagasse@oracle.com * and send them accordingly. 2403*13013Sglenn.lagasse@oracle.com */ 2404*13013Sglenn.lagasse@oracle.com if ((ret = zfs_iter_filesystems(zhp, be_send_fs_callback, bt)) != 0) { 2405*13013Sglenn.lagasse@oracle.com /* 2406*13013Sglenn.lagasse@oracle.com * Error occurred while processing a child dataset. 2407*13013Sglenn.lagasse@oracle.com * Destroy this dataset and return error. 2408*13013Sglenn.lagasse@oracle.com */ 2409*13013Sglenn.lagasse@oracle.com zfs_handle_t *d_zhp = NULL; 2410*13013Sglenn.lagasse@oracle.com 2411*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2412*13013Sglenn.lagasse@oracle.com 2413*13013Sglenn.lagasse@oracle.com if ((d_zhp = zfs_open(g_zfs, clone_ds, ZFS_TYPE_FILESYSTEM)) 2414*13013Sglenn.lagasse@oracle.com == NULL) { 2415*13013Sglenn.lagasse@oracle.com return (ret); 2416*13013Sglenn.lagasse@oracle.com } 2417*13013Sglenn.lagasse@oracle.com 2418*13013Sglenn.lagasse@oracle.com (void) zfs_destroy(d_zhp, B_FALSE); 2419*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(d_zhp); 2420*13013Sglenn.lagasse@oracle.com return (ret); 2421*13013Sglenn.lagasse@oracle.com } 2422*13013Sglenn.lagasse@oracle.com 2423*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2424*13013Sglenn.lagasse@oracle.com return (0); 2425*13013Sglenn.lagasse@oracle.com } 2426*13013Sglenn.lagasse@oracle.com 2427*13013Sglenn.lagasse@oracle.com /* 2428*13013Sglenn.lagasse@oracle.com * Function: be_destroy_callback 2429*13013Sglenn.lagasse@oracle.com * Description: Callback function used to destroy a BEs children datasets 2430*13013Sglenn.lagasse@oracle.com * and snapshots. 2431*13013Sglenn.lagasse@oracle.com * Parameters: 2432*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer to the filesystem being processed. 2433*13013Sglenn.lagasse@oracle.com * data - Not used. 2434*13013Sglenn.lagasse@oracle.com * Returns: 2435*13013Sglenn.lagasse@oracle.com * 0 - Success 2436*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 2437*13013Sglenn.lagasse@oracle.com * Scope: 2438*13013Sglenn.lagasse@oracle.com * Private 2439*13013Sglenn.lagasse@oracle.com */ 2440*13013Sglenn.lagasse@oracle.com static int 2441*13013Sglenn.lagasse@oracle.com be_destroy_callback(zfs_handle_t *zhp, void *data) 2442*13013Sglenn.lagasse@oracle.com { 2443*13013Sglenn.lagasse@oracle.com be_destroy_data_t *dd = data; 2444*13013Sglenn.lagasse@oracle.com int ret = 0; 2445*13013Sglenn.lagasse@oracle.com 2446*13013Sglenn.lagasse@oracle.com /* 2447*13013Sglenn.lagasse@oracle.com * Iterate down this file system's hierarchical children 2448*13013Sglenn.lagasse@oracle.com * and destroy them first. 2449*13013Sglenn.lagasse@oracle.com */ 2450*13013Sglenn.lagasse@oracle.com if ((ret = zfs_iter_filesystems(zhp, be_destroy_callback, dd)) != 0) { 2451*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2452*13013Sglenn.lagasse@oracle.com return (ret); 2453*13013Sglenn.lagasse@oracle.com } 2454*13013Sglenn.lagasse@oracle.com 2455*13013Sglenn.lagasse@oracle.com if (dd->destroy_snaps) { 2456*13013Sglenn.lagasse@oracle.com /* 2457*13013Sglenn.lagasse@oracle.com * Iterate through this file system's snapshots and 2458*13013Sglenn.lagasse@oracle.com * destroy them before destroying the file system itself. 2459*13013Sglenn.lagasse@oracle.com */ 2460*13013Sglenn.lagasse@oracle.com if ((ret = zfs_iter_snapshots(zhp, be_destroy_callback, dd)) 2461*13013Sglenn.lagasse@oracle.com != 0) { 2462*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2463*13013Sglenn.lagasse@oracle.com return (ret); 2464*13013Sglenn.lagasse@oracle.com } 2465*13013Sglenn.lagasse@oracle.com } 2466*13013Sglenn.lagasse@oracle.com 2467*13013Sglenn.lagasse@oracle.com /* Attempt to unmount the dataset before destroying it */ 2468*13013Sglenn.lagasse@oracle.com if (dd->force_unmount) { 2469*13013Sglenn.lagasse@oracle.com if ((ret = zfs_unmount(zhp, NULL, MS_FORCE)) != 0) { 2470*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_callback: " 2471*13013Sglenn.lagasse@oracle.com "failed to unmount %s: %s\n"), zfs_get_name(zhp), 2472*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2473*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2474*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2475*13013Sglenn.lagasse@oracle.com return (ret); 2476*13013Sglenn.lagasse@oracle.com } 2477*13013Sglenn.lagasse@oracle.com } 2478*13013Sglenn.lagasse@oracle.com 2479*13013Sglenn.lagasse@oracle.com if (zfs_destroy(zhp, B_FALSE) != 0) { 2480*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_destroy_callback: " 2481*13013Sglenn.lagasse@oracle.com "failed to destroy %s: %s\n"), zfs_get_name(zhp), 2482*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2483*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2484*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2485*13013Sglenn.lagasse@oracle.com return (ret); 2486*13013Sglenn.lagasse@oracle.com } 2487*13013Sglenn.lagasse@oracle.com 2488*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2489*13013Sglenn.lagasse@oracle.com return (0); 2490*13013Sglenn.lagasse@oracle.com } 2491*13013Sglenn.lagasse@oracle.com 2492*13013Sglenn.lagasse@oracle.com /* 2493*13013Sglenn.lagasse@oracle.com * Function: be_demote_callback 2494*13013Sglenn.lagasse@oracle.com * Description: This callback function is used to iterate through the file 2495*13013Sglenn.lagasse@oracle.com * systems of a BE, looking for the right clone to promote such 2496*13013Sglenn.lagasse@oracle.com * that this file system is left without any dependent clones. 2497*13013Sglenn.lagasse@oracle.com * If the file system has no dependent clones, it doesn't need 2498*13013Sglenn.lagasse@oracle.com * to get demoted, and the function will return success. 2499*13013Sglenn.lagasse@oracle.com * 2500*13013Sglenn.lagasse@oracle.com * The demotion will be done in two passes. The first pass 2501*13013Sglenn.lagasse@oracle.com * will attempt to find the youngest snapshot that has a clone 2502*13013Sglenn.lagasse@oracle.com * that is part of some other BE. The second pass will attempt 2503*13013Sglenn.lagasse@oracle.com * to find the youngest snapshot that has a clone that is not 2504*13013Sglenn.lagasse@oracle.com * part of a BE. Doing this helps ensure the aggregated set of 2505*13013Sglenn.lagasse@oracle.com * file systems that compose a BE stay coordinated wrt BE 2506*13013Sglenn.lagasse@oracle.com * snapshots and BE dependents. It also prevents a random user 2507*13013Sglenn.lagasse@oracle.com * generated clone of a BE dataset to become the parent of other 2508*13013Sglenn.lagasse@oracle.com * BE datasets after demoting this dataset. 2509*13013Sglenn.lagasse@oracle.com * 2510*13013Sglenn.lagasse@oracle.com * Parameters: 2511*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer to the current file system being 2512*13013Sglenn.lagasse@oracle.com * processed. 2513*13013Sglenn.lagasse@oracle.com * data - not used. 2514*13013Sglenn.lagasse@oracle.com * Return: 2515*13013Sglenn.lagasse@oracle.com * 0 - Success 2516*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 2517*13013Sglenn.lagasse@oracle.com * Scope: 2518*13013Sglenn.lagasse@oracle.com * Private 2519*13013Sglenn.lagasse@oracle.com */ 2520*13013Sglenn.lagasse@oracle.com static int 2521*13013Sglenn.lagasse@oracle.com /* LINTED */ 2522*13013Sglenn.lagasse@oracle.com be_demote_callback(zfs_handle_t *zhp, void *data) 2523*13013Sglenn.lagasse@oracle.com { 2524*13013Sglenn.lagasse@oracle.com be_demote_data_t dd = { 0 }; 2525*13013Sglenn.lagasse@oracle.com int i, ret = 0; 2526*13013Sglenn.lagasse@oracle.com 2527*13013Sglenn.lagasse@oracle.com /* 2528*13013Sglenn.lagasse@oracle.com * Initialize be_demote_data for the first pass - this will find a 2529*13013Sglenn.lagasse@oracle.com * clone in another BE, if one exists. 2530*13013Sglenn.lagasse@oracle.com */ 2531*13013Sglenn.lagasse@oracle.com dd.find_in_BE = B_TRUE; 2532*13013Sglenn.lagasse@oracle.com 2533*13013Sglenn.lagasse@oracle.com for (i = 0; i < 2; i++) { 2534*13013Sglenn.lagasse@oracle.com 2535*13013Sglenn.lagasse@oracle.com if (zfs_iter_snapshots(zhp, be_demote_find_clone_callback, &dd) 2536*13013Sglenn.lagasse@oracle.com != 0) { 2537*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_demote_callback: " 2538*13013Sglenn.lagasse@oracle.com "failed to iterate snapshots for %s: %s\n"), 2539*13013Sglenn.lagasse@oracle.com zfs_get_name(zhp), libzfs_error_description(g_zfs)); 2540*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2541*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2542*13013Sglenn.lagasse@oracle.com return (ret); 2543*13013Sglenn.lagasse@oracle.com } 2544*13013Sglenn.lagasse@oracle.com if (dd.clone_zhp != NULL) { 2545*13013Sglenn.lagasse@oracle.com /* Found the clone to promote. Promote it. */ 2546*13013Sglenn.lagasse@oracle.com if (zfs_promote(dd.clone_zhp) != 0) { 2547*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_demote_callback: " 2548*13013Sglenn.lagasse@oracle.com "failed to promote %s: %s\n"), 2549*13013Sglenn.lagasse@oracle.com zfs_get_name(dd.clone_zhp), 2550*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2551*13013Sglenn.lagasse@oracle.com ret = zfs_err_to_be_err(g_zfs); 2552*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(dd.clone_zhp); 2553*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2554*13013Sglenn.lagasse@oracle.com return (ret); 2555*13013Sglenn.lagasse@oracle.com } 2556*13013Sglenn.lagasse@oracle.com 2557*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(dd.clone_zhp); 2558*13013Sglenn.lagasse@oracle.com } 2559*13013Sglenn.lagasse@oracle.com 2560*13013Sglenn.lagasse@oracle.com /* 2561*13013Sglenn.lagasse@oracle.com * Reinitialize be_demote_data for the second pass. 2562*13013Sglenn.lagasse@oracle.com * This will find a user created clone outside of any BE 2563*13013Sglenn.lagasse@oracle.com * namespace, if one exists. 2564*13013Sglenn.lagasse@oracle.com */ 2565*13013Sglenn.lagasse@oracle.com dd.clone_zhp = NULL; 2566*13013Sglenn.lagasse@oracle.com dd.origin_creation = 0; 2567*13013Sglenn.lagasse@oracle.com dd.snapshot = NULL; 2568*13013Sglenn.lagasse@oracle.com dd.find_in_BE = B_FALSE; 2569*13013Sglenn.lagasse@oracle.com } 2570*13013Sglenn.lagasse@oracle.com 2571*13013Sglenn.lagasse@oracle.com /* Iterate down this file system's children and demote them */ 2572*13013Sglenn.lagasse@oracle.com if ((ret = zfs_iter_filesystems(zhp, be_demote_callback, NULL)) != 0) { 2573*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2574*13013Sglenn.lagasse@oracle.com return (ret); 2575*13013Sglenn.lagasse@oracle.com } 2576*13013Sglenn.lagasse@oracle.com 2577*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2578*13013Sglenn.lagasse@oracle.com return (0); 2579*13013Sglenn.lagasse@oracle.com } 2580*13013Sglenn.lagasse@oracle.com 2581*13013Sglenn.lagasse@oracle.com /* 2582*13013Sglenn.lagasse@oracle.com * Function: be_demote_find_clone_callback 2583*13013Sglenn.lagasse@oracle.com * Description: This callback function is used to iterate through the 2584*13013Sglenn.lagasse@oracle.com * snapshots of a dataset, looking for the youngest snapshot 2585*13013Sglenn.lagasse@oracle.com * that has a clone. If found, it returns a reference to the 2586*13013Sglenn.lagasse@oracle.com * clone back to the caller in the callback data. 2587*13013Sglenn.lagasse@oracle.com * Parameters: 2588*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer to current snapshot being looked at 2589*13013Sglenn.lagasse@oracle.com * data - be_demote_data_t pointer used to store the clone that 2590*13013Sglenn.lagasse@oracle.com * is found. 2591*13013Sglenn.lagasse@oracle.com * Returns: 2592*13013Sglenn.lagasse@oracle.com * 0 - Successfully iterated through all snapshots. 2593*13013Sglenn.lagasse@oracle.com * 1 - Failed to iterate through all snapshots. 2594*13013Sglenn.lagasse@oracle.com * Scope: 2595*13013Sglenn.lagasse@oracle.com * Private 2596*13013Sglenn.lagasse@oracle.com */ 2597*13013Sglenn.lagasse@oracle.com static int 2598*13013Sglenn.lagasse@oracle.com be_demote_find_clone_callback(zfs_handle_t *zhp, void *data) 2599*13013Sglenn.lagasse@oracle.com { 2600*13013Sglenn.lagasse@oracle.com be_demote_data_t *dd = data; 2601*13013Sglenn.lagasse@oracle.com time_t snap_creation; 2602*13013Sglenn.lagasse@oracle.com int zret = 0; 2603*13013Sglenn.lagasse@oracle.com 2604*13013Sglenn.lagasse@oracle.com /* If snapshot has no clones, no need to look at it */ 2605*13013Sglenn.lagasse@oracle.com if (zfs_prop_get_int(zhp, ZFS_PROP_NUMCLONES) == 0) { 2606*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2607*13013Sglenn.lagasse@oracle.com return (0); 2608*13013Sglenn.lagasse@oracle.com } 2609*13013Sglenn.lagasse@oracle.com 2610*13013Sglenn.lagasse@oracle.com dd->snapshot = zfs_get_name(zhp); 2611*13013Sglenn.lagasse@oracle.com 2612*13013Sglenn.lagasse@oracle.com /* Get the creation time of this snapshot */ 2613*13013Sglenn.lagasse@oracle.com snap_creation = (time_t)zfs_prop_get_int(zhp, ZFS_PROP_CREATION); 2614*13013Sglenn.lagasse@oracle.com 2615*13013Sglenn.lagasse@oracle.com /* 2616*13013Sglenn.lagasse@oracle.com * If this snapshot's creation time is greater than (or younger than) 2617*13013Sglenn.lagasse@oracle.com * the current youngest snapshot found, iterate this snapshot to 2618*13013Sglenn.lagasse@oracle.com * check if it has a clone that we're looking for. 2619*13013Sglenn.lagasse@oracle.com */ 2620*13013Sglenn.lagasse@oracle.com if (snap_creation >= dd->origin_creation) { 2621*13013Sglenn.lagasse@oracle.com /* 2622*13013Sglenn.lagasse@oracle.com * Iterate the dependents of this snapshot to find a 2623*13013Sglenn.lagasse@oracle.com * a clone that's a direct dependent. 2624*13013Sglenn.lagasse@oracle.com */ 2625*13013Sglenn.lagasse@oracle.com if ((zret = zfs_iter_dependents(zhp, B_FALSE, 2626*13013Sglenn.lagasse@oracle.com be_demote_get_one_clone, dd)) == -1) { 2627*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_demote_find_clone_callback: " 2628*13013Sglenn.lagasse@oracle.com "failed to iterate dependents of %s\n"), 2629*13013Sglenn.lagasse@oracle.com zfs_get_name(zhp)); 2630*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2631*13013Sglenn.lagasse@oracle.com return (1); 2632*13013Sglenn.lagasse@oracle.com } else if (zret == 1) { 2633*13013Sglenn.lagasse@oracle.com /* 2634*13013Sglenn.lagasse@oracle.com * Found a clone, update the origin_creation time 2635*13013Sglenn.lagasse@oracle.com * in the callback data. 2636*13013Sglenn.lagasse@oracle.com */ 2637*13013Sglenn.lagasse@oracle.com dd->origin_creation = snap_creation; 2638*13013Sglenn.lagasse@oracle.com } 2639*13013Sglenn.lagasse@oracle.com } 2640*13013Sglenn.lagasse@oracle.com 2641*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2642*13013Sglenn.lagasse@oracle.com return (0); 2643*13013Sglenn.lagasse@oracle.com } 2644*13013Sglenn.lagasse@oracle.com 2645*13013Sglenn.lagasse@oracle.com /* 2646*13013Sglenn.lagasse@oracle.com * Function: be_demote_get_one_clone 2647*13013Sglenn.lagasse@oracle.com * Description: This callback function is used to iterate through a 2648*13013Sglenn.lagasse@oracle.com * snapshot's dependencies to find a filesystem that is a 2649*13013Sglenn.lagasse@oracle.com * direct clone of the snapshot being iterated. 2650*13013Sglenn.lagasse@oracle.com * Parameters: 2651*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer to current dataset being looked at 2652*13013Sglenn.lagasse@oracle.com * data - be_demote_data_t pointer used to store the clone 2653*13013Sglenn.lagasse@oracle.com * that is found, and also provides flag to note 2654*13013Sglenn.lagasse@oracle.com * whether or not the clone filesystem being searched 2655*13013Sglenn.lagasse@oracle.com * for needs to be found in a BE dataset hierarchy. 2656*13013Sglenn.lagasse@oracle.com * Return: 2657*13013Sglenn.lagasse@oracle.com * 1 - Success, found clone and its also a BE's root dataset. 2658*13013Sglenn.lagasse@oracle.com * 0 - Failure, clone not found. 2659*13013Sglenn.lagasse@oracle.com * Scope: 2660*13013Sglenn.lagasse@oracle.com * Private 2661*13013Sglenn.lagasse@oracle.com */ 2662*13013Sglenn.lagasse@oracle.com static int 2663*13013Sglenn.lagasse@oracle.com be_demote_get_one_clone(zfs_handle_t *zhp, void *data) 2664*13013Sglenn.lagasse@oracle.com { 2665*13013Sglenn.lagasse@oracle.com be_demote_data_t *dd = data; 2666*13013Sglenn.lagasse@oracle.com char origin[ZFS_MAXNAMELEN]; 2667*13013Sglenn.lagasse@oracle.com char ds_path[ZFS_MAXNAMELEN]; 2668*13013Sglenn.lagasse@oracle.com 2669*13013Sglenn.lagasse@oracle.com if (zfs_get_type(zhp) != ZFS_TYPE_FILESYSTEM) { 2670*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2671*13013Sglenn.lagasse@oracle.com return (0); 2672*13013Sglenn.lagasse@oracle.com } 2673*13013Sglenn.lagasse@oracle.com 2674*13013Sglenn.lagasse@oracle.com (void) strlcpy(ds_path, zfs_get_name(zhp), sizeof (ds_path)); 2675*13013Sglenn.lagasse@oracle.com 2676*13013Sglenn.lagasse@oracle.com /* 2677*13013Sglenn.lagasse@oracle.com * Make sure this is a direct clone of the snapshot 2678*13013Sglenn.lagasse@oracle.com * we're iterating. 2679*13013Sglenn.lagasse@oracle.com */ 2680*13013Sglenn.lagasse@oracle.com if (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin, sizeof (origin), NULL, 2681*13013Sglenn.lagasse@oracle.com NULL, 0, B_FALSE) != 0) { 2682*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_demote_get_one_clone: " 2683*13013Sglenn.lagasse@oracle.com "failed to get origin of %s: %s\n"), ds_path, 2684*13013Sglenn.lagasse@oracle.com libzfs_error_description(g_zfs)); 2685*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2686*13013Sglenn.lagasse@oracle.com return (0); 2687*13013Sglenn.lagasse@oracle.com } 2688*13013Sglenn.lagasse@oracle.com if (strcmp(origin, dd->snapshot) != 0) { 2689*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2690*13013Sglenn.lagasse@oracle.com return (0); 2691*13013Sglenn.lagasse@oracle.com } 2692*13013Sglenn.lagasse@oracle.com 2693*13013Sglenn.lagasse@oracle.com if (dd->find_in_BE) { 2694*13013Sglenn.lagasse@oracle.com if ((zpool_iter(g_zfs, be_check_be_roots_callback, ds_path)) 2695*13013Sglenn.lagasse@oracle.com > 0) { 2696*13013Sglenn.lagasse@oracle.com if (dd->clone_zhp != NULL) 2697*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(dd->clone_zhp); 2698*13013Sglenn.lagasse@oracle.com dd->clone_zhp = zhp; 2699*13013Sglenn.lagasse@oracle.com return (1); 2700*13013Sglenn.lagasse@oracle.com } 2701*13013Sglenn.lagasse@oracle.com 2702*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2703*13013Sglenn.lagasse@oracle.com return (0); 2704*13013Sglenn.lagasse@oracle.com } 2705*13013Sglenn.lagasse@oracle.com 2706*13013Sglenn.lagasse@oracle.com if (dd->clone_zhp != NULL) 2707*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(dd->clone_zhp); 2708*13013Sglenn.lagasse@oracle.com 2709*13013Sglenn.lagasse@oracle.com dd->clone_zhp = zhp; 2710*13013Sglenn.lagasse@oracle.com return (1); 2711*13013Sglenn.lagasse@oracle.com } 2712*13013Sglenn.lagasse@oracle.com 2713*13013Sglenn.lagasse@oracle.com /* 2714*13013Sglenn.lagasse@oracle.com * Function: be_get_snap 2715*13013Sglenn.lagasse@oracle.com * Description: This function takes a snapshot dataset name and separates 2716*13013Sglenn.lagasse@oracle.com * out the parent dataset portion from the snapshot name. 2717*13013Sglenn.lagasse@oracle.com * I.e. it finds the '@' in the snapshot dataset name and 2718*13013Sglenn.lagasse@oracle.com * replaces it with a '\0'. 2719*13013Sglenn.lagasse@oracle.com * Parameters: 2720*13013Sglenn.lagasse@oracle.com * origin - char pointer to a snapshot dataset name. Its 2721*13013Sglenn.lagasse@oracle.com * contents will be modified by this function. 2722*13013Sglenn.lagasse@oracle.com * *snap - pointer to a char pointer. Will be set to the 2723*13013Sglenn.lagasse@oracle.com * snapshot name portion upon success. 2724*13013Sglenn.lagasse@oracle.com * Return: 2725*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 2726*13013Sglenn.lagasse@oracle.com * 1 - Failure 2727*13013Sglenn.lagasse@oracle.com * Scope: 2728*13013Sglenn.lagasse@oracle.com * Private 2729*13013Sglenn.lagasse@oracle.com */ 2730*13013Sglenn.lagasse@oracle.com static int 2731*13013Sglenn.lagasse@oracle.com be_get_snap(char *origin, char **snap) 2732*13013Sglenn.lagasse@oracle.com { 2733*13013Sglenn.lagasse@oracle.com char *cp; 2734*13013Sglenn.lagasse@oracle.com 2735*13013Sglenn.lagasse@oracle.com /* 2736*13013Sglenn.lagasse@oracle.com * Separate out the origin's dataset and snapshot portions by 2737*13013Sglenn.lagasse@oracle.com * replacing the @ with a '\0' 2738*13013Sglenn.lagasse@oracle.com */ 2739*13013Sglenn.lagasse@oracle.com cp = strrchr(origin, '@'); 2740*13013Sglenn.lagasse@oracle.com if (cp != NULL) { 2741*13013Sglenn.lagasse@oracle.com if (cp[1] != NULL && cp[1] != '\0') { 2742*13013Sglenn.lagasse@oracle.com cp[0] = '\0'; 2743*13013Sglenn.lagasse@oracle.com *snap = cp+1; 2744*13013Sglenn.lagasse@oracle.com } else { 2745*13013Sglenn.lagasse@oracle.com return (1); 2746*13013Sglenn.lagasse@oracle.com } 2747*13013Sglenn.lagasse@oracle.com } else { 2748*13013Sglenn.lagasse@oracle.com return (1); 2749*13013Sglenn.lagasse@oracle.com } 2750*13013Sglenn.lagasse@oracle.com 2751*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 2752*13013Sglenn.lagasse@oracle.com } 2753*13013Sglenn.lagasse@oracle.com 2754*13013Sglenn.lagasse@oracle.com /* 2755*13013Sglenn.lagasse@oracle.com * Function: be_create_container_ds 2756*13013Sglenn.lagasse@oracle.com * Description: This function checks that the zpool passed has the BE 2757*13013Sglenn.lagasse@oracle.com * container dataset, and if not, then creates it. 2758*13013Sglenn.lagasse@oracle.com * Parameters: 2759*13013Sglenn.lagasse@oracle.com * zpool - name of pool to create BE container dataset in. 2760*13013Sglenn.lagasse@oracle.com * Return: 2761*13013Sglenn.lagasse@oracle.com * B_TRUE - Successfully created BE container dataset, or it 2762*13013Sglenn.lagasse@oracle.com * already existed. 2763*13013Sglenn.lagasse@oracle.com * B_FALSE - Failed to create container dataset. 2764*13013Sglenn.lagasse@oracle.com * Scope: 2765*13013Sglenn.lagasse@oracle.com * Private 2766*13013Sglenn.lagasse@oracle.com */ 2767*13013Sglenn.lagasse@oracle.com static boolean_t 2768*13013Sglenn.lagasse@oracle.com be_create_container_ds(char *zpool) 2769*13013Sglenn.lagasse@oracle.com { 2770*13013Sglenn.lagasse@oracle.com nvlist_t *props = NULL; 2771*13013Sglenn.lagasse@oracle.com char be_container_ds[MAXPATHLEN]; 2772*13013Sglenn.lagasse@oracle.com 2773*13013Sglenn.lagasse@oracle.com /* Generate string for BE container dataset for this pool */ 2774*13013Sglenn.lagasse@oracle.com be_make_container_ds(zpool, be_container_ds, 2775*13013Sglenn.lagasse@oracle.com sizeof (be_container_ds)); 2776*13013Sglenn.lagasse@oracle.com 2777*13013Sglenn.lagasse@oracle.com if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) { 2778*13013Sglenn.lagasse@oracle.com 2779*13013Sglenn.lagasse@oracle.com if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 2780*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_create_container_ds: " 2781*13013Sglenn.lagasse@oracle.com "nvlist_alloc failed\n")); 2782*13013Sglenn.lagasse@oracle.com return (B_FALSE); 2783*13013Sglenn.lagasse@oracle.com } 2784*13013Sglenn.lagasse@oracle.com 2785*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(props, 2786*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 2787*13013Sglenn.lagasse@oracle.com ZFS_MOUNTPOINT_LEGACY) != 0) { 2788*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_create_container_ds: " 2789*13013Sglenn.lagasse@oracle.com "internal error: out of memory\n")); 2790*13013Sglenn.lagasse@oracle.com nvlist_free(props); 2791*13013Sglenn.lagasse@oracle.com return (B_FALSE); 2792*13013Sglenn.lagasse@oracle.com } 2793*13013Sglenn.lagasse@oracle.com 2794*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(props, 2795*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_CANMOUNT), "off") != 0) { 2796*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_create_container_ds: " 2797*13013Sglenn.lagasse@oracle.com "internal error: out of memory\n")); 2798*13013Sglenn.lagasse@oracle.com nvlist_free(props); 2799*13013Sglenn.lagasse@oracle.com return (B_FALSE); 2800*13013Sglenn.lagasse@oracle.com } 2801*13013Sglenn.lagasse@oracle.com 2802*13013Sglenn.lagasse@oracle.com if (zfs_create(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM, 2803*13013Sglenn.lagasse@oracle.com props) != 0) { 2804*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_create_container_ds: " 2805*13013Sglenn.lagasse@oracle.com "failed to create container dataset (%s): %s\n"), 2806*13013Sglenn.lagasse@oracle.com be_container_ds, libzfs_error_description(g_zfs)); 2807*13013Sglenn.lagasse@oracle.com nvlist_free(props); 2808*13013Sglenn.lagasse@oracle.com return (B_FALSE); 2809*13013Sglenn.lagasse@oracle.com } 2810*13013Sglenn.lagasse@oracle.com 2811*13013Sglenn.lagasse@oracle.com nvlist_free(props); 2812*13013Sglenn.lagasse@oracle.com } 2813*13013Sglenn.lagasse@oracle.com 2814*13013Sglenn.lagasse@oracle.com return (B_TRUE); 2815*13013Sglenn.lagasse@oracle.com } 2816*13013Sglenn.lagasse@oracle.com 2817*13013Sglenn.lagasse@oracle.com /* 2818*13013Sglenn.lagasse@oracle.com * Function: be_prep_clone_send_fs 2819*13013Sglenn.lagasse@oracle.com * Description: This function takes a zfs handle to a dataset from the 2820*13013Sglenn.lagasse@oracle.com * original BE, and generates the name of the clone dataset 2821*13013Sglenn.lagasse@oracle.com * to create for the new BE. It also prepares the zfs 2822*13013Sglenn.lagasse@oracle.com * properties to be used for the new BE. 2823*13013Sglenn.lagasse@oracle.com * Parameters: 2824*13013Sglenn.lagasse@oracle.com * zhp - pointer to zfs_handle_t of the file system being 2825*13013Sglenn.lagasse@oracle.com * cloned/copied. 2826*13013Sglenn.lagasse@oracle.com * bt - be_transaction_data pointer providing information 2827*13013Sglenn.lagasse@oracle.com * about the original BE and new BE. 2828*13013Sglenn.lagasse@oracle.com * clone_ds - buffer to store the name of the dataset 2829*13013Sglenn.lagasse@oracle.com * for the new BE. 2830*13013Sglenn.lagasse@oracle.com * clone_ds_len - length of clone_ds buffer 2831*13013Sglenn.lagasse@oracle.com * Return: 2832*13013Sglenn.lagasse@oracle.com * BE_SUCCESS - Success 2833*13013Sglenn.lagasse@oracle.com * be_errno_t - Failure 2834*13013Sglenn.lagasse@oracle.com * Scope: 2835*13013Sglenn.lagasse@oracle.com * Private 2836*13013Sglenn.lagasse@oracle.com */ 2837*13013Sglenn.lagasse@oracle.com static int 2838*13013Sglenn.lagasse@oracle.com be_prep_clone_send_fs(zfs_handle_t *zhp, be_transaction_data_t *bt, 2839*13013Sglenn.lagasse@oracle.com char *clone_ds, int clone_ds_len) 2840*13013Sglenn.lagasse@oracle.com { 2841*13013Sglenn.lagasse@oracle.com zprop_source_t sourcetype; 2842*13013Sglenn.lagasse@oracle.com char source[ZFS_MAXNAMELEN]; 2843*13013Sglenn.lagasse@oracle.com char zhp_name[ZFS_MAXNAMELEN]; 2844*13013Sglenn.lagasse@oracle.com char mountpoint[MAXPATHLEN]; 2845*13013Sglenn.lagasse@oracle.com char *child_fs = NULL; 2846*13013Sglenn.lagasse@oracle.com char *zhp_mountpoint = NULL; 2847*13013Sglenn.lagasse@oracle.com int err = 0; 2848*13013Sglenn.lagasse@oracle.com 2849*13013Sglenn.lagasse@oracle.com /* 2850*13013Sglenn.lagasse@oracle.com * Get a copy of the dataset name zfs_name from zhp 2851*13013Sglenn.lagasse@oracle.com */ 2852*13013Sglenn.lagasse@oracle.com (void) strlcpy(zhp_name, zfs_get_name(zhp), sizeof (zhp_name)); 2853*13013Sglenn.lagasse@oracle.com 2854*13013Sglenn.lagasse@oracle.com /* 2855*13013Sglenn.lagasse@oracle.com * Get file system name relative to the root. 2856*13013Sglenn.lagasse@oracle.com */ 2857*13013Sglenn.lagasse@oracle.com if (strncmp(zhp_name, bt->obe_root_ds, strlen(bt->obe_root_ds)) 2858*13013Sglenn.lagasse@oracle.com == 0) { 2859*13013Sglenn.lagasse@oracle.com child_fs = zhp_name + strlen(bt->obe_root_ds); 2860*13013Sglenn.lagasse@oracle.com 2861*13013Sglenn.lagasse@oracle.com /* 2862*13013Sglenn.lagasse@oracle.com * if child_fs is NULL, this means we're processing the 2863*13013Sglenn.lagasse@oracle.com * root dataset itself; set child_fs to the empty string. 2864*13013Sglenn.lagasse@oracle.com */ 2865*13013Sglenn.lagasse@oracle.com if (child_fs == NULL) 2866*13013Sglenn.lagasse@oracle.com child_fs = ""; 2867*13013Sglenn.lagasse@oracle.com } else { 2868*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 2869*13013Sglenn.lagasse@oracle.com } 2870*13013Sglenn.lagasse@oracle.com 2871*13013Sglenn.lagasse@oracle.com /* 2872*13013Sglenn.lagasse@oracle.com * Generate the name of the clone file system. 2873*13013Sglenn.lagasse@oracle.com */ 2874*13013Sglenn.lagasse@oracle.com (void) snprintf(clone_ds, clone_ds_len, "%s%s", bt->nbe_root_ds, 2875*13013Sglenn.lagasse@oracle.com child_fs); 2876*13013Sglenn.lagasse@oracle.com 2877*13013Sglenn.lagasse@oracle.com /* Get the mountpoint and source properties of the existing dataset */ 2878*13013Sglenn.lagasse@oracle.com if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, mountpoint, 2879*13013Sglenn.lagasse@oracle.com sizeof (mountpoint), &sourcetype, source, sizeof (source), 2880*13013Sglenn.lagasse@oracle.com B_FALSE) != 0) { 2881*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_prep_clone_send_fs: " 2882*13013Sglenn.lagasse@oracle.com "failed to get mountpoint for (%s): %s\n"), 2883*13013Sglenn.lagasse@oracle.com zhp_name, libzfs_error_description(g_zfs)); 2884*13013Sglenn.lagasse@oracle.com return (zfs_err_to_be_err(g_zfs)); 2885*13013Sglenn.lagasse@oracle.com } 2886*13013Sglenn.lagasse@oracle.com 2887*13013Sglenn.lagasse@oracle.com /* 2888*13013Sglenn.lagasse@oracle.com * Workaround for 6668667 where a mountpoint property of "/" comes 2889*13013Sglenn.lagasse@oracle.com * back as "". 2890*13013Sglenn.lagasse@oracle.com */ 2891*13013Sglenn.lagasse@oracle.com if (strcmp(mountpoint, "") == 0) { 2892*13013Sglenn.lagasse@oracle.com (void) snprintf(mountpoint, sizeof (mountpoint), "/"); 2893*13013Sglenn.lagasse@oracle.com } 2894*13013Sglenn.lagasse@oracle.com 2895*13013Sglenn.lagasse@oracle.com /* 2896*13013Sglenn.lagasse@oracle.com * Figure out what to set as the mountpoint for the new dataset. 2897*13013Sglenn.lagasse@oracle.com * If the source of the mountpoint property is local, use the 2898*13013Sglenn.lagasse@oracle.com * mountpoint value itself. Otherwise, remove it from the 2899*13013Sglenn.lagasse@oracle.com * zfs properties list so that it gets inherited. 2900*13013Sglenn.lagasse@oracle.com */ 2901*13013Sglenn.lagasse@oracle.com if (sourcetype & ZPROP_SRC_LOCAL) { 2902*13013Sglenn.lagasse@oracle.com /* 2903*13013Sglenn.lagasse@oracle.com * If the BE that this file system is a part of is 2904*13013Sglenn.lagasse@oracle.com * currently mounted, strip off the BE altroot portion 2905*13013Sglenn.lagasse@oracle.com * from the mountpoint. 2906*13013Sglenn.lagasse@oracle.com */ 2907*13013Sglenn.lagasse@oracle.com zhp_mountpoint = mountpoint; 2908*13013Sglenn.lagasse@oracle.com 2909*13013Sglenn.lagasse@oracle.com if (strcmp(mountpoint, ZFS_MOUNTPOINT_LEGACY) != 0 && 2910*13013Sglenn.lagasse@oracle.com bt->obe_altroot != NULL && strcmp(bt->obe_altroot, 2911*13013Sglenn.lagasse@oracle.com "/") != 0 && zfs_is_mounted(zhp, NULL)) { 2912*13013Sglenn.lagasse@oracle.com 2913*13013Sglenn.lagasse@oracle.com int altroot_len = strlen(bt->obe_altroot); 2914*13013Sglenn.lagasse@oracle.com 2915*13013Sglenn.lagasse@oracle.com if (strncmp(bt->obe_altroot, mountpoint, altroot_len) 2916*13013Sglenn.lagasse@oracle.com == 0) { 2917*13013Sglenn.lagasse@oracle.com if (mountpoint[altroot_len] == '/') 2918*13013Sglenn.lagasse@oracle.com zhp_mountpoint = mountpoint + 2919*13013Sglenn.lagasse@oracle.com altroot_len; 2920*13013Sglenn.lagasse@oracle.com else if (mountpoint[altroot_len] == '\0') 2921*13013Sglenn.lagasse@oracle.com (void) snprintf(mountpoint, 2922*13013Sglenn.lagasse@oracle.com sizeof (mountpoint), "/"); 2923*13013Sglenn.lagasse@oracle.com } 2924*13013Sglenn.lagasse@oracle.com } 2925*13013Sglenn.lagasse@oracle.com 2926*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(bt->nbe_zfs_props, 2927*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 2928*13013Sglenn.lagasse@oracle.com zhp_mountpoint) != 0) { 2929*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_prep_clone_send_fs: " 2930*13013Sglenn.lagasse@oracle.com "internal error: out of memory\n")); 2931*13013Sglenn.lagasse@oracle.com return (BE_ERR_NOMEM); 2932*13013Sglenn.lagasse@oracle.com } 2933*13013Sglenn.lagasse@oracle.com } else { 2934*13013Sglenn.lagasse@oracle.com err = nvlist_remove_all(bt->nbe_zfs_props, 2935*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_MOUNTPOINT)); 2936*13013Sglenn.lagasse@oracle.com if (err != 0 && err != ENOENT) { 2937*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_prep_clone_send_fs: " 2938*13013Sglenn.lagasse@oracle.com "failed to remove mountpoint from " 2939*13013Sglenn.lagasse@oracle.com "nvlist\n")); 2940*13013Sglenn.lagasse@oracle.com return (BE_ERR_INVAL); 2941*13013Sglenn.lagasse@oracle.com } 2942*13013Sglenn.lagasse@oracle.com } 2943*13013Sglenn.lagasse@oracle.com 2944*13013Sglenn.lagasse@oracle.com /* 2945*13013Sglenn.lagasse@oracle.com * Set the 'canmount' property 2946*13013Sglenn.lagasse@oracle.com */ 2947*13013Sglenn.lagasse@oracle.com if (nvlist_add_string(bt->nbe_zfs_props, 2948*13013Sglenn.lagasse@oracle.com zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) { 2949*13013Sglenn.lagasse@oracle.com be_print_err(gettext("be_prep_clone_send_fs: " 2950*13013Sglenn.lagasse@oracle.com "internal error: out of memory\n")); 2951*13013Sglenn.lagasse@oracle.com return (BE_ERR_NOMEM); 2952*13013Sglenn.lagasse@oracle.com } 2953*13013Sglenn.lagasse@oracle.com 2954*13013Sglenn.lagasse@oracle.com return (BE_SUCCESS); 2955*13013Sglenn.lagasse@oracle.com } 2956*13013Sglenn.lagasse@oracle.com 2957*13013Sglenn.lagasse@oracle.com /* 2958*13013Sglenn.lagasse@oracle.com * Function: be_get_zone_be_name 2959*13013Sglenn.lagasse@oracle.com * Description: This function takes the zones root dataset, the container 2960*13013Sglenn.lagasse@oracle.com * dataset and returns the zones BE name based on the zone 2961*13013Sglenn.lagasse@oracle.com * root dataset. 2962*13013Sglenn.lagasse@oracle.com * Parameters: 2963*13013Sglenn.lagasse@oracle.com * root_ds - the zones root dataset. 2964*13013Sglenn.lagasse@oracle.com * container_ds - the container dataset for the zone. 2965*13013Sglenn.lagasse@oracle.com * Returns: 2966*13013Sglenn.lagasse@oracle.com * char * - the BE name of this zone based on the root dataset. 2967*13013Sglenn.lagasse@oracle.com */ 2968*13013Sglenn.lagasse@oracle.com static char * 2969*13013Sglenn.lagasse@oracle.com be_get_zone_be_name(char *root_ds, char *container_ds) 2970*13013Sglenn.lagasse@oracle.com { 2971*13013Sglenn.lagasse@oracle.com return (root_ds + (strlen(container_ds) + 1)); 2972*13013Sglenn.lagasse@oracle.com } 2973*13013Sglenn.lagasse@oracle.com 2974*13013Sglenn.lagasse@oracle.com /* 2975*13013Sglenn.lagasse@oracle.com * Function: be_zone_root_exists_callback 2976*13013Sglenn.lagasse@oracle.com * Description: This callback function is used to determine if a 2977*13013Sglenn.lagasse@oracle.com * zone root container dataset has any children. It always 2978*13013Sglenn.lagasse@oracle.com * returns 1, signifying a hierarchical child of the zone 2979*13013Sglenn.lagasse@oracle.com * root container dataset has been traversed and therefore 2980*13013Sglenn.lagasse@oracle.com * it has children. 2981*13013Sglenn.lagasse@oracle.com * Parameters: 2982*13013Sglenn.lagasse@oracle.com * zhp - zfs_handle_t pointer to current dataset being processed. 2983*13013Sglenn.lagasse@oracle.com * data - not used. 2984*13013Sglenn.lagasse@oracle.com * Returns: 2985*13013Sglenn.lagasse@oracle.com * 1 - dataset exists 2986*13013Sglenn.lagasse@oracle.com * Scope: 2987*13013Sglenn.lagasse@oracle.com * Private 2988*13013Sglenn.lagasse@oracle.com */ 2989*13013Sglenn.lagasse@oracle.com static int 2990*13013Sglenn.lagasse@oracle.com /* LINTED */ 2991*13013Sglenn.lagasse@oracle.com be_zone_root_exists_callback(zfs_handle_t *zhp, void *data) 2992*13013Sglenn.lagasse@oracle.com { 2993*13013Sglenn.lagasse@oracle.com ZFS_CLOSE(zhp); 2994*13013Sglenn.lagasse@oracle.com return (1); 2995*13013Sglenn.lagasse@oracle.com } 2996