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