1f169c0eaSGlenn Lagasse /* 2f169c0eaSGlenn Lagasse * CDDL HEADER START 3f169c0eaSGlenn Lagasse * 4f169c0eaSGlenn Lagasse * The contents of this file are subject to the terms of the 5f169c0eaSGlenn Lagasse * Common Development and Distribution License (the "License"). 6f169c0eaSGlenn Lagasse * You may not use this file except in compliance with the License. 7f169c0eaSGlenn Lagasse * 8f169c0eaSGlenn Lagasse * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9f169c0eaSGlenn Lagasse * or http://www.opensolaris.org/os/licensing. 10f169c0eaSGlenn Lagasse * See the License for the specific language governing permissions 11f169c0eaSGlenn Lagasse * and limitations under the License. 12f169c0eaSGlenn Lagasse * 13f169c0eaSGlenn Lagasse * When distributing Covered Code, include this CDDL HEADER in each 14f169c0eaSGlenn Lagasse * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15f169c0eaSGlenn Lagasse * If applicable, add the following below this CDDL HEADER, with the 16f169c0eaSGlenn Lagasse * fields enclosed by brackets "[]" replaced with your own identifying 17f169c0eaSGlenn Lagasse * information: Portions Copyright [yyyy] [name of copyright owner] 18f169c0eaSGlenn Lagasse * 19f169c0eaSGlenn Lagasse * CDDL HEADER END 20f169c0eaSGlenn Lagasse */ 21f169c0eaSGlenn Lagasse 22f169c0eaSGlenn Lagasse /* 23f169c0eaSGlenn Lagasse * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 247e0e2549SAlexander Eremin * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 259adfa60dSMatthew Ahrens * Copyright (c) 2014, 2015 by Delphix. All rights reserved. 260d8fa8f8SMartin Matuska * Copyright (c) 2016 Martin Matuska. All rights reserved. 27*f976337aSAndy Fiddaman * Copyright 2021 OmniOS Community Edition (OmniOSce) Association. 28de1ab35cSAlexander Eremin */ 29de1ab35cSAlexander Eremin 30f169c0eaSGlenn Lagasse /* 31f169c0eaSGlenn Lagasse * System includes 32f169c0eaSGlenn Lagasse */ 33f169c0eaSGlenn Lagasse 34f169c0eaSGlenn Lagasse #include <assert.h> 35f169c0eaSGlenn Lagasse #include <ctype.h> 36f169c0eaSGlenn Lagasse #include <errno.h> 37f169c0eaSGlenn Lagasse #include <libgen.h> 38f169c0eaSGlenn Lagasse #include <libintl.h> 39f169c0eaSGlenn Lagasse #include <libnvpair.h> 40f169c0eaSGlenn Lagasse #include <libzfs.h> 41f169c0eaSGlenn Lagasse #include <stdio.h> 42f169c0eaSGlenn Lagasse #include <stdlib.h> 43f169c0eaSGlenn Lagasse #include <string.h> 44f169c0eaSGlenn Lagasse #include <sys/mnttab.h> 45f169c0eaSGlenn Lagasse #include <sys/mount.h> 46f169c0eaSGlenn Lagasse #include <sys/stat.h> 47f169c0eaSGlenn Lagasse #include <sys/types.h> 48f169c0eaSGlenn Lagasse #include <sys/wait.h> 49f169c0eaSGlenn Lagasse #include <unistd.h> 50f169c0eaSGlenn Lagasse 51f169c0eaSGlenn Lagasse #include <libbe.h> 52f169c0eaSGlenn Lagasse #include <libbe_priv.h> 53*f976337aSAndy Fiddaman #include <libzfsbootenv.h> 54f169c0eaSGlenn Lagasse 55f169c0eaSGlenn Lagasse /* Library wide variables */ 56f169c0eaSGlenn Lagasse libzfs_handle_t *g_zfs = NULL; 57f169c0eaSGlenn Lagasse 58f169c0eaSGlenn Lagasse /* Private function prototypes */ 59f169c0eaSGlenn Lagasse static int _be_destroy(const char *, be_destroy_data_t *); 60f169c0eaSGlenn Lagasse static int be_destroy_zones(char *, char *, be_destroy_data_t *); 61f169c0eaSGlenn Lagasse static int be_destroy_zone_roots(char *, be_destroy_data_t *); 62f169c0eaSGlenn Lagasse static int be_destroy_zone_roots_callback(zfs_handle_t *, void *); 63f169c0eaSGlenn Lagasse static int be_copy_zones(char *, char *, char *); 64f169c0eaSGlenn Lagasse static int be_clone_fs_callback(zfs_handle_t *, void *); 65f169c0eaSGlenn Lagasse static int be_destroy_callback(zfs_handle_t *, void *); 66f169c0eaSGlenn Lagasse static int be_send_fs_callback(zfs_handle_t *, void *); 67f169c0eaSGlenn Lagasse static int be_demote_callback(zfs_handle_t *, void *); 68f169c0eaSGlenn Lagasse static int be_demote_find_clone_callback(zfs_handle_t *, void *); 695ee7c793SAlexander Eremin static int be_has_snapshot_callback(zfs_handle_t *, void *); 70f169c0eaSGlenn Lagasse static int be_demote_get_one_clone(zfs_handle_t *, void *); 71f169c0eaSGlenn Lagasse static int be_get_snap(char *, char **); 72f169c0eaSGlenn Lagasse static int be_prep_clone_send_fs(zfs_handle_t *, be_transaction_data_t *, 73f169c0eaSGlenn Lagasse char *, int); 74f169c0eaSGlenn Lagasse static boolean_t be_create_container_ds(char *); 75f169c0eaSGlenn Lagasse static char *be_get_zone_be_name(char *root_ds, char *container_ds); 76f169c0eaSGlenn Lagasse static int be_zone_root_exists_callback(zfs_handle_t *, void *); 77f169c0eaSGlenn Lagasse 78f169c0eaSGlenn Lagasse /* ******************************************************************** */ 79f169c0eaSGlenn Lagasse /* Public Functions */ 80f169c0eaSGlenn Lagasse /* ******************************************************************** */ 81f169c0eaSGlenn Lagasse 82f169c0eaSGlenn Lagasse /* 83f169c0eaSGlenn Lagasse * Function: be_init 84f169c0eaSGlenn Lagasse * Description: Creates the initial datasets for a BE and leaves them 85f169c0eaSGlenn Lagasse * unpopulated. The resultant BE can be mounted but can't 86f169c0eaSGlenn Lagasse * yet be activated or booted. 87f169c0eaSGlenn Lagasse * Parameters: 88f169c0eaSGlenn Lagasse * be_attrs - pointer to nvlist_t of attributes being passed in. 89f169c0eaSGlenn Lagasse * The following attributes are used by this function: 90f169c0eaSGlenn Lagasse * 91f169c0eaSGlenn Lagasse * BE_ATTR_NEW_BE_NAME *required 92f169c0eaSGlenn Lagasse * BE_ATTR_NEW_BE_POOL *required 93f169c0eaSGlenn Lagasse * BE_ATTR_ZFS_PROPERTIES *optional 94f169c0eaSGlenn Lagasse * BE_ATTR_FS_NAMES *optional 95f169c0eaSGlenn Lagasse * BE_ATTR_FS_NUM *optional 96f169c0eaSGlenn Lagasse * BE_ATTR_SHARED_FS_NAMES *optional 97f169c0eaSGlenn Lagasse * BE_ATTR_SHARED_FS_NUM *optional 98f169c0eaSGlenn Lagasse * Return: 99f169c0eaSGlenn Lagasse * BE_SUCCESS - Success 100f169c0eaSGlenn Lagasse * be_errno_t - Failure 101f169c0eaSGlenn Lagasse * Scope: 102f169c0eaSGlenn Lagasse * Public 103f169c0eaSGlenn Lagasse */ 104f169c0eaSGlenn Lagasse int 105f169c0eaSGlenn Lagasse be_init(nvlist_t *be_attrs) 106f169c0eaSGlenn Lagasse { 107f169c0eaSGlenn Lagasse be_transaction_data_t bt = { 0 }; 108f169c0eaSGlenn Lagasse zpool_handle_t *zlp; 109f169c0eaSGlenn Lagasse nvlist_t *zfs_props = NULL; 110f169c0eaSGlenn Lagasse char nbe_root_ds[MAXPATHLEN]; 111f169c0eaSGlenn Lagasse char child_fs[MAXPATHLEN]; 112f169c0eaSGlenn Lagasse char **fs_names = NULL; 113f169c0eaSGlenn Lagasse char **shared_fs_names = NULL; 114f169c0eaSGlenn Lagasse uint16_t fs_num = 0; 115f169c0eaSGlenn Lagasse uint16_t shared_fs_num = 0; 116f169c0eaSGlenn Lagasse int nelem; 117f169c0eaSGlenn Lagasse int i; 118f169c0eaSGlenn Lagasse int zret = 0, ret = BE_SUCCESS; 119f169c0eaSGlenn Lagasse 120f169c0eaSGlenn Lagasse /* Initialize libzfs handle */ 121f169c0eaSGlenn Lagasse if (!be_zfs_init()) 122f169c0eaSGlenn Lagasse return (BE_ERR_INIT); 123f169c0eaSGlenn Lagasse 124f169c0eaSGlenn Lagasse /* Get new BE name */ 125f169c0eaSGlenn Lagasse if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_NAME, &bt.nbe_name) 126f169c0eaSGlenn Lagasse != 0) { 127f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to lookup " 128f169c0eaSGlenn Lagasse "BE_ATTR_NEW_BE_NAME attribute\n")); 129f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 130f169c0eaSGlenn Lagasse } 131f169c0eaSGlenn Lagasse 132f169c0eaSGlenn Lagasse /* Validate new BE name */ 133f169c0eaSGlenn Lagasse if (!be_valid_be_name(bt.nbe_name)) { 134f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: invalid BE name %s\n"), 135f169c0eaSGlenn Lagasse bt.nbe_name); 136f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 137f169c0eaSGlenn Lagasse } 138f169c0eaSGlenn Lagasse 139f169c0eaSGlenn Lagasse /* Get zpool name */ 140f169c0eaSGlenn Lagasse if (nvlist_lookup_string(be_attrs, BE_ATTR_NEW_BE_POOL, &bt.nbe_zpool) 141f169c0eaSGlenn Lagasse != 0) { 142f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to lookup " 143f169c0eaSGlenn Lagasse "BE_ATTR_NEW_BE_POOL attribute\n")); 144f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 145f169c0eaSGlenn Lagasse } 146f169c0eaSGlenn Lagasse 147f169c0eaSGlenn Lagasse /* Get file system attributes */ 148f169c0eaSGlenn Lagasse nelem = 0; 149f169c0eaSGlenn Lagasse if (nvlist_lookup_pairs(be_attrs, 0, 150f169c0eaSGlenn Lagasse BE_ATTR_FS_NUM, DATA_TYPE_UINT16, &fs_num, 151f169c0eaSGlenn Lagasse BE_ATTR_FS_NAMES, DATA_TYPE_STRING_ARRAY, &fs_names, &nelem, 152f169c0eaSGlenn Lagasse NULL) != 0) { 153f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to lookup fs " 154f169c0eaSGlenn Lagasse "attributes\n")); 155f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 156f169c0eaSGlenn Lagasse } 157f169c0eaSGlenn Lagasse if (nelem != fs_num) { 158f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: size of FS_NAMES array (%d) " 159f169c0eaSGlenn Lagasse "does not match FS_NUM (%d)\n"), nelem, fs_num); 160f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 161f169c0eaSGlenn Lagasse } 162f169c0eaSGlenn Lagasse 163f169c0eaSGlenn Lagasse /* Get shared file system attributes */ 164f169c0eaSGlenn Lagasse nelem = 0; 165f169c0eaSGlenn Lagasse if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 166f169c0eaSGlenn Lagasse BE_ATTR_SHARED_FS_NUM, DATA_TYPE_UINT16, &shared_fs_num, 167f169c0eaSGlenn Lagasse BE_ATTR_SHARED_FS_NAMES, DATA_TYPE_STRING_ARRAY, &shared_fs_names, 168f169c0eaSGlenn Lagasse &nelem, NULL) != 0) { 169f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to lookup " 170f169c0eaSGlenn Lagasse "shared fs attributes\n")); 171f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 172f169c0eaSGlenn Lagasse } 173f169c0eaSGlenn Lagasse if (nelem != shared_fs_num) { 174f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: size of SHARED_FS_NAMES " 175f169c0eaSGlenn Lagasse "array does not match SHARED_FS_NUM\n")); 176f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 177f169c0eaSGlenn Lagasse } 178f169c0eaSGlenn Lagasse 179f169c0eaSGlenn Lagasse /* Verify that nbe_zpool exists */ 180f169c0eaSGlenn Lagasse if ((zlp = zpool_open(g_zfs, bt.nbe_zpool)) == NULL) { 181f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to " 182f169c0eaSGlenn Lagasse "find existing zpool (%s): %s\n"), bt.nbe_zpool, 183f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs)); 184f169c0eaSGlenn Lagasse return (zfs_err_to_be_err(g_zfs)); 185f169c0eaSGlenn Lagasse } 186f169c0eaSGlenn Lagasse zpool_close(zlp); 187f169c0eaSGlenn Lagasse 188f169c0eaSGlenn Lagasse /* 189f169c0eaSGlenn Lagasse * Verify BE container dataset in nbe_zpool exists. 190f169c0eaSGlenn Lagasse * If not, create it. 191f169c0eaSGlenn Lagasse */ 192f169c0eaSGlenn Lagasse if (!be_create_container_ds(bt.nbe_zpool)) 193f169c0eaSGlenn Lagasse return (BE_ERR_CREATDS); 194f169c0eaSGlenn Lagasse 195f169c0eaSGlenn Lagasse /* 196f169c0eaSGlenn Lagasse * Verify that nbe_name doesn't already exist in some pool. 197f169c0eaSGlenn Lagasse */ 198f169c0eaSGlenn Lagasse if ((zret = zpool_iter(g_zfs, be_exists_callback, bt.nbe_name)) > 0) { 199f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: BE (%s) already exists\n"), 200f169c0eaSGlenn Lagasse bt.nbe_name); 201f169c0eaSGlenn Lagasse return (BE_ERR_BE_EXISTS); 202f169c0eaSGlenn Lagasse } else if (zret < 0) { 203f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: zpool_iter failed: %s\n"), 204f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs)); 205f169c0eaSGlenn Lagasse return (zfs_err_to_be_err(g_zfs)); 206f169c0eaSGlenn Lagasse } 207f169c0eaSGlenn Lagasse 208f169c0eaSGlenn Lagasse /* Generate string for BE's root dataset */ 209f169c0eaSGlenn Lagasse be_make_root_ds(bt.nbe_zpool, bt.nbe_name, nbe_root_ds, 210f169c0eaSGlenn Lagasse sizeof (nbe_root_ds)); 211f169c0eaSGlenn Lagasse 212f169c0eaSGlenn Lagasse /* 213f169c0eaSGlenn Lagasse * Create property list for new BE root dataset. If some 214f169c0eaSGlenn Lagasse * zfs properties were already provided by the caller, dup 215f169c0eaSGlenn Lagasse * that list. Otherwise initialize a new property list. 216f169c0eaSGlenn Lagasse */ 217f169c0eaSGlenn Lagasse if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 218f169c0eaSGlenn Lagasse BE_ATTR_ZFS_PROPERTIES, DATA_TYPE_NVLIST, &zfs_props, NULL) 219f169c0eaSGlenn Lagasse != 0) { 220f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to lookup " 221f169c0eaSGlenn Lagasse "BE_ATTR_ZFS_PROPERTIES attribute\n")); 222f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 223f169c0eaSGlenn Lagasse } 224f169c0eaSGlenn Lagasse if (zfs_props != NULL) { 225f169c0eaSGlenn Lagasse /* Make sure its a unique nvlist */ 226f169c0eaSGlenn Lagasse if (!(zfs_props->nvl_nvflag & NV_UNIQUE_NAME) && 227f169c0eaSGlenn Lagasse !(zfs_props->nvl_nvflag & NV_UNIQUE_NAME_TYPE)) { 228f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: ZFS property list " 229f169c0eaSGlenn Lagasse "not unique\n")); 230f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 231f169c0eaSGlenn Lagasse } 232f169c0eaSGlenn Lagasse 233f169c0eaSGlenn Lagasse /* Dup the list */ 234f169c0eaSGlenn Lagasse if (nvlist_dup(zfs_props, &bt.nbe_zfs_props, 0) != 0) { 235f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to dup ZFS " 236f169c0eaSGlenn Lagasse "property list\n")); 237f169c0eaSGlenn Lagasse return (BE_ERR_NOMEM); 238f169c0eaSGlenn Lagasse } 239f169c0eaSGlenn Lagasse } else { 240f169c0eaSGlenn Lagasse /* Initialize new nvlist */ 241f169c0eaSGlenn Lagasse if (nvlist_alloc(&bt.nbe_zfs_props, NV_UNIQUE_NAME, 0) != 0) { 242f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: internal " 243f169c0eaSGlenn Lagasse "error: out of memory\n")); 244f169c0eaSGlenn Lagasse return (BE_ERR_NOMEM); 245f169c0eaSGlenn Lagasse } 246f169c0eaSGlenn Lagasse } 247f169c0eaSGlenn Lagasse 248f169c0eaSGlenn Lagasse /* Set the mountpoint property for the root dataset */ 249f169c0eaSGlenn Lagasse if (nvlist_add_string(bt.nbe_zfs_props, 250f169c0eaSGlenn Lagasse zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), "/") != 0) { 251f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: internal error " 252f169c0eaSGlenn Lagasse "out of memory\n")); 253f169c0eaSGlenn Lagasse ret = BE_ERR_NOMEM; 254f169c0eaSGlenn Lagasse goto done; 255f169c0eaSGlenn Lagasse } 256f169c0eaSGlenn Lagasse 257f169c0eaSGlenn Lagasse /* Set the 'canmount' property */ 258f169c0eaSGlenn Lagasse if (nvlist_add_string(bt.nbe_zfs_props, 259f169c0eaSGlenn Lagasse zfs_prop_to_name(ZFS_PROP_CANMOUNT), "noauto") != 0) { 260f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: internal error " 261f169c0eaSGlenn Lagasse "out of memory\n")); 262f169c0eaSGlenn Lagasse ret = BE_ERR_NOMEM; 263f169c0eaSGlenn Lagasse goto done; 264f169c0eaSGlenn Lagasse } 265f169c0eaSGlenn Lagasse 266f169c0eaSGlenn Lagasse /* Create BE root dataset for the new BE */ 267f169c0eaSGlenn Lagasse if (zfs_create(g_zfs, nbe_root_ds, ZFS_TYPE_FILESYSTEM, 268f169c0eaSGlenn Lagasse bt.nbe_zfs_props) != 0) { 269f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to " 270f169c0eaSGlenn Lagasse "create BE root dataset (%s): %s\n"), nbe_root_ds, 271f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs)); 272f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs); 273f169c0eaSGlenn Lagasse goto done; 274f169c0eaSGlenn Lagasse } 275f169c0eaSGlenn Lagasse 276f169c0eaSGlenn Lagasse /* Set UUID for new BE */ 277f169c0eaSGlenn Lagasse if ((ret = be_set_uuid(nbe_root_ds)) != BE_SUCCESS) { 278f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to " 279f169c0eaSGlenn Lagasse "set uuid for new BE\n")); 280f169c0eaSGlenn Lagasse } 281f169c0eaSGlenn Lagasse 282f169c0eaSGlenn Lagasse /* 283f169c0eaSGlenn Lagasse * Clear the mountpoint property so that the non-shared 284f169c0eaSGlenn Lagasse * file systems created below inherit their mountpoints. 285f169c0eaSGlenn Lagasse */ 286f169c0eaSGlenn Lagasse (void) nvlist_remove(bt.nbe_zfs_props, 287f169c0eaSGlenn Lagasse zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), DATA_TYPE_STRING); 288f169c0eaSGlenn Lagasse 289f169c0eaSGlenn Lagasse /* Create the new BE's non-shared file systems */ 290f169c0eaSGlenn Lagasse for (i = 0; i < fs_num && fs_names[i]; i++) { 291f169c0eaSGlenn Lagasse /* 292f169c0eaSGlenn Lagasse * If fs == "/", skip it; 293f169c0eaSGlenn Lagasse * we already created the root dataset 294f169c0eaSGlenn Lagasse */ 295f169c0eaSGlenn Lagasse if (strcmp(fs_names[i], "/") == 0) 296f169c0eaSGlenn Lagasse continue; 297f169c0eaSGlenn Lagasse 298f169c0eaSGlenn Lagasse /* Generate string for file system */ 299f169c0eaSGlenn Lagasse (void) snprintf(child_fs, sizeof (child_fs), "%s%s", 300f169c0eaSGlenn Lagasse nbe_root_ds, fs_names[i]); 301f169c0eaSGlenn Lagasse 302f169c0eaSGlenn Lagasse /* Create file system */ 303f169c0eaSGlenn Lagasse if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM, 304f169c0eaSGlenn Lagasse bt.nbe_zfs_props) != 0) { 305f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to create " 306f169c0eaSGlenn Lagasse "BE's child dataset (%s): %s\n"), child_fs, 307f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs)); 308f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs); 309f169c0eaSGlenn Lagasse goto done; 310f169c0eaSGlenn Lagasse } 311f169c0eaSGlenn Lagasse } 312f169c0eaSGlenn Lagasse 313f169c0eaSGlenn Lagasse /* Create the new BE's shared file systems */ 314f169c0eaSGlenn Lagasse if (shared_fs_num > 0) { 315f169c0eaSGlenn Lagasse nvlist_t *props = NULL; 316f169c0eaSGlenn Lagasse 317f169c0eaSGlenn Lagasse if (nvlist_alloc(&props, NV_UNIQUE_NAME, 0) != 0) { 318f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: nvlist_alloc failed\n")); 319f169c0eaSGlenn Lagasse ret = BE_ERR_NOMEM; 320f169c0eaSGlenn Lagasse goto done; 321f169c0eaSGlenn Lagasse } 322f169c0eaSGlenn Lagasse 323f169c0eaSGlenn Lagasse for (i = 0; i < shared_fs_num; i++) { 324f169c0eaSGlenn Lagasse /* Generate string for shared file system */ 325f169c0eaSGlenn Lagasse (void) snprintf(child_fs, sizeof (child_fs), "%s%s", 326f169c0eaSGlenn Lagasse bt.nbe_zpool, shared_fs_names[i]); 327f169c0eaSGlenn Lagasse 328f169c0eaSGlenn Lagasse if (nvlist_add_string(props, 329f169c0eaSGlenn Lagasse zfs_prop_to_name(ZFS_PROP_MOUNTPOINT), 330f169c0eaSGlenn Lagasse shared_fs_names[i]) != 0) { 331f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: " 332f169c0eaSGlenn Lagasse "internal error: out of memory\n")); 333f169c0eaSGlenn Lagasse nvlist_free(props); 334f169c0eaSGlenn Lagasse ret = BE_ERR_NOMEM; 335f169c0eaSGlenn Lagasse goto done; 336f169c0eaSGlenn Lagasse } 337f169c0eaSGlenn Lagasse 338f169c0eaSGlenn Lagasse /* Create file system if it doesn't already exist */ 339f169c0eaSGlenn Lagasse if (zfs_dataset_exists(g_zfs, child_fs, 340f169c0eaSGlenn Lagasse ZFS_TYPE_FILESYSTEM)) { 341f169c0eaSGlenn Lagasse continue; 342f169c0eaSGlenn Lagasse } 343f169c0eaSGlenn Lagasse if (zfs_create(g_zfs, child_fs, ZFS_TYPE_FILESYSTEM, 344f169c0eaSGlenn Lagasse props) != 0) { 345f169c0eaSGlenn Lagasse be_print_err(gettext("be_init: failed to " 346f169c0eaSGlenn Lagasse "create BE's shared dataset (%s): %s\n"), 347f169c0eaSGlenn Lagasse child_fs, libzfs_error_description(g_zfs)); 348f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs); 349f169c0eaSGlenn Lagasse nvlist_free(props); 350f169c0eaSGlenn Lagasse goto done; 351f169c0eaSGlenn Lagasse } 352f169c0eaSGlenn Lagasse } 353f169c0eaSGlenn Lagasse 354f169c0eaSGlenn Lagasse nvlist_free(props); 355f169c0eaSGlenn Lagasse } 356f169c0eaSGlenn Lagasse 357f169c0eaSGlenn Lagasse done: 358aab83bb8SJosef 'Jeff' Sipek nvlist_free(bt.nbe_zfs_props); 359f169c0eaSGlenn Lagasse 360f169c0eaSGlenn Lagasse be_zfs_fini(); 361f169c0eaSGlenn Lagasse 362f169c0eaSGlenn Lagasse return (ret); 363f169c0eaSGlenn Lagasse } 364f169c0eaSGlenn Lagasse 365f169c0eaSGlenn Lagasse /* 366f169c0eaSGlenn Lagasse * Function: be_destroy 367f169c0eaSGlenn Lagasse * Description: Destroy a BE and all of its children datasets, snapshots and 368f169c0eaSGlenn Lagasse * zones that belong to the parent BE. 369f169c0eaSGlenn Lagasse * Parameters: 370f169c0eaSGlenn Lagasse * be_attrs - pointer to nvlist_t of attributes being passed in. 371f169c0eaSGlenn Lagasse * The following attributes are used by this function: 372f169c0eaSGlenn Lagasse * 373f169c0eaSGlenn Lagasse * BE_ATTR_ORIG_BE_NAME *required 374f169c0eaSGlenn Lagasse * BE_ATTR_DESTROY_FLAGS *optional 375f169c0eaSGlenn Lagasse * Return: 376f169c0eaSGlenn Lagasse * BE_SUCCESS - Success 377f169c0eaSGlenn Lagasse * be_errno_t - Failure 378f169c0eaSGlenn Lagasse * Scope: 379f169c0eaSGlenn Lagasse * Public 380f169c0eaSGlenn Lagasse */ 381f169c0eaSGlenn Lagasse int 382f169c0eaSGlenn Lagasse be_destroy(nvlist_t *be_attrs) 383f169c0eaSGlenn Lagasse { 384f169c0eaSGlenn Lagasse zfs_handle_t *zhp = NULL; 385f169c0eaSGlenn Lagasse be_transaction_data_t bt = { 0 }; 386f169c0eaSGlenn Lagasse be_transaction_data_t cur_bt = { 0 }; 387f169c0eaSGlenn Lagasse be_destroy_data_t dd = { 0 }; 388f169c0eaSGlenn Lagasse int ret = BE_SUCCESS; 389f169c0eaSGlenn Lagasse uint16_t flags = 0; 3905ee7c793SAlexander Eremin boolean_t bs_found = B_FALSE; 391f169c0eaSGlenn Lagasse int zret; 392f169c0eaSGlenn Lagasse char obe_root_ds[MAXPATHLEN]; 393f169c0eaSGlenn Lagasse char *mp = NULL; 394f169c0eaSGlenn Lagasse 395f169c0eaSGlenn Lagasse /* Initialize libzfs handle */ 396f169c0eaSGlenn Lagasse if (!be_zfs_init()) 397f169c0eaSGlenn Lagasse return (BE_ERR_INIT); 398f169c0eaSGlenn Lagasse 399f169c0eaSGlenn Lagasse /* Get name of BE to delete */ 400f169c0eaSGlenn Lagasse if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &bt.obe_name) 401f169c0eaSGlenn Lagasse != 0) { 402f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to lookup " 403f169c0eaSGlenn Lagasse "BE_ATTR_ORIG_BE_NAME attribute\n")); 404f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 405f169c0eaSGlenn Lagasse } 406f169c0eaSGlenn Lagasse 407f169c0eaSGlenn Lagasse /* 408f169c0eaSGlenn Lagasse * Validate BE name. If valid, then check that the original BE is not 409f169c0eaSGlenn Lagasse * the active BE. If it is the 'active' BE then return an error code 410f169c0eaSGlenn Lagasse * since we can't destroy the active BE. 411f169c0eaSGlenn Lagasse */ 412f169c0eaSGlenn Lagasse if (!be_valid_be_name(bt.obe_name)) { 413f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: invalid BE name %s\n"), 414f169c0eaSGlenn Lagasse bt.obe_name); 415f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 416f169c0eaSGlenn Lagasse } else if (bt.obe_name != NULL) { 417f169c0eaSGlenn Lagasse if ((ret = be_find_current_be(&cur_bt)) != BE_SUCCESS) { 418f169c0eaSGlenn Lagasse return (ret); 419f169c0eaSGlenn Lagasse } 420f169c0eaSGlenn Lagasse if (strcmp(cur_bt.obe_name, bt.obe_name) == 0) { 421f169c0eaSGlenn Lagasse return (BE_ERR_DESTROY_CURR_BE); 422f169c0eaSGlenn Lagasse } 423f169c0eaSGlenn Lagasse } 424f169c0eaSGlenn Lagasse 425f169c0eaSGlenn Lagasse /* Get destroy flags if provided */ 426f169c0eaSGlenn Lagasse if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK, 427f169c0eaSGlenn Lagasse BE_ATTR_DESTROY_FLAGS, DATA_TYPE_UINT16, &flags, NULL) 428f169c0eaSGlenn Lagasse != 0) { 429f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to lookup " 430f169c0eaSGlenn Lagasse "BE_ATTR_DESTROY_FLAGS attribute\n")); 431f169c0eaSGlenn Lagasse return (BE_ERR_INVAL); 432f169c0eaSGlenn Lagasse } 433f169c0eaSGlenn Lagasse 434f169c0eaSGlenn Lagasse dd.destroy_snaps = flags & BE_DESTROY_FLAG_SNAPSHOTS; 435f169c0eaSGlenn Lagasse dd.force_unmount = flags & BE_DESTROY_FLAG_FORCE_UNMOUNT; 436f169c0eaSGlenn Lagasse 437f169c0eaSGlenn Lagasse /* Find which zpool obe_name lives in */ 438f169c0eaSGlenn Lagasse if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &bt)) == 0) { 439f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to find zpool " 440f169c0eaSGlenn Lagasse "for BE (%s)\n"), bt.obe_name); 441f169c0eaSGlenn Lagasse return (BE_ERR_BE_NOENT); 442f169c0eaSGlenn Lagasse } else if (zret < 0) { 443f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: zpool_iter failed: %s\n"), 444f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs)); 445f169c0eaSGlenn Lagasse return (zfs_err_to_be_err(g_zfs)); 446f169c0eaSGlenn Lagasse } 447f169c0eaSGlenn Lagasse 448f169c0eaSGlenn Lagasse /* Generate string for obe_name's root dataset */ 449f169c0eaSGlenn Lagasse be_make_root_ds(bt.obe_zpool, bt.obe_name, obe_root_ds, 450f169c0eaSGlenn Lagasse sizeof (obe_root_ds)); 451f169c0eaSGlenn Lagasse bt.obe_root_ds = obe_root_ds; 452f169c0eaSGlenn Lagasse 4537e0e2549SAlexander Eremin if (getzoneid() != GLOBAL_ZONEID) { 4547e0e2549SAlexander Eremin if (!be_zone_compare_uuids(bt.obe_root_ds)) { 4557e0e2549SAlexander Eremin if (be_is_active_on_boot(bt.obe_name)) { 4567e0e2549SAlexander Eremin be_print_err(gettext("be_destroy: destroying " 4577e0e2549SAlexander Eremin "active zone root dataset from non-active " 4587e0e2549SAlexander Eremin "global BE is not supported\n")); 4597e0e2549SAlexander Eremin return (BE_ERR_NOTSUP); 4607e0e2549SAlexander Eremin } 4617e0e2549SAlexander Eremin } 4627e0e2549SAlexander Eremin } 4637e0e2549SAlexander Eremin 464f169c0eaSGlenn Lagasse /* 465f169c0eaSGlenn Lagasse * Detect if the BE to destroy has the 'active on boot' property set. 466f169c0eaSGlenn Lagasse * If so, set the 'active on boot' property on the the 'active' BE. 467f169c0eaSGlenn Lagasse */ 468f169c0eaSGlenn Lagasse if (be_is_active_on_boot(bt.obe_name)) { 469f169c0eaSGlenn Lagasse if ((ret = be_activate_current_be()) != BE_SUCCESS) { 470f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to " 471f169c0eaSGlenn Lagasse "make the current BE 'active on boot'\n")); 472f169c0eaSGlenn Lagasse return (ret); 473f169c0eaSGlenn Lagasse } 474f169c0eaSGlenn Lagasse } 475f169c0eaSGlenn Lagasse 476*f976337aSAndy Fiddaman /* 477*f976337aSAndy Fiddaman * Detect if the BE to destroy is referenced in the pool's nextboot 478*f976337aSAndy Fiddaman * field, and unset it if so. 479*f976337aSAndy Fiddaman */ 480*f976337aSAndy Fiddaman if (getzoneid() == GLOBAL_ZONEID) { 481*f976337aSAndy Fiddaman char *nextboot = NULL; 482*f976337aSAndy Fiddaman 483*f976337aSAndy Fiddaman if (lzbe_get_boot_device(bt.obe_zpool, &nextboot) == 0 && 484*f976337aSAndy Fiddaman nextboot != NULL && strcmp(nextboot, bt.obe_root_ds) == 0) { 485*f976337aSAndy Fiddaman if (lzbe_set_boot_device(bt.obe_zpool, 486*f976337aSAndy Fiddaman lzbe_add, "") != 0) { 487*f976337aSAndy Fiddaman be_print_err(gettext("be_destroy: failed to " 488*f976337aSAndy Fiddaman "remove temporary activation for " 489*f976337aSAndy Fiddaman "dataset %s on pool %s\n"), 490*f976337aSAndy Fiddaman bt.obe_root_ds, bt.obe_zpool); 491*f976337aSAndy Fiddaman free(nextboot); 492*f976337aSAndy Fiddaman return (BE_ERR_UNKNOWN); 493*f976337aSAndy Fiddaman } 494*f976337aSAndy Fiddaman } 495*f976337aSAndy Fiddaman free(nextboot); 496*f976337aSAndy Fiddaman } 497*f976337aSAndy Fiddaman 498f169c0eaSGlenn Lagasse /* Get handle to BE's root dataset */ 499f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, bt.obe_root_ds, ZFS_TYPE_FILESYSTEM)) == 500f169c0eaSGlenn Lagasse NULL) { 501f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to " 502f169c0eaSGlenn Lagasse "open BE root dataset (%s): %s\n"), bt.obe_root_ds, 503f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs)); 504f169c0eaSGlenn Lagasse return (zfs_err_to_be_err(g_zfs)); 505f169c0eaSGlenn Lagasse } 506f169c0eaSGlenn Lagasse 5075ee7c793SAlexander Eremin /* 5085ee7c793SAlexander Eremin * Check if BE has snapshots and BE_DESTROY_FLAG_SNAPSHOTS 5095ee7c793SAlexander Eremin * is not set. 5105ee7c793SAlexander Eremin */ 5110d8fa8f8SMartin Matuska (void) zfs_iter_snapshots(zhp, B_FALSE, be_has_snapshot_callback, 5120d8fa8f8SMartin Matuska &bs_found); 5135ee7c793SAlexander Eremin if (!dd.destroy_snaps && bs_found) { 5145ee7c793SAlexander Eremin ZFS_CLOSE(zhp); 5155ee7c793SAlexander Eremin return (BE_ERR_SS_EXISTS); 5165ee7c793SAlexander Eremin } 5175ee7c793SAlexander Eremin 518f169c0eaSGlenn Lagasse /* Get the UUID of the global BE */ 5197e0e2549SAlexander Eremin if (getzoneid() == GLOBAL_ZONEID) { 5207e0e2549SAlexander Eremin if (be_get_uuid(zfs_get_name(zhp), 5217e0e2549SAlexander Eremin &dd.gz_be_uuid) != BE_SUCCESS) { 5227e0e2549SAlexander Eremin be_print_err(gettext("be_destroy: BE has no " 5237e0e2549SAlexander Eremin "UUID (%s)\n"), zfs_get_name(zhp)); 5247e0e2549SAlexander Eremin } 525f169c0eaSGlenn Lagasse } 526f169c0eaSGlenn Lagasse 527f169c0eaSGlenn Lagasse /* 528f169c0eaSGlenn Lagasse * If the global BE is mounted, make sure we've been given the 529f169c0eaSGlenn Lagasse * flag to forcibly unmount it. 530f169c0eaSGlenn Lagasse */ 531f169c0eaSGlenn Lagasse if (zfs_is_mounted(zhp, &mp)) { 532f169c0eaSGlenn Lagasse if (!(dd.force_unmount)) { 533f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: " 534f169c0eaSGlenn Lagasse "%s is currently mounted at %s, cannot destroy\n"), 535f169c0eaSGlenn Lagasse bt.obe_name, mp != NULL ? mp : "<unknown>"); 536f169c0eaSGlenn Lagasse 537f169c0eaSGlenn Lagasse free(mp); 538f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp); 539f169c0eaSGlenn Lagasse return (BE_ERR_MOUNTED); 540f169c0eaSGlenn Lagasse } 541f169c0eaSGlenn Lagasse free(mp); 542f169c0eaSGlenn Lagasse } 543f169c0eaSGlenn Lagasse 544f169c0eaSGlenn Lagasse /* 545f169c0eaSGlenn Lagasse * Destroy the non-global zone BE's if we are in the global zone 546f169c0eaSGlenn Lagasse * and there is a UUID associated with the global zone BE 547f169c0eaSGlenn Lagasse */ 548f169c0eaSGlenn Lagasse if (getzoneid() == GLOBAL_ZONEID && !uuid_is_null(dd.gz_be_uuid)) { 549f169c0eaSGlenn Lagasse if ((ret = be_destroy_zones(bt.obe_name, bt.obe_root_ds, &dd)) 550f169c0eaSGlenn Lagasse != BE_SUCCESS) { 551f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to " 552f169c0eaSGlenn Lagasse "destroy one or more zones for BE %s\n"), 553f169c0eaSGlenn Lagasse bt.obe_name); 554f169c0eaSGlenn Lagasse goto done; 555f169c0eaSGlenn Lagasse } 556f169c0eaSGlenn Lagasse } 557f169c0eaSGlenn Lagasse 558f169c0eaSGlenn Lagasse /* Unmount the BE if it was mounted */ 559f169c0eaSGlenn Lagasse if (zfs_is_mounted(zhp, NULL)) { 560f169c0eaSGlenn Lagasse if ((ret = _be_unmount(bt.obe_name, BE_UNMOUNT_FLAG_FORCE)) 561f169c0eaSGlenn Lagasse != BE_SUCCESS) { 562f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: " 563f169c0eaSGlenn Lagasse "failed to unmount %s\n"), bt.obe_name); 564f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp); 565f169c0eaSGlenn Lagasse return (ret); 566f169c0eaSGlenn Lagasse } 567f169c0eaSGlenn Lagasse } 568f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp); 569f169c0eaSGlenn Lagasse 570f169c0eaSGlenn Lagasse /* Destroy this BE */ 571f169c0eaSGlenn Lagasse if ((ret = _be_destroy((const char *)bt.obe_root_ds, &dd)) 572f169c0eaSGlenn Lagasse != BE_SUCCESS) { 573f169c0eaSGlenn Lagasse goto done; 574f169c0eaSGlenn Lagasse } 575f169c0eaSGlenn Lagasse 576f169c0eaSGlenn Lagasse /* Remove BE's entry from the boot menu */ 577f169c0eaSGlenn Lagasse if (getzoneid() == GLOBAL_ZONEID) { 578f169c0eaSGlenn Lagasse if ((ret = be_remove_menu(bt.obe_name, bt.obe_zpool, NULL)) 579f169c0eaSGlenn Lagasse != BE_SUCCESS) { 580f169c0eaSGlenn Lagasse be_print_err(gettext("be_destroy: failed to " 581f169c0eaSGlenn Lagasse "remove BE %s from the boot menu\n"), 582f169c0eaSGlenn Lagasse bt.obe_root_ds); 583f169c0eaSGlenn Lagasse goto done; 584f169c0eaSGlenn Lagasse } 585f169c0eaSGlenn Lagasse } 586f169c0eaSGlenn Lagasse 587f169c0eaSGlenn Lagasse done: 588f169c0eaSGlenn Lagasse be_zfs_fini(); 589f169c0eaSGlenn Lagasse 590f169c0eaSGlenn Lagasse return (ret); 591f169c0eaSGlenn Lagasse } 592f169c0eaSGlenn Lagasse 593f169c0eaSGlenn Lagasse /* 594f169c0eaSGlenn Lagasse * Function: be_copy 595f169c0eaSGlenn Lagasse * Description: This function makes a copy of an existing BE. If the original 596f169c0eaSGlenn Lagasse * BE and the new BE are in the same pool, it uses zfs cloning to 597f169c0eaSGlenn Lagasse * create the new BE, otherwise it does a physical copy. 598f169c0eaSGlenn Lagasse * If the original BE name isn't provided, it uses the currently 599f169c0eaSGlenn Lagasse * booted BE. If the new BE name isn't provided, it creates an 600f169c0eaSGlenn Lagasse * auto named BE and returns that name to the caller. 601f169c0eaSGlenn Lagasse * Parameters: 602f169c0eaSGlenn Lagasse * be_attrs - pointer to nvlist_t of attributes being passed in. 603f169c0eaSGlenn Lagasse * The following attributes are used by this function: 604f169c0eaSGlenn Lagasse * 605