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 /* 228802SSanjeev.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 { 569365SChris.Gerhard@sun.com uu_avl_t *cb_avl; 579365SChris.Gerhard@sun.com int cb_flags; 589365SChris.Gerhard@sun.com zfs_type_t cb_types; 599365SChris.Gerhard@sun.com zfs_sort_column_t *cb_sortcol; 609365SChris.Gerhard@sun.com zprop_list_t **cb_proplist; 619365SChris.Gerhard@sun.com int cb_depth_limit; 629365SChris.Gerhard@sun.com int cb_depth; 639365SChris.Gerhard@sun.com uint8_t cb_props_table[ZFS_NUM_PROPS]; 64789Sahrens } callback_data_t; 65789Sahrens 66789Sahrens uu_avl_pool_t *avl_pool; 67789Sahrens 68789Sahrens /* 697538SRichard.Morris@Sun.COM * Include snaps if they were requested or if this a zfs list where types 707538SRichard.Morris@Sun.COM * were not specified and the "listsnapshots" property is set on this pool. 717538SRichard.Morris@Sun.COM */ 727538SRichard.Morris@Sun.COM static int 737538SRichard.Morris@Sun.COM zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb) 747538SRichard.Morris@Sun.COM { 757538SRichard.Morris@Sun.COM zpool_handle_t *zph; 767538SRichard.Morris@Sun.COM 777538SRichard.Morris@Sun.COM if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0) 787538SRichard.Morris@Sun.COM return (cb->cb_types & ZFS_TYPE_SNAPSHOT); 797538SRichard.Morris@Sun.COM 807538SRichard.Morris@Sun.COM zph = zfs_get_pool_handle(zhp); 817538SRichard.Morris@Sun.COM return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL)); 827538SRichard.Morris@Sun.COM } 837538SRichard.Morris@Sun.COM 847538SRichard.Morris@Sun.COM /* 857538SRichard.Morris@Sun.COM * Called for each dataset. If the object is of an appropriate type, 86789Sahrens * add it to the avl tree and recurse over any children as necessary. 87789Sahrens */ 885367Sahrens static int 89789Sahrens zfs_callback(zfs_handle_t *zhp, void *data) 90789Sahrens { 91789Sahrens callback_data_t *cb = data; 92789Sahrens int dontclose = 0; 937538SRichard.Morris@Sun.COM int include_snaps = zfs_include_snapshots(zhp, cb); 94789Sahrens 957538SRichard.Morris@Sun.COM if ((zfs_get_type(zhp) & cb->cb_types) || 967538SRichard.Morris@Sun.COM ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) { 97789Sahrens uu_avl_index_t idx; 98789Sahrens zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 99789Sahrens 100789Sahrens node->zn_handle = zhp; 101789Sahrens uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 1022379Ssjelinek if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, 1032379Ssjelinek &idx) == NULL) { 1048802SSanjeev.Bagewadi@Sun.COM if (cb->cb_proplist) { 1058802SSanjeev.Bagewadi@Sun.COM if ((*cb->cb_proplist) && 1068802SSanjeev.Bagewadi@Sun.COM !(*cb->cb_proplist)->pl_all) 1078802SSanjeev.Bagewadi@Sun.COM zfs_prune_proplist(zhp, 1088802SSanjeev.Bagewadi@Sun.COM cb->cb_props_table); 1098802SSanjeev.Bagewadi@Sun.COM 1108802SSanjeev.Bagewadi@Sun.COM if (zfs_expand_proplist(zhp, cb->cb_proplist) 1118802SSanjeev.Bagewadi@Sun.COM != 0) { 1128802SSanjeev.Bagewadi@Sun.COM free(node); 1138802SSanjeev.Bagewadi@Sun.COM return (-1); 1148802SSanjeev.Bagewadi@Sun.COM } 1152676Seschrock } 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 */ 1269365SChris.Gerhard@sun.com if (cb->cb_flags & ZFS_ITER_RECURSE && 1279365SChris.Gerhard@sun.com ((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 || 1289365SChris.Gerhard@sun.com cb->cb_depth < cb->cb_depth_limit)) { 1299365SChris.Gerhard@sun.com cb->cb_depth++; 1305367Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) 1315367Sahrens (void) zfs_iter_filesystems(zhp, zfs_callback, data); 1327538SRichard.Morris@Sun.COM if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) 1335367Sahrens (void) zfs_iter_snapshots(zhp, zfs_callback, data); 1349365SChris.Gerhard@sun.com cb->cb_depth--; 1355367Sahrens } 136789Sahrens 137789Sahrens if (!dontclose) 138789Sahrens zfs_close(zhp); 139789Sahrens 140789Sahrens return (0); 141789Sahrens } 142789Sahrens 1432676Seschrock int 1442676Seschrock zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 1452379Ssjelinek boolean_t reverse) 1462379Ssjelinek { 1472379Ssjelinek zfs_sort_column_t *col; 1482676Seschrock zfs_prop_t prop; 1492676Seschrock 1505094Slling if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && 1512676Seschrock !zfs_prop_user(name)) 1522676Seschrock return (-1); 1532379Ssjelinek 1542379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 1552379Ssjelinek 1562379Ssjelinek col->sc_prop = prop; 1572379Ssjelinek col->sc_reverse = reverse; 1585094Slling if (prop == ZPROP_INVAL) { 1592676Seschrock col->sc_user_prop = safe_malloc(strlen(name) + 1); 1602676Seschrock (void) strcpy(col->sc_user_prop, name); 1612676Seschrock } 1622379Ssjelinek 1632379Ssjelinek if (*sc == NULL) { 1642379Ssjelinek col->sc_last = col; 1652379Ssjelinek *sc = col; 1662379Ssjelinek } else { 1672379Ssjelinek (*sc)->sc_last->sc_next = col; 1682379Ssjelinek (*sc)->sc_last = col; 1692379Ssjelinek } 1702676Seschrock 1712676Seschrock return (0); 1722379Ssjelinek } 1732379Ssjelinek 1742379Ssjelinek void 1752379Ssjelinek zfs_free_sort_columns(zfs_sort_column_t *sc) 1762379Ssjelinek { 1772379Ssjelinek zfs_sort_column_t *col; 1782379Ssjelinek 1792379Ssjelinek while (sc != NULL) { 1802379Ssjelinek col = sc->sc_next; 1812676Seschrock free(sc->sc_user_prop); 1822379Ssjelinek free(sc); 1832379Ssjelinek sc = col; 1842379Ssjelinek } 1852379Ssjelinek } 1862379Ssjelinek 187789Sahrens /* ARGSUSED */ 188789Sahrens static int 189789Sahrens zfs_compare(const void *larg, const void *rarg, void *unused) 190789Sahrens { 191789Sahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 192789Sahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 193789Sahrens const char *lname = zfs_get_name(l); 194789Sahrens const char *rname = zfs_get_name(r); 195789Sahrens char *lat, *rat; 196789Sahrens uint64_t lcreate, rcreate; 197789Sahrens int ret; 198789Sahrens 199789Sahrens lat = (char *)strchr(lname, '@'); 200789Sahrens rat = (char *)strchr(rname, '@'); 201789Sahrens 202789Sahrens if (lat != NULL) 203789Sahrens *lat = '\0'; 204789Sahrens if (rat != NULL) 205789Sahrens *rat = '\0'; 206789Sahrens 207789Sahrens ret = strcmp(lname, rname); 208789Sahrens if (ret == 0) { 209789Sahrens /* 210789Sahrens * If we're comparing a dataset to one of its snapshots, we 211789Sahrens * always make the full dataset first. 212789Sahrens */ 213789Sahrens if (lat == NULL) { 214789Sahrens ret = -1; 215789Sahrens } else if (rat == NULL) { 216789Sahrens ret = 1; 217789Sahrens } else { 218789Sahrens /* 219789Sahrens * If we have two snapshots from the same dataset, then 220789Sahrens * we want to sort them according to creation time. We 221789Sahrens * use the hidden CREATETXG property to get an absolute 222789Sahrens * ordering of snapshots. 223789Sahrens */ 224789Sahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 225789Sahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 226789Sahrens 227789Sahrens if (lcreate < rcreate) 228789Sahrens ret = -1; 229789Sahrens else if (lcreate > rcreate) 230789Sahrens ret = 1; 231789Sahrens } 232789Sahrens } 233789Sahrens 234789Sahrens if (lat != NULL) 235789Sahrens *lat = '@'; 236789Sahrens if (rat != NULL) 237789Sahrens *rat = '@'; 238789Sahrens 239789Sahrens return (ret); 240789Sahrens } 241789Sahrens 2422379Ssjelinek /* 2432379Ssjelinek * Sort datasets by specified columns. 2442379Ssjelinek * 2452379Ssjelinek * o Numeric types sort in ascending order. 2462379Ssjelinek * o String types sort in alphabetical order. 2472379Ssjelinek * o Types inappropriate for a row sort that row to the literal 2482379Ssjelinek * bottom, regardless of the specified ordering. 2492379Ssjelinek * 2502379Ssjelinek * If no sort columns are specified, or two datasets compare equally 2512379Ssjelinek * across all specified columns, they are sorted alphabetically by name 2522379Ssjelinek * with snapshots grouped under their parents. 2532379Ssjelinek */ 2542379Ssjelinek static int 2552379Ssjelinek zfs_sort(const void *larg, const void *rarg, void *data) 2562379Ssjelinek { 2572379Ssjelinek zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 2582379Ssjelinek zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 2592379Ssjelinek zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 2602379Ssjelinek zfs_sort_column_t *psc; 2612379Ssjelinek 2622379Ssjelinek for (psc = sc; psc != NULL; psc = psc->sc_next) { 2632676Seschrock char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 2642676Seschrock char *lstr, *rstr; 2652379Ssjelinek uint64_t lnum, rnum; 2662676Seschrock boolean_t lvalid, rvalid; 2672379Ssjelinek int ret = 0; 2682379Ssjelinek 2692676Seschrock /* 2702676Seschrock * We group the checks below the generic code. If 'lstr' and 2712676Seschrock * 'rstr' are non-NULL, then we do a string based comparison. 2722676Seschrock * Otherwise, we compare 'lnum' and 'rnum'. 2732676Seschrock */ 2742676Seschrock lstr = rstr = NULL; 2755094Slling if (psc->sc_prop == ZPROP_INVAL) { 2762676Seschrock nvlist_t *luser, *ruser; 2772676Seschrock nvlist_t *lval, *rval; 2782676Seschrock 2792676Seschrock luser = zfs_get_user_props(l); 2802676Seschrock ruser = zfs_get_user_props(r); 2812379Ssjelinek 2822676Seschrock lvalid = (nvlist_lookup_nvlist(luser, 2832676Seschrock psc->sc_user_prop, &lval) == 0); 2842676Seschrock rvalid = (nvlist_lookup_nvlist(ruser, 2852676Seschrock psc->sc_user_prop, &rval) == 0); 2862379Ssjelinek 2872676Seschrock if (lvalid) 2882676Seschrock verify(nvlist_lookup_string(lval, 2895094Slling ZPROP_VALUE, &lstr) == 0); 2902676Seschrock if (rvalid) 2912676Seschrock verify(nvlist_lookup_string(rval, 2925094Slling ZPROP_VALUE, &rstr) == 0); 2932676Seschrock 2942676Seschrock } else if (zfs_prop_is_string(psc->sc_prop)) { 2952676Seschrock lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 2962676Seschrock sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 2972676Seschrock rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 2982676Seschrock sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 2992676Seschrock 3002676Seschrock lstr = lbuf; 3012676Seschrock rstr = rbuf; 3022379Ssjelinek } else { 3032379Ssjelinek lvalid = zfs_prop_valid_for_type(psc->sc_prop, 3042379Ssjelinek zfs_get_type(l)); 3052379Ssjelinek rvalid = zfs_prop_valid_for_type(psc->sc_prop, 3062379Ssjelinek zfs_get_type(r)); 3072379Ssjelinek 3082676Seschrock if (lvalid) 3092676Seschrock (void) zfs_prop_get_numeric(l, psc->sc_prop, 3102676Seschrock &lnum, NULL, NULL, 0); 3112676Seschrock if (rvalid) 3122676Seschrock (void) zfs_prop_get_numeric(r, psc->sc_prop, 3132676Seschrock &rnum, NULL, NULL, 0); 3142676Seschrock } 3152379Ssjelinek 3162676Seschrock if (!lvalid && !rvalid) 3172676Seschrock continue; 3182676Seschrock else if (!lvalid) 3192676Seschrock return (1); 3202676Seschrock else if (!rvalid) 3212676Seschrock return (-1); 3222379Ssjelinek 3232676Seschrock if (lstr) 3242676Seschrock ret = strcmp(lstr, rstr); 3257060Srm160521 else if (lnum < rnum) 3262676Seschrock ret = -1; 3272676Seschrock else if (lnum > rnum) 3282676Seschrock ret = 1; 3292379Ssjelinek 3302379Ssjelinek if (ret != 0) { 3312379Ssjelinek if (psc->sc_reverse == B_TRUE) 3322379Ssjelinek ret = (ret < 0) ? 1 : -1; 3332379Ssjelinek return (ret); 3342379Ssjelinek } 3352379Ssjelinek } 3362379Ssjelinek 3372379Ssjelinek return (zfs_compare(larg, rarg, NULL)); 3382379Ssjelinek } 3392379Ssjelinek 340789Sahrens int 3417538SRichard.Morris@Sun.COM zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, 3429365SChris.Gerhard@sun.com zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit, 3437538SRichard.Morris@Sun.COM zfs_iter_f callback, void *data) 344789Sahrens { 3458802SSanjeev.Bagewadi@Sun.COM callback_data_t cb = {0}; 346789Sahrens int ret = 0; 347789Sahrens zfs_node_t *node; 348789Sahrens uu_avl_walk_t *walk; 349789Sahrens 350789Sahrens avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 3512379Ssjelinek offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 352789Sahrens 353789Sahrens if (avl_pool == NULL) { 354789Sahrens (void) fprintf(stderr, 355789Sahrens gettext("internal error: out of memory\n")); 356789Sahrens exit(1); 357789Sahrens } 358789Sahrens 3592379Ssjelinek cb.cb_sortcol = sortcol; 3607538SRichard.Morris@Sun.COM cb.cb_flags = flags; 3612676Seschrock cb.cb_proplist = proplist; 362789Sahrens cb.cb_types = types; 3639365SChris.Gerhard@sun.com cb.cb_depth_limit = limit; 3648802SSanjeev.Bagewadi@Sun.COM /* 365*10242Schris.kirby@sun.com * If cb_proplist is provided then in the zfs_handles created we 3668802SSanjeev.Bagewadi@Sun.COM * retain only those properties listed in cb_proplist and sortcol. 3678802SSanjeev.Bagewadi@Sun.COM * The rest are pruned. So, the caller should make sure that no other 3688802SSanjeev.Bagewadi@Sun.COM * properties other than those listed in cb_proplist/sortcol are 3698802SSanjeev.Bagewadi@Sun.COM * accessed. 3708802SSanjeev.Bagewadi@Sun.COM * 3719396SMatthew.Ahrens@Sun.COM * If cb_proplist is NULL then we retain all the properties. We 3729396SMatthew.Ahrens@Sun.COM * always retain the zoned property, which some other properties 3739396SMatthew.Ahrens@Sun.COM * need (userquota & friends), and the createtxg property, which 3749396SMatthew.Ahrens@Sun.COM * we need to sort snapshots. 3758802SSanjeev.Bagewadi@Sun.COM */ 3768802SSanjeev.Bagewadi@Sun.COM if (cb.cb_proplist && *cb.cb_proplist) { 3778802SSanjeev.Bagewadi@Sun.COM zprop_list_t *p = *cb.cb_proplist; 3788802SSanjeev.Bagewadi@Sun.COM 3798802SSanjeev.Bagewadi@Sun.COM while (p) { 3808802SSanjeev.Bagewadi@Sun.COM if (p->pl_prop >= ZFS_PROP_TYPE && 3818802SSanjeev.Bagewadi@Sun.COM p->pl_prop < ZFS_NUM_PROPS) { 3828802SSanjeev.Bagewadi@Sun.COM cb.cb_props_table[p->pl_prop] = B_TRUE; 3838802SSanjeev.Bagewadi@Sun.COM } 3848802SSanjeev.Bagewadi@Sun.COM p = p->pl_next; 3858802SSanjeev.Bagewadi@Sun.COM } 3868802SSanjeev.Bagewadi@Sun.COM 3878802SSanjeev.Bagewadi@Sun.COM while (sortcol) { 3888802SSanjeev.Bagewadi@Sun.COM if (sortcol->sc_prop >= ZFS_PROP_TYPE && 3898802SSanjeev.Bagewadi@Sun.COM sortcol->sc_prop < ZFS_NUM_PROPS) { 3908802SSanjeev.Bagewadi@Sun.COM cb.cb_props_table[sortcol->sc_prop] = B_TRUE; 3918802SSanjeev.Bagewadi@Sun.COM } 3928802SSanjeev.Bagewadi@Sun.COM sortcol = sortcol->sc_next; 3938802SSanjeev.Bagewadi@Sun.COM } 3949396SMatthew.Ahrens@Sun.COM 3959396SMatthew.Ahrens@Sun.COM cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE; 3969396SMatthew.Ahrens@Sun.COM cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE; 3978802SSanjeev.Bagewadi@Sun.COM } else { 3988802SSanjeev.Bagewadi@Sun.COM (void) memset(cb.cb_props_table, B_TRUE, 3998802SSanjeev.Bagewadi@Sun.COM sizeof (cb.cb_props_table)); 4008802SSanjeev.Bagewadi@Sun.COM } 4018802SSanjeev.Bagewadi@Sun.COM 402789Sahrens if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 403789Sahrens (void) fprintf(stderr, 404789Sahrens gettext("internal error: out of memory\n")); 405789Sahrens exit(1); 406789Sahrens } 407789Sahrens 408789Sahrens if (argc == 0) { 409789Sahrens /* 410789Sahrens * If given no arguments, iterate over all datasets. 411789Sahrens */ 4127538SRichard.Morris@Sun.COM cb.cb_flags |= ZFS_ITER_RECURSE; 4132082Seschrock ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 414789Sahrens } else { 415789Sahrens int i; 416789Sahrens zfs_handle_t *zhp; 417789Sahrens zfs_type_t argtype; 418789Sahrens 419789Sahrens /* 420789Sahrens * If we're recursive, then we always allow filesystems as 421789Sahrens * arguments. If we also are interested in snapshots, then we 422789Sahrens * can take volumes as well. 423789Sahrens */ 424789Sahrens argtype = types; 4257538SRichard.Morris@Sun.COM if (flags & ZFS_ITER_RECURSE) { 426789Sahrens argtype |= ZFS_TYPE_FILESYSTEM; 427789Sahrens if (types & ZFS_TYPE_SNAPSHOT) 428789Sahrens argtype |= ZFS_TYPE_VOLUME; 429789Sahrens } 430789Sahrens 431789Sahrens for (i = 0; i < argc; i++) { 4327538SRichard.Morris@Sun.COM if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) { 4333635Sck153898 zhp = zfs_path_to_zhandle(g_zfs, argv[i], 4343635Sck153898 argtype); 4353635Sck153898 } else { 4363635Sck153898 zhp = zfs_open(g_zfs, argv[i], argtype); 4373635Sck153898 } 4383635Sck153898 if (zhp != NULL) 4392082Seschrock ret |= zfs_callback(zhp, &cb); 440789Sahrens else 441789Sahrens ret = 1; 442789Sahrens } 443789Sahrens } 444789Sahrens 445789Sahrens /* 446789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 447789Sahrens * over each one and execute the real user callback. 448789Sahrens */ 449789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 450789Sahrens node = uu_avl_next(cb.cb_avl, node)) 451789Sahrens ret |= callback(node->zn_handle, data); 452789Sahrens 453789Sahrens /* 454789Sahrens * Finally, clean up the AVL tree. 455789Sahrens */ 456789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 457789Sahrens (void) fprintf(stderr, 458789Sahrens gettext("internal error: out of memory")); 459789Sahrens exit(1); 460789Sahrens } 461789Sahrens 462789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 463789Sahrens uu_avl_remove(cb.cb_avl, node); 464789Sahrens zfs_close(node->zn_handle); 465789Sahrens free(node); 466789Sahrens } 467789Sahrens 468789Sahrens uu_avl_walk_end(walk); 469789Sahrens uu_avl_destroy(cb.cb_avl); 470789Sahrens uu_avl_pool_destroy(avl_pool); 471789Sahrens 472789Sahrens return (ret); 473789Sahrens } 474