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