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