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 /* 223635Sck153898 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23789Sahrens * Use is subject to license terms. 24789Sahrens */ 25789Sahrens 26789Sahrens #pragma ident "%Z%%M% %I% %E% SMI" 27789Sahrens 28789Sahrens #include <libintl.h> 29789Sahrens #include <libuutil.h> 30789Sahrens #include <stddef.h> 31789Sahrens #include <stdio.h> 32789Sahrens #include <stdlib.h> 33789Sahrens #include <strings.h> 34789Sahrens 35789Sahrens #include <libzfs.h> 36789Sahrens 37789Sahrens #include "zfs_util.h" 382379Ssjelinek #include "zfs_iter.h" 39789Sahrens 40789Sahrens /* 41789Sahrens * This is a private interface used to gather up all the datasets specified on 42789Sahrens * the command line so that we can iterate over them in order. 43789Sahrens * 44789Sahrens * First, we iterate over all filesystems, gathering them together into an 452379Ssjelinek * AVL tree. We report errors for any explicitly specified datasets 46789Sahrens * that we couldn't open. 47789Sahrens * 48789Sahrens * When finished, we have an AVL tree of ZFS handles. We go through and execute 49789Sahrens * the provided callback for each one, passing whatever data the user supplied. 50789Sahrens */ 51789Sahrens 52789Sahrens typedef struct zfs_node { 53789Sahrens zfs_handle_t *zn_handle; 54789Sahrens uu_avl_node_t zn_avlnode; 55789Sahrens } zfs_node_t; 56789Sahrens 57789Sahrens typedef struct callback_data { 58789Sahrens uu_avl_t *cb_avl; 59789Sahrens int cb_recurse; 60789Sahrens zfs_type_t cb_types; 612379Ssjelinek zfs_sort_column_t *cb_sortcol; 62*5094Slling zprop_list_t **cb_proplist; 63789Sahrens } callback_data_t; 64789Sahrens 65789Sahrens uu_avl_pool_t *avl_pool; 66789Sahrens 67789Sahrens /* 68789Sahrens * Called for each dataset. If the object the object is of an appropriate type, 69789Sahrens * add it to the avl tree and recurse over any children as necessary. 70789Sahrens */ 71789Sahrens int 72789Sahrens zfs_callback(zfs_handle_t *zhp, void *data) 73789Sahrens { 74789Sahrens callback_data_t *cb = data; 75789Sahrens int dontclose = 0; 76789Sahrens 77789Sahrens /* 78789Sahrens * If this object is of the appropriate type, add it to the AVL tree. 79789Sahrens */ 80789Sahrens if (zfs_get_type(zhp) & cb->cb_types) { 81789Sahrens uu_avl_index_t idx; 82789Sahrens zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 83789Sahrens 84789Sahrens node->zn_handle = zhp; 85789Sahrens uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 862379Ssjelinek if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, 872379Ssjelinek &idx) == NULL) { 882676Seschrock if (cb->cb_proplist && 892676Seschrock zfs_expand_proplist(zhp, cb->cb_proplist) != 0) { 902676Seschrock free(node); 912676Seschrock return (-1); 922676Seschrock } 93789Sahrens uu_avl_insert(cb->cb_avl, node, idx); 94789Sahrens dontclose = 1; 95789Sahrens } else { 96789Sahrens free(node); 97789Sahrens } 98789Sahrens } 99789Sahrens 100789Sahrens /* 1011356Seschrock * Recurse if necessary. 102789Sahrens */ 103789Sahrens if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM || 1041356Seschrock (zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types & 1051356Seschrock ZFS_TYPE_SNAPSHOT)))) 106789Sahrens (void) zfs_iter_children(zhp, zfs_callback, data); 107789Sahrens 108789Sahrens if (!dontclose) 109789Sahrens zfs_close(zhp); 110789Sahrens 111789Sahrens return (0); 112789Sahrens } 113789Sahrens 1142676Seschrock int 1152676Seschrock zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 1162379Ssjelinek boolean_t reverse) 1172379Ssjelinek { 1182379Ssjelinek zfs_sort_column_t *col; 1192676Seschrock zfs_prop_t prop; 1202676Seschrock 121*5094Slling if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && 1222676Seschrock !zfs_prop_user(name)) 1232676Seschrock return (-1); 1242379Ssjelinek 1252379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 1262379Ssjelinek 1272379Ssjelinek col->sc_prop = prop; 1282379Ssjelinek col->sc_reverse = reverse; 129*5094Slling if (prop == ZPROP_INVAL) { 1302676Seschrock col->sc_user_prop = safe_malloc(strlen(name) + 1); 1312676Seschrock (void) strcpy(col->sc_user_prop, name); 1322676Seschrock } 1332379Ssjelinek 1342379Ssjelinek if (*sc == NULL) { 1352379Ssjelinek col->sc_last = col; 1362379Ssjelinek *sc = col; 1372379Ssjelinek } else { 1382379Ssjelinek (*sc)->sc_last->sc_next = col; 1392379Ssjelinek (*sc)->sc_last = col; 1402379Ssjelinek } 1412676Seschrock 1422676Seschrock return (0); 1432379Ssjelinek } 1442379Ssjelinek 1452379Ssjelinek void 1462379Ssjelinek zfs_free_sort_columns(zfs_sort_column_t *sc) 1472379Ssjelinek { 1482379Ssjelinek zfs_sort_column_t *col; 1492379Ssjelinek 1502379Ssjelinek while (sc != NULL) { 1512379Ssjelinek col = sc->sc_next; 1522676Seschrock free(sc->sc_user_prop); 1532379Ssjelinek free(sc); 1542379Ssjelinek sc = col; 1552379Ssjelinek } 1562379Ssjelinek } 1572379Ssjelinek 158789Sahrens /* ARGSUSED */ 159789Sahrens static int 160789Sahrens zfs_compare(const void *larg, const void *rarg, void *unused) 161789Sahrens { 162789Sahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 163789Sahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 164789Sahrens const char *lname = zfs_get_name(l); 165789Sahrens const char *rname = zfs_get_name(r); 166789Sahrens char *lat, *rat; 167789Sahrens uint64_t lcreate, rcreate; 168789Sahrens int ret; 169789Sahrens 170789Sahrens lat = (char *)strchr(lname, '@'); 171789Sahrens rat = (char *)strchr(rname, '@'); 172789Sahrens 173789Sahrens if (lat != NULL) 174789Sahrens *lat = '\0'; 175789Sahrens if (rat != NULL) 176789Sahrens *rat = '\0'; 177789Sahrens 178789Sahrens ret = strcmp(lname, rname); 179789Sahrens if (ret == 0) { 180789Sahrens /* 181789Sahrens * If we're comparing a dataset to one of its snapshots, we 182789Sahrens * always make the full dataset first. 183789Sahrens */ 184789Sahrens if (lat == NULL) { 185789Sahrens ret = -1; 186789Sahrens } else if (rat == NULL) { 187789Sahrens ret = 1; 188789Sahrens } else { 189789Sahrens /* 190789Sahrens * If we have two snapshots from the same dataset, then 191789Sahrens * we want to sort them according to creation time. We 192789Sahrens * use the hidden CREATETXG property to get an absolute 193789Sahrens * ordering of snapshots. 194789Sahrens */ 195789Sahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 196789Sahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 197789Sahrens 198789Sahrens if (lcreate < rcreate) 199789Sahrens ret = -1; 200789Sahrens else if (lcreate > rcreate) 201789Sahrens ret = 1; 202789Sahrens } 203789Sahrens } 204789Sahrens 205789Sahrens if (lat != NULL) 206789Sahrens *lat = '@'; 207789Sahrens if (rat != NULL) 208789Sahrens *rat = '@'; 209789Sahrens 210789Sahrens return (ret); 211789Sahrens } 212789Sahrens 2132379Ssjelinek /* 2142379Ssjelinek * Sort datasets by specified columns. 2152379Ssjelinek * 2162379Ssjelinek * o Numeric types sort in ascending order. 2172379Ssjelinek * o String types sort in alphabetical order. 2182379Ssjelinek * o Types inappropriate for a row sort that row to the literal 2192379Ssjelinek * bottom, regardless of the specified ordering. 2202379Ssjelinek * 2212379Ssjelinek * If no sort columns are specified, or two datasets compare equally 2222379Ssjelinek * across all specified columns, they are sorted alphabetically by name 2232379Ssjelinek * with snapshots grouped under their parents. 2242379Ssjelinek */ 2252379Ssjelinek static int 2262379Ssjelinek zfs_sort(const void *larg, const void *rarg, void *data) 2272379Ssjelinek { 2282379Ssjelinek zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 2292379Ssjelinek zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 2302379Ssjelinek zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 2312379Ssjelinek zfs_sort_column_t *psc; 2322379Ssjelinek 2332379Ssjelinek for (psc = sc; psc != NULL; psc = psc->sc_next) { 2342676Seschrock char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 2352676Seschrock char *lstr, *rstr; 2362379Ssjelinek uint64_t lnum, rnum; 2372676Seschrock boolean_t lvalid, rvalid; 2382379Ssjelinek int ret = 0; 2392379Ssjelinek 2402676Seschrock /* 2412676Seschrock * We group the checks below the generic code. If 'lstr' and 2422676Seschrock * 'rstr' are non-NULL, then we do a string based comparison. 2432676Seschrock * Otherwise, we compare 'lnum' and 'rnum'. 2442676Seschrock */ 2452676Seschrock lstr = rstr = NULL; 246*5094Slling if (psc->sc_prop == ZPROP_INVAL) { 2472676Seschrock nvlist_t *luser, *ruser; 2482676Seschrock nvlist_t *lval, *rval; 2492676Seschrock 2502676Seschrock luser = zfs_get_user_props(l); 2512676Seschrock ruser = zfs_get_user_props(r); 2522379Ssjelinek 2532676Seschrock lvalid = (nvlist_lookup_nvlist(luser, 2542676Seschrock psc->sc_user_prop, &lval) == 0); 2552676Seschrock rvalid = (nvlist_lookup_nvlist(ruser, 2562676Seschrock psc->sc_user_prop, &rval) == 0); 2572379Ssjelinek 2582676Seschrock if (lvalid) 2592676Seschrock verify(nvlist_lookup_string(lval, 260*5094Slling ZPROP_VALUE, &lstr) == 0); 2612676Seschrock if (rvalid) 2622676Seschrock verify(nvlist_lookup_string(rval, 263*5094Slling ZPROP_VALUE, &rstr) == 0); 2642676Seschrock 2652676Seschrock } else if (zfs_prop_is_string(psc->sc_prop)) { 2662676Seschrock lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 2672676Seschrock sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 2682676Seschrock rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 2692676Seschrock sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 2702676Seschrock 2712676Seschrock lstr = lbuf; 2722676Seschrock rstr = rbuf; 2732379Ssjelinek } else { 2742379Ssjelinek lvalid = zfs_prop_valid_for_type(psc->sc_prop, 2752379Ssjelinek zfs_get_type(l)); 2762379Ssjelinek rvalid = zfs_prop_valid_for_type(psc->sc_prop, 2772379Ssjelinek zfs_get_type(r)); 2782379Ssjelinek 2792676Seschrock if (lvalid) 2802676Seschrock (void) zfs_prop_get_numeric(l, psc->sc_prop, 2812676Seschrock &lnum, NULL, NULL, 0); 2822676Seschrock if (rvalid) 2832676Seschrock (void) zfs_prop_get_numeric(r, psc->sc_prop, 2842676Seschrock &rnum, NULL, NULL, 0); 2852676Seschrock } 2862379Ssjelinek 2872676Seschrock if (!lvalid && !rvalid) 2882676Seschrock continue; 2892676Seschrock else if (!lvalid) 2902676Seschrock return (1); 2912676Seschrock else if (!rvalid) 2922676Seschrock return (-1); 2932379Ssjelinek 2942676Seschrock if (lstr) 2952676Seschrock ret = strcmp(lstr, rstr); 2962676Seschrock if (lnum < rnum) 2972676Seschrock ret = -1; 2982676Seschrock else if (lnum > rnum) 2992676Seschrock ret = 1; 3002379Ssjelinek 3012379Ssjelinek if (ret != 0) { 3022379Ssjelinek if (psc->sc_reverse == B_TRUE) 3032379Ssjelinek ret = (ret < 0) ? 1 : -1; 3042379Ssjelinek return (ret); 3052379Ssjelinek } 3062379Ssjelinek } 3072379Ssjelinek 3082379Ssjelinek return (zfs_compare(larg, rarg, NULL)); 3092379Ssjelinek } 3102379Ssjelinek 311789Sahrens int 3122082Seschrock zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, 313*5094Slling zfs_sort_column_t *sortcol, zprop_list_t **proplist, zfs_iter_f callback, 3143635Sck153898 void *data, boolean_t args_can_be_paths) 315789Sahrens { 316789Sahrens callback_data_t cb; 317789Sahrens int ret = 0; 318789Sahrens zfs_node_t *node; 319789Sahrens uu_avl_walk_t *walk; 320789Sahrens 321789Sahrens avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 3222379Ssjelinek offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 323789Sahrens 324789Sahrens if (avl_pool == NULL) { 325789Sahrens (void) fprintf(stderr, 326789Sahrens gettext("internal error: out of memory\n")); 327789Sahrens exit(1); 328789Sahrens } 329789Sahrens 3302379Ssjelinek cb.cb_sortcol = sortcol; 331789Sahrens cb.cb_recurse = recurse; 3322676Seschrock cb.cb_proplist = proplist; 333789Sahrens cb.cb_types = types; 334789Sahrens if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 335789Sahrens (void) fprintf(stderr, 336789Sahrens gettext("internal error: out of memory\n")); 337789Sahrens exit(1); 338789Sahrens } 339789Sahrens 340789Sahrens if (argc == 0) { 341789Sahrens /* 342789Sahrens * If given no arguments, iterate over all datasets. 343789Sahrens */ 344789Sahrens cb.cb_recurse = 1; 3452082Seschrock ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 346789Sahrens } else { 347789Sahrens int i; 348789Sahrens zfs_handle_t *zhp; 349789Sahrens zfs_type_t argtype; 350789Sahrens 351789Sahrens /* 352789Sahrens * If we're recursive, then we always allow filesystems as 353789Sahrens * arguments. If we also are interested in snapshots, then we 354789Sahrens * can take volumes as well. 355789Sahrens */ 356789Sahrens argtype = types; 357789Sahrens if (recurse) { 358789Sahrens argtype |= ZFS_TYPE_FILESYSTEM; 359789Sahrens if (types & ZFS_TYPE_SNAPSHOT) 360789Sahrens argtype |= ZFS_TYPE_VOLUME; 361789Sahrens } 362789Sahrens 363789Sahrens for (i = 0; i < argc; i++) { 3643635Sck153898 if (args_can_be_paths) { 3653635Sck153898 zhp = zfs_path_to_zhandle(g_zfs, argv[i], 3663635Sck153898 argtype); 3673635Sck153898 } else { 3683635Sck153898 zhp = zfs_open(g_zfs, argv[i], argtype); 3693635Sck153898 } 3703635Sck153898 if (zhp != NULL) 3712082Seschrock ret |= zfs_callback(zhp, &cb); 372789Sahrens else 373789Sahrens ret = 1; 374789Sahrens } 375789Sahrens } 376789Sahrens 377789Sahrens /* 378789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 379789Sahrens * over each one and execute the real user callback. 380789Sahrens */ 381789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 382789Sahrens node = uu_avl_next(cb.cb_avl, node)) 383789Sahrens ret |= callback(node->zn_handle, data); 384789Sahrens 385789Sahrens /* 386789Sahrens * Finally, clean up the AVL tree. 387789Sahrens */ 388789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 389789Sahrens (void) fprintf(stderr, 390789Sahrens gettext("internal error: out of memory")); 391789Sahrens exit(1); 392789Sahrens } 393789Sahrens 394789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 395789Sahrens uu_avl_remove(cb.cb_avl, node); 396789Sahrens zfs_close(node->zn_handle); 397789Sahrens free(node); 398789Sahrens } 399789Sahrens 400789Sahrens uu_avl_walk_end(walk); 401789Sahrens uu_avl_destroy(cb.cb_avl); 402789Sahrens uu_avl_pool_destroy(avl_pool); 403789Sahrens 404789Sahrens return (ret); 405789Sahrens } 406