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 /*
27*1a902ef8SHans Rosenfeld * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
287e0e2549SAlexander Eremin */
297e0e2549SAlexander Eremin
30f169c0eaSGlenn Lagasse #include <assert.h>
31f169c0eaSGlenn Lagasse #include <libintl.h>
32f169c0eaSGlenn Lagasse #include <libnvpair.h>
33f169c0eaSGlenn Lagasse #include <libzfs.h>
34f169c0eaSGlenn Lagasse #include <stdio.h>
35f169c0eaSGlenn Lagasse #include <stdlib.h>
36f169c0eaSGlenn Lagasse #include <string.h>
37*1a902ef8SHans Rosenfeld #include <strings.h>
38f169c0eaSGlenn Lagasse #include <errno.h>
39f169c0eaSGlenn Lagasse #include <sys/mnttab.h>
40f169c0eaSGlenn Lagasse #include <sys/types.h>
41f169c0eaSGlenn Lagasse #include <sys/stat.h>
42*1a902ef8SHans Rosenfeld #include <fcntl.h>
43f169c0eaSGlenn Lagasse #include <unistd.h>
44*1a902ef8SHans Rosenfeld #include <sys/efi_partition.h>
45f169c0eaSGlenn Lagasse
46f169c0eaSGlenn Lagasse #include <libbe.h>
47f169c0eaSGlenn Lagasse #include <libbe_priv.h>
48f169c0eaSGlenn Lagasse
49f169c0eaSGlenn Lagasse char *mnttab = MNTTAB;
50f169c0eaSGlenn Lagasse
51f169c0eaSGlenn Lagasse /*
52f169c0eaSGlenn Lagasse * Private function prototypes
53f169c0eaSGlenn Lagasse */
54f169c0eaSGlenn Lagasse static int set_bootfs(char *boot_rpool, char *be_root_ds);
55f169c0eaSGlenn Lagasse static int set_canmount(be_node_list_t *, char *);
56*1a902ef8SHans Rosenfeld static boolean_t be_do_installgrub_mbr(char *, nvlist_t *);
57*1a902ef8SHans Rosenfeld static int be_do_installgrub_helper(zpool_handle_t *, nvlist_t *, char *,
58*1a902ef8SHans Rosenfeld char *);
59f169c0eaSGlenn Lagasse static int be_do_installgrub(be_transaction_data_t *);
60f169c0eaSGlenn Lagasse static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
61f169c0eaSGlenn Lagasse static int get_ver_from_capfile(char *, char **);
62f169c0eaSGlenn Lagasse static int be_promote_zone_ds(char *, char *);
63f169c0eaSGlenn Lagasse static int be_promote_ds_callback(zfs_handle_t *, void *);
64f169c0eaSGlenn Lagasse
65f169c0eaSGlenn Lagasse /* ******************************************************************** */
66f169c0eaSGlenn Lagasse /* Public Functions */
67f169c0eaSGlenn Lagasse /* ******************************************************************** */
68f169c0eaSGlenn Lagasse
69f169c0eaSGlenn Lagasse /*
70f169c0eaSGlenn Lagasse * Function: be_activate
71f169c0eaSGlenn Lagasse * Description: Calls _be_activate which activates the BE named in the
72f169c0eaSGlenn Lagasse * attributes passed in through be_attrs. The process of
73f169c0eaSGlenn Lagasse * activation sets the bootfs property of the root pool, resets
74f169c0eaSGlenn Lagasse * the canmount property to noauto, and sets the default in the
75f169c0eaSGlenn Lagasse * grub menu to the entry corresponding to the entry for the named
76f169c0eaSGlenn Lagasse * BE.
77f169c0eaSGlenn Lagasse * Parameters:
78f169c0eaSGlenn Lagasse * be_attrs - pointer to nvlist_t of attributes being passed in.
79f169c0eaSGlenn Lagasse * The follow attribute values are used by this function:
80f169c0eaSGlenn Lagasse *
81f169c0eaSGlenn Lagasse * BE_ATTR_ORIG_BE_NAME *required
82f169c0eaSGlenn Lagasse * Return:
83f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
84f169c0eaSGlenn Lagasse * be_errno_t - Failure
85f169c0eaSGlenn Lagasse * Scope:
86f169c0eaSGlenn Lagasse * Public
87f169c0eaSGlenn Lagasse */
88f169c0eaSGlenn Lagasse int
be_activate(nvlist_t * be_attrs)89f169c0eaSGlenn Lagasse be_activate(nvlist_t *be_attrs)
90f169c0eaSGlenn Lagasse {
91f169c0eaSGlenn Lagasse int ret = BE_SUCCESS;
92f169c0eaSGlenn Lagasse char *be_name = NULL;
93f169c0eaSGlenn Lagasse
94f169c0eaSGlenn Lagasse /* Initialize libzfs handle */
95f169c0eaSGlenn Lagasse if (!be_zfs_init())
96f169c0eaSGlenn Lagasse return (BE_ERR_INIT);
97f169c0eaSGlenn Lagasse
98f169c0eaSGlenn Lagasse /* Get the BE name to activate */
99f169c0eaSGlenn Lagasse if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
100f169c0eaSGlenn Lagasse != 0) {
101f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to "
102f169c0eaSGlenn Lagasse "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
103f169c0eaSGlenn Lagasse be_zfs_fini();
104f169c0eaSGlenn Lagasse return (BE_ERR_INVAL);
105f169c0eaSGlenn Lagasse }
106f169c0eaSGlenn Lagasse
107f169c0eaSGlenn Lagasse /* Validate BE name */
108f169c0eaSGlenn Lagasse if (!be_valid_be_name(be_name)) {
109f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: invalid BE name %s\n"),
110f169c0eaSGlenn Lagasse be_name);
111f169c0eaSGlenn Lagasse be_zfs_fini();
112f169c0eaSGlenn Lagasse return (BE_ERR_INVAL);
113f169c0eaSGlenn Lagasse }
114f169c0eaSGlenn Lagasse
115f169c0eaSGlenn Lagasse ret = _be_activate(be_name);
116f169c0eaSGlenn Lagasse
117f169c0eaSGlenn Lagasse be_zfs_fini();
118f169c0eaSGlenn Lagasse
119f169c0eaSGlenn Lagasse return (ret);
120f169c0eaSGlenn Lagasse }
121f169c0eaSGlenn Lagasse
122f169c0eaSGlenn Lagasse /* ******************************************************************** */
123f169c0eaSGlenn Lagasse /* Semi Private Functions */
124f169c0eaSGlenn Lagasse /* ******************************************************************** */
125f169c0eaSGlenn Lagasse
126f169c0eaSGlenn Lagasse /*
127f169c0eaSGlenn Lagasse * Function: _be_activate
128f169c0eaSGlenn Lagasse * Description: This does the actual work described in be_activate.
129f169c0eaSGlenn Lagasse * Parameters:
130f169c0eaSGlenn Lagasse * be_name - pointer to the name of BE to activate.
131f169c0eaSGlenn Lagasse *
132f169c0eaSGlenn Lagasse * Return:
133f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
134f169c0eaSGlenn Lagasse * be_errnot_t - Failure
135f169c0eaSGlenn Lagasse * Scope:
136f169c0eaSGlenn Lagasse * Public
137f169c0eaSGlenn Lagasse */
138f169c0eaSGlenn Lagasse int
_be_activate(char * be_name)139f169c0eaSGlenn Lagasse _be_activate(char *be_name)
140f169c0eaSGlenn Lagasse {
141f169c0eaSGlenn Lagasse be_transaction_data_t cb = { 0 };
142f169c0eaSGlenn Lagasse zfs_handle_t *zhp = NULL;
143f169c0eaSGlenn Lagasse char root_ds[MAXPATHLEN];
1447e0e2549SAlexander Eremin char active_ds[MAXPATHLEN];
145f169c0eaSGlenn Lagasse char *cur_vers = NULL, *new_vers = NULL;
146f169c0eaSGlenn Lagasse be_node_list_t *be_nodes = NULL;
147f169c0eaSGlenn Lagasse uuid_t uu = {0};
148f169c0eaSGlenn Lagasse int entry, ret = BE_SUCCESS;
149f169c0eaSGlenn Lagasse int zret = 0;
150f169c0eaSGlenn Lagasse
151f169c0eaSGlenn Lagasse /*
152f169c0eaSGlenn Lagasse * TODO: The BE needs to be validated to make sure that it is actually
153f169c0eaSGlenn Lagasse * a bootable BE.
154f169c0eaSGlenn Lagasse */
155f169c0eaSGlenn Lagasse
156f169c0eaSGlenn Lagasse if (be_name == NULL)
157f169c0eaSGlenn Lagasse return (BE_ERR_INVAL);
158f169c0eaSGlenn Lagasse
159f169c0eaSGlenn Lagasse /* Set obe_name to be_name in the cb structure */
160f169c0eaSGlenn Lagasse cb.obe_name = be_name;
161f169c0eaSGlenn Lagasse
162f169c0eaSGlenn Lagasse /* find which zpool the be is in */
163f169c0eaSGlenn Lagasse if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
164f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to "
165f169c0eaSGlenn Lagasse "find zpool for BE (%s)\n"), cb.obe_name);
166f169c0eaSGlenn Lagasse return (BE_ERR_BE_NOENT);
167f169c0eaSGlenn Lagasse } else if (zret < 0) {
168f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: "
169f169c0eaSGlenn Lagasse "zpool_iter failed: %s\n"),
170f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
171f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
172f169c0eaSGlenn Lagasse return (ret);
173f169c0eaSGlenn Lagasse }
174f169c0eaSGlenn Lagasse
175f169c0eaSGlenn Lagasse be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
176f169c0eaSGlenn Lagasse cb.obe_root_ds = strdup(root_ds);
177f169c0eaSGlenn Lagasse
178f169c0eaSGlenn Lagasse if (getzoneid() == GLOBAL_ZONEID) {
179f169c0eaSGlenn Lagasse if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers,
180f169c0eaSGlenn Lagasse &new_vers)) != BE_SUCCESS) {
181f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to get grub "
182f169c0eaSGlenn Lagasse "versions from capability files.\n"));
183f169c0eaSGlenn Lagasse return (ret);
184f169c0eaSGlenn Lagasse }
185f169c0eaSGlenn Lagasse if (cur_vers != NULL) {
186f169c0eaSGlenn Lagasse /*
187f169c0eaSGlenn Lagasse * We need to check to see if the version number from
188f169c0eaSGlenn Lagasse * the BE being activated is greater than the current
189f169c0eaSGlenn Lagasse * one.
190f169c0eaSGlenn Lagasse */
191f169c0eaSGlenn Lagasse if (new_vers != NULL &&
192f169c0eaSGlenn Lagasse atof(cur_vers) < atof(new_vers)) {
193f169c0eaSGlenn Lagasse if ((ret = be_do_installgrub(&cb))
194f169c0eaSGlenn Lagasse != BE_SUCCESS) {
195f169c0eaSGlenn Lagasse free(new_vers);
196f169c0eaSGlenn Lagasse free(cur_vers);
197f169c0eaSGlenn Lagasse return (ret);
198f169c0eaSGlenn Lagasse }
199f169c0eaSGlenn Lagasse free(new_vers);
200f169c0eaSGlenn Lagasse }
201f169c0eaSGlenn Lagasse free(cur_vers);
202f169c0eaSGlenn Lagasse } else if (new_vers != NULL) {
203f169c0eaSGlenn Lagasse if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) {
204f169c0eaSGlenn Lagasse free(new_vers);
205f169c0eaSGlenn Lagasse return (ret);
206f169c0eaSGlenn Lagasse }
207f169c0eaSGlenn Lagasse free(new_vers);
208f169c0eaSGlenn Lagasse }
209f169c0eaSGlenn Lagasse if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
210f169c0eaSGlenn Lagasse if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
211f169c0eaSGlenn Lagasse NULL, NULL, NULL)) != BE_SUCCESS) {
212f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: Failed to "
213f169c0eaSGlenn Lagasse "add BE (%s) to the GRUB menu\n"),
214f169c0eaSGlenn Lagasse cb.obe_name);
215f169c0eaSGlenn Lagasse goto done;
216f169c0eaSGlenn Lagasse }
217f169c0eaSGlenn Lagasse }
218f169c0eaSGlenn Lagasse if (be_has_grub()) {
219f169c0eaSGlenn Lagasse if ((ret = be_change_grub_default(cb.obe_name,
220f169c0eaSGlenn Lagasse cb.obe_zpool)) != BE_SUCCESS) {
221f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to "
222f169c0eaSGlenn Lagasse "change the default entry in menu.lst\n"));
223f169c0eaSGlenn Lagasse goto done;
224f169c0eaSGlenn Lagasse }
225f169c0eaSGlenn Lagasse }
226f169c0eaSGlenn Lagasse }
227f169c0eaSGlenn Lagasse
228f169c0eaSGlenn Lagasse if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) {
229f169c0eaSGlenn Lagasse return (ret);
230f169c0eaSGlenn Lagasse }
231f169c0eaSGlenn Lagasse
232f169c0eaSGlenn Lagasse if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
233f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to set "
234f169c0eaSGlenn Lagasse "canmount dataset property\n"));
235f169c0eaSGlenn Lagasse goto done;
236f169c0eaSGlenn Lagasse }
237f169c0eaSGlenn Lagasse
2387e0e2549SAlexander Eremin if (getzoneid() == GLOBAL_ZONEID) {
2397e0e2549SAlexander Eremin if ((ret = set_bootfs(be_nodes->be_rpool,
2407e0e2549SAlexander Eremin root_ds)) != BE_SUCCESS) {
241f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to set "
242f169c0eaSGlenn Lagasse "bootfs pool property for %s\n"), root_ds);
243f169c0eaSGlenn Lagasse goto done;
244f169c0eaSGlenn Lagasse }
2457e0e2549SAlexander Eremin }
246f169c0eaSGlenn Lagasse
247f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
248f169c0eaSGlenn Lagasse /*
249f169c0eaSGlenn Lagasse * We don't need to close the zfs handle at this
250f169c0eaSGlenn Lagasse * point because The callback funtion
251f169c0eaSGlenn Lagasse * be_promote_ds_callback() will close it for us.
252f169c0eaSGlenn Lagasse */
253f169c0eaSGlenn Lagasse if (be_promote_ds_callback(zhp, NULL) != 0) {
254f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: "
255f169c0eaSGlenn Lagasse "failed to activate the "
256f169c0eaSGlenn Lagasse "datasets for %s: %s\n"),
257f169c0eaSGlenn Lagasse root_ds,
258f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
259f169c0eaSGlenn Lagasse ret = BE_ERR_PROMOTE;
260f169c0eaSGlenn Lagasse goto done;
261f169c0eaSGlenn Lagasse }
262f169c0eaSGlenn Lagasse } else {
2637e0e2549SAlexander Eremin be_print_err(gettext("be_activate: failed to open "
264f169c0eaSGlenn Lagasse "dataset (%s): %s\n"), root_ds,
265f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
266f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
267f169c0eaSGlenn Lagasse goto done;
268f169c0eaSGlenn Lagasse }
269f169c0eaSGlenn Lagasse
270f169c0eaSGlenn Lagasse if (getzoneid() == GLOBAL_ZONEID &&
271f169c0eaSGlenn Lagasse be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
272f169c0eaSGlenn Lagasse (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
273f169c0eaSGlenn Lagasse != BE_SUCCESS) {
274f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate: failed to promote "
275f169c0eaSGlenn Lagasse "the active zonepath datasets for zones in BE %s\n"),
276f169c0eaSGlenn Lagasse cb.obe_name);
277f169c0eaSGlenn Lagasse }
278f169c0eaSGlenn Lagasse
2797e0e2549SAlexander Eremin if (getzoneid() != GLOBAL_ZONEID) {
2807e0e2549SAlexander Eremin if (!be_zone_compare_uuids(root_ds)) {
2817e0e2549SAlexander Eremin be_print_err(gettext("be_activate: activating zone "
2827e0e2549SAlexander Eremin "root dataset from non-active global BE is not "
2837e0e2549SAlexander Eremin "supported\n"));
2847e0e2549SAlexander Eremin ret = BE_ERR_NOTSUP;
2857e0e2549SAlexander Eremin goto done;
2867e0e2549SAlexander Eremin }
2877e0e2549SAlexander Eremin if ((zhp = zfs_open(g_zfs, root_ds,
2887e0e2549SAlexander Eremin ZFS_TYPE_FILESYSTEM)) == NULL) {
2897e0e2549SAlexander Eremin be_print_err(gettext("be_activate: failed to open "
2907e0e2549SAlexander Eremin "dataset (%s): %s\n"), root_ds,
2917e0e2549SAlexander Eremin libzfs_error_description(g_zfs));
2927e0e2549SAlexander Eremin ret = zfs_err_to_be_err(g_zfs);
2937e0e2549SAlexander Eremin goto done;
2947e0e2549SAlexander Eremin }
2957e0e2549SAlexander Eremin /* Find current active zone root dataset */
2967e0e2549SAlexander Eremin if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
2977e0e2549SAlexander Eremin active_ds, sizeof (active_ds))) != BE_SUCCESS) {
2987e0e2549SAlexander Eremin be_print_err(gettext("be_activate: failed to find "
2997e0e2549SAlexander Eremin "active zone root dataset\n"));
3007e0e2549SAlexander Eremin ZFS_CLOSE(zhp);
3017e0e2549SAlexander Eremin goto done;
3027e0e2549SAlexander Eremin }
3037e0e2549SAlexander Eremin /* Do nothing if requested BE is already active */
3047e0e2549SAlexander Eremin if (strcmp(root_ds, active_ds) == 0) {
3057e0e2549SAlexander Eremin ret = BE_SUCCESS;
3067e0e2549SAlexander Eremin ZFS_CLOSE(zhp);
3077e0e2549SAlexander Eremin goto done;
3087e0e2549SAlexander Eremin }
3097e0e2549SAlexander Eremin
3107e0e2549SAlexander Eremin /* Set active property for BE */
3117e0e2549SAlexander Eremin if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
3127e0e2549SAlexander Eremin be_print_err(gettext("be_activate: failed to set "
3137e0e2549SAlexander Eremin "active property (%s): %s\n"), root_ds,
3147e0e2549SAlexander Eremin libzfs_error_description(g_zfs));
3157e0e2549SAlexander Eremin ret = zfs_err_to_be_err(g_zfs);
3167e0e2549SAlexander Eremin ZFS_CLOSE(zhp);
3177e0e2549SAlexander Eremin goto done;
3187e0e2549SAlexander Eremin }
3197e0e2549SAlexander Eremin ZFS_CLOSE(zhp);
3207e0e2549SAlexander Eremin
3217e0e2549SAlexander Eremin /* Unset active property for old active root dataset */
3227e0e2549SAlexander Eremin if ((zhp = zfs_open(g_zfs, active_ds,
3237e0e2549SAlexander Eremin ZFS_TYPE_FILESYSTEM)) == NULL) {
3247e0e2549SAlexander Eremin be_print_err(gettext("be_activate: failed to open "
3257e0e2549SAlexander Eremin "dataset (%s): %s\n"), active_ds,
3267e0e2549SAlexander Eremin libzfs_error_description(g_zfs));
3277e0e2549SAlexander Eremin ret = zfs_err_to_be_err(g_zfs);
3287e0e2549SAlexander Eremin goto done;
3297e0e2549SAlexander Eremin }
3307e0e2549SAlexander Eremin if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
3317e0e2549SAlexander Eremin be_print_err(gettext("be_activate: failed to unset "
3327e0e2549SAlexander Eremin "active property (%s): %s\n"), active_ds,
3337e0e2549SAlexander Eremin libzfs_error_description(g_zfs));
3347e0e2549SAlexander Eremin ret = zfs_err_to_be_err(g_zfs);
3357e0e2549SAlexander Eremin ZFS_CLOSE(zhp);
3367e0e2549SAlexander Eremin goto done;
3377e0e2549SAlexander Eremin }
3387e0e2549SAlexander Eremin ZFS_CLOSE(zhp);
3397e0e2549SAlexander Eremin }
340f169c0eaSGlenn Lagasse done:
341f169c0eaSGlenn Lagasse be_free_list(be_nodes);
342f169c0eaSGlenn Lagasse return (ret);
343f169c0eaSGlenn Lagasse }
344f169c0eaSGlenn Lagasse
345f169c0eaSGlenn Lagasse /*
346f169c0eaSGlenn Lagasse * Function: be_activate_current_be
347f169c0eaSGlenn Lagasse * Description: Set the currently "active" BE to be "active on boot"
348f169c0eaSGlenn Lagasse * Paramters:
349f169c0eaSGlenn Lagasse * none
350f169c0eaSGlenn Lagasse * Returns:
351f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
352f169c0eaSGlenn Lagasse * be_errnot_t - Failure
353f169c0eaSGlenn Lagasse * Scope:
354f169c0eaSGlenn Lagasse * Semi-private (library wide use only)
355f169c0eaSGlenn Lagasse */
356f169c0eaSGlenn Lagasse int
be_activate_current_be(void)357f169c0eaSGlenn Lagasse be_activate_current_be(void)
358f169c0eaSGlenn Lagasse {
359f169c0eaSGlenn Lagasse int ret = BE_SUCCESS;
360f169c0eaSGlenn Lagasse be_transaction_data_t bt = { 0 };
361f169c0eaSGlenn Lagasse
362f169c0eaSGlenn Lagasse if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
363f169c0eaSGlenn Lagasse return (ret);
364f169c0eaSGlenn Lagasse }
365f169c0eaSGlenn Lagasse
366f169c0eaSGlenn Lagasse if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
367f169c0eaSGlenn Lagasse be_print_err(gettext("be_activate_current_be: failed to "
368f169c0eaSGlenn Lagasse "activate %s\n"), bt.obe_name);
369f169c0eaSGlenn Lagasse return (ret);
370f169c0eaSGlenn Lagasse }
371f169c0eaSGlenn Lagasse
372f169c0eaSGlenn Lagasse return (BE_SUCCESS);
373f169c0eaSGlenn Lagasse }
374f169c0eaSGlenn Lagasse
375f169c0eaSGlenn Lagasse /*
376f169c0eaSGlenn Lagasse * Function: be_is_active_on_boot
377f169c0eaSGlenn Lagasse * Description: Checks if the BE name passed in has the "active on boot"
378f169c0eaSGlenn Lagasse * property set to B_TRUE.
379f169c0eaSGlenn Lagasse * Paramters:
380f169c0eaSGlenn Lagasse * be_name - the name of the BE to check
381f169c0eaSGlenn Lagasse * Returns:
382f169c0eaSGlenn Lagasse * B_TRUE - if active on boot.
383f169c0eaSGlenn Lagasse * B_FALSE - if not active on boot.
384f169c0eaSGlenn Lagasse * Scope:
385f169c0eaSGlenn Lagasse * Semi-private (library wide use only)
386f169c0eaSGlenn Lagasse */
387f169c0eaSGlenn Lagasse boolean_t
be_is_active_on_boot(char * be_name)388f169c0eaSGlenn Lagasse be_is_active_on_boot(char *be_name)
389f169c0eaSGlenn Lagasse {
390f169c0eaSGlenn Lagasse be_node_list_t *be_node = NULL;
391f169c0eaSGlenn Lagasse
392f169c0eaSGlenn Lagasse if (be_name == NULL) {
393f169c0eaSGlenn Lagasse be_print_err(gettext("be_is_active_on_boot: "
394f169c0eaSGlenn Lagasse "be_name must not be NULL\n"));
395f169c0eaSGlenn Lagasse return (B_FALSE);
396f169c0eaSGlenn Lagasse }
397f169c0eaSGlenn Lagasse
398f169c0eaSGlenn Lagasse if (_be_list(be_name, &be_node) != BE_SUCCESS) {
399f169c0eaSGlenn Lagasse return (B_FALSE);
400f169c0eaSGlenn Lagasse }
401f169c0eaSGlenn Lagasse
402f169c0eaSGlenn Lagasse if (be_node == NULL) {
403f169c0eaSGlenn Lagasse return (B_FALSE);
404f169c0eaSGlenn Lagasse }
405f169c0eaSGlenn Lagasse
406f169c0eaSGlenn Lagasse if (be_node->be_active_on_boot) {
407f169c0eaSGlenn Lagasse be_free_list(be_node);
408f169c0eaSGlenn Lagasse return (B_TRUE);
409f169c0eaSGlenn Lagasse } else {
410f169c0eaSGlenn Lagasse be_free_list(be_node);
411f169c0eaSGlenn Lagasse return (B_FALSE);
412f169c0eaSGlenn Lagasse }
413f169c0eaSGlenn Lagasse }
414f169c0eaSGlenn Lagasse
415f169c0eaSGlenn Lagasse /* ******************************************************************** */
416f169c0eaSGlenn Lagasse /* Private Functions */
417f169c0eaSGlenn Lagasse /* ******************************************************************** */
418f169c0eaSGlenn Lagasse
419f169c0eaSGlenn Lagasse /*
420f169c0eaSGlenn Lagasse * Function: set_bootfs
421f169c0eaSGlenn Lagasse * Description: Sets the bootfs property on the boot pool to be the
422f169c0eaSGlenn Lagasse * root dataset of the activated BE.
423f169c0eaSGlenn Lagasse * Parameters:
424f169c0eaSGlenn Lagasse * boot_pool - The pool we're setting bootfs in.
425f169c0eaSGlenn Lagasse * be_root_ds - The main dataset for the BE.
426f169c0eaSGlenn Lagasse * Return:
427f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
428f169c0eaSGlenn Lagasse * be_errno_t - Failure
429f169c0eaSGlenn Lagasse * Scope:
430f169c0eaSGlenn Lagasse * Private
431f169c0eaSGlenn Lagasse */
432f169c0eaSGlenn Lagasse static int
set_bootfs(char * boot_rpool,char * be_root_ds)433f169c0eaSGlenn Lagasse set_bootfs(char *boot_rpool, char *be_root_ds)
434f169c0eaSGlenn Lagasse {
435f169c0eaSGlenn Lagasse zpool_handle_t *zhp;
436f169c0eaSGlenn Lagasse int err = BE_SUCCESS;
437f169c0eaSGlenn Lagasse
438f169c0eaSGlenn Lagasse if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
439f169c0eaSGlenn Lagasse be_print_err(gettext("set_bootfs: failed to open pool "
440f169c0eaSGlenn Lagasse "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
441f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
442f169c0eaSGlenn Lagasse return (err);
443f169c0eaSGlenn Lagasse }
444f169c0eaSGlenn Lagasse
445f169c0eaSGlenn Lagasse err = zpool_set_prop(zhp, "bootfs", be_root_ds);
446f169c0eaSGlenn Lagasse if (err) {
447f169c0eaSGlenn Lagasse be_print_err(gettext("set_bootfs: failed to set "
448f169c0eaSGlenn Lagasse "bootfs property for pool %s: %s\n"), boot_rpool,
449f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
450f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
451f169c0eaSGlenn Lagasse zpool_close(zhp);
452f169c0eaSGlenn Lagasse return (err);
453f169c0eaSGlenn Lagasse }
454f169c0eaSGlenn Lagasse
455f169c0eaSGlenn Lagasse zpool_close(zhp);
456f169c0eaSGlenn Lagasse return (BE_SUCCESS);
457f169c0eaSGlenn Lagasse }
458f169c0eaSGlenn Lagasse
459f169c0eaSGlenn Lagasse /*
460f169c0eaSGlenn Lagasse * Function: set_canmount
461f169c0eaSGlenn Lagasse * Description: Sets the canmount property on the datasets of the
462f169c0eaSGlenn Lagasse * activated BE.
463f169c0eaSGlenn Lagasse * Parameters:
464f169c0eaSGlenn Lagasse * be_nodes - The be_node_t returned from be_list
465f169c0eaSGlenn Lagasse * value - The value of canmount we setting, on|off|noauto.
466f169c0eaSGlenn Lagasse * Return:
467f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
468f169c0eaSGlenn Lagasse * be_errno_t - Failure
469f169c0eaSGlenn Lagasse * Scope:
470f169c0eaSGlenn Lagasse * Private
471f169c0eaSGlenn Lagasse */
472f169c0eaSGlenn Lagasse static int
set_canmount(be_node_list_t * be_nodes,char * value)473f169c0eaSGlenn Lagasse set_canmount(be_node_list_t *be_nodes, char *value)
474f169c0eaSGlenn Lagasse {
475f169c0eaSGlenn Lagasse char ds_path[MAXPATHLEN];
476f169c0eaSGlenn Lagasse zfs_handle_t *zhp = NULL;
477f169c0eaSGlenn Lagasse be_node_list_t *list = be_nodes;
478f169c0eaSGlenn Lagasse int err = BE_SUCCESS;
479f169c0eaSGlenn Lagasse
480f169c0eaSGlenn Lagasse while (list != NULL) {
481f169c0eaSGlenn Lagasse be_dataset_list_t *datasets = list->be_node_datasets;
482f169c0eaSGlenn Lagasse
483f169c0eaSGlenn Lagasse be_make_root_ds(list->be_rpool, list->be_node_name, ds_path,
484f169c0eaSGlenn Lagasse sizeof (ds_path));
485f169c0eaSGlenn Lagasse
486f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
487f169c0eaSGlenn Lagasse NULL) {
488f169c0eaSGlenn Lagasse be_print_err(gettext("set_canmount: failed to open "
489f169c0eaSGlenn Lagasse "dataset (%s): %s\n"), ds_path,
490f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
491f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
492f169c0eaSGlenn Lagasse return (err);
493f169c0eaSGlenn Lagasse }
494f169c0eaSGlenn Lagasse if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
495f169c0eaSGlenn Lagasse /*
496f169c0eaSGlenn Lagasse * it's already mounted so we can't change the
497f169c0eaSGlenn Lagasse * canmount property anyway.
498f169c0eaSGlenn Lagasse */
499f169c0eaSGlenn Lagasse err = BE_SUCCESS;
500f169c0eaSGlenn Lagasse } else {
501f169c0eaSGlenn Lagasse err = zfs_prop_set(zhp,
502f169c0eaSGlenn Lagasse zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
503f169c0eaSGlenn Lagasse if (err) {
504f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
505f169c0eaSGlenn Lagasse be_print_err(gettext("set_canmount: failed to "
506f169c0eaSGlenn Lagasse "set dataset property (%s): %s\n"),
507f169c0eaSGlenn Lagasse ds_path, libzfs_error_description(g_zfs));
508f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
509f169c0eaSGlenn Lagasse return (err);
510f169c0eaSGlenn Lagasse }
511f169c0eaSGlenn Lagasse }
512f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
513f169c0eaSGlenn Lagasse
514f169c0eaSGlenn Lagasse while (datasets != NULL) {
515f169c0eaSGlenn Lagasse be_make_root_ds(list->be_rpool,
516f169c0eaSGlenn Lagasse datasets->be_dataset_name, ds_path,
517f169c0eaSGlenn Lagasse sizeof (ds_path));
518f169c0eaSGlenn Lagasse
519f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
520f169c0eaSGlenn Lagasse == NULL) {
521f169c0eaSGlenn Lagasse be_print_err(gettext("set_canmount: failed to "
522f169c0eaSGlenn Lagasse "open dataset %s: %s\n"), ds_path,
523f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
524f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
525f169c0eaSGlenn Lagasse return (err);
526f169c0eaSGlenn Lagasse }
527f169c0eaSGlenn Lagasse if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
528f169c0eaSGlenn Lagasse /*
529f169c0eaSGlenn Lagasse * it's already mounted so we can't change the
530f169c0eaSGlenn Lagasse * canmount property anyway.
531f169c0eaSGlenn Lagasse */
532f169c0eaSGlenn Lagasse err = BE_SUCCESS;
533f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
534f169c0eaSGlenn Lagasse break;
535f169c0eaSGlenn Lagasse }
536f169c0eaSGlenn Lagasse err = zfs_prop_set(zhp,
537f169c0eaSGlenn Lagasse zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
538f169c0eaSGlenn Lagasse if (err) {
539f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
540f169c0eaSGlenn Lagasse be_print_err(gettext("set_canmount: "
541f169c0eaSGlenn Lagasse "Failed to set property value %s "
542f169c0eaSGlenn Lagasse "for dataset %s: %s\n"), value, ds_path,
543f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
544f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
545f169c0eaSGlenn Lagasse return (err);
546f169c0eaSGlenn Lagasse }
547f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
548f169c0eaSGlenn Lagasse datasets = datasets->be_next_dataset;
549f169c0eaSGlenn Lagasse }
550f169c0eaSGlenn Lagasse list = list->be_next_node;
551f169c0eaSGlenn Lagasse }
552f169c0eaSGlenn Lagasse return (err);
553f169c0eaSGlenn Lagasse }
554f169c0eaSGlenn Lagasse
555f169c0eaSGlenn Lagasse /*
556f169c0eaSGlenn Lagasse * Function: be_get_grub_vers
557f169c0eaSGlenn Lagasse * Description: Gets the grub version number from /boot/grub/capability. If
558f169c0eaSGlenn Lagasse * capability file doesn't exist NULL is returned.
559f169c0eaSGlenn Lagasse * Parameters:
560f169c0eaSGlenn Lagasse * bt - The transaction data for the BE we're getting the grub
561f169c0eaSGlenn Lagasse * version for.
562f169c0eaSGlenn Lagasse * cur_vers - used to return the current version of grub from
563f169c0eaSGlenn Lagasse * the root pool.
564f169c0eaSGlenn Lagasse * new_vers - used to return the grub version of the BE we're
565f169c0eaSGlenn Lagasse * activating.
566f169c0eaSGlenn Lagasse * Return:
567f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
568f169c0eaSGlenn Lagasse * be_errno_t - Failed to find version
569f169c0eaSGlenn Lagasse * Scope:
570f169c0eaSGlenn Lagasse * Private
571f169c0eaSGlenn Lagasse */
572f169c0eaSGlenn Lagasse static int
be_get_grub_vers(be_transaction_data_t * bt,char ** cur_vers,char ** new_vers)573f169c0eaSGlenn Lagasse be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
574f169c0eaSGlenn Lagasse {
575f169c0eaSGlenn Lagasse zfs_handle_t *zhp = NULL;
576f169c0eaSGlenn Lagasse zfs_handle_t *pool_zhp = NULL;
577f169c0eaSGlenn Lagasse int ret = BE_SUCCESS;
578f169c0eaSGlenn Lagasse char cap_file[MAXPATHLEN];
579f169c0eaSGlenn Lagasse char *temp_mntpnt = NULL;
580f169c0eaSGlenn Lagasse char *zpool_mntpt = NULL;
581f169c0eaSGlenn Lagasse char *ptmp_mntpnt = NULL;
582f169c0eaSGlenn Lagasse char *orig_mntpnt = NULL;
583f169c0eaSGlenn Lagasse boolean_t be_mounted = B_FALSE;
584f169c0eaSGlenn Lagasse boolean_t pool_mounted = B_FALSE;
585f169c0eaSGlenn Lagasse
586f169c0eaSGlenn Lagasse if (!be_has_grub()) {
587f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: Not supported on "
588f169c0eaSGlenn Lagasse "this architecture\n"));
589f169c0eaSGlenn Lagasse return (BE_ERR_NOTSUP);
590f169c0eaSGlenn Lagasse }
591f169c0eaSGlenn Lagasse
592f169c0eaSGlenn Lagasse if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
593f169c0eaSGlenn Lagasse bt->obe_root_ds == NULL) {
594f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
595f169c0eaSGlenn Lagasse return (BE_ERR_INVAL);
596f169c0eaSGlenn Lagasse }
597f169c0eaSGlenn Lagasse
598f169c0eaSGlenn Lagasse if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
599f169c0eaSGlenn Lagasse NULL) {
600f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
601f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
602f169c0eaSGlenn Lagasse return (zfs_err_to_be_err(g_zfs));
603f169c0eaSGlenn Lagasse }
604f169c0eaSGlenn Lagasse
605f169c0eaSGlenn Lagasse /*
606f169c0eaSGlenn Lagasse * Check to see if the pool's dataset is mounted. If it isn't we'll
607f169c0eaSGlenn Lagasse * attempt to mount it.
608f169c0eaSGlenn Lagasse */
609f169c0eaSGlenn Lagasse if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
610f169c0eaSGlenn Lagasse &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
611f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: pool dataset "
612f169c0eaSGlenn Lagasse "(%s) could not be mounted\n"), bt->obe_zpool);
613f169c0eaSGlenn Lagasse ZFS_CLOSE(pool_zhp);
614f169c0eaSGlenn Lagasse return (ret);
615f169c0eaSGlenn Lagasse }
616f169c0eaSGlenn Lagasse
617f169c0eaSGlenn Lagasse /*
618f169c0eaSGlenn Lagasse * Get the mountpoint for the root pool dataset.
619f169c0eaSGlenn Lagasse */
620f169c0eaSGlenn Lagasse if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
621f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: pool "
622f169c0eaSGlenn Lagasse "dataset (%s) is not mounted. Can't set the "
623f169c0eaSGlenn Lagasse "default BE in the grub menu.\n"), bt->obe_zpool);
624f169c0eaSGlenn Lagasse ret = BE_ERR_NO_MENU;
625f169c0eaSGlenn Lagasse goto cleanup;
626f169c0eaSGlenn Lagasse }
627f169c0eaSGlenn Lagasse
628f169c0eaSGlenn Lagasse /*
629f169c0eaSGlenn Lagasse * get the version of the most recent grub update.
630f169c0eaSGlenn Lagasse */
631f169c0eaSGlenn Lagasse (void) snprintf(cap_file, sizeof (cap_file), "%s%s",
632f169c0eaSGlenn Lagasse zpool_mntpt, BE_CAP_FILE);
633f169c0eaSGlenn Lagasse free(zpool_mntpt);
634f169c0eaSGlenn Lagasse zpool_mntpt = NULL;
635f169c0eaSGlenn Lagasse
636f169c0eaSGlenn Lagasse if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
637f169c0eaSGlenn Lagasse goto cleanup;
638f169c0eaSGlenn Lagasse
639f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
640f169c0eaSGlenn Lagasse NULL) {
641f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: failed to "
642f169c0eaSGlenn Lagasse "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
643f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
644f169c0eaSGlenn Lagasse free(cur_vers);
645f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
646f169c0eaSGlenn Lagasse goto cleanup;
647f169c0eaSGlenn Lagasse }
648f169c0eaSGlenn Lagasse if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
649f169c0eaSGlenn Lagasse if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
650f169c0eaSGlenn Lagasse BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
651f169c0eaSGlenn Lagasse be_print_err(gettext("be_get_grub_vers: failed to "
652f169c0eaSGlenn Lagasse "mount BE (%s)\n"), bt->obe_name);
653f169c0eaSGlenn Lagasse free(*cur_vers);
654f169c0eaSGlenn Lagasse *cur_vers = NULL;
655f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
656f169c0eaSGlenn Lagasse goto cleanup;
657f169c0eaSGlenn Lagasse }
658f169c0eaSGlenn Lagasse be_mounted = B_TRUE;
659f169c0eaSGlenn Lagasse }
660f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
661f169c0eaSGlenn Lagasse
662f169c0eaSGlenn Lagasse /*
663f169c0eaSGlenn Lagasse * Now get the grub version for the BE being activated.
664f169c0eaSGlenn Lagasse */
665f169c0eaSGlenn Lagasse (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
666f169c0eaSGlenn Lagasse BE_CAP_FILE);
667f169c0eaSGlenn Lagasse ret = get_ver_from_capfile(cap_file, new_vers);
668f169c0eaSGlenn Lagasse if (ret != BE_SUCCESS) {
669f169c0eaSGlenn Lagasse free(*cur_vers);
670f169c0eaSGlenn Lagasse *cur_vers = NULL;
671f169c0eaSGlenn Lagasse }
672f169c0eaSGlenn Lagasse if (be_mounted)
673f169c0eaSGlenn Lagasse (void) _be_unmount(bt->obe_name, 0);
674f169c0eaSGlenn Lagasse
675f169c0eaSGlenn Lagasse cleanup:
676f169c0eaSGlenn Lagasse if (pool_mounted) {
677f169c0eaSGlenn Lagasse int iret = BE_SUCCESS;
678f169c0eaSGlenn Lagasse iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
679f169c0eaSGlenn Lagasse if (ret == BE_SUCCESS)
680f169c0eaSGlenn Lagasse ret = iret;
681f169c0eaSGlenn Lagasse free(orig_mntpnt);
682f169c0eaSGlenn Lagasse free(ptmp_mntpnt);
683f169c0eaSGlenn Lagasse }
684f169c0eaSGlenn Lagasse ZFS_CLOSE(pool_zhp);
685f169c0eaSGlenn Lagasse
686f169c0eaSGlenn Lagasse free(temp_mntpnt);
687f169c0eaSGlenn Lagasse return (ret);
688f169c0eaSGlenn Lagasse }
689f169c0eaSGlenn Lagasse
690f169c0eaSGlenn Lagasse /*
691f169c0eaSGlenn Lagasse * Function: get_ver_from_capfile
692f169c0eaSGlenn Lagasse * Description: Parses the capability file passed in looking for the VERSION
693f169c0eaSGlenn Lagasse * line. If found the version is returned in vers, if not then
694f169c0eaSGlenn Lagasse * NULL is returned in vers.
695f169c0eaSGlenn Lagasse *
696f169c0eaSGlenn Lagasse * Parameters:
697f169c0eaSGlenn Lagasse * file - the path to the capability file we want to parse.
698f169c0eaSGlenn Lagasse * vers - the version string that will be passed back.
699f169c0eaSGlenn Lagasse * Return:
700f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
701f169c0eaSGlenn Lagasse * be_errno_t - Failed to find version
702f169c0eaSGlenn Lagasse * Scope:
703f169c0eaSGlenn Lagasse * Private
704f169c0eaSGlenn Lagasse */
705f169c0eaSGlenn Lagasse static int
get_ver_from_capfile(char * file,char ** vers)706f169c0eaSGlenn Lagasse get_ver_from_capfile(char *file, char **vers)
707f169c0eaSGlenn Lagasse {
708f169c0eaSGlenn Lagasse FILE *fp = NULL;
709f169c0eaSGlenn Lagasse char line[BUFSIZ];
710f169c0eaSGlenn Lagasse char *last = NULL;
711f169c0eaSGlenn Lagasse int err = BE_SUCCESS;
712f169c0eaSGlenn Lagasse errno = 0;
713f169c0eaSGlenn Lagasse
714f169c0eaSGlenn Lagasse if (!be_has_grub()) {
715f169c0eaSGlenn Lagasse be_print_err(gettext("get_ver_from_capfile: Not supported "
716f169c0eaSGlenn Lagasse "on this architecture\n"));
717f169c0eaSGlenn Lagasse return (BE_ERR_NOTSUP);
718f169c0eaSGlenn Lagasse }
719f169c0eaSGlenn Lagasse
720f169c0eaSGlenn Lagasse /*
721f169c0eaSGlenn Lagasse * Set version string to NULL; the only case this shouldn't be set
722f169c0eaSGlenn Lagasse * to be NULL is when we've actually found a version in the capability
723f169c0eaSGlenn Lagasse * file, which is set below.
724f169c0eaSGlenn Lagasse */
725f169c0eaSGlenn Lagasse *vers = NULL;
726f169c0eaSGlenn Lagasse
727f169c0eaSGlenn Lagasse /*
728f169c0eaSGlenn Lagasse * If the capability file doesn't exist, we're returning success
729f169c0eaSGlenn Lagasse * because on older releases, the capability file did not exist
730f169c0eaSGlenn Lagasse * so this is a valid scenario.
731f169c0eaSGlenn Lagasse */
732f169c0eaSGlenn Lagasse if (access(file, F_OK) == 0) {
733f169c0eaSGlenn Lagasse if ((fp = fopen(file, "r")) == NULL) {
734f169c0eaSGlenn Lagasse err = errno;
735f169c0eaSGlenn Lagasse be_print_err(gettext("get_ver_from_capfile: failed to "
736f169c0eaSGlenn Lagasse "open file %s with error %s\n"), file,
737f169c0eaSGlenn Lagasse strerror(err));
738f169c0eaSGlenn Lagasse err = errno_to_be_err(err);
739f169c0eaSGlenn Lagasse return (err);
740f169c0eaSGlenn Lagasse }
741f169c0eaSGlenn Lagasse
742f169c0eaSGlenn Lagasse while (fgets(line, BUFSIZ, fp)) {
743f169c0eaSGlenn Lagasse char *tok = strtok_r(line, "=", &last);
744f169c0eaSGlenn Lagasse
745f169c0eaSGlenn Lagasse if (tok == NULL || tok[0] == '#') {
746f169c0eaSGlenn Lagasse continue;
747f169c0eaSGlenn Lagasse } else if (strcmp(tok, "VERSION") == 0) {
748f169c0eaSGlenn Lagasse *vers = strdup(last);
749f169c0eaSGlenn Lagasse break;
750f169c0eaSGlenn Lagasse }
751f169c0eaSGlenn Lagasse }
752f169c0eaSGlenn Lagasse (void) fclose(fp);
753f169c0eaSGlenn Lagasse }
754f169c0eaSGlenn Lagasse
755f169c0eaSGlenn Lagasse return (BE_SUCCESS);
756f169c0eaSGlenn Lagasse }
757f169c0eaSGlenn Lagasse
758f169c0eaSGlenn Lagasse /*
759*1a902ef8SHans Rosenfeld * To be able to boot EFI labeled disks, GRUB stage1 needs to be written
760*1a902ef8SHans Rosenfeld * into the MBR. We do not do this if we're on disks with a traditional
761*1a902ef8SHans Rosenfeld * fdisk partition table only, or if any foreign EFI partitions exist.
762*1a902ef8SHans Rosenfeld * In the trivial case of a whole-disk vdev we always write stage1 into
763*1a902ef8SHans Rosenfeld * the MBR.
764*1a902ef8SHans Rosenfeld */
765*1a902ef8SHans Rosenfeld static boolean_t
be_do_installgrub_mbr(char * diskname,nvlist_t * child)766*1a902ef8SHans Rosenfeld be_do_installgrub_mbr(char *diskname, nvlist_t *child)
767*1a902ef8SHans Rosenfeld {
768*1a902ef8SHans Rosenfeld struct uuid allowed_uuids[] = {
769*1a902ef8SHans Rosenfeld EFI_UNUSED,
770*1a902ef8SHans Rosenfeld EFI_RESV1,
771*1a902ef8SHans Rosenfeld EFI_BOOT,
772*1a902ef8SHans Rosenfeld EFI_ROOT,
773*1a902ef8SHans Rosenfeld EFI_SWAP,
774*1a902ef8SHans Rosenfeld EFI_USR,
775*1a902ef8SHans Rosenfeld EFI_BACKUP,
776*1a902ef8SHans Rosenfeld EFI_RESV2,
777*1a902ef8SHans Rosenfeld EFI_VAR,
778*1a902ef8SHans Rosenfeld EFI_HOME,
779*1a902ef8SHans Rosenfeld EFI_ALTSCTR,
780*1a902ef8SHans Rosenfeld EFI_RESERVED,
781*1a902ef8SHans Rosenfeld EFI_SYSTEM,
782*1a902ef8SHans Rosenfeld EFI_BIOS_BOOT,
783*1a902ef8SHans Rosenfeld EFI_SYMC_PUB,
784*1a902ef8SHans Rosenfeld EFI_SYMC_CDS
785*1a902ef8SHans Rosenfeld };
786*1a902ef8SHans Rosenfeld
787*1a902ef8SHans Rosenfeld uint64_t whole;
788*1a902ef8SHans Rosenfeld struct dk_gpt *gpt;
789*1a902ef8SHans Rosenfeld struct uuid *u;
790*1a902ef8SHans Rosenfeld int fd, npart, i, j;
791*1a902ef8SHans Rosenfeld
792*1a902ef8SHans Rosenfeld (void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_WHOLE_DISK,
793*1a902ef8SHans Rosenfeld &whole);
794*1a902ef8SHans Rosenfeld
795*1a902ef8SHans Rosenfeld if (whole)
796*1a902ef8SHans Rosenfeld return (B_TRUE);
797*1a902ef8SHans Rosenfeld
798*1a902ef8SHans Rosenfeld if ((fd = open(diskname, O_RDONLY|O_NDELAY)) < 0)
799*1a902ef8SHans Rosenfeld return (B_FALSE);
800*1a902ef8SHans Rosenfeld
801*1a902ef8SHans Rosenfeld if ((npart = efi_alloc_and_read(fd, &gpt)) <= 0)
802*1a902ef8SHans Rosenfeld return (B_FALSE);
803*1a902ef8SHans Rosenfeld
804*1a902ef8SHans Rosenfeld for (i = 0; i != npart; i++) {
805*1a902ef8SHans Rosenfeld int match = 0;
806*1a902ef8SHans Rosenfeld
807*1a902ef8SHans Rosenfeld u = &gpt->efi_parts[i].p_guid;
808*1a902ef8SHans Rosenfeld
809*1a902ef8SHans Rosenfeld for (j = 0;
810*1a902ef8SHans Rosenfeld j != sizeof (allowed_uuids) / sizeof (struct uuid);
811*1a902ef8SHans Rosenfeld j++)
812*1a902ef8SHans Rosenfeld if (bcmp(u, &allowed_uuids[j],
813*1a902ef8SHans Rosenfeld sizeof (struct uuid)) == 0)
814*1a902ef8SHans Rosenfeld match++;
815*1a902ef8SHans Rosenfeld
816*1a902ef8SHans Rosenfeld if (match == 0)
817*1a902ef8SHans Rosenfeld return (B_FALSE);
818*1a902ef8SHans Rosenfeld }
819*1a902ef8SHans Rosenfeld
820*1a902ef8SHans Rosenfeld return (B_TRUE);
821*1a902ef8SHans Rosenfeld }
822*1a902ef8SHans Rosenfeld
823*1a902ef8SHans Rosenfeld static int
be_do_installgrub_helper(zpool_handle_t * zphp,nvlist_t * child,char * stage1,char * stage2)824*1a902ef8SHans Rosenfeld be_do_installgrub_helper(zpool_handle_t *zphp, nvlist_t *child, char *stage1,
825*1a902ef8SHans Rosenfeld char *stage2)
826*1a902ef8SHans Rosenfeld {
827*1a902ef8SHans Rosenfeld char installgrub_cmd[MAXPATHLEN];
828*1a902ef8SHans Rosenfeld char be_run_cmd_errbuf[BUFSIZ];
829*1a902ef8SHans Rosenfeld char diskname[MAXPATHLEN];
830*1a902ef8SHans Rosenfeld char *vname;
831*1a902ef8SHans Rosenfeld char *path, *dsk_ptr;
832*1a902ef8SHans Rosenfeld char *m_flag = "";
833*1a902ef8SHans Rosenfeld
834*1a902ef8SHans Rosenfeld if (nvlist_lookup_string(child, ZPOOL_CONFIG_PATH, &path) != 0) {
835*1a902ef8SHans Rosenfeld be_print_err(gettext("be_do_installgrub: "
836*1a902ef8SHans Rosenfeld "failed to get device path\n"));
837*1a902ef8SHans Rosenfeld return (BE_ERR_NODEV);
838*1a902ef8SHans Rosenfeld }
839*1a902ef8SHans Rosenfeld
840*1a902ef8SHans Rosenfeld /*
841*1a902ef8SHans Rosenfeld * Modify the vdev path to point to the raw disk.
842*1a902ef8SHans Rosenfeld */
843*1a902ef8SHans Rosenfeld path = strdup(path);
844*1a902ef8SHans Rosenfeld if (path == NULL)
845*1a902ef8SHans Rosenfeld return (BE_ERR_NOMEM);
846*1a902ef8SHans Rosenfeld
847*1a902ef8SHans Rosenfeld dsk_ptr = strstr(path, "/dsk/");
848*1a902ef8SHans Rosenfeld if (dsk_ptr != NULL) {
849*1a902ef8SHans Rosenfeld *dsk_ptr = '\0';
850*1a902ef8SHans Rosenfeld dsk_ptr++;
851*1a902ef8SHans Rosenfeld } else {
852*1a902ef8SHans Rosenfeld dsk_ptr = "";
853*1a902ef8SHans Rosenfeld }
854*1a902ef8SHans Rosenfeld
855*1a902ef8SHans Rosenfeld (void) snprintf(diskname, sizeof (diskname), "%s/r%s", path, dsk_ptr);
856*1a902ef8SHans Rosenfeld free(path);
857*1a902ef8SHans Rosenfeld
858*1a902ef8SHans Rosenfeld if (be_do_installgrub_mbr(diskname, child))
859*1a902ef8SHans Rosenfeld m_flag = "-m -f";
860*1a902ef8SHans Rosenfeld
861*1a902ef8SHans Rosenfeld vname = zpool_vdev_name(g_zfs, zphp, child, B_FALSE);
862*1a902ef8SHans Rosenfeld if (vname == NULL) {
863*1a902ef8SHans Rosenfeld be_print_err(gettext("be_do_installgrub: "
864*1a902ef8SHans Rosenfeld "failed to get device name: %s\n"),
865*1a902ef8SHans Rosenfeld libzfs_error_description(g_zfs));
866*1a902ef8SHans Rosenfeld return (zfs_err_to_be_err(g_zfs));
867*1a902ef8SHans Rosenfeld }
868*1a902ef8SHans Rosenfeld
869*1a902ef8SHans Rosenfeld (void) snprintf(installgrub_cmd, sizeof (installgrub_cmd),
870*1a902ef8SHans Rosenfeld "%s %s %s %s %s", BE_INSTALL_GRUB, m_flag, stage1, stage2,
871*1a902ef8SHans Rosenfeld diskname);
872*1a902ef8SHans Rosenfeld if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf, BUFSIZ, NULL, 0)
873*1a902ef8SHans Rosenfeld != BE_SUCCESS) {
874*1a902ef8SHans Rosenfeld be_print_err(gettext("be_do_installgrub: installgrub "
875*1a902ef8SHans Rosenfeld "failed for device %s.\n"), vname);
876*1a902ef8SHans Rosenfeld /* Assume localized cmd err output. */
877*1a902ef8SHans Rosenfeld be_print_err(gettext(" Command: \"%s\"\n"),
878*1a902ef8SHans Rosenfeld installgrub_cmd);
879*1a902ef8SHans Rosenfeld be_print_err("%s", be_run_cmd_errbuf);
880*1a902ef8SHans Rosenfeld free(vname);
881*1a902ef8SHans Rosenfeld return (BE_ERR_BOOTFILE_INST);
882*1a902ef8SHans Rosenfeld }
883*1a902ef8SHans Rosenfeld free(vname);
884*1a902ef8SHans Rosenfeld
885*1a902ef8SHans Rosenfeld return (BE_SUCCESS);
886*1a902ef8SHans Rosenfeld }
887*1a902ef8SHans Rosenfeld
888*1a902ef8SHans Rosenfeld /*
889f169c0eaSGlenn Lagasse * Function: be_do_installgrub
890f169c0eaSGlenn Lagasse * Description: This function runs installgrub using the grub loader files
891f169c0eaSGlenn Lagasse * from the BE we're activating and installing them on the
892f169c0eaSGlenn Lagasse * pool the BE lives in.
893f169c0eaSGlenn Lagasse *
894f169c0eaSGlenn Lagasse * Parameters:
895f169c0eaSGlenn Lagasse * bt - The transaction data for the BE we're activating.
896f169c0eaSGlenn Lagasse * Return:
897f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
898f169c0eaSGlenn Lagasse * be_errno_t - Failure
899f169c0eaSGlenn Lagasse *
900f169c0eaSGlenn Lagasse * Scope:
901f169c0eaSGlenn Lagasse * Private
902f169c0eaSGlenn Lagasse */
903f169c0eaSGlenn Lagasse static int
be_do_installgrub(be_transaction_data_t * bt)904f169c0eaSGlenn Lagasse be_do_installgrub(be_transaction_data_t *bt)
905f169c0eaSGlenn Lagasse {
906f169c0eaSGlenn Lagasse zpool_handle_t *zphp = NULL;
907f169c0eaSGlenn Lagasse zfs_handle_t *zhp = NULL;
908f169c0eaSGlenn Lagasse nvlist_t **child, *nv, *config;
909f169c0eaSGlenn Lagasse uint_t c, children = 0;
910f169c0eaSGlenn Lagasse char *tmp_mntpt = NULL;
911f169c0eaSGlenn Lagasse char *pool_mntpnt = NULL;
912f169c0eaSGlenn Lagasse char *ptmp_mntpnt = NULL;
913f169c0eaSGlenn Lagasse char *orig_mntpnt = NULL;
914f169c0eaSGlenn Lagasse FILE *cap_fp = NULL;
915f169c0eaSGlenn Lagasse FILE *zpool_cap_fp = NULL;
916f169c0eaSGlenn Lagasse char line[BUFSIZ];
917f169c0eaSGlenn Lagasse char cap_file[MAXPATHLEN];
918f169c0eaSGlenn Lagasse char zpool_cap_file[MAXPATHLEN];
919f169c0eaSGlenn Lagasse char stage1[MAXPATHLEN];
920f169c0eaSGlenn Lagasse char stage2[MAXPATHLEN];
921f169c0eaSGlenn Lagasse char *vname;
922f169c0eaSGlenn Lagasse int ret = BE_SUCCESS;
923f169c0eaSGlenn Lagasse int err = 0;
924f169c0eaSGlenn Lagasse boolean_t be_mounted = B_FALSE;
925f169c0eaSGlenn Lagasse boolean_t pool_mounted = B_FALSE;
926f169c0eaSGlenn Lagasse
927f169c0eaSGlenn Lagasse if (!be_has_grub()) {
928f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: Not supported "
929f169c0eaSGlenn Lagasse "on this architecture\n"));
930f169c0eaSGlenn Lagasse return (BE_ERR_NOTSUP);
931f169c0eaSGlenn Lagasse }
932f169c0eaSGlenn Lagasse
933f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
934f169c0eaSGlenn Lagasse NULL) {
935f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to "
936f169c0eaSGlenn Lagasse "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
937f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
938f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
939f169c0eaSGlenn Lagasse return (ret);
940f169c0eaSGlenn Lagasse }
941f169c0eaSGlenn Lagasse if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
942f169c0eaSGlenn Lagasse if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
943f169c0eaSGlenn Lagasse BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
944f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to "
945f169c0eaSGlenn Lagasse "mount BE (%s)\n"), bt->obe_name);
946f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
947f169c0eaSGlenn Lagasse return (ret);
948f169c0eaSGlenn Lagasse }
949f169c0eaSGlenn Lagasse be_mounted = B_TRUE;
950f169c0eaSGlenn Lagasse }
951f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
952f169c0eaSGlenn Lagasse
953f169c0eaSGlenn Lagasse (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
954f169c0eaSGlenn Lagasse (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
955f169c0eaSGlenn Lagasse
956f169c0eaSGlenn Lagasse if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
957f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to open "
958f169c0eaSGlenn Lagasse "pool (%s): %s\n"), bt->obe_zpool,
959f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
960f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
961f169c0eaSGlenn Lagasse if (be_mounted)
962f169c0eaSGlenn Lagasse (void) _be_unmount(bt->obe_name, 0);
963f169c0eaSGlenn Lagasse free(tmp_mntpt);
964f169c0eaSGlenn Lagasse return (ret);
965f169c0eaSGlenn Lagasse }
966f169c0eaSGlenn Lagasse
967f169c0eaSGlenn Lagasse if ((config = zpool_get_config(zphp, NULL)) == NULL) {
968f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to get zpool "
969f169c0eaSGlenn Lagasse "configuration information. %s\n"),
970f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
971f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
972f169c0eaSGlenn Lagasse goto done;
973f169c0eaSGlenn Lagasse }
974f169c0eaSGlenn Lagasse
975f169c0eaSGlenn Lagasse /*
976f169c0eaSGlenn Lagasse * Get the vdev tree
977f169c0eaSGlenn Lagasse */
978f169c0eaSGlenn Lagasse if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
979f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to get vdev "
980f169c0eaSGlenn Lagasse "tree: %s\n"), libzfs_error_description(g_zfs));
981f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
982f169c0eaSGlenn Lagasse goto done;
983f169c0eaSGlenn Lagasse }
984f169c0eaSGlenn Lagasse
985f169c0eaSGlenn Lagasse if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
986f169c0eaSGlenn Lagasse &children) != 0) {
987f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to traverse "
988f169c0eaSGlenn Lagasse "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
989f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
990f169c0eaSGlenn Lagasse goto done;
991f169c0eaSGlenn Lagasse }
992f169c0eaSGlenn Lagasse for (c = 0; c < children; c++) {
993f169c0eaSGlenn Lagasse uint_t i, nchildren = 0;
994f169c0eaSGlenn Lagasse nvlist_t **nvchild;
995f169c0eaSGlenn Lagasse vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE);
996f169c0eaSGlenn Lagasse if (vname == NULL) {
997f169c0eaSGlenn Lagasse be_print_err(gettext(
998f169c0eaSGlenn Lagasse "be_do_installgrub: "
999f169c0eaSGlenn Lagasse "failed to get device name: %s\n"),
1000f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1001f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
1002f169c0eaSGlenn Lagasse goto done;
1003f169c0eaSGlenn Lagasse }
1004f169c0eaSGlenn Lagasse if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
1005*1a902ef8SHans Rosenfeld free(vname);
1006f169c0eaSGlenn Lagasse
1007f169c0eaSGlenn Lagasse if (nvlist_lookup_nvlist_array(child[c],
1008f169c0eaSGlenn Lagasse ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
1009f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: "
1010f169c0eaSGlenn Lagasse "failed to traverse the vdev tree: %s\n"),
1011f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1012f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
1013f169c0eaSGlenn Lagasse goto done;
1014f169c0eaSGlenn Lagasse }
1015f169c0eaSGlenn Lagasse
1016f169c0eaSGlenn Lagasse for (i = 0; i < nchildren; i++) {
1017*1a902ef8SHans Rosenfeld ret = be_do_installgrub_helper(zphp, nvchild[i],
1018*1a902ef8SHans Rosenfeld stage1, stage2);
1019*1a902ef8SHans Rosenfeld if (ret != BE_SUCCESS)
1020f169c0eaSGlenn Lagasse goto done;
1021f169c0eaSGlenn Lagasse }
1022f169c0eaSGlenn Lagasse } else {
1023f169c0eaSGlenn Lagasse free(vname);
1024*1a902ef8SHans Rosenfeld
1025*1a902ef8SHans Rosenfeld ret = be_do_installgrub_helper(zphp, child[c], stage1,
1026*1a902ef8SHans Rosenfeld stage2);
1027*1a902ef8SHans Rosenfeld if (ret != BE_SUCCESS)
1028f169c0eaSGlenn Lagasse goto done;
1029f169c0eaSGlenn Lagasse }
1030f169c0eaSGlenn Lagasse }
1031f169c0eaSGlenn Lagasse
1032f169c0eaSGlenn Lagasse /*
1033f169c0eaSGlenn Lagasse * Copy the grub capability file from the BE we're activating into
1034f169c0eaSGlenn Lagasse * the root pool.
1035f169c0eaSGlenn Lagasse */
1036f169c0eaSGlenn Lagasse (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
1037f169c0eaSGlenn Lagasse BE_CAP_FILE);
1038f169c0eaSGlenn Lagasse
1039f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
1040f169c0eaSGlenn Lagasse NULL) {
1041f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: zfs_open "
1042f169c0eaSGlenn Lagasse "failed: %s\n"), libzfs_error_description(g_zfs));
1043f169c0eaSGlenn Lagasse zpool_close(zphp);
1044f169c0eaSGlenn Lagasse return (zfs_err_to_be_err(g_zfs));
1045f169c0eaSGlenn Lagasse }
1046f169c0eaSGlenn Lagasse
1047f169c0eaSGlenn Lagasse /*
1048f169c0eaSGlenn Lagasse * Check to see if the pool's dataset is mounted. If it isn't we'll
1049f169c0eaSGlenn Lagasse * attempt to mount it.
1050f169c0eaSGlenn Lagasse */
1051f169c0eaSGlenn Lagasse if ((ret = be_mount_pool(zhp, &ptmp_mntpnt,
1052f169c0eaSGlenn Lagasse &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
1053f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: pool dataset "
1054f169c0eaSGlenn Lagasse "(%s) could not be mounted\n"), bt->obe_zpool);
1055f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1056f169c0eaSGlenn Lagasse zpool_close(zphp);
1057f169c0eaSGlenn Lagasse return (ret);
1058f169c0eaSGlenn Lagasse }
1059f169c0eaSGlenn Lagasse
1060f169c0eaSGlenn Lagasse /*
1061f169c0eaSGlenn Lagasse * Get the mountpoint for the root pool dataset.
1062f169c0eaSGlenn Lagasse */
1063f169c0eaSGlenn Lagasse if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1064f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: pool "
1065f169c0eaSGlenn Lagasse "dataset (%s) is not mounted. Can't check the grub "
1066f169c0eaSGlenn Lagasse "version from the grub capability file.\n"), bt->obe_zpool);
1067f169c0eaSGlenn Lagasse ret = BE_ERR_NO_MENU;
1068f169c0eaSGlenn Lagasse goto done;
1069f169c0eaSGlenn Lagasse }
1070f169c0eaSGlenn Lagasse
1071f169c0eaSGlenn Lagasse (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
1072f169c0eaSGlenn Lagasse pool_mntpnt, BE_CAP_FILE);
1073f169c0eaSGlenn Lagasse
1074f169c0eaSGlenn Lagasse free(pool_mntpnt);
1075f169c0eaSGlenn Lagasse pool_mntpnt = NULL;
1076f169c0eaSGlenn Lagasse
1077f169c0eaSGlenn Lagasse if ((cap_fp = fopen(cap_file, "r")) == NULL) {
1078f169c0eaSGlenn Lagasse err = errno;
1079f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to open grub "
1080f169c0eaSGlenn Lagasse "capability file\n"));
1081f169c0eaSGlenn Lagasse ret = errno_to_be_err(err);
1082f169c0eaSGlenn Lagasse goto done;
1083f169c0eaSGlenn Lagasse }
1084f169c0eaSGlenn Lagasse if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
1085f169c0eaSGlenn Lagasse err = errno;
1086f169c0eaSGlenn Lagasse be_print_err(gettext("be_do_installgrub: failed to open new "
1087f169c0eaSGlenn Lagasse "grub capability file\n"));
1088f169c0eaSGlenn Lagasse ret = errno_to_be_err(err);
1089f169c0eaSGlenn Lagasse (void) fclose(cap_fp);
1090f169c0eaSGlenn Lagasse goto done;
1091f169c0eaSGlenn Lagasse }
1092f169c0eaSGlenn Lagasse
1093f169c0eaSGlenn Lagasse while (fgets(line, BUFSIZ, cap_fp)) {
1094f169c0eaSGlenn Lagasse (void) fputs(line, zpool_cap_fp);
1095f169c0eaSGlenn Lagasse }
1096f169c0eaSGlenn Lagasse
1097f169c0eaSGlenn Lagasse (void) fclose(zpool_cap_fp);
1098f169c0eaSGlenn Lagasse (void) fclose(cap_fp);
1099f169c0eaSGlenn Lagasse
1100f169c0eaSGlenn Lagasse done:
1101f169c0eaSGlenn Lagasse if (pool_mounted) {
1102f169c0eaSGlenn Lagasse int iret = 0;
1103f169c0eaSGlenn Lagasse iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1104f169c0eaSGlenn Lagasse if (ret == BE_SUCCESS)
1105f169c0eaSGlenn Lagasse ret = iret;
1106f169c0eaSGlenn Lagasse free(orig_mntpnt);
1107f169c0eaSGlenn Lagasse free(ptmp_mntpnt);
1108f169c0eaSGlenn Lagasse }
1109f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1110f169c0eaSGlenn Lagasse if (be_mounted)
1111f169c0eaSGlenn Lagasse (void) _be_unmount(bt->obe_name, 0);
1112f169c0eaSGlenn Lagasse zpool_close(zphp);
1113f169c0eaSGlenn Lagasse free(tmp_mntpt);
1114f169c0eaSGlenn Lagasse return (ret);
1115f169c0eaSGlenn Lagasse }
1116f169c0eaSGlenn Lagasse
1117f169c0eaSGlenn Lagasse /*
1118f169c0eaSGlenn Lagasse * Function: be_promote_zone_ds
1119f169c0eaSGlenn Lagasse * Description: This function finds the zones for the BE being activated
1120f169c0eaSGlenn Lagasse * and the active zonepath dataset for each zone. Then each
1121f169c0eaSGlenn Lagasse * active zonepath dataset is promoted.
1122f169c0eaSGlenn Lagasse *
1123f169c0eaSGlenn Lagasse * Parameters:
1124f169c0eaSGlenn Lagasse * be_name - the name of the global zone BE that we need to
1125f169c0eaSGlenn Lagasse * find the zones for.
1126f169c0eaSGlenn Lagasse * be_root_ds - the root dataset for be_name.
1127f169c0eaSGlenn Lagasse * Return:
1128f169c0eaSGlenn Lagasse * BE_SUCCESS - Success
1129f169c0eaSGlenn Lagasse * be_errno_t - Failure
1130f169c0eaSGlenn Lagasse *
1131f169c0eaSGlenn Lagasse * Scope:
1132f169c0eaSGlenn Lagasse * Private
1133f169c0eaSGlenn Lagasse */
1134f169c0eaSGlenn Lagasse static int
be_promote_zone_ds(char * be_name,char * be_root_ds)1135f169c0eaSGlenn Lagasse be_promote_zone_ds(char *be_name, char *be_root_ds)
1136f169c0eaSGlenn Lagasse {
1137f169c0eaSGlenn Lagasse char *zone_ds = NULL;
1138f169c0eaSGlenn Lagasse char *temp_mntpt = NULL;
1139f169c0eaSGlenn Lagasse char origin[MAXPATHLEN];
1140f169c0eaSGlenn Lagasse char zoneroot_ds[MAXPATHLEN];
1141f169c0eaSGlenn Lagasse zfs_handle_t *zhp = NULL;
1142f169c0eaSGlenn Lagasse zfs_handle_t *z_zhp = NULL;
1143f169c0eaSGlenn Lagasse zoneList_t zone_list = NULL;
1144f169c0eaSGlenn Lagasse zoneBrandList_t *brands = NULL;
1145f169c0eaSGlenn Lagasse boolean_t be_mounted = B_FALSE;
1146f169c0eaSGlenn Lagasse int zone_index = 0;
1147f169c0eaSGlenn Lagasse int err = BE_SUCCESS;
1148f169c0eaSGlenn Lagasse
1149f169c0eaSGlenn Lagasse /*
1150f169c0eaSGlenn Lagasse * Get the supported zone brands so we can pass that
1151f169c0eaSGlenn Lagasse * to z_get_nonglobal_zone_list_by_brand. Currently
1152f169c0eaSGlenn Lagasse * only the ipkg and labeled brand zones are supported
1153f169c0eaSGlenn Lagasse *
1154f169c0eaSGlenn Lagasse */
1155f169c0eaSGlenn Lagasse if ((brands = be_get_supported_brandlist()) == NULL) {
1156f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_zone_ds: no supported "
1157f169c0eaSGlenn Lagasse "brands\n"));
1158f169c0eaSGlenn Lagasse return (BE_SUCCESS);
1159f169c0eaSGlenn Lagasse }
1160f169c0eaSGlenn Lagasse
1161f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, be_root_ds,
1162f169c0eaSGlenn Lagasse ZFS_TYPE_FILESYSTEM)) == NULL) {
1163f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_zone_ds: Failed to open "
1164f169c0eaSGlenn Lagasse "dataset (%s): %s\n"), be_root_ds,
1165f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1166f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
1167f169c0eaSGlenn Lagasse z_free_brand_list(brands);
1168f169c0eaSGlenn Lagasse return (err);
1169f169c0eaSGlenn Lagasse }
1170f169c0eaSGlenn Lagasse
1171f169c0eaSGlenn Lagasse if (!zfs_is_mounted(zhp, &temp_mntpt)) {
1172f169c0eaSGlenn Lagasse if ((err = _be_mount(be_name, &temp_mntpt,
1173f169c0eaSGlenn Lagasse BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1174f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_zone_ds: failed to "
1175f169c0eaSGlenn Lagasse "mount the BE for zones procesing.\n"));
1176f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1177f169c0eaSGlenn Lagasse z_free_brand_list(brands);
1178f169c0eaSGlenn Lagasse return (err);
1179f169c0eaSGlenn Lagasse }
1180f169c0eaSGlenn Lagasse be_mounted = B_TRUE;
1181f169c0eaSGlenn Lagasse }
1182f169c0eaSGlenn Lagasse
1183f169c0eaSGlenn Lagasse /*
1184f169c0eaSGlenn Lagasse * Set the zone root to the temp mount point for the BE we just mounted.
1185f169c0eaSGlenn Lagasse */
1186f169c0eaSGlenn Lagasse z_set_zone_root(temp_mntpt);
1187f169c0eaSGlenn Lagasse
1188f169c0eaSGlenn Lagasse /*
1189f169c0eaSGlenn Lagasse * Get all the zones based on the brands we're looking for. If no zones
1190f169c0eaSGlenn Lagasse * are found that we're interested in unmount the BE and move on.
1191f169c0eaSGlenn Lagasse */
1192f169c0eaSGlenn Lagasse if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
1193f169c0eaSGlenn Lagasse if (be_mounted)
1194f169c0eaSGlenn Lagasse (void) _be_unmount(be_name, 0);
1195f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1196f169c0eaSGlenn Lagasse z_free_brand_list(brands);
1197f169c0eaSGlenn Lagasse free(temp_mntpt);
1198f169c0eaSGlenn Lagasse return (BE_SUCCESS);
1199f169c0eaSGlenn Lagasse }
1200f169c0eaSGlenn Lagasse for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
1201f169c0eaSGlenn Lagasse != NULL; zone_index++) {
1202f169c0eaSGlenn Lagasse char *zone_path = NULL;
1203f169c0eaSGlenn Lagasse
1204f169c0eaSGlenn Lagasse /* Skip zones that aren't at least installed */
1205f169c0eaSGlenn Lagasse if (z_zlist_get_current_state(zone_list, zone_index) <
1206f169c0eaSGlenn Lagasse ZONE_STATE_INSTALLED)
1207f169c0eaSGlenn Lagasse continue;
1208f169c0eaSGlenn Lagasse
1209f169c0eaSGlenn Lagasse if (((zone_path =
1210f169c0eaSGlenn Lagasse z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
1211f169c0eaSGlenn Lagasse ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
1212f169c0eaSGlenn Lagasse !be_zone_supported(zone_ds))
1213f169c0eaSGlenn Lagasse continue;
1214f169c0eaSGlenn Lagasse
1215f169c0eaSGlenn Lagasse if (be_find_active_zone_root(zhp, zone_ds,
1216f169c0eaSGlenn Lagasse zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
1217f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_zone_ds: "
1218f169c0eaSGlenn Lagasse "Zone does not have an active root "
1219f169c0eaSGlenn Lagasse "dataset, skipping this zone.\n"));
1220f169c0eaSGlenn Lagasse continue;
1221f169c0eaSGlenn Lagasse }
1222f169c0eaSGlenn Lagasse
1223f169c0eaSGlenn Lagasse if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
1224f169c0eaSGlenn Lagasse ZFS_TYPE_FILESYSTEM)) == NULL) {
1225f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_zone_ds: "
1226f169c0eaSGlenn Lagasse "Failed to open dataset "
1227f169c0eaSGlenn Lagasse "(%s): %s\n"), zoneroot_ds,
1228f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1229f169c0eaSGlenn Lagasse err = zfs_err_to_be_err(g_zfs);
1230f169c0eaSGlenn Lagasse goto done;
1231f169c0eaSGlenn Lagasse }
1232f169c0eaSGlenn Lagasse
1233f169c0eaSGlenn Lagasse if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
1234f169c0eaSGlenn Lagasse sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
1235f169c0eaSGlenn Lagasse ZFS_CLOSE(z_zhp);
1236f169c0eaSGlenn Lagasse continue;
1237f169c0eaSGlenn Lagasse }
1238f169c0eaSGlenn Lagasse
1239f169c0eaSGlenn Lagasse /*
1240f169c0eaSGlenn Lagasse * We don't need to close the zfs handle at this
1241f169c0eaSGlenn Lagasse * point because the callback funtion
1242f169c0eaSGlenn Lagasse * be_promote_ds_callback() will close it for us.
1243f169c0eaSGlenn Lagasse */
1244f169c0eaSGlenn Lagasse if (be_promote_ds_callback(z_zhp, NULL) != 0) {
1245f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_zone_ds: "
1246f169c0eaSGlenn Lagasse "failed to activate the "
1247f169c0eaSGlenn Lagasse "datasets for %s: %s\n"),
1248f169c0eaSGlenn Lagasse zoneroot_ds,
1249f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1250f169c0eaSGlenn Lagasse err = BE_ERR_PROMOTE;
1251f169c0eaSGlenn Lagasse goto done;
1252f169c0eaSGlenn Lagasse }
1253f169c0eaSGlenn Lagasse }
1254f169c0eaSGlenn Lagasse done:
1255f169c0eaSGlenn Lagasse if (be_mounted)
1256f169c0eaSGlenn Lagasse (void) _be_unmount(be_name, 0);
1257f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1258f169c0eaSGlenn Lagasse free(temp_mntpt);
1259f169c0eaSGlenn Lagasse z_free_brand_list(brands);
1260f169c0eaSGlenn Lagasse z_free_zone_list(zone_list);
1261f169c0eaSGlenn Lagasse return (err);
1262f169c0eaSGlenn Lagasse }
1263f169c0eaSGlenn Lagasse
1264f169c0eaSGlenn Lagasse /*
1265f169c0eaSGlenn Lagasse * Function: be_promote_ds_callback
1266f169c0eaSGlenn Lagasse * Description: This function is used to promote the datasets for the BE
1267f169c0eaSGlenn Lagasse * being activated as well as the datasets for the zones BE
1268f169c0eaSGlenn Lagasse * being activated.
1269f169c0eaSGlenn Lagasse *
1270f169c0eaSGlenn Lagasse * Parameters:
1271f169c0eaSGlenn Lagasse * zhp - the zfs handle for zone BE being activated.
1272f169c0eaSGlenn Lagasse * data - not used.
1273f169c0eaSGlenn Lagasse * Return:
1274f169c0eaSGlenn Lagasse * 0 - Success
1275f169c0eaSGlenn Lagasse * be_errno_t - Failure
1276f169c0eaSGlenn Lagasse *
1277f169c0eaSGlenn Lagasse * Scope:
1278f169c0eaSGlenn Lagasse * Private
1279f169c0eaSGlenn Lagasse */
1280f169c0eaSGlenn Lagasse static int
1281f169c0eaSGlenn Lagasse /* LINTED */
be_promote_ds_callback(zfs_handle_t * zhp,void * data)1282f169c0eaSGlenn Lagasse be_promote_ds_callback(zfs_handle_t *zhp, void *data)
1283f169c0eaSGlenn Lagasse {
1284f169c0eaSGlenn Lagasse char origin[MAXPATHLEN];
1285f169c0eaSGlenn Lagasse char *sub_dataset = NULL;
1286f169c0eaSGlenn Lagasse int ret = 0;
1287f169c0eaSGlenn Lagasse
1288f169c0eaSGlenn Lagasse if (zhp != NULL) {
1289f169c0eaSGlenn Lagasse sub_dataset = strdup(zfs_get_name(zhp));
1290f169c0eaSGlenn Lagasse if (sub_dataset == NULL) {
1291f169c0eaSGlenn Lagasse ret = BE_ERR_NOMEM;
1292f169c0eaSGlenn Lagasse goto done;
1293f169c0eaSGlenn Lagasse }
1294f169c0eaSGlenn Lagasse } else {
1295f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_ds_callback: "
1296f169c0eaSGlenn Lagasse "Invalid zfs handle passed into function\n"));
1297f169c0eaSGlenn Lagasse ret = BE_ERR_INVAL;
1298f169c0eaSGlenn Lagasse goto done;
1299f169c0eaSGlenn Lagasse }
1300f169c0eaSGlenn Lagasse
1301f169c0eaSGlenn Lagasse /*
1302f169c0eaSGlenn Lagasse * This loop makes sure that we promote the dataset to the
1303f169c0eaSGlenn Lagasse * top of the tree so that it is no longer a decendent of any
1304f169c0eaSGlenn Lagasse * dataset. The ZFS close and then open is used to make sure that
1305f169c0eaSGlenn Lagasse * the promotion is updated before we move on.
1306f169c0eaSGlenn Lagasse */
1307f169c0eaSGlenn Lagasse while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
1308f169c0eaSGlenn Lagasse sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
1309f169c0eaSGlenn Lagasse if (zfs_promote(zhp) != 0) {
1310f169c0eaSGlenn Lagasse if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
1311f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_ds_callback: "
1312f169c0eaSGlenn Lagasse "promote of %s failed: %s\n"),
1313f169c0eaSGlenn Lagasse zfs_get_name(zhp),
1314f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1315f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
1316f169c0eaSGlenn Lagasse goto done;
1317f169c0eaSGlenn Lagasse } else {
1318f169c0eaSGlenn Lagasse /*
1319f169c0eaSGlenn Lagasse * If the call to zfs_promote returns the
1320f169c0eaSGlenn Lagasse * error EZFS_EXISTS we've hit a snapshot name
1321f169c0eaSGlenn Lagasse * collision. This means we're probably
1322f169c0eaSGlenn Lagasse * attemping to promote a zone dataset above a
1323f169c0eaSGlenn Lagasse * parent dataset that belongs to another zone
1324f169c0eaSGlenn Lagasse * which this zone was cloned from.
1325f169c0eaSGlenn Lagasse *
1326f169c0eaSGlenn Lagasse * TODO: If this is a zone dataset at some
1327f169c0eaSGlenn Lagasse * point we should skip this if the zone
1328f169c0eaSGlenn Lagasse * paths for the dataset and the snapshot
1329f169c0eaSGlenn Lagasse * don't match.
1330f169c0eaSGlenn Lagasse */
1331f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_ds_callback: "
1332f169c0eaSGlenn Lagasse "promote of %s failed due to snapshot "
1333f169c0eaSGlenn Lagasse "name collision: %s\n"), zfs_get_name(zhp),
1334f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1335f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
1336f169c0eaSGlenn Lagasse goto done;
1337f169c0eaSGlenn Lagasse }
1338f169c0eaSGlenn Lagasse }
1339f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1340f169c0eaSGlenn Lagasse if ((zhp = zfs_open(g_zfs, sub_dataset,
1341f169c0eaSGlenn Lagasse ZFS_TYPE_FILESYSTEM)) == NULL) {
1342f169c0eaSGlenn Lagasse be_print_err(gettext("be_promote_ds_callback: "
1343f169c0eaSGlenn Lagasse "Failed to open dataset (%s): %s\n"), sub_dataset,
1344f169c0eaSGlenn Lagasse libzfs_error_description(g_zfs));
1345f169c0eaSGlenn Lagasse ret = zfs_err_to_be_err(g_zfs);
1346f169c0eaSGlenn Lagasse goto done;
1347f169c0eaSGlenn Lagasse }
1348f169c0eaSGlenn Lagasse }
1349f169c0eaSGlenn Lagasse
1350f169c0eaSGlenn Lagasse /* Iterate down this dataset's children and promote them */
1351f169c0eaSGlenn Lagasse ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
1352f169c0eaSGlenn Lagasse
1353f169c0eaSGlenn Lagasse done:
1354f169c0eaSGlenn Lagasse free(sub_dataset);
1355f169c0eaSGlenn Lagasse ZFS_CLOSE(zhp);
1356f169c0eaSGlenn Lagasse return (ret);
1357f169c0eaSGlenn Lagasse }
1358