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