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