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 2011 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_get_node_data: 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 char *prop_str = NULL;
828 char *grub_default_bootfs = NULL;
829 zpool_handle_t *zphp = NULL;
830 int err = 0;
831
832 if (be_node == NULL || be_name == NULL || current_be == NULL ||
833 be_ds == NULL) {
834 be_print_err(gettext("be_get_node_data: invalid arguments, "
835 "can not be NULL\n"));
836 return (BE_ERR_INVAL);
837 }
838
839 errno = 0;
840
841 be_node->be_root_ds = strdup(be_ds);
842 if ((err = errno) != 0 || be_node->be_root_ds == NULL) {
843 be_print_err(gettext("be_get_node_data: failed to "
844 "copy root dataset name\n"));
845 return (errno_to_be_err(err));
846 }
847
848 be_node->be_node_name = strdup(be_name);
849 if ((err = errno) != 0 || be_node->be_node_name == NULL) {
850 be_print_err(gettext("be_get_node_data: failed to "
851 "copy BE name\n"));
852 return (errno_to_be_err(err));
853 }
854 if (strncmp(be_name, current_be, MAXPATHLEN) == 0)
855 be_node->be_active = B_TRUE;
856 else
857 be_node->be_active = B_FALSE;
858
859 be_node->be_rpool = strdup(rpool);
860 if (be_node->be_rpool == NULL || (err = errno) != 0) {
861 be_print_err(gettext("be_get_node_data: failed to "
862 "copy root pool name\n"));
863 return (errno_to_be_err(err));
864 }
865
866 be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED);
867
868 if ((zphp = zpool_open(g_zfs, rpool)) == NULL) {
869 be_print_err(gettext("be_get_node_data: failed to open pool "
870 "(%s): %s\n"), rpool, libzfs_error_description(g_zfs));
871 return (zfs_err_to_be_err(g_zfs));
872 }
873
874 (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, ZFS_MAXPROPLEN,
875 NULL);
876 if (be_has_grub() &&
877 (be_default_grub_bootfs(rpool, &grub_default_bootfs)
878 == BE_SUCCESS) && grub_default_bootfs != NULL)
879 if (strcmp(grub_default_bootfs, be_ds) == 0)
880 be_node->be_active_on_boot = B_TRUE;
881 else
882 be_node->be_active_on_boot = B_FALSE;
883 else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0)
884 be_node->be_active_on_boot = B_TRUE;
885 else
886 be_node->be_active_on_boot = B_FALSE;
887 free(grub_default_bootfs);
888 zpool_close(zphp);
889
890 /*
891 * If the dataset is mounted use the mount point
892 * returned from the zfs_is_mounted call. If the
893 * dataset is not mounted then pull the mount
894 * point information out of the zfs properties.
895 */
896 be_node->be_mounted = zfs_is_mounted(zhp,
897 &(be_node->be_mntpt));
898 if (!be_node->be_mounted) {
899 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf,
900 ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0)
901 be_node->be_mntpt = strdup(prop_buf);
902 else
903 return (zfs_err_to_be_err(g_zfs));
904 }
905
906 be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp,
907 ZFS_PROP_CREATION);
908
909 /* Get all user properties used for libbe */
910 if ((userprops = zfs_get_user_props(zhp)) == NULL) {
911 be_node->be_policy_type = strdup(be_default_policy());
912 } else {
913 if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
914 &propval) != 0 || propval == NULL) {
915 be_node->be_policy_type =
916 strdup(be_default_policy());
917 } else {
918 verify(nvlist_lookup_string(propval, ZPROP_VALUE,
919 &prop_str) == 0);
920 if (prop_str == NULL || strcmp(prop_str, "-") == 0 ||
921 strcmp(prop_str, "") == 0)
922 be_node->be_policy_type =
923 strdup(be_default_policy());
924 else
925 be_node->be_policy_type = strdup(prop_str);
926 }
927
928 if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, &propval)
929 == 0 && nvlist_lookup_string(propval, ZPROP_VALUE,
930 &prop_str) == 0) {
931 be_node->be_uuid_str = strdup(prop_str);
932 }
933 }
934
935 /*
936 * Increment the dataset counter to include the root dataset
937 * of the BE.
938 */
939 be_node->be_node_num_datasets++;
940
941 return (BE_SUCCESS);
942 }
943
944 /*
945 * Function: be_get_ds_data
946 * Description: Helper function used by be_add_children_callback to collect
947 * the dataset related information that will be returned by
948 * be_list.
949 * Parameters:
950 * zhp - Handle to the zfs dataset whose information we're
951 * collecting.
952 * name - The name of the dataset we're processing.
953 * dataset - A pointer to the be_dataset_list structure
954 * we're filling in.
955 * node - The node structure that this dataset belongs to.
956 * Return:
957 * BE_SUCCESS - Success
958 * be_errno_t - Failure
959 * Scope:
960 * Private
961 */
962 static int
963 be_get_ds_data(
964 zfs_handle_t *zfshp,
965 char *name,
966 be_dataset_list_t *dataset,
967 be_node_list_t *node)
968 {
969 char prop_buf[ZFS_MAXPROPLEN];
970 nvlist_t *propval = NULL;
971 nvlist_t *userprops = NULL;
972 char *prop_str = NULL;
973 int err = 0;
974
975 if (zfshp == NULL || name == NULL || dataset == NULL || node == NULL) {
976 be_print_err(gettext("be_get_ds_data: invalid arguments, "
977 "can not be NULL\n"));
978 return (BE_ERR_INVAL);
979 }
980
981 errno = 0;
982
983 dataset->be_dataset_name = strdup(name);
984 if ((err = errno) != 0) {
985 be_print_err(gettext("be_get_ds_data: failed to copy "
986 "dataset name\n"));
987 return (errno_to_be_err(err));
988 }
989
990 dataset->be_ds_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED);
991
992 /*
993 * If the dataset is mounted use the mount point
994 * returned from the zfs_is_mounted call. If the
995 * dataset is not mounted then pull the mount
996 * point information out of the zfs properties.
997 */
998 if (!(dataset->be_ds_mounted = zfs_is_mounted(zfshp,
999 &(dataset->be_ds_mntpt)))) {
1000 if (zfs_prop_get(zfshp, ZFS_PROP_MOUNTPOINT,
1001 prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0,
1002 B_FALSE) == 0)
1003 dataset->be_ds_mntpt = strdup(prop_buf);
1004 else
1005 return (zfs_err_to_be_err(g_zfs));
1006 }
1007 dataset->be_ds_creation =
1008 (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION);
1009
1010 /*
1011 * Get the user property used for the libbe
1012 * cleaup policy
1013 */
1014 if ((userprops = zfs_get_user_props(zfshp)) == NULL) {
1015 dataset->be_ds_plcy_type =
1016 strdup(node->be_policy_type);
1017 } else {
1018 if (nvlist_lookup_nvlist(userprops,
1019 BE_POLICY_PROPERTY, &propval) != 0 ||
1020 propval == NULL) {
1021 dataset->be_ds_plcy_type =
1022 strdup(node->be_policy_type);
1023 } else {
1024 verify(nvlist_lookup_string(propval,
1025 ZPROP_VALUE, &prop_str) == 0);
1026 if (prop_str == NULL ||
1027 strcmp(prop_str, "-") == 0 ||
1028 strcmp(prop_str, "") == 0)
1029 dataset->be_ds_plcy_type
1030 = strdup(node->be_policy_type);
1031 else
1032 dataset->be_ds_plcy_type = strdup(prop_str);
1033 }
1034 }
1035
1036 node->be_node_num_datasets++;
1037 return (BE_SUCCESS);
1038 }
1039
1040 /*
1041 * Function: be_get_ss_data
1042 * Description: Helper function used by be_add_children_callback to collect
1043 * the dataset related information that will be returned by
1044 * be_list.
1045 * Parameters:
1046 * zhp - Handle to the zfs snapshot whose information we're
1047 * collecting.
1048 * name - The name of the snapshot we're processing.
1049 * shapshot - A pointer to the be_snapshot_list structure
1050 * we're filling in.
1051 * node - The node structure that this snapshot belongs to.
1052 * Returns:
1053 * BE_SUCCESS - Success
1054 * be_errno_t - Failure
1055 * Scope:
1056 * Private
1057 */
1058 static int
1059 be_get_ss_data(
1060 zfs_handle_t *zfshp,
1061 char *name,
1062 be_snapshot_list_t *snapshot,
1063 be_node_list_t *node)
1064 {
1065 nvlist_t *propval = NULL;
1066 nvlist_t *userprops = NULL;
1067 char *prop_str = NULL;
1068 int err = 0;
1069
1070 if (zfshp == NULL || name == NULL || snapshot == NULL || node == NULL) {
1071 be_print_err(gettext("be_get_ss_data: invalid arguments, "
1072 "can not be NULL\n"));
1073 return (BE_ERR_INVAL);
1074 }
1075
1076 errno = 0;
1077
1078 snapshot->be_snapshot_name = strdup(name);
1079 if ((err = errno) != 0) {
1080 be_print_err(gettext("be_get_ss_data: failed to copy name\n"));
1081 return (errno_to_be_err(err));
1082 }
1083
1084 snapshot->be_snapshot_creation = (time_t)zfs_prop_get_int(zfshp,
1085 ZFS_PROP_CREATION);
1086
1087 /*
1088 * Try to get this snapshot's cleanup policy from its
1089 * user properties first. If not there, use default
1090 * cleanup policy.
1091 */
1092 if ((userprops = zfs_get_user_props(zfshp)) != NULL &&
1093 nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY,
1094 &propval) == 0 && nvlist_lookup_string(propval,
1095 ZPROP_VALUE, &prop_str) == 0) {
1096 snapshot->be_snapshot_type =
1097 strdup(prop_str);
1098 } else {
1099 snapshot->be_snapshot_type =
1100 strdup(be_default_policy());
1101 }
1102
1103 snapshot->be_snapshot_space_used = zfs_prop_get_int(zfshp,
1104 ZFS_PROP_USED);
1105
1106 node->be_node_num_snapshots++;
1107 return (BE_SUCCESS);
1108 }
1109
1110 /*
1111 * Function: be_list_alloc
1112 * Description: Helper function used to allocate memory for the various
1113 * sructures that make up a BE node.
1114 * Parameters:
1115 * err - Used to return any errors encountered.
1116 * BE_SUCCESS - Success
1117 * BE_ERR_NOMEM - Allocation failure
1118 * size - The size of memory to allocate.
1119 * Returns:
1120 * Success - A pointer to the allocated memory
1121 * Failure - NULL
1122 * Scope:
1123 * Private
1124 */
1125 static void*
1126 be_list_alloc(int *err, size_t size)
1127 {
1128 void *bep = NULL;
1129
1130 bep = calloc(1, size);
1131 if (bep == NULL) {
1132 be_print_err(gettext("be_list_alloc: memory "
1133 "allocation failed\n"));
1134 *err = BE_ERR_NOMEM;
1135 }
1136 *err = BE_SUCCESS;
1137 return (bep);
1138 }
1139
1140 /*
1141 * Function: be_get_zone_node_data
1142 * Description: Helper function used to collect all the information to
1143 * fill in the be_node_list structure to be returned by
1144 * be_get_zone_list.
1145 * Parameters:
1146 * be_node - a pointer to the node structure we're filling in.
1147 * be_name - The BE name of the node whose information we're
1148 * Returns:
1149 * BE_SUCCESS - Success
1150 * be_errno_t - Failure
1151 * Scope:
1152 * Private
1153 *
1154 * NOTE: This function currently only collects the zone BE name but when
1155 * support for beadm/libbe in a zone is provided it will need to fill
1156 * in the rest of the information needed for a zone BE.
1157 */
1158 static int
1159 be_get_zone_node_data(be_node_list_t *be_node, char *be_name)
1160 {
1161 if ((be_node->be_node_name = strdup(be_name)) != NULL)
1162 return (BE_SUCCESS);
1163 return (BE_ERR_NOMEM);
1164 }