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