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" 38*2379Ssjelinek #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 45*2379Ssjelinek * 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; 61*2379Ssjelinek zfs_sort_column_t *cb_sortcol; 62789Sahrens } callback_data_t; 63789Sahrens 64789Sahrens uu_avl_pool_t *avl_pool; 65789Sahrens 66789Sahrens /* 67789Sahrens * Called for each dataset. If the object the object is of an appropriate type, 68789Sahrens * add it to the avl tree and recurse over any children as necessary. 69789Sahrens */ 70789Sahrens int 71789Sahrens zfs_callback(zfs_handle_t *zhp, void *data) 72789Sahrens { 73789Sahrens callback_data_t *cb = data; 74789Sahrens int dontclose = 0; 75789Sahrens 76789Sahrens /* 77789Sahrens * If this object is of the appropriate type, add it to the AVL tree. 78789Sahrens */ 79789Sahrens if (zfs_get_type(zhp) & cb->cb_types) { 80789Sahrens uu_avl_index_t idx; 81789Sahrens zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 82789Sahrens 83789Sahrens node->zn_handle = zhp; 84789Sahrens uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 85*2379Ssjelinek if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, 86*2379Ssjelinek &idx) == NULL) { 87789Sahrens uu_avl_insert(cb->cb_avl, node, idx); 88789Sahrens dontclose = 1; 89789Sahrens } else { 90789Sahrens free(node); 91789Sahrens } 92789Sahrens } 93789Sahrens 94789Sahrens /* 951356Seschrock * Recurse if necessary. 96789Sahrens */ 97789Sahrens if (cb->cb_recurse && (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM || 981356Seschrock (zfs_get_type(zhp) == ZFS_TYPE_VOLUME && (cb->cb_types & 991356Seschrock ZFS_TYPE_SNAPSHOT)))) 100789Sahrens (void) zfs_iter_children(zhp, zfs_callback, data); 101789Sahrens 102789Sahrens if (!dontclose) 103789Sahrens zfs_close(zhp); 104789Sahrens 105789Sahrens return (0); 106789Sahrens } 107789Sahrens 108*2379Ssjelinek void 109*2379Ssjelinek zfs_add_sort_column(zfs_sort_column_t **sc, zfs_prop_t prop, 110*2379Ssjelinek boolean_t reverse) 111*2379Ssjelinek { 112*2379Ssjelinek zfs_sort_column_t *col; 113*2379Ssjelinek 114*2379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 115*2379Ssjelinek 116*2379Ssjelinek col->sc_prop = prop; 117*2379Ssjelinek col->sc_reverse = reverse; 118*2379Ssjelinek col->sc_next = NULL; 119*2379Ssjelinek 120*2379Ssjelinek if (*sc == NULL) { 121*2379Ssjelinek col->sc_last = col; 122*2379Ssjelinek *sc = col; 123*2379Ssjelinek } else { 124*2379Ssjelinek (*sc)->sc_last->sc_next = col; 125*2379Ssjelinek (*sc)->sc_last = col; 126*2379Ssjelinek } 127*2379Ssjelinek } 128*2379Ssjelinek 129*2379Ssjelinek void 130*2379Ssjelinek zfs_free_sort_columns(zfs_sort_column_t *sc) 131*2379Ssjelinek { 132*2379Ssjelinek zfs_sort_column_t *col; 133*2379Ssjelinek 134*2379Ssjelinek while (sc != NULL) { 135*2379Ssjelinek col = sc->sc_next; 136*2379Ssjelinek free(sc); 137*2379Ssjelinek sc = col; 138*2379Ssjelinek } 139*2379Ssjelinek } 140*2379Ssjelinek 141789Sahrens /* ARGSUSED */ 142789Sahrens static int 143789Sahrens zfs_compare(const void *larg, const void *rarg, void *unused) 144789Sahrens { 145789Sahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 146789Sahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 147789Sahrens const char *lname = zfs_get_name(l); 148789Sahrens const char *rname = zfs_get_name(r); 149789Sahrens char *lat, *rat; 150789Sahrens uint64_t lcreate, rcreate; 151789Sahrens int ret; 152789Sahrens 153789Sahrens lat = (char *)strchr(lname, '@'); 154789Sahrens rat = (char *)strchr(rname, '@'); 155789Sahrens 156789Sahrens if (lat != NULL) 157789Sahrens *lat = '\0'; 158789Sahrens if (rat != NULL) 159789Sahrens *rat = '\0'; 160789Sahrens 161789Sahrens ret = strcmp(lname, rname); 162789Sahrens if (ret == 0) { 163789Sahrens /* 164789Sahrens * If we're comparing a dataset to one of its snapshots, we 165789Sahrens * always make the full dataset first. 166789Sahrens */ 167789Sahrens if (lat == NULL) { 168789Sahrens ret = -1; 169789Sahrens } else if (rat == NULL) { 170789Sahrens ret = 1; 171789Sahrens } else { 172789Sahrens /* 173789Sahrens * If we have two snapshots from the same dataset, then 174789Sahrens * we want to sort them according to creation time. We 175789Sahrens * use the hidden CREATETXG property to get an absolute 176789Sahrens * ordering of snapshots. 177789Sahrens */ 178789Sahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 179789Sahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 180789Sahrens 181789Sahrens if (lcreate < rcreate) 182789Sahrens ret = -1; 183789Sahrens else if (lcreate > rcreate) 184789Sahrens ret = 1; 185789Sahrens } 186789Sahrens } 187789Sahrens 188789Sahrens if (lat != NULL) 189789Sahrens *lat = '@'; 190789Sahrens if (rat != NULL) 191789Sahrens *rat = '@'; 192789Sahrens 193789Sahrens return (ret); 194789Sahrens } 195789Sahrens 196*2379Ssjelinek /* 197*2379Ssjelinek * Sort datasets by specified columns. 198*2379Ssjelinek * 199*2379Ssjelinek * o Numeric types sort in ascending order. 200*2379Ssjelinek * o String types sort in alphabetical order. 201*2379Ssjelinek * o Types inappropriate for a row sort that row to the literal 202*2379Ssjelinek * bottom, regardless of the specified ordering. 203*2379Ssjelinek * 204*2379Ssjelinek * If no sort columns are specified, or two datasets compare equally 205*2379Ssjelinek * across all specified columns, they are sorted alphabetically by name 206*2379Ssjelinek * with snapshots grouped under their parents. 207*2379Ssjelinek */ 208*2379Ssjelinek static int 209*2379Ssjelinek zfs_sort(const void *larg, const void *rarg, void *data) 210*2379Ssjelinek { 211*2379Ssjelinek zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 212*2379Ssjelinek zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 213*2379Ssjelinek zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 214*2379Ssjelinek zfs_sort_column_t *psc; 215*2379Ssjelinek 216*2379Ssjelinek for (psc = sc; psc != NULL; psc = psc->sc_next) { 217*2379Ssjelinek char lstr[ZFS_MAXPROPLEN], rstr[ZFS_MAXPROPLEN]; 218*2379Ssjelinek uint64_t lnum, rnum; 219*2379Ssjelinek int lvalid, rvalid; 220*2379Ssjelinek int ret = 0; 221*2379Ssjelinek 222*2379Ssjelinek if (zfs_prop_is_string(psc->sc_prop)) { 223*2379Ssjelinek lvalid = zfs_prop_get(l, psc->sc_prop, lstr, 224*2379Ssjelinek sizeof (lstr), NULL, NULL, 0, B_TRUE); 225*2379Ssjelinek rvalid = zfs_prop_get(r, psc->sc_prop, rstr, 226*2379Ssjelinek sizeof (rstr), NULL, NULL, 0, B_TRUE); 227*2379Ssjelinek 228*2379Ssjelinek if ((lvalid == -1) && (rvalid == -1)) 229*2379Ssjelinek continue; 230*2379Ssjelinek if (lvalid == -1) 231*2379Ssjelinek return (1); 232*2379Ssjelinek else if (rvalid == -1) 233*2379Ssjelinek return (-1); 234*2379Ssjelinek 235*2379Ssjelinek ret = strcmp(lstr, rstr); 236*2379Ssjelinek } else { 237*2379Ssjelinek lvalid = zfs_prop_valid_for_type(psc->sc_prop, 238*2379Ssjelinek zfs_get_type(l)); 239*2379Ssjelinek rvalid = zfs_prop_valid_for_type(psc->sc_prop, 240*2379Ssjelinek zfs_get_type(r)); 241*2379Ssjelinek 242*2379Ssjelinek if (!lvalid && !rvalid) 243*2379Ssjelinek continue; 244*2379Ssjelinek else if (!lvalid) 245*2379Ssjelinek return (1); 246*2379Ssjelinek else if (!rvalid) 247*2379Ssjelinek return (-1); 248*2379Ssjelinek 249*2379Ssjelinek (void) zfs_prop_get_numeric(l, psc->sc_prop, &lnum, 250*2379Ssjelinek NULL, NULL, 0); 251*2379Ssjelinek (void) zfs_prop_get_numeric(r, psc->sc_prop, &rnum, 252*2379Ssjelinek NULL, NULL, 0); 253*2379Ssjelinek 254*2379Ssjelinek if (lnum < rnum) 255*2379Ssjelinek ret = -1; 256*2379Ssjelinek else if (lnum > rnum) 257*2379Ssjelinek ret = 1; 258*2379Ssjelinek } 259*2379Ssjelinek 260*2379Ssjelinek if (ret != 0) { 261*2379Ssjelinek if (psc->sc_reverse == B_TRUE) 262*2379Ssjelinek ret = (ret < 0) ? 1 : -1; 263*2379Ssjelinek return (ret); 264*2379Ssjelinek } 265*2379Ssjelinek } 266*2379Ssjelinek 267*2379Ssjelinek return (zfs_compare(larg, rarg, NULL)); 268*2379Ssjelinek } 269*2379Ssjelinek 270789Sahrens int 2712082Seschrock zfs_for_each(int argc, char **argv, boolean_t recurse, zfs_type_t types, 272*2379Ssjelinek zfs_sort_column_t *sortcol, zfs_iter_f callback, void *data) 273789Sahrens { 274789Sahrens callback_data_t cb; 275789Sahrens int ret = 0; 276789Sahrens zfs_node_t *node; 277789Sahrens uu_avl_walk_t *walk; 278789Sahrens 279789Sahrens avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 280*2379Ssjelinek offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 281789Sahrens 282789Sahrens if (avl_pool == NULL) { 283789Sahrens (void) fprintf(stderr, 284789Sahrens gettext("internal error: out of memory\n")); 285789Sahrens exit(1); 286789Sahrens } 287789Sahrens 288*2379Ssjelinek cb.cb_sortcol = sortcol; 289789Sahrens cb.cb_recurse = recurse; 290789Sahrens cb.cb_types = types; 291789Sahrens if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 292789Sahrens (void) fprintf(stderr, 293789Sahrens gettext("internal error: out of memory\n")); 294789Sahrens exit(1); 295789Sahrens } 296789Sahrens 297789Sahrens if (argc == 0) { 298789Sahrens /* 299789Sahrens * If given no arguments, iterate over all datasets. 300789Sahrens */ 301789Sahrens cb.cb_recurse = 1; 3022082Seschrock ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 303789Sahrens } else { 304789Sahrens int i; 305789Sahrens zfs_handle_t *zhp; 306789Sahrens zfs_type_t argtype; 307789Sahrens 308789Sahrens /* 309789Sahrens * If we're recursive, then we always allow filesystems as 310789Sahrens * arguments. If we also are interested in snapshots, then we 311789Sahrens * can take volumes as well. 312789Sahrens */ 313789Sahrens argtype = types; 314789Sahrens if (recurse) { 315789Sahrens argtype |= ZFS_TYPE_FILESYSTEM; 316789Sahrens if (types & ZFS_TYPE_SNAPSHOT) 317789Sahrens argtype |= ZFS_TYPE_VOLUME; 318789Sahrens } 319789Sahrens 320789Sahrens for (i = 0; i < argc; i++) { 3212082Seschrock if ((zhp = zfs_open(g_zfs, argv[i], argtype)) != NULL) 3222082Seschrock ret |= zfs_callback(zhp, &cb); 323789Sahrens else 324789Sahrens ret = 1; 325789Sahrens } 326789Sahrens } 327789Sahrens 328789Sahrens /* 329789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 330789Sahrens * over each one and execute the real user callback. 331789Sahrens */ 332789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 333789Sahrens node = uu_avl_next(cb.cb_avl, node)) 334789Sahrens ret |= callback(node->zn_handle, data); 335789Sahrens 336789Sahrens /* 337789Sahrens * Finally, clean up the AVL tree. 338789Sahrens */ 339789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 340789Sahrens (void) fprintf(stderr, 341789Sahrens gettext("internal error: out of memory")); 342789Sahrens exit(1); 343789Sahrens } 344789Sahrens 345789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 346789Sahrens uu_avl_remove(cb.cb_avl, node); 347789Sahrens zfs_close(node->zn_handle); 348789Sahrens free(node); 349789Sahrens } 350789Sahrens 351789Sahrens uu_avl_walk_end(walk); 352789Sahrens uu_avl_destroy(cb.cb_avl); 353789Sahrens uu_avl_pool_destroy(avl_pool); 354789Sahrens 355789Sahrens return (ret); 356789Sahrens } 357