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