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