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