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