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