1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 28 * Copyright 2015 Toomas Soome <tsoome@me.com> 29 * Copyright 2015 Gary Mills 30 * Copyright (c) 2016 Martin Matuska. All rights reserved. 31 */ 32 33 #include <assert.h> 34 #include <libintl.h> 35 #include <libnvpair.h> 36 #include <libzfs.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <strings.h> 41 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <unistd.h> 44 #include <errno.h> 45 46 #include <libbe.h> 47 #include <libbe_priv.h> 48 49 /* 50 * Callback data used for zfs_iter calls. 51 */ 52 typedef struct list_callback_data { 53 char *zpool_name; 54 char *be_name; 55 be_node_list_t *be_nodes_head; 56 be_node_list_t *be_nodes; 57 char current_be[MAXPATHLEN]; 58 } list_callback_data_t; 59 60 /* 61 * Private function prototypes 62 */ 63 static int be_add_children_callback(zfs_handle_t *zhp, void *data); 64 static int be_get_list_callback(zpool_handle_t *, void *); 65 static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *, 66 const char *, char *, char *); 67 static int be_get_zone_node_data(be_node_list_t *, char *); 68 static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *, 69 be_node_list_t *); 70 static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *, 71 be_node_list_t *); 72 static int be_sort_list(be_node_list_t **, 73 int (*)(const void *, const void *)); 74 static int be_qsort_compare_BEs_name(const void *, const void *); 75 static int be_qsort_compare_BEs_name_rev(const void *, const void *); 76 static int be_qsort_compare_BEs_date(const void *, const void *); 77 static int be_qsort_compare_BEs_date_rev(const void *, const void *); 78 static int be_qsort_compare_BEs_space(const void *, const void *); 79 static int be_qsort_compare_BEs_space_rev(const void *, const void *); 80 static int be_qsort_compare_snapshots(const void *x, const void *y); 81 static int be_qsort_compare_datasets(const void *x, const void *y); 82 static void *be_list_alloc(int *, size_t); 83 84 /* 85 * Private data. 86 */ 87 static char be_container_ds[MAXPATHLEN]; 88 static boolean_t zone_be = B_FALSE; 89 90 /* ******************************************************************** */ 91 /* Public Functions */ 92 /* ******************************************************************** */ 93 94 /* 95 * Function: be_list 96 * Description: Calls _be_list which finds all the BEs on the system and 97 * returns the datasets and snapshots belonging to each BE. 98 * Also data, such as dataset and snapshot properties, 99 * for each BE and their snapshots and datasets is 100 * returned. The data returned is as described in the 101 * be_dataset_list_t, be_snapshot_list_t and be_node_list_t 102 * structures. 103 * Parameters: 104 * be_name - The name of the BE to look up. 105 * If NULL a list of all BEs will be returned. 106 * be_nodes - A reference pointer to the list of BEs. The list 107 * structure will be allocated by _be_list and must 108 * be freed by a call to be_free_list. If there are no 109 * BEs found on the system this reference will be 110 * set to NULL. 111 * Return: 112 * BE_SUCCESS - Success 113 * be_errno_t - Failure 114 * Scope: 115 * Public 116 */ 117 int 118 be_list(char *be_name, be_node_list_t **be_nodes) 119 { 120 int ret = BE_SUCCESS; 121 122 /* Initialize libzfs handle */ 123 if (!be_zfs_init()) 124 return (BE_ERR_INIT); 125 126 /* Validate be_name if its not NULL */ 127 if (be_name != NULL) { 128 if (!be_valid_be_name(be_name)) { 129 be_print_err(gettext("be_list: " 130 "invalid BE name %s\n"), be_name); 131 return (BE_ERR_INVAL); 132 } 133 } 134 135 ret = _be_list(be_name, be_nodes); 136 137 be_zfs_fini(); 138 139 return (ret); 140 } 141 142 /* 143 * Function: be_sort 144 * Description: Sort BE node list 145 * Parameters: 146 * pointer to address of list head 147 * sort order type 148 * Return: 149 * BE_SUCCESS - Success 150 * be_errno_t - Failure 151 * Side effect: 152 * node list sorted by name 153 * Scope: 154 * Public 155 */ 156 int 157 be_sort(be_node_list_t **be_nodes, int order) 158 { 159 int (*compar)(const void *, const void *) = be_qsort_compare_BEs_date; 160 161 if (be_nodes == NULL) 162 return (BE_ERR_INVAL); 163 164 switch (order) { 165 case BE_SORT_UNSPECIFIED: 166 case BE_SORT_DATE: 167 compar = be_qsort_compare_BEs_date; 168 break; 169 case BE_SORT_DATE_REV: 170 compar = be_qsort_compare_BEs_date_rev; 171 break; 172 case BE_SORT_NAME: 173 compar = be_qsort_compare_BEs_name; 174 break; 175 case BE_SORT_NAME_REV: 176 compar = be_qsort_compare_BEs_name_rev; 177 break; 178 case BE_SORT_SPACE: 179 compar = be_qsort_compare_BEs_space; 180 break; 181 case BE_SORT_SPACE_REV: 182 compar = be_qsort_compare_BEs_space_rev; 183 break; 184 default: 185 be_print_err(gettext("be_sort: invalid sort order %d\n"), 186 order); 187 return (BE_ERR_INVAL); 188 } 189 190 return (be_sort_list(be_nodes, compar)); 191 } 192 193 /* ******************************************************************** */ 194 /* Semi-Private Functions */ 195 /* ******************************************************************** */ 196 197 /* 198 * Function: _be_list 199 * Description: This does the actual work described in be_list. 200 * Parameters: 201 * be_name - The name of the BE to look up. 202 * If NULL a list of all BEs will be returned. 203 * be_nodes - A reference pointer to the list of BEs. The list 204 * structure will be allocated here and must 205 * be freed by a call to be_free_list. If there are no 206 * BEs found on the system this reference will be 207 * set to NULL. 208 * Return: 209 * BE_SUCCESS - Success 210 * be_errno_t - Failure 211 * Scope: 212 * Semi-private (library wide use only) 213 */ 214 int 215 _be_list(char *be_name, be_node_list_t **be_nodes) 216 { 217 list_callback_data_t cb = { 0 }; 218 be_transaction_data_t bt = { 0 }; 219 int ret = BE_SUCCESS; 220 int sret; 221 zpool_handle_t *zphp; 222 char *rpool = NULL; 223 struct be_defaults be_defaults; 224 225 if (be_nodes == NULL) 226 return (BE_ERR_INVAL); 227 228 be_get_defaults(&be_defaults); 229 230 if (be_find_current_be(&bt) != BE_SUCCESS) { 231 /* 232 * We were unable to find a currently booted BE which 233 * probably means that we're not booted in a BE envoronment. 234 * None of the BE's will be marked as the active BE. 235 */ 236 (void) strcpy(cb.current_be, "-"); 237 } else { 238 (void) strncpy(cb.current_be, bt.obe_name, 239 sizeof (cb.current_be)); 240 rpool = bt.obe_zpool; 241 } 242 243 /* 244 * If be_name is NULL we'll look for all BE's on the system. 245 * If not then we will only return data for the specified BE. 246 */ 247 if (be_name != NULL) 248 cb.be_name = strdup(be_name); 249 250 if (be_defaults.be_deflt_rpool_container && rpool != NULL) { 251 if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { 252 be_print_err(gettext("be_list: failed to " 253 "open rpool (%s): %s\n"), rpool, 254 libzfs_error_description(g_zfs)); 255 free(cb.be_name); 256 return (zfs_err_to_be_err(g_zfs)); 257 } 258 259 ret = be_get_list_callback(zphp, &cb); 260 } else { 261 if ((zpool_iter(g_zfs, be_get_list_callback, &cb)) != 0) { 262 if (cb.be_nodes_head != NULL) { 263 be_free_list(cb.be_nodes_head); 264 cb.be_nodes_head = NULL; 265 cb.be_nodes = NULL; 266 } 267 ret = BE_ERR_BE_NOENT; 268 } 269 } 270 271 if (cb.be_nodes_head == NULL) { 272 if (be_name != NULL) 273 be_print_err(gettext("be_list: BE (%s) does not " 274 "exist\n"), be_name); 275 else 276 be_print_err(gettext("be_list: No BE's found\n")); 277 ret = BE_ERR_BE_NOENT; 278 } 279 280 *be_nodes = cb.be_nodes_head; 281 282 free(cb.be_name); 283 284 sret = be_sort(be_nodes, BE_SORT_DATE); 285 286 return ((ret == BE_SUCCESS) ? sret : ret); 287 } 288 289 /* 290 * Function: be_free_list 291 * Description: Frees up all the data allocated for the list of BEs, 292 * datasets and snapshots returned by be_list. 293 * Parameters: 294 * be_node - be_nodes_t structure returned from call to be_list. 295 * Returns: 296 * none 297 * Scope: 298 * Semi-private (library wide use only) 299 */ 300 void 301 be_free_list(be_node_list_t *be_nodes) 302 { 303 be_node_list_t *temp_node = NULL; 304 be_node_list_t *list = be_nodes; 305 306 while (list != NULL) { 307 be_dataset_list_t *datasets = list->be_node_datasets; 308 be_snapshot_list_t *snapshots = list->be_node_snapshots; 309 310 while (datasets != NULL) { 311 be_dataset_list_t *temp_ds = datasets; 312 datasets = datasets->be_next_dataset; 313 free(temp_ds->be_dataset_name); 314 free(temp_ds->be_ds_mntpt); 315 free(temp_ds->be_ds_plcy_type); 316 free(temp_ds); 317 } 318 319 while (snapshots != NULL) { 320 be_snapshot_list_t *temp_ss = snapshots; 321 snapshots = snapshots->be_next_snapshot; 322 free(temp_ss->be_snapshot_name); 323 free(temp_ss->be_snapshot_type); 324 free(temp_ss); 325 } 326 327 temp_node = list; 328 list = list->be_next_node; 329 free(temp_node->be_node_name); 330 free(temp_node->be_root_ds); 331 free(temp_node->be_rpool); 332 free(temp_node->be_mntpt); 333 free(temp_node->be_policy_type); 334 free(temp_node->be_uuid_str); 335 free(temp_node); 336 } 337 } 338 339 /* 340 * Function: be_get_zone_be_list 341 * Description: Finds all the BEs for this zone on the system. 342 * Parameters: 343 * zone_be_name - The name of the BE to look up. 344 * zone_be_container_ds - The dataset for the zone. 345 * zbe_nodes - A reference pointer to the list of BEs. The list 346 * structure will be allocated here and must 347 * be freed by a call to be_free_list. If there are no 348 * BEs found on the system this reference will be 349 * set to NULL. 350 * Return: 351 * BE_SUCCESS - Success 352 * be_errno_t - Failure 353 * Scope: 354 * Semi-private (library wide use only) 355 */ 356 int 357 /* LINTED */ 358 be_get_zone_be_list(char *zone_be_name, char *zone_be_container_ds, 359 be_node_list_t **zbe_nodes) 360 { 361 zfs_handle_t *zhp = NULL; 362 list_callback_data_t cb = { 0 }; 363 int ret = BE_SUCCESS; 364 365 if (zbe_nodes == NULL) 366 return (BE_ERR_INVAL); 367 368 if (!zfs_dataset_exists(g_zfs, zone_be_container_ds, 369 ZFS_TYPE_FILESYSTEM)) { 370 return (BE_ERR_BE_NOENT); 371 } 372 373 zone_be = B_TRUE; 374 375 if ((zhp = zfs_open(g_zfs, zone_be_container_ds, 376 ZFS_TYPE_FILESYSTEM)) == NULL) { 377 be_print_err(gettext("be_get_zone_be_list: failed to open " 378 "the zone BE dataset %s: %s\n"), zone_be_container_ds, 379 libzfs_error_description(g_zfs)); 380 ret = zfs_err_to_be_err(g_zfs); 381 goto cleanup; 382 } 383 384 (void) strcpy(be_container_ds, zone_be_container_ds); 385 386 if (cb.be_nodes_head == NULL) { 387 if ((cb.be_nodes_head = be_list_alloc(&ret, 388 sizeof (be_node_list_t))) == NULL) { 389 ZFS_CLOSE(zhp); 390 goto cleanup; 391 } 392 cb.be_nodes = cb.be_nodes_head; 393 } 394 if (ret == 0) 395 ret = zfs_iter_filesystems(zhp, be_add_children_callback, &cb); 396 ZFS_CLOSE(zhp); 397 398 *zbe_nodes = cb.be_nodes_head; 399 400 cleanup: 401 zone_be = B_FALSE; 402 403 return (ret); 404 } 405 406 /* ******************************************************************** */ 407 /* Private Functions */ 408 /* ******************************************************************** */ 409 410 /* 411 * Function: be_get_list_callback 412 * Description: Callback function used by zfs_iter to look through all 413 * the pools on the system looking for BEs. If a BE name was 414 * specified only that BE's information will be collected and 415 * returned. 416 * Parameters: 417 * zlp - handle to the first zfs dataset. (provided by the 418 * zfs_iter_* call) 419 * data - pointer to the callback data and where we'll pass 420 * the BE information back. 421 * Returns: 422 * 0 - Success 423 * be_errno_t - Failure 424 * Scope: 425 * Private 426 */ 427 static int 428 be_get_list_callback(zpool_handle_t *zlp, void *data) 429 { 430 list_callback_data_t *cb = (list_callback_data_t *)data; 431 char be_ds[MAXPATHLEN]; 432 char *open_ds = NULL; 433 char *rpool = NULL; 434 zfs_handle_t *zhp = NULL; 435 int ret = 0; 436 437 cb->zpool_name = rpool = (char *)zpool_get_name(zlp); 438 439 /* 440 * Generate string for the BE container dataset 441 */ 442 be_make_container_ds(rpool, be_container_ds, 443 sizeof (be_container_ds)); 444 445 /* 446 * If a BE name was specified we use it's root dataset in place of 447 * the container dataset. This is because we only want to collect 448 * the information for the specified BE. 449 */ 450 if (cb->be_name != NULL) { 451 if (!be_valid_be_name(cb->be_name)) 452 return (BE_ERR_INVAL); 453 /* 454 * Generate string for the BE root dataset 455 */ 456 be_make_root_ds(rpool, cb->be_name, be_ds, sizeof (be_ds)); 457 open_ds = be_ds; 458 } else { 459 open_ds = be_container_ds; 460 } 461 462 /* 463 * Check if the dataset exists 464 */ 465 if (!zfs_dataset_exists(g_zfs, open_ds, 466 ZFS_TYPE_FILESYSTEM)) { 467 /* 468 * The specified dataset does not exist in this pool or 469 * there are no valid BE's in this pool. Try the next zpool. 470 */ 471 zpool_close(zlp); 472 return (0); 473 } 474 475 if ((zhp = zfs_open(g_zfs, open_ds, ZFS_TYPE_FILESYSTEM)) == NULL) { 476 be_print_err(gettext("be_get_list_callback: failed to open " 477 "the BE dataset %s: %s\n"), open_ds, 478 libzfs_error_description(g_zfs)); 479 ret = zfs_err_to_be_err(g_zfs); 480 zpool_close(zlp); 481 return (ret); 482 } 483 484 /* 485 * If a BE name was specified we iterate through the datasets 486 * and snapshots for this BE only. Otherwise we will iterate 487 * through the next level of datasets to find all the BE's 488 * within the pool 489 */ 490 if (cb->be_name != NULL) { 491 if (cb->be_nodes_head == NULL) { 492 if ((cb->be_nodes_head = be_list_alloc(&ret, 493 sizeof (be_node_list_t))) == NULL) { 494 ZFS_CLOSE(zhp); 495 zpool_close(zlp); 496 return (ret); 497 } 498 cb->be_nodes = cb->be_nodes_head; 499 } 500 501 if ((ret = be_get_node_data(zhp, cb->be_nodes, cb->be_name, 502 rpool, cb->current_be, be_ds)) != BE_SUCCESS) { 503 ZFS_CLOSE(zhp); 504 zpool_close(zlp); 505 return (ret); 506 } 507 ret = zfs_iter_snapshots(zhp, B_FALSE, be_add_children_callback, 508 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 (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 } 1382