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