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 /* 221356Seschrock * Copyright 2006 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*2676Seschrock zfs_proplist_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) { 88*2676Seschrock if (cb->cb_proplist && 89*2676Seschrock zfs_expand_proplist(zhp, cb->cb_proplist) != 0) { 90*2676Seschrock free(node); 91*2676Seschrock return (-1); 92*2676Seschrock } 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 114*2676Seschrock int 115*2676Seschrock zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 1162379Ssjelinek boolean_t reverse) 1172379Ssjelinek { 1182379Ssjelinek zfs_sort_column_t *col; 119*2676Seschrock zfs_prop_t prop; 120*2676Seschrock 121*2676Seschrock if ((prop = zfs_name_to_prop(name)) == ZFS_PROP_INVAL && 122*2676Seschrock !zfs_prop_user(name)) 123*2676Seschrock return (-1); 1242379Ssjelinek 1252379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 1262379Ssjelinek 1272379Ssjelinek col->sc_prop = prop; 1282379Ssjelinek col->sc_reverse = reverse; 129*2676Seschrock if (prop == ZFS_PROP_INVAL) { 130*2676Seschrock col->sc_user_prop = safe_malloc(strlen(name) + 1); 131*2676Seschrock (void) strcpy(col->sc_user_prop, name); 132*2676Seschrock } 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 } 141*2676Seschrock 142*2676Seschrock 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; 152*2676Seschrock 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) { 234*2676Seschrock char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 235*2676Seschrock char *lstr, *rstr; 2362379Ssjelinek uint64_t lnum, rnum; 237*2676Seschrock boolean_t lvalid, rvalid; 2382379Ssjelinek int ret = 0; 2392379Ssjelinek 240*2676Seschrock /* 241*2676Seschrock * We group the checks below the generic code. If 'lstr' and 242*2676Seschrock * 'rstr' are non-NULL, then we do a string based comparison. 243*2676Seschrock * Otherwise, we compare 'lnum' and 'rnum'. 244*2676Seschrock */ 245*2676Seschrock lstr = rstr = NULL; 246*2676Seschrock if (psc->sc_prop == ZFS_PROP_INVAL) { 247*2676Seschrock nvlist_t *luser, *ruser; 248*2676Seschrock nvlist_t *lval, *rval; 249*2676Seschrock 250*2676Seschrock luser = zfs_get_user_props(l); 251*2676Seschrock ruser = zfs_get_user_props(r); 2522379Ssjelinek 253*2676Seschrock lvalid = (nvlist_lookup_nvlist(luser, 254*2676Seschrock psc->sc_user_prop, &lval) == 0); 255*2676Seschrock rvalid = (nvlist_lookup_nvlist(ruser, 256*2676Seschrock psc->sc_user_prop, &rval) == 0); 2572379Ssjelinek 258*2676Seschrock if (lvalid) 259*2676Seschrock verify(nvlist_lookup_string(lval, 260*2676Seschrock ZFS_PROP_VALUE, &lstr) == 0); 261*2676Seschrock if (rvalid) 262*2676Seschrock verify(nvlist_lookup_string(rval, 263*2676Seschrock ZFS_PROP_VALUE, &rstr) == 0); 264*2676Seschrock 265*2676Seschrock } else if (zfs_prop_is_string(psc->sc_prop)) { 266*2676Seschrock lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 267*2676Seschrock sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 268*2676Seschrock rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 269*2676Seschrock sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 270*2676Seschrock 271*2676Seschrock lstr = lbuf; 272*2676Seschrock 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 279*2676Seschrock if (lvalid) 280*2676Seschrock (void) zfs_prop_get_numeric(l, psc->sc_prop, 281*2676Seschrock &lnum, NULL, NULL, 0); 282*2676Seschrock if (rvalid) 283*2676Seschrock (void) zfs_prop_get_numeric(r, psc->sc_prop, 284*2676Seschrock &rnum, NULL, NULL, 0); 285*2676Seschrock } 2862379Ssjelinek 287*2676Seschrock if (!lvalid && !rvalid) 288*2676Seschrock continue; 289*2676Seschrock else if (!lvalid) 290*2676Seschrock return (1); 291*2676Seschrock else if (!rvalid) 292*2676Seschrock return (-1); 2932379Ssjelinek 294*2676Seschrock if (lstr) 295*2676Seschrock ret = strcmp(lstr, rstr); 296*2676Seschrock if (lnum < rnum) 297*2676Seschrock ret = -1; 298*2676Seschrock else if (lnum > rnum) 299*2676Seschrock 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*2676Seschrock zfs_sort_column_t *sortcol, zfs_proplist_t **proplist, zfs_iter_f callback, 314*2676Seschrock void *data) 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; 332*2676Seschrock 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++) { 3642082Seschrock if ((zhp = zfs_open(g_zfs, argv[i], argtype)) != NULL) 3652082Seschrock ret |= zfs_callback(zhp, &cb); 366789Sahrens else 367789Sahrens ret = 1; 368789Sahrens } 369789Sahrens } 370789Sahrens 371789Sahrens /* 372789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 373789Sahrens * over each one and execute the real user callback. 374789Sahrens */ 375789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 376789Sahrens node = uu_avl_next(cb.cb_avl, node)) 377789Sahrens ret |= callback(node->zn_handle, data); 378789Sahrens 379789Sahrens /* 380789Sahrens * Finally, clean up the AVL tree. 381789Sahrens */ 382789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 383789Sahrens (void) fprintf(stderr, 384789Sahrens gettext("internal error: out of memory")); 385789Sahrens exit(1); 386789Sahrens } 387789Sahrens 388789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 389789Sahrens uu_avl_remove(cb.cb_avl, node); 390789Sahrens zfs_close(node->zn_handle); 391789Sahrens free(node); 392789Sahrens } 393789Sahrens 394789Sahrens uu_avl_walk_end(walk); 395789Sahrens uu_avl_destroy(cb.cb_avl); 396789Sahrens uu_avl_pool_destroy(avl_pool); 397789Sahrens 398789Sahrens return (ret); 399789Sahrens } 400