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