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