5561 support root pools on EFI/GPT partitioned disks
[unleashed.git] / usr / src / lib / libbe / common / be_activate.c
blobda6ed3fb18cbf7ef52659d1fc54c90c895d5145b
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
30 #include <assert.h>
31 #include <libintl.h>
32 #include <libnvpair.h>
33 #include <libzfs.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <errno.h>
39 #include <sys/mnttab.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <sys/efi_partition.h>
46 #include <libbe.h>
47 #include <libbe_priv.h>
49 char *mnttab = MNTTAB;
52 * Private function prototypes
54 static int set_bootfs(char *boot_rpool, char *be_root_ds);
55 static int set_canmount(be_node_list_t *, char *);
56 static boolean_t be_do_installgrub_mbr(char *, nvlist_t *);
57 static int be_do_installgrub_helper(zpool_handle_t *, nvlist_t *, char *,
58 char *);
59 static int be_do_installgrub(be_transaction_data_t *);
60 static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
61 static int get_ver_from_capfile(char *, char **);
62 static int be_promote_zone_ds(char *, char *);
63 static int be_promote_ds_callback(zfs_handle_t *, void *);
65 /* ******************************************************************** */
66 /* Public Functions */
67 /* ******************************************************************** */
70 * Function: be_activate
71 * Description: Calls _be_activate which activates the BE named in the
72 * attributes passed in through be_attrs. The process of
73 * activation sets the bootfs property of the root pool, resets
74 * the canmount property to noauto, and sets the default in the
75 * grub menu to the entry corresponding to the entry for the named
76 * BE.
77 * Parameters:
78 * be_attrs - pointer to nvlist_t of attributes being passed in.
79 * The follow attribute values are used by this function:
81 * BE_ATTR_ORIG_BE_NAME *required
82 * Return:
83 * BE_SUCCESS - Success
84 * be_errno_t - Failure
85 * Scope:
86 * Public
88 int
89 be_activate(nvlist_t *be_attrs)
91 int ret = BE_SUCCESS;
92 char *be_name = NULL;
94 /* Initialize libzfs handle */
95 if (!be_zfs_init())
96 return (BE_ERR_INIT);
98 /* Get the BE name to activate */
99 if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
100 != 0) {
101 be_print_err(gettext("be_activate: failed to "
102 "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
103 be_zfs_fini();
104 return (BE_ERR_INVAL);
107 /* Validate BE name */
108 if (!be_valid_be_name(be_name)) {
109 be_print_err(gettext("be_activate: invalid BE name %s\n"),
110 be_name);
111 be_zfs_fini();
112 return (BE_ERR_INVAL);
115 ret = _be_activate(be_name);
117 be_zfs_fini();
119 return (ret);
122 /* ******************************************************************** */
123 /* Semi Private Functions */
124 /* ******************************************************************** */
127 * Function: _be_activate
128 * Description: This does the actual work described in be_activate.
129 * Parameters:
130 * be_name - pointer to the name of BE to activate.
132 * Return:
133 * BE_SUCCESS - Success
134 * be_errnot_t - Failure
135 * Scope:
136 * Public
139 _be_activate(char *be_name)
141 be_transaction_data_t cb = { 0 };
142 zfs_handle_t *zhp = NULL;
143 char root_ds[MAXPATHLEN];
144 char active_ds[MAXPATHLEN];
145 char *cur_vers = NULL, *new_vers = NULL;
146 be_node_list_t *be_nodes = NULL;
147 uuid_t uu = {0};
148 int entry, ret = BE_SUCCESS;
149 int zret = 0;
152 * TODO: The BE needs to be validated to make sure that it is actually
153 * a bootable BE.
156 if (be_name == NULL)
157 return (BE_ERR_INVAL);
159 /* Set obe_name to be_name in the cb structure */
160 cb.obe_name = be_name;
162 /* find which zpool the be is in */
163 if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
164 be_print_err(gettext("be_activate: failed to "
165 "find zpool for BE (%s)\n"), cb.obe_name);
166 return (BE_ERR_BE_NOENT);
167 } else if (zret < 0) {
168 be_print_err(gettext("be_activate: "
169 "zpool_iter failed: %s\n"),
170 libzfs_error_description(g_zfs));
171 ret = zfs_err_to_be_err(g_zfs);
172 return (ret);
175 be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
176 cb.obe_root_ds = strdup(root_ds);
178 if (getzoneid() == GLOBAL_ZONEID) {
179 if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers,
180 &new_vers)) != BE_SUCCESS) {
181 be_print_err(gettext("be_activate: failed to get grub "
182 "versions from capability files.\n"));
183 return (ret);
185 if (cur_vers != NULL) {
187 * We need to check to see if the version number from
188 * the BE being activated is greater than the current
189 * one.
191 if (new_vers != NULL &&
192 atof(cur_vers) < atof(new_vers)) {
193 if ((ret = be_do_installgrub(&cb))
194 != BE_SUCCESS) {
195 free(new_vers);
196 free(cur_vers);
197 return (ret);
199 free(new_vers);
201 free(cur_vers);
202 } else if (new_vers != NULL) {
203 if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) {
204 free(new_vers);
205 return (ret);
207 free(new_vers);
209 if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
210 if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
211 NULL, NULL, NULL)) != BE_SUCCESS) {
212 be_print_err(gettext("be_activate: Failed to "
213 "add BE (%s) to the GRUB menu\n"),
214 cb.obe_name);
215 goto done;
218 if (be_has_grub()) {
219 if ((ret = be_change_grub_default(cb.obe_name,
220 cb.obe_zpool)) != BE_SUCCESS) {
221 be_print_err(gettext("be_activate: failed to "
222 "change the default entry in menu.lst\n"));
223 goto done;
228 if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) {
229 return (ret);
232 if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
233 be_print_err(gettext("be_activate: failed to set "
234 "canmount dataset property\n"));
235 goto done;
238 if (getzoneid() == GLOBAL_ZONEID) {
239 if ((ret = set_bootfs(be_nodes->be_rpool,
240 root_ds)) != BE_SUCCESS) {
241 be_print_err(gettext("be_activate: failed to set "
242 "bootfs pool property for %s\n"), root_ds);
243 goto done;
247 if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
249 * We don't need to close the zfs handle at this
250 * point because The callback funtion
251 * be_promote_ds_callback() will close it for us.
253 if (be_promote_ds_callback(zhp, NULL) != 0) {
254 be_print_err(gettext("be_activate: "
255 "failed to activate the "
256 "datasets for %s: %s\n"),
257 root_ds,
258 libzfs_error_description(g_zfs));
259 ret = BE_ERR_PROMOTE;
260 goto done;
262 } else {
263 be_print_err(gettext("be_activate: failed to open "
264 "dataset (%s): %s\n"), root_ds,
265 libzfs_error_description(g_zfs));
266 ret = zfs_err_to_be_err(g_zfs);
267 goto done;
270 if (getzoneid() == GLOBAL_ZONEID &&
271 be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
272 (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
273 != BE_SUCCESS) {
274 be_print_err(gettext("be_activate: failed to promote "
275 "the active zonepath datasets for zones in BE %s\n"),
276 cb.obe_name);
279 if (getzoneid() != GLOBAL_ZONEID) {
280 if (!be_zone_compare_uuids(root_ds)) {
281 be_print_err(gettext("be_activate: activating zone "
282 "root dataset from non-active global BE is not "
283 "supported\n"));
284 ret = BE_ERR_NOTSUP;
285 goto done;
287 if ((zhp = zfs_open(g_zfs, root_ds,
288 ZFS_TYPE_FILESYSTEM)) == NULL) {
289 be_print_err(gettext("be_activate: failed to open "
290 "dataset (%s): %s\n"), root_ds,
291 libzfs_error_description(g_zfs));
292 ret = zfs_err_to_be_err(g_zfs);
293 goto done;
295 /* Find current active zone root dataset */
296 if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
297 active_ds, sizeof (active_ds))) != BE_SUCCESS) {
298 be_print_err(gettext("be_activate: failed to find "
299 "active zone root dataset\n"));
300 ZFS_CLOSE(zhp);
301 goto done;
303 /* Do nothing if requested BE is already active */
304 if (strcmp(root_ds, active_ds) == 0) {
305 ret = BE_SUCCESS;
306 ZFS_CLOSE(zhp);
307 goto done;
310 /* Set active property for BE */
311 if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
312 be_print_err(gettext("be_activate: failed to set "
313 "active property (%s): %s\n"), root_ds,
314 libzfs_error_description(g_zfs));
315 ret = zfs_err_to_be_err(g_zfs);
316 ZFS_CLOSE(zhp);
317 goto done;
319 ZFS_CLOSE(zhp);
321 /* Unset active property for old active root dataset */
322 if ((zhp = zfs_open(g_zfs, active_ds,
323 ZFS_TYPE_FILESYSTEM)) == NULL) {
324 be_print_err(gettext("be_activate: failed to open "
325 "dataset (%s): %s\n"), active_ds,
326 libzfs_error_description(g_zfs));
327 ret = zfs_err_to_be_err(g_zfs);
328 goto done;
330 if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
331 be_print_err(gettext("be_activate: failed to unset "
332 "active property (%s): %s\n"), active_ds,
333 libzfs_error_description(g_zfs));
334 ret = zfs_err_to_be_err(g_zfs);
335 ZFS_CLOSE(zhp);
336 goto done;
338 ZFS_CLOSE(zhp);
340 done:
341 be_free_list(be_nodes);
342 return (ret);
346 * Function: be_activate_current_be
347 * Description: Set the currently "active" BE to be "active on boot"
348 * Paramters:
349 * none
350 * Returns:
351 * BE_SUCCESS - Success
352 * be_errnot_t - Failure
353 * Scope:
354 * Semi-private (library wide use only)
357 be_activate_current_be(void)
359 int ret = BE_SUCCESS;
360 be_transaction_data_t bt = { 0 };
362 if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
363 return (ret);
366 if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
367 be_print_err(gettext("be_activate_current_be: failed to "
368 "activate %s\n"), bt.obe_name);
369 return (ret);
372 return (BE_SUCCESS);
376 * Function: be_is_active_on_boot
377 * Description: Checks if the BE name passed in has the "active on boot"
378 * property set to B_TRUE.
379 * Paramters:
380 * be_name - the name of the BE to check
381 * Returns:
382 * B_TRUE - if active on boot.
383 * B_FALSE - if not active on boot.
384 * Scope:
385 * Semi-private (library wide use only)
387 boolean_t
388 be_is_active_on_boot(char *be_name)
390 be_node_list_t *be_node = NULL;
392 if (be_name == NULL) {
393 be_print_err(gettext("be_is_active_on_boot: "
394 "be_name must not be NULL\n"));
395 return (B_FALSE);
398 if (_be_list(be_name, &be_node) != BE_SUCCESS) {
399 return (B_FALSE);
402 if (be_node == NULL) {
403 return (B_FALSE);
406 if (be_node->be_active_on_boot) {
407 be_free_list(be_node);
408 return (B_TRUE);
409 } else {
410 be_free_list(be_node);
411 return (B_FALSE);
415 /* ******************************************************************** */
416 /* Private Functions */
417 /* ******************************************************************** */
420 * Function: set_bootfs
421 * Description: Sets the bootfs property on the boot pool to be the
422 * root dataset of the activated BE.
423 * Parameters:
424 * boot_pool - The pool we're setting bootfs in.
425 * be_root_ds - The main dataset for the BE.
426 * Return:
427 * BE_SUCCESS - Success
428 * be_errno_t - Failure
429 * Scope:
430 * Private
432 static int
433 set_bootfs(char *boot_rpool, char *be_root_ds)
435 zpool_handle_t *zhp;
436 int err = BE_SUCCESS;
438 if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
439 be_print_err(gettext("set_bootfs: failed to open pool "
440 "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
441 err = zfs_err_to_be_err(g_zfs);
442 return (err);
445 err = zpool_set_prop(zhp, "bootfs", be_root_ds);
446 if (err) {
447 be_print_err(gettext("set_bootfs: failed to set "
448 "bootfs property for pool %s: %s\n"), boot_rpool,
449 libzfs_error_description(g_zfs));
450 err = zfs_err_to_be_err(g_zfs);
451 zpool_close(zhp);
452 return (err);
455 zpool_close(zhp);
456 return (BE_SUCCESS);
460 * Function: set_canmount
461 * Description: Sets the canmount property on the datasets of the
462 * activated BE.
463 * Parameters:
464 * be_nodes - The be_node_t returned from be_list
465 * value - The value of canmount we setting, on|off|noauto.
466 * Return:
467 * BE_SUCCESS - Success
468 * be_errno_t - Failure
469 * Scope:
470 * Private
472 static int
473 set_canmount(be_node_list_t *be_nodes, char *value)
475 char ds_path[MAXPATHLEN];
476 zfs_handle_t *zhp = NULL;
477 be_node_list_t *list = be_nodes;
478 int err = BE_SUCCESS;
480 while (list != NULL) {
481 be_dataset_list_t *datasets = list->be_node_datasets;
483 be_make_root_ds(list->be_rpool, list->be_node_name, ds_path,
484 sizeof (ds_path));
486 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
487 NULL) {
488 be_print_err(gettext("set_canmount: failed to open "
489 "dataset (%s): %s\n"), ds_path,
490 libzfs_error_description(g_zfs));
491 err = zfs_err_to_be_err(g_zfs);
492 return (err);
494 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
496 * it's already mounted so we can't change the
497 * canmount property anyway.
499 err = BE_SUCCESS;
500 } else {
501 err = zfs_prop_set(zhp,
502 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
503 if (err) {
504 ZFS_CLOSE(zhp);
505 be_print_err(gettext("set_canmount: failed to "
506 "set dataset property (%s): %s\n"),
507 ds_path, libzfs_error_description(g_zfs));
508 err = zfs_err_to_be_err(g_zfs);
509 return (err);
512 ZFS_CLOSE(zhp);
514 while (datasets != NULL) {
515 be_make_root_ds(list->be_rpool,
516 datasets->be_dataset_name, ds_path,
517 sizeof (ds_path));
519 if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
520 == NULL) {
521 be_print_err(gettext("set_canmount: failed to "
522 "open dataset %s: %s\n"), ds_path,
523 libzfs_error_description(g_zfs));
524 err = zfs_err_to_be_err(g_zfs);
525 return (err);
527 if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
529 * it's already mounted so we can't change the
530 * canmount property anyway.
532 err = BE_SUCCESS;
533 ZFS_CLOSE(zhp);
534 break;
536 err = zfs_prop_set(zhp,
537 zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
538 if (err) {
539 ZFS_CLOSE(zhp);
540 be_print_err(gettext("set_canmount: "
541 "Failed to set property value %s "
542 "for dataset %s: %s\n"), value, ds_path,
543 libzfs_error_description(g_zfs));
544 err = zfs_err_to_be_err(g_zfs);
545 return (err);
547 ZFS_CLOSE(zhp);
548 datasets = datasets->be_next_dataset;
550 list = list->be_next_node;
552 return (err);
556 * Function: be_get_grub_vers
557 * Description: Gets the grub version number from /boot/grub/capability. If
558 * capability file doesn't exist NULL is returned.
559 * Parameters:
560 * bt - The transaction data for the BE we're getting the grub
561 * version for.
562 * cur_vers - used to return the current version of grub from
563 * the root pool.
564 * new_vers - used to return the grub version of the BE we're
565 * activating.
566 * Return:
567 * BE_SUCCESS - Success
568 * be_errno_t - Failed to find version
569 * Scope:
570 * Private
572 static int
573 be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
575 zfs_handle_t *zhp = NULL;
576 zfs_handle_t *pool_zhp = NULL;
577 int ret = BE_SUCCESS;
578 char cap_file[MAXPATHLEN];
579 char *temp_mntpnt = NULL;
580 char *zpool_mntpt = NULL;
581 char *ptmp_mntpnt = NULL;
582 char *orig_mntpnt = NULL;
583 boolean_t be_mounted = B_FALSE;
584 boolean_t pool_mounted = B_FALSE;
586 if (!be_has_grub()) {
587 be_print_err(gettext("be_get_grub_vers: Not supported on "
588 "this architecture\n"));
589 return (BE_ERR_NOTSUP);
592 if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
593 bt->obe_root_ds == NULL) {
594 be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
595 return (BE_ERR_INVAL);
598 if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
599 NULL) {
600 be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
601 libzfs_error_description(g_zfs));
602 return (zfs_err_to_be_err(g_zfs));
606 * Check to see if the pool's dataset is mounted. If it isn't we'll
607 * attempt to mount it.
609 if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
610 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
611 be_print_err(gettext("be_get_grub_vers: pool dataset "
612 "(%s) could not be mounted\n"), bt->obe_zpool);
613 ZFS_CLOSE(pool_zhp);
614 return (ret);
618 * Get the mountpoint for the root pool dataset.
620 if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
621 be_print_err(gettext("be_get_grub_vers: pool "
622 "dataset (%s) is not mounted. Can't set the "
623 "default BE in the grub menu.\n"), bt->obe_zpool);
624 ret = BE_ERR_NO_MENU;
625 goto cleanup;
629 * get the version of the most recent grub update.
631 (void) snprintf(cap_file, sizeof (cap_file), "%s%s",
632 zpool_mntpt, BE_CAP_FILE);
633 free(zpool_mntpt);
634 zpool_mntpt = NULL;
636 if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
637 goto cleanup;
639 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
640 NULL) {
641 be_print_err(gettext("be_get_grub_vers: failed to "
642 "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
643 libzfs_error_description(g_zfs));
644 free(cur_vers);
645 ret = zfs_err_to_be_err(g_zfs);
646 goto cleanup;
648 if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
649 if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
650 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
651 be_print_err(gettext("be_get_grub_vers: failed to "
652 "mount BE (%s)\n"), bt->obe_name);
653 free(*cur_vers);
654 *cur_vers = NULL;
655 ZFS_CLOSE(zhp);
656 goto cleanup;
658 be_mounted = B_TRUE;
660 ZFS_CLOSE(zhp);
663 * Now get the grub version for the BE being activated.
665 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
666 BE_CAP_FILE);
667 ret = get_ver_from_capfile(cap_file, new_vers);
668 if (ret != BE_SUCCESS) {
669 free(*cur_vers);
670 *cur_vers = NULL;
672 if (be_mounted)
673 (void) _be_unmount(bt->obe_name, 0);
675 cleanup:
676 if (pool_mounted) {
677 int iret = BE_SUCCESS;
678 iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
679 if (ret == BE_SUCCESS)
680 ret = iret;
681 free(orig_mntpnt);
682 free(ptmp_mntpnt);
684 ZFS_CLOSE(pool_zhp);
686 free(temp_mntpnt);
687 return (ret);
691 * Function: get_ver_from_capfile
692 * Description: Parses the capability file passed in looking for the VERSION
693 * line. If found the version is returned in vers, if not then
694 * NULL is returned in vers.
696 * Parameters:
697 * file - the path to the capability file we want to parse.
698 * vers - the version string that will be passed back.
699 * Return:
700 * BE_SUCCESS - Success
701 * be_errno_t - Failed to find version
702 * Scope:
703 * Private
705 static int
706 get_ver_from_capfile(char *file, char **vers)
708 FILE *fp = NULL;
709 char line[BUFSIZ];
710 char *last = NULL;
711 int err = BE_SUCCESS;
712 errno = 0;
714 if (!be_has_grub()) {
715 be_print_err(gettext("get_ver_from_capfile: Not supported "
716 "on this architecture\n"));
717 return (BE_ERR_NOTSUP);
721 * Set version string to NULL; the only case this shouldn't be set
722 * to be NULL is when we've actually found a version in the capability
723 * file, which is set below.
725 *vers = NULL;
728 * If the capability file doesn't exist, we're returning success
729 * because on older releases, the capability file did not exist
730 * so this is a valid scenario.
732 if (access(file, F_OK) == 0) {
733 if ((fp = fopen(file, "r")) == NULL) {
734 err = errno;
735 be_print_err(gettext("get_ver_from_capfile: failed to "
736 "open file %s with error %s\n"), file,
737 strerror(err));
738 err = errno_to_be_err(err);
739 return (err);
742 while (fgets(line, BUFSIZ, fp)) {
743 char *tok = strtok_r(line, "=", &last);
745 if (tok == NULL || tok[0] == '#') {
746 continue;
747 } else if (strcmp(tok, "VERSION") == 0) {
748 *vers = strdup(last);
749 break;
752 (void) fclose(fp);
755 return (BE_SUCCESS);
759 * To be able to boot EFI labeled disks, GRUB stage1 needs to be written
760 * into the MBR. We do not do this if we're on disks with a traditional
761 * fdisk partition table only, or if any foreign EFI partitions exist.
762 * In the trivial case of a whole-disk vdev we always write stage1 into
763 * the MBR.
765 static boolean_t
766 be_do_installgrub_mbr(char *diskname, nvlist_t *child)
768 struct uuid allowed_uuids[] = {
769 EFI_UNUSED,
770 EFI_RESV1,
771 EFI_BOOT,
772 EFI_ROOT,
773 EFI_SWAP,
774 EFI_USR,
775 EFI_BACKUP,
776 EFI_RESV2,
777 EFI_VAR,
778 EFI_HOME,
779 EFI_ALTSCTR,
780 EFI_RESERVED,
781 EFI_SYSTEM,
782 EFI_BIOS_BOOT,
783 EFI_SYMC_PUB,
784 EFI_SYMC_CDS
787 uint64_t whole;
788 struct dk_gpt *gpt;
789 struct uuid *u;
790 int fd, npart, i, j;
792 (void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_WHOLE_DISK,
793 &whole);
795 if (whole)
796 return (B_TRUE);
798 if ((fd = open(diskname, O_RDONLY|O_NDELAY)) < 0)
799 return (B_FALSE);
801 if ((npart = efi_alloc_and_read(fd, &gpt)) <= 0)
802 return (B_FALSE);
804 for (i = 0; i != npart; i++) {
805 int match = 0;
807 u = &gpt->efi_parts[i].p_guid;
809 for (j = 0;
810 j != sizeof (allowed_uuids) / sizeof (struct uuid);
811 j++)
812 if (bcmp(u, &allowed_uuids[j],
813 sizeof (struct uuid)) == 0)
814 match++;
816 if (match == 0)
817 return (B_FALSE);
820 return (B_TRUE);
823 static int
824 be_do_installgrub_helper(zpool_handle_t *zphp, nvlist_t *child, char *stage1,
825 char *stage2)
827 char installgrub_cmd[MAXPATHLEN];
828 char be_run_cmd_errbuf[BUFSIZ];
829 char diskname[MAXPATHLEN];
830 char *vname;
831 char *path, *dsk_ptr;
832 char *m_flag = "";
834 if (nvlist_lookup_string(child, ZPOOL_CONFIG_PATH, &path) != 0) {
835 be_print_err(gettext("be_do_installgrub: "
836 "failed to get device path\n"));
837 return (BE_ERR_NODEV);
841 * Modify the vdev path to point to the raw disk.
843 path = strdup(path);
844 if (path == NULL)
845 return (BE_ERR_NOMEM);
847 dsk_ptr = strstr(path, "/dsk/");
848 if (dsk_ptr != NULL) {
849 *dsk_ptr = '\0';
850 dsk_ptr++;
851 } else {
852 dsk_ptr = "";
855 (void) snprintf(diskname, sizeof (diskname), "%s/r%s", path, dsk_ptr);
856 free(path);
858 if (be_do_installgrub_mbr(diskname, child))
859 m_flag = "-m -f";
861 vname = zpool_vdev_name(g_zfs, zphp, child, B_FALSE);
862 if (vname == NULL) {
863 be_print_err(gettext("be_do_installgrub: "
864 "failed to get device name: %s\n"),
865 libzfs_error_description(g_zfs));
866 return (zfs_err_to_be_err(g_zfs));
869 (void) snprintf(installgrub_cmd, sizeof (installgrub_cmd),
870 "%s %s %s %s %s", BE_INSTALL_GRUB, m_flag, stage1, stage2,
871 diskname);
872 if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf, BUFSIZ, NULL, 0)
873 != BE_SUCCESS) {
874 be_print_err(gettext("be_do_installgrub: installgrub "
875 "failed for device %s.\n"), vname);
876 /* Assume localized cmd err output. */
877 be_print_err(gettext(" Command: \"%s\"\n"),
878 installgrub_cmd);
879 be_print_err("%s", be_run_cmd_errbuf);
880 free(vname);
881 return (BE_ERR_BOOTFILE_INST);
883 free(vname);
885 return (BE_SUCCESS);
889 * Function: be_do_installgrub
890 * Description: This function runs installgrub using the grub loader files
891 * from the BE we're activating and installing them on the
892 * pool the BE lives in.
894 * Parameters:
895 * bt - The transaction data for the BE we're activating.
896 * Return:
897 * BE_SUCCESS - Success
898 * be_errno_t - Failure
900 * Scope:
901 * Private
903 static int
904 be_do_installgrub(be_transaction_data_t *bt)
906 zpool_handle_t *zphp = NULL;
907 zfs_handle_t *zhp = NULL;
908 nvlist_t **child, *nv, *config;
909 uint_t c, children = 0;
910 char *tmp_mntpt = NULL;
911 char *pool_mntpnt = NULL;
912 char *ptmp_mntpnt = NULL;
913 char *orig_mntpnt = NULL;
914 FILE *cap_fp = NULL;
915 FILE *zpool_cap_fp = NULL;
916 char line[BUFSIZ];
917 char cap_file[MAXPATHLEN];
918 char zpool_cap_file[MAXPATHLEN];
919 char stage1[MAXPATHLEN];
920 char stage2[MAXPATHLEN];
921 char *vname;
922 int ret = BE_SUCCESS;
923 int err = 0;
924 boolean_t be_mounted = B_FALSE;
925 boolean_t pool_mounted = B_FALSE;
927 if (!be_has_grub()) {
928 be_print_err(gettext("be_do_installgrub: Not supported "
929 "on this architecture\n"));
930 return (BE_ERR_NOTSUP);
933 if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
934 NULL) {
935 be_print_err(gettext("be_do_installgrub: failed to "
936 "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
937 libzfs_error_description(g_zfs));
938 ret = zfs_err_to_be_err(g_zfs);
939 return (ret);
941 if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
942 if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
943 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
944 be_print_err(gettext("be_do_installgrub: failed to "
945 "mount BE (%s)\n"), bt->obe_name);
946 ZFS_CLOSE(zhp);
947 return (ret);
949 be_mounted = B_TRUE;
951 ZFS_CLOSE(zhp);
953 (void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
954 (void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
956 if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
957 be_print_err(gettext("be_do_installgrub: failed to open "
958 "pool (%s): %s\n"), bt->obe_zpool,
959 libzfs_error_description(g_zfs));
960 ret = zfs_err_to_be_err(g_zfs);
961 if (be_mounted)
962 (void) _be_unmount(bt->obe_name, 0);
963 free(tmp_mntpt);
964 return (ret);
967 if ((config = zpool_get_config(zphp, NULL)) == NULL) {
968 be_print_err(gettext("be_do_installgrub: failed to get zpool "
969 "configuration information. %s\n"),
970 libzfs_error_description(g_zfs));
971 ret = zfs_err_to_be_err(g_zfs);
972 goto done;
976 * Get the vdev tree
978 if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
979 be_print_err(gettext("be_do_installgrub: failed to get vdev "
980 "tree: %s\n"), libzfs_error_description(g_zfs));
981 ret = zfs_err_to_be_err(g_zfs);
982 goto done;
985 if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
986 &children) != 0) {
987 be_print_err(gettext("be_do_installgrub: failed to traverse "
988 "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
989 ret = zfs_err_to_be_err(g_zfs);
990 goto done;
992 for (c = 0; c < children; c++) {
993 uint_t i, nchildren = 0;
994 nvlist_t **nvchild;
995 vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE);
996 if (vname == NULL) {
997 be_print_err(gettext(
998 "be_do_installgrub: "
999 "failed to get device name: %s\n"),
1000 libzfs_error_description(g_zfs));
1001 ret = zfs_err_to_be_err(g_zfs);
1002 goto done;
1004 if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
1005 free(vname);
1007 if (nvlist_lookup_nvlist_array(child[c],
1008 ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
1009 be_print_err(gettext("be_do_installgrub: "
1010 "failed to traverse the vdev tree: %s\n"),
1011 libzfs_error_description(g_zfs));
1012 ret = zfs_err_to_be_err(g_zfs);
1013 goto done;
1016 for (i = 0; i < nchildren; i++) {
1017 ret = be_do_installgrub_helper(zphp, nvchild[i],
1018 stage1, stage2);
1019 if (ret != BE_SUCCESS)
1020 goto done;
1022 } else {
1023 free(vname);
1025 ret = be_do_installgrub_helper(zphp, child[c], stage1,
1026 stage2);
1027 if (ret != BE_SUCCESS)
1028 goto done;
1033 * Copy the grub capability file from the BE we're activating into
1034 * the root pool.
1036 (void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
1037 BE_CAP_FILE);
1039 if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
1040 NULL) {
1041 be_print_err(gettext("be_do_installgrub: zfs_open "
1042 "failed: %s\n"), libzfs_error_description(g_zfs));
1043 zpool_close(zphp);
1044 return (zfs_err_to_be_err(g_zfs));
1048 * Check to see if the pool's dataset is mounted. If it isn't we'll
1049 * attempt to mount it.
1051 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt,
1052 &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
1053 be_print_err(gettext("be_do_installgrub: pool dataset "
1054 "(%s) could not be mounted\n"), bt->obe_zpool);
1055 ZFS_CLOSE(zhp);
1056 zpool_close(zphp);
1057 return (ret);
1061 * Get the mountpoint for the root pool dataset.
1063 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1064 be_print_err(gettext("be_do_installgrub: pool "
1065 "dataset (%s) is not mounted. Can't check the grub "
1066 "version from the grub capability file.\n"), bt->obe_zpool);
1067 ret = BE_ERR_NO_MENU;
1068 goto done;
1071 (void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
1072 pool_mntpnt, BE_CAP_FILE);
1074 free(pool_mntpnt);
1075 pool_mntpnt = NULL;
1077 if ((cap_fp = fopen(cap_file, "r")) == NULL) {
1078 err = errno;
1079 be_print_err(gettext("be_do_installgrub: failed to open grub "
1080 "capability file\n"));
1081 ret = errno_to_be_err(err);
1082 goto done;
1084 if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
1085 err = errno;
1086 be_print_err(gettext("be_do_installgrub: failed to open new "
1087 "grub capability file\n"));
1088 ret = errno_to_be_err(err);
1089 (void) fclose(cap_fp);
1090 goto done;
1093 while (fgets(line, BUFSIZ, cap_fp)) {
1094 (void) fputs(line, zpool_cap_fp);
1097 (void) fclose(zpool_cap_fp);
1098 (void) fclose(cap_fp);
1100 done:
1101 if (pool_mounted) {
1102 int iret = 0;
1103 iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1104 if (ret == BE_SUCCESS)
1105 ret = iret;
1106 free(orig_mntpnt);
1107 free(ptmp_mntpnt);
1109 ZFS_CLOSE(zhp);
1110 if (be_mounted)
1111 (void) _be_unmount(bt->obe_name, 0);
1112 zpool_close(zphp);
1113 free(tmp_mntpt);
1114 return (ret);
1118 * Function: be_promote_zone_ds
1119 * Description: This function finds the zones for the BE being activated
1120 * and the active zonepath dataset for each zone. Then each
1121 * active zonepath dataset is promoted.
1123 * Parameters:
1124 * be_name - the name of the global zone BE that we need to
1125 * find the zones for.
1126 * be_root_ds - the root dataset for be_name.
1127 * Return:
1128 * BE_SUCCESS - Success
1129 * be_errno_t - Failure
1131 * Scope:
1132 * Private
1134 static int
1135 be_promote_zone_ds(char *be_name, char *be_root_ds)
1137 char *zone_ds = NULL;
1138 char *temp_mntpt = NULL;
1139 char origin[MAXPATHLEN];
1140 char zoneroot_ds[MAXPATHLEN];
1141 zfs_handle_t *zhp = NULL;
1142 zfs_handle_t *z_zhp = NULL;
1143 zoneList_t zone_list = NULL;
1144 zoneBrandList_t *brands = NULL;
1145 boolean_t be_mounted = B_FALSE;
1146 int zone_index = 0;
1147 int err = BE_SUCCESS;
1150 * Get the supported zone brands so we can pass that
1151 * to z_get_nonglobal_zone_list_by_brand. Currently
1152 * only the ipkg and labeled brand zones are supported
1155 if ((brands = be_get_supported_brandlist()) == NULL) {
1156 be_print_err(gettext("be_promote_zone_ds: no supported "
1157 "brands\n"));
1158 return (BE_SUCCESS);
1161 if ((zhp = zfs_open(g_zfs, be_root_ds,
1162 ZFS_TYPE_FILESYSTEM)) == NULL) {
1163 be_print_err(gettext("be_promote_zone_ds: Failed to open "
1164 "dataset (%s): %s\n"), be_root_ds,
1165 libzfs_error_description(g_zfs));
1166 err = zfs_err_to_be_err(g_zfs);
1167 z_free_brand_list(brands);
1168 return (err);
1171 if (!zfs_is_mounted(zhp, &temp_mntpt)) {
1172 if ((err = _be_mount(be_name, &temp_mntpt,
1173 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1174 be_print_err(gettext("be_promote_zone_ds: failed to "
1175 "mount the BE for zones procesing.\n"));
1176 ZFS_CLOSE(zhp);
1177 z_free_brand_list(brands);
1178 return (err);
1180 be_mounted = B_TRUE;
1184 * Set the zone root to the temp mount point for the BE we just mounted.
1186 z_set_zone_root(temp_mntpt);
1189 * Get all the zones based on the brands we're looking for. If no zones
1190 * are found that we're interested in unmount the BE and move on.
1192 if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
1193 if (be_mounted)
1194 (void) _be_unmount(be_name, 0);
1195 ZFS_CLOSE(zhp);
1196 z_free_brand_list(brands);
1197 free(temp_mntpt);
1198 return (BE_SUCCESS);
1200 for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
1201 != NULL; zone_index++) {
1202 char *zone_path = NULL;
1204 /* Skip zones that aren't at least installed */
1205 if (z_zlist_get_current_state(zone_list, zone_index) <
1206 ZONE_STATE_INSTALLED)
1207 continue;
1209 if (((zone_path =
1210 z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
1211 ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
1212 !be_zone_supported(zone_ds))
1213 continue;
1215 if (be_find_active_zone_root(zhp, zone_ds,
1216 zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
1217 be_print_err(gettext("be_promote_zone_ds: "
1218 "Zone does not have an active root "
1219 "dataset, skipping this zone.\n"));
1220 continue;
1223 if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
1224 ZFS_TYPE_FILESYSTEM)) == NULL) {
1225 be_print_err(gettext("be_promote_zone_ds: "
1226 "Failed to open dataset "
1227 "(%s): %s\n"), zoneroot_ds,
1228 libzfs_error_description(g_zfs));
1229 err = zfs_err_to_be_err(g_zfs);
1230 goto done;
1233 if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
1234 sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
1235 ZFS_CLOSE(z_zhp);
1236 continue;
1240 * We don't need to close the zfs handle at this
1241 * point because the callback funtion
1242 * be_promote_ds_callback() will close it for us.
1244 if (be_promote_ds_callback(z_zhp, NULL) != 0) {
1245 be_print_err(gettext("be_promote_zone_ds: "
1246 "failed to activate the "
1247 "datasets for %s: %s\n"),
1248 zoneroot_ds,
1249 libzfs_error_description(g_zfs));
1250 err = BE_ERR_PROMOTE;
1251 goto done;
1254 done:
1255 if (be_mounted)
1256 (void) _be_unmount(be_name, 0);
1257 ZFS_CLOSE(zhp);
1258 free(temp_mntpt);
1259 z_free_brand_list(brands);
1260 z_free_zone_list(zone_list);
1261 return (err);
1265 * Function: be_promote_ds_callback
1266 * Description: This function is used to promote the datasets for the BE
1267 * being activated as well as the datasets for the zones BE
1268 * being activated.
1270 * Parameters:
1271 * zhp - the zfs handle for zone BE being activated.
1272 * data - not used.
1273 * Return:
1274 * 0 - Success
1275 * be_errno_t - Failure
1277 * Scope:
1278 * Private
1280 static int
1281 /* LINTED */
1282 be_promote_ds_callback(zfs_handle_t *zhp, void *data)
1284 char origin[MAXPATHLEN];
1285 char *sub_dataset = NULL;
1286 int ret = 0;
1288 if (zhp != NULL) {
1289 sub_dataset = strdup(zfs_get_name(zhp));
1290 if (sub_dataset == NULL) {
1291 ret = BE_ERR_NOMEM;
1292 goto done;
1294 } else {
1295 be_print_err(gettext("be_promote_ds_callback: "
1296 "Invalid zfs handle passed into function\n"));
1297 ret = BE_ERR_INVAL;
1298 goto done;
1302 * This loop makes sure that we promote the dataset to the
1303 * top of the tree so that it is no longer a decendent of any
1304 * dataset. The ZFS close and then open is used to make sure that
1305 * the promotion is updated before we move on.
1307 while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
1308 sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
1309 if (zfs_promote(zhp) != 0) {
1310 if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
1311 be_print_err(gettext("be_promote_ds_callback: "
1312 "promote of %s failed: %s\n"),
1313 zfs_get_name(zhp),
1314 libzfs_error_description(g_zfs));
1315 ret = zfs_err_to_be_err(g_zfs);
1316 goto done;
1317 } else {
1319 * If the call to zfs_promote returns the
1320 * error EZFS_EXISTS we've hit a snapshot name
1321 * collision. This means we're probably
1322 * attemping to promote a zone dataset above a
1323 * parent dataset that belongs to another zone
1324 * which this zone was cloned from.
1326 * TODO: If this is a zone dataset at some
1327 * point we should skip this if the zone
1328 * paths for the dataset and the snapshot
1329 * don't match.
1331 be_print_err(gettext("be_promote_ds_callback: "
1332 "promote of %s failed due to snapshot "
1333 "name collision: %s\n"), zfs_get_name(zhp),
1334 libzfs_error_description(g_zfs));
1335 ret = zfs_err_to_be_err(g_zfs);
1336 goto done;
1339 ZFS_CLOSE(zhp);
1340 if ((zhp = zfs_open(g_zfs, sub_dataset,
1341 ZFS_TYPE_FILESYSTEM)) == NULL) {
1342 be_print_err(gettext("be_promote_ds_callback: "
1343 "Failed to open dataset (%s): %s\n"), sub_dataset,
1344 libzfs_error_description(g_zfs));
1345 ret = zfs_err_to_be_err(g_zfs);
1346 goto done;
1350 /* Iterate down this dataset's children and promote them */
1351 ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
1353 done:
1354 free(sub_dataset);
1355 ZFS_CLOSE(zhp);
1356 return (ret);