be_list.c revision 13013:3c7681e3e323
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 */
45typedef 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 */
56static int be_add_children_callback(zfs_handle_t *zhp, void *data);
57static int be_get_list_callback(zpool_handle_t *, void *);
58static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *,
59    const char *, char *, char *);
60static int be_get_zone_node_data(be_node_list_t *, char *);
61static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *,
62    be_node_list_t *);
63static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *,
64    be_node_list_t *);
65static void be_sort_list(be_node_list_t **);
66static int be_qsort_compare_BEs(const void *, const void *);
67static int be_qsort_compare_snapshots(const void *x, const void *y);
68static int be_qsort_compare_datasets(const void *x, const void *y);
69static void *be_list_alloc(int *, size_t);
70
71/*
72 * Private data.
73 */
74static char be_container_ds[MAXPATHLEN];
75static 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 */
104int
105be_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 */
150int
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 */
217void
218be_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 */
273int
274be_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
319cleanup:
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 */
346static int
347be_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 */
451static int
452be_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 */
594static void
595be_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];
649end_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];
677end_dataset:
678			free(slist);
679		}
680	}
681free:
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 */
695static int
696be_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 */
718static int
719be_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 */
741static int
742be_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 */
773static int
774be_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 */
920static int
921be_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 */
1016static int
1017be_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 */
1083static void*
1084be_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 */
1116static int
1117be_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