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 }