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; 625094Slling 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 */ 71*5367Sahrens static 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 */ 103*5367Sahrens if (cb->cb_recurse) { 104*5367Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) 105*5367Sahrens (void) zfs_iter_filesystems(zhp, zfs_callback, data); 106*5367Sahrens if (zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT && 107*5367Sahrens (cb->cb_types & ZFS_TYPE_SNAPSHOT)) 108*5367Sahrens (void) zfs_iter_snapshots(zhp, zfs_callback, data); 109*5367Sahrens } 110789Sahrens 111789Sahrens if (!dontclose) 112789Sahrens zfs_close(zhp); 113789Sahrens 114789Sahrens return (0); 115789Sahrens } 116789Sahrens 1172676Seschrock int 1182676Seschrock zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 1192379Ssjelinek boolean_t reverse) 1202379Ssjelinek { 1212379Ssjelinek zfs_sort_column_t *col; 1222676Seschrock zfs_prop_t prop; 1232676Seschrock 1245094Slling if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && 1252676Seschrock !zfs_prop_user(name)) 1262676Seschrock return (-1); 1272379Ssjelinek 1282379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 1292379Ssjelinek 1302379Ssjelinek col->sc_prop = prop; 1312379Ssjelinek col->sc_reverse = reverse; 1325094Slling if (prop == ZPROP_INVAL) { 1332676Seschrock col->sc_user_prop = safe_malloc(strlen(name) + 1); 1342676Seschrock (void) strcpy(col->sc_user_prop, name); 1352676Seschrock } 1362379Ssjelinek 1372379Ssjelinek if (*sc == NULL) { 1382379Ssjelinek col->sc_last = col; 1392379Ssjelinek *sc = col; 1402379Ssjelinek } else { 1412379Ssjelinek (*sc)->sc_last->sc_next = col; 1422379Ssjelinek (*sc)->sc_last = col; 1432379Ssjelinek } 1442676Seschrock 1452676Seschrock return (0); 1462379Ssjelinek } 1472379Ssjelinek 1482379Ssjelinek void 1492379Ssjelinek zfs_free_sort_columns(zfs_sort_column_t *sc) 1502379Ssjelinek { 1512379Ssjelinek zfs_sort_column_t *col; 1522379Ssjelinek 1532379Ssjelinek while (sc != NULL) { 1542379Ssjelinek col = sc->sc_next; 1552676Seschrock free(sc->sc_user_prop); 1562379Ssjelinek free(sc); 1572379Ssjelinek sc = col; 1582379Ssjelinek } 1592379Ssjelinek } 1602379Ssjelinek 161789Sahrens /* ARGSUSED */ 162789Sahrens static int 163789Sahrens zfs_compare(const void *larg, const void *rarg, void *unused) 164789Sahrens { 165789Sahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 166789Sahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 167789Sahrens const char *lname = zfs_get_name(l); 168789Sahrens const char *rname = zfs_get_name(r); 169789Sahrens char *lat, *rat; 170789Sahrens uint64_t lcreate, rcreate; 171789Sahrens int ret; 172789Sahrens 173789Sahrens lat = (char *)strchr(lname, '@'); 174789Sahrens rat = (char *)strchr(rname, '@'); 175789Sahrens 176789Sahrens if (lat != NULL) 177789Sahrens *lat = '\0'; 178789Sahrens if (rat != NULL) 179789Sahrens *rat = '\0'; 180789Sahrens 181789Sahrens ret = strcmp(lname, rname); 182789Sahrens if (ret == 0) { 183789Sahrens /* 184789Sahrens * If we're comparing a dataset to one of its snapshots, we 185789Sahrens * always make the full dataset first. 186789Sahrens */ 187789Sahrens if (lat == NULL) { 188789Sahrens ret = -1; 189789Sahrens } else if (rat == NULL) { 190789Sahrens ret = 1; 191789Sahrens } else { 192789Sahrens /* 193789Sahrens * If we have two snapshots from the same dataset, then 194789Sahrens * we want to sort them according to creation time. We 195789Sahrens * use the hidden CREATETXG property to get an absolute 196789Sahrens * ordering of snapshots. 197789Sahrens */ 198789Sahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 199789Sahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 200789Sahrens 201789Sahrens if (lcreate < rcreate) 202789Sahrens ret = -1; 203789Sahrens else if (lcreate > rcreate) 204789Sahrens ret = 1; 205789Sahrens } 206789Sahrens } 207789Sahrens 208789Sahrens if (lat != NULL) 209789Sahrens *lat = '@'; 210789Sahrens if (rat != NULL) 211789Sahrens *rat = '@'; 212789Sahrens 213789Sahrens return (ret); 214789Sahrens } 215789Sahrens 2162379Ssjelinek /* 2172379Ssjelinek * Sort datasets by specified columns. 2182379Ssjelinek * 2192379Ssjelinek * o Numeric types sort in ascending order. 2202379Ssjelinek * o String types sort in alphabetical order. 2212379Ssjelinek * o Types inappropriate for a row sort that row to the literal 2222379Ssjelinek * bottom, regardless of the specified ordering. 2232379Ssjelinek * 2242379Ssjelinek * If no sort columns are specified, or two datasets compare equally 2252379Ssjelinek * across all specified columns, they are sorted alphabetically by name 2262379Ssjelinek * with snapshots grouped under their parents. 2272379Ssjelinek */ 2282379Ssjelinek static int 2292379Ssjelinek zfs_sort(const void *larg, const void *rarg, void *data) 2302379Ssjelinek { 2312379Ssjelinek zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 2322379Ssjelinek zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 2332379Ssjelinek zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 2342379Ssjelinek zfs_sort_column_t *psc; 2352379Ssjelinek 2362379Ssjelinek for (psc = sc; psc != NULL; psc = psc->sc_next) { 2372676Seschrock char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 2382676Seschrock char *lstr, *rstr; 2392379Ssjelinek uint64_t lnum, rnum; 2402676Seschrock boolean_t lvalid, rvalid; 2412379Ssjelinek int ret = 0; 2422379Ssjelinek 2432676Seschrock /* 2442676Seschrock * We group the checks below the generic code. If 'lstr' and 2452676Seschrock * 'rstr' are non-NULL, then we do a string based comparison. 2462676Seschrock * Otherwise, we compare 'lnum' and 'rnum'. 2472676Seschrock */ 2482676Seschrock lstr = rstr = NULL; 2495094Slling if (psc->sc_prop == ZPROP_INVAL) { 2502676Seschrock nvlist_t *luser, *ruser; 2512676Seschrock nvlist_t *lval, *rval; 2522676Seschrock 2532676Seschrock luser = zfs_get_user_props(l); 2542676Seschrock ruser = zfs_get_user_props(r); 2552379Ssjelinek 2562676Seschrock lvalid = (nvlist_lookup_nvlist(luser, 2572676Seschrock psc->sc_user_prop, &lval) == 0); 2582676Seschrock rvalid = (nvlist_lookup_nvlist(ruser, 2592676Seschrock psc->sc_user_prop, &rval) == 0); 2602379Ssjelinek 2612676Seschrock if (lvalid) 2622676Seschrock verify(nvlist_lookup_string(lval, 2635094Slling ZPROP_VALUE, &lstr) == 0); 2642676Seschrock if (rvalid) 2652676Seschrock verify(nvlist_lookup_string(rval, 2665094Slling ZPROP_VALUE, &rstr) == 0); 2672676Seschrock 2682676Seschrock } else if (zfs_prop_is_string(psc->sc_prop)) { 2692676Seschrock lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 2702676Seschrock sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 2712676Seschrock rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 2722676Seschrock sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 2732676Seschrock 2742676Seschrock lstr = lbuf; 2752676Seschrock rstr = rbuf; 2762379Ssjelinek } else { 2772379Ssjelinek lvalid = zfs_prop_valid_for_type(psc->sc_prop, 2782379Ssjelinek zfs_get_type(l)); 2792379Ssjelinek rvalid = zfs_prop_valid_for_type(psc->sc_prop, 2802379Ssjelinek zfs_get_type(r)); 2812379Ssjelinek 2822676Seschrock if (lvalid) 2832676Seschrock (void) zfs_prop_get_numeric(l, psc->sc_prop, 2842676Seschrock &lnum, NULL, NULL, 0); 2852676Seschrock if (rvalid) 2862676Seschrock (void) zfs_prop_get_numeric(r, psc->sc_prop, 2872676Seschrock &rnum, NULL, NULL, 0); 2882676Seschrock } 2892379Ssjelinek 2902676Seschrock if (!lvalid && !rvalid) 2912676Seschrock continue; 2922676Seschrock else if (!lvalid) 2932676Seschrock return (1); 2942676Seschrock else if (!rvalid) 2952676Seschrock return (-1); 2962379Ssjelinek 2972676Seschrock if (lstr) 2982676Seschrock ret = strcmp(lstr, rstr); 2992676Seschrock if (lnum < rnum) 3002676Seschrock ret = -1; 3012676Seschrock else if (lnum > rnum) 3022676Seschrock ret = 1; 3032379Ssjelinek 3042379Ssjelinek if (ret != 0) { 3052379Ssjelinek if (psc->sc_reverse == B_TRUE) 3062379Ssjelinek ret = (ret < 0) ? 1 : -1; 3072379Ssjelinek return (ret); 3082379Ssjelinek } 3092379Ssjelinek } 3102379Ssjelinek 3112379Ssjelinek return (zfs_compare(larg, rarg, NULL)); 3122379Ssjelinek } 3132379Ssjelinek 314789Sahrens int 3152082Seschrock zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, 3165094Slling zfs_sort_column_t *sortcol, zprop_list_t **proplist, zfs_iter_f callback, 3173635Sck153898 void *data, boolean_t args_can_be_paths) 318789Sahrens { 319789Sahrens callback_data_t cb; 320789Sahrens int ret = 0; 321789Sahrens zfs_node_t *node; 322789Sahrens uu_avl_walk_t *walk; 323789Sahrens 324789Sahrens avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 3252379Ssjelinek offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 326789Sahrens 327789Sahrens if (avl_pool == NULL) { 328789Sahrens (void) fprintf(stderr, 329789Sahrens gettext("internal error: out of memory\n")); 330789Sahrens exit(1); 331789Sahrens } 332789Sahrens 3332379Ssjelinek cb.cb_sortcol = sortcol; 334789Sahrens cb.cb_recurse = recurse; 3352676Seschrock cb.cb_proplist = proplist; 336789Sahrens cb.cb_types = types; 337789Sahrens if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 338789Sahrens (void) fprintf(stderr, 339789Sahrens gettext("internal error: out of memory\n")); 340789Sahrens exit(1); 341789Sahrens } 342789Sahrens 343789Sahrens if (argc == 0) { 344789Sahrens /* 345789Sahrens * If given no arguments, iterate over all datasets. 346789Sahrens */ 347789Sahrens cb.cb_recurse = 1; 3482082Seschrock ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 349789Sahrens } else { 350789Sahrens int i; 351789Sahrens zfs_handle_t *zhp; 352789Sahrens zfs_type_t argtype; 353789Sahrens 354789Sahrens /* 355789Sahrens * If we're recursive, then we always allow filesystems as 356789Sahrens * arguments. If we also are interested in snapshots, then we 357789Sahrens * can take volumes as well. 358789Sahrens */ 359789Sahrens argtype = types; 360789Sahrens if (recurse) { 361789Sahrens argtype |= ZFS_TYPE_FILESYSTEM; 362789Sahrens if (types & ZFS_TYPE_SNAPSHOT) 363789Sahrens argtype |= ZFS_TYPE_VOLUME; 364789Sahrens } 365789Sahrens 366789Sahrens for (i = 0; i < argc; i++) { 3673635Sck153898 if (args_can_be_paths) { 3683635Sck153898 zhp = zfs_path_to_zhandle(g_zfs, argv[i], 3693635Sck153898 argtype); 3703635Sck153898 } else { 3713635Sck153898 zhp = zfs_open(g_zfs, argv[i], argtype); 3723635Sck153898 } 3733635Sck153898 if (zhp != NULL) 3742082Seschrock ret |= zfs_callback(zhp, &cb); 375789Sahrens else 376789Sahrens ret = 1; 377789Sahrens } 378789Sahrens } 379789Sahrens 380789Sahrens /* 381789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 382789Sahrens * over each one and execute the real user callback. 383789Sahrens */ 384789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 385789Sahrens node = uu_avl_next(cb.cb_avl, node)) 386789Sahrens ret |= callback(node->zn_handle, data); 387789Sahrens 388789Sahrens /* 389789Sahrens * Finally, clean up the AVL tree. 390789Sahrens */ 391789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 392789Sahrens (void) fprintf(stderr, 393789Sahrens gettext("internal error: out of memory")); 394789Sahrens exit(1); 395789Sahrens } 396789Sahrens 397789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 398789Sahrens uu_avl_remove(cb.cb_avl, node); 399789Sahrens zfs_close(node->zn_handle); 400789Sahrens free(node); 401789Sahrens } 402789Sahrens 403789Sahrens uu_avl_walk_end(walk); 404789Sahrens uu_avl_destroy(cb.cb_avl); 405789Sahrens uu_avl_pool_destroy(avl_pool); 406789Sahrens 407789Sahrens return (ret); 408789Sahrens } 409