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 */ 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_list: 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 nvlist_t *zone_propval = NULL; 828 char *prop_str = NULL; 829 char *zone_prop_str = NULL; 830 char *grub_default_bootfs = NULL; 831 zpool_handle_t *zphp = NULL; 832 int err = 0; 833 834 if (be_node == NULL || be_name == NULL || current_be == NULL || 835 be_ds == NULL) { 836 be_print_err(gettext("be_get_node_data: invalid arguments, " 837 "can not be NULL\n")); 838 return (BE_ERR_INVAL); 839 } 840 841 errno = 0; 842 843 be_node->be_root_ds = strdup(be_ds); 844 if ((err = errno) != 0 || be_node->be_root_ds == NULL) { 845 be_print_err(gettext("be_get_node_data: failed to " 846 "copy root dataset name\n")); 847 return (errno_to_be_err(err)); 848 } 849 850 be_node->be_node_name = strdup(be_name); 851 if ((err = errno) != 0 || be_node->be_node_name == NULL) { 852 be_print_err(gettext("be_get_node_data: failed to " 853 "copy BE name\n")); 854 return (errno_to_be_err(err)); 855 } 856 if (strncmp(be_name, current_be, MAXPATHLEN) == 0) 857 be_node->be_active = B_TRUE; 858 else 859 be_node->be_active = B_FALSE; 860 861 be_node->be_rpool = strdup(rpool); 862 if (be_node->be_rpool == NULL || (err = errno) != 0) { 863 be_print_err(gettext("be_get_node_data: failed to " 864 "copy root pool name\n")); 865 return (errno_to_be_err(err)); 866 } 867 868 be_node->be_space_used = zfs_prop_get_int(zhp, ZFS_PROP_USED); 869 870 if (getzoneid() == GLOBAL_ZONEID) { 871 if ((zphp = zpool_open(g_zfs, rpool)) == NULL) { 872 be_print_err(gettext("be_get_node_data: failed to open " 873 "pool (%s): %s\n"), rpool, 874 libzfs_error_description(g_zfs)); 875 return (zfs_err_to_be_err(g_zfs)); 876 } 877 878 (void) zpool_get_prop(zphp, ZPOOL_PROP_BOOTFS, prop_buf, 879 ZFS_MAXPROPLEN, NULL, B_FALSE); 880 if (be_has_grub() && (be_default_grub_bootfs(rpool, 881 &grub_default_bootfs) == BE_SUCCESS) && 882 grub_default_bootfs != NULL) 883 if (strcmp(grub_default_bootfs, be_ds) == 0) 884 be_node->be_active_on_boot = B_TRUE; 885 else 886 be_node->be_active_on_boot = B_FALSE; 887 else if (prop_buf != NULL && strcmp(prop_buf, be_ds) == 0) 888 be_node->be_active_on_boot = B_TRUE; 889 else 890 be_node->be_active_on_boot = B_FALSE; 891 892 be_node->be_global_active = B_TRUE; 893 894 free(grub_default_bootfs); 895 zpool_close(zphp); 896 } else { 897 if (be_zone_compare_uuids(be_node->be_root_ds)) 898 be_node->be_global_active = B_TRUE; 899 else 900 be_node->be_global_active = B_FALSE; 901 } 902 903 /* 904 * If the dataset is mounted use the mount point 905 * returned from the zfs_is_mounted call. If the 906 * dataset is not mounted then pull the mount 907 * point information out of the zfs properties. 908 */ 909 be_node->be_mounted = zfs_is_mounted(zhp, 910 &(be_node->be_mntpt)); 911 if (!be_node->be_mounted) { 912 if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop_buf, 913 ZFS_MAXPROPLEN, NULL, NULL, 0, B_FALSE) == 0) 914 be_node->be_mntpt = strdup(prop_buf); 915 else 916 return (zfs_err_to_be_err(g_zfs)); 917 } 918 919 be_node->be_node_creation = (time_t)zfs_prop_get_int(zhp, 920 ZFS_PROP_CREATION); 921 922 /* Get all user properties used for libbe */ 923 if ((userprops = zfs_get_user_props(zhp)) == NULL) { 924 be_node->be_policy_type = strdup(be_default_policy()); 925 } else { 926 if (getzoneid() != GLOBAL_ZONEID) { 927 if (nvlist_lookup_nvlist(userprops, 928 BE_ZONE_ACTIVE_PROPERTY, &zone_propval) != 0 || 929 zone_propval == NULL) { 930 be_node->be_active_on_boot = B_FALSE; 931 } else { 932 verify(nvlist_lookup_string(zone_propval, 933 ZPROP_VALUE, &zone_prop_str) == 0); 934 if (strcmp(zone_prop_str, "on") == 0) { 935 be_node->be_active_on_boot = B_TRUE; 936 } else { 937 be_node->be_active_on_boot = B_FALSE; 938 } 939 } 940 } 941 942 if (nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY, 943 &propval) != 0 || propval == NULL) { 944 be_node->be_policy_type = 945 strdup(be_default_policy()); 946 } else { 947 verify(nvlist_lookup_string(propval, ZPROP_VALUE, 948 &prop_str) == 0); 949 if (prop_str == NULL || strcmp(prop_str, "-") == 0 || 950 strcmp(prop_str, "") == 0) 951 be_node->be_policy_type = 952 strdup(be_default_policy()); 953 else 954 be_node->be_policy_type = strdup(prop_str); 955 } 956 if (getzoneid() != GLOBAL_ZONEID) { 957 if (nvlist_lookup_nvlist(userprops, 958 BE_ZONE_PARENTBE_PROPERTY, &propval) != 0 && 959 nvlist_lookup_string(propval, ZPROP_VALUE, 960 &prop_str) == 0) { 961 be_node->be_uuid_str = strdup(prop_str); 962 } 963 } else { 964 if (nvlist_lookup_nvlist(userprops, BE_UUID_PROPERTY, 965 &propval) == 0 && nvlist_lookup_string(propval, 966 ZPROP_VALUE, &prop_str) == 0) { 967 be_node->be_uuid_str = strdup(prop_str); 968 } 969 } 970 } 971 972 /* 973 * Increment the dataset counter to include the root dataset 974 * of the BE. 975 */ 976 be_node->be_node_num_datasets++; 977 978 return (BE_SUCCESS); 979 } 980 981 /* 982 * Function: be_get_ds_data 983 * Description: Helper function used by be_add_children_callback to collect 984 * the dataset related information that will be returned by 985 * be_list. 986 * Parameters: 987 * zhp - Handle to the zfs dataset whose information we're 988 * collecting. 989 * name - The name of the dataset we're processing. 990 * dataset - A pointer to the be_dataset_list structure 991 * we're filling in. 992 * node - The node structure that this dataset belongs to. 993 * Return: 994 * BE_SUCCESS - Success 995 * be_errno_t - Failure 996 * Scope: 997 * Private 998 */ 999 static int 1000 be_get_ds_data( 1001 zfs_handle_t *zfshp, 1002 char *name, 1003 be_dataset_list_t *dataset, 1004 be_node_list_t *node) 1005 { 1006 char prop_buf[ZFS_MAXPROPLEN]; 1007 nvlist_t *propval = NULL; 1008 nvlist_t *userprops = NULL; 1009 char *prop_str = NULL; 1010 int err = 0; 1011 1012 if (zfshp == NULL || name == NULL || dataset == NULL || node == NULL) { 1013 be_print_err(gettext("be_get_ds_data: invalid arguments, " 1014 "can not be NULL\n")); 1015 return (BE_ERR_INVAL); 1016 } 1017 1018 errno = 0; 1019 1020 dataset->be_dataset_name = strdup(name); 1021 if ((err = errno) != 0) { 1022 be_print_err(gettext("be_get_ds_data: failed to copy " 1023 "dataset name\n")); 1024 return (errno_to_be_err(err)); 1025 } 1026 1027 dataset->be_ds_space_used = zfs_prop_get_int(zfshp, ZFS_PROP_USED); 1028 1029 /* 1030 * If the dataset is mounted use the mount point 1031 * returned from the zfs_is_mounted call. If the 1032 * dataset is not mounted then pull the mount 1033 * point information out of the zfs properties. 1034 */ 1035 if (!(dataset->be_ds_mounted = zfs_is_mounted(zfshp, 1036 &(dataset->be_ds_mntpt)))) { 1037 if (zfs_prop_get(zfshp, ZFS_PROP_MOUNTPOINT, 1038 prop_buf, ZFS_MAXPROPLEN, NULL, NULL, 0, 1039 B_FALSE) == 0) 1040 dataset->be_ds_mntpt = strdup(prop_buf); 1041 else 1042 return (zfs_err_to_be_err(g_zfs)); 1043 } 1044 dataset->be_ds_creation = 1045 (time_t)zfs_prop_get_int(zfshp, ZFS_PROP_CREATION); 1046 1047 /* 1048 * Get the user property used for the libbe 1049 * cleaup policy 1050 */ 1051 if ((userprops = zfs_get_user_props(zfshp)) == NULL) { 1052 dataset->be_ds_plcy_type = 1053 strdup(node->be_policy_type); 1054 } else { 1055 if (nvlist_lookup_nvlist(userprops, 1056 BE_POLICY_PROPERTY, &propval) != 0 || 1057 propval == NULL) { 1058 dataset->be_ds_plcy_type = 1059 strdup(node->be_policy_type); 1060 } else { 1061 verify(nvlist_lookup_string(propval, 1062 ZPROP_VALUE, &prop_str) == 0); 1063 if (prop_str == NULL || 1064 strcmp(prop_str, "-") == 0 || 1065 strcmp(prop_str, "") == 0) 1066 dataset->be_ds_plcy_type 1067 = strdup(node->be_policy_type); 1068 else 1069 dataset->be_ds_plcy_type = strdup(prop_str); 1070 } 1071 } 1072 1073 node->be_node_num_datasets++; 1074 return (BE_SUCCESS); 1075 } 1076 1077 /* 1078 * Function: be_get_ss_data 1079 * Description: Helper function used by be_add_children_callback to collect 1080 * the dataset related information that will be returned by 1081 * be_list. 1082 * Parameters: 1083 * zhp - Handle to the zfs snapshot whose information we're 1084 * collecting. 1085 * name - The name of the snapshot we're processing. 1086 * shapshot - A pointer to the be_snapshot_list structure 1087 * we're filling in. 1088 * node - The node structure that this snapshot belongs to. 1089 * Returns: 1090 * BE_SUCCESS - Success 1091 * be_errno_t - Failure 1092 * Scope: 1093 * Private 1094 */ 1095 static int 1096 be_get_ss_data( 1097 zfs_handle_t *zfshp, 1098 char *name, 1099 be_snapshot_list_t *snapshot, 1100 be_node_list_t *node) 1101 { 1102 nvlist_t *propval = NULL; 1103 nvlist_t *userprops = NULL; 1104 char *prop_str = NULL; 1105 int err = 0; 1106 1107 if (zfshp == NULL || name == NULL || snapshot == NULL || node == NULL) { 1108 be_print_err(gettext("be_get_ss_data: invalid arguments, " 1109 "can not be NULL\n")); 1110 return (BE_ERR_INVAL); 1111 } 1112 1113 errno = 0; 1114 1115 snapshot->be_snapshot_name = strdup(name); 1116 if ((err = errno) != 0) { 1117 be_print_err(gettext("be_get_ss_data: failed to copy name\n")); 1118 return (errno_to_be_err(err)); 1119 } 1120 1121 snapshot->be_snapshot_creation = (time_t)zfs_prop_get_int(zfshp, 1122 ZFS_PROP_CREATION); 1123 1124 /* 1125 * Try to get this snapshot's cleanup policy from its 1126 * user properties first. If not there, use default 1127 * cleanup policy. 1128 */ 1129 if ((userprops = zfs_get_user_props(zfshp)) != NULL && 1130 nvlist_lookup_nvlist(userprops, BE_POLICY_PROPERTY, 1131 &propval) == 0 && nvlist_lookup_string(propval, 1132 ZPROP_VALUE, &prop_str) == 0) { 1133 snapshot->be_snapshot_type = 1134 strdup(prop_str); 1135 } else { 1136 snapshot->be_snapshot_type = 1137 strdup(be_default_policy()); 1138 } 1139 1140 snapshot->be_snapshot_space_used = zfs_prop_get_int(zfshp, 1141 ZFS_PROP_USED); 1142 1143 node->be_node_num_snapshots++; 1144 return (BE_SUCCESS); 1145 } 1146 1147 /* 1148 * Function: be_list_alloc 1149 * Description: Helper function used to allocate memory for the various 1150 * sructures that make up a BE node. 1151 * Parameters: 1152 * err - Used to return any errors encountered. 1153 * BE_SUCCESS - Success 1154 * BE_ERR_NOMEM - Allocation failure 1155 * size - The size of memory to allocate. 1156 * Returns: 1157 * Success - A pointer to the allocated memory 1158 * Failure - NULL 1159 * Scope: 1160 * Private 1161 */ 1162 static void* 1163 be_list_alloc(int *err, size_t size) 1164 { 1165 void *bep = NULL; 1166 1167 bep = calloc(1, size); 1168 if (bep == NULL) { 1169 be_print_err(gettext("be_list_alloc: memory " 1170 "allocation failed\n")); 1171 *err = BE_ERR_NOMEM; 1172 } 1173 *err = BE_SUCCESS; 1174 return (bep); 1175 } 1176 1177 /* 1178 * Function: be_get_zone_node_data 1179 * Description: Helper function used to collect all the information to 1180 * fill in the be_node_list structure to be returned by 1181 * be_get_zone_list. 1182 * Parameters: 1183 * be_node - a pointer to the node structure we're filling in. 1184 * be_name - The BE name of the node whose information we're 1185 * Returns: 1186 * BE_SUCCESS - Success 1187 * be_errno_t - Failure 1188 * Scope: 1189 * Private 1190 * 1191 * NOTE: This function currently only collects the zone BE name but when 1192 * support for beadm/libbe in a zone is provided it will need to fill 1193 * in the rest of the information needed for a zone BE. 1194 */ 1195 static int 1196 be_get_zone_node_data(be_node_list_t *be_node, char *be_name) 1197 { 1198 if ((be_node->be_node_name = strdup(be_name)) != NULL) 1199 return (BE_SUCCESS); 1200 return (BE_ERR_NOMEM); 1201 } 1202