Print this page
5679 be_sort_list(): Possible null pointer dereference


   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  */
  30 
  31 #include <assert.h>
  32 #include <libintl.h>
  33 #include <libnvpair.h>
  34 #include <libzfs.h>
  35 #include <stdio.h>
  36 #include <stdlib.h>
  37 #include <string.h>
  38 #include <strings.h>
  39 #include <sys/types.h>
  40 #include <sys/stat.h>
  41 #include <unistd.h>
  42 #include <errno.h>
  43 
  44 #include <libbe.h>
  45 #include <libbe_priv.h>
  46 
  47 /*
  48  * Callback data used for zfs_iter calls.


  50 typedef struct list_callback_data {
  51         char *zpool_name;
  52         char *be_name;
  53         be_node_list_t *be_nodes_head;
  54         be_node_list_t *be_nodes;
  55         char current_be[MAXPATHLEN];
  56 } list_callback_data_t;
  57 
  58 /*
  59  * Private function prototypes
  60  */
  61 static int be_add_children_callback(zfs_handle_t *zhp, void *data);
  62 static int be_get_list_callback(zpool_handle_t *, void *);
  63 static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *,
  64     const char *, char *, char *);
  65 static int be_get_zone_node_data(be_node_list_t *, char *);
  66 static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *,
  67     be_node_list_t *);
  68 static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *,
  69     be_node_list_t *);
  70 static void be_sort_list(be_node_list_t **,
  71     int (*)(const void *, const void *));
  72 static int be_qsort_compare_BEs_name(const void *, const void *);
  73 static int be_qsort_compare_BEs_name_rev(const void *, const void *);
  74 static int be_qsort_compare_BEs_date(const void *, const void *);
  75 static int be_qsort_compare_BEs_date_rev(const void *, const void *);
  76 static int be_qsort_compare_BEs_space(const void *, const void *);
  77 static int be_qsort_compare_BEs_space_rev(const void *, const void *);
  78 static int be_qsort_compare_snapshots(const void *x, const void *y);
  79 static int be_qsort_compare_datasets(const void *x, const void *y);
  80 static void *be_list_alloc(int *, size_t);
  81 
  82 /*
  83  * Private data.
  84  */
  85 static char be_container_ds[MAXPATHLEN];
  86 static boolean_t zone_be = B_FALSE;
  87 
  88 /* ******************************************************************** */
  89 /*                      Public Functions                                */
  90 /* ******************************************************************** */


 126                 if (!be_valid_be_name(be_name)) {
 127                         be_print_err(gettext("be_list: "
 128                             "invalid BE name %s\n"), be_name);
 129                         return (BE_ERR_INVAL);
 130                 }
 131         }
 132 
 133         ret = _be_list(be_name, be_nodes);
 134 
 135         be_zfs_fini();
 136 
 137         return (ret);
 138 }
 139 
 140 /*
 141  * Function:    be_sort
 142  * Description: Sort BE node list
 143  * Parameters:
 144  *              pointer to address of list head
 145  *              sort order type
 146  * Returns:
 147  *              nothing

 148  * Side effect:
 149  *              node list sorted by name
 150  * Scope:
 151  *              Public
 152  */
 153 void
 154 be_sort(be_node_list_t **be_nodes, int order)
 155 {
 156         int (*compar)(const void *, const void *) = be_qsort_compare_BEs_date;
 157 
 158         if (be_nodes == NULL)
 159                 return;
 160 
 161         switch (order) {
 162         case BE_SORT_UNSPECIFIED:
 163         case BE_SORT_DATE:
 164                 compar = be_qsort_compare_BEs_date;
 165                 break;
 166         case BE_SORT_DATE_REV:
 167                 compar = be_qsort_compare_BEs_date_rev;
 168                 break;
 169         case BE_SORT_NAME:
 170                 compar = be_qsort_compare_BEs_name;
 171                 break;
 172         case BE_SORT_NAME_REV:
 173                 compar = be_qsort_compare_BEs_name_rev;
 174                 break;
 175         case BE_SORT_SPACE:
 176                 compar = be_qsort_compare_BEs_space;
 177                 break;
 178         case BE_SORT_SPACE_REV:
 179                 compar = be_qsort_compare_BEs_space_rev;
 180                 break;
 181         default:
 182                 be_print_err(gettext("be_sort: invalid sort order %d\n"),
 183                     order);
 184                 return;
 185         }
 186 
 187         be_sort_list(be_nodes, compar);
 188 }
 189 
 190 /* ******************************************************************** */
 191 /*                      Semi-Private Functions                          */
 192 /* ******************************************************************** */
 193 
 194 /*
 195  * Function:    _be_list
 196  * Description: This does the actual work described in be_list.
 197  * Parameters:
 198  *              be_name - The name of the BE to look up.
 199  *                        If NULL a list of all BEs will be returned.
 200  *              be_nodes - A reference pointer to the list of BEs. The list
 201  *                         structure will be allocated here and must
 202  *                         be freed by a call to be_free_list. If there are no
 203  *                         BEs found on the system this reference will be
 204  *                         set to NULL.
 205  * Return:
 206  *              BE_SUCCESS - Success
 207  *              be_errno_t - Failure
 208  * Scope:
 209  *              Semi-private (library wide use only)
 210  */
 211 int
 212 _be_list(char *be_name, be_node_list_t **be_nodes)
 213 {
 214         list_callback_data_t cb = { 0 };
 215         be_transaction_data_t bt = { 0 };
 216         int ret = BE_SUCCESS;

 217         zpool_handle_t *zphp;
 218         char *rpool = NULL;
 219         struct be_defaults be_defaults;
 220 
 221         if (be_nodes == NULL)
 222                 return (BE_ERR_INVAL);
 223 
 224         be_get_defaults(&be_defaults);
 225 
 226         if (be_find_current_be(&bt) != BE_SUCCESS) {
 227                 /*
 228                  * We were unable to find a currently booted BE which
 229                  * probably means that we're not booted in a BE envoronment.
 230                  * None of the BE's will be marked as the active BE.
 231                  */
 232                 (void) strcpy(cb.current_be, "-");
 233         } else {
 234                 (void) strncpy(cb.current_be, bt.obe_name,
 235                     sizeof (cb.current_be));
 236                 rpool = bt.obe_zpool;


 260                                 cb.be_nodes_head = NULL;
 261                                 cb.be_nodes = NULL;
 262                         }
 263                         ret = BE_ERR_BE_NOENT;
 264                 }
 265         }
 266 
 267         if (cb.be_nodes_head == NULL) {
 268                 if (be_name != NULL)
 269                         be_print_err(gettext("be_list: BE (%s) does not "
 270                             "exist\n"), be_name);
 271                 else
 272                         be_print_err(gettext("be_list: No BE's found\n"));
 273                 ret = BE_ERR_BE_NOENT;
 274         }
 275 
 276         *be_nodes = cb.be_nodes_head;
 277 
 278         free(cb.be_name);
 279 
 280         be_sort(be_nodes, BE_SORT_DATE);
 281 
 282         return (ret);
 283 }
 284 
 285 /*
 286  * Function:    be_free_list
 287  * Description: Frees up all the data allocated for the list of BEs,
 288  *              datasets and snapshots returned by be_list.
 289  * Parameters:
 290  *              be_node - be_nodes_t structure returned from call to be_list.
 291  * Returns:
 292  *              none
 293  * Scope:
 294  *              Semi-private (library wide use only)
 295  */
 296 void
 297 be_free_list(be_node_list_t *be_nodes)
 298 {
 299         be_node_list_t *temp_node = NULL;
 300         be_node_list_t *list = be_nodes;
 301 
 302         while (list != NULL) {


 667                         return (ret);
 668                 }
 669         }
 670         ret = zfs_iter_children(zhp, be_add_children_callback, cb);
 671         if (ret != 0) {
 672                 be_print_err(gettext("be_add_children_callback: "
 673                     "encountered error: %s\n"),
 674                     libzfs_error_description(g_zfs));
 675                 ret = zfs_err_to_be_err(g_zfs);
 676         }
 677         ZFS_CLOSE(zhp);
 678         return (ret);
 679 }
 680 
 681 /*
 682  * Function:    be_sort_list
 683  * Description: Sort BE node list
 684  * Parameters:
 685  *              pointer to address of list head
 686  *              compare function
 687  * Returns:
 688  *              nothing

 689  * Side effect:
 690  *              node list sorted by name
 691  * Scope:
 692  *              Private
 693  */
 694 static void
 695 be_sort_list(be_node_list_t **pstart, int (*compar)(const void *, const void *))
 696 {

 697         size_t ibe, nbe;
 698         be_node_list_t *p = NULL;
 699         be_node_list_t **ptrlist = NULL;

 700 
 701         if (pstart == NULL)
 702                 return;
 703         /* build array of linked list BE struct pointers */
 704         for (p = *pstart, nbe = 0; p != NULL; nbe++, p = p->be_next_node) {
 705                 ptrlist = realloc(ptrlist,
 706                     sizeof (be_node_list_t *) * (nbe + 2));







 707                 ptrlist[nbe] = p;
 708         }
 709         if (nbe == 0)
 710                 return;
 711         /* in-place list quicksort using qsort(3C) */
 712         if (nbe > 1) /* no sort if less than 2 BEs */
 713                 qsort(ptrlist, nbe, sizeof (be_node_list_t *), compar);
 714 
 715         ptrlist[nbe] = NULL; /* add linked list terminator */
 716         *pstart = ptrlist[0]; /* set new linked list header */
 717         /* for each BE in list */
 718         for (ibe = 0; ibe < nbe; ibe++) {
 719                 size_t k, ns;   /* subordinate index, count */
 720 
 721                 /* rewrite list pointer chain, including terminator */
 722                 ptrlist[ibe]->be_next_node = ptrlist[ibe + 1];
 723                 /* sort subordinate snapshots */
 724                 if (ptrlist[ibe]->be_node_num_snapshots > 1) {
 725                         const size_t nmax = ptrlist[ibe]->be_node_num_snapshots;
 726                         be_snapshot_list_t ** const slist =
 727                             malloc(sizeof (be_snapshot_list_t *) * (nmax + 1));
 728                         be_snapshot_list_t *p;
 729 
 730                         if (slist == NULL)

 731                                 continue;

 732                         /* build array of linked list snapshot struct ptrs */
 733                         for (ns = 0, p = ptrlist[ibe]->be_node_snapshots;
 734                             ns < nmax && p != NULL;
 735                             ns++, p = p->be_next_snapshot) {
 736                                 slist[ns] = p;
 737                         }
 738                         if (ns < 2)
 739                                 goto end_snapshot;
 740                         slist[ns] = NULL; /* add terminator */
 741                         /* in-place list quicksort using qsort(3C) */
 742                         qsort(slist, ns, sizeof (be_snapshot_list_t *),
 743                             be_qsort_compare_snapshots);
 744                         /* rewrite list pointer chain, including terminator */
 745                         ptrlist[ibe]->be_node_snapshots = slist[0];
 746                         for (k = 0; k < ns; k++)
 747                                 slist[k]->be_next_snapshot = slist[k + 1];
 748 end_snapshot:
 749                         free(slist);
 750                 }
 751                 /* sort subordinate datasets */
 752                 if (ptrlist[ibe]->be_node_num_datasets > 1) {
 753                         const size_t nmax = ptrlist[ibe]->be_node_num_datasets;
 754                         be_dataset_list_t ** const slist =
 755                             malloc(sizeof (be_dataset_list_t *) * (nmax + 1));
 756                         be_dataset_list_t *p;
 757 
 758                         if (slist == NULL)

 759                                 continue;

 760                         /* build array of linked list dataset struct ptrs */
 761                         for (ns = 0, p = ptrlist[ibe]->be_node_datasets;
 762                             ns < nmax && p != NULL;
 763                             ns++, p = p->be_next_dataset) {
 764                                 slist[ns] = p;
 765                         }
 766                         if (ns < 2) /* subordinate datasets < 2 - no sort */
 767                                 goto end_dataset;
 768                         slist[ns] = NULL; /* add terminator */
 769                         /* in-place list quicksort using qsort(3C) */
 770                         qsort(slist, ns, sizeof (be_dataset_list_t *),
 771                             be_qsort_compare_datasets);
 772                         /* rewrite list pointer chain, including terminator */
 773                         ptrlist[ibe]->be_node_datasets = slist[0];
 774                         for (k = 0; k < ns; k++)
 775                                 slist[k]->be_next_dataset = slist[k + 1];
 776 end_dataset:
 777                         free(slist);
 778                 }
 779         }
 780 free:
 781         free(ptrlist);

 782 }
 783 
 784 /*
 785  * Function:    be_qsort_compare_BEs_date
 786  * Description: compare BE creation times for qsort(3C)
 787  *              will sort BE list from oldest to most recent
 788  * Parameters:
 789  *              x,y - BEs with names to compare
 790  * Returns:
 791  *              positive if x>y, negative if y>x, 0 if equal
 792  * Scope:
 793  *              Private
 794  */
 795 static int
 796 be_qsort_compare_BEs_date(const void *x, const void *y)
 797 {
 798         be_node_list_t *p = *(be_node_list_t **)x;
 799         be_node_list_t *q = *(be_node_list_t **)y;
 800 
 801         assert(p != NULL);




   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  */
  31 
  32 #include <assert.h>
  33 #include <libintl.h>
  34 #include <libnvpair.h>
  35 #include <libzfs.h>
  36 #include <stdio.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 #include <strings.h>
  40 #include <sys/types.h>
  41 #include <sys/stat.h>
  42 #include <unistd.h>
  43 #include <errno.h>
  44 
  45 #include <libbe.h>
  46 #include <libbe_priv.h>
  47 
  48 /*
  49  * Callback data used for zfs_iter calls.


  51 typedef struct list_callback_data {
  52         char *zpool_name;
  53         char *be_name;
  54         be_node_list_t *be_nodes_head;
  55         be_node_list_t *be_nodes;
  56         char current_be[MAXPATHLEN];
  57 } list_callback_data_t;
  58 
  59 /*
  60  * Private function prototypes
  61  */
  62 static int be_add_children_callback(zfs_handle_t *zhp, void *data);
  63 static int be_get_list_callback(zpool_handle_t *, void *);
  64 static int be_get_node_data(zfs_handle_t *, be_node_list_t *, char *,
  65     const char *, char *, char *);
  66 static int be_get_zone_node_data(be_node_list_t *, char *);
  67 static int be_get_ds_data(zfs_handle_t *, char *, be_dataset_list_t *,
  68     be_node_list_t *);
  69 static int be_get_ss_data(zfs_handle_t *, char *, be_snapshot_list_t *,
  70     be_node_list_t *);
  71 static int be_sort_list(be_node_list_t **,
  72     int (*)(const void *, const void *));
  73 static int be_qsort_compare_BEs_name(const void *, const void *);
  74 static int be_qsort_compare_BEs_name_rev(const void *, const void *);
  75 static int be_qsort_compare_BEs_date(const void *, const void *);
  76 static int be_qsort_compare_BEs_date_rev(const void *, const void *);
  77 static int be_qsort_compare_BEs_space(const void *, const void *);
  78 static int be_qsort_compare_BEs_space_rev(const void *, const void *);
  79 static int be_qsort_compare_snapshots(const void *x, const void *y);
  80 static int be_qsort_compare_datasets(const void *x, const void *y);
  81 static void *be_list_alloc(int *, size_t);
  82 
  83 /*
  84  * Private data.
  85  */
  86 static char be_container_ds[MAXPATHLEN];
  87 static boolean_t zone_be = B_FALSE;
  88 
  89 /* ******************************************************************** */
  90 /*                      Public Functions                                */
  91 /* ******************************************************************** */


 127                 if (!be_valid_be_name(be_name)) {
 128                         be_print_err(gettext("be_list: "
 129                             "invalid BE name %s\n"), be_name);
 130                         return (BE_ERR_INVAL);
 131                 }
 132         }
 133 
 134         ret = _be_list(be_name, be_nodes);
 135 
 136         be_zfs_fini();
 137 
 138         return (ret);
 139 }
 140 
 141 /*
 142  * Function:    be_sort
 143  * Description: Sort BE node list
 144  * Parameters:
 145  *              pointer to address of list head
 146  *              sort order type
 147  * Return:
 148  *              BE_SUCCESS - Success
 149  *              be_errno_t - Failure
 150  * Side effect:
 151  *              node list sorted by name
 152  * Scope:
 153  *              Public
 154  */
 155 int
 156 be_sort(be_node_list_t **be_nodes, int order)
 157 {
 158         int (*compar)(const void *, const void *) = be_qsort_compare_BEs_date;
 159 
 160         if (be_nodes == NULL)
 161                 return (BE_ERR_INVAL);
 162 
 163         switch (order) {
 164         case BE_SORT_UNSPECIFIED:
 165         case BE_SORT_DATE:
 166                 compar = be_qsort_compare_BEs_date;
 167                 break;
 168         case BE_SORT_DATE_REV:
 169                 compar = be_qsort_compare_BEs_date_rev;
 170                 break;
 171         case BE_SORT_NAME:
 172                 compar = be_qsort_compare_BEs_name;
 173                 break;
 174         case BE_SORT_NAME_REV:
 175                 compar = be_qsort_compare_BEs_name_rev;
 176                 break;
 177         case BE_SORT_SPACE:
 178                 compar = be_qsort_compare_BEs_space;
 179                 break;
 180         case BE_SORT_SPACE_REV:
 181                 compar = be_qsort_compare_BEs_space_rev;
 182                 break;
 183         default:
 184                 be_print_err(gettext("be_sort: invalid sort order %d\n"),
 185                     order);
 186                 return (BE_ERR_INVAL);
 187         }
 188 
 189         return (be_sort_list(be_nodes, compar));
 190 }
 191 
 192 /* ******************************************************************** */
 193 /*                      Semi-Private Functions                          */
 194 /* ******************************************************************** */
 195 
 196 /*
 197  * Function:    _be_list
 198  * Description: This does the actual work described in be_list.
 199  * Parameters:
 200  *              be_name - The name of the BE to look up.
 201  *                        If NULL a list of all BEs will be returned.
 202  *              be_nodes - A reference pointer to the list of BEs. The list
 203  *                         structure will be allocated here and must
 204  *                         be freed by a call to be_free_list. If there are no
 205  *                         BEs found on the system this reference will be
 206  *                         set to NULL.
 207  * Return:
 208  *              BE_SUCCESS - Success
 209  *              be_errno_t - Failure
 210  * Scope:
 211  *              Semi-private (library wide use only)
 212  */
 213 int
 214 _be_list(char *be_name, be_node_list_t **be_nodes)
 215 {
 216         list_callback_data_t cb = { 0 };
 217         be_transaction_data_t bt = { 0 };
 218         int ret = BE_SUCCESS;
 219         int sret;
 220         zpool_handle_t *zphp;
 221         char *rpool = NULL;
 222         struct be_defaults be_defaults;
 223 
 224         if (be_nodes == NULL)
 225                 return (BE_ERR_INVAL);
 226 
 227         be_get_defaults(&be_defaults);
 228 
 229         if (be_find_current_be(&bt) != BE_SUCCESS) {
 230                 /*
 231                  * We were unable to find a currently booted BE which
 232                  * probably means that we're not booted in a BE envoronment.
 233                  * None of the BE's will be marked as the active BE.
 234                  */
 235                 (void) strcpy(cb.current_be, "-");
 236         } else {
 237                 (void) strncpy(cb.current_be, bt.obe_name,
 238                     sizeof (cb.current_be));
 239                 rpool = bt.obe_zpool;


 263                                 cb.be_nodes_head = NULL;
 264                                 cb.be_nodes = NULL;
 265                         }
 266                         ret = BE_ERR_BE_NOENT;
 267                 }
 268         }
 269 
 270         if (cb.be_nodes_head == NULL) {
 271                 if (be_name != NULL)
 272                         be_print_err(gettext("be_list: BE (%s) does not "
 273                             "exist\n"), be_name);
 274                 else
 275                         be_print_err(gettext("be_list: No BE's found\n"));
 276                 ret = BE_ERR_BE_NOENT;
 277         }
 278 
 279         *be_nodes = cb.be_nodes_head;
 280 
 281         free(cb.be_name);
 282 
 283         sret = be_sort(be_nodes, BE_SORT_DATE);
 284 
 285         return ((ret == BE_SUCCESS) ? sret : ret);
 286 }
 287 
 288 /*
 289  * Function:    be_free_list
 290  * Description: Frees up all the data allocated for the list of BEs,
 291  *              datasets and snapshots returned by be_list.
 292  * Parameters:
 293  *              be_node - be_nodes_t structure returned from call to be_list.
 294  * Returns:
 295  *              none
 296  * Scope:
 297  *              Semi-private (library wide use only)
 298  */
 299 void
 300 be_free_list(be_node_list_t *be_nodes)
 301 {
 302         be_node_list_t *temp_node = NULL;
 303         be_node_list_t *list = be_nodes;
 304 
 305         while (list != NULL) {


 670                         return (ret);
 671                 }
 672         }
 673         ret = zfs_iter_children(zhp, be_add_children_callback, cb);
 674         if (ret != 0) {
 675                 be_print_err(gettext("be_add_children_callback: "
 676                     "encountered error: %s\n"),
 677                     libzfs_error_description(g_zfs));
 678                 ret = zfs_err_to_be_err(g_zfs);
 679         }
 680         ZFS_CLOSE(zhp);
 681         return (ret);
 682 }
 683 
 684 /*
 685  * Function:    be_sort_list
 686  * Description: Sort BE node list
 687  * Parameters:
 688  *              pointer to address of list head
 689  *              compare function
 690  * Return:
 691  *              BE_SUCCESS - Success
 692  *              be_errno_t - Failure
 693  * Side effect:
 694  *              node list sorted by name
 695  * Scope:
 696  *              Private
 697  */
 698 static int
 699 be_sort_list(be_node_list_t **pstart, int (*compar)(const void *, const void *))
 700 {
 701         int ret = BE_SUCCESS;
 702         size_t ibe, nbe;
 703         be_node_list_t *p = NULL;
 704         be_node_list_t **ptrlist = NULL;
 705         be_node_list_t **ptrtmp;
 706 
 707         if (pstart == NULL) /* Nothing to sort */
 708                 return (BE_SUCCESS);
 709         /* build array of linked list BE struct pointers */
 710         for (p = *pstart, nbe = 0; p != NULL; nbe++, p = p->be_next_node) {
 711                 ptrtmp = realloc(ptrlist,
 712                     sizeof (be_node_list_t *) * (nbe + 2));
 713                 if (ptrtmp == NULL) { /* out of memory */
 714                         be_print_err(gettext("be_sort_list: memory "
 715                             "allocation failed\n"));
 716                         ret = BE_ERR_NOMEM;
 717                         goto free;
 718                 }
 719                 ptrlist = ptrtmp;
 720                 ptrlist[nbe] = p;
 721         }
 722         if (nbe == 0) /* Nothing to sort */
 723                 return (BE_SUCCESS);
 724         /* in-place list quicksort using qsort(3C) */
 725         if (nbe > 1) /* no sort if less than 2 BEs */
 726                 qsort(ptrlist, nbe, sizeof (be_node_list_t *), compar);
 727 
 728         ptrlist[nbe] = NULL; /* add linked list terminator */
 729         *pstart = ptrlist[0]; /* set new linked list header */
 730         /* for each BE in list */
 731         for (ibe = 0; ibe < nbe; ibe++) {
 732                 size_t k, ns;   /* subordinate index, count */
 733 
 734                 /* rewrite list pointer chain, including terminator */
 735                 ptrlist[ibe]->be_next_node = ptrlist[ibe + 1];
 736                 /* sort subordinate snapshots */
 737                 if (ptrlist[ibe]->be_node_num_snapshots > 1) {
 738                         const size_t nmax = ptrlist[ibe]->be_node_num_snapshots;
 739                         be_snapshot_list_t ** const slist =
 740                             malloc(sizeof (be_snapshot_list_t *) * (nmax + 1));
 741                         be_snapshot_list_t *p;
 742 
 743                         if (slist == NULL) {
 744                                 ret = BE_ERR_NOMEM;
 745                                 continue;
 746                         }
 747                         /* build array of linked list snapshot struct ptrs */
 748                         for (ns = 0, p = ptrlist[ibe]->be_node_snapshots;
 749                             ns < nmax && p != NULL;
 750                             ns++, p = p->be_next_snapshot) {
 751                                 slist[ns] = p;
 752                         }
 753                         if (ns < 2)
 754                                 goto end_snapshot;
 755                         slist[ns] = NULL; /* add terminator */
 756                         /* in-place list quicksort using qsort(3C) */
 757                         qsort(slist, ns, sizeof (be_snapshot_list_t *),
 758                             be_qsort_compare_snapshots);
 759                         /* rewrite list pointer chain, including terminator */
 760                         ptrlist[ibe]->be_node_snapshots = slist[0];
 761                         for (k = 0; k < ns; k++)
 762                                 slist[k]->be_next_snapshot = slist[k + 1];
 763 end_snapshot:
 764                         free(slist);
 765                 }
 766                 /* sort subordinate datasets */
 767                 if (ptrlist[ibe]->be_node_num_datasets > 1) {
 768                         const size_t nmax = ptrlist[ibe]->be_node_num_datasets;
 769                         be_dataset_list_t ** const slist =
 770                             malloc(sizeof (be_dataset_list_t *) * (nmax + 1));
 771                         be_dataset_list_t *p;
 772 
 773                         if (slist == NULL) {
 774                                 ret = BE_ERR_NOMEM;
 775                                 continue;
 776                         }
 777                         /* build array of linked list dataset struct ptrs */
 778                         for (ns = 0, p = ptrlist[ibe]->be_node_datasets;
 779                             ns < nmax && p != NULL;
 780                             ns++, p = p->be_next_dataset) {
 781                                 slist[ns] = p;
 782                         }
 783                         if (ns < 2) /* subordinate datasets < 2 - no sort */
 784                                 goto end_dataset;
 785                         slist[ns] = NULL; /* add terminator */
 786                         /* in-place list quicksort using qsort(3C) */
 787                         qsort(slist, ns, sizeof (be_dataset_list_t *),
 788                             be_qsort_compare_datasets);
 789                         /* rewrite list pointer chain, including terminator */
 790                         ptrlist[ibe]->be_node_datasets = slist[0];
 791                         for (k = 0; k < ns; k++)
 792                                 slist[k]->be_next_dataset = slist[k + 1];
 793 end_dataset:
 794                         free(slist);
 795                 }
 796         }
 797 free:
 798         free(ptrlist);
 799         return (ret);
 800 }
 801 
 802 /*
 803  * Function:    be_qsort_compare_BEs_date
 804  * Description: compare BE creation times for qsort(3C)
 805  *              will sort BE list from oldest to most recent
 806  * Parameters:
 807  *              x,y - BEs with names to compare
 808  * Returns:
 809  *              positive if x>y, negative if y>x, 0 if equal
 810  * Scope:
 811  *              Private
 812  */
 813 static int
 814 be_qsort_compare_BEs_date(const void *x, const void *y)
 815 {
 816         be_node_list_t *p = *(be_node_list_t **)x;
 817         be_node_list_t *q = *(be_node_list_t **)y;
 818 
 819         assert(p != NULL);