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