1789Sahrens /* 2789Sahrens * CDDL HEADER START 3789Sahrens * 4789Sahrens * The contents of this file are subject to the terms of the 52082Seschrock * Common Development and Distribution License (the "License"). 62082Seschrock * You may not use this file except in compliance with the License. 7789Sahrens * 8789Sahrens * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9789Sahrens * or http://www.opensolaris.org/os/licensing. 10789Sahrens * See the License for the specific language governing permissions 11789Sahrens * and limitations under the License. 12789Sahrens * 13789Sahrens * When distributing Covered Code, include this CDDL HEADER in each 14789Sahrens * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15789Sahrens * If applicable, add the following below this CDDL HEADER, with the 16789Sahrens * fields enclosed by brackets "[]" replaced with your own identifying 17789Sahrens * information: Portions Copyright [yyyy] [name of copyright owner] 18789Sahrens * 19789Sahrens * CDDL HEADER END 20789Sahrens */ 21789Sahrens /* 22*8802SSanjeev.Bagewadi@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #include <libintl.h> 27789Sahrens #include <libuutil.h> 28789Sahrens #include <stddef.h> 29789Sahrens #include <stdio.h> 30789Sahrens #include <stdlib.h> 31789Sahrens #include <strings.h> 32789Sahrens 33789Sahrens #include <libzfs.h> 34789Sahrens 35789Sahrens #include "zfs_util.h" 362379Ssjelinek #include "zfs_iter.h" 37789Sahrens 38789Sahrens /* 39789Sahrens * This is a private interface used to gather up all the datasets specified on 40789Sahrens * the command line so that we can iterate over them in order. 41789Sahrens * 42789Sahrens * First, we iterate over all filesystems, gathering them together into an 432379Ssjelinek * AVL tree. We report errors for any explicitly specified datasets 44789Sahrens * that we couldn't open. 45789Sahrens * 46789Sahrens * When finished, we have an AVL tree of ZFS handles. We go through and execute 47789Sahrens * the provided callback for each one, passing whatever data the user supplied. 48789Sahrens */ 49789Sahrens 50789Sahrens typedef struct zfs_node { 51789Sahrens zfs_handle_t *zn_handle; 52789Sahrens uu_avl_node_t zn_avlnode; 53789Sahrens } zfs_node_t; 54789Sahrens 55789Sahrens typedef struct callback_data { 56789Sahrens uu_avl_t *cb_avl; 577538SRichard.Morris@Sun.COM int cb_flags; 58789Sahrens zfs_type_t cb_types; 592379Ssjelinek zfs_sort_column_t *cb_sortcol; 605094Slling zprop_list_t **cb_proplist; 61*8802SSanjeev.Bagewadi@Sun.COM uint8_t cb_props_table[ZFS_NUM_PROPS]; 62789Sahrens } callback_data_t; 63789Sahrens 64789Sahrens uu_avl_pool_t *avl_pool; 65789Sahrens 66789Sahrens /* 677538SRichard.Morris@Sun.COM * Include snaps if they were requested or if this a zfs list where types 687538SRichard.Morris@Sun.COM * were not specified and the "listsnapshots" property is set on this pool. 697538SRichard.Morris@Sun.COM */ 707538SRichard.Morris@Sun.COM static int 717538SRichard.Morris@Sun.COM zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb) 727538SRichard.Morris@Sun.COM { 737538SRichard.Morris@Sun.COM zpool_handle_t *zph; 747538SRichard.Morris@Sun.COM 757538SRichard.Morris@Sun.COM if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0) 767538SRichard.Morris@Sun.COM return (cb->cb_types & ZFS_TYPE_SNAPSHOT); 777538SRichard.Morris@Sun.COM 787538SRichard.Morris@Sun.COM zph = zfs_get_pool_handle(zhp); 797538SRichard.Morris@Sun.COM return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL)); 807538SRichard.Morris@Sun.COM } 817538SRichard.Morris@Sun.COM 827538SRichard.Morris@Sun.COM /* 837538SRichard.Morris@Sun.COM * Called for each dataset. If the object is of an appropriate type, 84789Sahrens * add it to the avl tree and recurse over any children as necessary. 85789Sahrens */ 865367Sahrens static int 87789Sahrens zfs_callback(zfs_handle_t *zhp, void *data) 88789Sahrens { 89789Sahrens callback_data_t *cb = data; 90789Sahrens int dontclose = 0; 917538SRichard.Morris@Sun.COM int include_snaps = zfs_include_snapshots(zhp, cb); 92789Sahrens 937538SRichard.Morris@Sun.COM if ((zfs_get_type(zhp) & cb->cb_types) || 947538SRichard.Morris@Sun.COM ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) { 95789Sahrens uu_avl_index_t idx; 96789Sahrens zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 97789Sahrens 98789Sahrens node->zn_handle = zhp; 99789Sahrens uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 1002379Ssjelinek if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, 1012379Ssjelinek &idx) == NULL) { 102*8802SSanjeev.Bagewadi@Sun.COM 103*8802SSanjeev.Bagewadi@Sun.COM if (cb->cb_proplist) { 104*8802SSanjeev.Bagewadi@Sun.COM if ((*cb->cb_proplist) && 105*8802SSanjeev.Bagewadi@Sun.COM !(*cb->cb_proplist)->pl_all) 106*8802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zhp, 107*8802SSanjeev.Bagewadi@Sun.COM cb->cb_props_table); 108*8802SSanjeev.Bagewadi@Sun.COM 109*8802SSanjeev.Bagewadi@Sun.COM if (zfs_expand_proplist(zhp, cb->cb_proplist) 110*8802SSanjeev.Bagewadi@Sun.COM != 0) { 111*8802SSanjeev.Bagewadi@Sun.COM free(node); 112*8802SSanjeev.Bagewadi@Sun.COM return (-1); 113*8802SSanjeev.Bagewadi@Sun.COM } 1142676Seschrock } 115*8802SSanjeev.Bagewadi@Sun.COM 116789Sahrens uu_avl_insert(cb->cb_avl, node, idx); 117789Sahrens dontclose = 1; 118789Sahrens } else { 119789Sahrens free(node); 120789Sahrens } 121789Sahrens } 122789Sahrens 123789Sahrens /* 1241356Seschrock * Recurse if necessary. 125789Sahrens */ 1267538SRichard.Morris@Sun.COM if (cb->cb_flags & ZFS_ITER_RECURSE) { 1275367Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) 1285367Sahrens (void) zfs_iter_filesystems(zhp, zfs_callback, data); 1297538SRichard.Morris@Sun.COM if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) 1305367Sahrens (void) zfs_iter_snapshots(zhp, zfs_callback, data); 1315367Sahrens } 132789Sahrens 133789Sahrens if (!dontclose) 134789Sahrens zfs_close(zhp); 135789Sahrens 136789Sahrens return (0); 137789Sahrens } 138789Sahrens 1392676Seschrock int 1402676Seschrock zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 1412379Ssjelinek boolean_t reverse) 1422379Ssjelinek { 1432379Ssjelinek zfs_sort_column_t *col; 1442676Seschrock zfs_prop_t prop; 1452676Seschrock 1465094Slling if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && 1472676Seschrock !zfs_prop_user(name)) 1482676Seschrock return (-1); 1492379Ssjelinek 1502379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 1512379Ssjelinek 1522379Ssjelinek col->sc_prop = prop; 1532379Ssjelinek col->sc_reverse = reverse; 1545094Slling if (prop == ZPROP_INVAL) { 1552676Seschrock col->sc_user_prop = safe_malloc(strlen(name) + 1); 1562676Seschrock (void) strcpy(col->sc_user_prop, name); 1572676Seschrock } 1582379Ssjelinek 1592379Ssjelinek if (*sc == NULL) { 1602379Ssjelinek col->sc_last = col; 1612379Ssjelinek *sc = col; 1622379Ssjelinek } else { 1632379Ssjelinek (*sc)->sc_last->sc_next = col; 1642379Ssjelinek (*sc)->sc_last = col; 1652379Ssjelinek } 1662676Seschrock 1672676Seschrock return (0); 1682379Ssjelinek } 1692379Ssjelinek 1702379Ssjelinek void 1712379Ssjelinek zfs_free_sort_columns(zfs_sort_column_t *sc) 1722379Ssjelinek { 1732379Ssjelinek zfs_sort_column_t *col; 1742379Ssjelinek 1752379Ssjelinek while (sc != NULL) { 1762379Ssjelinek col = sc->sc_next; 1772676Seschrock free(sc->sc_user_prop); 1782379Ssjelinek free(sc); 1792379Ssjelinek sc = col; 1802379Ssjelinek } 1812379Ssjelinek } 1822379Ssjelinek 183789Sahrens /* ARGSUSED */ 184789Sahrens static int 185789Sahrens zfs_compare(const void *larg, const void *rarg, void *unused) 186789Sahrens { 187789Sahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 188789Sahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 189789Sahrens const char *lname = zfs_get_name(l); 190789Sahrens const char *rname = zfs_get_name(r); 191789Sahrens char *lat, *rat; 192789Sahrens uint64_t lcreate, rcreate; 193789Sahrens int ret; 194789Sahrens 195789Sahrens lat = (char *)strchr(lname, '@'); 196789Sahrens rat = (char *)strchr(rname, '@'); 197789Sahrens 198789Sahrens if (lat != NULL) 199789Sahrens *lat = '\0'; 200789Sahrens if (rat != NULL) 201789Sahrens *rat = '\0'; 202789Sahrens 203789Sahrens ret = strcmp(lname, rname); 204789Sahrens if (ret == 0) { 205789Sahrens /* 206789Sahrens * If we're comparing a dataset to one of its snapshots, we 207789Sahrens * always make the full dataset first. 208789Sahrens */ 209789Sahrens if (lat == NULL) { 210789Sahrens ret = -1; 211789Sahrens } else if (rat == NULL) { 212789Sahrens ret = 1; 213789Sahrens } else { 214789Sahrens /* 215789Sahrens * If we have two snapshots from the same dataset, then 216789Sahrens * we want to sort them according to creation time. We 217789Sahrens * use the hidden CREATETXG property to get an absolute 218789Sahrens * ordering of snapshots. 219789Sahrens */ 220789Sahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 221789Sahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 222789Sahrens 223789Sahrens if (lcreate < rcreate) 224789Sahrens ret = -1; 225789Sahrens else if (lcreate > rcreate) 226789Sahrens ret = 1; 227789Sahrens } 228789Sahrens } 229789Sahrens 230789Sahrens if (lat != NULL) 231789Sahrens *lat = '@'; 232789Sahrens if (rat != NULL) 233789Sahrens *rat = '@'; 234789Sahrens 235789Sahrens return (ret); 236789Sahrens } 237789Sahrens 2382379Ssjelinek /* 2392379Ssjelinek * Sort datasets by specified columns. 2402379Ssjelinek * 2412379Ssjelinek * o Numeric types sort in ascending order. 2422379Ssjelinek * o String types sort in alphabetical order. 2432379Ssjelinek * o Types inappropriate for a row sort that row to the literal 2442379Ssjelinek * bottom, regardless of the specified ordering. 2452379Ssjelinek * 2462379Ssjelinek * If no sort columns are specified, or two datasets compare equally 2472379Ssjelinek * across all specified columns, they are sorted alphabetically by name 2482379Ssjelinek * with snapshots grouped under their parents. 2492379Ssjelinek */ 2502379Ssjelinek static int 2512379Ssjelinek zfs_sort(const void *larg, const void *rarg, void *data) 2522379Ssjelinek { 2532379Ssjelinek zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 2542379Ssjelinek zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 2552379Ssjelinek zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 2562379Ssjelinek zfs_sort_column_t *psc; 2572379Ssjelinek 2582379Ssjelinek for (psc = sc; psc != NULL; psc = psc->sc_next) { 2592676Seschrock char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 2602676Seschrock char *lstr, *rstr; 2612379Ssjelinek uint64_t lnum, rnum; 2622676Seschrock boolean_t lvalid, rvalid; 2632379Ssjelinek int ret = 0; 2642379Ssjelinek 2652676Seschrock /* 2662676Seschrock * We group the checks below the generic code. If 'lstr' and 2672676Seschrock * 'rstr' are non-NULL, then we do a string based comparison. 2682676Seschrock * Otherwise, we compare 'lnum' and 'rnum'. 2692676Seschrock */ 2702676Seschrock lstr = rstr = NULL; 2715094Slling if (psc->sc_prop == ZPROP_INVAL) { 2722676Seschrock nvlist_t *luser, *ruser; 2732676Seschrock nvlist_t *lval, *rval; 2742676Seschrock 2752676Seschrock luser = zfs_get_user_props(l); 2762676Seschrock ruser = zfs_get_user_props(r); 2772379Ssjelinek 2782676Seschrock lvalid = (nvlist_lookup_nvlist(luser, 2792676Seschrock psc->sc_user_prop, &lval) == 0); 2802676Seschrock rvalid = (nvlist_lookup_nvlist(ruser, 2812676Seschrock psc->sc_user_prop, &rval) == 0); 2822379Ssjelinek 2832676Seschrock if (lvalid) 2842676Seschrock verify(nvlist_lookup_string(lval, 2855094Slling ZPROP_VALUE, &lstr) == 0); 2862676Seschrock if (rvalid) 2872676Seschrock verify(nvlist_lookup_string(rval, 2885094Slling ZPROP_VALUE, &rstr) == 0); 2892676Seschrock 2902676Seschrock } else if (zfs_prop_is_string(psc->sc_prop)) { 2912676Seschrock lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 2922676Seschrock sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 2932676Seschrock rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 2942676Seschrock sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 2952676Seschrock 2962676Seschrock lstr = lbuf; 2972676Seschrock rstr = rbuf; 2982379Ssjelinek } else { 2992379Ssjelinek lvalid = zfs_prop_valid_for_type(psc->sc_prop, 3002379Ssjelinek zfs_get_type(l)); 3012379Ssjelinek rvalid = zfs_prop_valid_for_type(psc->sc_prop, 3022379Ssjelinek zfs_get_type(r)); 3032379Ssjelinek 3042676Seschrock if (lvalid) 3052676Seschrock (void) zfs_prop_get_numeric(l, psc->sc_prop, 3062676Seschrock &lnum, NULL, NULL, 0); 3072676Seschrock if (rvalid) 3082676Seschrock (void) zfs_prop_get_numeric(r, psc->sc_prop, 3092676Seschrock &rnum, NULL, NULL, 0); 3102676Seschrock } 3112379Ssjelinek 3122676Seschrock if (!lvalid && !rvalid) 3132676Seschrock continue; 3142676Seschrock else if (!lvalid) 3152676Seschrock return (1); 3162676Seschrock else if (!rvalid) 3172676Seschrock return (-1); 3182379Ssjelinek 3192676Seschrock if (lstr) 3202676Seschrock ret = strcmp(lstr, rstr); 3217060Srm160521 else if (lnum < rnum) 3222676Seschrock ret = -1; 3232676Seschrock else if (lnum > rnum) 3242676Seschrock ret = 1; 3252379Ssjelinek 3262379Ssjelinek if (ret != 0) { 3272379Ssjelinek if (psc->sc_reverse == B_TRUE) 3282379Ssjelinek ret = (ret < 0) ? 1 : -1; 3292379Ssjelinek return (ret); 3302379Ssjelinek } 3312379Ssjelinek } 3322379Ssjelinek 3332379Ssjelinek return (zfs_compare(larg, rarg, NULL)); 3342379Ssjelinek } 3352379Ssjelinek 336789Sahrens int 3377538SRichard.Morris@Sun.COM zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, 3387538SRichard.Morris@Sun.COM zfs_sort_column_t *sortcol, zprop_list_t **proplist, 3397538SRichard.Morris@Sun.COM zfs_iter_f callback, void *data) 340789Sahrens { 341*8802SSanjeev.Bagewadi@Sun.COM callback_data_t cb = {0}; 342789Sahrens int ret = 0; 343789Sahrens zfs_node_t *node; 344789Sahrens uu_avl_walk_t *walk; 345789Sahrens 346789Sahrens avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 3472379Ssjelinek offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 348789Sahrens 349789Sahrens if (avl_pool == NULL) { 350789Sahrens (void) fprintf(stderr, 351789Sahrens gettext("internal error: out of memory\n")); 352789Sahrens exit(1); 353789Sahrens } 354789Sahrens 3552379Ssjelinek cb.cb_sortcol = sortcol; 3567538SRichard.Morris@Sun.COM cb.cb_flags = flags; 3572676Seschrock cb.cb_proplist = proplist; 358789Sahrens cb.cb_types = types; 359*8802SSanjeev.Bagewadi@Sun.COM 360*8802SSanjeev.Bagewadi@Sun.COM /* 361*8802SSanjeev.Bagewadi@Sun.COM * If cb_proplist is provided then in the zfs_handles created we 362*8802SSanjeev.Bagewadi@Sun.COM * retain only those properties listed in cb_proplist and sortcol. 363*8802SSanjeev.Bagewadi@Sun.COM * The rest are pruned. So, the caller should make sure that no other 364*8802SSanjeev.Bagewadi@Sun.COM * properties other than those listed in cb_proplist/sortcol are 365*8802SSanjeev.Bagewadi@Sun.COM * accessed. 366*8802SSanjeev.Bagewadi@Sun.COM * 367*8802SSanjeev.Bagewadi@Sun.COM * If cb_proplist is NULL then we retain all the properties. 368*8802SSanjeev.Bagewadi@Sun.COM */ 369*8802SSanjeev.Bagewadi@Sun.COM if (cb.cb_proplist && *cb.cb_proplist) { 370*8802SSanjeev.Bagewadi@Sun.COM zprop_list_t *p = *cb.cb_proplist; 371*8802SSanjeev.Bagewadi@Sun.COM 372*8802SSanjeev.Bagewadi@Sun.COM while (p) { 373*8802SSanjeev.Bagewadi@Sun.COM if (p->pl_prop >= ZFS_PROP_TYPE && 374*8802SSanjeev.Bagewadi@Sun.COM p->pl_prop < ZFS_NUM_PROPS) { 375*8802SSanjeev.Bagewadi@Sun.COM cb.cb_props_table[p->pl_prop] = B_TRUE; 376*8802SSanjeev.Bagewadi@Sun.COM } 377*8802SSanjeev.Bagewadi@Sun.COM p = p->pl_next; 378*8802SSanjeev.Bagewadi@Sun.COM } 379*8802SSanjeev.Bagewadi@Sun.COM 380*8802SSanjeev.Bagewadi@Sun.COM while (sortcol) { 381*8802SSanjeev.Bagewadi@Sun.COM if (sortcol->sc_prop >= ZFS_PROP_TYPE && 382*8802SSanjeev.Bagewadi@Sun.COM sortcol->sc_prop < ZFS_NUM_PROPS) { 383*8802SSanjeev.Bagewadi@Sun.COM cb.cb_props_table[sortcol->sc_prop] = B_TRUE; 384*8802SSanjeev.Bagewadi@Sun.COM } 385*8802SSanjeev.Bagewadi@Sun.COM sortcol = sortcol->sc_next; 386*8802SSanjeev.Bagewadi@Sun.COM } 387*8802SSanjeev.Bagewadi@Sun.COM } else { 388*8802SSanjeev.Bagewadi@Sun.COM (void) memset(cb.cb_props_table, B_TRUE, 389*8802SSanjeev.Bagewadi@Sun.COM sizeof (cb.cb_props_table)); 390*8802SSanjeev.Bagewadi@Sun.COM } 391*8802SSanjeev.Bagewadi@Sun.COM 392789Sahrens if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 393789Sahrens (void) fprintf(stderr, 394789Sahrens gettext("internal error: out of memory\n")); 395789Sahrens exit(1); 396789Sahrens } 397789Sahrens 398789Sahrens if (argc == 0) { 399789Sahrens /* 400789Sahrens * If given no arguments, iterate over all datasets. 401789Sahrens */ 4027538SRichard.Morris@Sun.COM cb.cb_flags |= ZFS_ITER_RECURSE; 4032082Seschrock ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 404789Sahrens } else { 405789Sahrens int i; 406789Sahrens zfs_handle_t *zhp; 407789Sahrens zfs_type_t argtype; 408789Sahrens 409789Sahrens /* 410789Sahrens * If we're recursive, then we always allow filesystems as 411789Sahrens * arguments. If we also are interested in snapshots, then we 412789Sahrens * can take volumes as well. 413789Sahrens */ 414789Sahrens argtype = types; 4157538SRichard.Morris@Sun.COM if (flags & ZFS_ITER_RECURSE) { 416789Sahrens argtype |= ZFS_TYPE_FILESYSTEM; 417789Sahrens if (types & ZFS_TYPE_SNAPSHOT) 418789Sahrens argtype |= ZFS_TYPE_VOLUME; 419789Sahrens } 420789Sahrens 421789Sahrens for (i = 0; i < argc; i++) { 4227538SRichard.Morris@Sun.COM if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) { 4233635Sck153898 zhp = zfs_path_to_zhandle(g_zfs, argv[i], 4243635Sck153898 argtype); 4253635Sck153898 } else { 4263635Sck153898 zhp = zfs_open(g_zfs, argv[i], argtype); 4273635Sck153898 } 4283635Sck153898 if (zhp != NULL) 4292082Seschrock ret |= zfs_callback(zhp, &cb); 430789Sahrens else 431789Sahrens ret = 1; 432789Sahrens } 433789Sahrens } 434789Sahrens 435789Sahrens /* 436789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 437789Sahrens * over each one and execute the real user callback. 438789Sahrens */ 439789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 440789Sahrens node = uu_avl_next(cb.cb_avl, node)) 441789Sahrens ret |= callback(node->zn_handle, data); 442789Sahrens 443789Sahrens /* 444789Sahrens * Finally, clean up the AVL tree. 445789Sahrens */ 446789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 447789Sahrens (void) fprintf(stderr, 448789Sahrens gettext("internal error: out of memory")); 449789Sahrens exit(1); 450789Sahrens } 451789Sahrens 452789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 453789Sahrens uu_avl_remove(cb.cb_avl, node); 454789Sahrens zfs_close(node->zn_handle); 455789Sahrens free(node); 456789Sahrens } 457789Sahrens 458789Sahrens uu_avl_walk_end(walk); 459789Sahrens uu_avl_destroy(cb.cb_avl); 460789Sahrens uu_avl_pool_destroy(avl_pool); 461789Sahrens 462789Sahrens return (ret); 463789Sahrens } 464