xref: /illumos-gate/usr/src/lib/libbe/common/be_list.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
28  */
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 <sys/types.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <errno.h>
42 
43 #include <libbe.h>
44 #include <libbe_priv.h>
45 
46 /*
47  * Callback data used for zfs_iter calls.
48  */
49 typedef struct list_callback_data {
50 	char *zpool_name;
51 	char *be_name;
52 	be_node_list_t *be_nodes_head;
53 	be_node_list_t *be_nodes;
54 	char current_be[MAXPATHLEN];
55 } list_callback_data_t;
56 
57 /*
58  * Private function prototypes
59  */
60 static int be_add_children_callback(zfs_handle_t *zhp, void *data);
61 static int be_get_list_callback(zpool_handle_t *, void *);
62 static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *,
63     const char *, char *, char *);
64 static int be_get_zone_node_data(be_node_list_t *, char *);
65 static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *,
66     be_node_list_t *);
67 static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *,
68     be_node_list_t *);
69 static void be_sort_list(be_node_list_t **);
70 static int be_qsort_compare_BEs(const void *, const void *);
71 static int be_qsort_compare_snapshots(const void *x, const void *y);
72 static int be_qsort_compare_datasets(const void *x, const void *y);
73 static void *be_list_alloc(int *, size_t);
74 
75 /*
76  * Private data.
77  */
78 static char be_container_ds[MAXPATHLEN];
79 static boolean_t zone_be = B_FALSE;
80 
81 /* ******************************************************************** */
82 /*			Public Functions				*/
83 /* ******************************************************************** */
84 
85 /*
86  * Function:	be_list
87  * Description:	Calls _be_list which finds all the BEs on the system and
88  *		returns the datasets and snapshots belonging to each BE.
89  *		Also data, such as dataset and snapshot properties,
90  *		for each BE and their snapshots and datasets is
91  *		returned. The data returned is as described in the
92  *		be_dataset_list_t, be_snapshot_list_t and be_node_list_t
93  *		structures.
94  * Parameters:
95  *		be_name - The name of the BE to look up.
96  *			  If NULL a list of all BEs will be returned.
97  *		be_nodes - A reference pointer to the list of BEs. The list
98  *			   structure will be allocated by _be_list and must
99  *			   be freed by a call to be_free_list. If there are no
100  *			   BEs found on the system this reference will be
101  *			   set to NULL.
102  * Return:
103  *		BE_SUCCESS - Success
104  *		be_errno_t - Failure
105  * Scope:
106  *		Public
107  */
108 int
109 be_list(char *be_name, be_node_list_t **be_nodes)
110 {
111 	int	ret = BE_SUCCESS;
112 
113 	/* Initialize libzfs handle */
114 	if (!be_zfs_init())
115 		return (BE_ERR_INIT);
116 
117 	/* Validate be_name if its not NULL */
118 	if (be_name != NULL) {
119 		if (!be_valid_be_name(be_name)) {
120 			be_print_err(gettext("be_list: "
121 			    "invalid BE name %s\n"), be_name);
122 			return (BE_ERR_INVAL);
123 		}
124 	}
125 
126 	ret = _be_list(be_name, be_nodes);
127 
128 	be_zfs_fini();
129 
130 	return (ret);
131 }
132 
133 /* ******************************************************************** */
134 /*			Semi-Private Functions				*/
135 /* ******************************************************************** */
136 
137 /*
138  * Function:	_be_list
139  * Description:	This does the actual work described in be_list.
140  * Parameters:
141  *		be_name - The name of the BE to look up.
142  *			  If NULL a list of all BEs will be returned.
143  *		be_nodes - A reference pointer to the list of BEs. The list
144  *			   structure will be allocated here and must
145  *			   be freed by a call to be_free_list. If there are no
146  *			   BEs found on the system this reference will be
147  *			   set to NULL.
148  * Return:
149  *		BE_SUCCESS - Success
150  *		be_errno_t - Failure
151  * Scope:
152  *		Semi-private (library wide use only)
153  */
154 int
155 _be_list(char *be_name, be_node_list_t **be_nodes)
156 {
157 	list_callback_data_t cb = { 0 };
158 	be_transaction_data_t bt = { 0 };
159 	int ret = BE_SUCCESS;
160 	zpool_handle_t *zphp;
161 	char *rpool = NULL;
162 	struct be_defaults be_defaults;
163 
164 	if (be_nodes == NULL)
165 		return (BE_ERR_INVAL);
166 
167 	be_get_defaults(&be_defaults);
168 
169 	if (be_find_current_be(&bt) != BE_SUCCESS) {
170 		/*
171 		 * We were unable to find a currently booted BE which
172 		 * probably means that we're not booted in a BE envoronment.
173 		 * None of the BE's will be marked as the active BE.
174 		 */
175 		(void) strcpy(cb.current_be, "-");
176 	} else {
177 		(void) strncpy(cb.current_be, bt.obe_name,
178 		    sizeof (cb.current_be));
179 		rpool = bt.obe_zpool;
180 	}
181 
182 	/*
183 	 * If be_name is NULL we'll look for all BE's on the system.
184 	 * If not then we will only return data for the specified BE.
185 	 */
186 	if (be_name != NULL)
187 		cb.be_name = strdup(be_name);
188 
189 	if (be_defaults.be_deflt_rpool_container && rpool != NULL) {
190 		if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
191 			be_print_err(gettext("be_list: failed to "
192 			    "open rpool (%s): %s\n"), rpool,
193 			    libzfs_error_description(g_zfs));
194 			free(cb.be_name);
195 			return (zfs_err_to_be_err(g_zfs));
196 		}
197 
198 		ret = be_get_list_callback(zphp, &cb);
199 	} else {
200 		if ((zpool_iter(g_zfs, be_get_list_callback, &cb)) != 0) {
201 			if (cb.be_nodes_head != NULL) {
202 				be_free_list(cb.be_nodes_head);
203 				cb.be_nodes_head = NULL;
204 				cb.be_nodes = NULL;
205 			}
206 			ret = BE_ERR_BE_NOENT;
207 		}
208 	}
209 
210 	if (cb.be_nodes_head == NULL) {
211 		if (be_name != NULL)
212 			be_print_err(gettext("be_list: BE (%s) does not "
213 			    "exist\n"), be_name);
214 		else
215 			be_print_err(gettext("be_list: No BE's found\n"));
216 		ret = BE_ERR_BE_NOENT;
217 	}
218 
219 	*be_nodes = cb.be_nodes_head;
220 
221 	free(cb.be_name);
222 
223 	be_sort_list(be_nodes);
224 
225 	return (ret);
226 }
227 
228 /*
229  * Function:	be_free_list
230  * Description:	Frees up all the data allocated for the list of BEs,
231  *		datasets and snapshots returned by be_list.
232  * Parameters:
233  *		be_node - be_nodes_t structure returned from call to be_list.
234  * Returns:
235  *		none
236  * Scope:
237  *		Semi-private (library wide use only)
238  */
239 void
240 be_free_list(be_node_list_t *be_nodes)
241 {
242 	be_node_list_t *temp_node = NULL;
243 	be_node_list_t *list = be_nodes;
244 
245 	while (list != NULL) {
246 		be_dataset_list_t *datasets = list->be_node_datasets;
247 		be_snapshot_list_t *snapshots = list->be_node_snapshots;
248 
249 		while (datasets != NULL) {
250 			be_dataset_list_t *temp_ds = datasets;
251 			datasets = datasets->be_next_dataset;
252 			free(temp_ds->be_dataset_name);
253 			free(temp_ds->be_ds_mntpt);
254 			free(temp_ds->be_ds_plcy_type);
255 			free(temp_ds);
256 		}
257 
258 		while (snapshots != NULL) {
259 			be_snapshot_list_t *temp_ss = snapshots;
260 			snapshots = snapshots->be_next_snapshot;
261 			free(temp_ss->be_snapshot_name);
262 			free(temp_ss->be_snapshot_type);
263 			free(temp_ss);
264 		}
265 
266 		temp_node = list;
267 		list = list->be_next_node;
268 		free(temp_node->be_node_name);
269 		free(temp_node->be_root_ds);
270 		free(temp_node->be_rpool);
271 		free(temp_node->be_mntpt);
272 		free(temp_node->be_policy_type);
273 		free(temp_node->be_uuid_str);
274 		free(temp_node);
275 	}
276 }
277 
278 /*
279  * Function:	be_get_zone_be_list
280  * Description:	Finds all the BEs for this zone on the system.
281  * Parameters:
282  *		zone_be_name - The name of the BE to look up.
283  *              zone_be_container_ds - The dataset for the zone.
284  *		zbe_nodes - A reference pointer to the list of BEs. The list
285  *			   structure will be allocated here and must
286  *			   be freed by a call to be_free_list. If there are no
287  *			   BEs found on the system this reference will be
288  *			   set to NULL.
289  * Return:
290  *		BE_SUCCESS - Success
291  *		be_errno_t - Failure
292  * Scope:
293  *		Semi-private (library wide use only)
294  */
295 int
296 be_get_zone_be_list(
297 /* LINTED */
298 	char *zone_be_name,
299 	char *zone_be_container_ds,
300 	be_node_list_t **zbe_nodes)
301 {
302 	zfs_handle_t *zhp = NULL;
303 	list_callback_data_t cb = { 0 };
304 	int ret = BE_SUCCESS;
305 
306 	if (zbe_nodes == NULL)
307 		return (BE_ERR_INVAL);
308 
309 	if (!zfs_dataset_exists(g_zfs, zone_be_container_ds,
310 	    ZFS_TYPE_FILESYSTEM)) {
311 		return (BE_ERR_BE_NOENT);
312 	}
313 
314 	zone_be = B_TRUE;
315 
316 	if ((zhp = zfs_open(g_zfs, zone_be_container_ds,
317 	    ZFS_TYPE_FILESYSTEM)) == NULL) {
318 		be_print_err(gettext("be_get_zone_be_list: failed to open "
319 		    "the zone BE dataset %s: %s\n"), zone_be_container_ds,
320 		    libzfs_error_description(g_zfs));
321 		ret = zfs_err_to_be_err(g_zfs);
322 		goto cleanup;
323 	}
324 
325 	(void) strcpy(be_container_ds, zone_be_container_ds);
326 
327 	if (cb.be_nodes_head == NULL) {
328 		if ((cb.be_nodes_head = be_list_alloc(&ret,
329 		    sizeof (be_node_list_t))) == NULL) {
330 			ZFS_CLOSE(zhp);
331 			goto cleanup;
332 		}
333 		cb.be_nodes = cb.be_nodes_head;
334 	}
335 	if (ret == 0)
336 		ret = zfs_iter_filesystems(zhp, be_add_children_callback, &cb);
337 	ZFS_CLOSE(zhp);
338 
339 	*zbe_nodes = cb.be_nodes_head;
340 
341 cleanup:
342 	zone_be = B_FALSE;
343 
344 	return (ret);
345 }
346 
347 /* ******************************************************************** */
348 /*			Private Functions				*/
349 /* ******************************************************************** */
350 
351 /*
352  * Function:	be_get_list_callback
353  * Description:	Callback function used by zfs_iter to look through all
354  *		the pools on the system looking for BEs. If a BE name was
355  *		specified only that BE's information will be collected and
356  *		returned.
357  * Parameters:
358  *		zlp - handle to the first zfs dataset. (provided by the
359  *		      zfs_iter_* call)
360  *		data - pointer to the callback data and where we'll pass
361  *		       the BE information back.
362  * Returns:
363  *		0 - Success
364  *		be_errno_t - Failure
365  * Scope:
366  *		Private
367  */
368 static int
369 be_get_list_callback(zpool_handle_t *zlp, void *data)
370 {
371 	list_callback_data_t *cb = (list_callback_data_t *)data;
372 	char be_ds[MAXPATHLEN];
373 	char *open_ds = NULL;
374 	char *rpool = NULL;
375 	zfs_handle_t *zhp = NULL;
376 	int ret = 0;
377 
378 	cb->zpool_name = rpool =  (char *)zpool_get_name(zlp);
379 
380 	/*
381 	 * Generate string for the BE container dataset
382 	 */
383 	be_make_container_ds(rpool, be_container_ds,
384 	    sizeof (be_container_ds));
385 
386 	/*
387 	 * If a BE name was specified we use it's root dataset in place of
388 	 * the container dataset. This is because we only want to collect
389 	 * the information for the specified BE.
390 	 */
391 	if (cb->be_name != NULL) {
392 		if (!be_valid_be_name(cb->be_name))
393 			return (BE_ERR_INVAL);
394 		/*
395 		 * Generate string for the BE root dataset
396 		 */
397 		be_make_root_ds(rpool, cb->be_name, be_ds, sizeof (be_ds));
398 		open_ds = be_ds;
399 	} else {
400 		open_ds = be_container_ds;
401 	}
402 
403 	/*
404 	 * Check if the dataset exists
405 	 */
406 	if (!zfs_dataset_exists(g_zfs, open_ds,
407 	    ZFS_TYPE_FILESYSTEM)) {
408 		/*
409 		 * The specified dataset does not exist in this pool or
410 		 * there are no valid BE's in this pool. Try the next zpool.
411 		 */
412 		zpool_close(zlp);
413 		return (0);
414 	}
415 
416 	if ((zhp = zfs_open(g_zfs, open_ds, ZFS_TYPE_FILESYSTEM)) == NULL) {
417 		be_print_err(gettext("be_get_list_callback: failed to open "
418 		    "the BE dataset %s: %s\n"), open_ds,
419 		    libzfs_error_description(g_zfs));
420 		ret = zfs_err_to_be_err(g_zfs);
421 		zpool_close(zlp);
422 		return (ret);
423 	}
424 
425 	/*
426 	 * If a BE name was specified we iterate through the datasets
427 	 * and snapshots for this BE only. Otherwise we will iterate
428 	 * through the next level of datasets to find all the BE's
429 	 * within the pool
430 	 */
431 	if (cb->be_name != NULL) {
432 		if (cb->be_nodes_head == NULL) {
433 			if ((cb->be_nodes_head = be_list_alloc(&ret,
434 			    sizeof (be_node_list_t))) == NULL) {
435 				ZFS_CLOSE(zhp);
436 				zpool_close(zlp);
437 				return (ret);
438 			}
439 			cb->be_nodes = cb->be_nodes_head;
440 		}
441 
442 		if ((ret = be_get_node_data(zhp, cb->be_nodes, cb->be_name,
443 		    rpool, cb->current_be, be_ds)) != BE_SUCCESS) {
444 			ZFS_CLOSE(zhp);
445 			zpool_close(zlp);
446 			return (ret);
447 		}
448 		ret = zfs_iter_snapshots(zhp, be_add_children_callback, cb);
449 	}
450 
451 	if (ret == 0)
452 		ret = zfs_iter_filesystems(zhp, be_add_children_callback, cb);
453 	ZFS_CLOSE(zhp);
454 
455 	zpool_close(zlp);
456 	return (ret);
457 }
458 
459 /*
460  * Function:	be_add_children_callback
461  * Description:	Callback function used by zfs_iter to look through all
462  *		the datasets and snapshots for each BE and add them to
463  *		the lists of information to be passed back.
464  * Parameters:
465  *		zhp - handle to the first zfs dataset. (provided by the
466  *		      zfs_iter_* call)
467  *		data - pointer to the callback data and where we'll pass
468  *		       the BE information back.
469  * Returns:
470  *		0 - Success
471  *		be_errno_t - Failure
472  * Scope:
473  *		Private
474  */
475 static int
476 be_add_children_callback(zfs_handle_t *zhp, void *data)
477 {
478 	list_callback_data_t	*cb = (list_callback_data_t *)data;
479 	char			*str = NULL, *ds_path = NULL;
480 	int			ret = 0;
481 	struct be_defaults be_defaults;
482 
483 	be_get_defaults(&be_defaults);
484 
485 	ds_path = str = strdup(zfs_get_name(zhp));
486 
487 	/*
488 	 * get past the end of the container dataset plus the trailing "/"
489 	 */
490 	str = str + (strlen(be_container_ds) + 1);
491 	if (be_defaults.be_deflt_rpool_container) {
492 		/* just skip if invalid */
493 		if (!be_valid_be_name(str))
494 			return (BE_SUCCESS);
495 	}
496 
497 	if (cb->be_nodes_head == NULL) {
498 		if ((cb->be_nodes_head = be_list_alloc(&ret,
499 		    sizeof (be_node_list_t))) == NULL) {
500 			ZFS_CLOSE(zhp);
501 			return (ret);
502 		}
503 		cb->be_nodes = cb->be_nodes_head;
504 	}
505 
506 	if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT && !zone_be) {
507 		be_snapshot_list_t *snapshots = NULL;
508 		if (cb->be_nodes->be_node_snapshots == NULL) {
509 			if ((cb->be_nodes->be_node_snapshots =
510 			    be_list_alloc(&ret, sizeof (be_snapshot_list_t)))
511 			    == NULL || ret != BE_SUCCESS) {
512 				ZFS_CLOSE(zhp);
513 				return (ret);
514 			}
515 			cb->be_nodes->be_node_snapshots->be_next_snapshot =
516 			    NULL;
517 			snapshots = cb->be_nodes->be_node_snapshots;
518 		} else {
519 			for (snapshots = cb->be_nodes->be_node_snapshots;
520 			    snapshots != NULL;
521 			    snapshots = snapshots->be_next_snapshot) {
522 				if (snapshots->be_next_snapshot != NULL)
523 					continue;
524 				/*
525 				 * We're at the end of the list add the
526 				 * new snapshot.
527 				 */
528 				if ((snapshots->be_next_snapshot =
529 				    be_list_alloc(&ret,
530 				    sizeof (be_snapshot_list_t))) == NULL ||
531 				    ret != BE_SUCCESS) {
532 					ZFS_CLOSE(zhp);
533 					return (ret);
534 				}
535 				snapshots = snapshots->be_next_snapshot;
536 				snapshots->be_next_snapshot = NULL;
537 				break;
538 			}
539 		}
540 		if ((ret = be_get_ss_data(zhp, str, snapshots,
541 		    cb->be_nodes)) != BE_SUCCESS) {
542 			ZFS_CLOSE(zhp);
543 			return (ret);
544 		}
545 	} else if (strchr(str, '/') == NULL) {
546 		if (cb->be_nodes->be_node_name != NULL) {
547 			if ((cb->be_nodes->be_next_node =
548 			    be_list_alloc(&ret, sizeof (be_node_list_t))) ==
549 			    NULL || ret != BE_SUCCESS) {
550 				ZFS_CLOSE(zhp);
551 				return (ret);
552 			}
553 			cb->be_nodes = cb->be_nodes->be_next_node;
554 			cb->be_nodes->be_next_node = NULL;
555 		}
556 
557 		/*
558 		 * If this is a zone root dataset then we only need
559 		 * the name of the zone BE at this point. We grab that
560 		 * and return.
561 		 */
562 		if (zone_be) {
563 			ret = be_get_zone_node_data(cb->be_nodes, str);
564 			ZFS_CLOSE(zhp);
565 			return (ret);
566 		}
567 
568 		if ((ret = be_get_node_data(zhp, cb->be_nodes, str,
569 		    cb->zpool_name, cb->current_be, ds_path)) != BE_SUCCESS) {
570 			ZFS_CLOSE(zhp);
571 			return (ret);
572 		}
573 	} else if (strchr(str, '/') != NULL && !zone_be) {
574 		be_dataset_list_t *datasets = NULL;
575 		if (cb->be_nodes->be_node_datasets == NULL) {
576 			if ((cb->be_nodes->be_node_datasets =
577 			    be_list_alloc(&ret, sizeof (be_dataset_list_t)))
578 			    == NULL || ret != BE_SUCCESS) {
579 				ZFS_CLOSE(zhp);
580 				return (ret);
581 			}
582 			cb->be_nodes->be_node_datasets->be_next_dataset = NULL;
583 			datasets = cb->be_nodes->be_node_datasets;
584 		} else {
585 			for (datasets = cb->be_nodes->be_node_datasets;
586 			    datasets != NULL;
587 			    datasets = datasets->be_next_dataset) {
588 				if (datasets->be_next_dataset != NULL)
589 					continue;
590 				/*
591 				 * We're at the end of the list add
592 				 * the new dataset.
593 				 */
594 				if ((datasets->be_next_dataset =
595 				    be_list_alloc(&ret,
596 				    sizeof (be_dataset_list_t)))
597 				    == NULL || ret != BE_SUCCESS) {
598 					ZFS_CLOSE(zhp);
599 					return (ret);
600 				}
601 				datasets = datasets->be_next_dataset;
602 				datasets->be_next_dataset = NULL;
603 				break;
604 			}
605 		}
606 
607 		if ((ret = be_get_ds_data(zhp, str,
608 		    datasets, cb->be_nodes)) != BE_SUCCESS) {
609 			ZFS_CLOSE(zhp);
610 			return (ret);
611 		}
612 	}
613 	ret = zfs_iter_children(zhp, be_add_children_callback, cb);
614 	if (ret != 0) {
615 		be_print_err(gettext("be_add_children_callback: "
616 		    "encountered error: %s\n"),
617 		    libzfs_error_description(g_zfs));
618 		ret = zfs_err_to_be_err(g_zfs);
619 	}
620 	ZFS_CLOSE(zhp);
621 	return (ret);
622 }
623 
624 /*
625  * Function:	be_sort_list
626  * Description:	Sort BE node list
627  * Parameters:
628  *		pointer to address of list head
629  * Returns:
630  *		nothing
631  * Side effect:
632  *		node list sorted by name
633  * Scope:
634  *		Private
635  */
636 static void
637 be_sort_list(be_node_list_t **pstart)
638 {
639 	size_t ibe, nbe;
640 	be_node_list_t *p = NULL;
641 	be_node_list_t **ptrlist = NULL;
642 
643 	if (pstart == NULL)
644 		return;
645 	/* build array of linked list BE struct pointers */
646 	for (p = *pstart, nbe = 0; p != NULL; nbe++, p = p->be_next_node) {
647 		ptrlist = realloc(ptrlist,
648 		    sizeof (be_node_list_t *) * (nbe + 2));
649 		ptrlist[nbe] = p;
650 	}
651 	if (nbe == 0)
652 		return;
653 	/* in-place list quicksort using qsort(3C) */
654 	if (nbe > 1)	/* no sort if less than 2 BEs */
655 		qsort(ptrlist, nbe, sizeof (be_node_list_t *),
656 		    be_qsort_compare_BEs);
657 
658 	ptrlist[nbe] = NULL; /* add linked list terminator */
659 	*pstart = ptrlist[0]; /* set new linked list header */
660 	/* for each BE in list */
661 	for (ibe = 0; ibe < nbe; ibe++) {
662 		size_t k, ns;	/* subordinate index, count */
663 
664 		/* rewrite list pointer chain, including terminator */
665 		ptrlist[ibe]->be_next_node = ptrlist[ibe + 1];
666 		/* sort subordinate snapshots */
667 		if (ptrlist[ibe]->be_node_num_snapshots > 1) {
668 			const size_t nmax = ptrlist[ibe]->be_node_num_snapshots;
669 			be_snapshot_list_t ** const slist =
670 			    malloc(sizeof (be_snapshot_list_t *) * (nmax + 1));
671 			be_snapshot_list_t *p;
672 
673 			if (slist == NULL)
674 				continue;
675 			/* build array of linked list snapshot struct ptrs */
676 			for (ns = 0, p = ptrlist[ibe]->be_node_snapshots;
677 			    ns < nmax && p != NULL;
678 			    ns++, p = p->be_next_snapshot) {
679 				slist[ns] = p;
680 			}
681 			if (ns < 2)
682 				goto end_snapshot;
683 			slist[ns] = NULL; /* add terminator */
684 			/* in-place list quicksort using qsort(3C) */
685 			qsort(slist, ns, sizeof (be_snapshot_list_t *),
686 			    be_qsort_compare_snapshots);
687 			/* rewrite list pointer chain, including terminator */
688 			ptrlist[ibe]->be_node_snapshots = slist[0];
689 			for (k = 0; k < ns; k++)
690 				slist[k]->be_next_snapshot = slist[k + 1];
691 end_snapshot:
692 			free(slist);
693 		}
694 		/* sort subordinate datasets */
695 		if (ptrlist[ibe]->be_node_num_datasets > 1) {
696 			const size_t nmax = ptrlist[ibe]->be_node_num_datasets;
697 			be_dataset_list_t ** const slist =
698 			    malloc(sizeof (be_dataset_list_t *) * (nmax + 1));
699 			be_dataset_list_t *p;
700 
701 			if (slist == NULL)
702 				continue;
703 			/* build array of linked list dataset struct ptrs */
704 			for (ns = 0, p = ptrlist[ibe]->be_node_datasets;
705 			    ns < nmax && p != NULL;
706 			    ns++, p = p->be_next_dataset) {
707 				slist[ns] = p;
708 			}
709 			if (ns < 2) /* subordinate datasets < 2 - no sort */
710 				goto end_dataset;
711 			slist[ns] = NULL; /* add terminator */
712 			/* in-place list quicksort using qsort(3C) */
713 			qsort(slist, ns, sizeof (be_dataset_list_t *),
714 			    be_qsort_compare_datasets);
715 			/* rewrite list pointer chain, including terminator */
716 			ptrlist[ibe]->be_node_datasets = slist[0];
717 			for (k = 0; k < ns; k++)
718 				slist[k]->be_next_dataset = slist[k + 1];
719 end_dataset:
720 			free(slist);
721 		}
722 	}
723 free:
724 	free(ptrlist);
725 }
726 
727 /*
728  * Function:	be_qsort_compare_BEs
729  * Description:	lexical compare of BE names for qsort(3C)
730  * Parameters:
731  *		x,y - BEs with names to compare
732  * Returns:
733  *		positive if y>x, negative if x>y, 0 if equal
734  * Scope:
735  *		Private
736  */
737 static int
738 be_qsort_compare_BEs(const void *x, const void *y)
739 {
740 	be_node_list_t *p = *(be_node_list_t **)x;
741 	be_node_list_t *q = *(be_node_list_t **)y;
742 
743 	if (p == NULL || p->be_node_name == NULL)
744 		return (1);
745 	if (q == NULL || q->be_node_name == NULL)
746 		return (-1);
747 	return (strcmp(p->be_node_name, q->be_node_name));
748 }
749 
750 /*
751  * Function:	be_qsort_compare_snapshots
752  * Description:	lexical compare of BE names for qsort(3C)
753  * Parameters:
754  *		x,y - BE snapshots with names to compare
755  * Returns:
756  *		positive if y>x, negative if x>y, 0 if equal
757  * Scope:
758  *		Private
759  */
760 static int
761 be_qsort_compare_snapshots(const void *x, const void *y)
762 {
763 	be_snapshot_list_t *p = *(be_snapshot_list_t **)x;
764 	be_snapshot_list_t *q = *(be_snapshot_list_t **)y;
765 
766 	if (p == NULL || p->be_snapshot_name == NULL)
767 		return (1);
768 	if (q == NULL || q->be_snapshot_name == NULL)
769 		return (-1);
770 	return (strcmp(p->be_snapshot_name, q->be_snapshot_name));
771 }
772 
773 /*
774  * Function:	be_qsort_compare_datasets
775  * Description:	lexical compare of dataset names for qsort(3C)
776  * Parameters:
777  *		x,y - BE snapshots with names to compare
778  * Returns:
779  *		positive if y>x, negative if x>y, 0 if equal
780  * Scope:
781  *		Private
782  */
783 static int
784 be_qsort_compare_datasets(const void *x, const void *y)
785 {
786 	be_dataset_list_t *p = *(be_dataset_list_t **)x;
787 	be_dataset_list_t *q = *(be_dataset_list_t **)y;
788 
789 	if (p == NULL || p->be_dataset_name == NULL)
790 		return (1);
791 	if (q == NULL || q->be_dataset_name == NULL)
792 		return (-1);
793 	return (strcmp(p->be_dataset_name, q->be_dataset_name));
794 }
795 
796 /*
797  * Function:	be_get_node_data
798  * Description:	Helper function used to collect all the information to fill
799  *		in the be_node_list structure to be returned by be_list.
800  * Parameters:
801  *		zhp - Handle to the root dataset for the BE whose information
802  *		      we're collecting.
803  *		be_node - a pointer to the node structure we're filling in.
804  *		be_name - The BE name of the node whose information we're
805  *		          collecting.
806  *		current_be - the name of the currently active BE.
807  *		be_ds - The dataset name for the BE.
808  *
809  * Returns:
810  *		BE_SUCCESS - Success
811  *		be_errno_t - Failure
812  * Scope:
813  *		Private
814  */
815 static int
816 be_get_node_data(
817 	zfs_handle_t *zhp,
818 	be_node_list_t *be_node,
819 	char *be_name,
820 	const char *rpool,
821 	char *current_be,
822 	char *be_ds)
823 {
824 	char prop_buf[MAXPATHLEN];
825 	nvlist_t *userprops = NULL;
826 	nvlist_t *propval = NULL;
827 	nvlist_t *zone_propval = NULL;
828 	char *prop_str = NULL;
829 	char *zone_prop_str = NULL;
830 	char *grub_default_bootfs = NULL;
831 	zpool_handle_t *zphp = NULL;
832 	int err = 0;
833 
834 	if (be_node == NULL || be_name == NULL || current_be == NULL ||
835 	    be_ds == NULL) {
836 		be_print_err(gettext("be_get_node_data: invalid arguments, "
837 		    "can not be NULL\n"));
838 		return (BE_ERR_INVAL);
839 	}
840 
841 	errno = 0;
842 
843 	be_node->be_root_ds = strdup(be_ds);
844 	if ((err = errno) != 0 || be_node->be_root_ds == NULL) {
845 		be_print_err(gettext("be_get_node_data: failed to "
846 		    "copy root dataset name\n"));
847 		return (errno_to_be_err(err));
848 	}
849 
850 	be_node->be_node_name = strdup(be_name);
851 	if ((err = errno) != 0 || be_node->be_node_name == NULL) {
852 		be_print_err(gettext("be_get_node_data: failed to "
853 		    "copy BE name\n"));
854 		return (errno_to_be_err(err));
855 	}
856 	if (strncmp(be_name, current_be, MAXPATHLEN) == 0)
857 		be_node->be_active = B_TRUE;
858 	else
859 		be_node->be_active = B_FALSE;
860 
861 	be_node->be_rpool = strdup(rpool);
862 	if (be_node->be_rpool == NULL || (err = errno) != 0) {
863 		be_print_err(gettext("be_get_node_data: failed to "
864 		    "copy root pool name\n"));
865 		return (errno_to_be_err(err));
866 	}
867 
868 	be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED);
869 
870 	if (getzoneid() == GLOBAL_ZONEID) {
871 		if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
872 			be_print_err(gettext("be_get_node_data: failed to open "
873 			    "pool (%s): %s\n"), rpool,
874 			    libzfs_error_description(g_zfs));
875 			return (zfs_err_to_be_err(g_zfs));
876 		}
877 
878 		(void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf,
879 		    ZFS_MAXPROPLEN, NULL, B_FALSE);
880 		if (be_has_grub() && (be_default_grub_bootfs(rpool,
881 		    &grub_default_bootfs) == BE_SUCCESS) &&
882 		    grub_default_bootfs != NULL)
883 			if (strcmp(grub_default_bootfs, be_ds) == 0)
884 				be_node->be_active_on_boot = B_TRUE;
885 			else
886 				be_node->be_active_on_boot = B_FALSE;
887 		else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0)
888 			be_node->be_active_on_boot = B_TRUE;
889 		else
890 			be_node->be_active_on_boot = B_FALSE;
891 
892 		be_node->be_global_active = B_TRUE;
893 
894 		free(grub_default_bootfs);
895 		zpool_close(zphp);
896 	} else {
897 		if (be_zone_compare_uuids(be_node->be_root_ds))
898 			be_node->be_global_active = B_TRUE;
899 		else
900 			be_node->be_global_active = B_FALSE;
901 	}
902 
903 	/*
904 	 * If the dataset is mounted use the mount point
905 	 * returned from the zfs_is_mounted call. If the
906 	 * dataset is not mounted then pull the mount
907 	 * point information out of the zfs properties.
908 	 */
909 	be_node->be_mounted = zfs_is_mounted(zhp,
910 	    &(be_node->be_mntpt));
911 	if (!be_node->be_mounted) {
912 		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf,
913 		    ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0)
914 			be_node->be_mntpt = strdup(prop_buf);
915 		else
916 			return (zfs_err_to_be_err(g_zfs));
917 	}
918 
919 	be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp,
920 	    ZFS_PROP_CREATION);
921 
922 	/* Get all user properties used for libbe */
923 	if ((userprops = zfs_get_user_props(zhp)) == NULL) {
924 		be_node->be_policy_type = strdup(be_default_policy());
925 	} else {
926 		if (getzoneid() != GLOBAL_ZONEID) {
927 			if (nvlist_lookup_nvlist(userprops,
928 			    BE_ZONE_ACTIVE_PROPERTY, &zone_propval) != 0 ||
929 			    zone_propval == NULL) {
930 				be_node->be_active_on_boot = B_FALSE;
931 			} else {
932 				verify(nvlist_lookup_string(zone_propval,
933 				    ZPROP_VALUE, &zone_prop_str) == 0);
934 				if (strcmp(zone_prop_str, "on") == 0) {
935 					be_node->be_active_on_boot = B_TRUE;
936 				} else {
937 					be_node->be_active_on_boot = B_FALSE;
938 				}
939 			}
940 		}
941 
942 		if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
943 		    &propval) != 0 || propval == NULL) {
944 			be_node->be_policy_type =
945 			    strdup(be_default_policy());
946 		} else {
947 			verify(nvlist_lookup_string(propval, ZPROP_VALUE,
948 			    &prop_str) == 0);
949 			if (prop_str == NULL || strcmp(prop_str, "-") == 0 ||
950 			    strcmp(prop_str, "") == 0)
951 				be_node->be_policy_type =
952 				    strdup(be_default_policy());
953 			else
954 				be_node->be_policy_type = strdup(prop_str);
955 		}
956 		if (getzoneid() != GLOBAL_ZONEID) {
957 			if (nvlist_lookup_nvlist(userprops,
958 			    BE_ZONE_PARENTBE_PROPERTY, &propval) != 0 &&
959 			    nvlist_lookup_string(propval, ZPROP_VALUE,
960 			    &prop_str) == 0) {
961 				be_node->be_uuid_str = strdup(prop_str);
962 			}
963 		} else {
964 			if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY,
965 			    &propval) == 0 && nvlist_lookup_string(propval,
966 			    ZPROP_VALUE, &prop_str) == 0) {
967 				be_node->be_uuid_str = strdup(prop_str);
968 			}
969 		}
970 	}
971 
972 	/*
973 	 * Increment the dataset counter to include the root dataset
974 	 * of the BE.
975 	 */
976 	be_node->be_node_num_datasets++;
977 
978 	return (BE_SUCCESS);
979 }
980 
981 /*
982  * Function:	be_get_ds_data
983  * Description:	Helper function used by be_add_children_callback to collect
984  *		the dataset related information that will be returned by
985  *		be_list.
986  * Parameters:
987  *		zhp - Handle to the zfs dataset whose information we're
988  *		      collecting.
989  *		name - The name of the dataset we're processing.
990  *		dataset - A pointer to the be_dataset_list structure
991  *			  we're filling in.
992  *		node - The node structure that this dataset belongs to.
993  * Return:
994  *		BE_SUCCESS - Success
995  *		be_errno_t - Failure
996  * Scope:
997  *		Private
998  */
999 static int
1000 be_get_ds_data(
1001 	zfs_handle_t *zfshp,
1002 	char *name,
1003 	be_dataset_list_t *dataset,
1004 	be_node_list_t *node)
1005 {
1006 	char			prop_buf[ZFS_MAXPROPLEN];
1007 	nvlist_t		*propval = NULL;
1008 	nvlist_t		*userprops = NULL;
1009 	char			*prop_str = NULL;
1010 	int			err = 0;
1011 
1012 	if (zfshp == NULL || name == NULL || dataset == NULL || node == NULL) {
1013 		be_print_err(gettext("be_get_ds_data: invalid arguments, "
1014 		    "can not be NULL\n"));
1015 		return (BE_ERR_INVAL);
1016 	}
1017 
1018 	errno = 0;
1019 
1020 	dataset->be_dataset_name = strdup(name);
1021 	if ((err = errno) != 0) {
1022 		be_print_err(gettext("be_get_ds_data: failed to copy "
1023 		    "dataset name\n"));
1024 		return (errno_to_be_err(err));
1025 	}
1026 
1027 	dataset->be_ds_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED);
1028 
1029 	/*
1030 	 * If the dataset is mounted use the mount point
1031 	 * returned from the zfs_is_mounted call. If the
1032 	 * dataset is not mounted then pull the mount
1033 	 * point information out of the zfs properties.
1034 	 */
1035 	if (!(dataset->be_ds_mounted = zfs_is_mounted(zfshp,
1036 	    &(dataset->be_ds_mntpt)))) {
1037 		if (zfs_prop_get(zfshp, ZFS_PROP_MOUNTPOINT,
1038 		    prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0,
1039 		    B_FALSE) == 0)
1040 			dataset->be_ds_mntpt = strdup(prop_buf);
1041 		else
1042 			return (zfs_err_to_be_err(g_zfs));
1043 	}
1044 	dataset->be_ds_creation =
1045 	    (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION);
1046 
1047 	/*
1048 	 * Get the user property used for the libbe
1049 	 * cleaup policy
1050 	 */
1051 	if ((userprops = zfs_get_user_props(zfshp)) == NULL) {
1052 		dataset->be_ds_plcy_type =
1053 		    strdup(node->be_policy_type);
1054 	} else {
1055 		if (nvlist_lookup_nvlist(userprops,
1056 		    BE_POLICY_PROPERTY, &propval) != 0 ||
1057 		    propval == NULL) {
1058 			dataset->be_ds_plcy_type =
1059 			    strdup(node->be_policy_type);
1060 		} else {
1061 			verify(nvlist_lookup_string(propval,
1062 			    ZPROP_VALUE, &prop_str) == 0);
1063 			if (prop_str == NULL ||
1064 			    strcmp(prop_str, "-") == 0 ||
1065 			    strcmp(prop_str, "") == 0)
1066 				dataset->be_ds_plcy_type
1067 				    = strdup(node->be_policy_type);
1068 			else
1069 				dataset->be_ds_plcy_type = strdup(prop_str);
1070 		}
1071 	}
1072 
1073 	node->be_node_num_datasets++;
1074 	return (BE_SUCCESS);
1075 }
1076 
1077 /*
1078  * Function:	be_get_ss_data
1079  * Description: Helper function used by be_add_children_callback to collect
1080  *		the dataset related information that will be returned by
1081  *		be_list.
1082  * Parameters:
1083  *		zhp - Handle to the zfs snapshot whose information we're
1084  *		      collecting.
1085  *		name - The name of the snapshot we're processing.
1086  *		shapshot - A pointer to the be_snapshot_list structure
1087  *			   we're filling in.
1088  *		node - The node structure that this snapshot belongs to.
1089  * Returns:
1090  *		BE_SUCCESS - Success
1091  *		be_errno_t - Failure
1092  * Scope:
1093  *		Private
1094  */
1095 static int
1096 be_get_ss_data(
1097 	zfs_handle_t *zfshp,
1098 	char *name,
1099 	be_snapshot_list_t *snapshot,
1100 	be_node_list_t *node)
1101 {
1102 	nvlist_t	*propval = NULL;
1103 	nvlist_t	*userprops = NULL;
1104 	char		*prop_str = NULL;
1105 	int		err = 0;
1106 
1107 	if (zfshp == NULL || name == NULL || snapshot == NULL || node == NULL) {
1108 		be_print_err(gettext("be_get_ss_data: invalid arguments, "
1109 		    "can not be NULL\n"));
1110 		return (BE_ERR_INVAL);
1111 	}
1112 
1113 	errno = 0;
1114 
1115 	snapshot->be_snapshot_name = strdup(name);
1116 	if ((err = errno) != 0) {
1117 		be_print_err(gettext("be_get_ss_data: failed to copy name\n"));
1118 		return (errno_to_be_err(err));
1119 	}
1120 
1121 	snapshot->be_snapshot_creation = (time_t)zfs_prop_get_int(zfshp,
1122 	    ZFS_PROP_CREATION);
1123 
1124 	/*
1125 	 * Try to get this snapshot's cleanup policy from its
1126 	 * user properties first.  If not there, use default
1127 	 * cleanup policy.
1128 	 */
1129 	if ((userprops = zfs_get_user_props(zfshp)) != NULL &&
1130 	    nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
1131 	    &propval) == 0 && nvlist_lookup_string(propval,
1132 	    ZPROP_VALUE, &prop_str) == 0) {
1133 		snapshot->be_snapshot_type =
1134 		    strdup(prop_str);
1135 	} else {
1136 		snapshot->be_snapshot_type =
1137 		    strdup(be_default_policy());
1138 	}
1139 
1140 	snapshot->be_snapshot_space_used = zfs_prop_get_int(zfshp,
1141 	    ZFS_PROP_USED);
1142 
1143 	node->be_node_num_snapshots++;
1144 	return (BE_SUCCESS);
1145 }
1146 
1147 /*
1148  * Function:	be_list_alloc
1149  * Description: Helper function used to allocate memory for the various
1150  *		sructures that make up a BE node.
1151  * Parameters:
1152  *		err - Used to return any errors encountered.
1153  *			BE_SUCCESS - Success
1154  *			BE_ERR_NOMEM - Allocation failure
1155  *		size - The size of memory to allocate.
1156  * Returns:
1157  *		Success - A pointer to the allocated memory
1158  * 		Failure - NULL
1159  * Scope:
1160  *		Private
1161  */
1162 static void*
1163 be_list_alloc(int *err, size_t size)
1164 {
1165 	void *bep = NULL;
1166 
1167 	bep = calloc(1, size);
1168 	if (bep == NULL) {
1169 		be_print_err(gettext("be_list_alloc: memory "
1170 		    "allocation failed\n"));
1171 		*err = BE_ERR_NOMEM;
1172 	}
1173 	*err = BE_SUCCESS;
1174 	return (bep);
1175 }
1176 
1177 /*
1178  * Function:	be_get_zone_node_data
1179  * Description:	Helper function used to collect all the information to
1180  *		fill in the be_node_list structure to be returned by
1181  *              be_get_zone_list.
1182  * Parameters:
1183  *		be_node - a pointer to the node structure we're filling in.
1184  *		be_name - The BE name of the node whose information we're
1185  * Returns:
1186  *		BE_SUCCESS - Success
1187  *		be_errno_t - Failure
1188  * Scope:
1189  *		Private
1190  *
1191  * NOTE: This function currently only collects the zone BE name but when
1192  *       support for beadm/libbe in a zone is provided it will need to fill
1193  *       in the rest of the information needed for a zone BE.
1194  */
1195 static int
1196 be_get_zone_node_data(be_node_list_t *be_node, char *be_name)
1197 {
1198 	if ((be_node->be_node_name = strdup(be_name)) != NULL)
1199 		return (BE_SUCCESS);
1200 	return (BE_ERR_NOMEM);
1201 }
1202