xref: /illumos-gate/usr/src/lib/libbe/common/be_activate.c (revision 9348d232b12debc31fa8e694c517ad8ecfd54bda)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
28  */
29 
30 #include <assert.h>
31 #include <libintl.h>
32 #include <libnvpair.h>
33 #include <libzfs.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <errno.h>
39 #include <sys/mnttab.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include <unistd.h>
44 #include <sys/efi_partition.h>
45 
46 #include <libbe.h>
47 #include <libbe_priv.h>
48 
49 char	*mnttab = MNTTAB;
50 
51 /*
52  * Private function prototypes
53  */
54 static int set_bootfs(char *boot_rpool, char *be_root_ds);
55 static int set_canmount(be_node_list_t *, char *);
56 static boolean_t be_do_installgrub_mbr(char *, nvlist_t *);
57 static int be_do_installgrub_helper(zpool_handle_t *, nvlist_t *, char *,
58     char *);
59 static int be_do_installgrub(be_transaction_data_t *);
60 static int be_get_grub_vers(be_transaction_data_t *, char **, char **);
61 static int get_ver_from_capfile(char *, char **);
62 static int be_promote_zone_ds(char *, char *);
63 static int be_promote_ds_callback(zfs_handle_t *, void *);
64 
65 /* ******************************************************************** */
66 /*			Public Functions				*/
67 /* ******************************************************************** */
68 
69 /*
70  * Function:	be_activate
71  * Description:	Calls _be_activate which activates the BE named in the
72  *		attributes passed in through be_attrs. The process of
73  *		activation sets the bootfs property of the root pool, resets
74  *		the canmount property to noauto, and sets the default in the
75  *		grub menu to the entry corresponding to the entry for the named
76  *		BE.
77  * Parameters:
78  *		be_attrs - pointer to nvlist_t of attributes being passed in.
79  *			The follow attribute values are used by this function:
80  *
81  *			BE_ATTR_ORIG_BE_NAME		*required
82  * Return:
83  *		BE_SUCCESS - Success
84  *		be_errno_t - Failure
85  * Scope:
86  *		Public
87  */
88 int
89 be_activate(nvlist_t *be_attrs)
90 {
91 	int	ret = BE_SUCCESS;
92 	char	*be_name = NULL;
93 
94 	/* Initialize libzfs handle */
95 	if (!be_zfs_init())
96 		return (BE_ERR_INIT);
97 
98 	/* Get the BE name to activate */
99 	if (nvlist_lookup_string(be_attrs, BE_ATTR_ORIG_BE_NAME, &be_name)
100 	    != 0) {
101 		be_print_err(gettext("be_activate: failed to "
102 		    "lookup BE_ATTR_ORIG_BE_NAME attribute\n"));
103 		be_zfs_fini();
104 		return (BE_ERR_INVAL);
105 	}
106 
107 	/* Validate BE name */
108 	if (!be_valid_be_name(be_name)) {
109 		be_print_err(gettext("be_activate: invalid BE name %s\n"),
110 		    be_name);
111 		be_zfs_fini();
112 		return (BE_ERR_INVAL);
113 	}
114 
115 	ret = _be_activate(be_name);
116 
117 	be_zfs_fini();
118 
119 	return (ret);
120 }
121 
122 /* ******************************************************************** */
123 /*			Semi Private Functions				*/
124 /* ******************************************************************** */
125 
126 /*
127  * Function:	_be_activate
128  * Description:	This does the actual work described in be_activate.
129  * Parameters:
130  *		be_name - pointer to the name of BE to activate.
131  *
132  * Return:
133  *		BE_SUCCESS - Success
134  *		be_errnot_t - Failure
135  * Scope:
136  *		Public
137  */
138 int
139 _be_activate(char *be_name)
140 {
141 	be_transaction_data_t cb = { 0 };
142 	zfs_handle_t	*zhp = NULL;
143 	char		root_ds[MAXPATHLEN];
144 	char		active_ds[MAXPATHLEN];
145 	char		*cur_vers = NULL, *new_vers = NULL;
146 	be_node_list_t	*be_nodes = NULL;
147 	uuid_t		uu = {0};
148 	int		entry, ret = BE_SUCCESS;
149 	int		zret = 0;
150 
151 	/*
152 	 * TODO: The BE needs to be validated to make sure that it is actually
153 	 * a bootable BE.
154 	 */
155 
156 	if (be_name == NULL)
157 		return (BE_ERR_INVAL);
158 
159 	/* Set obe_name to be_name in the cb structure */
160 	cb.obe_name = be_name;
161 
162 	/* find which zpool the be is in */
163 	if ((zret = zpool_iter(g_zfs, be_find_zpool_callback, &cb)) == 0) {
164 		be_print_err(gettext("be_activate: failed to "
165 		    "find zpool for BE (%s)\n"), cb.obe_name);
166 		return (BE_ERR_BE_NOENT);
167 	} else if (zret < 0) {
168 		be_print_err(gettext("be_activate: "
169 		    "zpool_iter failed: %s\n"),
170 		    libzfs_error_description(g_zfs));
171 		ret = zfs_err_to_be_err(g_zfs);
172 		return (ret);
173 	}
174 
175 	be_make_root_ds(cb.obe_zpool, cb.obe_name, root_ds, sizeof (root_ds));
176 	cb.obe_root_ds = strdup(root_ds);
177 
178 	if (getzoneid() == GLOBAL_ZONEID) {
179 		if (be_has_grub() && (ret = be_get_grub_vers(&cb, &cur_vers,
180 		    &new_vers)) != BE_SUCCESS) {
181 			be_print_err(gettext("be_activate: failed to get grub "
182 			    "versions from capability files.\n"));
183 			return (ret);
184 		}
185 		if (cur_vers != NULL) {
186 			/*
187 			 * We need to check to see if the version number from
188 			 * the BE being activated is greater than the current
189 			 * one.
190 			 */
191 			if (new_vers != NULL &&
192 			    atof(cur_vers) < atof(new_vers)) {
193 				if ((ret = be_do_installgrub(&cb))
194 				    != BE_SUCCESS) {
195 					free(new_vers);
196 					free(cur_vers);
197 					return (ret);
198 				}
199 				free(new_vers);
200 			}
201 			free(cur_vers);
202 		} else if (new_vers != NULL) {
203 			if ((ret = be_do_installgrub(&cb)) != BE_SUCCESS) {
204 				free(new_vers);
205 				return (ret);
206 			}
207 			free(new_vers);
208 		}
209 		if (!be_has_menu_entry(root_ds, cb.obe_zpool, &entry)) {
210 			if ((ret = be_append_menu(cb.obe_name, cb.obe_zpool,
211 			    NULL, NULL, NULL)) != BE_SUCCESS) {
212 				be_print_err(gettext("be_activate: Failed to "
213 				    "add BE (%s) to the GRUB menu\n"),
214 				    cb.obe_name);
215 				goto done;
216 			}
217 		}
218 		if (be_has_grub()) {
219 			if ((ret = be_change_grub_default(cb.obe_name,
220 			    cb.obe_zpool)) != BE_SUCCESS) {
221 				be_print_err(gettext("be_activate: failed to "
222 				    "change the default entry in menu.lst\n"));
223 				goto done;
224 			}
225 		}
226 	}
227 
228 	if ((ret = _be_list(cb.obe_name, &be_nodes)) != BE_SUCCESS) {
229 		return (ret);
230 	}
231 
232 	if ((ret = set_canmount(be_nodes, "noauto")) != BE_SUCCESS) {
233 		be_print_err(gettext("be_activate: failed to set "
234 		    "canmount dataset property\n"));
235 		goto done;
236 	}
237 
238 	if (getzoneid() == GLOBAL_ZONEID) {
239 		if ((ret = set_bootfs(be_nodes->be_rpool,
240 		    root_ds)) != BE_SUCCESS) {
241 			be_print_err(gettext("be_activate: failed to set "
242 			    "bootfs pool property for %s\n"), root_ds);
243 			goto done;
244 		}
245 	}
246 
247 	if ((zhp = zfs_open(g_zfs, root_ds, ZFS_TYPE_FILESYSTEM)) != NULL) {
248 		/*
249 		 * We don't need to close the zfs handle at this
250 		 * point because The callback funtion
251 		 * be_promote_ds_callback() will close it for us.
252 		 */
253 		if (be_promote_ds_callback(zhp, NULL) != 0) {
254 			be_print_err(gettext("be_activate: "
255 			    "failed to activate the "
256 			    "datasets for %s: %s\n"),
257 			    root_ds,
258 			    libzfs_error_description(g_zfs));
259 			ret = BE_ERR_PROMOTE;
260 			goto done;
261 		}
262 	} else {
263 		be_print_err(gettext("be_activate: failed to open "
264 		    "dataset (%s): %s\n"), root_ds,
265 		    libzfs_error_description(g_zfs));
266 		ret = zfs_err_to_be_err(g_zfs);
267 		goto done;
268 	}
269 
270 	if (getzoneid() == GLOBAL_ZONEID &&
271 	    be_get_uuid(cb.obe_root_ds, &uu) == BE_SUCCESS &&
272 	    (ret = be_promote_zone_ds(cb.obe_name, cb.obe_root_ds))
273 	    != BE_SUCCESS) {
274 		be_print_err(gettext("be_activate: failed to promote "
275 		    "the active zonepath datasets for zones in BE %s\n"),
276 		    cb.obe_name);
277 	}
278 
279 	if (getzoneid() != GLOBAL_ZONEID) {
280 		if (!be_zone_compare_uuids(root_ds)) {
281 			be_print_err(gettext("be_activate: activating zone "
282 			    "root dataset from non-active global BE is not "
283 			    "supported\n"));
284 			ret = BE_ERR_NOTSUP;
285 			goto done;
286 		}
287 		if ((zhp = zfs_open(g_zfs, root_ds,
288 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
289 			be_print_err(gettext("be_activate: failed to open "
290 			    "dataset (%s): %s\n"), root_ds,
291 			    libzfs_error_description(g_zfs));
292 			ret = zfs_err_to_be_err(g_zfs);
293 			goto done;
294 		}
295 		/* Find current active zone root dataset */
296 		if ((ret = be_find_active_zone_root(zhp, cb.obe_zpool,
297 		    active_ds, sizeof (active_ds))) != BE_SUCCESS) {
298 			be_print_err(gettext("be_activate: failed to find "
299 			    "active zone root dataset\n"));
300 			ZFS_CLOSE(zhp);
301 			goto done;
302 		}
303 		/* Do nothing if requested BE is already active */
304 		if (strcmp(root_ds, active_ds) == 0) {
305 			ret = BE_SUCCESS;
306 			ZFS_CLOSE(zhp);
307 			goto done;
308 		}
309 
310 		/* Set active property for BE */
311 		if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "on") != 0) {
312 			be_print_err(gettext("be_activate: failed to set "
313 			    "active property (%s): %s\n"), root_ds,
314 			    libzfs_error_description(g_zfs));
315 			ret = zfs_err_to_be_err(g_zfs);
316 			ZFS_CLOSE(zhp);
317 			goto done;
318 		}
319 		ZFS_CLOSE(zhp);
320 
321 		/* Unset active property for old active root dataset */
322 		if ((zhp = zfs_open(g_zfs, active_ds,
323 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
324 			be_print_err(gettext("be_activate: failed to open "
325 			    "dataset (%s): %s\n"), active_ds,
326 			    libzfs_error_description(g_zfs));
327 			ret = zfs_err_to_be_err(g_zfs);
328 			goto done;
329 		}
330 		if (zfs_prop_set(zhp, BE_ZONE_ACTIVE_PROPERTY, "off") != 0) {
331 			be_print_err(gettext("be_activate: failed to unset "
332 			    "active property (%s): %s\n"), active_ds,
333 			    libzfs_error_description(g_zfs));
334 			ret = zfs_err_to_be_err(g_zfs);
335 			ZFS_CLOSE(zhp);
336 			goto done;
337 		}
338 		ZFS_CLOSE(zhp);
339 	}
340 done:
341 	be_free_list(be_nodes);
342 	return (ret);
343 }
344 
345 /*
346  * Function:	be_activate_current_be
347  * Description:	Set the currently "active" BE to be "active on boot"
348  * Paramters:
349  *		none
350  * Returns:
351  *		BE_SUCCESS - Success
352  *		be_errnot_t - Failure
353  * Scope:
354  *		Semi-private (library wide use only)
355  */
356 int
357 be_activate_current_be(void)
358 {
359 	int ret = BE_SUCCESS;
360 	be_transaction_data_t bt = { 0 };
361 
362 	if ((ret = be_find_current_be(&bt)) != BE_SUCCESS) {
363 		return (ret);
364 	}
365 
366 	if ((ret = _be_activate(bt.obe_name)) != BE_SUCCESS) {
367 		be_print_err(gettext("be_activate_current_be: failed to "
368 		    "activate %s\n"), bt.obe_name);
369 		return (ret);
370 	}
371 
372 	return (BE_SUCCESS);
373 }
374 
375 /*
376  * Function:	be_is_active_on_boot
377  * Description:	Checks if the BE name passed in has the "active on boot"
378  *		property set to B_TRUE.
379  * Paramters:
380  *		be_name - the name of the BE to check
381  * Returns:
382  *		B_TRUE - if active on boot.
383  *		B_FALSE - if not active on boot.
384  * Scope:
385  *		Semi-private (library wide use only)
386  */
387 boolean_t
388 be_is_active_on_boot(char *be_name)
389 {
390 	be_node_list_t *be_node = NULL;
391 
392 	if (be_name == NULL) {
393 		be_print_err(gettext("be_is_active_on_boot: "
394 		    "be_name must not be NULL\n"));
395 		return (B_FALSE);
396 	}
397 
398 	if (_be_list(be_name, &be_node) != BE_SUCCESS) {
399 		return (B_FALSE);
400 	}
401 
402 	if (be_node == NULL) {
403 		return (B_FALSE);
404 	}
405 
406 	if (be_node->be_active_on_boot) {
407 		be_free_list(be_node);
408 		return (B_TRUE);
409 	} else {
410 		be_free_list(be_node);
411 		return (B_FALSE);
412 	}
413 }
414 
415 /* ******************************************************************** */
416 /*			Private Functions				*/
417 /* ******************************************************************** */
418 
419 /*
420  * Function:	set_bootfs
421  * Description:	Sets the bootfs property on the boot pool to be the
422  *		root dataset of the activated BE.
423  * Parameters:
424  *		boot_pool - The pool we're setting bootfs in.
425  *		be_root_ds - The main dataset for the BE.
426  * Return:
427  *		BE_SUCCESS - Success
428  *		be_errno_t - Failure
429  * Scope:
430  *		Private
431  */
432 static int
433 set_bootfs(char *boot_rpool, char *be_root_ds)
434 {
435 	zpool_handle_t *zhp;
436 	int err = BE_SUCCESS;
437 
438 	if ((zhp = zpool_open(g_zfs, boot_rpool)) == NULL) {
439 		be_print_err(gettext("set_bootfs: failed to open pool "
440 		    "(%s): %s\n"), boot_rpool, libzfs_error_description(g_zfs));
441 		err = zfs_err_to_be_err(g_zfs);
442 		return (err);
443 	}
444 
445 	err = zpool_set_prop(zhp, "bootfs", be_root_ds);
446 	if (err) {
447 		be_print_err(gettext("set_bootfs: failed to set "
448 		    "bootfs property for pool %s: %s\n"), boot_rpool,
449 		    libzfs_error_description(g_zfs));
450 		err = zfs_err_to_be_err(g_zfs);
451 		zpool_close(zhp);
452 		return (err);
453 	}
454 
455 	zpool_close(zhp);
456 	return (BE_SUCCESS);
457 }
458 
459 /*
460  * Function:	set_canmount
461  * Description:	Sets the canmount property on the datasets of the
462  *		activated BE.
463  * Parameters:
464  *		be_nodes - The be_node_t returned from be_list
465  *		value - The value of canmount we setting, on|off|noauto.
466  * Return:
467  *		BE_SUCCESS - Success
468  *		be_errno_t - Failure
469  * Scope:
470  *		Private
471  */
472 static int
473 set_canmount(be_node_list_t *be_nodes, char *value)
474 {
475 	char		ds_path[MAXPATHLEN];
476 	zfs_handle_t	*zhp = NULL;
477 	be_node_list_t	*list = be_nodes;
478 	int		err = BE_SUCCESS;
479 
480 	while (list != NULL) {
481 		be_dataset_list_t *datasets = list->be_node_datasets;
482 
483 		be_make_root_ds(list->be_rpool, list->be_node_name, ds_path,
484 		    sizeof (ds_path));
485 
486 		if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET)) ==
487 		    NULL) {
488 			be_print_err(gettext("set_canmount: failed to open "
489 			    "dataset (%s): %s\n"), ds_path,
490 			    libzfs_error_description(g_zfs));
491 			err = zfs_err_to_be_err(g_zfs);
492 			return (err);
493 		}
494 		if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
495 			/*
496 			 * it's already mounted so we can't change the
497 			 * canmount property anyway.
498 			 */
499 			err = BE_SUCCESS;
500 		} else {
501 			err = zfs_prop_set(zhp,
502 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
503 			if (err) {
504 				ZFS_CLOSE(zhp);
505 				be_print_err(gettext("set_canmount: failed to "
506 				    "set dataset property (%s): %s\n"),
507 				    ds_path, libzfs_error_description(g_zfs));
508 				err = zfs_err_to_be_err(g_zfs);
509 				return (err);
510 			}
511 		}
512 		ZFS_CLOSE(zhp);
513 
514 		while (datasets != NULL) {
515 			be_make_root_ds(list->be_rpool,
516 			    datasets->be_dataset_name, ds_path,
517 			    sizeof (ds_path));
518 
519 			if ((zhp = zfs_open(g_zfs, ds_path, ZFS_TYPE_DATASET))
520 			    == NULL) {
521 				be_print_err(gettext("set_canmount: failed to "
522 				    "open dataset %s: %s\n"), ds_path,
523 				    libzfs_error_description(g_zfs));
524 				err = zfs_err_to_be_err(g_zfs);
525 				return (err);
526 			}
527 			if (zfs_prop_get_int(zhp, ZFS_PROP_MOUNTED)) {
528 				/*
529 				 * it's already mounted so we can't change the
530 				 * canmount property anyway.
531 				 */
532 				err = BE_SUCCESS;
533 				ZFS_CLOSE(zhp);
534 				break;
535 			}
536 			err = zfs_prop_set(zhp,
537 			    zfs_prop_to_name(ZFS_PROP_CANMOUNT), value);
538 			if (err) {
539 				ZFS_CLOSE(zhp);
540 				be_print_err(gettext("set_canmount: "
541 				    "Failed to set property value %s "
542 				    "for dataset %s: %s\n"), value, ds_path,
543 				    libzfs_error_description(g_zfs));
544 				err = zfs_err_to_be_err(g_zfs);
545 				return (err);
546 			}
547 			ZFS_CLOSE(zhp);
548 			datasets = datasets->be_next_dataset;
549 		}
550 		list = list->be_next_node;
551 	}
552 	return (err);
553 }
554 
555 /*
556  * Function:	be_get_grub_vers
557  * Description:	Gets the grub version number from /boot/grub/capability. If
558  *              capability file doesn't exist NULL is returned.
559  * Parameters:
560  *              bt - The transaction data for the BE we're getting the grub
561  *                   version for.
562  *              cur_vers - used to return the current version of grub from
563  *                         the root pool.
564  *              new_vers - used to return the grub version of the BE we're
565  *                         activating.
566  * Return:
567  *              BE_SUCCESS - Success
568  *              be_errno_t - Failed to find version
569  * Scope:
570  *		Private
571  */
572 static int
573 be_get_grub_vers(be_transaction_data_t *bt, char **cur_vers, char **new_vers)
574 {
575 	zfs_handle_t	*zhp = NULL;
576 	zfs_handle_t	*pool_zhp = NULL;
577 	int ret = BE_SUCCESS;
578 	char cap_file[MAXPATHLEN];
579 	char *temp_mntpnt = NULL;
580 	char *zpool_mntpt = NULL;
581 	char *ptmp_mntpnt = NULL;
582 	char *orig_mntpnt = NULL;
583 	boolean_t be_mounted = B_FALSE;
584 	boolean_t pool_mounted = B_FALSE;
585 
586 	if (!be_has_grub()) {
587 		be_print_err(gettext("be_get_grub_vers: Not supported on "
588 		    "this architecture\n"));
589 		return (BE_ERR_NOTSUP);
590 	}
591 
592 	if (bt == NULL || bt->obe_name == NULL || bt->obe_zpool == NULL ||
593 	    bt->obe_root_ds == NULL) {
594 		be_print_err(gettext("be_get_grub_vers: Invalid BE\n"));
595 		return (BE_ERR_INVAL);
596 	}
597 
598 	if ((pool_zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
599 	    NULL) {
600 		be_print_err(gettext("be_get_grub_vers: zfs_open failed: %s\n"),
601 		    libzfs_error_description(g_zfs));
602 		return (zfs_err_to_be_err(g_zfs));
603 	}
604 
605 	/*
606 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
607 	 * attempt to mount it.
608 	 */
609 	if ((ret = be_mount_pool(pool_zhp, &ptmp_mntpnt,
610 	    &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
611 		be_print_err(gettext("be_get_grub_vers: pool dataset "
612 		    "(%s) could not be mounted\n"), bt->obe_zpool);
613 		ZFS_CLOSE(pool_zhp);
614 		return (ret);
615 	}
616 
617 	/*
618 	 * Get the mountpoint for the root pool dataset.
619 	 */
620 	if (!zfs_is_mounted(pool_zhp, &zpool_mntpt)) {
621 		be_print_err(gettext("be_get_grub_vers: pool "
622 		    "dataset (%s) is not mounted. Can't set the "
623 		    "default BE in the grub menu.\n"), bt->obe_zpool);
624 		ret = BE_ERR_NO_MENU;
625 		goto cleanup;
626 	}
627 
628 	/*
629 	 * get the version of the most recent grub update.
630 	 */
631 	(void) snprintf(cap_file, sizeof (cap_file), "%s%s",
632 	    zpool_mntpt, BE_CAP_FILE);
633 	free(zpool_mntpt);
634 	zpool_mntpt = NULL;
635 
636 	if ((ret = get_ver_from_capfile(cap_file, cur_vers)) != BE_SUCCESS)
637 		goto cleanup;
638 
639 	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
640 	    NULL) {
641 		be_print_err(gettext("be_get_grub_vers: failed to "
642 		    "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
643 		    libzfs_error_description(g_zfs));
644 		free(cur_vers);
645 		ret = zfs_err_to_be_err(g_zfs);
646 		goto cleanup;
647 	}
648 	if (!zfs_is_mounted(zhp, &temp_mntpnt)) {
649 		if ((ret = _be_mount(bt->obe_name, &temp_mntpnt,
650 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
651 			be_print_err(gettext("be_get_grub_vers: failed to "
652 			    "mount BE (%s)\n"), bt->obe_name);
653 			free(*cur_vers);
654 			*cur_vers = NULL;
655 			ZFS_CLOSE(zhp);
656 			goto cleanup;
657 		}
658 		be_mounted = B_TRUE;
659 	}
660 	ZFS_CLOSE(zhp);
661 
662 	/*
663 	 * Now get the grub version for the BE being activated.
664 	 */
665 	(void) snprintf(cap_file, sizeof (cap_file), "%s%s", temp_mntpnt,
666 	    BE_CAP_FILE);
667 	ret = get_ver_from_capfile(cap_file, new_vers);
668 	if (ret != BE_SUCCESS) {
669 		free(*cur_vers);
670 		*cur_vers = NULL;
671 	}
672 	if (be_mounted)
673 		(void) _be_unmount(bt->obe_name, 0);
674 
675 cleanup:
676 	if (pool_mounted) {
677 		int iret = BE_SUCCESS;
678 		iret = be_unmount_pool(pool_zhp, ptmp_mntpnt, orig_mntpnt);
679 		if (ret == BE_SUCCESS)
680 			ret = iret;
681 		free(orig_mntpnt);
682 		free(ptmp_mntpnt);
683 	}
684 	ZFS_CLOSE(pool_zhp);
685 
686 	free(temp_mntpnt);
687 	return (ret);
688 }
689 
690 /*
691  * Function:	get_ver_from_capfile
692  * Description: Parses the capability file passed in looking for the VERSION
693  *              line. If found the version is returned in vers, if not then
694  *              NULL is returned in vers.
695  *
696  * Parameters:
697  *              file - the path to the capability file we want to parse.
698  *              vers - the version string that will be passed back.
699  * Return:
700  *              BE_SUCCESS - Success
701  *              be_errno_t - Failed to find version
702  * Scope:
703  *		Private
704  */
705 static int
706 get_ver_from_capfile(char *file, char **vers)
707 {
708 	FILE *fp = NULL;
709 	char line[BUFSIZ];
710 	char *last = NULL;
711 	int err = BE_SUCCESS;
712 	errno = 0;
713 
714 	if (!be_has_grub()) {
715 		be_print_err(gettext("get_ver_from_capfile: Not supported "
716 		    "on this architecture\n"));
717 		return (BE_ERR_NOTSUP);
718 	}
719 
720 	/*
721 	 * Set version string to NULL; the only case this shouldn't be set
722 	 * to be NULL is when we've actually found a version in the capability
723 	 * file, which is set below.
724 	 */
725 	*vers = NULL;
726 
727 	/*
728 	 * If the capability file doesn't exist, we're returning success
729 	 * because on older releases, the capability file did not exist
730 	 * so this is a valid scenario.
731 	 */
732 	if (access(file, F_OK) == 0) {
733 		if ((fp = fopen(file, "r")) == NULL) {
734 			err = errno;
735 			be_print_err(gettext("get_ver_from_capfile: failed to "
736 			    "open file %s with error %s\n"), file,
737 			    strerror(err));
738 			err = errno_to_be_err(err);
739 			return (err);
740 		}
741 
742 		while (fgets(line, BUFSIZ, fp)) {
743 			char *tok = strtok_r(line, "=", &last);
744 
745 			if (tok == NULL || tok[0] == '#') {
746 				continue;
747 			} else if (strcmp(tok, "VERSION") == 0) {
748 				*vers = strdup(last);
749 				break;
750 			}
751 		}
752 		(void) fclose(fp);
753 	}
754 
755 	return (BE_SUCCESS);
756 }
757 
758 /*
759  * To be able to boot EFI labeled disks, GRUB stage1 needs to be written
760  * into the MBR. We do not do this if we're on disks with a traditional
761  * fdisk partition table only, or if any foreign EFI partitions exist.
762  * In the trivial case of a whole-disk vdev we always write stage1 into
763  * the MBR.
764  */
765 static boolean_t
766 be_do_installgrub_mbr(char *diskname, nvlist_t *child)
767 {
768 	struct uuid allowed_uuids[] = {
769 		EFI_UNUSED,
770 		EFI_RESV1,
771 		EFI_BOOT,
772 		EFI_ROOT,
773 		EFI_SWAP,
774 		EFI_USR,
775 		EFI_BACKUP,
776 		EFI_RESV2,
777 		EFI_VAR,
778 		EFI_HOME,
779 		EFI_ALTSCTR,
780 		EFI_RESERVED,
781 		EFI_SYSTEM,
782 		EFI_BIOS_BOOT,
783 		EFI_SYMC_PUB,
784 		EFI_SYMC_CDS
785 	};
786 
787 	uint64_t whole;
788 	struct dk_gpt *gpt;
789 	struct uuid *u;
790 	int fd, npart, i, j;
791 
792 	(void) nvlist_lookup_uint64(child, ZPOOL_CONFIG_WHOLE_DISK,
793 	    &whole);
794 
795 	if (whole)
796 		return (B_TRUE);
797 
798 	if ((fd = open(diskname, O_RDONLY|O_NDELAY)) < 0)
799 		return (B_FALSE);
800 
801 	if ((npart = efi_alloc_and_read(fd, &gpt)) <= 0)
802 		return (B_FALSE);
803 
804 	for (i = 0; i != npart; i++) {
805 		int match = 0;
806 
807 		u = &gpt->efi_parts[i].p_guid;
808 
809 		for (j = 0;
810 		    j != sizeof (allowed_uuids) / sizeof (struct uuid);
811 		    j++)
812 			if (bcmp(u, &allowed_uuids[j],
813 			    sizeof (struct uuid)) == 0)
814 				match++;
815 
816 		if (match == 0)
817 			return (B_FALSE);
818 	}
819 
820 	return (B_TRUE);
821 }
822 
823 static int
824 be_do_installgrub_helper(zpool_handle_t *zphp, nvlist_t *child, char *stage1,
825     char *stage2)
826 {
827 	char installgrub_cmd[MAXPATHLEN];
828 	char be_run_cmd_errbuf[BUFSIZ];
829 	char diskname[MAXPATHLEN];
830 	char *vname;
831 	char *path, *dsk_ptr;
832 	char *m_flag = "";
833 
834 	if (nvlist_lookup_string(child, ZPOOL_CONFIG_PATH, &path) != 0) {
835 		be_print_err(gettext("be_do_installgrub: "
836 		    "failed to get device path\n"));
837 		return (BE_ERR_NODEV);
838 	}
839 
840 	/*
841 	 * Modify the vdev path to point to the raw disk.
842 	 */
843 	path = strdup(path);
844 	if (path == NULL)
845 		return (BE_ERR_NOMEM);
846 
847 	dsk_ptr = strstr(path, "/dsk/");
848 	if (dsk_ptr != NULL) {
849 		*dsk_ptr = '\0';
850 		dsk_ptr++;
851 	} else {
852 		dsk_ptr = "";
853 	}
854 
855 	(void) snprintf(diskname, sizeof (diskname), "%s/r%s", path, dsk_ptr);
856 	free(path);
857 
858 	if (be_do_installgrub_mbr(diskname, child))
859 		m_flag = "-m -f";
860 
861 	vname = zpool_vdev_name(g_zfs, zphp, child, B_FALSE);
862 	if (vname == NULL) {
863 		be_print_err(gettext("be_do_installgrub: "
864 		    "failed to get device name: %s\n"),
865 		    libzfs_error_description(g_zfs));
866 		return (zfs_err_to_be_err(g_zfs));
867 	}
868 
869 	(void) snprintf(installgrub_cmd, sizeof (installgrub_cmd),
870 	    "%s %s %s %s %s", BE_INSTALL_GRUB, m_flag, stage1, stage2,
871 	    diskname);
872 	if (be_run_cmd(installgrub_cmd, be_run_cmd_errbuf, BUFSIZ, NULL, 0)
873 	    != BE_SUCCESS) {
874 		be_print_err(gettext("be_do_installgrub: installgrub "
875 		    "failed for device %s.\n"), vname);
876 		/* Assume localized cmd err output. */
877 		be_print_err(gettext("  Command: \"%s\"\n"),
878 		    installgrub_cmd);
879 		be_print_err("%s", be_run_cmd_errbuf);
880 		free(vname);
881 		return (BE_ERR_BOOTFILE_INST);
882 	}
883 	free(vname);
884 
885 	return (BE_SUCCESS);
886 }
887 
888 /*
889  * Function:	be_do_installgrub
890  * Description:	This function runs installgrub using the grub loader files
891  *              from the BE we're activating and installing them on the
892  *              pool the BE lives in.
893  *
894  * Parameters:
895  *              bt - The transaction data for the BE we're activating.
896  * Return:
897  *		BE_SUCCESS - Success
898  *		be_errno_t - Failure
899  *
900  * Scope:
901  *		Private
902  */
903 static int
904 be_do_installgrub(be_transaction_data_t *bt)
905 {
906 	zpool_handle_t  *zphp = NULL;
907 	zfs_handle_t	*zhp = NULL;
908 	nvlist_t **child, *nv, *config;
909 	uint_t c, children = 0;
910 	char *tmp_mntpt = NULL;
911 	char *pool_mntpnt = NULL;
912 	char *ptmp_mntpnt = NULL;
913 	char *orig_mntpnt = NULL;
914 	FILE *cap_fp = NULL;
915 	FILE *zpool_cap_fp = NULL;
916 	char line[BUFSIZ];
917 	char cap_file[MAXPATHLEN];
918 	char zpool_cap_file[MAXPATHLEN];
919 	char stage1[MAXPATHLEN];
920 	char stage2[MAXPATHLEN];
921 	char *vname;
922 	int ret = BE_SUCCESS;
923 	int err = 0;
924 	boolean_t be_mounted = B_FALSE;
925 	boolean_t pool_mounted = B_FALSE;
926 
927 	if (!be_has_grub()) {
928 		be_print_err(gettext("be_do_installgrub: Not supported "
929 		    "on this architecture\n"));
930 		return (BE_ERR_NOTSUP);
931 	}
932 
933 	if ((zhp = zfs_open(g_zfs, bt->obe_root_ds, ZFS_TYPE_FILESYSTEM)) ==
934 	    NULL) {
935 		be_print_err(gettext("be_do_installgrub: failed to "
936 		    "open BE root dataset (%s): %s\n"), bt->obe_root_ds,
937 		    libzfs_error_description(g_zfs));
938 		ret = zfs_err_to_be_err(g_zfs);
939 		return (ret);
940 	}
941 	if (!zfs_is_mounted(zhp, &tmp_mntpt)) {
942 		if ((ret = _be_mount(bt->obe_name, &tmp_mntpt,
943 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
944 			be_print_err(gettext("be_do_installgrub: failed to "
945 			    "mount BE (%s)\n"), bt->obe_name);
946 			ZFS_CLOSE(zhp);
947 			return (ret);
948 		}
949 		be_mounted = B_TRUE;
950 	}
951 	ZFS_CLOSE(zhp);
952 
953 	(void) snprintf(stage1, sizeof (stage1), "%s%s", tmp_mntpt, BE_STAGE_1);
954 	(void) snprintf(stage2, sizeof (stage2), "%s%s", tmp_mntpt, BE_STAGE_2);
955 
956 	if ((zphp = zpool_open(g_zfs, bt->obe_zpool)) == NULL) {
957 		be_print_err(gettext("be_do_installgrub: failed to open "
958 		    "pool (%s): %s\n"), bt->obe_zpool,
959 		    libzfs_error_description(g_zfs));
960 		ret = zfs_err_to_be_err(g_zfs);
961 		if (be_mounted)
962 			(void) _be_unmount(bt->obe_name, 0);
963 		free(tmp_mntpt);
964 		return (ret);
965 	}
966 
967 	if ((config = zpool_get_config(zphp, NULL)) == NULL) {
968 		be_print_err(gettext("be_do_installgrub: failed to get zpool "
969 		    "configuration information. %s\n"),
970 		    libzfs_error_description(g_zfs));
971 		ret = zfs_err_to_be_err(g_zfs);
972 		goto done;
973 	}
974 
975 	/*
976 	 * Get the vdev tree
977 	 */
978 	if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &nv) != 0) {
979 		be_print_err(gettext("be_do_installgrub: failed to get vdev "
980 		    "tree: %s\n"), libzfs_error_description(g_zfs));
981 		ret = zfs_err_to_be_err(g_zfs);
982 		goto done;
983 	}
984 
985 	if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN, &child,
986 	    &children) != 0) {
987 		be_print_err(gettext("be_do_installgrub: failed to traverse "
988 		    "the vdev tree: %s\n"), libzfs_error_description(g_zfs));
989 		ret = zfs_err_to_be_err(g_zfs);
990 		goto done;
991 	}
992 	for (c = 0; c < children; c++) {
993 		uint_t i, nchildren = 0;
994 		nvlist_t **nvchild;
995 		vname = zpool_vdev_name(g_zfs, zphp, child[c], B_FALSE);
996 		if (vname == NULL) {
997 			be_print_err(gettext(
998 			    "be_do_installgrub: "
999 			    "failed to get device name: %s\n"),
1000 			    libzfs_error_description(g_zfs));
1001 			ret = zfs_err_to_be_err(g_zfs);
1002 			goto done;
1003 		}
1004 		if (strcmp(vname, "mirror") == 0 || vname[0] != 'c') {
1005 			free(vname);
1006 
1007 			if (nvlist_lookup_nvlist_array(child[c],
1008 			    ZPOOL_CONFIG_CHILDREN, &nvchild, &nchildren) != 0) {
1009 				be_print_err(gettext("be_do_installgrub: "
1010 				    "failed to traverse the vdev tree: %s\n"),
1011 				    libzfs_error_description(g_zfs));
1012 				ret = zfs_err_to_be_err(g_zfs);
1013 				goto done;
1014 			}
1015 
1016 			for (i = 0; i < nchildren; i++) {
1017 				ret = be_do_installgrub_helper(zphp, nvchild[i],
1018 				    stage1, stage2);
1019 				if (ret != BE_SUCCESS)
1020 					goto done;
1021 			}
1022 		} else {
1023 			free(vname);
1024 
1025 			ret = be_do_installgrub_helper(zphp, child[c], stage1,
1026 			    stage2);
1027 			if (ret != BE_SUCCESS)
1028 				goto done;
1029 		}
1030 	}
1031 
1032 	/*
1033 	 * Copy the grub capability file from the BE we're activating into
1034 	 * the root pool.
1035 	 */
1036 	(void) snprintf(cap_file, sizeof (cap_file), "%s%s", tmp_mntpt,
1037 	    BE_CAP_FILE);
1038 
1039 	if ((zhp = zfs_open(g_zfs, bt->obe_zpool, ZFS_TYPE_FILESYSTEM)) ==
1040 	    NULL) {
1041 		be_print_err(gettext("be_do_installgrub: zfs_open "
1042 		    "failed: %s\n"), libzfs_error_description(g_zfs));
1043 		zpool_close(zphp);
1044 		return (zfs_err_to_be_err(g_zfs));
1045 	}
1046 
1047 	/*
1048 	 * Check to see if the pool's dataset is mounted. If it isn't we'll
1049 	 * attempt to mount it.
1050 	 */
1051 	if ((ret = be_mount_pool(zhp, &ptmp_mntpnt,
1052 	    &orig_mntpnt, &pool_mounted)) != BE_SUCCESS) {
1053 		be_print_err(gettext("be_do_installgrub: pool dataset "
1054 		    "(%s) could not be mounted\n"), bt->obe_zpool);
1055 		ZFS_CLOSE(zhp);
1056 		zpool_close(zphp);
1057 		return (ret);
1058 	}
1059 
1060 	/*
1061 	 * Get the mountpoint for the root pool dataset.
1062 	 */
1063 	if (!zfs_is_mounted(zhp, &pool_mntpnt)) {
1064 		be_print_err(gettext("be_do_installgrub: pool "
1065 		    "dataset (%s) is not mounted. Can't check the grub "
1066 		    "version from the grub capability file.\n"), bt->obe_zpool);
1067 		ret = BE_ERR_NO_MENU;
1068 		goto done;
1069 	}
1070 
1071 	(void) snprintf(zpool_cap_file, sizeof (zpool_cap_file), "%s%s",
1072 	    pool_mntpnt, BE_CAP_FILE);
1073 
1074 	free(pool_mntpnt);
1075 	pool_mntpnt = NULL;
1076 
1077 	if ((cap_fp = fopen(cap_file, "r")) == NULL) {
1078 		err = errno;
1079 		be_print_err(gettext("be_do_installgrub: failed to open grub "
1080 		    "capability file\n"));
1081 		ret = errno_to_be_err(err);
1082 		goto done;
1083 	}
1084 	if ((zpool_cap_fp = fopen(zpool_cap_file, "w")) == NULL) {
1085 		err = errno;
1086 		be_print_err(gettext("be_do_installgrub: failed to open new "
1087 		    "grub capability file\n"));
1088 		ret = errno_to_be_err(err);
1089 		(void) fclose(cap_fp);
1090 		goto done;
1091 	}
1092 
1093 	while (fgets(line, BUFSIZ, cap_fp)) {
1094 		(void) fputs(line, zpool_cap_fp);
1095 	}
1096 
1097 	(void) fclose(zpool_cap_fp);
1098 	(void) fclose(cap_fp);
1099 
1100 done:
1101 	if (pool_mounted) {
1102 		int iret = 0;
1103 		iret = be_unmount_pool(zhp, ptmp_mntpnt, orig_mntpnt);
1104 		if (ret == BE_SUCCESS)
1105 			ret = iret;
1106 		free(orig_mntpnt);
1107 		free(ptmp_mntpnt);
1108 	}
1109 	ZFS_CLOSE(zhp);
1110 	if (be_mounted)
1111 		(void) _be_unmount(bt->obe_name, 0);
1112 	zpool_close(zphp);
1113 	free(tmp_mntpt);
1114 	return (ret);
1115 }
1116 
1117 /*
1118  * Function:	be_promote_zone_ds
1119  * Description:	This function finds the zones for the BE being activated
1120  *              and the active zonepath dataset for each zone. Then each
1121  *              active zonepath dataset is promoted.
1122  *
1123  * Parameters:
1124  *              be_name - the name of the global zone BE that we need to
1125  *                       find the zones for.
1126  *              be_root_ds - the root dataset for be_name.
1127  * Return:
1128  *		BE_SUCCESS - Success
1129  *		be_errno_t - Failure
1130  *
1131  * Scope:
1132  *		Private
1133  */
1134 static int
1135 be_promote_zone_ds(char *be_name, char *be_root_ds)
1136 {
1137 	char		*zone_ds = NULL;
1138 	char		*temp_mntpt = NULL;
1139 	char		origin[MAXPATHLEN];
1140 	char		zoneroot_ds[MAXPATHLEN];
1141 	zfs_handle_t	*zhp = NULL;
1142 	zfs_handle_t	*z_zhp = NULL;
1143 	zoneList_t	zone_list = NULL;
1144 	zoneBrandList_t *brands = NULL;
1145 	boolean_t	be_mounted = B_FALSE;
1146 	int		zone_index = 0;
1147 	int		err = BE_SUCCESS;
1148 
1149 	/*
1150 	 * Get the supported zone brands so we can pass that
1151 	 * to z_get_nonglobal_zone_list_by_brand. Currently
1152 	 * only the ipkg and labeled brand zones are supported
1153 	 *
1154 	 */
1155 	if ((brands = be_get_supported_brandlist()) == NULL) {
1156 		be_print_err(gettext("be_promote_zone_ds: no supported "
1157 		    "brands\n"));
1158 		return (BE_SUCCESS);
1159 	}
1160 
1161 	if ((zhp = zfs_open(g_zfs, be_root_ds,
1162 	    ZFS_TYPE_FILESYSTEM)) == NULL) {
1163 		be_print_err(gettext("be_promote_zone_ds: Failed to open "
1164 		    "dataset (%s): %s\n"), be_root_ds,
1165 		    libzfs_error_description(g_zfs));
1166 		err = zfs_err_to_be_err(g_zfs);
1167 		z_free_brand_list(brands);
1168 		return (err);
1169 	}
1170 
1171 	if (!zfs_is_mounted(zhp, &temp_mntpt)) {
1172 		if ((err = _be_mount(be_name, &temp_mntpt,
1173 		    BE_MOUNT_FLAG_NO_ZONES)) != BE_SUCCESS) {
1174 			be_print_err(gettext("be_promote_zone_ds: failed to "
1175 			    "mount the BE for zones procesing.\n"));
1176 			ZFS_CLOSE(zhp);
1177 			z_free_brand_list(brands);
1178 			return (err);
1179 		}
1180 		be_mounted = B_TRUE;
1181 	}
1182 
1183 	/*
1184 	 * Set the zone root to the temp mount point for the BE we just mounted.
1185 	 */
1186 	z_set_zone_root(temp_mntpt);
1187 
1188 	/*
1189 	 * Get all the zones based on the brands we're looking for. If no zones
1190 	 * are found that we're interested in unmount the BE and move on.
1191 	 */
1192 	if ((zone_list = z_get_nonglobal_zone_list_by_brand(brands)) == NULL) {
1193 		if (be_mounted)
1194 			(void) _be_unmount(be_name, 0);
1195 		ZFS_CLOSE(zhp);
1196 		z_free_brand_list(brands);
1197 		free(temp_mntpt);
1198 		return (BE_SUCCESS);
1199 	}
1200 	for (zone_index = 0; z_zlist_get_zonename(zone_list, zone_index)
1201 	    != NULL; zone_index++) {
1202 		char *zone_path = NULL;
1203 
1204 		/* Skip zones that aren't at least installed */
1205 		if (z_zlist_get_current_state(zone_list, zone_index) <
1206 		    ZONE_STATE_INSTALLED)
1207 			continue;
1208 
1209 		if (((zone_path =
1210 		    z_zlist_get_zonepath(zone_list, zone_index)) == NULL) ||
1211 		    ((zone_ds = be_get_ds_from_dir(zone_path)) == NULL) ||
1212 		    !be_zone_supported(zone_ds))
1213 			continue;
1214 
1215 		if (be_find_active_zone_root(zhp, zone_ds,
1216 		    zoneroot_ds, sizeof (zoneroot_ds)) != 0) {
1217 			be_print_err(gettext("be_promote_zone_ds: "
1218 			    "Zone does not have an active root "
1219 			    "dataset, skipping this zone.\n"));
1220 			continue;
1221 		}
1222 
1223 		if ((z_zhp = zfs_open(g_zfs, zoneroot_ds,
1224 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
1225 			be_print_err(gettext("be_promote_zone_ds: "
1226 			    "Failed to open dataset "
1227 			    "(%s): %s\n"), zoneroot_ds,
1228 			    libzfs_error_description(g_zfs));
1229 			err = zfs_err_to_be_err(g_zfs);
1230 			goto done;
1231 		}
1232 
1233 		if (zfs_prop_get(z_zhp, ZFS_PROP_ORIGIN, origin,
1234 		    sizeof (origin), NULL, NULL, 0, B_FALSE) != 0) {
1235 			ZFS_CLOSE(z_zhp);
1236 			continue;
1237 		}
1238 
1239 		/*
1240 		 * We don't need to close the zfs handle at this
1241 		 * point because the callback funtion
1242 		 * be_promote_ds_callback() will close it for us.
1243 		 */
1244 		if (be_promote_ds_callback(z_zhp, NULL) != 0) {
1245 			be_print_err(gettext("be_promote_zone_ds: "
1246 			    "failed to activate the "
1247 			    "datasets for %s: %s\n"),
1248 			    zoneroot_ds,
1249 			    libzfs_error_description(g_zfs));
1250 			err = BE_ERR_PROMOTE;
1251 			goto done;
1252 		}
1253 	}
1254 done:
1255 	if (be_mounted)
1256 		(void) _be_unmount(be_name, 0);
1257 	ZFS_CLOSE(zhp);
1258 	free(temp_mntpt);
1259 	z_free_brand_list(brands);
1260 	z_free_zone_list(zone_list);
1261 	return (err);
1262 }
1263 
1264 /*
1265  * Function:	be_promote_ds_callback
1266  * Description:	This function is used to promote the datasets for the BE
1267  *		being activated as well as the datasets for the zones BE
1268  *		being activated.
1269  *
1270  * Parameters:
1271  *              zhp - the zfs handle for zone BE being activated.
1272  *		data - not used.
1273  * Return:
1274  *		0 - Success
1275  *		be_errno_t - Failure
1276  *
1277  * Scope:
1278  *		Private
1279  */
1280 static int
1281 /* LINTED */
1282 be_promote_ds_callback(zfs_handle_t *zhp, void *data)
1283 {
1284 	char	origin[MAXPATHLEN];
1285 	char	*sub_dataset = NULL;
1286 	int	ret = 0;
1287 
1288 	if (zhp != NULL) {
1289 		sub_dataset = strdup(zfs_get_name(zhp));
1290 		if (sub_dataset == NULL) {
1291 			ret = BE_ERR_NOMEM;
1292 			goto done;
1293 		}
1294 	} else {
1295 		be_print_err(gettext("be_promote_ds_callback: "
1296 		    "Invalid zfs handle passed into function\n"));
1297 		ret = BE_ERR_INVAL;
1298 		goto done;
1299 	}
1300 
1301 	/*
1302 	 * This loop makes sure that we promote the dataset to the
1303 	 * top of the tree so that it is no longer a decendent of any
1304 	 * dataset. The ZFS close and then open is used to make sure that
1305 	 * the promotion is updated before we move on.
1306 	 */
1307 	while (zfs_prop_get(zhp, ZFS_PROP_ORIGIN, origin,
1308 	    sizeof (origin), NULL, NULL, 0, B_FALSE) == 0) {
1309 		if (zfs_promote(zhp) != 0) {
1310 			if (libzfs_errno(g_zfs) != EZFS_EXISTS) {
1311 				be_print_err(gettext("be_promote_ds_callback: "
1312 				    "promote of %s failed: %s\n"),
1313 				    zfs_get_name(zhp),
1314 				    libzfs_error_description(g_zfs));
1315 				ret = zfs_err_to_be_err(g_zfs);
1316 				goto done;
1317 			} else {
1318 				/*
1319 				 * If the call to zfs_promote returns the
1320 				 * error EZFS_EXISTS we've hit a snapshot name
1321 				 * collision. This means we're probably
1322 				 * attemping to promote a zone dataset above a
1323 				 * parent dataset that belongs to another zone
1324 				 * which this zone was cloned from.
1325 				 *
1326 				 * TODO: If this is a zone dataset at some
1327 				 * point we should skip this if the zone
1328 				 * paths for the dataset and the snapshot
1329 				 * don't match.
1330 				 */
1331 				be_print_err(gettext("be_promote_ds_callback: "
1332 				    "promote of %s failed due to snapshot "
1333 				    "name collision: %s\n"), zfs_get_name(zhp),
1334 				    libzfs_error_description(g_zfs));
1335 				ret = zfs_err_to_be_err(g_zfs);
1336 				goto done;
1337 			}
1338 		}
1339 		ZFS_CLOSE(zhp);
1340 		if ((zhp = zfs_open(g_zfs, sub_dataset,
1341 		    ZFS_TYPE_FILESYSTEM)) == NULL) {
1342 			be_print_err(gettext("be_promote_ds_callback: "
1343 			    "Failed to open dataset (%s): %s\n"), sub_dataset,
1344 			    libzfs_error_description(g_zfs));
1345 			ret = zfs_err_to_be_err(g_zfs);
1346 			goto done;
1347 		}
1348 	}
1349 
1350 	/* Iterate down this dataset's children and promote them */
1351 	ret = zfs_iter_filesystems(zhp, be_promote_ds_callback, NULL);
1352 
1353 done:
1354 	free(sub_dataset);
1355 	ZFS_CLOSE(zhp);
1356 	return (ret);
1357 }
1358