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.
24f169c0eaSGlenn Lagasse  */
25f169c0eaSGlenn Lagasse 
267e0e2549SAlexander Eremin /*
271a902ef8SHans Rosenfeld  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28ce3cb817SToomas Soome  * Copyright 2016 Toomas Soome <tsoome@me.com>
29*ec8422d0SAndy Fiddaman  * Copyright 2022 OmniOS Community Edition (OmniOSce) Association.
307e0e2549SAlexander Eremin  */
317e0e2549SAlexander Eremin 
32f169c0eaSGlenn Lagasse #include <assert.h>
33f169c0eaSGlenn Lagasse #include <libintl.h>
34f169c0eaSGlenn Lagasse #include <libnvpair.h>
35f169c0eaSGlenn Lagasse #include <libzfs.h>
36f169c0eaSGlenn Lagasse #include <stdio.h>
37f169c0eaSGlenn Lagasse #include <stdlib.h>
38f169c0eaSGlenn Lagasse #include <string.h>
391a902ef8SHans Rosenfeld #include <strings.h>
40f169c0eaSGlenn Lagasse #include <errno.h>
41f169c0eaSGlenn Lagasse #include <sys/mnttab.h>
42f169c0eaSGlenn Lagasse #include <sys/types.h>
43f169c0eaSGlenn Lagasse #include <sys/stat.h>
441a902ef8SHans Rosenfeld #include <fcntl.h>
45f169c0eaSGlenn Lagasse #include <unistd.h>
461a902ef8SHans Rosenfeld #include <sys/efi_partition.h>
47f169c0eaSGlenn Lagasse 
48f169c0eaSGlenn Lagasse #include <libbe.h>
49f169c0eaSGlenn Lagasse #include <libbe_priv.h>
50b713c91eSToomas Soome #include <libzfsbootenv.h>
51f169c0eaSGlenn Lagasse 
52f169c0eaSGlenn Lagasse char	*mnttab = MNTTAB;
53f169c0eaSGlenn Lagasse 
54f169c0eaSGlenn Lagasse /*
55f169c0eaSGlenn Lagasse  * Private function prototypes
56f169c0eaSGlenn Lagasse  */
57f169c0eaSGlenn Lagasse static int set_bootfs(char *boot_rpool, char *be_root_ds);
58f169c0eaSGlenn Lagasse static int set_canmount(be_node_list_t *, char *);
59a63c99a2SToomas Soome static boolean_t be_do_install_mbr(char *, nvlist_t *);
60a63c99a2SToomas Soome static int be_do_installboot_helper(zpool_handle_t *, nvlist_t *, char *,
61c7c0ceafSToomas Soome     char *, uint16_t);
62c7c0ceafSToomas Soome static int be_do_installboot(be_transaction_data_t *, uint16_t);
63f169c0eaSGlenn Lagasse static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
64f169c0eaSGlenn Lagasse static int get_ver_from_capfile(char *, char **);
65f169c0eaSGlenn Lagasse static int be_promote_zone_ds(char *, char *);
66f169c0eaSGlenn Lagasse static int be_promote_ds_callback(zfs_handle_t *, void *);
67f169c0eaSGlenn Lagasse 
68f169c0eaSGlenn Lagasse /* ******************************************************************** */
69f169c0eaSGlenn Lagasse /*			Public Functions				*/
70f169c0eaSGlenn Lagasse /* ******************************************************************** */
71f169c0eaSGlenn Lagasse 
72f169c0eaSGlenn Lagasse /*
73f169c0eaSGlenn Lagasse  * Function:	be_activate
74f169c0eaSGlenn Lagasse  * Description:	Calls _be_activate which activates the BE named in the
75f169c0eaSGlenn Lagasse  *		attributes passed in through be_attrs. The process of
76f169c0eaSGlenn Lagasse  *		activation sets the bootfs property of the root pool, resets
77f169c0eaSGlenn Lagasse  *		the canmount property to noauto, and sets the default in the
78f169c0eaSGlenn Lagasse  *		grub menu to the entry corresponding to the entry for the named
79f169c0eaSGlenn Lagasse  *		BE.
80f169c0eaSGlenn Lagasse  * Parameters:
81f169c0eaSGlenn Lagasse  *		be_attrs - pointer to nvlist_t of attributes being passed in.
82f169c0eaSGlenn Lagasse  *			The follow attribute values are used by this function:
83f169c0eaSGlenn Lagasse  *
84f169c0eaSGlenn Lagasse  *			BE_ATTR_ORIG_BE_NAME		*required
85f169c0eaSGlenn Lagasse  * Return:
86f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
87f169c0eaSGlenn Lagasse  *		be_errno_t - Failure
88f169c0eaSGlenn Lagasse  * Scope:
89f169c0eaSGlenn Lagasse  *		Public
90f169c0eaSGlenn Lagasse  */
91f169c0eaSGlenn Lagasse int
be_activate(nvlist_t * be_attrs)92f169c0eaSGlenn Lagasse be_activate(nvlist_t *be_attrs)
93f169c0eaSGlenn Lagasse {
94f169c0eaSGlenn Lagasse 	int	ret = BE_SUCCESS;
95f169c0eaSGlenn Lagasse 	char	*be_name = NULL;
96b713c91eSToomas Soome 	be_nextboot_state_t nextboot;
97b713c91eSToomas Soome 	boolean_t next_boot;
98f169c0eaSGlenn Lagasse 
99f169c0eaSGlenn Lagasse 	/* Initialize libzfs handle */
100f169c0eaSGlenn Lagasse 	if (!be_zfs_init())
101f169c0eaSGlenn Lagasse 		return (BE_ERR_INIT);
102f169c0eaSGlenn Lagasse 
103f169c0eaSGlenn Lagasse 	/* Get the BE name to activate */
104f169c0eaSGlenn Lagasse 	if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
105f169c0eaSGlenn Lagasse 	    != 0) {
106f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_activate: failed to "
107f169c0eaSGlenn Lagasse 		    "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
108f169c0eaSGlenn Lagasse 		be_zfs_fini();
109f169c0eaSGlenn Lagasse 		return (BE_ERR_INVAL);
110f169c0eaSGlenn Lagasse 	}
111f169c0eaSGlenn Lagasse 
112f169c0eaSGlenn Lagasse 	/* Validate BE name */
113f169c0eaSGlenn Lagasse 	if (!be_valid_be_name(be_name)) {
114f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_activate: invalid BE name %s\n"),
115f169c0eaSGlenn Lagasse 		    be_name);
116f169c0eaSGlenn Lagasse 		be_zfs_fini();
117f169c0eaSGlenn Lagasse 		return (BE_ERR_INVAL);
118f169c0eaSGlenn Lagasse 	}
119f169c0eaSGlenn Lagasse 
120b713c91eSToomas Soome 	if (nvlist_lookup_boolean_value(be_attrs, BE_ATTR_ACTIVE_NEXTBOOT,
121b713c91eSToomas Soome 	    &next_boot) == 0) {
122b713c91eSToomas Soome 		if (next_boot)
123b713c91eSToomas Soome 			nextboot = BE_NEXTBOOT_SET;
124b713c91eSToomas Soome 		else
125b713c91eSToomas Soome 			nextboot = BE_NEXTBOOT_UNSET;
126b713c91eSToomas Soome 	} else {
127b713c91eSToomas Soome 		nextboot = BE_NEXTBOOT_IGNORE;
128b713c91eSToomas Soome 	}
129b713c91eSToomas Soome 
130b713c91eSToomas Soome 	ret = _be_activate(be_name, nextboot);
131f169c0eaSGlenn Lagasse 
132f169c0eaSGlenn Lagasse 	be_zfs_fini();
133f169c0eaSGlenn Lagasse 
134f169c0eaSGlenn Lagasse 	return (ret);
135f169c0eaSGlenn Lagasse }
136f169c0eaSGlenn Lagasse 
137c7c0ceafSToomas Soome /*
138c7c0ceafSToomas Soome  * Function:	be_installboot
139c7c0ceafSToomas Soome  * Description:	Calls be_do_installboot to install/update bootloader on
140c7c0ceafSToomas Soome  *		pool passed in through be_attrs. The primary consumer is
141c7c0ceafSToomas Soome  *		bootadm command to avoid duplication of the code.
142c7c0ceafSToomas Soome  * Parameters:
143c7c0ceafSToomas Soome  *		be_attrs - pointer to nvlist_t of attributes being passed in.
144c7c0ceafSToomas Soome  *			The following attribute values are used:
145c7c0ceafSToomas Soome  *
146c7c0ceafSToomas Soome  *			BE_ATTR_ORIG_BE_NAME		*required
147c7c0ceafSToomas Soome  *			BE_ATTR_ORIG_BE_POOL		*required
148c7c0ceafSToomas Soome  *			BE_ATTR_ORIG_BE_ROOT		*required
149c7c0ceafSToomas Soome  *			BE_ATTR_INSTALL_FLAGS		optional
150c7c0ceafSToomas Soome  *
151c7c0ceafSToomas Soome  * Return:
152c7c0ceafSToomas Soome  *		BE_SUCCESS - Success
153c7c0ceafSToomas Soome  *		be_errno_t - Failure
154c7c0ceafSToomas Soome  * Scope:
155c7c0ceafSToomas Soome  *		Public
156c7c0ceafSToomas Soome  */
157c7c0ceafSToomas Soome int
be_installboot(nvlist_t * be_attrs)158c7c0ceafSToomas Soome be_installboot(nvlist_t *be_attrs)
159c7c0ceafSToomas Soome {
160c7c0ceafSToomas Soome 	int		ret = BE_SUCCESS;
161c7c0ceafSToomas Soome 	uint16_t	flags = 0;
162c7c0ceafSToomas Soome 	uint16_t	verbose;
163c7c0ceafSToomas Soome 	be_transaction_data_t bt = { 0 };
164c7c0ceafSToomas Soome 
165c7c0ceafSToomas Soome 	/* Get flags */
166c7c0ceafSToomas Soome 	if (nvlist_lookup_pairs(be_attrs, NV_FLAG_NOENTOK,
167c7c0ceafSToomas Soome 	    BE_ATTR_INSTALL_FLAGS, DATA_TYPE_UINT16, &flags, NULL) != 0) {
168c7c0ceafSToomas Soome 		be_print_err(gettext("be_installboot: failed to lookup "
169c7c0ceafSToomas Soome 		    "BE_ATTR_INSTALL_FLAGS attribute\n"));
170c7c0ceafSToomas Soome 		return (BE_ERR_INVAL);
171c7c0ceafSToomas Soome 	}
172c7c0ceafSToomas Soome 
173c7c0ceafSToomas Soome 	/* Set verbose early, so we get all messages */
174c7c0ceafSToomas Soome 	verbose = flags & BE_INSTALLBOOT_FLAG_VERBOSE;
175c7c0ceafSToomas Soome 	if (verbose == BE_INSTALLBOOT_FLAG_VERBOSE)
176c7c0ceafSToomas Soome 		libbe_print_errors(B_TRUE);
177c7c0ceafSToomas Soome 
178c7c0ceafSToomas Soome 	ret = nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME,
179c7c0ceafSToomas Soome 	    &bt.obe_name);
180c7c0ceafSToomas Soome 	if (ret != 0) {
181c7c0ceafSToomas Soome 		be_print_err(gettext("be_installboot: failed to "
182c7c0ceafSToomas Soome 		    "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
183c7c0ceafSToomas Soome 		return (BE_ERR_INVAL);
184c7c0ceafSToomas Soome 	}
185c7c0ceafSToomas Soome 
186c7c0ceafSToomas Soome 	ret = nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_POOL,
187c7c0ceafSToomas Soome 	    &bt.obe_zpool);
188c7c0ceafSToomas Soome 	if (ret != 0) {
189c7c0ceafSToomas Soome 		be_print_err(gettext("be_installboot: failed to "
190c7c0ceafSToomas Soome 		    "lookup BE_ATTR_ORIG_BE_POOL attribute\n"));
191c7c0ceafSToomas Soome 		return (BE_ERR_INVAL);
192c7c0ceafSToomas Soome 	}
193c7c0ceafSToomas Soome 
194c7c0ceafSToomas Soome 	ret = nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_ROOT,
195c7c0ceafSToomas Soome 	    &bt.obe_root_ds);
196c7c0ceafSToomas Soome 	if (ret != 0) {
197c7c0ceafSToomas Soome 		be_print_err(gettext("be_installboot: failed to "
198c7c0ceafSToomas Soome 		    "lookup BE_ATTR_ORIG_BE_ROOT attribute\n"));
199c7c0ceafSToomas Soome 		return (BE_ERR_INVAL);
200c7c0ceafSToomas Soome 	}
201c7c0ceafSToomas Soome 
202c7c0ceafSToomas Soome 	/* Initialize libzfs handle */
203c7c0ceafSToomas Soome 	if (!be_zfs_init())
204c7c0ceafSToomas Soome 		return (BE_ERR_INIT);
205c7c0ceafSToomas Soome 
206c7c0ceafSToomas Soome 	ret = be_do_installboot(&bt, flags);
207c7c0ceafSToomas Soome 
208c7c0ceafSToomas Soome 	be_zfs_fini();
209c7c0ceafSToomas Soome 
210c7c0ceafSToomas Soome 	return (ret);
211c7c0ceafSToomas Soome }
212c7c0ceafSToomas Soome 
213f169c0eaSGlenn Lagasse /* ******************************************************************** */
214f169c0eaSGlenn Lagasse /*			Semi Private Functions				*/
215f169c0eaSGlenn Lagasse /* ******************************************************************** */
216f169c0eaSGlenn Lagasse 
217f169c0eaSGlenn Lagasse /*
218f169c0eaSGlenn Lagasse  * Function:	_be_activate
219f169c0eaSGlenn Lagasse  * Description:	This does the actual work described in be_activate.
220f169c0eaSGlenn Lagasse  * Parameters:
221f169c0eaSGlenn Lagasse  *		be_name - pointer to the name of BE to activate.
222b713c91eSToomas Soome  *		nextboot - flag to ignore, set or unset nextboot
223f169c0eaSGlenn Lagasse  *
224f169c0eaSGlenn Lagasse  * Return:
225f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
226f169c0eaSGlenn Lagasse  *		be_errnot_t - Failure
227f169c0eaSGlenn Lagasse  * Scope:
228f169c0eaSGlenn Lagasse  *		Public
229f169c0eaSGlenn Lagasse  */
230f169c0eaSGlenn Lagasse int
_be_activate(char * be_name,be_nextboot_state_t nextboot)231b713c91eSToomas Soome _be_activate(char *be_name, be_nextboot_state_t nextboot)
232f169c0eaSGlenn Lagasse {
233f169c0eaSGlenn Lagasse 	be_transaction_data_t cb = { 0 };
234f169c0eaSGlenn Lagasse 	zfs_handle_t	*zhp = NULL;
235f169c0eaSGlenn Lagasse 	char		root_ds[MAXPATHLEN];
2367e0e2549SAlexander Eremin 	char		active_ds[MAXPATHLEN];
237f169c0eaSGlenn Lagasse 	be_node_list_t	*be_nodes = NULL;
238f169c0eaSGlenn Lagasse 	uuid_t		uu = {0};
239f169c0eaSGlenn Lagasse 	int		entry, ret = BE_SUCCESS;
240f169c0eaSGlenn Lagasse 	int		zret = 0;
241f169c0eaSGlenn Lagasse 
242f169c0eaSGlenn Lagasse 	/*
243f169c0eaSGlenn Lagasse 	 * TODO: The BE needs to be validated to make sure that it is actually
244f169c0eaSGlenn Lagasse 	 * a bootable BE.
245f169c0eaSGlenn Lagasse 	 */
246f169c0eaSGlenn Lagasse 
247f169c0eaSGlenn Lagasse 	if (be_name == NULL)
248f169c0eaSGlenn Lagasse 		return (BE_ERR_INVAL);
249f169c0eaSGlenn Lagasse 
250b713c91eSToomas Soome 	if (nextboot == BE_NEXTBOOT_SET && getzoneid() != GLOBAL_ZONEID)
251b713c91eSToomas Soome 		return (BE_ERR_INVAL);
252b713c91eSToomas Soome 
253f169c0eaSGlenn Lagasse 	/* Set obe_name to be_name in the cb structure */
254f169c0eaSGlenn Lagasse 	cb.obe_name = be_name;
255f169c0eaSGlenn Lagasse 
256f169c0eaSGlenn Lagasse 	/* find which zpool the be is in */
257f169c0eaSGlenn Lagasse 	if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
258f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_activate: failed to "
259f169c0eaSGlenn Lagasse 		    "find zpool for BE (%s)\n"), cb.obe_name);
260f169c0eaSGlenn Lagasse 		return (BE_ERR_BE_NOENT);
261f169c0eaSGlenn Lagasse 	} else if (zret < 0) {
262f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_activate: "
263f169c0eaSGlenn Lagasse 		    "zpool_iter failed: %s\n"),
264f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
265f169c0eaSGlenn Lagasse 		ret = zfs_err_to_be_err(g_zfs);
266f169c0eaSGlenn Lagasse 		return (ret);
267f169c0eaSGlenn Lagasse 	}
268f169c0eaSGlenn Lagasse 
269*ec8422d0SAndy Fiddaman 	if ((ret = be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds,
270*ec8422d0SAndy Fiddaman 	    sizeof (root_ds))) != BE_SUCCESS) {
271*ec8422d0SAndy Fiddaman 		be_print_err(gettext("%s: failed to get BE container dataset "
272*ec8422d0SAndy Fiddaman 		    "for %s/%s\n"), __func__, cb.obe_zpool, cb.obe_name);
273*ec8422d0SAndy Fiddaman 		return (ret);
274*ec8422d0SAndy Fiddaman 	}
275f169c0eaSGlenn Lagasse 	cb.obe_root_ds = strdup(root_ds);
276f169c0eaSGlenn Lagasse 
277f169c0eaSGlenn Lagasse 	if (getzoneid() == GLOBAL_ZONEID) {
278c7c0ceafSToomas Soome 		ret = be_do_installboot(&cb, BE_INSTALLBOOT_FLAG_NULL);
279c7c0ceafSToomas Soome 		if (ret != BE_SUCCESS)
280f169c0eaSGlenn Lagasse 			return (ret);
281a63c99a2SToomas Soome 
282f169c0eaSGlenn Lagasse 		if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
283f169c0eaSGlenn Lagasse 			if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
284f169c0eaSGlenn Lagasse 			    NULL, NULL, NULL)) != BE_SUCCESS) {
285f169c0eaSGlenn Lagasse 				be_print_err(gettext("be_activate: Failed to "
286a63c99a2SToomas Soome 				    "add BE (%s) to the menu\n"),
287f169c0eaSGlenn Lagasse 				    cb.obe_name);
288f169c0eaSGlenn Lagasse 				goto done;
289f169c0eaSGlenn Lagasse 			}
290f169c0eaSGlenn Lagasse 		}
291f169c0eaSGlenn Lagasse 		if (be_has_grub()) {
292f169c0eaSGlenn Lagasse 			if ((ret = be_change_grub_default(cb.obe_name,
293f169c0eaSGlenn Lagasse 			    cb.obe_zpool)) != BE_SUCCESS) {
294f169c0eaSGlenn Lagasse 				be_print_err(gettext("be_activate: failed to "
295f169c0eaSGlenn Lagasse 				    "change the default entry in menu.lst\n"));
296f169c0eaSGlenn Lagasse 				goto done;
297f169c0eaSGlenn Lagasse 			}
298f169c0eaSGlenn Lagasse 		}
299f169c0eaSGlenn Lagasse 	}
300f169c0eaSGlenn Lagasse 
301a897f28bSAndy Fiddaman 	if ((ret = _be_list(cb.obe_name, &be_nodes, BE_LIST_DEFAULT))
302a897f28bSAndy Fiddaman 	    != BE_SUCCESS) {
303f169c0eaSGlenn Lagasse 		return (ret);
304f169c0eaSGlenn Lagasse 	}
305f169c0eaSGlenn Lagasse 
306f169c0eaSGlenn Lagasse 	if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
307f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_activate: failed to set "
308f169c0eaSGlenn Lagasse 		    "canmount dataset property\n"));
309f169c0eaSGlenn Lagasse 		goto done;
310f169c0eaSGlenn Lagasse 	}
311f169c0eaSGlenn Lagasse 
3127e0e2549SAlexander Eremin 	if (getzoneid() == GLOBAL_ZONEID) {
313b713c91eSToomas Soome 		switch (nextboot) {
314b713c91eSToomas Soome 		case BE_NEXTBOOT_SET:
315b713c91eSToomas Soome 			if ((ret = lzbe_set_boot_device(be_nodes->be_rpool,
316b713c91eSToomas Soome 			    lzbe_add, root_ds)) != 0) {
317b713c91eSToomas Soome 				be_print_err(gettext("be_activate: failed to "
318b713c91eSToomas Soome 				    "set nextboot for %s\n"), root_ds);
319b713c91eSToomas Soome 				goto done;
320b713c91eSToomas Soome 			}
321b713c91eSToomas Soome 			break;
322b713c91eSToomas Soome 		case BE_NEXTBOOT_UNSET:
323b713c91eSToomas Soome 			if ((ret = lzbe_set_boot_device(be_nodes->be_rpool,
324b713c91eSToomas Soome 			    lzbe_add, "")) != 0) {
325b713c91eSToomas Soome 				be_print_err(gettext("be_activate: failed to "
326b713c91eSToomas Soome 				    "clear nextboot for %s\n"), root_ds);
327b713c91eSToomas Soome 				goto done;
328b713c91eSToomas Soome 			}
329b713c91eSToomas Soome 			break;
330b713c91eSToomas Soome 		default:
331b713c91eSToomas Soome 			if ((ret = set_bootfs(be_nodes->be_rpool,
332b713c91eSToomas Soome 			    root_ds)) != BE_SUCCESS) {
333b713c91eSToomas Soome 				be_print_err(gettext("be_activate: failed to "
334b713c91eSToomas Soome 				    "set bootfs pool property for %s\n"),
335b713c91eSToomas Soome 				    root_ds);
336b713c91eSToomas Soome 				goto done;
337b713c91eSToomas Soome 			}
3387e0e2549SAlexander Eremin 		}
339f169c0eaSGlenn Lagasse 	}
340f169c0eaSGlenn Lagasse 
3418b1df8bfSAndy Fiddaman 	if (nextboot == BE_NEXTBOOT_IGNORE) {
3428b1df8bfSAndy Fiddaman 		if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) !=
3438b1df8bfSAndy Fiddaman 		    NULL) {
3448b1df8bfSAndy Fiddaman 			/*
3458b1df8bfSAndy Fiddaman 			 * We don't need to close the zfs handle at this
3468b1df8bfSAndy Fiddaman 			 * point because The callback funtion
3478b1df8bfSAndy Fiddaman 			 * be_promote_ds_callback() will close it for us.
3488b1df8bfSAndy Fiddaman 			 */
3498b1df8bfSAndy Fiddaman 			if (be_promote_ds_callback(zhp, NULL) != 0) {
3508b1df8bfSAndy Fiddaman 				be_print_err(gettext("be_activate: "
3518b1df8bfSAndy Fiddaman 				    "failed to activate the "
3528b1df8bfSAndy Fiddaman 				    "datasets for %s: %s\n"),
3538b1df8bfSAndy Fiddaman 				    root_ds,
3548b1df8bfSAndy Fiddaman 				    libzfs_error_description(g_zfs));
3558b1df8bfSAndy Fiddaman 				ret = BE_ERR_PROMOTE;
3568b1df8bfSAndy Fiddaman 				goto done;
3578b1df8bfSAndy Fiddaman 			}
3588b1df8bfSAndy Fiddaman 		} else {
3598b1df8bfSAndy Fiddaman 			be_print_err(gettext("be_activate: failed to open "
3608b1df8bfSAndy Fiddaman 			    "dataset (%s): %s\n"), root_ds,
361f169c0eaSGlenn Lagasse 			    libzfs_error_description(g_zfs));
3628b1df8bfSAndy Fiddaman 			ret = zfs_err_to_be_err(g_zfs);
363f169c0eaSGlenn Lagasse 			goto done;
364f169c0eaSGlenn Lagasse 		}
365f169c0eaSGlenn Lagasse 
3668b1df8bfSAndy Fiddaman 		if (getzoneid() == GLOBAL_ZONEID &&
3678b1df8bfSAndy Fiddaman 		    be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
3688b1df8bfSAndy Fiddaman 		    (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
3698b1df8bfSAndy Fiddaman 		    != BE_SUCCESS) {
3708b1df8bfSAndy Fiddaman 			be_print_err(gettext("be_activate: failed to promote "
3718b1df8bfSAndy Fiddaman 			    "the active zonepath datasets for zones in BE "
3728b1df8bfSAndy Fiddaman 			    "%s\n"), cb.obe_name);
3738b1df8bfSAndy Fiddaman 		}
374f169c0eaSGlenn Lagasse 	}
375f169c0eaSGlenn Lagasse 
3767e0e2549SAlexander Eremin 	if (getzoneid() != GLOBAL_ZONEID) {
3777e0e2549SAlexander Eremin 		if (!be_zone_compare_uuids(root_ds)) {
3787e0e2549SAlexander Eremin 			be_print_err(gettext("be_activate: activating zone "
3797e0e2549SAlexander Eremin 			    "root dataset from non-active global BE is not "
3807e0e2549SAlexander Eremin 			    "supported\n"));
3817e0e2549SAlexander Eremin 			ret = BE_ERR_NOTSUP;
3827e0e2549SAlexander Eremin 			goto done;
3837e0e2549SAlexander Eremin 		}
3847e0e2549SAlexander Eremin 		if ((zhp = zfs_open(g_zfs, root_ds,
3857e0e2549SAlexander Eremin 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
3867e0e2549SAlexander Eremin 			be_print_err(gettext("be_activate: failed to open "
3877e0e2549SAlexander Eremin 			    "dataset (%s): %s\n"), root_ds,
3887e0e2549SAlexander Eremin 			    libzfs_error_description(g_zfs));
3897e0e2549SAlexander Eremin 			ret = zfs_err_to_be_err(g_zfs);
3907e0e2549SAlexander Eremin 			goto done;
3917e0e2549SAlexander Eremin 		}
3927e0e2549SAlexander Eremin 		/* Find current active zone root dataset */
3937e0e2549SAlexander Eremin 		if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
3947e0e2549SAlexander Eremin 		    active_ds, sizeof (active_ds))) != BE_SUCCESS) {
3957e0e2549SAlexander Eremin 			be_print_err(gettext("be_activate: failed to find "
3967e0e2549SAlexander Eremin 			    "active zone root dataset\n"));
3977e0e2549SAlexander Eremin 			ZFS_CLOSE(zhp);
3987e0e2549SAlexander Eremin 			goto done;
3997e0e2549SAlexander Eremin 		}
4007e0e2549SAlexander Eremin 		/* Do nothing if requested BE is already active */
4017e0e2549SAlexander Eremin 		if (strcmp(root_ds, active_ds) == 0) {
4027e0e2549SAlexander Eremin 			ret = BE_SUCCESS;
4037e0e2549SAlexander Eremin 			ZFS_CLOSE(zhp);
4047e0e2549SAlexander Eremin 			goto done;
4057e0e2549SAlexander Eremin 		}
4067e0e2549SAlexander Eremin 
4077e0e2549SAlexander Eremin 		/* Set active property for BE */
4087e0e2549SAlexander Eremin 		if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
4097e0e2549SAlexander Eremin 			be_print_err(gettext("be_activate: failed to set "
4107e0e2549SAlexander Eremin 			    "active property (%s): %s\n"), root_ds,
4117e0e2549SAlexander Eremin 			    libzfs_error_description(g_zfs));
4127e0e2549SAlexander Eremin 			ret = zfs_err_to_be_err(g_zfs);
4137e0e2549SAlexander Eremin 			ZFS_CLOSE(zhp);
4147e0e2549SAlexander Eremin 			goto done;
4157e0e2549SAlexander Eremin 		}
4167e0e2549SAlexander Eremin 		ZFS_CLOSE(zhp);
4177e0e2549SAlexander Eremin 
4187e0e2549SAlexander Eremin 		/* Unset active property for old active root dataset */
4197e0e2549SAlexander Eremin 		if ((zhp = zfs_open(g_zfs, active_ds,
4207e0e2549SAlexander Eremin 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
4217e0e2549SAlexander Eremin 			be_print_err(gettext("be_activate: failed to open "
4227e0e2549SAlexander Eremin 			    "dataset (%s): %s\n"), active_ds,
4237e0e2549SAlexander Eremin 			    libzfs_error_description(g_zfs));
4247e0e2549SAlexander Eremin 			ret = zfs_err_to_be_err(g_zfs);
4257e0e2549SAlexander Eremin 			goto done;
4267e0e2549SAlexander Eremin 		}
4277e0e2549SAlexander Eremin 		if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
4287e0e2549SAlexander Eremin 			be_print_err(gettext("be_activate: failed to unset "
4297e0e2549SAlexander Eremin 			    "active property (%s): %s\n"), active_ds,
4307e0e2549SAlexander Eremin 			    libzfs_error_description(g_zfs));
4317e0e2549SAlexander Eremin 			ret = zfs_err_to_be_err(g_zfs);
4327e0e2549SAlexander Eremin 			ZFS_CLOSE(zhp);
4337e0e2549SAlexander Eremin 			goto done;
4347e0e2549SAlexander Eremin 		}
4357e0e2549SAlexander Eremin 		ZFS_CLOSE(zhp);
4367e0e2549SAlexander Eremin 	}
437f169c0eaSGlenn Lagasse done:
438f169c0eaSGlenn Lagasse 	be_free_list(be_nodes);
439f169c0eaSGlenn Lagasse 	return (ret);
440f169c0eaSGlenn Lagasse }
441f169c0eaSGlenn Lagasse 
442f169c0eaSGlenn Lagasse /*
443f169c0eaSGlenn Lagasse  * Function:	be_activate_current_be
444f169c0eaSGlenn Lagasse  * Description:	Set the currently "active" BE to be "active on boot"
445f169c0eaSGlenn Lagasse  * Paramters:
446f169c0eaSGlenn Lagasse  *		none
447f169c0eaSGlenn Lagasse  * Returns:
448f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
449f169c0eaSGlenn Lagasse  *		be_errnot_t - Failure
450f169c0eaSGlenn Lagasse  * Scope:
451f169c0eaSGlenn Lagasse  *		Semi-private (library wide use only)
452f169c0eaSGlenn Lagasse  */
453f169c0eaSGlenn Lagasse int
be_activate_current_be(void)454f169c0eaSGlenn Lagasse be_activate_current_be(void)
455f169c0eaSGlenn Lagasse {
456f169c0eaSGlenn Lagasse 	int ret = BE_SUCCESS;
457f169c0eaSGlenn Lagasse 	be_transaction_data_t bt = { 0 };
458f169c0eaSGlenn Lagasse 
459f169c0eaSGlenn Lagasse 	if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
460f169c0eaSGlenn Lagasse 		return (ret);
461f169c0eaSGlenn Lagasse 	}
462f169c0eaSGlenn Lagasse 
463b713c91eSToomas Soome 	ret = _be_activate(bt.obe_name, BE_NEXTBOOT_IGNORE);
464b713c91eSToomas Soome 	if (ret != BE_SUCCESS) {
465f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_activate_current_be: failed to "
466f169c0eaSGlenn Lagasse 		    "activate %s\n"), bt.obe_name);
467f169c0eaSGlenn Lagasse 		return (ret);
468f169c0eaSGlenn Lagasse 	}
469f169c0eaSGlenn Lagasse 
470f169c0eaSGlenn Lagasse 	return (BE_SUCCESS);
471f169c0eaSGlenn Lagasse }
472f169c0eaSGlenn Lagasse 
473f169c0eaSGlenn Lagasse /*
474f169c0eaSGlenn Lagasse  * Function:	be_is_active_on_boot
475f169c0eaSGlenn Lagasse  * Description:	Checks if the BE name passed in has the "active on boot"
476f169c0eaSGlenn Lagasse  *		property set to B_TRUE.
477f169c0eaSGlenn Lagasse  * Paramters:
478f169c0eaSGlenn Lagasse  *		be_name - the name of the BE to check
479f169c0eaSGlenn Lagasse  * Returns:
480f169c0eaSGlenn Lagasse  *		B_TRUE - if active on boot.
481f169c0eaSGlenn Lagasse  *		B_FALSE - if not active on boot.
482f169c0eaSGlenn Lagasse  * Scope:
483f169c0eaSGlenn Lagasse  *		Semi-private (library wide use only)
484f169c0eaSGlenn Lagasse  */
485f169c0eaSGlenn Lagasse boolean_t
be_is_active_on_boot(char * be_name)486f169c0eaSGlenn Lagasse be_is_active_on_boot(char *be_name)
487f169c0eaSGlenn Lagasse {
488f169c0eaSGlenn Lagasse 	be_node_list_t *be_node = NULL;
489f169c0eaSGlenn Lagasse 
490f169c0eaSGlenn Lagasse 	if (be_name == NULL) {
491f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_is_active_on_boot: "
492f169c0eaSGlenn Lagasse 		    "be_name must not be NULL\n"));
493f169c0eaSGlenn Lagasse 		return (B_FALSE);
494f169c0eaSGlenn Lagasse 	}
495f169c0eaSGlenn Lagasse 
496a897f28bSAndy Fiddaman 	if (_be_list(be_name, &be_node, BE_LIST_DEFAULT) != BE_SUCCESS) {
497f169c0eaSGlenn Lagasse 		return (B_FALSE);
498f169c0eaSGlenn Lagasse 	}
499f169c0eaSGlenn Lagasse 
500f169c0eaSGlenn Lagasse 	if (be_node == NULL) {
501f169c0eaSGlenn Lagasse 		return (B_FALSE);
502f169c0eaSGlenn Lagasse 	}
503f169c0eaSGlenn Lagasse 
504f169c0eaSGlenn Lagasse 	if (be_node->be_active_on_boot) {
505f169c0eaSGlenn Lagasse 		be_free_list(be_node);
506f169c0eaSGlenn Lagasse 		return (B_TRUE);
507f169c0eaSGlenn Lagasse 	} else {
508f169c0eaSGlenn Lagasse 		be_free_list(be_node);
509f169c0eaSGlenn Lagasse 		return (B_FALSE);
510f169c0eaSGlenn Lagasse 	}
511f169c0eaSGlenn Lagasse }
512f169c0eaSGlenn Lagasse 
513f169c0eaSGlenn Lagasse /* ******************************************************************** */
514f169c0eaSGlenn Lagasse /*			Private Functions				*/
515f169c0eaSGlenn Lagasse /* ******************************************************************** */
516f169c0eaSGlenn Lagasse 
517f169c0eaSGlenn Lagasse /*
518f169c0eaSGlenn Lagasse  * Function:	set_bootfs
519f169c0eaSGlenn Lagasse  * Description:	Sets the bootfs property on the boot pool to be the
520f169c0eaSGlenn Lagasse  *		root dataset of the activated BE.
521f169c0eaSGlenn Lagasse  * Parameters:
522f169c0eaSGlenn Lagasse  *		boot_pool - The pool we're setting bootfs in.
523f169c0eaSGlenn Lagasse  *		be_root_ds - The main dataset for the BE.
524f169c0eaSGlenn Lagasse  * Return:
525f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
526f169c0eaSGlenn Lagasse  *		be_errno_t - Failure
527f169c0eaSGlenn Lagasse  * Scope:
528f169c0eaSGlenn Lagasse  *		Private
529f169c0eaSGlenn Lagasse  */
530f169c0eaSGlenn Lagasse static int
set_bootfs(char * boot_rpool,char * be_root_ds)531f169c0eaSGlenn Lagasse set_bootfs(char *boot_rpool, char *be_root_ds)
532f169c0eaSGlenn Lagasse {
533f169c0eaSGlenn Lagasse 	zpool_handle_t *zhp;
534f169c0eaSGlenn Lagasse 	int err = BE_SUCCESS;
535f169c0eaSGlenn Lagasse 
536f169c0eaSGlenn Lagasse 	if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
537f169c0eaSGlenn Lagasse 		be_print_err(gettext("set_bootfs: failed to open pool "
538f169c0eaSGlenn Lagasse 		    "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
539f169c0eaSGlenn Lagasse 		err = zfs_err_to_be_err(g_zfs);
540f169c0eaSGlenn Lagasse 		return (err);
541f169c0eaSGlenn Lagasse 	}
542f169c0eaSGlenn Lagasse 
543f169c0eaSGlenn Lagasse 	err = zpool_set_prop(zhp, "bootfs", be_root_ds);
544f169c0eaSGlenn Lagasse 	if (err) {
545f169c0eaSGlenn Lagasse 		be_print_err(gettext("set_bootfs: failed to set "
546f169c0eaSGlenn Lagasse 		    "bootfs property for pool %s: %s\n"), boot_rpool,
547f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
548f169c0eaSGlenn Lagasse 		err = zfs_err_to_be_err(g_zfs);
549f169c0eaSGlenn Lagasse 		zpool_close(zhp);
550f169c0eaSGlenn Lagasse 		return (err);
551f169c0eaSGlenn Lagasse 	}
552f169c0eaSGlenn Lagasse 
553f169c0eaSGlenn Lagasse 	zpool_close(zhp);
554f169c0eaSGlenn Lagasse 	return (BE_SUCCESS);
555f169c0eaSGlenn Lagasse }
556f169c0eaSGlenn Lagasse 
557f169c0eaSGlenn Lagasse /*
558f169c0eaSGlenn Lagasse  * Function:	set_canmount
559f169c0eaSGlenn Lagasse  * Description:	Sets the canmount property on the datasets of the
560f169c0eaSGlenn Lagasse  *		activated BE.
561f169c0eaSGlenn Lagasse  * Parameters:
562f169c0eaSGlenn Lagasse  *		be_nodes - The be_node_t returned from be_list
563f169c0eaSGlenn Lagasse  *		value - The value of canmount we setting, on|off|noauto.
564f169c0eaSGlenn Lagasse  * Return:
565f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
566f169c0eaSGlenn Lagasse  *		be_errno_t - Failure
567f169c0eaSGlenn Lagasse  * Scope:
568f169c0eaSGlenn Lagasse  *		Private
569f169c0eaSGlenn Lagasse  */
570f169c0eaSGlenn Lagasse static int
set_canmount(be_node_list_t * be_nodes,char * value)571f169c0eaSGlenn Lagasse set_canmount(be_node_list_t *be_nodes, char *value)
572f169c0eaSGlenn Lagasse {
573f169c0eaSGlenn Lagasse 	char		ds_path[MAXPATHLEN];
574f169c0eaSGlenn Lagasse 	zfs_handle_t	*zhp = NULL;
575f169c0eaSGlenn Lagasse 	be_node_list_t	*list = be_nodes;
576f169c0eaSGlenn Lagasse 	int		err = BE_SUCCESS;
577f169c0eaSGlenn Lagasse 
578f169c0eaSGlenn Lagasse 	while (list != NULL) {
579f169c0eaSGlenn Lagasse 		be_dataset_list_t *datasets = list->be_node_datasets;
580f169c0eaSGlenn Lagasse 
581*ec8422d0SAndy Fiddaman 		if ((err = be_make_root_ds(list->be_rpool, list->be_node_name,
582*ec8422d0SAndy Fiddaman 		    ds_path, sizeof (ds_path))) != BE_SUCCESS) {
583*ec8422d0SAndy Fiddaman 			be_print_err(gettext("%s: failed to get BE container "
584*ec8422d0SAndy Fiddaman 			    "dataset for %s/%s\n"), __func__,
585*ec8422d0SAndy Fiddaman 			    list->be_rpool, list->be_node_name);
586*ec8422d0SAndy Fiddaman 			return (err);
587*ec8422d0SAndy Fiddaman 		}
588f169c0eaSGlenn Lagasse 
589f169c0eaSGlenn Lagasse 		if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
590f169c0eaSGlenn Lagasse 		    NULL) {
591f169c0eaSGlenn Lagasse 			be_print_err(gettext("set_canmount: failed to open "
592f169c0eaSGlenn Lagasse 			    "dataset (%s): %s\n"), ds_path,
593f169c0eaSGlenn Lagasse 			    libzfs_error_description(g_zfs));
594f169c0eaSGlenn Lagasse 			err = zfs_err_to_be_err(g_zfs);
595f169c0eaSGlenn Lagasse 			return (err);
596f169c0eaSGlenn Lagasse 		}
597f169c0eaSGlenn Lagasse 		if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
598f169c0eaSGlenn Lagasse 			/*
599f169c0eaSGlenn Lagasse 			 * it's already mounted so we can't change the
600f169c0eaSGlenn Lagasse 			 * canmount property anyway.
601f169c0eaSGlenn Lagasse 			 */
602f169c0eaSGlenn Lagasse 			err = BE_SUCCESS;
603f169c0eaSGlenn Lagasse 		} else {
604f169c0eaSGlenn Lagasse 			err = zfs_prop_set(zhp,
605f169c0eaSGlenn Lagasse 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
606f169c0eaSGlenn Lagasse 			if (err) {
607f169c0eaSGlenn Lagasse 				ZFS_CLOSE(zhp);
608f169c0eaSGlenn Lagasse 				be_print_err(gettext("set_canmount: failed to "
609f169c0eaSGlenn Lagasse 				    "set dataset property (%s): %s\n"),
610f169c0eaSGlenn Lagasse 				    ds_path, libzfs_error_description(g_zfs));
611f169c0eaSGlenn Lagasse 				err = zfs_err_to_be_err(g_zfs);
612f169c0eaSGlenn Lagasse 				return (err);
613f169c0eaSGlenn Lagasse 			}
614f169c0eaSGlenn Lagasse 		}
615f169c0eaSGlenn Lagasse 		ZFS_CLOSE(zhp);
616f169c0eaSGlenn Lagasse 
617f169c0eaSGlenn Lagasse 		while (datasets != NULL) {
618*ec8422d0SAndy Fiddaman 			if ((err = be_make_root_ds(list->be_rpool,
619f169c0eaSGlenn Lagasse 			    datasets->be_dataset_name, ds_path,
620*ec8422d0SAndy Fiddaman 			    sizeof (ds_path))) != BE_SUCCESS) {
621*ec8422d0SAndy Fiddaman 				be_print_err(gettext("%s: failed to get BE "
622*ec8422d0SAndy Fiddaman 				    "container dataset for %s/%s\n"), __func__,
623*ec8422d0SAndy Fiddaman 				    list->be_rpool, datasets->be_dataset_name);
624*ec8422d0SAndy Fiddaman 				return (err);
625*ec8422d0SAndy Fiddaman 			}
626f169c0eaSGlenn Lagasse 
627f169c0eaSGlenn Lagasse 			if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
628f169c0eaSGlenn Lagasse 			    == NULL) {
629f169c0eaSGlenn Lagasse 				be_print_err(gettext("set_canmount: failed to "
630f169c0eaSGlenn Lagasse 				    "open dataset %s: %s\n"), ds_path,
631f169c0eaSGlenn Lagasse 				    libzfs_error_description(g_zfs));
632f169c0eaSGlenn Lagasse 				err = zfs_err_to_be_err(g_zfs);
633f169c0eaSGlenn Lagasse 				return (err);
634f169c0eaSGlenn Lagasse 			}
635f169c0eaSGlenn Lagasse 			if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
636f169c0eaSGlenn Lagasse 				/*
637f169c0eaSGlenn Lagasse 				 * it's already mounted so we can't change the
638f169c0eaSGlenn Lagasse 				 * canmount property anyway.
639f169c0eaSGlenn Lagasse 				 */
640f169c0eaSGlenn Lagasse 				err = BE_SUCCESS;
641f169c0eaSGlenn Lagasse 				ZFS_CLOSE(zhp);
642f169c0eaSGlenn Lagasse 				break;
643f169c0eaSGlenn Lagasse 			}
644f169c0eaSGlenn Lagasse 			err = zfs_prop_set(zhp,
645f169c0eaSGlenn Lagasse 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
646f169c0eaSGlenn Lagasse 			if (err) {
647f169c0eaSGlenn Lagasse 				ZFS_CLOSE(zhp);
648f169c0eaSGlenn Lagasse 				be_print_err(gettext("set_canmount: "
649f169c0eaSGlenn Lagasse 				    "Failed to set property value %s "
650f169c0eaSGlenn Lagasse 				    "for dataset %s: %s\n"), value, ds_path,
651f169c0eaSGlenn Lagasse 				    libzfs_error_description(g_zfs));
652f169c0eaSGlenn Lagasse 				err = zfs_err_to_be_err(g_zfs);
653f169c0eaSGlenn Lagasse 				return (err);
654f169c0eaSGlenn Lagasse 			}
655f169c0eaSGlenn Lagasse 			ZFS_CLOSE(zhp);
656f169c0eaSGlenn Lagasse 			datasets = datasets->be_next_dataset;
657f169c0eaSGlenn Lagasse 		}
658f169c0eaSGlenn Lagasse 		list = list->be_next_node;
659f169c0eaSGlenn Lagasse 	}
660f169c0eaSGlenn Lagasse 	return (err);
661f169c0eaSGlenn Lagasse }
662f169c0eaSGlenn Lagasse 
663f169c0eaSGlenn Lagasse /*
664f169c0eaSGlenn Lagasse  * Function:	be_get_grub_vers
665f169c0eaSGlenn Lagasse  * Description:	Gets the grub version number from /boot/grub/capability. If
666f169c0eaSGlenn Lagasse  *              capability file doesn't exist NULL is returned.
667f169c0eaSGlenn Lagasse  * Parameters:
668f169c0eaSGlenn Lagasse  *              bt - The transaction data for the BE we're getting the grub
669f169c0eaSGlenn Lagasse  *                   version for.
670f169c0eaSGlenn Lagasse  *              cur_vers - used to return the current version of grub from
671f169c0eaSGlenn Lagasse  *                         the root pool.
672f169c0eaSGlenn Lagasse  *              new_vers - used to return the grub version of the BE we're
673f169c0eaSGlenn Lagasse  *                         activating.
674f169c0eaSGlenn Lagasse  * Return:
675f169c0eaSGlenn Lagasse  *              BE_SUCCESS - Success
676f169c0eaSGlenn Lagasse  *              be_errno_t - Failed to find version
677f169c0eaSGlenn Lagasse  * Scope:
678f169c0eaSGlenn Lagasse  *		Private
679f169c0eaSGlenn Lagasse  */
680f169c0eaSGlenn Lagasse static int
be_get_grub_vers(be_transaction_data_t * bt,char ** cur_vers,char ** new_vers)681f169c0eaSGlenn Lagasse be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
682f169c0eaSGlenn Lagasse {
683f169c0eaSGlenn Lagasse 	zfs_handle_t	*zhp = NULL;
684f169c0eaSGlenn Lagasse 	zfs_handle_t	*pool_zhp = NULL;
685f169c0eaSGlenn Lagasse 	int ret = BE_SUCCESS;
686f169c0eaSGlenn Lagasse 	char cap_file[MAXPATHLEN];
687f169c0eaSGlenn Lagasse 	char *temp_mntpnt = NULL;
688f169c0eaSGlenn Lagasse 	char *zpool_mntpt = NULL;
689f169c0eaSGlenn Lagasse 	char *ptmp_mntpnt = NULL;
690f169c0eaSGlenn Lagasse 	char *orig_mntpnt = NULL;
691f169c0eaSGlenn Lagasse 	boolean_t be_mounted = B_FALSE;
692f169c0eaSGlenn Lagasse 	boolean_t pool_mounted = B_FALSE;
693f169c0eaSGlenn Lagasse 
694f169c0eaSGlenn Lagasse 	if (!be_has_grub()) {
695f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_get_grub_vers: Not supported on "
696f169c0eaSGlenn Lagasse 		    "this architecture\n"));
697f169c0eaSGlenn Lagasse 		return (BE_ERR_NOTSUP);
698f169c0eaSGlenn Lagasse 	}
699f169c0eaSGlenn Lagasse 
700f169c0eaSGlenn Lagasse 	if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
701f169c0eaSGlenn Lagasse 	    bt->obe_root_ds == NULL) {
702f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
703f169c0eaSGlenn Lagasse 		return (BE_ERR_INVAL);
704f169c0eaSGlenn Lagasse 	}
705f169c0eaSGlenn Lagasse 
706f169c0eaSGlenn Lagasse 	if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
707f169c0eaSGlenn Lagasse 	    NULL) {
708f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
709f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
710f169c0eaSGlenn Lagasse 		return (zfs_err_to_be_err(g_zfs));
711f169c0eaSGlenn Lagasse 	}
712f169c0eaSGlenn Lagasse 
713f169c0eaSGlenn Lagasse 	/*
714f169c0eaSGlenn Lagasse 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
715f169c0eaSGlenn Lagasse 	 * attempt to mount it.
716f169c0eaSGlenn Lagasse 	 */
717f169c0eaSGlenn Lagasse 	if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
718f169c0eaSGlenn Lagasse 	    &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
719f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_get_grub_vers: pool dataset "
720f169c0eaSGlenn Lagasse 		    "(%s) could not be mounted\n"), bt->obe_zpool);
721f169c0eaSGlenn Lagasse 		ZFS_CLOSE(pool_zhp);
722f169c0eaSGlenn Lagasse 		return (ret);
723f169c0eaSGlenn Lagasse 	}
724f169c0eaSGlenn Lagasse 
725f169c0eaSGlenn Lagasse 	/*
726f169c0eaSGlenn Lagasse 	 * Get the mountpoint for the root pool dataset.
727f169c0eaSGlenn Lagasse 	 */
728f169c0eaSGlenn Lagasse 	if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
729f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_get_grub_vers: pool "
730a63c99a2SToomas Soome 		    "dataset (%s) is not mounted. Can't read the "
731a63c99a2SToomas Soome 		    "grub capability file.\n"), bt->obe_zpool);
732f169c0eaSGlenn Lagasse 		ret = BE_ERR_NO_MENU;
733f169c0eaSGlenn Lagasse 		goto cleanup;
734f169c0eaSGlenn Lagasse 	}
735f169c0eaSGlenn Lagasse 
736f169c0eaSGlenn Lagasse 	/*
737f169c0eaSGlenn Lagasse 	 * get the version of the most recent grub update.
738f169c0eaSGlenn Lagasse 	 */
739f169c0eaSGlenn Lagasse 	(void) snprintf(cap_file, sizeof (cap_file), "%s%s",
740f169c0eaSGlenn Lagasse 	    zpool_mntpt, BE_CAP_FILE);
741f169c0eaSGlenn Lagasse 	free(zpool_mntpt);
742f169c0eaSGlenn Lagasse 	zpool_mntpt = NULL;
743f169c0eaSGlenn Lagasse 
744f169c0eaSGlenn Lagasse 	if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
745f169c0eaSGlenn Lagasse 		goto cleanup;
746f169c0eaSGlenn Lagasse 
747f169c0eaSGlenn Lagasse 	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
748f169c0eaSGlenn Lagasse 	    NULL) {
749f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_get_grub_vers: failed to "
750f169c0eaSGlenn Lagasse 		    "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
751f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
752f169c0eaSGlenn Lagasse 		free(cur_vers);
753f169c0eaSGlenn Lagasse 		ret = zfs_err_to_be_err(g_zfs);
754f169c0eaSGlenn Lagasse 		goto cleanup;
755f169c0eaSGlenn Lagasse 	}
756f169c0eaSGlenn Lagasse 	if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
757f169c0eaSGlenn Lagasse 		if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
758f169c0eaSGlenn Lagasse 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
759f169c0eaSGlenn Lagasse 			be_print_err(gettext("be_get_grub_vers: failed to "
760f169c0eaSGlenn Lagasse 			    "mount BE (%s)\n"), bt->obe_name);
761f169c0eaSGlenn Lagasse 			free(*cur_vers);
762f169c0eaSGlenn Lagasse 			*cur_vers = NULL;
763f169c0eaSGlenn Lagasse 			ZFS_CLOSE(zhp);
764f169c0eaSGlenn Lagasse 			goto cleanup;
765f169c0eaSGlenn Lagasse 		}
766f169c0eaSGlenn Lagasse 		be_mounted = B_TRUE;
767f169c0eaSGlenn Lagasse 	}
768f169c0eaSGlenn Lagasse 	ZFS_CLOSE(zhp);
769f169c0eaSGlenn Lagasse 
770f169c0eaSGlenn Lagasse 	/*
771f169c0eaSGlenn Lagasse 	 * Now get the grub version for the BE being activated.
772f169c0eaSGlenn Lagasse 	 */
773f169c0eaSGlenn Lagasse 	(void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
774f169c0eaSGlenn Lagasse 	    BE_CAP_FILE);
775f169c0eaSGlenn Lagasse 	ret = get_ver_from_capfile(cap_file, new_vers);
776f169c0eaSGlenn Lagasse 	if (ret != BE_SUCCESS) {
777f169c0eaSGlenn Lagasse 		free(*cur_vers);
778f169c0eaSGlenn Lagasse 		*cur_vers = NULL;
779f169c0eaSGlenn Lagasse 	}
780f169c0eaSGlenn Lagasse 	if (be_mounted)
781f169c0eaSGlenn Lagasse 		(void) _be_unmount(bt->obe_name, 0);
782f169c0eaSGlenn Lagasse 
783f169c0eaSGlenn Lagasse cleanup:
784f169c0eaSGlenn Lagasse 	if (pool_mounted) {
785f169c0eaSGlenn Lagasse 		int iret = BE_SUCCESS;
786f169c0eaSGlenn Lagasse 		iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
787f169c0eaSGlenn Lagasse 		if (ret == BE_SUCCESS)
788f169c0eaSGlenn Lagasse 			ret = iret;
789f169c0eaSGlenn Lagasse 		free(orig_mntpnt);
790f169c0eaSGlenn Lagasse 		free(ptmp_mntpnt);
791f169c0eaSGlenn Lagasse 	}
792f169c0eaSGlenn Lagasse 	ZFS_CLOSE(pool_zhp);
793f169c0eaSGlenn Lagasse 
794f169c0eaSGlenn Lagasse 	free(temp_mntpnt);
795f169c0eaSGlenn Lagasse 	return (ret);
796f169c0eaSGlenn Lagasse }
797f169c0eaSGlenn Lagasse 
798f169c0eaSGlenn Lagasse /*
799f169c0eaSGlenn Lagasse  * Function:	get_ver_from_capfile
800f169c0eaSGlenn Lagasse  * Description: Parses the capability file passed in looking for the VERSION
801f169c0eaSGlenn Lagasse  *              line. If found the version is returned in vers, if not then
802f169c0eaSGlenn Lagasse  *              NULL is returned in vers.
803f169c0eaSGlenn Lagasse  *
804f169c0eaSGlenn Lagasse  * Parameters:
805f169c0eaSGlenn Lagasse  *              file - the path to the capability file we want to parse.
806f169c0eaSGlenn Lagasse  *              vers - the version string that will be passed back.
807f169c0eaSGlenn Lagasse  * Return:
808f169c0eaSGlenn Lagasse  *              BE_SUCCESS - Success
809f169c0eaSGlenn Lagasse  *              be_errno_t - Failed to find version
810f169c0eaSGlenn Lagasse  * Scope:
811f169c0eaSGlenn Lagasse  *		Private
812f169c0eaSGlenn Lagasse  */
813f169c0eaSGlenn Lagasse static int
get_ver_from_capfile(char * file,char ** vers)814f169c0eaSGlenn Lagasse get_ver_from_capfile(char *file, char **vers)
815f169c0eaSGlenn Lagasse {
816f169c0eaSGlenn Lagasse 	FILE *fp = NULL;
817f169c0eaSGlenn Lagasse 	char line[BUFSIZ];
818f169c0eaSGlenn Lagasse 	char *last = NULL;
819f169c0eaSGlenn Lagasse 	int err = BE_SUCCESS;
820f169c0eaSGlenn Lagasse 	errno = 0;
821f169c0eaSGlenn Lagasse 
822f169c0eaSGlenn Lagasse 	if (!be_has_grub()) {
823f169c0eaSGlenn Lagasse 		be_print_err(gettext("get_ver_from_capfile: Not supported "
824f169c0eaSGlenn Lagasse 		    "on this architecture\n"));
825f169c0eaSGlenn Lagasse 		return (BE_ERR_NOTSUP);
826f169c0eaSGlenn Lagasse 	}
827f169c0eaSGlenn Lagasse 
828f169c0eaSGlenn Lagasse 	/*
829f169c0eaSGlenn Lagasse 	 * Set version string to NULL; the only case this shouldn't be set
830f169c0eaSGlenn Lagasse 	 * to be NULL is when we've actually found a version in the capability
831f169c0eaSGlenn Lagasse 	 * file, which is set below.
832f169c0eaSGlenn Lagasse 	 */
833f169c0eaSGlenn Lagasse 	*vers = NULL;
834f169c0eaSGlenn Lagasse 
835f169c0eaSGlenn Lagasse 	/*
836f169c0eaSGlenn Lagasse 	 * If the capability file doesn't exist, we're returning success
837f169c0eaSGlenn Lagasse 	 * because on older releases, the capability file did not exist
838f169c0eaSGlenn Lagasse 	 * so this is a valid scenario.
839f169c0eaSGlenn Lagasse 	 */
840f169c0eaSGlenn Lagasse 	if (access(file, F_OK) == 0) {
841f169c0eaSGlenn Lagasse 		if ((fp = fopen(file, "r")) == NULL) {
842f169c0eaSGlenn Lagasse 			err = errno;
843f169c0eaSGlenn Lagasse 			be_print_err(gettext("get_ver_from_capfile: failed to "
844f169c0eaSGlenn Lagasse 			    "open file %s with error %s\n"), file,
845f169c0eaSGlenn Lagasse 			    strerror(err));
846f169c0eaSGlenn Lagasse 			err = errno_to_be_err(err);
847f169c0eaSGlenn Lagasse 			return (err);
848f169c0eaSGlenn Lagasse 		}
849f169c0eaSGlenn Lagasse 
850f169c0eaSGlenn Lagasse 		while (fgets(line, BUFSIZ, fp)) {
851f169c0eaSGlenn Lagasse 			char *tok = strtok_r(line, "=", &last);
852f169c0eaSGlenn Lagasse 
853f169c0eaSGlenn Lagasse 			if (tok == NULL || tok[0] == '#') {
854f169c0eaSGlenn Lagasse 				continue;
855f169c0eaSGlenn Lagasse 			} else if (strcmp(tok, "VERSION") == 0) {
856f169c0eaSGlenn Lagasse 				*vers = strdup(last);
857f169c0eaSGlenn Lagasse 				break;
858f169c0eaSGlenn Lagasse 			}
859f169c0eaSGlenn Lagasse 		}
860f169c0eaSGlenn Lagasse 		(void) fclose(fp);
861f169c0eaSGlenn Lagasse 	}
862f169c0eaSGlenn Lagasse 
863f169c0eaSGlenn Lagasse 	return (BE_SUCCESS);
864f169c0eaSGlenn Lagasse }
865f169c0eaSGlenn Lagasse 
8661a902ef8SHans Rosenfeld /*
867a63c99a2SToomas Soome  * To be able to boot EFI labeled disks, stage1 needs to be written
8681a902ef8SHans Rosenfeld  * into the MBR. We do not do this if we're on disks with a traditional
8691a902ef8SHans Rosenfeld  * fdisk partition table only, or if any foreign EFI partitions exist.
8701a902ef8SHans Rosenfeld  * In the trivial case of a whole-disk vdev we always write stage1 into
8711a902ef8SHans Rosenfeld  * the MBR.
8721a902ef8SHans Rosenfeld  */
8731a902ef8SHans Rosenfeld static boolean_t
be_do_install_mbr(char * diskname,nvlist_t * child)874a63c99a2SToomas Soome be_do_install_mbr(char *diskname, nvlist_t *child)
8751a902ef8SHans Rosenfeld {
8761a902ef8SHans Rosenfeld 	struct uuid allowed_uuids[] = {
8771a902ef8SHans Rosenfeld 		EFI_UNUSED,
8781a902ef8SHans Rosenfeld 		EFI_RESV1,
8791a902ef8SHans Rosenfeld 		EFI_BOOT,
8801a902ef8SHans Rosenfeld 		EFI_ROOT,
8811a902ef8SHans Rosenfeld 		EFI_SWAP,
8821a902ef8SHans Rosenfeld 		EFI_USR,
8831a902ef8SHans Rosenfeld 		EFI_BACKUP,
8841a902ef8SHans Rosenfeld 		EFI_RESV2,
8851a902ef8SHans Rosenfeld 		EFI_VAR,
8861a902ef8SHans Rosenfeld 		EFI_HOME,
8871a902ef8SHans Rosenfeld 		EFI_ALTSCTR,
8881a902ef8SHans Rosenfeld 		EFI_RESERVED,
8891a902ef8SHans Rosenfeld 		EFI_SYSTEM,
8901a902ef8SHans Rosenfeld 		EFI_BIOS_BOOT,
8911a902ef8SHans Rosenfeld 		EFI_SYMC_PUB,
8921a902ef8SHans Rosenfeld 		EFI_SYMC_CDS
8931a902ef8SHans Rosenfeld 	};
8941a902ef8SHans Rosenfeld 
8951a902ef8SHans Rosenfeld 	uint64_t whole;
8961a902ef8SHans Rosenfeld 	struct dk_gpt *gpt;
8971a902ef8SHans Rosenfeld 	struct uuid *u;
8981a902ef8SHans Rosenfeld 	int fd, npart, i, j;
8991a902ef8SHans Rosenfeld 
9001a902ef8SHans Rosenfeld 	(void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_WHOLE_DISK,
9011a902ef8SHans Rosenfeld 	    &whole);
9021a902ef8SHans Rosenfeld 
9031a902ef8SHans Rosenfeld 	if (whole)
9041a902ef8SHans Rosenfeld 		return (B_TRUE);
9051a902ef8SHans Rosenfeld 
9061a902ef8SHans Rosenfeld 	if ((fd = open(diskname, O_RDONLY|O_NDELAY)) < 0)
9071a902ef8SHans Rosenfeld 		return (B_FALSE);
9081a902ef8SHans Rosenfeld 
9091a902ef8SHans Rosenfeld 	if ((npart = efi_alloc_and_read(fd, &gpt)) <= 0)
9101a902ef8SHans Rosenfeld 		return (B_FALSE);
9111a902ef8SHans Rosenfeld 
9121a902ef8SHans Rosenfeld 	for (i = 0; i != npart; i++) {
9131a902ef8SHans Rosenfeld 		int match = 0;
9141a902ef8SHans Rosenfeld 
9151a902ef8SHans Rosenfeld 		u = &gpt->efi_parts[i].p_guid;
9161a902ef8SHans Rosenfeld 
9171a902ef8SHans Rosenfeld 		for (j = 0;
9181a902ef8SHans Rosenfeld 		    j != sizeof (allowed_uuids) / sizeof (struct uuid);
9191a902ef8SHans Rosenfeld 		    j++)
9201a902ef8SHans Rosenfeld 			if (bcmp(u, &allowed_uuids[j],
9211a902ef8SHans Rosenfeld 			    sizeof (struct uuid)) == 0)
9221a902ef8SHans Rosenfeld 				match++;
9231a902ef8SHans Rosenfeld 
9241a902ef8SHans Rosenfeld 		if (match == 0)
9251a902ef8SHans Rosenfeld 			return (B_FALSE);
9261a902ef8SHans Rosenfeld 	}
9271a902ef8SHans Rosenfeld 
9281a902ef8SHans Rosenfeld 	return (B_TRUE);
9291a902ef8SHans Rosenfeld }
9301a902ef8SHans Rosenfeld 
9311a902ef8SHans Rosenfeld static int
be_do_installboot_helper(zpool_handle_t * zphp,nvlist_t * child,char * stage1,char * stage2,uint16_t flags)932a63c99a2SToomas Soome be_do_installboot_helper(zpool_handle_t *zphp, nvlist_t *child, char *stage1,
933c7c0ceafSToomas Soome     char *stage2, uint16_t flags)
9341a902ef8SHans Rosenfeld {
935a63c99a2SToomas Soome 	char install_cmd[MAXPATHLEN];
9361a902ef8SHans Rosenfeld 	char be_run_cmd_errbuf[BUFSIZ];
937c7c0ceafSToomas Soome 	char be_run_cmd_outbuf[BUFSIZ];
9381a902ef8SHans Rosenfeld 	char diskname[MAXPATHLEN];
9391a902ef8SHans Rosenfeld 	char *vname;
94066b042a7SToomas Soome 	char *path, *type, *dsk_ptr;
941a63c99a2SToomas Soome 	char *flag = "";
942c7c0ceafSToomas Soome 	int ret;
94308e9b2dfSHans Rosenfeld 	vdev_stat_t *vs;
94408e9b2dfSHans Rosenfeld 	uint_t vsc;
9451a902ef8SHans Rosenfeld 
94666b042a7SToomas Soome 	if (nvlist_lookup_string(child, ZPOOL_CONFIG_TYPE, &type) != 0) {
94766b042a7SToomas Soome 		be_print_err(gettext("%s: failed to get device type\n"),
94866b042a7SToomas Soome 		    __func__);
94966b042a7SToomas Soome 		return (BE_ERR_NODEV);
95066b042a7SToomas Soome 	}
95166b042a7SToomas Soome 	/* Skip indirect devices. */
95266b042a7SToomas Soome 	if (strcmp(type, VDEV_TYPE_INDIRECT) == 0)
95366b042a7SToomas Soome 		return (BE_ERR_NOTSUP);
95466b042a7SToomas Soome 
9551a902ef8SHans Rosenfeld 	if (nvlist_lookup_string(child, ZPOOL_CONFIG_PATH, &path) != 0) {
95666b042a7SToomas Soome 		be_print_err(gettext("%s: failed to get device path\n"),
95766b042a7SToomas Soome 		    __func__);
9581a902ef8SHans Rosenfeld 		return (BE_ERR_NODEV);
9591a902ef8SHans Rosenfeld 	}
9601a902ef8SHans Rosenfeld 
96108e9b2dfSHans Rosenfeld 	if ((nvlist_lookup_uint64_array(child, ZPOOL_CONFIG_VDEV_STATS,
96208e9b2dfSHans Rosenfeld 	    (uint64_t **)&vs, &vsc) != 0) ||
96308e9b2dfSHans Rosenfeld 	    vs->vs_state < VDEV_STATE_DEGRADED) {
96408e9b2dfSHans Rosenfeld 		/*
96508e9b2dfSHans Rosenfeld 		 * Don't try to run installgrub on a vdev that is not ONLINE
96608e9b2dfSHans Rosenfeld 		 * or DEGRADED. Try to print a warning for each such vdev.
96708e9b2dfSHans Rosenfeld 		 */
96866b042a7SToomas Soome 		be_print_err(gettext("%s: vdev %s is %s, can't install "
96966b042a7SToomas Soome 		    "boot loader\n"), __func__, path,
97066b042a7SToomas Soome 		    zpool_state_to_name(vs->vs_state, vs->vs_aux));
97108e9b2dfSHans Rosenfeld 		return (BE_SUCCESS);
97208e9b2dfSHans Rosenfeld 	}
97308e9b2dfSHans Rosenfeld 
9741a902ef8SHans Rosenfeld 	/*
9751a902ef8SHans Rosenfeld 	 * Modify the vdev path to point to the raw disk.
9761a902ef8SHans Rosenfeld 	 */
9771a902ef8SHans Rosenfeld 	path = strdup(path);
9781a902ef8SHans Rosenfeld 	if (path == NULL)
9791a902ef8SHans Rosenfeld 		return (BE_ERR_NOMEM);
9801a902ef8SHans Rosenfeld 
9811a902ef8SHans Rosenfeld 	dsk_ptr = strstr(path, "/dsk/");
9821a902ef8SHans Rosenfeld 	if (dsk_ptr != NULL) {
9831a902ef8SHans Rosenfeld 		*dsk_ptr = '\0';
9841a902ef8SHans Rosenfeld 		dsk_ptr++;
9851a902ef8SHans Rosenfeld 	} else {
9861a902ef8SHans Rosenfeld 		dsk_ptr = "";
9871a902ef8SHans Rosenfeld 	}
9881a902ef8SHans Rosenfeld 
9891a902ef8SHans Rosenfeld 	(void) snprintf(diskname, sizeof (diskname), "%s/r%s", path, dsk_ptr);
9901a902ef8SHans Rosenfeld 	free(path);
9911a902ef8SHans Rosenfeld 
9921a902ef8SHans Rosenfeld 	vname = zpool_vdev_name(g_zfs, zphp, child, B_FALSE);
9931a902ef8SHans Rosenfeld 	if (vname == NULL) {
99466b042a7SToomas Soome 		be_print_err(gettext("%s: failed to get device name: %s\n"),
99566b042a7SToomas Soome 		    __func__, libzfs_error_description(g_zfs));
9961a902ef8SHans Rosenfeld 		return (zfs_err_to_be_err(g_zfs));
9971a902ef8SHans Rosenfeld 	}
9981a902ef8SHans Rosenfeld 
999a63c99a2SToomas Soome 	if (be_is_isa("i386")) {
1000c7c0ceafSToomas Soome 		uint16_t force = flags & BE_INSTALLBOOT_FLAG_FORCE;
1001c7c0ceafSToomas Soome 		uint16_t mbr = flags & BE_INSTALLBOOT_FLAG_MBR;
1002c7c0ceafSToomas Soome 
1003c7c0ceafSToomas Soome 		if (force == BE_INSTALLBOOT_FLAG_FORCE) {
1004c7c0ceafSToomas Soome 			if (mbr == BE_INSTALLBOOT_FLAG_MBR ||
1005c7c0ceafSToomas Soome 			    be_do_install_mbr(diskname, child))
1006c7c0ceafSToomas Soome 				flag = "-F -m -f";
1007c7c0ceafSToomas Soome 			else
1008c7c0ceafSToomas Soome 				flag = "-F";
1009c7c0ceafSToomas Soome 		} else {
1010c7c0ceafSToomas Soome 			if (mbr == BE_INSTALLBOOT_FLAG_MBR ||
1011c7c0ceafSToomas Soome 			    be_do_install_mbr(diskname, child))
1012c7c0ceafSToomas Soome 				flag = "-m -f";
1013c7c0ceafSToomas Soome 		}
1014c7c0ceafSToomas Soome 
1015ce3cb817SToomas Soome 		if (be_has_grub()) {
1016ce3cb817SToomas Soome 			(void) snprintf(install_cmd, sizeof (install_cmd),
1017ce3cb817SToomas Soome 			    "%s %s %s %s %s", BE_INSTALL_GRUB, flag,
1018ce3cb817SToomas Soome 			    stage1, stage2, diskname);
1019ce3cb817SToomas Soome 		} else {
1020d7802caeSToomas Soome 			/*
1021d7802caeSToomas Soome 			 * With updated installboot, we only need boot
1022d7802caeSToomas Soome 			 * directory.
1023d7802caeSToomas Soome 			 */
1024ce3cb817SToomas Soome 			(void) snprintf(install_cmd, sizeof (install_cmd),
1025d7802caeSToomas Soome 			    "%s %s -b %s %s", BE_INSTALL_BOOT, flag,
1026d7802caeSToomas Soome 			    stage1, diskname);
1027ce3cb817SToomas Soome 		}
102866f18efaSToomas Soome 	} else if (be_is_isa("sparc")) {
1029c7c0ceafSToomas Soome 		if ((flags & BE_INSTALLBOOT_FLAG_FORCE) ==
1030c7c0ceafSToomas Soome 		    BE_INSTALLBOOT_FLAG_FORCE)
1031c7c0ceafSToomas Soome 			flag = "-f -F zfs";
1032c7c0ceafSToomas Soome 		else
1033c7c0ceafSToomas Soome 			flag = "-F zfs";
1034c7c0ceafSToomas Soome 
1035a63c99a2SToomas Soome 		(void) snprintf(install_cmd, sizeof (install_cmd),
1036a63c99a2SToomas Soome 		    "%s %s %s %s", BE_INSTALL_BOOT, flag, stage2, diskname);
103766f18efaSToomas Soome 	} else {
103866b042a7SToomas Soome 		be_print_err(gettext("%s: unsupported architecture.\n"),
103966b042a7SToomas Soome 		    __func__);
104066f18efaSToomas Soome 		return (BE_ERR_BOOTFILE_INST);
1041a63c99a2SToomas Soome 	}
1042a63c99a2SToomas Soome 
1043c7c0ceafSToomas Soome 	*be_run_cmd_outbuf = '\0';
1044c7c0ceafSToomas Soome 	*be_run_cmd_errbuf = '\0';
1045c7c0ceafSToomas Soome 
1046c7c0ceafSToomas Soome 	ret = be_run_cmd(install_cmd, be_run_cmd_errbuf, BUFSIZ,
1047c7c0ceafSToomas Soome 	    be_run_cmd_outbuf, BUFSIZ);
1048c7c0ceafSToomas Soome 
1049c7c0ceafSToomas Soome 	if (ret != BE_SUCCESS) {
105066b042a7SToomas Soome 		be_print_err(gettext("%s: install failed for device %s.\n"),
105166b042a7SToomas Soome 		    __func__, vname);
1052c7c0ceafSToomas Soome 		ret = BE_ERR_BOOTFILE_INST;
1053c7c0ceafSToomas Soome 	}
1054c7c0ceafSToomas Soome 
1055c7c0ceafSToomas Soome 	be_print_err(gettext("  Command: \"%s\"\n"), install_cmd);
1056c7c0ceafSToomas Soome 	if (be_run_cmd_outbuf[0] != 0) {
1057c7c0ceafSToomas Soome 		be_print_err(gettext("  Output:\n"));
1058c7c0ceafSToomas Soome 		be_print_err("%s", be_run_cmd_outbuf);
1059c7c0ceafSToomas Soome 	}
1060c7c0ceafSToomas Soome 
1061c7c0ceafSToomas Soome 	if (be_run_cmd_errbuf[0] != 0) {
1062c7c0ceafSToomas Soome 		be_print_err(gettext("  Errors:\n"));
10631a902ef8SHans Rosenfeld 		be_print_err("%s", be_run_cmd_errbuf);
10641a902ef8SHans Rosenfeld 	}
10651a902ef8SHans Rosenfeld 	free(vname);
10661a902ef8SHans Rosenfeld 
1067c7c0ceafSToomas Soome 	return (ret);
10681a902ef8SHans Rosenfeld }
10691a902ef8SHans Rosenfeld 
1070f169c0eaSGlenn Lagasse /*
1071a63c99a2SToomas Soome  * Function:	be_do_copy_grub_cap
1072a63c99a2SToomas Soome  * Description:	This function will copy grub capability file to BE.
1073f169c0eaSGlenn Lagasse  *
1074f169c0eaSGlenn Lagasse  * Parameters:
1075f169c0eaSGlenn Lagasse  *              bt - The transaction data for the BE we're activating.
1076f169c0eaSGlenn Lagasse  * Return:
1077f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
1078f169c0eaSGlenn Lagasse  *		be_errno_t - Failure
1079f169c0eaSGlenn Lagasse  *
1080f169c0eaSGlenn Lagasse  * Scope:
1081f169c0eaSGlenn Lagasse  *		Private
1082f169c0eaSGlenn Lagasse  */
1083f169c0eaSGlenn Lagasse static int
be_do_copy_grub_cap(be_transaction_data_t * bt)1084a63c99a2SToomas Soome be_do_copy_grub_cap(be_transaction_data_t *bt)
1085f169c0eaSGlenn Lagasse {
1086a21e1692SToomas Soome 	zfs_handle_t *zhp = NULL;
1087a63c99a2SToomas Soome 	char cap_file[MAXPATHLEN];
1088a63c99a2SToomas Soome 	char zpool_cap_file[MAXPATHLEN];
1089a63c99a2SToomas Soome 	char line[BUFSIZ];
1090a63c99a2SToomas Soome 	char *tmp_mntpnt = NULL;
1091a63c99a2SToomas Soome 	char *orig_mntpnt = NULL;
1092f169c0eaSGlenn Lagasse 	char *pool_mntpnt = NULL;
1093f169c0eaSGlenn Lagasse 	FILE *cap_fp = NULL;
1094f169c0eaSGlenn Lagasse 	FILE *zpool_cap_fp = NULL;
1095a63c99a2SToomas Soome 	int err = 0;
1096a63c99a2SToomas Soome 	int ret = BE_SUCCESS;
1097a63c99a2SToomas Soome 	boolean_t pool_mounted = B_FALSE;
1098a63c99a2SToomas Soome 	boolean_t be_mounted = B_FALSE;
1099a63c99a2SToomas Soome 
1100a63c99a2SToomas Soome 	/*
1101a21e1692SToomas Soome 	 * first get BE dataset mountpoint, we can free all the resources
1102a21e1692SToomas Soome 	 * once cap_file is built, leaving only be unmount to be done.
1103a63c99a2SToomas Soome 	 */
1104a21e1692SToomas Soome 	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
1105a21e1692SToomas Soome 	    NULL) {
110666b042a7SToomas Soome 		be_print_err(gettext("%s: failed to "
110766b042a7SToomas Soome 		    "open BE root dataset (%s): %s\n"), __func__,
110866b042a7SToomas Soome 		    bt->obe_root_ds, libzfs_error_description(g_zfs));
1109a21e1692SToomas Soome 		return (zfs_err_to_be_err(g_zfs));
1110a21e1692SToomas Soome 	}
1111a21e1692SToomas Soome 
1112a21e1692SToomas Soome 	if (!zfs_is_mounted(zhp, &tmp_mntpnt)) {
1113a21e1692SToomas Soome 		if ((ret = _be_mount(bt->obe_name, &tmp_mntpnt,
1114a21e1692SToomas Soome 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
111566b042a7SToomas Soome 			be_print_err(gettext("%s: failed to "
111666b042a7SToomas Soome 			    "mount BE (%s)\n"), __func__, bt->obe_name);
1117a21e1692SToomas Soome 			ZFS_CLOSE(zhp);
1118a21e1692SToomas Soome 			goto done;
1119a21e1692SToomas Soome 		}
1120a21e1692SToomas Soome 		be_mounted = B_TRUE;
1121a21e1692SToomas Soome 	}
1122a21e1692SToomas Soome 	ZFS_CLOSE(zhp);	/* BE dataset handle is not needed any more */
1123a21e1692SToomas Soome 
1124a21e1692SToomas Soome 	(void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpnt,
1125a21e1692SToomas Soome 	    BE_CAP_FILE);
1126a21e1692SToomas Soome 	free(tmp_mntpnt);
1127a21e1692SToomas Soome 
1128a21e1692SToomas Soome 	/* get pool root dataset mountpoint */
1129a63c99a2SToomas Soome 	zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM);
1130a63c99a2SToomas Soome 	if (zhp == NULL) {
113166b042a7SToomas Soome 		be_print_err(gettext("%s: zfs_open failed: %s\n"),
113266b042a7SToomas Soome 		    __func__, libzfs_error_description(g_zfs));
1133a21e1692SToomas Soome 		ret = zfs_err_to_be_err(g_zfs);
1134a21e1692SToomas Soome 		goto done;
1135a63c99a2SToomas Soome 	}
1136a63c99a2SToomas Soome 
1137a63c99a2SToomas Soome 	/*
1138a63c99a2SToomas Soome 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
1139a63c99a2SToomas Soome 	 * attempt to mount it.
1140a63c99a2SToomas Soome 	 */
1141a21e1692SToomas Soome 	if ((ret = be_mount_pool(zhp, &tmp_mntpnt,
1142a63c99a2SToomas Soome 	    &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
114366b042a7SToomas Soome 		be_print_err(gettext("%s: pool dataset "
114466b042a7SToomas Soome 		    "(%s) could not be mounted\n"), __func__, bt->obe_zpool);
1145a63c99a2SToomas Soome 		ZFS_CLOSE(zhp);
1146a21e1692SToomas Soome 		goto done;
1147a63c99a2SToomas Soome 	}
1148a63c99a2SToomas Soome 
1149a63c99a2SToomas Soome 	/*
1150a63c99a2SToomas Soome 	 * Get the mountpoint for the root pool dataset.
1151a21e1692SToomas Soome 	 * NOTE: zhp must be kept for _be_unmount_pool()
1152a63c99a2SToomas Soome 	 */
1153a63c99a2SToomas Soome 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
115466b042a7SToomas Soome 		be_print_err(gettext("%s: pool "
1155a63c99a2SToomas Soome 		    "dataset (%s) is not mounted. Can't check the grub "
115666b042a7SToomas Soome 		    "version from the grub capability file.\n"), __func__,
115766b042a7SToomas Soome 		    bt->obe_zpool);
1158a63c99a2SToomas Soome 		ret = BE_ERR_NO_MENU;
1159a63c99a2SToomas Soome 		goto done;
1160a63c99a2SToomas Soome 	}
1161a63c99a2SToomas Soome 
1162a63c99a2SToomas Soome 	(void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
1163a63c99a2SToomas Soome 	    pool_mntpnt, BE_CAP_FILE);
1164a63c99a2SToomas Soome 	free(pool_mntpnt);
1165a63c99a2SToomas Soome 
1166a63c99a2SToomas Soome 	if ((cap_fp = fopen(cap_file, "r")) == NULL) {
1167a63c99a2SToomas Soome 		err = errno;
116866b042a7SToomas Soome 		be_print_err(gettext("%s: failed to open grub "
116966b042a7SToomas Soome 		    "capability file\n"), __func__);
1170a63c99a2SToomas Soome 		ret = errno_to_be_err(err);
1171a63c99a2SToomas Soome 		goto done;
1172a63c99a2SToomas Soome 	}
1173a63c99a2SToomas Soome 	if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
1174a63c99a2SToomas Soome 		err = errno;
117566b042a7SToomas Soome 		be_print_err(gettext("%s: failed to open new "
117666b042a7SToomas Soome 		    "grub capability file\n"), __func__);
1177a63c99a2SToomas Soome 		ret = errno_to_be_err(err);
1178a63c99a2SToomas Soome 		(void) fclose(cap_fp);
1179a63c99a2SToomas Soome 		goto done;
1180a63c99a2SToomas Soome 	}
1181a63c99a2SToomas Soome 
1182a63c99a2SToomas Soome 	while (fgets(line, BUFSIZ, cap_fp)) {
1183a63c99a2SToomas Soome 		(void) fputs(line, zpool_cap_fp);
1184a63c99a2SToomas Soome 	}
1185a63c99a2SToomas Soome 
1186a63c99a2SToomas Soome 	(void) fclose(zpool_cap_fp);
1187a63c99a2SToomas Soome 	(void) fclose(cap_fp);
1188a63c99a2SToomas Soome 
1189a63c99a2SToomas Soome done:
1190a63c99a2SToomas Soome 	if (be_mounted)
1191a63c99a2SToomas Soome 		(void) _be_unmount(bt->obe_name, 0);
1192a63c99a2SToomas Soome 
1193a63c99a2SToomas Soome 	if (pool_mounted) {
1194a21e1692SToomas Soome 		err = be_unmount_pool(zhp, tmp_mntpnt, orig_mntpnt);
1195a63c99a2SToomas Soome 		if (ret == BE_SUCCESS)
1196a21e1692SToomas Soome 			ret = err;
1197a63c99a2SToomas Soome 		free(orig_mntpnt);
1198a21e1692SToomas Soome 		free(tmp_mntpnt);
1199a21e1692SToomas Soome 		zfs_close(zhp);
1200a63c99a2SToomas Soome 	}
1201a63c99a2SToomas Soome 	return (ret);
1202a63c99a2SToomas Soome }
1203a63c99a2SToomas Soome 
1204a63c99a2SToomas Soome /*
1205a63c99a2SToomas Soome  * Function:	be_is_install_needed
1206a63c99a2SToomas Soome  * Description:	Check detached version files to detect if bootloader
1207a63c99a2SToomas Soome  *		install/update is needed.
1208a63c99a2SToomas Soome  *
1209a63c99a2SToomas Soome  * Parameters:
1210a63c99a2SToomas Soome  *              bt - The transaction data for the BE we're activating.
1211a63c99a2SToomas Soome  *		update - set B_TRUE is update is needed.
1212a63c99a2SToomas Soome  * Return:
1213a63c99a2SToomas Soome  *		BE_SUCCESS - Success
1214a63c99a2SToomas Soome  *		be_errno_t - Failure
1215a63c99a2SToomas Soome  *
1216a63c99a2SToomas Soome  * Scope:
1217a63c99a2SToomas Soome  *		Private
1218a63c99a2SToomas Soome  */
1219a63c99a2SToomas Soome static int
be_is_install_needed(be_transaction_data_t * bt,boolean_t * update)1220a63c99a2SToomas Soome be_is_install_needed(be_transaction_data_t *bt, boolean_t *update)
1221a63c99a2SToomas Soome {
1222a63c99a2SToomas Soome 	int	ret = BE_SUCCESS;
1223a63c99a2SToomas Soome 	char	*cur_vers = NULL, *new_vers = NULL;
1224a63c99a2SToomas Soome 
1225a63c99a2SToomas Soome 	assert(bt != NULL);
1226a63c99a2SToomas Soome 	assert(update != NULL);
1227a63c99a2SToomas Soome 
1228a63c99a2SToomas Soome 	if (!be_has_grub()) {
1229a63c99a2SToomas Soome 		/*
1230a63c99a2SToomas Soome 		 * no detached versioning, let installboot to manage
1231a63c99a2SToomas Soome 		 * versioning.
1232a63c99a2SToomas Soome 		 */
1233a63c99a2SToomas Soome 		*update = B_TRUE;
1234a63c99a2SToomas Soome 		return (ret);
1235a63c99a2SToomas Soome 	}
1236a63c99a2SToomas Soome 
1237a63c99a2SToomas Soome 	*update = B_FALSE;	/* set default */
1238a63c99a2SToomas Soome 
1239a63c99a2SToomas Soome 	/*
1240a63c99a2SToomas Soome 	 * We need to check to see if the version number from
1241a63c99a2SToomas Soome 	 * the BE being activated is greater than the current
1242a63c99a2SToomas Soome 	 * one.
1243a63c99a2SToomas Soome 	 */
1244a63c99a2SToomas Soome 	ret = be_get_grub_vers(bt, &cur_vers, &new_vers);
1245a63c99a2SToomas Soome 	if (ret != BE_SUCCESS) {
1246a63c99a2SToomas Soome 		be_print_err(gettext("be_activate: failed to get grub "
1247a63c99a2SToomas Soome 		    "versions from capability files.\n"));
1248a63c99a2SToomas Soome 		return (ret);
1249a63c99a2SToomas Soome 	}
1250a63c99a2SToomas Soome 	/* update if we have both versions and can compare */
1251a63c99a2SToomas Soome 	if (cur_vers != NULL) {
1252a63c99a2SToomas Soome 		if (new_vers != NULL) {
1253a63c99a2SToomas Soome 			if (atof(cur_vers) < atof(new_vers))
1254a63c99a2SToomas Soome 				*update = B_TRUE;
1255a63c99a2SToomas Soome 			free(new_vers);
1256a63c99a2SToomas Soome 		}
1257a63c99a2SToomas Soome 		free(cur_vers);
1258a63c99a2SToomas Soome 	} else if (new_vers != NULL) {
1259a63c99a2SToomas Soome 		/* we only got new version - update */
1260a63c99a2SToomas Soome 		*update = B_TRUE;
1261a63c99a2SToomas Soome 		free(new_vers);
1262a63c99a2SToomas Soome 	}
1263a63c99a2SToomas Soome 	return (ret);
1264a63c99a2SToomas Soome }
1265a63c99a2SToomas Soome 
126666b042a7SToomas Soome static int
be_do_installboot_walk(zpool_handle_t * zphp,nvlist_t * nv,char * stage1,char * stage2,uint16_t flags)126766b042a7SToomas Soome be_do_installboot_walk(zpool_handle_t *zphp, nvlist_t *nv, char *stage1,
126866b042a7SToomas Soome     char *stage2, uint16_t flags)
126966b042a7SToomas Soome {
127066b042a7SToomas Soome 	boolean_t verbose = do_print;
127166b042a7SToomas Soome 	nvlist_t **child;
127266b042a7SToomas Soome 	uint_t children = 0;
127366b042a7SToomas Soome 	int ret = -1;
127466b042a7SToomas Soome 
127566b042a7SToomas Soome 	/* It is OK to have no children. */
127666b042a7SToomas Soome 	(void) nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
127766b042a7SToomas Soome 	    &children);
127866b042a7SToomas Soome 
127966b042a7SToomas Soome 	for (int c = 0; c < children; c++) {
128066b042a7SToomas Soome 		char *vname;
128166b042a7SToomas Soome 		int rv;
128266b042a7SToomas Soome 
128366b042a7SToomas Soome 		/* ensure update on child status */
128466b042a7SToomas Soome 		vname = zpool_vdev_name(g_zfs, zphp, child[c], verbose);
128566b042a7SToomas Soome 		if (vname == NULL) {
128666b042a7SToomas Soome 			be_print_err(gettext("%s: "
128766b042a7SToomas Soome 			    "failed to get device name: %s\n"), __func__,
128866b042a7SToomas Soome 			    libzfs_error_description(g_zfs));
128966b042a7SToomas Soome 			return (zfs_err_to_be_err(g_zfs));
129066b042a7SToomas Soome 		} else {
129166b042a7SToomas Soome 			be_print_err(gettext("%s: child %d of %d device %s\n"),
129266b042a7SToomas Soome 			    __func__, c, children, vname);
129366b042a7SToomas Soome 		}
129466b042a7SToomas Soome 
129566b042a7SToomas Soome 		rv = be_do_installboot_walk(zphp, child[c], stage1, stage2,
129666b042a7SToomas Soome 		    flags);
129766b042a7SToomas Soome 		switch (rv) {
129866b042a7SToomas Soome 		case BE_ERR_NOTSUP:
129966b042a7SToomas Soome 			/* ignore unsupported devices */
130066b042a7SToomas Soome 			be_print_err(
130166b042a7SToomas Soome 			    gettext("%s: device %s is not supported\n"),
130266b042a7SToomas Soome 			    __func__, vname);
130366b042a7SToomas Soome 			break;
130466b042a7SToomas Soome 		case BE_SUCCESS:
130566b042a7SToomas Soome 			/* catch at least one success */
130666b042a7SToomas Soome 			ret = rv;
130766b042a7SToomas Soome 			break;
130866b042a7SToomas Soome 		default:
130966b042a7SToomas Soome 			if (ret == -1)
131066b042a7SToomas Soome 				ret = rv;
131166b042a7SToomas Soome 			break;
131266b042a7SToomas Soome 		}
131366b042a7SToomas Soome 		free(vname);
131466b042a7SToomas Soome 	}
131566b042a7SToomas Soome 
131666b042a7SToomas Soome 	if (children > 0)
131766b042a7SToomas Soome 		return (ret == -1? BE_ERR_NOTSUP : ret);
131866b042a7SToomas Soome 	return (be_do_installboot_helper(zphp, nv, stage1, stage2, flags));
131966b042a7SToomas Soome }
132066b042a7SToomas Soome 
1321a63c99a2SToomas Soome /*
1322a63c99a2SToomas Soome  * Function:	be_do_installboot
1323a63c99a2SToomas Soome  * Description:	This function runs installgrub/installboot using the boot
1324a63c99a2SToomas Soome  *		loader files from the BE we're activating and installing
1325a63c99a2SToomas Soome  *		them on the pool the BE lives in.
1326a63c99a2SToomas Soome  *
1327a63c99a2SToomas Soome  * Parameters:
1328a63c99a2SToomas Soome  *              bt - The transaction data for the BE we're activating.
1329c7c0ceafSToomas Soome  *		flags - flags for bootloader install
1330a63c99a2SToomas Soome  * Return:
1331a63c99a2SToomas Soome  *		BE_SUCCESS - Success
1332a63c99a2SToomas Soome  *		be_errno_t - Failure
1333a63c99a2SToomas Soome  *
1334a63c99a2SToomas Soome  * Scope:
1335a63c99a2SToomas Soome  *		Private
1336a63c99a2SToomas Soome  */
1337a63c99a2SToomas Soome static int
be_do_installboot(be_transaction_data_t * bt,uint16_t flags)1338c7c0ceafSToomas Soome be_do_installboot(be_transaction_data_t *bt, uint16_t flags)
1339a63c99a2SToomas Soome {
1340a63c99a2SToomas Soome 	zpool_handle_t  *zphp = NULL;
1341a63c99a2SToomas Soome 	zfs_handle_t	*zhp = NULL;
134266b042a7SToomas Soome 	nvlist_t *nv, *config;
1343a63c99a2SToomas Soome 	char *tmp_mntpt = NULL;
1344f169c0eaSGlenn Lagasse 	char stage1[MAXPATHLEN];
1345f169c0eaSGlenn Lagasse 	char stage2[MAXPATHLEN];
1346f169c0eaSGlenn Lagasse 	int ret = BE_SUCCESS;
1347f169c0eaSGlenn Lagasse 	boolean_t be_mounted = B_FALSE;
1348a63c99a2SToomas Soome 	boolean_t update = B_FALSE;
1349f169c0eaSGlenn Lagasse 
1350a63c99a2SToomas Soome 	/*
1351a63c99a2SToomas Soome 	 * check versions. This call is to support detached
1352a63c99a2SToomas Soome 	 * version implementation like grub. Embedded versioning is
1353a63c99a2SToomas Soome 	 * checked by actual installer.
1354a63c99a2SToomas Soome 	 */
1355c7c0ceafSToomas Soome 	if ((flags & BE_INSTALLBOOT_FLAG_FORCE) != BE_INSTALLBOOT_FLAG_FORCE) {
1356c7c0ceafSToomas Soome 		ret = be_is_install_needed(bt, &update);
1357c7c0ceafSToomas Soome 		if (ret != BE_SUCCESS || update == B_FALSE)
1358c7c0ceafSToomas Soome 			return (ret);
1359c7c0ceafSToomas Soome 	}
1360f169c0eaSGlenn Lagasse 
1361f169c0eaSGlenn Lagasse 	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
1362f169c0eaSGlenn Lagasse 	    NULL) {
136366b042a7SToomas Soome 		be_print_err(gettext("%s: failed to "
136466b042a7SToomas Soome 		    "open BE root dataset (%s): %s\n"), __func__,
136566b042a7SToomas Soome 		    bt->obe_root_ds, libzfs_error_description(g_zfs));
1366f169c0eaSGlenn Lagasse 		ret = zfs_err_to_be_err(g_zfs);
1367f169c0eaSGlenn Lagasse 		return (ret);
1368f169c0eaSGlenn Lagasse 	}
1369f169c0eaSGlenn Lagasse 	if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
1370f169c0eaSGlenn Lagasse 		if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
1371f169c0eaSGlenn Lagasse 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
137266b042a7SToomas Soome 			be_print_err(gettext("%s: failed to "
137366b042a7SToomas Soome 			    "mount BE (%s)\n"), __func__, bt->obe_name);
1374f169c0eaSGlenn Lagasse 			ZFS_CLOSE(zhp);
1375f169c0eaSGlenn Lagasse 			return (ret);
1376f169c0eaSGlenn Lagasse 		}
1377f169c0eaSGlenn Lagasse 		be_mounted = B_TRUE;
1378f169c0eaSGlenn Lagasse 	}
1379f169c0eaSGlenn Lagasse 	ZFS_CLOSE(zhp);
1380f169c0eaSGlenn Lagasse 
1381ce3cb817SToomas Soome 	if (be_is_isa("i386")) {
1382ce3cb817SToomas Soome 		if (be_has_grub()) {
1383ce3cb817SToomas Soome 			(void) snprintf(stage1, sizeof (stage1), "%s%s",
1384ce3cb817SToomas Soome 			    tmp_mntpt, BE_GRUB_STAGE_1);
1385ce3cb817SToomas Soome 			(void) snprintf(stage2, sizeof (stage2), "%s%s",
1386ce3cb817SToomas Soome 			    tmp_mntpt, BE_GRUB_STAGE_2);
1387ce3cb817SToomas Soome 		} else {
1388ce3cb817SToomas Soome 			(void) snprintf(stage1, sizeof (stage1), "%s%s",
1389d7802caeSToomas Soome 			    tmp_mntpt, BE_LOADER_STAGES);
1390d7802caeSToomas Soome 			/* Skip stage2 */
1391ce3cb817SToomas Soome 		}
1392ce3cb817SToomas Soome 	} else if (be_is_isa("sparc")) {
1393a63c99a2SToomas Soome 		char *platform = be_get_platform();
1394a63c99a2SToomas Soome 
1395a63c99a2SToomas Soome 		if (platform == NULL) {
139666b042a7SToomas Soome 			be_print_err(gettext("%s: failed to detect system "
139766b042a7SToomas Soome 			    "platform name\n"), __func__);
1398a63c99a2SToomas Soome 			if (be_mounted)
1399a63c99a2SToomas Soome 				(void) _be_unmount(bt->obe_name, 0);
1400a63c99a2SToomas Soome 			free(tmp_mntpt);
1401a63c99a2SToomas Soome 			return (BE_ERR_BOOTFILE_INST);
1402a63c99a2SToomas Soome 		}
1403a63c99a2SToomas Soome 		stage1[0] = '\0';	/* sparc has no stage1 */
1404a63c99a2SToomas Soome 		(void) snprintf(stage2, sizeof (stage2),
1405a63c99a2SToomas Soome 		    "%s/usr/platform/%s%s", tmp_mntpt,
1406a63c99a2SToomas Soome 		    platform, BE_SPARC_BOOTBLK);
1407ce3cb817SToomas Soome 	} else {
140866b042a7SToomas Soome 		be_print_err(gettext("%s: unsupported architecture.\n"),
140966b042a7SToomas Soome 		    __func__);
1410ce3cb817SToomas Soome 		return (BE_ERR_BOOTFILE_INST);
1411a63c99a2SToomas Soome 	}
1412f169c0eaSGlenn Lagasse 
1413f169c0eaSGlenn Lagasse 	if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
141466b042a7SToomas Soome 		be_print_err(gettext("%s: failed to open "
141566b042a7SToomas Soome 		    "pool (%s): %s\n"), __func__, bt->obe_zpool,
1416f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
1417f169c0eaSGlenn Lagasse 		ret = zfs_err_to_be_err(g_zfs);
1418f169c0eaSGlenn Lagasse 		if (be_mounted)
1419f169c0eaSGlenn Lagasse 			(void) _be_unmount(bt->obe_name, 0);
1420f169c0eaSGlenn Lagasse 		free(tmp_mntpt);
1421f169c0eaSGlenn Lagasse 		return (ret);
1422f169c0eaSGlenn Lagasse 	}
1423f169c0eaSGlenn Lagasse 
1424f169c0eaSGlenn Lagasse 	if ((config = zpool_get_config(zphp, NULL)) == NULL) {
142566b042a7SToomas Soome 		be_print_err(gettext("%s: failed to get zpool "
142666b042a7SToomas Soome 		    "configuration information. %s\n"), __func__,
1427f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
1428f169c0eaSGlenn Lagasse 		ret = zfs_err_to_be_err(g_zfs);
1429f169c0eaSGlenn Lagasse 		goto done;
1430f169c0eaSGlenn Lagasse 	}
1431f169c0eaSGlenn Lagasse 
1432f169c0eaSGlenn Lagasse 	/*
1433f169c0eaSGlenn Lagasse 	 * Get the vdev tree
1434f169c0eaSGlenn Lagasse 	 */
1435f169c0eaSGlenn Lagasse 	if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
143666b042a7SToomas Soome 		be_print_err(gettext("%s: failed to get vdev "
143766b042a7SToomas Soome 		    "tree: %s\n"), __func__, libzfs_error_description(g_zfs));
1438f169c0eaSGlenn Lagasse 		ret = zfs_err_to_be_err(g_zfs);
1439f169c0eaSGlenn Lagasse 		goto done;
1440f169c0eaSGlenn Lagasse 	}
1441ce3cb817SToomas Soome 
144266b042a7SToomas Soome 	ret = be_do_installboot_walk(zphp, nv, stage1, stage2, flags);
1443f169c0eaSGlenn Lagasse 
1444a63c99a2SToomas Soome 	if (be_has_grub()) {
1445a63c99a2SToomas Soome 		ret = be_do_copy_grub_cap(bt);
1446f169c0eaSGlenn Lagasse 	}
1447f169c0eaSGlenn Lagasse 
1448f169c0eaSGlenn Lagasse done:
1449f169c0eaSGlenn Lagasse 	ZFS_CLOSE(zhp);
1450f169c0eaSGlenn Lagasse 	if (be_mounted)
1451f169c0eaSGlenn Lagasse 		(void) _be_unmount(bt->obe_name, 0);
1452f169c0eaSGlenn Lagasse 	zpool_close(zphp);
1453f169c0eaSGlenn Lagasse 	free(tmp_mntpt);
1454f169c0eaSGlenn Lagasse 	return (ret);
1455f169c0eaSGlenn Lagasse }
1456f169c0eaSGlenn Lagasse 
1457f169c0eaSGlenn Lagasse /*
1458f169c0eaSGlenn Lagasse  * Function:	be_promote_zone_ds
1459f169c0eaSGlenn Lagasse  * Description:	This function finds the zones for the BE being activated
1460f169c0eaSGlenn Lagasse  *              and the active zonepath dataset for each zone. Then each
1461f169c0eaSGlenn Lagasse  *              active zonepath dataset is promoted.
1462f169c0eaSGlenn Lagasse  *
1463f169c0eaSGlenn Lagasse  * Parameters:
1464f169c0eaSGlenn Lagasse  *              be_name - the name of the global zone BE that we need to
1465f169c0eaSGlenn Lagasse  *                       find the zones for.
1466f169c0eaSGlenn Lagasse  *              be_root_ds - the root dataset for be_name.
1467f169c0eaSGlenn Lagasse  * Return:
1468f169c0eaSGlenn Lagasse  *		BE_SUCCESS - Success
1469f169c0eaSGlenn Lagasse  *		be_errno_t - Failure
1470f169c0eaSGlenn Lagasse  *
1471f169c0eaSGlenn Lagasse  * Scope:
1472f169c0eaSGlenn Lagasse  *		Private
1473f169c0eaSGlenn Lagasse  */
1474f169c0eaSGlenn Lagasse static int
be_promote_zone_ds(char * be_name,char * be_root_ds)1475f169c0eaSGlenn Lagasse be_promote_zone_ds(char *be_name, char *be_root_ds)
1476f169c0eaSGlenn Lagasse {
1477f169c0eaSGlenn Lagasse 	char		*zone_ds = NULL;
1478f169c0eaSGlenn Lagasse 	char		*temp_mntpt = NULL;
1479f169c0eaSGlenn Lagasse 	char		origin[MAXPATHLEN];
1480f169c0eaSGlenn Lagasse 	char		zoneroot_ds[MAXPATHLEN];
1481f169c0eaSGlenn Lagasse 	zfs_handle_t	*zhp = NULL;
1482f169c0eaSGlenn Lagasse 	zfs_handle_t	*z_zhp = NULL;
1483f169c0eaSGlenn Lagasse 	zoneList_t	zone_list = NULL;
1484f169c0eaSGlenn Lagasse 	zoneBrandList_t *brands = NULL;
1485f169c0eaSGlenn Lagasse 	boolean_t	be_mounted = B_FALSE;
1486f169c0eaSGlenn Lagasse 	int		zone_index = 0;
1487f169c0eaSGlenn Lagasse 	int		err = BE_SUCCESS;
1488f169c0eaSGlenn Lagasse 
1489f169c0eaSGlenn Lagasse 	/*
1490f169c0eaSGlenn Lagasse 	 * Get the supported zone brands so we can pass that
1491f169c0eaSGlenn Lagasse 	 * to z_get_nonglobal_zone_list_by_brand. Currently
1492f169c0eaSGlenn Lagasse 	 * only the ipkg and labeled brand zones are supported
1493f169c0eaSGlenn Lagasse 	 *
1494f169c0eaSGlenn Lagasse 	 */
1495f169c0eaSGlenn Lagasse 	if ((brands = be_get_supported_brandlist()) == NULL) {
1496f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_promote_zone_ds: no supported "
1497f169c0eaSGlenn Lagasse 		    "brands\n"));
1498f169c0eaSGlenn Lagasse 		return (BE_SUCCESS);
1499f169c0eaSGlenn Lagasse 	}
1500f169c0eaSGlenn Lagasse 
1501f169c0eaSGlenn Lagasse 	if ((zhp = zfs_open(g_zfs, be_root_ds,
1502f169c0eaSGlenn Lagasse 	    ZFS_TYPE_FILESYSTEM)) == NULL) {
1503f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_promote_zone_ds: Failed to open "
1504f169c0eaSGlenn Lagasse 		    "dataset (%s): %s\n"), be_root_ds,
1505f169c0eaSGlenn Lagasse 		    libzfs_error_description(g_zfs));
1506f169c0eaSGlenn Lagasse 		err = zfs_err_to_be_err(g_zfs);
1507f169c0eaSGlenn Lagasse 		z_free_brand_list(brands);
1508f169c0eaSGlenn Lagasse 		return (err);
1509f169c0eaSGlenn Lagasse 	}
1510f169c0eaSGlenn Lagasse 
1511f169c0eaSGlenn Lagasse 	if (!zfs_is_mounted(zhp, &temp_mntpt)) {
1512f169c0eaSGlenn Lagasse 		if ((err = _be_mount(be_name, &temp_mntpt,
1513f169c0eaSGlenn Lagasse 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1514f169c0eaSGlenn Lagasse 			be_print_err(gettext("be_promote_zone_ds: failed to "
1515f169c0eaSGlenn Lagasse 			    "mount the BE for zones procesing.\n"));
1516f169c0eaSGlenn Lagasse 			ZFS_CLOSE(zhp);
1517f169c0eaSGlenn Lagasse 			z_free_brand_list(brands);
1518f169c0eaSGlenn Lagasse 			return (err);
1519f169c0eaSGlenn Lagasse 		}
1520f169c0eaSGlenn Lagasse 		be_mounted = B_TRUE;
1521f169c0eaSGlenn Lagasse 	}
1522f169c0eaSGlenn Lagasse 
1523f169c0eaSGlenn Lagasse 	/*
1524f169c0eaSGlenn Lagasse 	 * Set the zone root to the temp mount point for the BE we just mounted.
1525f169c0eaSGlenn Lagasse 	 */
1526f169c0eaSGlenn Lagasse 	z_set_zone_root(temp_mntpt);
1527f169c0eaSGlenn Lagasse 
1528f169c0eaSGlenn Lagasse 	/*
1529f169c0eaSGlenn Lagasse 	 * Get all the zones based on the brands we're looking for. If no zones
1530f169c0eaSGlenn Lagasse 	 * are found that we're interested in unmount the BE and move on.
1531f169c0eaSGlenn Lagasse 	 */
1532f169c0eaSGlenn Lagasse 	if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
1533f169c0eaSGlenn Lagasse 		if (be_mounted)
1534f169c0eaSGlenn Lagasse 			(void) _be_unmount(be_name, 0);
1535f169c0eaSGlenn Lagasse 		ZFS_CLOSE(zhp);
1536f169c0eaSGlenn Lagasse 		z_free_brand_list(brands);
1537f169c0eaSGlenn Lagasse 		free(temp_mntpt);
1538f169c0eaSGlenn Lagasse 		return (BE_SUCCESS);
1539f169c0eaSGlenn Lagasse 	}
1540f169c0eaSGlenn Lagasse 	for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
1541f169c0eaSGlenn Lagasse 	    != NULL; zone_index++) {
1542f169c0eaSGlenn Lagasse 		char *zone_path = NULL;
1543f169c0eaSGlenn Lagasse 
1544f169c0eaSGlenn Lagasse 		/* Skip zones that aren't at least installed */
1545f169c0eaSGlenn Lagasse 		if (z_zlist_get_current_state(zone_list, zone_index) <
1546f169c0eaSGlenn Lagasse 		    ZONE_STATE_INSTALLED)
1547f169c0eaSGlenn Lagasse 			continue;
1548f169c0eaSGlenn Lagasse 
1549f169c0eaSGlenn Lagasse 		if (((zone_path =
1550f169c0eaSGlenn Lagasse 		    z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
1551f169c0eaSGlenn Lagasse 		    ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
1552f169c0eaSGlenn Lagasse 		    !be_zone_supported(zone_ds))
1553f169c0eaSGlenn Lagasse 			continue;
1554f169c0eaSGlenn Lagasse 
1555f169c0eaSGlenn Lagasse 		if (be_find_active_zone_root(zhp, zone_ds,
1556f169c0eaSGlenn Lagasse 		    zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
1557f169c0eaSGlenn Lagasse 			be_print_err(gettext("be_promote_zone_ds: "
1558f169c0eaSGlenn Lagasse 			    "Zone does not have an active root "
1559f169c0eaSGlenn Lagasse 			    "dataset, skipping this zone.\n"));
1560f169c0eaSGlenn Lagasse 			continue;
1561f169c0eaSGlenn Lagasse 		}
1562f169c0eaSGlenn Lagasse 
1563f169c0eaSGlenn Lagasse 		if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
1564f169c0eaSGlenn Lagasse 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
1565f169c0eaSGlenn Lagasse 			be_print_err(gettext("be_promote_zone_ds: "
1566f169c0eaSGlenn Lagasse 			    "Failed to open dataset "
1567f169c0eaSGlenn Lagasse 			    "(%s): %s\n"), zoneroot_ds,
1568f169c0eaSGlenn Lagasse 			    libzfs_error_description(g_zfs));
1569f169c0eaSGlenn Lagasse 			err = zfs_err_to_be_err(g_zfs);
1570f169c0eaSGlenn Lagasse 			goto done;
1571f169c0eaSGlenn Lagasse 		}
1572f169c0eaSGlenn Lagasse 
1573f169c0eaSGlenn Lagasse 		if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
1574f169c0eaSGlenn Lagasse 		    sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
1575f169c0eaSGlenn Lagasse 			ZFS_CLOSE(z_zhp);
1576f169c0eaSGlenn Lagasse 			continue;
1577f169c0eaSGlenn Lagasse 		}
1578f169c0eaSGlenn Lagasse 
1579f169c0eaSGlenn Lagasse 		/*
1580f169c0eaSGlenn Lagasse 		 * We don't need to close the zfs handle at this
1581f169c0eaSGlenn Lagasse 		 * point because the callback funtion
1582f169c0eaSGlenn Lagasse 		 * be_promote_ds_callback() will close it for us.
1583f169c0eaSGlenn Lagasse 		 */
1584f169c0eaSGlenn Lagasse 		if (be_promote_ds_callback(z_zhp, NULL) != 0) {
1585f169c0eaSGlenn Lagasse 			be_print_err(gettext("be_promote_zone_ds: "
1586f169c0eaSGlenn Lagasse 			    "failed to activate the "
1587f169c0eaSGlenn Lagasse 			    "datasets for %s: %s\n"),
1588f169c0eaSGlenn Lagasse 			    zoneroot_ds,
1589f169c0eaSGlenn Lagasse 			    libzfs_error_description(g_zfs));
1590f169c0eaSGlenn Lagasse 			err = BE_ERR_PROMOTE;
1591f169c0eaSGlenn Lagasse 			goto done;
1592f169c0eaSGlenn Lagasse 		}
1593f169c0eaSGlenn Lagasse 	}
1594f169c0eaSGlenn Lagasse done:
1595f169c0eaSGlenn Lagasse 	if (be_mounted)
1596f169c0eaSGlenn Lagasse 		(void) _be_unmount(be_name, 0);
1597f169c0eaSGlenn Lagasse 	ZFS_CLOSE(zhp);
1598f169c0eaSGlenn Lagasse 	free(temp_mntpt);
1599f169c0eaSGlenn Lagasse 	z_free_brand_list(brands);
1600f169c0eaSGlenn Lagasse 	z_free_zone_list(zone_list);
1601f169c0eaSGlenn Lagasse 	return (err);
1602f169c0eaSGlenn Lagasse }
1603f169c0eaSGlenn Lagasse 
1604f169c0eaSGlenn Lagasse /*
1605f169c0eaSGlenn Lagasse  * Function:	be_promote_ds_callback
1606f169c0eaSGlenn Lagasse  * Description:	This function is used to promote the datasets for the BE
1607f169c0eaSGlenn Lagasse  *		being activated as well as the datasets for the zones BE
1608f169c0eaSGlenn Lagasse  *		being activated.
1609f169c0eaSGlenn Lagasse  *
1610f169c0eaSGlenn Lagasse  * Parameters:
1611f169c0eaSGlenn Lagasse  *              zhp - the zfs handle for zone BE being activated.
1612f169c0eaSGlenn Lagasse  *		data - not used.
1613f169c0eaSGlenn Lagasse  * Return:
1614f169c0eaSGlenn Lagasse  *		0 - Success
1615f169c0eaSGlenn Lagasse  *		be_errno_t - Failure
1616f169c0eaSGlenn Lagasse  *
1617f169c0eaSGlenn Lagasse  * Scope:
1618f169c0eaSGlenn Lagasse  *		Private
1619f169c0eaSGlenn Lagasse  */
1620f169c0eaSGlenn Lagasse static int
1621f169c0eaSGlenn Lagasse /* LINTED */
be_promote_ds_callback(zfs_handle_t * zhp,void * data)1622f169c0eaSGlenn Lagasse be_promote_ds_callback(zfs_handle_t *zhp, void *data)
1623f169c0eaSGlenn Lagasse {
1624f169c0eaSGlenn Lagasse 	char	origin[MAXPATHLEN];
1625f169c0eaSGlenn Lagasse 	char	*sub_dataset = NULL;
1626f169c0eaSGlenn Lagasse 	int	ret = 0;
1627f169c0eaSGlenn Lagasse 
1628f169c0eaSGlenn Lagasse 	if (zhp != NULL) {
1629f169c0eaSGlenn Lagasse 		sub_dataset = strdup(zfs_get_name(zhp));
1630f169c0eaSGlenn Lagasse 		if (sub_dataset == NULL) {
1631f169c0eaSGlenn Lagasse 			ret = BE_ERR_NOMEM;
1632f169c0eaSGlenn Lagasse 			goto done;
1633f169c0eaSGlenn Lagasse 		}
1634f169c0eaSGlenn Lagasse 	} else {
1635f169c0eaSGlenn Lagasse 		be_print_err(gettext("be_promote_ds_callback: "
1636f169c0eaSGlenn Lagasse 		    "Invalid zfs handle passed into function\n"));
1637f169c0eaSGlenn Lagasse 		ret = BE_ERR_INVAL;
1638f169c0eaSGlenn Lagasse 		goto done;
1639f169c0eaSGlenn Lagasse 	}
1640f169c0eaSGlenn Lagasse 
1641f169c0eaSGlenn Lagasse 	/*
1642f169c0eaSGlenn Lagasse 	 * This loop makes sure that we promote the dataset to the
1643f169c0eaSGlenn Lagasse 	 * top of the tree so that it is no longer a decendent of any
1644f169c0eaSGlenn Lagasse 	 * dataset. The ZFS close and then open is used to make sure that
1645f169c0eaSGlenn Lagasse 	 * the promotion is updated before we move on.
1646f169c0eaSGlenn Lagasse 	 */
1647f169c0eaSGlenn Lagasse 	while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
1648f169c0eaSGlenn Lagasse 	    sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
1649f169c0eaSGlenn Lagasse 		if (zfs_promote(zhp) != 0) {
1650f169c0eaSGlenn Lagasse 			if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
1651f169c0eaSGlenn Lagasse 				be_print_err(gettext("be_promote_ds_callback: "
1652f169c0eaSGlenn Lagasse 				    "promote of %s failed: %s\n"),
1653f169c0eaSGlenn Lagasse 				    zfs_get_name(zhp),
1654f169c0eaSGlenn Lagasse 				    libzfs_error_description(g_zfs));
1655f169c0eaSGlenn Lagasse 				ret = zfs_err_to_be_err(g_zfs);
1656f169c0eaSGlenn Lagasse 				goto done;
1657f169c0eaSGlenn Lagasse 			} else {
1658f169c0eaSGlenn Lagasse 				/*
1659f169c0eaSGlenn Lagasse 				 * If the call to zfs_promote returns the
1660f169c0eaSGlenn Lagasse 				 * error EZFS_EXISTS we've hit a snapshot name
1661f169c0eaSGlenn Lagasse 				 * collision. This means we're probably
1662f169c0eaSGlenn Lagasse 				 * attemping to promote a zone dataset above a
1663f169c0eaSGlenn Lagasse 				 * parent dataset that belongs to another zone
1664f169c0eaSGlenn Lagasse 				 * which this zone was cloned from.
1665f169c0eaSGlenn Lagasse 				 *
1666f169c0eaSGlenn Lagasse 				 * TODO: If this is a zone dataset at some
1667f169c0eaSGlenn Lagasse 				 * point we should skip this if the zone
1668f169c0eaSGlenn Lagasse 				 * paths for the dataset and the snapshot
1669f169c0eaSGlenn Lagasse 				 * don't match.
1670f169c0eaSGlenn Lagasse 				 */
1671f169c0eaSGlenn Lagasse 				be_print_err(gettext("be_promote_ds_callback: "
1672f169c0eaSGlenn Lagasse 				    "promote of %s failed due to snapshot "
1673f169c0eaSGlenn Lagasse 				    "name collision: %s\n"), zfs_get_name(zhp),
1674f169c0eaSGlenn Lagasse 				    libzfs_error_description(g_zfs));
1675f169c0eaSGlenn Lagasse 				ret = zfs_err_to_be_err(g_zfs);
1676f169c0eaSGlenn Lagasse 				goto done;
1677f169c0eaSGlenn Lagasse 			}
1678f169c0eaSGlenn Lagasse 		}
1679f169c0eaSGlenn Lagasse 		ZFS_CLOSE(zhp);
1680f169c0eaSGlenn Lagasse 		if ((zhp = zfs_open(g_zfs, sub_dataset,
1681f169c0eaSGlenn Lagasse 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
1682f169c0eaSGlenn Lagasse 			be_print_err(gettext("be_promote_ds_callback: "
1683f169c0eaSGlenn Lagasse 			    "Failed to open dataset (%s): %s\n"), sub_dataset,
1684f169c0eaSGlenn Lagasse 			    libzfs_error_description(g_zfs));
1685f169c0eaSGlenn Lagasse 			ret = zfs_err_to_be_err(g_zfs);
1686f169c0eaSGlenn Lagasse 			goto done;
1687f169c0eaSGlenn Lagasse 		}
1688f169c0eaSGlenn Lagasse 	}
1689f169c0eaSGlenn Lagasse 
1690f169c0eaSGlenn Lagasse 	/* Iterate down this dataset's children and promote them */
1691f169c0eaSGlenn Lagasse 	ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
1692f169c0eaSGlenn Lagasse 
1693f169c0eaSGlenn Lagasse done:
1694f169c0eaSGlenn Lagasse 	free(sub_dataset);
1695f169c0eaSGlenn Lagasse 	ZFS_CLOSE(zhp);
1696f169c0eaSGlenn Lagasse 	return (ret);
1697f169c0eaSGlenn Lagasse }
1698