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 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
25 * Copyright 2016 Toomas Soome <tsoome@me.com>
26 * Copyright (c) 2015 by Delphix. All rights reserved.
27 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
28 * Copyright (c) 2018, Joyent, Inc.
29 */
30
31
32 /*
33 * System includes
34 */
35 #include <assert.h>
36 #include <errno.h>
37 #include <libgen.h>
38 #include <libintl.h>
39 #include <libnvpair.h>
40 #include <libzfs.h>
41 #include <libgen.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/vfstab.h>
48 #include <sys/param.h>
49 #include <sys/systeminfo.h>
50 #include <ctype.h>
51 #include <time.h>
52 #include <unistd.h>
53 #include <fcntl.h>
54 #include <deflt.h>
55 #include <wait.h>
56 #include <libdevinfo.h>
57 #include <libgen.h>
58
59 #include <libbe.h>
60 #include <libbe_priv.h>
61 #include <boot_utils.h>
62 #include <ficl.h>
63 #include <ficlplatform/emu.h>
64
65 /* Private function prototypes */
66 static int update_dataset(char *, int, char *, char *, char *);
67 static int _update_vfstab(char *, char *, char *, char *, be_fs_list_data_t *);
68 static int be_open_menu(char *, char *, FILE **, char *, boolean_t);
69 static int be_create_menu(char *, char *, FILE **, char *);
70 static char *be_get_auto_name(char *, char *, boolean_t);
71
72 /*
73 * Global error printing
74 */
75 boolean_t do_print = B_FALSE;
76
77 /*
78 * Private datatypes
79 */
80 typedef struct zone_be_name_cb_data {
81 char *base_be_name;
82 int num;
83 } zone_be_name_cb_data_t;
84
85 /* ******************************************************************** */
86 /* Public Functions */
87 /* ******************************************************************** */
88
89 /*
90 * Callback for ficl to suppress all output from ficl, as we do not
91 * want to confuse user with messages from ficl, and we are only
92 * checking results from function calls.
93 */
94 /*ARGSUSED*/
95 static void
96 ficlSuppressTextOutput(ficlCallback *cb, char *text)
97 {
98 /* This function is intentionally doing nothing. */
99 }
100
101 /*
102 * Function: be_get_boot_args
103 * Description: Returns the fast boot argument string for enumerated BE.
104 * Parameters:
105 * fbarg - pointer to argument string.
106 * entry - index of BE.
107 * Returns:
108 * fast boot argument string.
109 * Scope:
110 * Public
111 */
112 int
113 be_get_boot_args(char **fbarg, int entry)
114 {
115 be_node_list_t *node, *be_nodes = NULL;
116 be_transaction_data_t bt = {0};
117 char *mountpoint = NULL;
118 boolean_t be_mounted = B_FALSE;
119 int ret = BE_SUCCESS;
120 int index;
121 ficlVm *vm;
122
123 *fbarg = NULL;
124 if (!be_zfs_init())
125 return (BE_ERR_INIT);
126
127 /*
128 * need pool name, menu.lst has entries from our pool only
129 */
130 ret = be_find_current_be(&bt);
131 if (ret != BE_SUCCESS) {
132 be_zfs_fini();
133 return (ret);
134 }
135
136 /*
137 * be_get_boot_args() is for loader, fail with grub will trigger
138 * normal boot.
139 */
140 if (be_has_grub()) {
141 ret = BE_ERR_INIT;
142 goto done;
143 }
144
145 ret = _be_list(NULL, &be_nodes, BE_LIST_DEFAULT);
146 if (ret != BE_SUCCESS)
147 goto done;
148
149 /*
150 * iterate through be_nodes,
151 * if entry == -1, stop if be_active_on_boot,
152 * else stop if index == entry.
153 */
154 index = 0;
155 for (node = be_nodes; node != NULL; node = node->be_next_node) {
156 if (strcmp(node->be_rpool, bt.obe_zpool) != 0)
157 continue;
158 if (entry == BE_ENTRY_DEFAULT &&
159 node->be_active_on_boot == B_TRUE)
160 break;
161 if (index == entry)
162 break;
163 index++;
164 }
165 if (node == NULL) {
166 be_free_list(be_nodes);
167 ret = BE_ERR_NOENT;
168 goto done;
169 }
170
171 /* try to mount inactive be */
172 if (node->be_active == B_FALSE) {
173 ret = _be_mount(node->be_node_name, &mountpoint,
174 BE_MOUNT_FLAG_NO_ZONES);
175 if (ret != BE_SUCCESS && ret != BE_ERR_MOUNTED) {
176 be_free_list(be_nodes);
177 goto done;
178 } else
179 be_mounted = B_TRUE;
180 }
181
182 vm = bf_init("", ficlSuppressTextOutput);
183 if (vm != NULL) {
184 /*
185 * zfs MAXNAMELEN is 256, so we need to pick buf large enough
186 * to contain such names.
187 */
188 char buf[MAXNAMELEN * 2];
189 char *kernel_options = NULL;
190 char *kernel = NULL;
191 char *tmp;
192 zpool_handle_t *zph;
193
194 /*
195 * just try to interpret following words. on error
196 * we will be missing kernelname, and will get out.
197 */
198 (void) snprintf(buf, sizeof (buf), "set currdev=zfs:%s:",
199 node->be_root_ds);
200 ret = ficlVmEvaluate(vm, buf);
201 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
202 be_print_err(gettext("be_get_boot_args: error "
203 "interpreting boot config: %d\n"), ret);
204 bf_fini();
205 ret = BE_ERR_NO_MENU;
206 goto cleanup;
207 }
208 (void) snprintf(buf, sizeof (buf),
209 "include /boot/forth/loader.4th");
210 ret = ficlVmEvaluate(vm, buf);
211 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
212 be_print_err(gettext("be_get_boot_args: error "
213 "interpreting boot config: %d\n"), ret);
214 bf_fini();
215 ret = BE_ERR_NO_MENU;
216 goto cleanup;
217 }
218 (void) snprintf(buf, sizeof (buf), "start");
219 ret = ficlVmEvaluate(vm, buf);
220 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
221 be_print_err(gettext("be_get_boot_args: error "
222 "interpreting boot config: %d\n"), ret);
223 bf_fini();
224 ret = BE_ERR_NO_MENU;
225 goto cleanup;
226 }
227 (void) snprintf(buf, sizeof (buf), "boot");
228 ret = ficlVmEvaluate(vm, buf);
229 bf_fini();
230 if (ret != FICL_VM_STATUS_OUT_OF_TEXT) {
231 be_print_err(gettext("be_get_boot_args: error "
232 "interpreting boot config: %d\n"), ret);
233 ret = BE_ERR_NO_MENU;
234 goto cleanup;
235 }
236
237 kernel_options = getenv("boot-args");
238 kernel = getenv("kernelname");
239
240 if (kernel == NULL) {
241 be_print_err(gettext("be_get_boot_args: no kernel\n"));
242 ret = BE_ERR_NOENT;
243 goto cleanup;
244 }
245
246 if ((zph = zpool_open(g_zfs, node->be_rpool)) == NULL) {
247 be_print_err(gettext("be_get_boot_args: failed to "
248 "open root pool (%s): %s\n"), node->be_rpool,
249 libzfs_error_description(g_zfs));
250 ret = zfs_err_to_be_err(g_zfs);
251 goto cleanup;
252 }
253 ret = zpool_get_physpath(zph, buf, sizeof (buf));
254 zpool_close(zph);
255 if (ret != 0) {
256 be_print_err(gettext("be_get_boot_args: failed to "
257 "get physpath\n"));
258 goto cleanup;
259 }
260
261 /* zpool_get_physpath() can return space separated list */
262 tmp = buf;
263 tmp = strsep(&tmp, " ");
264
265 if (kernel_options == NULL || *kernel_options == '\0')
266 (void) asprintf(fbarg, "/ %s "
267 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
268 node->be_root_ds, tmp);
269 else
270 (void) asprintf(fbarg, "/ %s %s "
271 "-B zfs-bootfs=%s,bootpath=\"%s\"\n", kernel,
272 kernel_options, node->be_root_ds, tmp);
273
274 if (*fbarg == NULL)
275 ret = BE_ERR_NOMEM;
276 else
277 ret = 0;
278 } else
279 ret = BE_ERR_NOMEM;
280 cleanup:
281 if (be_mounted == B_TRUE)
282 (void) _be_unmount(node->be_node_name, BE_UNMOUNT_FLAG_FORCE);
283 be_free_list(be_nodes);
284 done:
285 free(mountpoint);
286 free(bt.obe_name);
287 free(bt.obe_root_ds);
288 free(bt.obe_zpool);
289 free(bt.obe_snap_name);
290 free(bt.obe_altroot);
291 be_zfs_fini();
292 return (ret);
293 }
294
295 /*
296 * Function: be_max_avail
297 * Description: Returns the available size for the zfs dataset passed in.
298 * Parameters:
299 * dataset - The dataset we want to get the available space for.
300 * ret - The available size will be returned in this.
301 * Returns:
302 * The error returned by the zfs get property function.
303 * Scope:
304 * Public
305 */
306 int
307 be_max_avail(char *dataset, uint64_t *ret)
308 {
309 zfs_handle_t *zhp;
310 int err = 0;
311
312 /* Initialize libzfs handle */
313 if (!be_zfs_init())
314 return (BE_ERR_INIT);
315
316 zhp = zfs_open(g_zfs, dataset, ZFS_TYPE_DATASET);
317 if (zhp == NULL) {
318 /*
319 * The zfs_open failed return an error
320 */
321 err = zfs_err_to_be_err(g_zfs);
322 } else {
323 err = be_maxsize_avail(zhp, ret);
324 }
325 ZFS_CLOSE(zhp);
326 be_zfs_fini();
327 return (err);
328 }
329
330 /*
331 * Function: libbe_print_errors
332 * Description: Turns on/off error output for the library.
333 * Parameter:
334 * set_do_print - Boolean that turns library error
335 * printing on or off.
336 * Returns:
337 * None
338 * Scope:
339 * Public;
340 */
341 void
342 libbe_print_errors(boolean_t set_do_print)
343 {
344 do_print = set_do_print;
345 }
346
347 /* ******************************************************************** */
348 /* Semi-Private Functions */
349 /* ******************************************************************** */
350
351 /*
352 * Function: be_zfs_init
353 * Description: Initializes the libary global libzfs handle.
354 * Parameters:
355 * None
356 * Returns:
357 * B_TRUE - Success
358 * B_FALSE - Failure
359 * Scope:
360 * Semi-private (library wide use only)
361 */
362 boolean_t
363 be_zfs_init(void)
364 {
365 be_zfs_fini();
366
367 if ((g_zfs = libzfs_init()) == NULL) {
368 be_print_err(gettext("be_zfs_init: failed to initialize ZFS "
369 "library\n"));
370 return (B_FALSE);
371 }
372
373 return (B_TRUE);
374 }
375
376 /*
377 * Function: be_zfs_fini
378 * Description: Closes the library global libzfs handle if it currently open.
379 * Parameter:
380 * None
381 * Returns:
382 * None
383 * Scope:
384 * Semi-private (library wide use only)
385 */
386 void
387 be_zfs_fini(void)
388 {
389 if (g_zfs)
390 libzfs_fini(g_zfs);
391
392 g_zfs = NULL;
393 }
394
395 /*
396 * Function: be_get_defaults
397 * Description: Open defaults and gets be default paramets
398 * Parameters:
399 * defaults - be defaults struct
400 * Returns:
401 * None
402 * Scope:
403 * Semi-private (library wide use only)
404 */
405 void
406 be_get_defaults(struct be_defaults *defaults)
407 {
408 void *defp;
409
410 defaults->be_deflt_grub = B_FALSE;
411 defaults->be_deflt_rpool_container = B_FALSE;
412 defaults->be_deflt_bename_starts_with[0] = '\0';
413
414 if ((defp = defopen_r(BE_DEFAULTS)) != NULL) {
415 const char *res = defread_r(BE_DFLT_BENAME_STARTS, defp);
416 if (res != NULL && res[0] != '\0') {
417 (void) strlcpy(defaults->be_deflt_bename_starts_with,
418 res, ZFS_MAX_DATASET_NAME_LEN);
419 defaults->be_deflt_rpool_container = B_TRUE;
420 }
421 if (be_is_isa("i386")) {
422 res = defread_r(BE_DFLT_BE_HAS_GRUB, defp);
423 if (res != NULL && res[0] != '\0') {
424 if (strcasecmp(res, "true") == 0)
425 defaults->be_deflt_grub = B_TRUE;
426 }
427 }
428 defclose_r(defp);
429 }
430 }
431
432 /*
433 * Function: be_make_root_ds
434 * Description: Generate string for BE's root dataset given the pool
435 * it lives in and the BE name.
436 * Parameters:
437 * zpool - pointer zpool name.
438 * be_name - pointer to BE name.
439 * be_root_ds - pointer to buffer to return BE root dataset in.
440 * be_root_ds_size - size of be_root_ds
441 * Returns:
442 * None
443 * Scope:
444 * Semi-private (library wide use only)
445 */
446 void
447 be_make_root_ds(const char *zpool, const char *be_name, char *be_root_ds,
448 int be_root_ds_size)
449 {
450 struct be_defaults be_defaults;
451 be_get_defaults(&be_defaults);
452 char *root_ds = NULL;
453
454 if (getzoneid() == GLOBAL_ZONEID) {
455 if (be_defaults.be_deflt_rpool_container) {
456 (void) snprintf(be_root_ds, be_root_ds_size,
457 "%s/%s", zpool, be_name);
458 } else {
459 (void) snprintf(be_root_ds, be_root_ds_size,
460 "%s/%s/%s", zpool, BE_CONTAINER_DS_NAME, be_name);
461 }
462 } else {
463 /*
464 * In non-global zone we can use path from mounted root dataset
465 * to generate BE's root dataset string.
466 */
467 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
468 (void) snprintf(be_root_ds, be_root_ds_size, "%s/%s",
469 dirname(root_ds), be_name);
470 } else {
471 be_print_err(gettext("be_make_root_ds: zone root "
472 "dataset is not mounted\n"));
473 return;
474 }
475 }
476 }
477
478 /*
479 * Function: be_make_container_ds
480 * Description: Generate string for the BE container dataset given a pool name.
481 * Parameters:
482 * zpool - pointer zpool name.
483 * container_ds - pointer to buffer to return BE container
484 * dataset in.
485 * container_ds_size - size of container_ds
486 * Returns:
487 * None
488 * Scope:
489 * Semi-private (library wide use only)
490 */
491 void
492 be_make_container_ds(const char *zpool, char *container_ds,
493 int container_ds_size)
494 {
495 struct be_defaults be_defaults;
496 be_get_defaults(&be_defaults);
497 char *root_ds = NULL;
498
499 if (getzoneid() == GLOBAL_ZONEID) {
500 if (be_defaults.be_deflt_rpool_container) {
501 (void) snprintf(container_ds, container_ds_size,
502 "%s", zpool);
503 } else {
504 (void) snprintf(container_ds, container_ds_size,
505 "%s/%s", zpool, BE_CONTAINER_DS_NAME);
506 }
507 } else {
508 if ((root_ds = be_get_ds_from_dir("/")) != NULL) {
509 (void) strlcpy(container_ds, dirname(root_ds),
510 container_ds_size);
511 } else {
512 be_print_err(gettext("be_make_container_ds: zone root "
513 "dataset is not mounted\n"));
514 return;
515 }
516 }
517 }
518
519 /*
520 * Function: be_make_name_from_ds
521 * Description: This function takes a dataset name and strips off the
522 * BE container dataset portion from the beginning. The
523 * returned name is allocated in heap storage, so the caller
524 * is responsible for freeing it.
525 * Parameters:
526 * dataset - dataset to get name from.
527 * rc_loc - dataset underwhich the root container dataset lives.
528 * Returns:
529 * name of dataset relative to BE container dataset.
530 * NULL if dataset is not under a BE root dataset.
531 * Scope:
532 * Semi-primate (library wide use only)
533 */
534 char *
535 be_make_name_from_ds(const char *dataset, char *rc_loc)
536 {
537 char ds[ZFS_MAX_DATASET_NAME_LEN];
538 char *tok = NULL;
539 char *name = NULL;
540 struct be_defaults be_defaults;
541 int rlen = strlen(rc_loc);
542
543 be_get_defaults(&be_defaults);
544
545 /*
546 * First token is the location of where the root container dataset
547 * lives; it must match rc_loc.
548 */
549 if (strncmp(dataset, rc_loc, rlen) == 0 && dataset[rlen] == '/')
550 (void) strlcpy(ds, dataset + rlen + 1, sizeof (ds));
551 else
552 return (NULL);
553
554 if (be_defaults.be_deflt_rpool_container) {
555 if ((name = strdup(ds)) == NULL) {
556 be_print_err(gettext("be_make_name_from_ds: "
557 "memory allocation failed\n"));
558 return (NULL);
559 }
560 } else {
561 /* Second token must be BE container dataset name */
562 if ((tok = strtok(ds, "/")) == NULL ||
563 strcmp(tok, BE_CONTAINER_DS_NAME) != 0)
564 return (NULL);
565
566 /* Return the remaining token if one exists */
567 if ((tok = strtok(NULL, "")) == NULL)
568 return (NULL);
569
570 if ((name = strdup(tok)) == NULL) {
571 be_print_err(gettext("be_make_name_from_ds: "
572 "memory allocation failed\n"));
573 return (NULL);
574 }
575 }
576
577 return (name);
578 }
579
580 /*
581 * Function: be_maxsize_avail
582 * Description: Returns the available size for the zfs handle passed in.
583 * Parameters:
584 * zhp - A pointer to the open zfs handle.
585 * ret - The available size will be returned in this.
586 * Returns:
587 * The error returned by the zfs get property function.
588 * Scope:
589 * Semi-private (library wide use only)
590 */
591 int
592 be_maxsize_avail(zfs_handle_t *zhp, uint64_t *ret)
593 {
594 return ((*ret = zfs_prop_get_int(zhp, ZFS_PROP_AVAILABLE)));
595 }
596
597 /*
598 * Function: be_append_menu
599 * Description: Appends an entry for a BE into the menu.lst.
600 * Parameters:
601 * be_name - pointer to name of BE to add boot menu entry for.
602 * be_root_pool - pointer to name of pool BE lives in.
603 * boot_pool - Used if the pool containing the grub menu is
604 * different than the one contaiing the BE. This
605 * will normally be NULL.
606 * be_orig_root_ds - The root dataset for the BE. This is
607 * used to check to see if an entry already exists
608 * for this BE.
609 * description - pointer to description of BE to be added in
610 * the title line for this BEs entry.
611 * Returns:
612 * BE_SUCCESS - Success
613 * be_errno_t - Failure
614 * Scope:
615 * Semi-private (library wide use only)
616 */
617 int
618 be_append_menu(char *be_name, char *be_root_pool, char *boot_pool,
619 char *be_orig_root_ds, char *description)
620 {
621 zfs_handle_t *zhp = NULL;
622 char menu_file[MAXPATHLEN];
623 char be_root_ds[MAXPATHLEN];
624 char line[BUFSIZ];
625 char temp_line[BUFSIZ];
626 char title[MAXPATHLEN];
627 char *entries[BUFSIZ];
628 char *tmp_entries[BUFSIZ];
629 char *pool_mntpnt = NULL;
630 char *ptmp_mntpnt = NULL;
631 char *orig_mntpnt = NULL;
632 boolean_t found_be = B_FALSE;
633 boolean_t found_orig_be = B_FALSE;
634 boolean_t found_title = B_FALSE;
635 boolean_t pool_mounted = B_FALSE;
636 boolean_t collect_lines = B_FALSE;
637 FILE *menu_fp = NULL;
638 int err = 0, ret = BE_SUCCESS;
639 int i, num_tmp_lines = 0, num_lines = 0;
640
641 if (be_name == NULL || be_root_pool == NULL)
642 return (BE_ERR_INVAL);
643
644 if (boot_pool == NULL)
645 boot_pool = be_root_pool;
646
647 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
648 be_print_err(gettext("be_append_menu: failed to open "
649 "pool dataset for %s: %s\n"), be_root_pool,
650 libzfs_error_description(g_zfs));
651 return (zfs_err_to_be_err(g_zfs));
652 }
653
654 /*
655 * Check to see if the pool's dataset is mounted. If it isn't we'll
656 * attempt to mount it.
657 */
658 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
659 &pool_mounted)) != BE_SUCCESS) {
660 be_print_err(gettext("be_append_menu: pool dataset "
661 "(%s) could not be mounted\n"), be_root_pool);
662 ZFS_CLOSE(zhp);
663 return (ret);
664 }
665
666 /*
667 * Get the mountpoint for the root pool dataset.
668 */
669 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
670 be_print_err(gettext("be_append_menu: pool "
671 "dataset (%s) is not mounted. Can't set "
672 "the default BE in the grub menu.\n"), be_root_pool);
673 ret = BE_ERR_NO_MENU;
674 goto cleanup;
675 }
676
677 /*
678 * Check to see if this system supports grub
679 */
680 if (be_has_grub()) {
681 (void) snprintf(menu_file, sizeof (menu_file),
682 "%s%s", pool_mntpnt, BE_GRUB_MENU);
683 } else {
684 (void) snprintf(menu_file, sizeof (menu_file),
685 "%s%s", pool_mntpnt, BE_SPARC_MENU);
686 }
687
688 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
689
690 /*
691 * Iterate through menu first to make sure the BE doesn't already
692 * have an entry in the menu.
693 *
694 * Additionally while iterating through the menu, if we have an
695 * original root dataset for a BE we're cloning from, we need to keep
696 * track of that BE's menu entry. We will then use the lines from
697 * that entry to create the entry for the new BE.
698 */
699 if ((ret = be_open_menu(be_root_pool, menu_file,
700 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
701 goto cleanup;
702 } else if (menu_fp == NULL) {
703 ret = BE_ERR_NO_MENU;
704 goto cleanup;
705 }
706
707 free(pool_mntpnt);
708 pool_mntpnt = NULL;
709
710 while (fgets(line, BUFSIZ, menu_fp)) {
711 char *tok = NULL;
712
713 (void) strlcpy(temp_line, line, BUFSIZ);
714 tok = strtok(line, BE_WHITE_SPACE);
715
716 if (tok == NULL || tok[0] == '#') {
717 continue;
718 } else if (strcmp(tok, "title") == 0) {
719 collect_lines = B_FALSE;
720 if ((tok = strtok(NULL, "\n")) == NULL)
721 (void) strlcpy(title, "", sizeof (title));
722 else
723 (void) strlcpy(title, tok, sizeof (title));
724 found_title = B_TRUE;
725
726 if (num_tmp_lines != 0) {
727 for (i = 0; i < num_tmp_lines; i++) {
728 free(tmp_entries[i]);
729 tmp_entries[i] = NULL;
730 }
731 num_tmp_lines = 0;
732 }
733 } else if (strcmp(tok, "bootfs") == 0) {
734 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
735 found_title = B_FALSE;
736 if (bootfs == NULL)
737 continue;
738
739 if (strcmp(bootfs, be_root_ds) == 0) {
740 found_be = B_TRUE;
741 break;
742 }
743
744 if (be_orig_root_ds != NULL &&
745 strcmp(bootfs, be_orig_root_ds) == 0 &&
746 !found_orig_be) {
747 char str[BUFSIZ];
748 found_orig_be = B_TRUE;
749 num_lines = 0;
750 /*
751 * Store the new title line
752 */
753 (void) snprintf(str, BUFSIZ, "title %s\n",
754 description ? description : be_name);
755 entries[num_lines] = strdup(str);
756 num_lines++;
757 /*
758 * If there are any lines between the title
759 * and the bootfs line store these. Also
760 * free the temporary lines.
761 */
762 for (i = 0; i < num_tmp_lines; i++) {
763 entries[num_lines] = tmp_entries[i];
764 tmp_entries[i] = NULL;
765 num_lines++;
766 }
767 num_tmp_lines = 0;
768 /*
769 * Store the new bootfs line.
770 */
771 (void) snprintf(str, BUFSIZ, "bootfs %s\n",
772 be_root_ds);
773 entries[num_lines] = strdup(str);
774 num_lines++;
775 collect_lines = B_TRUE;
776 }
777 } else if (found_orig_be && collect_lines) {
778 /*
779 * get the rest of the lines for the original BE and
780 * store them.
781 */
782 if (strstr(line, BE_GRUB_COMMENT) != NULL ||
783 strstr(line, "BOOTADM") != NULL)
784 continue;
785 if (strcmp(tok, "splashimage") == 0) {
786 entries[num_lines] =
787 strdup("splashimage "
788 "/boot/splashimage.xpm\n");
789 } else {
790 entries[num_lines] = strdup(temp_line);
791 }
792 num_lines++;
793 } else if (found_title && !found_orig_be) {
794 tmp_entries[num_tmp_lines] = strdup(temp_line);
795 num_tmp_lines++;
796 }
797 }
798
799 (void) fclose(menu_fp);
800
801 if (found_be) {
802 /*
803 * If an entry for this BE was already in the menu, then if
804 * that entry's title matches what we would have put in
805 * return success. Otherwise return failure.
806 */
807 char *new_title = description ? description : be_name;
808
809 if (strcmp(title, new_title) == 0) {
810 ret = BE_SUCCESS;
811 goto cleanup;
812 } else {
813 if (be_remove_menu(be_name, be_root_pool,
814 boot_pool) != BE_SUCCESS) {
815 be_print_err(gettext("be_append_menu: "
816 "Failed to remove existing unusable "
817 "entry '%s' in boot menu.\n"), be_name);
818 ret = BE_ERR_BE_EXISTS;
819 goto cleanup;
820 }
821 }
822 }
823
824 /* Append BE entry to the end of the file */
825 menu_fp = fopen(menu_file, "a+");
826 err = errno;
827 if (menu_fp == NULL) {
828 be_print_err(gettext("be_append_menu: failed "
829 "to open menu.lst file %s\n"), menu_file);
830 ret = errno_to_be_err(err);
831 goto cleanup;
832 }
833
834 if (found_orig_be) {
835 /*
836 * write out all the stored lines
837 */
838 for (i = 0; i < num_lines; i++) {
839 (void) fprintf(menu_fp, "%s", entries[i]);
840 free(entries[i]);
841 }
842 num_lines = 0;
843
844 /*
845 * Check to see if this system supports grub
846 */
847 if (be_has_grub())
848 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
849 ret = BE_SUCCESS;
850 } else {
851 (void) fprintf(menu_fp, "title %s\n",
852 description ? description : be_name);
853 (void) fprintf(menu_fp, "bootfs %s\n", be_root_ds);
854
855 /*
856 * Check to see if this system supports grub
857 */
858 if (be_has_grub()) {
859 (void) fprintf(menu_fp, "kernel$ "
860 "/platform/i86pc/kernel/$ISADIR/unix -B "
861 "$ZFS-BOOTFS\n");
862 (void) fprintf(menu_fp, "module$ "
863 "/platform/i86pc/$ISADIR/boot_archive\n");
864 (void) fprintf(menu_fp, "%s\n", BE_GRUB_COMMENT);
865 }
866 ret = BE_SUCCESS;
867 }
868 (void) fclose(menu_fp);
869 cleanup:
870 if (pool_mounted) {
871 int err = BE_SUCCESS;
872 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
873 if (ret == BE_SUCCESS)
874 ret = err;
875 free(orig_mntpnt);
876 free(ptmp_mntpnt);
877 }
878 ZFS_CLOSE(zhp);
879 if (num_tmp_lines > 0) {
880 for (i = 0; i < num_tmp_lines; i++) {
881 free(tmp_entries[i]);
882 tmp_entries[i] = NULL;
883 }
884 }
885 if (num_lines > 0) {
886 for (i = 0; i < num_lines; i++) {
887 free(entries[i]);
888 entries[i] = NULL;
889 }
890 }
891 return (ret);
892 }
893
894 /*
895 * Function: be_remove_menu
896 * Description: Removes a BE's entry from a menu.lst file.
897 * Parameters:
898 * be_name - the name of BE whose entry is to be removed from
899 * the menu.lst file.
900 * be_root_pool - the pool that be_name lives in.
901 * boot_pool - the pool where the BE is, if different than
902 * the pool containing the boot menu. If this is
903 * NULL it will be set to be_root_pool.
904 * Returns:
905 * BE_SUCCESS - Success
906 * be_errno_t - Failure
907 * Scope:
908 * Semi-private (library wide use only)
909 */
910 int
911 be_remove_menu(char *be_name, char *be_root_pool, char *boot_pool)
912 {
913 zfs_handle_t *zhp = NULL;
914 char be_root_ds[MAXPATHLEN];
915 char **buffer = NULL;
916 char menu_buf[BUFSIZ];
917 char menu[MAXPATHLEN];
918 char *pool_mntpnt = NULL;
919 char *ptmp_mntpnt = NULL;
920 char *orig_mntpnt = NULL;
921 char *tmp_menu = NULL;
922 FILE *menu_fp = NULL;
923 FILE *tmp_menu_fp = NULL;
924 struct stat sb;
925 int ret = BE_SUCCESS;
926 int i;
927 int fd;
928 int err = 0;
929 int nlines = 0;
930 int default_entry = 0;
931 int entry_cnt = 0;
932 int entry_del = 0;
933 int num_entry_del = 0;
934 int tmp_menu_len = 0;
935 boolean_t write = B_TRUE;
936 boolean_t do_buffer = B_FALSE;
937 boolean_t pool_mounted = B_FALSE;
938
939 if (boot_pool == NULL)
940 boot_pool = be_root_pool;
941
942 /* Get name of BE's root dataset */
943 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
944
945 /* Get handle to pool dataset */
946 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
947 be_print_err(gettext("be_remove_menu: "
948 "failed to open pool dataset for %s: %s"),
949 be_root_pool, libzfs_error_description(g_zfs));
950 return (zfs_err_to_be_err(g_zfs));
951 }
952
953 /*
954 * Check to see if the pool's dataset is mounted. If it isn't we'll
955 * attempt to mount it.
956 */
957 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
958 &pool_mounted)) != BE_SUCCESS) {
959 be_print_err(gettext("be_remove_menu: pool dataset "
960 "(%s) could not be mounted\n"), be_root_pool);
961 ZFS_CLOSE(zhp);
962 return (ret);
963 }
964
965 /*
966 * Get the mountpoint for the root pool dataset.
967 */
968 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
969 be_print_err(gettext("be_remove_menu: pool "
970 "dataset (%s) is not mounted. Can't set "
971 "the default BE in the grub menu.\n"), be_root_pool);
972 ret = BE_ERR_NO_MENU;
973 goto cleanup;
974 }
975
976 /* Get path to boot menu */
977 (void) strlcpy(menu, pool_mntpnt, sizeof (menu));
978
979 /*
980 * Check to see if this system supports grub
981 */
982 if (be_has_grub())
983 (void) strlcat(menu, BE_GRUB_MENU, sizeof (menu));
984 else
985 (void) strlcat(menu, BE_SPARC_MENU, sizeof (menu));
986
987 /* Get handle to boot menu file */
988 if ((ret = be_open_menu(be_root_pool, menu, &menu_fp, "r",
989 B_TRUE)) != BE_SUCCESS) {
990 goto cleanup;
991 } else if (menu_fp == NULL) {
992 ret = BE_ERR_NO_MENU;
993 goto cleanup;
994 }
995
996 free(pool_mntpnt);
997 pool_mntpnt = NULL;
998
999 /* Grab the stats of the original menu file */
1000 if (stat(menu, &sb) != 0) {
1001 err = errno;
1002 be_print_err(gettext("be_remove_menu: "
1003 "failed to stat file %s: %s\n"), menu, strerror(err));
1004 ret = errno_to_be_err(err);
1005 goto cleanup;
1006 }
1007
1008 /* Create a tmp file for the modified menu.lst */
1009 tmp_menu_len = strlen(menu) + 7;
1010 if ((tmp_menu = (char *)malloc(tmp_menu_len)) == NULL) {
1011 be_print_err(gettext("be_remove_menu: malloc failed\n"));
1012 ret = BE_ERR_NOMEM;
1013 goto cleanup;
1014 }
1015 (void) memset(tmp_menu, 0, tmp_menu_len);
1016 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1017 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1018 if ((fd = mkstemp(tmp_menu)) == -1) {
1019 err = errno;
1020 be_print_err(gettext("be_remove_menu: mkstemp failed\n"));
1021 ret = errno_to_be_err(err);
1022 free(tmp_menu);
1023 tmp_menu = NULL;
1024 goto cleanup;
1025 }
1026 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1027 err = errno;
1028 be_print_err(gettext("be_remove_menu: "
1029 "could not open tmp file for write: %s\n"), strerror(err));
1030 (void) close(fd);
1031 ret = errno_to_be_err(err);
1032 goto cleanup;
1033 }
1034
1035 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1036 char tline [BUFSIZ];
1037 char *tok = NULL;
1038
1039 (void) strlcpy(tline, menu_buf, sizeof (tline));
1040
1041 /* Tokenize line */
1042 tok = strtok(tline, BE_WHITE_SPACE);
1043
1044 if (tok == NULL || tok[0] == '#') {
1045 /* Found empty line or comment line */
1046 if (do_buffer) {
1047 /* Buffer this line */
1048 if ((buffer = (char **)realloc(buffer,
1049 sizeof (char *)*(nlines + 1))) == NULL) {
1050 ret = BE_ERR_NOMEM;
1051 goto cleanup;
1052 }
1053 if ((buffer[nlines++] = strdup(menu_buf))
1054 == NULL) {
1055 ret = BE_ERR_NOMEM;
1056 goto cleanup;
1057 }
1058
1059 } else if (write || strncmp(menu_buf, BE_GRUB_COMMENT,
1060 strlen(BE_GRUB_COMMENT)) != 0) {
1061 /* Write this line out */
1062 (void) fputs(menu_buf, tmp_menu_fp);
1063 }
1064 } else if (strcmp(tok, "default") == 0) {
1065 /*
1066 * Record what 'default' is set to because we might
1067 * need to adjust this upon deleting an entry.
1068 */
1069 tok = strtok(NULL, BE_WHITE_SPACE);
1070
1071 if (tok != NULL) {
1072 default_entry = atoi(tok);
1073 }
1074
1075 (void) fputs(menu_buf, tmp_menu_fp);
1076 } else if (strcmp(tok, "title") == 0) {
1077 /*
1078 * If we've reached a 'title' line and do_buffer is
1079 * is true, that means we've just buffered an entire
1080 * entry without finding a 'bootfs' directive. We
1081 * need to write that entry out and keep searching.
1082 */
1083 if (do_buffer) {
1084 for (i = 0; i < nlines; i++) {
1085 (void) fputs(buffer[i], tmp_menu_fp);
1086 free(buffer[i]);
1087 }
1088 free(buffer);
1089 buffer = NULL;
1090 nlines = 0;
1091 }
1092
1093 /*
1094 * Turn writing off and buffering on, and increment
1095 * our entry counter.
1096 */
1097 write = B_FALSE;
1098 do_buffer = B_TRUE;
1099 entry_cnt++;
1100
1101 /* Buffer this 'title' line */
1102 if ((buffer = (char **)realloc(buffer,
1103 sizeof (char *)*(nlines + 1))) == NULL) {
1104 ret = BE_ERR_NOMEM;
1105 goto cleanup;
1106 }
1107 if ((buffer[nlines++] = strdup(menu_buf)) == NULL) {
1108 ret = BE_ERR_NOMEM;
1109 goto cleanup;
1110 }
1111
1112 } else if (strcmp(tok, "bootfs") == 0) {
1113 char *bootfs = NULL;
1114
1115 /*
1116 * Found a 'bootfs' line. See if it matches the
1117 * BE we're looking for.
1118 */
1119 if ((bootfs = strtok(NULL, BE_WHITE_SPACE)) == NULL ||
1120 strcmp(bootfs, be_root_ds) != 0) {
1121 /*
1122 * Either there's nothing after the 'bootfs'
1123 * or this is not the BE we're looking for,
1124 * write out the line(s) we've buffered since
1125 * finding the title.
1126 */
1127 for (i = 0; i < nlines; i++) {
1128 (void) fputs(buffer[i], tmp_menu_fp);
1129 free(buffer[i]);
1130 }
1131 free(buffer);
1132 buffer = NULL;
1133 nlines = 0;
1134
1135 /*
1136 * Turn writing back on, and turn off buffering
1137 * since this isn't the entry we're looking
1138 * for.
1139 */
1140 write = B_TRUE;
1141 do_buffer = B_FALSE;
1142
1143 /* Write this 'bootfs' line out. */
1144 (void) fputs(menu_buf, tmp_menu_fp);
1145 } else {
1146 /*
1147 * Found the entry we're looking for.
1148 * Record its entry number, increment the
1149 * number of entries we've deleted, and turn
1150 * writing off. Also, throw away the lines
1151 * we've buffered for this entry so far, we
1152 * don't need them.
1153 */
1154 entry_del = entry_cnt - 1;
1155 num_entry_del++;
1156 write = B_FALSE;
1157 do_buffer = B_FALSE;
1158
1159 for (i = 0; i < nlines; i++) {
1160 free(buffer[i]);
1161 }
1162 free(buffer);
1163 buffer = NULL;
1164 nlines = 0;
1165 }
1166 } else {
1167 if (do_buffer) {
1168 /* Buffer this line */
1169 if ((buffer = (char **)realloc(buffer,
1170 sizeof (char *)*(nlines + 1))) == NULL) {
1171 ret = BE_ERR_NOMEM;
1172 goto cleanup;
1173 }
1174 if ((buffer[nlines++] = strdup(menu_buf))
1175 == NULL) {
1176 ret = BE_ERR_NOMEM;
1177 goto cleanup;
1178 }
1179 } else if (write) {
1180 /* Write this line out */
1181 (void) fputs(menu_buf, tmp_menu_fp);
1182 }
1183 }
1184 }
1185
1186 (void) fclose(menu_fp);
1187 menu_fp = NULL;
1188 (void) fclose(tmp_menu_fp);
1189 tmp_menu_fp = NULL;
1190
1191 /* Copy the modified menu.lst into place */
1192 if (rename(tmp_menu, menu) != 0) {
1193 err = errno;
1194 be_print_err(gettext("be_remove_menu: "
1195 "failed to rename file %s to %s: %s\n"),
1196 tmp_menu, menu, strerror(err));
1197 ret = errno_to_be_err(err);
1198 goto cleanup;
1199 }
1200 free(tmp_menu);
1201 tmp_menu = NULL;
1202
1203 /*
1204 * If we've removed an entry, see if we need to
1205 * adjust the default value in the menu.lst. If the
1206 * entry we've deleted comes before the default entry
1207 * we need to adjust the default value accordingly.
1208 *
1209 * be_has_grub is used here to check to see if this system
1210 * supports grub.
1211 */
1212 if (be_has_grub() && num_entry_del > 0) {
1213 if (entry_del <= default_entry) {
1214 default_entry = default_entry - num_entry_del;
1215 if (default_entry < 0)
1216 default_entry = 0;
1217
1218 /*
1219 * Adjust the default value by rewriting the
1220 * menu.lst file. This may be overkill, but to
1221 * preserve the location of the 'default' entry
1222 * in the file, we need to do this.
1223 */
1224
1225 /* Get handle to boot menu file */
1226 if ((menu_fp = fopen(menu, "r")) == NULL) {
1227 err = errno;
1228 be_print_err(gettext("be_remove_menu: "
1229 "failed to open menu.lst (%s): %s\n"),
1230 menu, strerror(err));
1231 ret = errno_to_be_err(err);
1232 goto cleanup;
1233 }
1234
1235 /* Create a tmp file for the modified menu.lst */
1236 tmp_menu_len = strlen(menu) + 7;
1237 if ((tmp_menu = (char *)malloc(tmp_menu_len))
1238 == NULL) {
1239 be_print_err(gettext("be_remove_menu: "
1240 "malloc failed\n"));
1241 ret = BE_ERR_NOMEM;
1242 goto cleanup;
1243 }
1244 (void) memset(tmp_menu, 0, tmp_menu_len);
1245 (void) strlcpy(tmp_menu, menu, tmp_menu_len);
1246 (void) strlcat(tmp_menu, "XXXXXX", tmp_menu_len);
1247 if ((fd = mkstemp(tmp_menu)) == -1) {
1248 err = errno;
1249 be_print_err(gettext("be_remove_menu: "
1250 "mkstemp failed: %s\n"), strerror(err));
1251 ret = errno_to_be_err(err);
1252 free(tmp_menu);
1253 tmp_menu = NULL;
1254 goto cleanup;
1255 }
1256 if ((tmp_menu_fp = fdopen(fd, "w")) == NULL) {
1257 err = errno;
1258 be_print_err(gettext("be_remove_menu: "
1259 "could not open tmp file for write: %s\n"),
1260 strerror(err));
1261 (void) close(fd);
1262 ret = errno_to_be_err(err);
1263 goto cleanup;
1264 }
1265
1266 while (fgets(menu_buf, BUFSIZ, menu_fp)) {
1267 char tline [BUFSIZ];
1268 char *tok = NULL;
1269
1270 (void) strlcpy(tline, menu_buf, sizeof (tline));
1271
1272 /* Tokenize line */
1273 tok = strtok(tline, BE_WHITE_SPACE);
1274
1275 if (tok == NULL) {
1276 /* Found empty line, write it out */
1277 (void) fputs(menu_buf, tmp_menu_fp);
1278 } else if (strcmp(tok, "default") == 0) {
1279 /* Found the default line, adjust it */
1280 (void) snprintf(tline, sizeof (tline),
1281 "default %d\n", default_entry);
1282
1283 (void) fputs(tline, tmp_menu_fp);
1284 } else {
1285 /* Pass through all other lines */
1286 (void) fputs(menu_buf, tmp_menu_fp);
1287 }
1288 }
1289
1290 (void) fclose(menu_fp);
1291 menu_fp = NULL;
1292 (void) fclose(tmp_menu_fp);
1293 tmp_menu_fp = NULL;
1294
1295 /* Copy the modified menu.lst into place */
1296 if (rename(tmp_menu, menu) != 0) {
1297 err = errno;
1298 be_print_err(gettext("be_remove_menu: "
1299 "failed to rename file %s to %s: %s\n"),
1300 tmp_menu, menu, strerror(err));
1301 ret = errno_to_be_err(err);
1302 goto cleanup;
1303 }
1304
1305 free(tmp_menu);
1306 tmp_menu = NULL;
1307 }
1308 }
1309
1310 /* Set the perms and ownership of the updated file */
1311 if (chmod(menu, sb.st_mode) != 0) {
1312 err = errno;
1313 be_print_err(gettext("be_remove_menu: "
1314 "failed to chmod %s: %s\n"), menu, strerror(err));
1315 ret = errno_to_be_err(err);
1316 goto cleanup;
1317 }
1318 if (chown(menu, sb.st_uid, sb.st_gid) != 0) {
1319 err = errno;
1320 be_print_err(gettext("be_remove_menu: "
1321 "failed to chown %s: %s\n"), menu, strerror(err));
1322 ret = errno_to_be_err(err);
1323 goto cleanup;
1324 }
1325
1326 cleanup:
1327 if (pool_mounted) {
1328 int err = BE_SUCCESS;
1329 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1330 if (ret == BE_SUCCESS)
1331 ret = err;
1332 free(orig_mntpnt);
1333 free(ptmp_mntpnt);
1334 }
1335 ZFS_CLOSE(zhp);
1336
1337 free(buffer);
1338 if (menu_fp != NULL)
1339 (void) fclose(menu_fp);
1340 if (tmp_menu_fp != NULL)
1341 (void) fclose(tmp_menu_fp);
1342 if (tmp_menu != NULL) {
1343 (void) unlink(tmp_menu);
1344 free(tmp_menu);
1345 }
1346
1347 return (ret);
1348 }
1349
1350 /*
1351 * Function: be_default_grub_bootfs
1352 * Description: This function returns the dataset in the default entry of
1353 * the grub menu. If no default entry is found with a valid bootfs
1354 * entry NULL is returned.
1355 * Parameters:
1356 * be_root_pool - This is the name of the root pool where the
1357 * grub menu can be found.
1358 * def_bootfs - This is used to pass back the bootfs string. On
1359 * error NULL is returned here.
1360 * Returns:
1361 * Success - BE_SUCCESS is returned.
1362 * Failure - a be_errno_t is returned.
1363 * Scope:
1364 * Semi-private (library wide use only)
1365 */
1366 int
1367 be_default_grub_bootfs(const char *be_root_pool, char **def_bootfs)
1368 {
1369 zfs_handle_t *zhp = NULL;
1370 char grub_file[MAXPATHLEN];
1371 FILE *menu_fp;
1372 char line[BUFSIZ];
1373 char *pool_mntpnt = NULL;
1374 char *ptmp_mntpnt = NULL;
1375 char *orig_mntpnt = NULL;
1376 int default_entry = 0, entries = 0;
1377 int found_default = 0;
1378 int ret = BE_SUCCESS;
1379 boolean_t pool_mounted = B_FALSE;
1380
1381 errno = 0;
1382
1383 /*
1384 * Check to see if this system supports grub
1385 */
1386 if (!be_has_grub()) {
1387 be_print_err(gettext("be_default_grub_bootfs: operation "
1388 "not supported on this architecture\n"));
1389 return (BE_ERR_NOTSUP);
1390 }
1391
1392 *def_bootfs = NULL;
1393
1394 /* Get handle to pool dataset */
1395 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1396 be_print_err(gettext("be_default_grub_bootfs: "
1397 "failed to open pool dataset for %s: %s"),
1398 be_root_pool, libzfs_error_description(g_zfs));
1399 return (zfs_err_to_be_err(g_zfs));
1400 }
1401
1402 /*
1403 * Check to see if the pool's dataset is mounted. If it isn't we'll
1404 * attempt to mount it.
1405 */
1406 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1407 &pool_mounted)) != BE_SUCCESS) {
1408 be_print_err(gettext("be_default_grub_bootfs: pool dataset "
1409 "(%s) could not be mounted\n"), be_root_pool);
1410 ZFS_CLOSE(zhp);
1411 return (ret);
1412 }
1413
1414 /*
1415 * Get the mountpoint for the root pool dataset.
1416 */
1417 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1418 be_print_err(gettext("be_default_grub_bootfs: failed "
1419 "to get mount point for the root pool. Can't set "
1420 "the default BE in the grub menu.\n"));
1421 ret = BE_ERR_NO_MENU;
1422 goto cleanup;
1423 }
1424
1425 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1426 pool_mntpnt, BE_GRUB_MENU);
1427
1428 if ((ret = be_open_menu((char *)be_root_pool, grub_file,
1429 &menu_fp, "r", B_FALSE)) != BE_SUCCESS) {
1430 goto cleanup;
1431 } else if (menu_fp == NULL) {
1432 ret = BE_ERR_NO_MENU;
1433 goto cleanup;
1434 }
1435
1436 free(pool_mntpnt);
1437 pool_mntpnt = NULL;
1438
1439 while (fgets(line, BUFSIZ, menu_fp)) {
1440 char *tok = strtok(line, BE_WHITE_SPACE);
1441
1442 if (tok != NULL && tok[0] != '#') {
1443 if (!found_default) {
1444 if (strcmp(tok, "default") == 0) {
1445 tok = strtok(NULL, BE_WHITE_SPACE);
1446 if (tok != NULL) {
1447 default_entry = atoi(tok);
1448 rewind(menu_fp);
1449 found_default = 1;
1450 }
1451 }
1452 continue;
1453 }
1454 if (strcmp(tok, "title") == 0) {
1455 entries++;
1456 } else if (default_entry == entries - 1) {
1457 if (strcmp(tok, "bootfs") == 0) {
1458 tok = strtok(NULL, BE_WHITE_SPACE);
1459 (void) fclose(menu_fp);
1460
1461 if (tok == NULL) {
1462 ret = BE_SUCCESS;
1463 goto cleanup;
1464 }
1465
1466 if ((*def_bootfs = strdup(tok)) !=
1467 NULL) {
1468 ret = BE_SUCCESS;
1469 goto cleanup;
1470 }
1471 be_print_err(gettext(
1472 "be_default_grub_bootfs: "
1473 "memory allocation failed\n"));
1474 ret = BE_ERR_NOMEM;
1475 goto cleanup;
1476 }
1477 } else if (default_entry < entries - 1) {
1478 /*
1479 * no bootfs entry for the default entry.
1480 */
1481 break;
1482 }
1483 }
1484 }
1485 (void) fclose(menu_fp);
1486
1487 cleanup:
1488 if (pool_mounted) {
1489 int err = BE_SUCCESS;
1490 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1491 if (ret == BE_SUCCESS)
1492 ret = err;
1493 free(orig_mntpnt);
1494 free(ptmp_mntpnt);
1495 }
1496 ZFS_CLOSE(zhp);
1497 return (ret);
1498 }
1499
1500 /*
1501 * Function: be_change_grub_default
1502 * Description: This function takes two parameters. These are the name of
1503 * the BE we want to have as the default booted in the grub
1504 * menu and the root pool where the path to the grub menu exists.
1505 * The code takes this and finds the BE's entry in the grub menu
1506 * and changes the default entry to point to that entry in the
1507 * list.
1508 * Parameters:
1509 * be_name - This is the name of the BE wanted as the default
1510 * for the next boot.
1511 * be_root_pool - This is the name of the root pool where the
1512 * grub menu can be found.
1513 * Returns:
1514 * BE_SUCCESS - Success
1515 * be_errno_t - Failure
1516 * Scope:
1517 * Semi-private (library wide use only)
1518 */
1519 int
1520 be_change_grub_default(char *be_name, char *be_root_pool)
1521 {
1522 zfs_handle_t *zhp = NULL;
1523 char grub_file[MAXPATHLEN];
1524 char *temp_grub = NULL;
1525 char *pool_mntpnt = NULL;
1526 char *ptmp_mntpnt = NULL;
1527 char *orig_mntpnt = NULL;
1528 char line[BUFSIZ];
1529 char temp_line[BUFSIZ];
1530 char be_root_ds[MAXPATHLEN];
1531 FILE *grub_fp = NULL;
1532 FILE *temp_fp = NULL;
1533 struct stat sb;
1534 int temp_grub_len = 0;
1535 int fd, entries = 0;
1536 int err = 0;
1537 int ret = BE_SUCCESS;
1538 boolean_t found_default = B_FALSE;
1539 boolean_t pool_mounted = B_FALSE;
1540
1541 errno = 0;
1542
1543 /*
1544 * Check to see if this system supports grub
1545 */
1546 if (!be_has_grub()) {
1547 be_print_err(gettext("be_change_grub_default: operation "
1548 "not supported on this architecture\n"));
1549 return (BE_ERR_NOTSUP);
1550 }
1551
1552 /* Generate string for BE's root dataset */
1553 be_make_root_ds(be_root_pool, be_name, be_root_ds, sizeof (be_root_ds));
1554
1555 /* Get handle to pool dataset */
1556 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1557 be_print_err(gettext("be_change_grub_default: "
1558 "failed to open pool dataset for %s: %s"),
1559 be_root_pool, libzfs_error_description(g_zfs));
1560 return (zfs_err_to_be_err(g_zfs));
1561 }
1562
1563 /*
1564 * Check to see if the pool's dataset is mounted. If it isn't we'll
1565 * attempt to mount it.
1566 */
1567 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1568 &pool_mounted)) != BE_SUCCESS) {
1569 be_print_err(gettext("be_change_grub_default: pool dataset "
1570 "(%s) could not be mounted\n"), be_root_pool);
1571 ZFS_CLOSE(zhp);
1572 return (ret);
1573 }
1574
1575 /*
1576 * Get the mountpoint for the root pool dataset.
1577 */
1578 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1579 be_print_err(gettext("be_change_grub_default: pool "
1580 "dataset (%s) is not mounted. Can't set "
1581 "the default BE in the grub menu.\n"), be_root_pool);
1582 ret = BE_ERR_NO_MENU;
1583 goto cleanup;
1584 }
1585
1586 (void) snprintf(grub_file, MAXPATHLEN, "%s%s",
1587 pool_mntpnt, BE_GRUB_MENU);
1588
1589 if ((ret = be_open_menu(be_root_pool, grub_file,
1590 &grub_fp, "r+", B_TRUE)) != BE_SUCCESS) {
1591 goto cleanup;
1592 } else if (grub_fp == NULL) {
1593 ret = BE_ERR_NO_MENU;
1594 goto cleanup;
1595 }
1596
1597 free(pool_mntpnt);
1598 pool_mntpnt = NULL;
1599
1600 /* Grab the stats of the original menu file */
1601 if (stat(grub_file, &sb) != 0) {
1602 err = errno;
1603 be_print_err(gettext("be_change_grub_default: "
1604 "failed to stat file %s: %s\n"), grub_file, strerror(err));
1605 ret = errno_to_be_err(err);
1606 goto cleanup;
1607 }
1608
1609 /* Create a tmp file for the modified menu.lst */
1610 temp_grub_len = strlen(grub_file) + 7;
1611 if ((temp_grub = (char *)malloc(temp_grub_len)) == NULL) {
1612 be_print_err(gettext("be_change_grub_default: "
1613 "malloc failed\n"));
1614 ret = BE_ERR_NOMEM;
1615 goto cleanup;
1616 }
1617 (void) memset(temp_grub, 0, temp_grub_len);
1618 (void) strlcpy(temp_grub, grub_file, temp_grub_len);
1619 (void) strlcat(temp_grub, "XXXXXX", temp_grub_len);
1620 if ((fd = mkstemp(temp_grub)) == -1) {
1621 err = errno;
1622 be_print_err(gettext("be_change_grub_default: "
1623 "mkstemp failed: %s\n"), strerror(err));
1624 ret = errno_to_be_err(err);
1625 free(temp_grub);
1626 temp_grub = NULL;
1627 goto cleanup;
1628 }
1629 if ((temp_fp = fdopen(fd, "w")) == NULL) {
1630 err = errno;
1631 be_print_err(gettext("be_change_grub_default: "
1632 "failed to open %s file: %s\n"),
1633 temp_grub, strerror(err));
1634 (void) close(fd);
1635 ret = errno_to_be_err(err);
1636 goto cleanup;
1637 }
1638
1639 while (fgets(line, BUFSIZ, grub_fp)) {
1640 char *tok = strtok(line, BE_WHITE_SPACE);
1641
1642 if (tok == NULL || tok[0] == '#') {
1643 continue;
1644 } else if (strcmp(tok, "title") == 0) {
1645 entries++;
1646 continue;
1647 } else if (strcmp(tok, "bootfs") == 0) {
1648 char *bootfs = strtok(NULL, BE_WHITE_SPACE);
1649 if (bootfs == NULL)
1650 continue;
1651
1652 if (strcmp(bootfs, be_root_ds) == 0) {
1653 found_default = B_TRUE;
1654 break;
1655 }
1656 }
1657 }
1658
1659 if (!found_default) {
1660 be_print_err(gettext("be_change_grub_default: failed "
1661 "to find entry for %s in the grub menu\n"),
1662 be_name);
1663 ret = BE_ERR_BE_NOENT;
1664 goto cleanup;
1665 }
1666
1667 rewind(grub_fp);
1668
1669 while (fgets(line, BUFSIZ, grub_fp)) {
1670 char *tok = NULL;
1671
1672 (void) strncpy(temp_line, line, BUFSIZ);
1673
1674 if ((tok = strtok(temp_line, BE_WHITE_SPACE)) != NULL &&
1675 strcmp(tok, "default") == 0) {
1676 (void) snprintf(temp_line, BUFSIZ, "default %d\n",
1677 entries - 1 >= 0 ? entries - 1 : 0);
1678 (void) fputs(temp_line, temp_fp);
1679 } else {
1680 (void) fputs(line, temp_fp);
1681 }
1682 }
1683
1684 (void) fclose(grub_fp);
1685 grub_fp = NULL;
1686 (void) fclose(temp_fp);
1687 temp_fp = NULL;
1688
1689 if (rename(temp_grub, grub_file) != 0) {
1690 err = errno;
1691 be_print_err(gettext("be_change_grub_default: "
1692 "failed to rename file %s to %s: %s\n"),
1693 temp_grub, grub_file, strerror(err));
1694 ret = errno_to_be_err(err);
1695 goto cleanup;
1696 }
1697 free(temp_grub);
1698 temp_grub = NULL;
1699
1700 /* Set the perms and ownership of the updated file */
1701 if (chmod(grub_file, sb.st_mode) != 0) {
1702 err = errno;
1703 be_print_err(gettext("be_change_grub_default: "
1704 "failed to chmod %s: %s\n"), grub_file, strerror(err));
1705 ret = errno_to_be_err(err);
1706 goto cleanup;
1707 }
1708 if (chown(grub_file, sb.st_uid, sb.st_gid) != 0) {
1709 err = errno;
1710 be_print_err(gettext("be_change_grub_default: "
1711 "failed to chown %s: %s\n"), grub_file, strerror(err));
1712 ret = errno_to_be_err(err);
1713 }
1714
1715 cleanup:
1716 if (pool_mounted) {
1717 int err = BE_SUCCESS;
1718 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1719 if (ret == BE_SUCCESS)
1720 ret = err;
1721 free(orig_mntpnt);
1722 free(ptmp_mntpnt);
1723 }
1724 ZFS_CLOSE(zhp);
1725 if (grub_fp != NULL)
1726 (void) fclose(grub_fp);
1727 if (temp_fp != NULL)
1728 (void) fclose(temp_fp);
1729 if (temp_grub != NULL) {
1730 (void) unlink(temp_grub);
1731 free(temp_grub);
1732 }
1733
1734 return (ret);
1735 }
1736
1737 /*
1738 * Function: be_update_menu
1739 * Description: This function is used by be_rename to change the BE name in
1740 * an existing entry in the grub menu to the new name of the BE.
1741 * Parameters:
1742 * be_orig_name - the original name of the BE
1743 * be_new_name - the new name the BE is being renameed to.
1744 * be_root_pool - The pool which contains the grub menu
1745 * boot_pool - the pool where the BE is, if different than
1746 * the pool containing the boot menu. If this is
1747 * NULL it will be set to be_root_pool.
1748 * Returns:
1749 * BE_SUCCESS - Success
1750 * be_errno_t - Failure
1751 * Scope:
1752 * Semi-private (library wide use only)
1753 */
1754 int
1755 be_update_menu(char *be_orig_name, char *be_new_name, char *be_root_pool,
1756 char *boot_pool)
1757 {
1758 zfs_handle_t *zhp = NULL;
1759 char menu_file[MAXPATHLEN];
1760 char be_root_ds[MAXPATHLEN];
1761 char be_new_root_ds[MAXPATHLEN];
1762 char line[BUFSIZ];
1763 char *pool_mntpnt = NULL;
1764 char *ptmp_mntpnt = NULL;
1765 char *orig_mntpnt = NULL;
1766 char *temp_menu = NULL;
1767 FILE *menu_fp = NULL;
1768 FILE *new_fp = NULL;
1769 struct stat sb;
1770 int temp_menu_len = 0;
1771 int tmp_fd;
1772 int ret = BE_SUCCESS;
1773 int err = 0;
1774 boolean_t pool_mounted = B_FALSE;
1775
1776 errno = 0;
1777
1778 if (boot_pool == NULL)
1779 boot_pool = be_root_pool;
1780
1781 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
1782 be_print_err(gettext("be_update_menu: failed to open "
1783 "pool dataset for %s: %s\n"), be_root_pool,
1784 libzfs_error_description(g_zfs));
1785 return (zfs_err_to_be_err(g_zfs));
1786 }
1787
1788 /*
1789 * Check to see if the pool's dataset is mounted. If it isn't we'll
1790 * attempt to mount it.
1791 */
1792 if ((ret = be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
1793 &pool_mounted)) != BE_SUCCESS) {
1794 be_print_err(gettext("be_update_menu: pool dataset "
1795 "(%s) could not be mounted\n"), be_root_pool);
1796 ZFS_CLOSE(zhp);
1797 return (ret);
1798 }
1799
1800 /*
1801 * Get the mountpoint for the root pool dataset.
1802 */
1803 if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1804 be_print_err(gettext("be_update_menu: failed "
1805 "to get mount point for the root pool. Can't set "
1806 "the default BE in the grub menu.\n"));
1807 ret = BE_ERR_NO_MENU;
1808 goto cleanup;
1809 }
1810
1811 /*
1812 * Check to see if this system supports grub
1813 */
1814 if (be_has_grub()) {
1815 (void) snprintf(menu_file, sizeof (menu_file),
1816 "%s%s", pool_mntpnt, BE_GRUB_MENU);
1817 } else {
1818 (void) snprintf(menu_file, sizeof (menu_file),
1819 "%s%s", pool_mntpnt, BE_SPARC_MENU);
1820 }
1821
1822 be_make_root_ds(be_root_pool, be_orig_name, be_root_ds,
1823 sizeof (be_root_ds));
1824 be_make_root_ds(be_root_pool, be_new_name, be_new_root_ds,
1825 sizeof (be_new_root_ds));
1826
1827 if ((ret = be_open_menu(be_root_pool, menu_file,
1828 &menu_fp, "r", B_TRUE)) != BE_SUCCESS) {
1829 goto cleanup;
1830 } else if (menu_fp == NULL) {
1831 ret = BE_ERR_NO_MENU;
1832 goto cleanup;
1833 }
1834
1835 free(pool_mntpnt);
1836 pool_mntpnt = NULL;
1837
1838 /* Grab the stat of the original menu file */
1839 if (stat(menu_file, &sb) != 0) {
1840 err = errno;
1841 be_print_err(gettext("be_update_menu: "
1842 "failed to stat file %s: %s\n"), menu_file, strerror(err));
1843 (void) fclose(menu_fp);
1844 ret = errno_to_be_err(err);
1845 goto cleanup;
1846 }
1847
1848 /* Create tmp file for modified menu.lst */
1849 temp_menu_len = strlen(menu_file) + 7;
1850 if ((temp_menu = (char *)malloc(temp_menu_len))
1851 == NULL) {
1852 be_print_err(gettext("be_update_menu: "
1853 "malloc failed\n"));
1854 (void) fclose(menu_fp);
1855 ret = BE_ERR_NOMEM;
1856 goto cleanup;
1857 }
1858 (void) memset(temp_menu, 0, temp_menu_len);
1859 (void) strlcpy(temp_menu, menu_file, temp_menu_len);
1860 (void) strlcat(temp_menu, "XXXXXX", temp_menu_len);
1861 if ((tmp_fd = mkstemp(temp_menu)) == -1) {
1862 err = errno;
1863 be_print_err(gettext("be_update_menu: "
1864 "mkstemp failed: %s\n"), strerror(err));
1865 (void) fclose(menu_fp);
1866 free(temp_menu);
1867 ret = errno_to_be_err(err);
1868 goto cleanup;
1869 }
1870 if ((new_fp = fdopen(tmp_fd, "w")) == NULL) {
1871 err = errno;
1872 be_print_err(gettext("be_update_menu: "
1873 "fdopen failed: %s\n"), strerror(err));
1874 (void) close(tmp_fd);
1875 (void) fclose(menu_fp);
1876 free(temp_menu);
1877 ret = errno_to_be_err(err);
1878 goto cleanup;
1879 }
1880
1881 while (fgets(line, BUFSIZ, menu_fp)) {
1882 char tline[BUFSIZ];
1883 char new_line[BUFSIZ];
1884 char *c = NULL;
1885
1886 (void) strlcpy(tline, line, sizeof (tline));
1887
1888 /* Tokenize line */
1889 c = strtok(tline, BE_WHITE_SPACE);
1890
1891 if (c == NULL) {
1892 /* Found empty line, write it out. */
1893 (void) fputs(line, new_fp);
1894 } else if (c[0] == '#') {
1895 /* Found a comment line, write it out. */
1896 (void) fputs(line, new_fp);
1897 } else if (strcmp(c, "title") == 0) {
1898 char *name = NULL;
1899 char *desc = NULL;
1900
1901 /*
1902 * Found a 'title' line, parse out BE name or
1903 * the description.
1904 */
1905 name = strtok(NULL, BE_WHITE_SPACE);
1906
1907 if (name == NULL) {
1908 /*
1909 * Nothing after 'title', just push
1910 * this line through
1911 */
1912 (void) fputs(line, new_fp);
1913 } else {
1914 /*
1915 * Grab the remainder of the title which
1916 * could be a multi worded description
1917 */
1918 desc = strtok(NULL, "\n");
1919
1920 if (strcmp(name, be_orig_name) == 0) {
1921 /*
1922 * The first token of the title is
1923 * the old BE name, replace it with
1924 * the new one, and write it out
1925 * along with the remainder of
1926 * description if there is one.
1927 */
1928 if (desc) {
1929 (void) snprintf(new_line,
1930 sizeof (new_line),
1931 "title %s %s\n",
1932 be_new_name, desc);
1933 } else {
1934 (void) snprintf(new_line,
1935 sizeof (new_line),
1936 "title %s\n", be_new_name);
1937 }
1938
1939 (void) fputs(new_line, new_fp);
1940 } else {
1941 (void) fputs(line, new_fp);
1942 }
1943 }
1944 } else if (strcmp(c, "bootfs") == 0) {
1945 /*
1946 * Found a 'bootfs' line, parse out the BE root
1947 * dataset value.
1948 */
1949 char *root_ds = strtok(NULL, BE_WHITE_SPACE);
1950
1951 if (root_ds == NULL) {
1952 /*
1953 * Nothing after 'bootfs', just push
1954 * this line through
1955 */
1956 (void) fputs(line, new_fp);
1957 } else {
1958 /*
1959 * If this bootfs is the one we're renaming,
1960 * write out the new root dataset value
1961 */
1962 if (strcmp(root_ds, be_root_ds) == 0) {
1963 (void) snprintf(new_line,
1964 sizeof (new_line), "bootfs %s\n",
1965 be_new_root_ds);
1966
1967 (void) fputs(new_line, new_fp);
1968 } else {
1969 (void) fputs(line, new_fp);
1970 }
1971 }
1972 } else {
1973 /*
1974 * Found some other line we don't care
1975 * about, write it out.
1976 */
1977 (void) fputs(line, new_fp);
1978 }
1979 }
1980
1981 (void) fclose(menu_fp);
1982 (void) fclose(new_fp);
1983 (void) close(tmp_fd);
1984
1985 if (rename(temp_menu, menu_file) != 0) {
1986 err = errno;
1987 be_print_err(gettext("be_update_menu: "
1988 "failed to rename file %s to %s: %s\n"),
1989 temp_menu, menu_file, strerror(err));
1990 ret = errno_to_be_err(err);
1991 }
1992 free(temp_menu);
1993
1994 /* Set the perms and ownership of the updated file */
1995 if (chmod(menu_file, sb.st_mode) != 0) {
1996 err = errno;
1997 be_print_err(gettext("be_update_menu: "
1998 "failed to chmod %s: %s\n"), menu_file, strerror(err));
1999 ret = errno_to_be_err(err);
2000 goto cleanup;
2001 }
2002 if (chown(menu_file, sb.st_uid, sb.st_gid) != 0) {
2003 err = errno;
2004 be_print_err(gettext("be_update_menu: "
2005 "failed to chown %s: %s\n"), menu_file, strerror(err));
2006 ret = errno_to_be_err(err);
2007 }
2008
2009 cleanup:
2010 if (pool_mounted) {
2011 int err = BE_SUCCESS;
2012 err = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2013 if (ret == BE_SUCCESS)
2014 ret = err;
2015 free(orig_mntpnt);
2016 free(ptmp_mntpnt);
2017 }
2018 ZFS_CLOSE(zhp);
2019 return (ret);
2020 }
2021
2022 /*
2023 * Function: be_has_menu_entry
2024 * Description: Checks to see if the BEs root dataset has an entry in the grub
2025 * menu.
2026 * Parameters:
2027 * be_dataset - The root dataset of the BE
2028 * be_root_pool - The pool which contains the boot menu
2029 * entry - A pointer the the entry number of the BE if found.
2030 * Returns:
2031 * B_TRUE - Success
2032 * B_FALSE - Failure
2033 * Scope:
2034 * Semi-private (library wide use only)
2035 */
2036 boolean_t
2037 be_has_menu_entry(char *be_dataset, char *be_root_pool, int *entry)
2038 {
2039 zfs_handle_t *zhp = NULL;
2040 char menu_file[MAXPATHLEN];
2041 FILE *menu_fp;
2042 char line[BUFSIZ];
2043 char *last;
2044 char *rpool_mntpnt = NULL;
2045 char *ptmp_mntpnt = NULL;
2046 char *orig_mntpnt = NULL;
2047 int ent_num = 0;
2048 boolean_t ret = 0;
2049 boolean_t pool_mounted = B_FALSE;
2050
2051
2052 /*
2053 * Check to see if this system supports grub
2054 */
2055 if ((zhp = zfs_open(g_zfs, be_root_pool, ZFS_TYPE_DATASET)) == NULL) {
2056 be_print_err(gettext("be_has_menu_entry: failed to open "
2057 "pool dataset for %s: %s\n"), be_root_pool,
2058 libzfs_error_description(g_zfs));
2059 return (B_FALSE);
2060 }
2061
2062 /*
2063 * Check to see if the pool's dataset is mounted. If it isn't we'll
2064 * attempt to mount it.
2065 */
2066 if (be_mount_pool(zhp, &ptmp_mntpnt, &orig_mntpnt,
2067 &pool_mounted) != 0) {
2068 be_print_err(gettext("be_has_menu_entry: pool dataset "
2069 "(%s) could not be mounted\n"), be_root_pool);
2070 ZFS_CLOSE(zhp);
2071 return (B_FALSE);
2072 }
2073
2074 /*
2075 * Get the mountpoint for the root pool dataset.
2076 */
2077 if (!zfs_is_mounted(zhp, &rpool_mntpnt)) {
2078 be_print_err(gettext("be_has_menu_entry: pool "
2079 "dataset (%s) is not mounted. Can't set "
2080 "the default BE in the grub menu.\n"), be_root_pool);
2081 ret = B_FALSE;
2082 goto cleanup;
2083 }
2084
2085 if (be_has_grub()) {
2086 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2087 rpool_mntpnt, BE_GRUB_MENU);
2088 } else {
2089 (void) snprintf(menu_file, MAXPATHLEN, "/%s%s",
2090 rpool_mntpnt, BE_SPARC_MENU);
2091 }
2092
2093 if (be_open_menu(be_root_pool, menu_file, &menu_fp, "r",
2094 B_FALSE) != 0) {
2095 ret = B_FALSE;
2096 goto cleanup;
2097 } else if (menu_fp == NULL) {
2098 ret = B_FALSE;
2099 goto cleanup;
2100 }
2101
2102 free(rpool_mntpnt);
2103 rpool_mntpnt = NULL;
2104
2105 while (fgets(line, BUFSIZ, menu_fp)) {
2106 char *tok = strtok_r(line, BE_WHITE_SPACE, &last);
2107
2108 if (tok != NULL && tok[0] != '#') {
2109 if (strcmp(tok, "bootfs") == 0) {
2110 tok = strtok_r(last, BE_WHITE_SPACE, &last);
2111 if (tok != NULL && strcmp(tok,
2112 be_dataset) == 0) {
2113 (void) fclose(menu_fp);
2114 /*
2115 * The entry number needs to be
2116 * decremented here because the title
2117 * will always be the first line for
2118 * an entry. Because of this we'll
2119 * always be off by one entry when we
2120 * check for bootfs.
2121 */
2122 *entry = ent_num - 1;
2123 ret = B_TRUE;
2124 goto cleanup;
2125 }
2126 } else if (strcmp(tok, "title") == 0)
2127 ent_num++;
2128 }
2129 }
2130
2131 cleanup:
2132 if (pool_mounted) {
2133 (void) be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
2134 free(orig_mntpnt);
2135 free(ptmp_mntpnt);
2136 }
2137 ZFS_CLOSE(zhp);
2138 (void) fclose(menu_fp);
2139 return (ret);
2140 }
2141
2142 /*
2143 * Function: be_update_vfstab
2144 * Description: This function digs into a BE's vfstab and updates all
2145 * entries with file systems listed in be_fs_list_data_t.
2146 * The entry's root container dataset and be_name will be
2147 * updated with the parameters passed in.
2148 * Parameters:
2149 * be_name - name of BE to update
2150 * old_rc_loc - dataset under which the root container dataset
2151 * of the old BE resides in.
2152 * new_rc_loc - dataset under which the root container dataset
2153 * of the new BE resides in.
2154 * fld - be_fs_list_data_t pointer providing the list of
2155 * file systems to look for in vfstab.
2156 * mountpoint - directory of where BE is currently mounted.
2157 * If NULL, then BE is not currently mounted.
2158 * Returns:
2159 * BE_SUCCESS - Success
2160 * be_errno_t - Failure
2161 * Scope:
2162 * Semi-private (library wide use only)
2163 */
2164 int
2165 be_update_vfstab(char *be_name, char *old_rc_loc, char *new_rc_loc,
2166 be_fs_list_data_t *fld, char *mountpoint)
2167 {
2168 char *tmp_mountpoint = NULL;
2169 char alt_vfstab[MAXPATHLEN];
2170 int ret = BE_SUCCESS, err = BE_SUCCESS;
2171
2172 if (fld == NULL || fld->fs_list == NULL || fld->fs_num == 0)
2173 return (BE_SUCCESS);
2174
2175 /* If BE not already mounted, mount the BE */
2176 if (mountpoint == NULL) {
2177 if ((ret = _be_mount(be_name, &tmp_mountpoint,
2178 BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
2179 be_print_err(gettext("be_update_vfstab: "
2180 "failed to mount BE (%s)\n"), be_name);
2181 return (ret);
2182 }
2183 } else {
2184 tmp_mountpoint = mountpoint;
2185 }
2186
2187 /* Get string for vfstab in the mounted BE. */
2188 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2189 tmp_mountpoint);
2190
2191 /* Update the vfstab */
2192 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2193 fld);
2194
2195 /* Unmount BE if we mounted it */
2196 if (mountpoint == NULL) {
2197 if ((err = _be_unmount(be_name, 0)) == BE_SUCCESS) {
2198 /* Remove temporary mountpoint */
2199 (void) rmdir(tmp_mountpoint);
2200 } else {
2201 be_print_err(gettext("be_update_vfstab: "
2202 "failed to unmount BE %s mounted at %s\n"),
2203 be_name, tmp_mountpoint);
2204 if (ret == BE_SUCCESS)
2205 ret = err;
2206 }
2207
2208 free(tmp_mountpoint);
2209 }
2210
2211 return (ret);
2212 }
2213
2214 /*
2215 * Function: be_update_zone_vfstab
2216 * Description: This function digs into a zone BE's vfstab and updates all
2217 * entries with file systems listed in be_fs_list_data_t.
2218 * The entry's root container dataset and be_name will be
2219 * updated with the parameters passed in.
2220 * Parameters:
2221 * zhp - zfs_handle_t pointer to zone root dataset.
2222 * be_name - name of zone BE to update
2223 * old_rc_loc - dataset under which the root container dataset
2224 * of the old zone BE resides in.
2225 * new_rc_loc - dataset under which the root container dataset
2226 * of the new zone BE resides in.
2227 * fld - be_fs_list_data_t pointer providing the list of
2228 * file systems to look for in vfstab.
2229 * Returns:
2230 * BE_SUCCESS - Success
2231 * be_errno_t - Failure
2232 * Scope:
2233 * Semi-private (library wide use only)
2234 */
2235 int
2236 be_update_zone_vfstab(zfs_handle_t *zhp, char *be_name, char *old_rc_loc,
2237 char *new_rc_loc, be_fs_list_data_t *fld)
2238 {
2239 be_mount_data_t md = { 0 };
2240 be_unmount_data_t ud = { 0 };
2241 char alt_vfstab[MAXPATHLEN];
2242 boolean_t mounted_here = B_FALSE;
2243 int ret = BE_SUCCESS;
2244
2245 /*
2246 * If zone root not already mounted, mount it at a
2247 * temporary location.
2248 */
2249 if (!zfs_is_mounted(zhp, &md.altroot)) {
2250 /* Generate temporary mountpoint to mount zone root */
2251 if ((ret = be_make_tmp_mountpoint(&md.altroot)) != BE_SUCCESS) {
2252 be_print_err(gettext("be_update_zone_vfstab: "
2253 "failed to make temporary mountpoint to "
2254 "mount zone root\n"));
2255 return (ret);
2256 }
2257
2258 if (be_mount_zone_root(zhp, &md) != BE_SUCCESS) {
2259 be_print_err(gettext("be_update_zone_vfstab: "
2260 "failed to mount zone root %s\n"),
2261 zfs_get_name(zhp));
2262 free(md.altroot);
2263 return (BE_ERR_MOUNT_ZONEROOT);
2264 }
2265 mounted_here = B_TRUE;
2266 }
2267
2268 /* Get string from vfstab in the mounted zone BE */
2269 (void) snprintf(alt_vfstab, sizeof (alt_vfstab), "%s/etc/vfstab",
2270 md.altroot);
2271
2272 /* Update the vfstab */
2273 ret = _update_vfstab(alt_vfstab, be_name, old_rc_loc, new_rc_loc,
2274 fld);
2275
2276 /* Unmount zone root if we mounted it */
2277 if (mounted_here) {
2278 ud.force = B_TRUE;
2279
2280 if (be_unmount_zone_root(zhp, &ud) == BE_SUCCESS) {
2281 /* Remove the temporary mountpoint */
2282 (void) rmdir(md.altroot);
2283 } else {
2284 be_print_err(gettext("be_update_zone_vfstab: "
2285 "failed to unmount zone root %s from %s\n"),
2286 zfs_get_name(zhp), md.altroot);
2287 if (ret == 0)
2288 ret = BE_ERR_UMOUNT_ZONEROOT;
2289 }
2290 }
2291
2292 free(md.altroot);
2293 return (ret);
2294 }
2295
2296 /*
2297 * Function: be_auto_snap_name
2298 * Description: Generate an auto snapshot name constructed based on the
2299 * current date and time. The auto snapshot name is of the form:
2300 *
2301 * <date>-<time>
2302 *
2303 * where <date> is in ISO standard format, so the resultant name
2304 * is of the form:
2305 *
2306 * %Y-%m-%d-%H:%M:%S
2307 *
2308 * Parameters:
2309 * None
2310 * Returns:
2311 * Success - pointer to auto generated snapshot name. The name
2312 * is allocated in heap storage so the caller is
2313 * responsible for free'ing the name.
2314 * Failure - NULL
2315 * Scope:
2316 * Semi-private (library wide use only)
2317 */
2318 char *
2319 be_auto_snap_name(void)
2320 {
2321 time_t utc_tm = NULL;
2322 struct tm *gmt_tm = NULL;
2323 char gmt_time_str[64];
2324 char *auto_snap_name = NULL;
2325
2326 if (time(&utc_tm) == -1) {
2327 be_print_err(gettext("be_auto_snap_name: time() failed\n"));
2328 return (NULL);
2329 }
2330
2331 if ((gmt_tm = gmtime(&utc_tm)) == NULL) {
2332 be_print_err(gettext("be_auto_snap_name: gmtime() failed\n"));
2333 return (NULL);
2334 }
2335
2336 (void) strftime(gmt_time_str, sizeof (gmt_time_str), "%F-%T", gmt_tm);
2337
2338 if ((auto_snap_name = strdup(gmt_time_str)) == NULL) {
2339 be_print_err(gettext("be_auto_snap_name: "
2340 "memory allocation failed\n"));
2341 return (NULL);
2342 }
2343
2344 return (auto_snap_name);
2345 }
2346
2347 /*
2348 * Function: be_auto_be_name
2349 * Description: Generate an auto BE name constructed based on the BE name
2350 * of the original BE being cloned.
2351 * Parameters:
2352 * obe_name - name of the original BE being cloned.
2353 * Returns:
2354 * Success - pointer to auto generated BE name. The name
2355 * is allocated in heap storage so the caller is
2356 * responsible for free'ing the name.
2357 * Failure - NULL
2358 * Scope:
2359 * Semi-private (library wide use only)
2360 */
2361 char *
2362 be_auto_be_name(char *obe_name)
2363 {
2364 return (be_get_auto_name(obe_name, NULL, B_FALSE));
2365 }
2366
2367 /*
2368 * Function: be_auto_zone_be_name
2369 * Description: Generate an auto BE name for a zone constructed based on
2370 * the BE name of the original zone BE being cloned.
2371 * Parameters:
2372 * container_ds - container dataset for the zone.
2373 * zbe_name - name of the original zone BE being cloned.
2374 * Returns:
2375 * Success - pointer to auto generated BE name. The name
2376 * is allocated in heap storage so the caller is
2377 * responsible for free'ing the name.
2378 * Failure - NULL
2379 * Scope:
2380 * Semi-private (library wide use only)
2381 */
2382 char *
2383 be_auto_zone_be_name(char *container_ds, char *zbe_name)
2384 {
2385 return (be_get_auto_name(zbe_name, container_ds, B_TRUE));
2386 }
2387
2388 /*
2389 * Function: be_valid_be_name
2390 * Description: Validates a BE name.
2391 * Parameters:
2392 * be_name - name of BE to validate
2393 * Returns:
2394 * B_TRUE - be_name is valid
2395 * B_FALSE - be_name is invalid
2396 * Scope:
2397 * Semi-private (library wide use only)
2398 */
2399
2400 boolean_t
2401 be_valid_be_name(const char *be_name)
2402 {
2403 const char *c = NULL;
2404 struct be_defaults be_defaults;
2405
2406 if (be_name == NULL)
2407 return (B_FALSE);
2408
2409 be_get_defaults(&be_defaults);
2410
2411 /*
2412 * A BE name must not be a multi-level dataset name. We also check
2413 * that it does not contain the ' ' and '%' characters. The ' ' is
2414 * a valid character for datasets, however we don't allow that in a
2415 * BE name. The '%' is invalid, but zfs_name_valid() allows it for
2416 * internal reasons, so we explicitly check for it here.
2417 */
2418 c = be_name;
2419 while (*c != '\0' && *c != '/' && *c != ' ' && *c != '%')
2420 c++;
2421
2422 if (*c != '\0')
2423 return (B_FALSE);
2424
2425 /*
2426 * The BE name must comply with a zfs dataset filesystem. We also
2427 * verify its length to be < BE_NAME_MAX_LEN.
2428 */
2429 if (!zfs_name_valid(be_name, ZFS_TYPE_FILESYSTEM) ||
2430 strlen(be_name) > BE_NAME_MAX_LEN)
2431 return (B_FALSE);
2432
2433 if (be_defaults.be_deflt_bename_starts_with[0] != '\0' &&
2434 strstr(be_name, be_defaults.be_deflt_bename_starts_with) == NULL) {
2435 return (B_FALSE);
2436 }
2437
2438 return (B_TRUE);
2439 }
2440
2441 /*
2442 * Function: be_valid_auto_snap_name
2443 * Description: This function checks that a snapshot name is a valid auto
2444 * generated snapshot name. A valid auto generated snapshot
2445 * name is of the form:
2446 *
2447 * %Y-%m-%d-%H:%M:%S
2448 *
2449 * An older form of the auto generated snapshot name also
2450 * included the snapshot's BE cleanup policy and a reserved
2451 * field. Those names will also be verified by this function.
2452 *
2453 * Examples of valid auto snapshot names are:
2454 *
2455 * 2008-03-31-18:41:30
2456 * 2008-03-31-22:17:24
2457 * <policy>:-:2008:04-05-09:12:55
2458 * <policy>:-:2008:04-06-15:34:12
2459 *
2460 * Parameters:
2461 * name - name of the snapshot to be validated.
2462 * Returns:
2463 * B_TRUE - the name is a valid auto snapshot name.
2464 * B_FALSE - the name is not a valid auto snapshot name.
2465 * Scope:
2466 * Semi-private (library wide use only)
2467 */
2468 boolean_t
2469 be_valid_auto_snap_name(char *name)
2470 {
2471 struct tm gmt_tm;
2472
2473 char *policy = NULL;
2474 char *reserved = NULL;
2475 char *date = NULL;
2476 char *c = NULL;
2477
2478 /* Validate the snapshot name by converting it into utc time */
2479 if (strptime(name, "%Y-%m-%d-%T", &gmt_tm) != NULL &&
2480 (mktime(&gmt_tm) != -1)) {
2481 return (B_TRUE);
2482 }
2483
2484 /*
2485 * Validate the snapshot name against the older form of an
2486 * auto generated snapshot name.
2487 */
2488 policy = strdup(name);
2489
2490 /*
2491 * Get the first field from the snapshot name,
2492 * which is the BE policy
2493 */
2494 c = strchr(policy, ':');
2495 if (c == NULL) {
2496 free(policy);
2497 return (B_FALSE);
2498 }
2499 c[0] = '\0';
2500
2501 /* Validate the policy name */
2502 if (!valid_be_policy(policy)) {
2503 free(policy);
2504 return (B_FALSE);
2505 }
2506
2507 /* Get the next field, which is the reserved field. */
2508 if (c[1] == NULL || c[1] == '\0') {
2509 free(policy);
2510 return (B_FALSE);
2511 }
2512 reserved = c+1;
2513 c = strchr(reserved, ':');
2514 if (c == NULL) {
2515 free(policy);
2516 return (B_FALSE);
2517 }
2518 c[0] = '\0';
2519
2520 /* Validate the reserved field */
2521 if (strcmp(reserved, "-") != 0) {
2522 free(policy);
2523 return (B_FALSE);
2524 }
2525
2526 /* The remaining string should be the date field */
2527 if (c[1] == NULL || c[1] == '\0') {
2528 free(policy);
2529 return (B_FALSE);
2530 }
2531 date = c+1;
2532
2533 /* Validate the date string by converting it into utc time */
2534 if (strptime(date, "%Y-%m-%d-%T", &gmt_tm) == NULL ||
2535 (mktime(&gmt_tm) == -1)) {
2536 be_print_err(gettext("be_valid_auto_snap_name: "
2537 "invalid auto snapshot name\n"));
2538 free(policy);
2539 return (B_FALSE);
2540 }
2541
2542 free(policy);
2543 return (B_TRUE);
2544 }
2545
2546 /*
2547 * Function: be_default_policy
2548 * Description: Temporary hardcoded policy support. This function returns
2549 * the default policy type to be used to create a BE or a BE
2550 * snapshot.
2551 * Parameters:
2552 * None
2553 * Returns:
2554 * Name of default BE policy.
2555 * Scope:
2556 * Semi-private (library wide use only)
2557 */
2558 char *
2559 be_default_policy(void)
2560 {
2561 return (BE_PLCY_STATIC);
2562 }
2563
2564 /*
2565 * Function: valid_be_policy
2566 * Description: Temporary hardcoded policy support. This function valids
2567 * whether a policy is a valid known policy or not.
2568 * Paramters:
2569 * policy - name of policy to validate.
2570 * Returns:
2571 * B_TRUE - policy is a valid.
2572 * B_FALSE - policy is invalid.
2573 * Scope:
2574 * Semi-private (library wide use only)
2575 */
2576 boolean_t
2577 valid_be_policy(char *policy)
2578 {
2579 if (policy == NULL)
2580 return (B_FALSE);
2581
2582 if (strcmp(policy, BE_PLCY_STATIC) == 0 ||
2583 strcmp(policy, BE_PLCY_VOLATILE) == 0) {
2584 return (B_TRUE);
2585 }
2586
2587 return (B_FALSE);
2588 }
2589
2590 /*
2591 * Function: be_print_err
2592 * Description: This function prints out error messages if do_print is
2593 * set to B_TRUE or if the BE_PRINT_ERR environment variable
2594 * is set to true.
2595 * Paramters:
2596 * prnt_str - the string we wish to print and any arguments
2597 * for the format of that string.
2598 * Returns:
2599 * void
2600 * Scope:
2601 * Semi-private (library wide use only)
2602 */
2603 void
2604 be_print_err(char *prnt_str, ...)
2605 {
2606 va_list ap;
2607 char buf[BUFSIZ];
2608 char *env_buf;
2609 static boolean_t env_checked = B_FALSE;
2610
2611 if (!env_checked) {
2612 if ((env_buf = getenv("BE_PRINT_ERR")) != NULL) {
2613 if (strcasecmp(env_buf, "true") == 0) {
2614 do_print = B_TRUE;
2615 }
2616 }
2617 env_checked = B_TRUE;
2618 }
2619
2620 if (do_print) {
2621 va_start(ap, prnt_str);
2622 /* LINTED variable format specifier */
2623 (void) vsnprintf(buf, BUFSIZ, prnt_str, ap);
2624 (void) fputs(buf, stderr);
2625 va_end(ap);
2626 }
2627 }
2628
2629 /*
2630 * Function: be_find_current_be
2631 * Description: Find the currently "active" BE. Fill in the
2632 * passed in be_transaction_data_t reference with the
2633 * active BE's data.
2634 * Paramters:
2635 * none
2636 * Returns:
2637 * BE_SUCCESS - Success
2638 * be_errnot_t - Failure
2639 * Scope:
2640 * Semi-private (library wide use only)
2641 * Notes:
2642 * The caller is responsible for initializing the libzfs handle
2643 * and freeing the memory used by the active be_name.
2644 */
2645 int
2646 be_find_current_be(be_transaction_data_t *bt)
2647 {
2648 int zret;
2649
2650 if ((zret = zpool_iter(g_zfs, be_zpool_find_current_be_callback,
2651 bt)) == 0) {
2652 be_print_err(gettext("be_find_current_be: failed to "
2653 "find current BE name\n"));
2654 return (BE_ERR_BE_NOENT);
2655 } else if (zret < 0) {
2656 be_print_err(gettext("be_find_current_be: "
2657 "zpool_iter failed: %s\n"),
2658 libzfs_error_description(g_zfs));
2659 return (zfs_err_to_be_err(g_zfs));
2660 }
2661
2662 return (BE_SUCCESS);
2663 }
2664
2665 /*
2666 * Function: be_zpool_find_current_be_callback
2667 * Description: Callback function used to iterate through all existing pools
2668 * to find the BE that is the currently booted BE.
2669 * Parameters:
2670 * zlp - zpool_handle_t pointer to the current pool being
2671 * looked at.
2672 * data - be_transaction_data_t pointer.
2673 * Upon successfully finding the current BE, the
2674 * obe_zpool member of this parameter is set to the
2675 * pool it is found in.
2676 * Return:
2677 * 1 - Found current BE in this pool.
2678 * 0 - Did not find current BE in this pool.
2679 * Scope:
2680 * Semi-private (library wide use only)
2681 */
2682 int
2683 be_zpool_find_current_be_callback(zpool_handle_t *zlp, void *data)
2684 {
2685 be_transaction_data_t *bt = data;
2686 zfs_handle_t *zhp = NULL;
2687 const char *zpool = zpool_get_name(zlp);
2688 char be_container_ds[MAXPATHLEN];
2689 char *zpath = NULL;
2690
2691 /*
2692 * Generate string for BE container dataset
2693 */
2694 if (getzoneid() != GLOBAL_ZONEID) {
2695 if ((zpath = be_get_ds_from_dir("/")) != NULL) {
2696 (void) strlcpy(be_container_ds, dirname(zpath),
2697 sizeof (be_container_ds));
2698 } else {
2699 be_print_err(gettext(
2700 "be_zpool_find_current_be_callback: "
2701 "zone root dataset is not mounted\n"));
2702 return (0);
2703 }
2704 } else {
2705 be_make_container_ds(zpool, be_container_ds,
2706 sizeof (be_container_ds));
2707 }
2708
2709 /*
2710 * Check if a BE container dataset exists in this pool.
2711 */
2712 if (!zfs_dataset_exists(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) {
2713 zpool_close(zlp);
2714 return (0);
2715 }
2716
2717 /*
2718 * Get handle to this zpool's BE container dataset.
2719 */
2720 if ((zhp = zfs_open(g_zfs, be_container_ds, ZFS_TYPE_FILESYSTEM)) ==
2721 NULL) {
2722 be_print_err(gettext("be_zpool_find_current_be_callback: "
2723 "failed to open BE container dataset (%s)\n"),
2724 be_container_ds);
2725 zpool_close(zlp);
2726 return (0);
2727 }
2728
2729 /*
2730 * Iterate through all potential BEs in this zpool
2731 */
2732 if (zfs_iter_filesystems(zhp, be_zfs_find_current_be_callback, bt)) {
2733 /*
2734 * Found current BE dataset; set obe_zpool
2735 */
2736 if ((bt->obe_zpool = strdup(zpool)) == NULL) {
2737 be_print_err(gettext(
2738 "be_zpool_find_current_be_callback: "
2739 "memory allocation failed\n"));
2740 ZFS_CLOSE(zhp);
2741 zpool_close(zlp);
2742 return (0);
2743 }
2744
2745 ZFS_CLOSE(zhp);
2746 zpool_close(zlp);
2747 return (1);
2748 }
2749
2750 ZFS_CLOSE(zhp);
2751 zpool_close(zlp);
2752
2753 return (0);
2754 }
2755
2756 /*
2757 * Function: be_zfs_find_current_be_callback
2758 * Description: Callback function used to iterate through all BEs in a
2759 * pool to find the BE that is the currently booted BE.
2760 * Parameters:
2761 * zhp - zfs_handle_t pointer to current filesystem being checked.
2762 * data - be_transaction-data_t pointer
2763 * Upon successfully finding the current BE, the
2764 * obe_name and obe_root_ds members of this parameter
2765 * are set to the BE name and BE's root dataset
2766 * respectively.
2767 * Return:
2768 * 1 - Found current BE.
2769 * 0 - Did not find current BE.
2770 * Scope:
2771 * Semi-private (library wide use only)
2772 */
2773 int
2774 be_zfs_find_current_be_callback(zfs_handle_t *zhp, void *data)
2775 {
2776 be_transaction_data_t *bt = data;
2777 char *mp = NULL;
2778
2779 /*
2780 * Check if dataset is mounted, and if so where.
2781 */
2782 if (zfs_is_mounted(zhp, &mp)) {
2783 /*
2784 * If mounted at root, set obe_root_ds and obe_name
2785 */
2786 if (mp != NULL && strcmp(mp, "/") == 0) {
2787 free(mp);
2788
2789 if ((bt->obe_root_ds = strdup(zfs_get_name(zhp)))
2790 == NULL) {
2791 be_print_err(gettext(
2792 "be_zfs_find_current_be_callback: "
2793 "memory allocation failed\n"));
2794 ZFS_CLOSE(zhp);
2795 return (0);
2796 }
2797
2798 if ((bt->obe_name = strdup(basename(bt->obe_root_ds)))
2799 == NULL) {
2800 be_print_err(gettext(
2801 "be_zfs_find_current_be_callback: "
2802 "memory allocation failed\n"));
2803 ZFS_CLOSE(zhp);
2804 return (0);
2805 }
2806
2807 ZFS_CLOSE(zhp);
2808 return (1);
2809 }
2810
2811 free(mp);
2812 }
2813 ZFS_CLOSE(zhp);
2814
2815 return (0);
2816 }
2817
2818 /*
2819 * Function: be_check_be_roots_callback
2820 * Description: This function checks whether or not the dataset name passed
2821 * is hierachically located under the BE root container dataset
2822 * for this pool.
2823 * Parameters:
2824 * zlp - zpool_handle_t pointer to current pool being processed.
2825 * data - name of dataset to check
2826 * Returns:
2827 * 0 - dataset is not in this pool's BE root container dataset
2828 * 1 - dataset is in this pool's BE root container dataset
2829 * Scope:
2830 * Semi-private (library wide use only)
2831 */
2832 int
2833 be_check_be_roots_callback(zpool_handle_t *zlp, void *data)
2834 {
2835 const char *zpool = zpool_get_name(zlp);
2836 char *ds = data;
2837 char be_container_ds[MAXPATHLEN];
2838
2839 /* Generate string for this pool's BE root container dataset */
2840 be_make_container_ds(zpool, be_container_ds, sizeof (be_container_ds));
2841
2842 /*
2843 * If dataset lives under the BE root container dataset
2844 * of this pool, return failure.
2845 */
2846 if (strncmp(be_container_ds, ds, strlen(be_container_ds)) == 0 &&
2847 ds[strlen(be_container_ds)] == '/') {
2848 zpool_close(zlp);
2849 return (1);
2850 }
2851
2852 zpool_close(zlp);
2853 return (0);
2854 }
2855
2856 /*
2857 * Function: zfs_err_to_be_err
2858 * Description: This function takes the error stored in the libzfs handle
2859 * and maps it to an be_errno_t. If there are no matching
2860 * be_errno_t's then BE_ERR_ZFS is returned.
2861 * Paramters:
2862 * zfsh - The libzfs handle containing the error we're looking up.
2863 * Returns:
2864 * be_errno_t
2865 * Scope:
2866 * Semi-private (library wide use only)
2867 */
2868 int
2869 zfs_err_to_be_err(libzfs_handle_t *zfsh)
2870 {
2871 int err = libzfs_errno(zfsh);
2872
2873 switch (err) {
2874 case 0:
2875 return (BE_SUCCESS);
2876 case EZFS_PERM:
2877 return (BE_ERR_PERM);
2878 case EZFS_INTR:
2879 return (BE_ERR_INTR);
2880 case EZFS_NOENT:
2881 return (BE_ERR_NOENT);
2882 case EZFS_NOSPC:
2883 return (BE_ERR_NOSPC);
2884 case EZFS_MOUNTFAILED:
2885 return (BE_ERR_MOUNT);
2886 case EZFS_UMOUNTFAILED:
2887 return (BE_ERR_UMOUNT);
2888 case EZFS_EXISTS:
2889 return (BE_ERR_BE_EXISTS);
2890 case EZFS_BUSY:
2891 return (BE_ERR_DEV_BUSY);
2892 case EZFS_POOLREADONLY:
2893 return (BE_ERR_ROFS);
2894 case EZFS_NAMETOOLONG:
2895 return (BE_ERR_NAMETOOLONG);
2896 case EZFS_NODEVICE:
2897 return (BE_ERR_NODEV);
2898 case EZFS_POOL_INVALARG:
2899 return (BE_ERR_INVAL);
2900 case EZFS_PROPTYPE:
2901 return (BE_ERR_INVALPROP);
2902 case EZFS_BADTYPE:
2903 return (BE_ERR_DSTYPE);
2904 case EZFS_PROPNONINHERIT:
2905 return (BE_ERR_NONINHERIT);
2906 case EZFS_PROPREADONLY:
2907 return (BE_ERR_READONLYPROP);
2908 case EZFS_RESILVERING:
2909 case EZFS_POOLUNAVAIL:
2910 return (BE_ERR_UNAVAIL);
2911 case EZFS_DSREADONLY:
2912 return (BE_ERR_READONLYDS);
2913 default:
2914 return (BE_ERR_ZFS);
2915 }
2916 }
2917
2918 /*
2919 * Function: errno_to_be_err
2920 * Description: This function takes an errno and maps it to an be_errno_t.
2921 * If there are no matching be_errno_t's then BE_ERR_UNKNOWN is
2922 * returned.
2923 * Paramters:
2924 * err - The errno we're compairing against.
2925 * Returns:
2926 * be_errno_t
2927 * Scope:
2928 * Semi-private (library wide use only)
2929 */
2930 int
2931 errno_to_be_err(int err)
2932 {
2933 switch (err) {
2934 case EPERM:
2935 return (BE_ERR_PERM);
2936 case EACCES:
2937 return (BE_ERR_ACCESS);
2938 case ECANCELED:
2939 return (BE_ERR_CANCELED);
2940 case EINTR:
2941 return (BE_ERR_INTR);
2942 case ENOENT:
2943 return (BE_ERR_NOENT);
2944 case ENOSPC:
2945 case EDQUOT:
2946 return (BE_ERR_NOSPC);
2947 case EEXIST:
2948 return (BE_ERR_BE_EXISTS);
2949 case EBUSY:
2950 return (BE_ERR_BUSY);
2951 case EROFS:
2952 return (BE_ERR_ROFS);
2953 case ENAMETOOLONG:
2954 return (BE_ERR_NAMETOOLONG);
2955 case ENXIO:
2956 return (BE_ERR_NXIO);
2957 case EINVAL:
2958 return (BE_ERR_INVAL);
2959 case EFAULT:
2960 return (BE_ERR_FAULT);
2961 default:
2962 return (BE_ERR_UNKNOWN);
2963 }
2964 }
2965
2966 /*
2967 * Function: be_err_to_str
2968 * Description: This function takes a be_errno_t and maps it to a message.
2969 * If there are no matching be_errno_t's then NULL is returned.
2970 * Paramters:
2971 * be_errno_t - The be_errno_t we're mapping.
2972 * Returns:
2973 * string or NULL if the error code is not known.
2974 * Scope:
2975 * Semi-private (library wide use only)
2976 */
2977 char *
2978 be_err_to_str(int err)
2979 {
2980 switch (err) {
2981 case BE_ERR_ACCESS:
2982 return (gettext("Permission denied."));
2983 case BE_ERR_ACTIVATE_CURR:
2984 return (gettext("Activation of current BE failed."));
2985 case BE_ERR_AUTONAME:
2986 return (gettext("Auto naming failed."));
2987 case BE_ERR_BE_NOENT:
2988 return (gettext("No such BE."));
2989 case BE_ERR_BUSY:
2990 return (gettext("Mount busy."));
2991 case BE_ERR_DEV_BUSY:
2992 return (gettext("Device busy."));
2993 case BE_ERR_CANCELED:
2994 return (gettext("Operation canceled."));
2995 case BE_ERR_CLONE:
2996 return (gettext("BE clone failed."));
2997 case BE_ERR_COPY:
2998 return (gettext("BE copy failed."));
2999 case BE_ERR_CREATDS:
3000 return (gettext("Dataset creation failed."));
3001 case BE_ERR_CURR_BE_NOT_FOUND:
3002 return (gettext("Can't find current BE."));
3003 case BE_ERR_DESTROY:
3004 return (gettext("Failed to destroy BE or snapshot."));
3005 case BE_ERR_DESTROY_CURR_BE:
3006 return (gettext("Cannot destroy current BE."));
3007 case BE_ERR_DEMOTE:
3008 return (gettext("BE demotion failed."));
3009 case BE_ERR_DSTYPE:
3010 return (gettext("Invalid dataset type."));
3011 case BE_ERR_BE_EXISTS:
3012 return (gettext("BE exists."));
3013 case BE_ERR_INIT:
3014 return (gettext("be_zfs_init failed."));
3015 case BE_ERR_INTR:
3016 return (gettext("Interupted system call."));
3017 case BE_ERR_INVAL:
3018 return (gettext("Invalid argument."));
3019 case BE_ERR_INVALPROP:
3020 return (gettext("Invalid property for dataset."));
3021 case BE_ERR_INVALMOUNTPOINT:
3022 return (gettext("Unexpected mountpoint."));
3023 case BE_ERR_MOUNT:
3024 return (gettext("Mount failed."));
3025 case BE_ERR_MOUNTED:
3026 return (gettext("Already mounted."));
3027 case BE_ERR_NAMETOOLONG:
3028 return (gettext("name > BUFSIZ."));
3029 case BE_ERR_NOENT:
3030 return (gettext("Doesn't exist."));
3031 case BE_ERR_POOL_NOENT:
3032 return (gettext("No such pool."));
3033 case BE_ERR_NODEV:
3034 return (gettext("No such device."));
3035 case BE_ERR_NOTMOUNTED:
3036 return (gettext("File system not mounted."));
3037 case BE_ERR_NOMEM:
3038 return (gettext("Not enough memory."));
3039 case BE_ERR_NONINHERIT:
3040 return (gettext(
3041 "Property is not inheritable for the BE dataset."));
3042 case BE_ERR_NXIO:
3043 return (gettext("No such device or address."));
3044 case BE_ERR_NOSPC:
3045 return (gettext("No space on device."));
3046 case BE_ERR_NOTSUP:
3047 return (gettext("Operation not supported."));
3048 case BE_ERR_OPEN:
3049 return (gettext("Open failed."));
3050 case BE_ERR_PERM:
3051 return (gettext("Not owner."));
3052 case BE_ERR_UNAVAIL:
3053 return (gettext("The BE is currently unavailable."));
3054 case BE_ERR_PROMOTE:
3055 return (gettext("BE promotion failed."));
3056 case BE_ERR_ROFS:
3057 return (gettext("Read only file system."));
3058 case BE_ERR_READONLYDS:
3059 return (gettext("Read only dataset."));
3060 case BE_ERR_READONLYPROP:
3061 return (gettext("Read only property."));
3062 case BE_ERR_RENAME_ACTIVE:
3063 return (gettext("Renaming the active BE is not supported."));
3064 case BE_ERR_SS_EXISTS:
3065 return (gettext("Snapshot exists."));
3066 case BE_ERR_SS_NOENT:
3067 return (gettext("No such snapshot."));
3068 case BE_ERR_UMOUNT:
3069 return (gettext("Unmount failed."));
3070 case BE_ERR_UMOUNT_CURR_BE:
3071 return (gettext("Can't unmount the current BE."));
3072 case BE_ERR_UMOUNT_SHARED:
3073 return (gettext("Unmount of a shared File System failed."));
3074 case BE_ERR_FAULT:
3075 return (gettext("Bad address."));
3076 case BE_ERR_UNKNOWN:
3077 return (gettext("Unknown error."));
3078 case BE_ERR_ZFS:
3079 return (gettext("ZFS returned an error."));
3080 case BE_ERR_GEN_UUID:
3081 return (gettext("Failed to generate uuid."));
3082 case BE_ERR_PARSE_UUID:
3083 return (gettext("Failed to parse uuid."));
3084 case BE_ERR_NO_UUID:
3085 return (gettext("No uuid"));
3086 case BE_ERR_ZONE_NO_PARENTBE:
3087 return (gettext("No parent uuid"));
3088 case BE_ERR_ZONE_MULTIPLE_ACTIVE:
3089 return (gettext("Multiple active zone roots"));
3090 case BE_ERR_ZONE_NO_ACTIVE_ROOT:
3091 return (gettext("No active zone root"));
3092 case BE_ERR_ZONE_ROOT_NOT_LEGACY:
3093 return (gettext("Zone root not legacy"));
3094 case BE_ERR_MOUNT_ZONEROOT:
3095 return (gettext("Failed to mount a zone root."));
3096 case BE_ERR_UMOUNT_ZONEROOT:
3097 return (gettext("Failed to unmount a zone root."));
3098 case BE_ERR_NO_MOUNTED_ZONE:
3099 return (gettext("Zone is not mounted"));
3100 case BE_ERR_ZONES_UNMOUNT:
3101 return (gettext("Unable to unmount a zone BE."));
3102 case BE_ERR_NO_MENU:
3103 return (gettext("Missing boot menu file."));
3104 case BE_ERR_BAD_MENU_PATH:
3105 return (gettext("Invalid path for menu.lst file"));
3106 case BE_ERR_ZONE_SS_EXISTS:
3107 return (gettext("Zone snapshot exists."));
3108 case BE_ERR_BOOTFILE_INST:
3109 return (gettext("Error installing boot files."));
3110 case BE_ERR_EXTCMD:
3111 return (gettext("Error running an external command."));
3112 default:
3113 return (NULL);
3114 }
3115 }
3116
3117 /*
3118 * Function: be_has_grub
3119 * Description: Boolean function indicating whether the current system
3120 * uses grub.
3121 * Return: B_FALSE - the system does not have grub
3122 * B_TRUE - the system does have grub.
3123 * Scope:
3124 * Semi-private (library wide use only)
3125 */
3126 boolean_t
3127 be_has_grub(void)
3128 {
3129 static struct be_defaults be_defaults;
3130 static boolean_t be_deflts_set = B_FALSE;
3131
3132 /* Cache the defaults, because be_has_grub is used often. */
3133 if (be_deflts_set == B_FALSE) {
3134 be_get_defaults(&be_defaults);
3135 be_deflts_set = B_TRUE;
3136 }
3137
3138 return (be_defaults.be_deflt_grub);
3139 }
3140
3141 /*
3142 * Function: be_is_isa
3143 * Description: Boolean function indicating whether the instruction set
3144 * architecture of the executing system matches the name provided.
3145 * The string must match a system defined architecture (e.g.
3146 * "i386", "sparc") and is case sensitive.
3147 * Parameters: name - string representing the name of instruction set
3148 * architecture being tested
3149 * Returns: B_FALSE - the system instruction set architecture is different
3150 * from the one specified
3151 * B_TRUE - the system instruction set architecture is the same
3152 * as the one specified
3153 * Scope:
3154 * Semi-private (library wide use only)
3155 */
3156 boolean_t
3157 be_is_isa(char *name)
3158 {
3159 return ((strcmp((char *)be_get_default_isa(), name) == 0));
3160 }
3161
3162 /*
3163 * Function: be_get_default_isa
3164 * Description:
3165 * Returns the default instruction set architecture of the
3166 * machine it is executed on. (eg. sparc, i386, ...)
3167 * NOTE: SYS_INST environment variable may override default
3168 * return value
3169 * Parameters:
3170 * none
3171 * Returns:
3172 * NULL - the architecture returned by sysinfo() was too
3173 * long for local variables
3174 * char * - pointer to a string containing the default
3175 * implementation
3176 * Scope:
3177 * Semi-private (library wide use only)
3178 */
3179 char *
3180 be_get_default_isa(void)
3181 {
3182 int i;
3183 char *envp;
3184 static char default_inst[ARCH_LENGTH] = "";
3185
3186 if (default_inst[0] == '\0') {
3187 if ((envp = getenv("SYS_INST")) != NULL) {
3188 if ((int)strlen(envp) >= ARCH_LENGTH)
3189 return (NULL);
3190 else
3191 (void) strcpy(default_inst, envp);
3192 } else {
3193 i = sysinfo(SI_ARCHITECTURE, default_inst, ARCH_LENGTH);
3194 if (i < 0 || i > ARCH_LENGTH)
3195 return (NULL);
3196 }
3197 }
3198 return (default_inst);
3199 }
3200
3201 /*
3202 * Function: be_get_platform
3203 * Description:
3204 * Returns the platfom name
3205 * Parameters:
3206 * none
3207 * Returns:
3208 * NULL - the platform name returned by sysinfo() was too
3209 * long for local variables
3210 * char * - pointer to a string containing the platform name
3211 * Scope:
3212 * Semi-private (library wide use only)
3213 */
3214 char *
3215 be_get_platform(void)
3216 {
3217 int i;
3218 static char default_inst[ARCH_LENGTH] = "";
3219
3220 if (default_inst[0] == '\0') {
3221 i = sysinfo(SI_PLATFORM, default_inst, ARCH_LENGTH);
3222 if (i < 0 || i > ARCH_LENGTH)
3223 return (NULL);
3224 }
3225 return (default_inst);
3226 }
3227
3228 /*
3229 * Function: be_run_cmd
3230 * Description:
3231 * Runs a command in a separate subprocess. Splits out stdout from stderr
3232 * and sends each to its own buffer. Buffers must be pre-allocated and
3233 * passed in as arguments. Buffer sizes are also passed in as arguments.
3234 *
3235 * Notes / caveats:
3236 * - Command being run is assumed to not have any stdout or stderr
3237 * redirection.
3238 * - Commands which emit total stderr output of greater than PIPE_BUF
3239 * bytes can hang. For such commands, a different implementation
3240 * which uses poll(2) must be used.
3241 * - stdout_buf can be NULL. In this case, stdout_bufsize is ignored, and
3242 * the stream which would have gone to it is sent to the bit
3243 * bucket.
3244 * - stderr_buf cannot be NULL.
3245 * - Only subprocess errors are appended to the stderr_buf. Errors
3246 * running the command are reported through be_print_err().
3247 * - Data which would overflow its respective buffer is sent to the bit
3248 * bucket.
3249 *
3250 * Parameters:
3251 * command: command to run. Assumed not to have embedded stdout
3252 * or stderr redirection. May have stdin redirection,
3253 * however.
3254 * stderr_buf: buffer returning subprocess stderr data. Errors
3255 * reported by this function are reported through
3256 * be_print_err().
3257 * stderr_bufsize: size of stderr_buf
3258 * stdout_buf: buffer returning subprocess stdout data.
3259 * stdout_bufsize: size of stdout_buf
3260 * Returns:
3261 * BE_SUCCESS - The command ran successfully without returning
3262 * errors.
3263 * BE_ERR_EXTCMD
3264 * - The command could not be run.
3265 * - The command terminated with error status.
3266 * - There were errors extracting or returning subprocess
3267 * data.
3268 * BE_ERR_NOMEM - The command exceeds the command buffer size.
3269 * BE_ERR_INVAL - An invalid argument was specified.
3270 * Scope:
3271 * Semi-private (library wide use only)
3272 */
3273 int
3274 be_run_cmd(char *command, char *stderr_buf, int stderr_bufsize,
3275 char *stdout_buf, int stdout_bufsize)
3276 {
3277 char *temp_filename = strdup(tmpnam(NULL));
3278 FILE *stdout_str = NULL;
3279 FILE *stderr_str = NULL;
3280 char cmdline[BUFSIZ];
3281 char oneline[BUFSIZ];
3282 int exit_status;
3283 int rval = BE_SUCCESS;
3284
3285 if ((command == NULL) || (stderr_buf == NULL) ||
3286 (stderr_bufsize <= 0) || (stdout_bufsize < 0) ||
3287 ((stdout_buf != NULL) ^ (stdout_bufsize != 0))) {
3288 return (BE_ERR_INVAL);
3289 }
3290
3291 /* Set up command so popen returns stderr, not stdout */
3292 if (snprintf(cmdline, BUFSIZ, "%s 2> %s", command,
3293 temp_filename) >= BUFSIZ) {
3294 rval = BE_ERR_NOMEM;
3295 goto cleanup;
3296 }
3297
3298 /* Set up the fifo that will make stderr available. */
3299 if (mkfifo(temp_filename, 0600) != 0) {
3300 (void) be_print_err(gettext("be_run_cmd: mkfifo: %s\n"),
3301 strerror(errno));
3302 rval = BE_ERR_EXTCMD;
3303 goto cleanup;
3304 }
3305
3306 if ((stdout_str = popen(cmdline, "r")) == NULL) {
3307 (void) be_print_err(gettext("be_run_cmd: popen: %s\n"),
3308 strerror(errno));
3309 rval = BE_ERR_EXTCMD;
3310 goto cleanup;
3311 }
3312
3313 if ((stderr_str = fopen(temp_filename, "r")) == NULL) {
3314 (void) be_print_err(gettext("be_run_cmd: fopen: %s\n"),
3315 strerror(errno));
3316 (void) pclose(stdout_str);
3317 rval = BE_ERR_EXTCMD;
3318 goto cleanup;
3319 }
3320
3321 /* Read stdout first, as it usually outputs more than stderr. */
3322 oneline[BUFSIZ-1] = '\0';
3323 while (fgets(oneline, BUFSIZ-1, stdout_str) != NULL) {
3324 if (stdout_str != NULL) {
3325 (void) strlcat(stdout_buf, oneline, stdout_bufsize);
3326 }
3327 }
3328
3329 while (fgets(oneline, BUFSIZ-1, stderr_str) != NULL) {
3330 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3331 }
3332
3333 /* Close pipe, get exit status. */
3334 if ((exit_status = pclose(stdout_str)) == -1) {
3335 (void) be_print_err(gettext("be_run_cmd: pclose: %s\n"),
3336 strerror(errno));
3337 rval = BE_ERR_EXTCMD;
3338 } else if (WIFEXITED(exit_status)) {
3339 exit_status = (int)((char)WEXITSTATUS(exit_status));
3340 /*
3341 * error code BC_NOUPDT means more recent version
3342 * is installed
3343 */
3344 if (exit_status != BC_SUCCESS && exit_status != BC_NOUPDT) {
3345 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: "
3346 "command terminated with error status: %d\n"),
3347 exit_status);
3348 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3349 rval = BE_ERR_EXTCMD;
3350 }
3351 } else {
3352 (void) snprintf(oneline, BUFSIZ, gettext("be_run_cmd: command "
3353 "terminated on signal: %s\n"),
3354 strsignal(WTERMSIG(exit_status)));
3355 (void) strlcat(stderr_buf, oneline, stderr_bufsize);
3356 rval = BE_ERR_EXTCMD;
3357 }
3358
3359 cleanup:
3360 (void) unlink(temp_filename);
3361 (void) free(temp_filename);
3362
3363 return (rval);
3364 }
3365
3366 /* ******************************************************************** */
3367 /* Private Functions */
3368 /* ******************************************************************** */
3369
3370 /*
3371 * Function: update_dataset
3372 * Description: This function takes a dataset name and replaces the zpool
3373 * and be_name components of the dataset with the new be_name
3374 * zpool passed in.
3375 * Parameters:
3376 * dataset - name of dataset
3377 * dataset_len - lenth of buffer in which dataset is passed in.
3378 * be_name - name of new BE name to update to.
3379 * old_rc_loc - dataset under which the root container dataset
3380 * for the old BE lives.
3381 * new_rc_loc - dataset under which the root container dataset
3382 * for the new BE lives.
3383 * Returns:
3384 * BE_SUCCESS - Success
3385 * be_errno_t - Failure
3386 * Scope:
3387 * Private
3388 */
3389 static int
3390 update_dataset(char *dataset, int dataset_len, char *be_name,
3391 char *old_rc_loc, char *new_rc_loc)
3392 {
3393 char *ds = NULL;
3394 char *sub_ds = NULL;
3395
3396 /* Tear off the BE container dataset */
3397 if ((ds = be_make_name_from_ds(dataset, old_rc_loc)) == NULL) {
3398 return (BE_ERR_INVAL);
3399 }
3400
3401 /* Get dataset name relative to BE root, if there is one */
3402 sub_ds = strchr(ds, '/');
3403
3404 /* Generate the BE root dataset name */
3405 be_make_root_ds(new_rc_loc, be_name, dataset, dataset_len);
3406
3407 /* If a subordinate dataset name was found, append it */
3408 if (sub_ds != NULL)
3409 (void) strlcat(dataset, sub_ds, dataset_len);
3410
3411 free(ds);
3412 return (BE_SUCCESS);
3413 }
3414
3415 /*
3416 * Function: _update_vfstab
3417 * Description: This function updates a vfstab file to reflect the new
3418 * root container dataset location and be_name for all
3419 * entries listed in the be_fs_list_data_t structure passed in.
3420 * Parameters:
3421 * vfstab - vfstab file to modify
3422 * be_name - name of BE to update.
3423 * old_rc_loc - dataset under which the root container dataset
3424 * of the old BE resides in.
3425 * new_rc_loc - dataset under which the root container dataset
3426 * of the new BE resides in.
3427 * fld - be_fs_list_data_t pointer providing the list of
3428 * file systems to look for in vfstab.
3429 * Returns:
3430 * BE_SUCCESS - Success
3431 * be_errno_t - Failure
3432 * Scope:
3433 * Private
3434 */
3435 static int
3436 _update_vfstab(char *vfstab, char *be_name, char *old_rc_loc,
3437 char *new_rc_loc, be_fs_list_data_t *fld)
3438 {
3439 struct vfstab vp;
3440 char *tmp_vfstab = NULL;
3441 char comments_buf[BUFSIZ];
3442 FILE *comments = NULL;
3443 FILE *vfs_ents = NULL;
3444 FILE *tfile = NULL;
3445 struct stat sb;
3446 char dev[MAXPATHLEN];
3447 char *c;
3448 int fd;
3449 int ret = BE_SUCCESS, err = 0;
3450 int i;
3451 int tmp_vfstab_len = 0;
3452
3453 errno = 0;
3454
3455 /*
3456 * Open vfstab for reading twice. First is for comments,
3457 * second is for actual entries.
3458 */
3459 if ((comments = fopen(vfstab, "r")) == NULL ||
3460 (vfs_ents = fopen(vfstab, "r")) == NULL) {
3461 err = errno;
3462 be_print_err(gettext("_update_vfstab: "
3463 "failed to open vfstab (%s): %s\n"), vfstab,
3464 strerror(err));
3465 ret = errno_to_be_err(err);
3466 goto cleanup;
3467 }
3468
3469 /* Grab the stats of the original vfstab file */
3470 if (stat(vfstab, &sb) != 0) {
3471 err = errno;
3472 be_print_err(gettext("_update_vfstab: "
3473 "failed to stat file %s: %s\n"), vfstab,
3474 strerror(err));
3475 ret = errno_to_be_err(err);
3476 goto cleanup;
3477 }
3478
3479 /* Create tmp file for modified vfstab */
3480 if ((tmp_vfstab = (char *)malloc(strlen(vfstab) + 7))
3481 == NULL) {
3482 be_print_err(gettext("_update_vfstab: "
3483 "malloc failed\n"));
3484 ret = BE_ERR_NOMEM;
3485 goto cleanup;
3486 }
3487 tmp_vfstab_len = strlen(vfstab) + 7;
3488 (void) memset(tmp_vfstab, 0, tmp_vfstab_len);
3489 (void) strlcpy(tmp_vfstab, vfstab, tmp_vfstab_len);
3490 (void) strlcat(tmp_vfstab, "XXXXXX", tmp_vfstab_len);
3491 if ((fd = mkstemp(tmp_vfstab)) == -1) {
3492 err = errno;
3493 be_print_err(gettext("_update_vfstab: "
3494 "mkstemp failed: %s\n"), strerror(err));
3495 ret = errno_to_be_err(err);
3496 goto cleanup;
3497 }
3498 if ((tfile = fdopen(fd, "w")) == NULL) {
3499 err = errno;
3500 be_print_err(gettext("_update_vfstab: "
3501 "could not open file for write\n"));
3502 (void) close(fd);
3503 ret = errno_to_be_err(err);
3504 goto cleanup;
3505 }
3506
3507 while (fgets(comments_buf, BUFSIZ, comments)) {
3508 for (c = comments_buf; *c != '\0' && isspace(*c); c++)
3509 ;
3510 if (*c == '\0') {
3511 continue;
3512 } else if (*c == '#') {
3513 /*
3514 * If line is a comment line, just put
3515 * it through to the tmp vfstab.
3516 */
3517 (void) fputs(comments_buf, tfile);
3518 } else {
3519 /*
3520 * Else line is a vfstab entry, grab it
3521 * into a vfstab struct.
3522 */
3523 if (getvfsent(vfs_ents, &vp) != 0) {
3524 err = errno;
3525 be_print_err(gettext("_update_vfstab: "
3526 "getvfsent failed: %s\n"), strerror(err));
3527 ret = errno_to_be_err(err);
3528 goto cleanup;
3529 }
3530
3531 if (vp.vfs_special == NULL || vp.vfs_mountp == NULL) {
3532 (void) putvfsent(tfile, &vp);
3533 continue;
3534 }
3535
3536 /*
3537 * If the entry is one of the entries in the list
3538 * of file systems to update, modify it's device
3539 * field to be correct for this BE.
3540 */
3541 for (i = 0; i < fld->fs_num; i++) {
3542 if (strcmp(vp.vfs_special, fld->fs_list[i])
3543 == 0) {
3544 /*
3545 * Found entry that needs an update.
3546 * Replace the root container dataset
3547 * location and be_name in the
3548 * entry's device.
3549 */
3550 (void) strlcpy(dev, vp.vfs_special,
3551 sizeof (dev));
3552
3553 if ((ret = update_dataset(dev,
3554 sizeof (dev), be_name, old_rc_loc,
3555 new_rc_loc)) != 0) {
3556 be_print_err(
3557 gettext("_update_vfstab: "
3558 "Failed to update device "
3559 "field for vfstab entry "
3560 "%s\n"), fld->fs_list[i]);
3561 goto cleanup;
3562 }
3563
3564 vp.vfs_special = dev;
3565 break;
3566 }
3567 }
3568
3569 /* Put entry through to tmp vfstab */
3570 (void) putvfsent(tfile, &vp);
3571 }
3572 }
3573
3574 (void) fclose(comments);
3575 comments = NULL;
3576 (void) fclose(vfs_ents);
3577 vfs_ents = NULL;
3578 (void) fclose(tfile);
3579 tfile = NULL;
3580
3581 /* Copy tmp vfstab into place */
3582 if (rename(tmp_vfstab, vfstab) != 0) {
3583 err = errno;
3584 be_print_err(gettext("_update_vfstab: "
3585 "failed to rename file %s to %s: %s\n"), tmp_vfstab,
3586 vfstab, strerror(err));
3587 ret = errno_to_be_err(err);
3588 goto cleanup;
3589 }
3590
3591 /* Set the perms and ownership of the updated file */
3592 if (chmod(vfstab, sb.st_mode) != 0) {
3593 err = errno;
3594 be_print_err(gettext("_update_vfstab: "
3595 "failed to chmod %s: %s\n"), vfstab, strerror(err));
3596 ret = errno_to_be_err(err);
3597 goto cleanup;
3598 }
3599 if (chown(vfstab, sb.st_uid, sb.st_gid) != 0) {
3600 err = errno;
3601 be_print_err(gettext("_update_vfstab: "
3602 "failed to chown %s: %s\n"), vfstab, strerror(err));
3603 ret = errno_to_be_err(err);
3604 goto cleanup;
3605 }
3606
3607 cleanup:
3608 if (comments != NULL)
3609 (void) fclose(comments);
3610 if (vfs_ents != NULL)
3611 (void) fclose(vfs_ents);
3612 (void) unlink(tmp_vfstab);
3613 (void) free(tmp_vfstab);
3614 if (tfile != NULL)
3615 (void) fclose(tfile);
3616
3617 return (ret);
3618 }
3619
3620
3621 /*
3622 * Function: be_get_auto_name
3623 * Description: Generate an auto name constructed based on the BE name
3624 * of the original BE or zone BE being cloned.
3625 * Parameters:
3626 * obe_name - name of the original BE or zone BE being cloned.
3627 * container_ds - container dataset for the zone.
3628 * Note: if zone_be is false this should be
3629 * NULL.
3630 * zone_be - flag that indicates if we are operating on a zone BE.
3631 * Returns:
3632 * Success - pointer to auto generated BE name. The name
3633 * is allocated in heap storage so the caller is
3634 * responsible for free'ing the name.
3635 * Failure - NULL
3636 * Scope:
3637 * Private
3638 */
3639 static char *
3640 be_get_auto_name(char *obe_name, char *be_container_ds, boolean_t zone_be)
3641 {
3642 be_node_list_t *be_nodes = NULL;
3643 be_node_list_t *cur_be = NULL;
3644 char auto_be_name[MAXPATHLEN];
3645 char base_be_name[MAXPATHLEN];
3646 char cur_be_name[MAXPATHLEN];
3647 char *num_str = NULL;
3648 char *c = NULL;
3649 int num = 0;
3650 int cur_num = 0;
3651
3652 errno = 0;
3653
3654 /*
3655 * Check if obe_name is already in an auto BE name format.
3656 * If it is, then strip off the increment number to get the
3657 * base name.
3658 */
3659 (void) strlcpy(base_be_name, obe_name, sizeof (base_be_name));
3660
3661 if ((num_str = strrchr(base_be_name, BE_AUTO_NAME_DELIM))
3662 != NULL) {
3663 /* Make sure remaining string is all digits */
3664 c = num_str + 1;
3665 while (c[0] != '\0' && isdigit(c[0]))
3666 c++;
3667 /*
3668 * If we're now at the end of the string strip off the
3669 * increment number.
3670 */
3671 if (c[0] == '\0')
3672 num_str[0] = '\0';
3673 }
3674
3675 if (zone_be) {
3676 if (be_container_ds == NULL)
3677 return (NULL);
3678 if (be_get_zone_be_list(obe_name, be_container_ds,
3679 &be_nodes) != BE_SUCCESS) {
3680 be_print_err(gettext("be_get_auto_name: "
3681 "be_get_zone_be_list failed\n"));
3682 return (NULL);
3683 }
3684 } else if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) != BE_SUCCESS) {
3685 be_print_err(gettext("be_get_auto_name: be_list failed\n"));
3686 return (NULL);
3687 }
3688
3689 for (cur_be = be_nodes; cur_be != NULL; cur_be = cur_be->be_next_node) {
3690 (void) strlcpy(cur_be_name, cur_be->be_node_name,
3691 sizeof (cur_be_name));
3692
3693 /* If cur_be_name doesn't match at least base be name, skip. */
3694 if (strncmp(cur_be_name, base_be_name, strlen(base_be_name))
3695 != 0)
3696 continue;
3697
3698 /* Get the string following the base be name */
3699 num_str = cur_be_name + strlen(base_be_name);
3700
3701 /*
3702 * If nothing follows the base be name, this cur_be_name
3703 * is the BE named with the base be name, skip.
3704 */
3705 if (num_str == NULL || num_str[0] == '\0')
3706 continue;
3707
3708 /*
3709 * Remove the name delimiter. If its not there,
3710 * cur_be_name isn't part of this BE name stream, skip.
3711 */
3712 if (num_str[0] == BE_AUTO_NAME_DELIM)
3713 num_str++;
3714 else
3715 continue;
3716
3717 /* Make sure remaining string is all digits */
3718 c = num_str;
3719 while (c[0] != '\0' && isdigit(c[0]))
3720 c++;
3721 if (c[0] != '\0')
3722 continue;
3723
3724 /* Convert the number string to an int */
3725 cur_num = atoi(num_str);
3726
3727 /*
3728 * If failed to convert the string, skip it. If its too
3729 * long to be converted to an int, we wouldn't auto generate
3730 * this number anyway so there couldn't be a conflict.
3731 * We treat it as a manually created BE name.
3732 */
3733 if (cur_num == 0 && errno == EINVAL)
3734 continue;
3735
3736 /*
3737 * Compare current number to current max number,
3738 * take higher of the two.
3739 */
3740 if (cur_num > num)
3741 num = cur_num;
3742 }
3743
3744 /*
3745 * Store off a copy of 'num' incase we need it later. If incrementing
3746 * 'num' causes it to roll over, this means 'num' is the largest
3747 * positive int possible; we'll need it later in the loop to determine
3748 * if we've exhausted all possible increment numbers. We store it in
3749 * 'cur_num'.
3750 */
3751 cur_num = num;
3752
3753 /* Increment 'num' to get new auto BE name number */
3754 if (++num <= 0) {
3755 int ret = 0;
3756
3757 /*
3758 * Since incrementing 'num' caused it to rollover, start
3759 * over at 0 and find the first available number.
3760 */
3761 for (num = 0; num < cur_num; num++) {
3762
3763 (void) snprintf(cur_be_name, sizeof (cur_be_name),
3764 "%s%c%d", base_be_name, BE_AUTO_NAME_DELIM, num);
3765
3766 ret = zpool_iter(g_zfs, be_exists_callback,
3767 cur_be_name);
3768
3769 if (ret == 0) {
3770 /*
3771 * BE name doesn't exist, break out
3772 * to use 'num'.
3773 */
3774 break;
3775 } else if (ret == 1) {
3776 /* BE name exists, continue looking */
3777 continue;
3778 } else {
3779 be_print_err(gettext("be_get_auto_name: "
3780 "zpool_iter failed: %s\n"),
3781 libzfs_error_description(g_zfs));
3782 be_free_list(be_nodes);
3783 return (NULL);
3784 }
3785 }
3786
3787 /*
3788 * If 'num' equals 'cur_num', we've exhausted all possible
3789 * auto BE names for this base BE name.
3790 */
3791 if (num == cur_num) {
3792 be_print_err(gettext("be_get_auto_name: "
3793 "No more available auto BE names for base "
3794 "BE name %s\n"), base_be_name);
3795 be_free_list(be_nodes);
3796 return (NULL);
3797 }
3798 }
3799
3800 be_free_list(be_nodes);
3801
3802 /*
3803 * Generate string for auto BE name.
3804 */
3805 (void) snprintf(auto_be_name, sizeof (auto_be_name), "%s%c%d",
3806 base_be_name, BE_AUTO_NAME_DELIM, num);
3807
3808 if ((c = strdup(auto_be_name)) == NULL) {
3809 be_print_err(gettext("be_get_auto_name: "
3810 "memory allocation failed\n"));
3811 return (NULL);
3812 }
3813
3814 return (c);
3815 }
3816
3817 /*
3818 * Function: be_get_console_prop
3819 * Description: Determine console device.
3820 * Returns:
3821 * Success - pointer to console setting.
3822 * Failure - NULL
3823 * Scope:
3824 * Private
3825 */
3826 static char *
3827 be_get_console_prop(void)
3828 {
3829 di_node_t dn;
3830 char *console = NULL;
3831
3832 if ((dn = di_init("/", DINFOPROP)) == DI_NODE_NIL) {
3833 be_print_err(gettext("be_get_console_prop: "
3834 "di_init() failed\n"));
3835 return (NULL);
3836 }
3837
3838 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3839 "console", &console) != -1) {
3840 di_fini(dn);
3841 return (console);
3842 }
3843
3844 if (console == NULL) {
3845 if (di_prop_lookup_strings(DDI_DEV_T_ANY, dn,
3846 "output-device", &console) != -1) {
3847 di_fini(dn);
3848 if (strncmp(console, "screen", strlen("screen")) == 0)
3849 console = BE_DEFAULT_CONSOLE;
3850 }
3851 }
3852
3853 /*
3854 * Default console to text
3855 */
3856 if (console == NULL) {
3857 console = BE_DEFAULT_CONSOLE;
3858 }
3859
3860 return (console);
3861 }
3862
3863 /*
3864 * Function: be_create_menu
3865 * Description:
3866 * This function is used if no menu.lst file exists. In
3867 * this case a new file is created and if needed default
3868 * lines are added to the file.
3869 * Parameters:
3870 * pool - The name of the pool the menu.lst file is on
3871 * menu_file - The name of the file we're creating.
3872 * menu_fp - A pointer to the file pointer of the file we
3873 * created. This is also used to pass back the file
3874 * pointer to the newly created file.
3875 * mode - the original mode used for the failed attempt to
3876 * non-existent file.
3877 * Returns:
3878 * BE_SUCCESS - Success
3879 * be_errno_t - Failure
3880 * Scope:
3881 * Private
3882 */
3883 static int
3884 be_create_menu(
3885 char *pool,
3886 char *menu_file,
3887 FILE **menu_fp,
3888 char *mode)
3889 {
3890 be_node_list_t *be_nodes = NULL;
3891 char *menu_path = NULL;
3892 char *be_rpool = NULL;
3893 char *be_name = NULL;
3894 char *console = NULL;
3895 errno = 0;
3896
3897 if (menu_file == NULL || menu_fp == NULL || mode == NULL)
3898 return (BE_ERR_INVAL);
3899
3900 menu_path = strdup(menu_file);
3901 if (menu_path == NULL)
3902 return (BE_ERR_NOMEM);
3903
3904 (void) dirname(menu_path);
3905 if (*menu_path == '.') {
3906 free(menu_path);
3907 return (BE_ERR_BAD_MENU_PATH);
3908 }
3909 if (mkdirp(menu_path,
3910 S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
3911 errno != EEXIST) {
3912 be_print_err(gettext("be_create_menu: Failed to create the %s "
3913 "directory: %s\n"), menu_path, strerror(errno));
3914 free(menu_path);
3915 return (errno_to_be_err(errno));
3916 }
3917 free(menu_path);
3918
3919 /*
3920 * Check to see if this system supports grub
3921 */
3922 if (be_has_grub()) {
3923 /*
3924 * The grub menu is missing so we need to create it
3925 * and fill in the first few lines.
3926 */
3927 FILE *temp_fp = fopen(menu_file, "a+");
3928 if (temp_fp == NULL) {
3929 *menu_fp = NULL;
3930 return (errno_to_be_err(errno));
3931 }
3932
3933 if ((console = be_get_console_prop()) != NULL) {
3934
3935 /*
3936 * If console is redirected to serial line,
3937 * GRUB splash screen will not be enabled.
3938 */
3939 if (strncmp(console, "text", strlen("text")) == 0 ||
3940 strncmp(console, "graphics",
3941 strlen("graphics")) == 0) {
3942
3943 (void) fprintf(temp_fp, "%s\n", BE_GRUB_SPLASH);
3944 (void) fprintf(temp_fp, "%s\n",
3945 BE_GRUB_FOREGROUND);
3946 (void) fprintf(temp_fp, "%s\n",
3947 BE_GRUB_BACKGROUND);
3948 (void) fprintf(temp_fp, "%s\n",
3949 BE_GRUB_DEFAULT);
3950 } else {
3951 be_print_err(gettext("be_create_menu: "
3952 "console on serial line, "
3953 "GRUB splash image will be disabled\n"));
3954 }
3955 }
3956
3957 (void) fprintf(temp_fp, "timeout 30\n");
3958 (void) fclose(temp_fp);
3959
3960 } else {
3961 /*
3962 * The menu file doesn't exist so we need to create a
3963 * blank file.
3964 */
3965 FILE *temp_fp = fopen(menu_file, "w+");
3966 if (temp_fp == NULL) {
3967 *menu_fp = NULL;
3968 return (errno_to_be_err(errno));
3969 }
3970 (void) fclose(temp_fp);
3971 }
3972
3973 /*
3974 * Now we need to add all the BE's back into the the file.
3975 */
3976 if (_be_list(NULL, &be_nodes, BE_LIST_DEFAULT) == BE_SUCCESS) {
3977 while (be_nodes != NULL) {
3978 if (strcmp(pool, be_nodes->be_rpool) == 0) {
3979 (void) be_append_menu(be_nodes->be_node_name,
3980 be_nodes->be_rpool, NULL, NULL, NULL);
3981 }
3982 if (be_nodes->be_active_on_boot) {
3983 be_rpool = strdup(be_nodes->be_rpool);
3984 be_name = strdup(be_nodes->be_node_name);
3985 }
3986
3987 be_nodes = be_nodes->be_next_node;
3988 }
3989 }
3990 be_free_list(be_nodes);
3991
3992 /*
3993 * Check to see if this system supports grub
3994 */
3995 if (be_has_grub()) {
3996 int err = be_change_grub_default(be_name, be_rpool);
3997 if (err != BE_SUCCESS)
3998 return (err);
3999 }
4000 *menu_fp = fopen(menu_file, mode);
4001 if (*menu_fp == NULL)
4002 return (errno_to_be_err(errno));
4003
4004 return (BE_SUCCESS);
4005 }
4006
4007 /*
4008 * Function: be_open_menu
4009 * Description:
4010 * This function is used it open the menu.lst file. If this
4011 * file does not exist be_create_menu is called to create it
4012 * and the open file pointer is returned. If the file does
4013 * exist it is simply opened using the mode passed in.
4014 * Parameters:
4015 * pool - The name of the pool the menu.lst file is on
4016 * menu_file - The name of the file we're opening.
4017 * menu_fp - A pointer to the file pointer of the file we're
4018 * opening. This is also used to pass back the file
4019 * pointer.
4020 * mode - the original mode to be used for opening the menu.lst
4021 * file.
4022 * create_menu - If this is true and the menu.lst file does not
4023 * exist we will attempt to re-create it. However
4024 * if it's false the error returned from the fopen
4025 * will be returned.
4026 * Returns:
4027 * BE_SUCCESS - Success
4028 * be_errno_t - Failure
4029 * Scope:
4030 * Private
4031 */
4032 static int
4033 be_open_menu(
4034 char *pool,
4035 char *menu_file,
4036 FILE **menu_fp,
4037 char *mode,
4038 boolean_t create_menu)
4039 {
4040 int err = 0;
4041 boolean_t set_print = B_FALSE;
4042
4043 *menu_fp = fopen(menu_file, mode);
4044 err = errno;
4045 if (*menu_fp == NULL) {
4046 if (err == ENOENT && create_menu) {
4047 be_print_err(gettext("be_open_menu: menu.lst "
4048 "file %s does not exist,\n"), menu_file);
4049 if (!do_print) {
4050 set_print = B_TRUE;
4051 do_print = B_TRUE;
4052 }
4053 be_print_err(gettext("WARNING: menu.lst "
4054 "file %s does not exist,\n generating "
4055 "a new menu.lst file\n"), menu_file);
4056 if (set_print)
4057 do_print = B_FALSE;
4058 err = 0;
4059 if ((err = be_create_menu(pool, menu_file,
4060 menu_fp, mode)) == ENOENT)
4061 return (BE_ERR_NO_MENU);
4062 else if (err != BE_SUCCESS)
4063 return (err);
4064 else if (*menu_fp == NULL)
4065 return (BE_ERR_NO_MENU);
4066 } else {
4067 be_print_err(gettext("be_open_menu: failed "
4068 "to open menu.lst file %s\n"), menu_file);
4069 if (err == ENOENT)
4070 return (BE_ERR_NO_MENU);
4071 else
4072 return (errno_to_be_err(err));
4073 }
4074 }
4075 return (BE_SUCCESS);
4076 }