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