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