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