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