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 /* 227060Srm160521 * Copyright 2008 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 { 56789Sahrens uu_avl_t *cb_avl; 57*7538SRichard.Morris@Sun.COM int cb_flags; 58789Sahrens zfs_type_t cb_types; 592379Ssjelinek zfs_sort_column_t *cb_sortcol; 605094Slling zprop_list_t **cb_proplist; 61789Sahrens } callback_data_t; 62789Sahrens 63789Sahrens uu_avl_pool_t *avl_pool; 64789Sahrens 65789Sahrens /* 66*7538SRichard.Morris@Sun.COM * Include snaps if they were requested or if this a zfs list where types 67*7538SRichard.Morris@Sun.COM * were not specified and the "listsnapshots" property is set on this pool. 68*7538SRichard.Morris@Sun.COM */ 69*7538SRichard.Morris@Sun.COM static int 70*7538SRichard.Morris@Sun.COM zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb) 71*7538SRichard.Morris@Sun.COM { 72*7538SRichard.Morris@Sun.COM zpool_handle_t *zph; 73*7538SRichard.Morris@Sun.COM 74*7538SRichard.Morris@Sun.COM if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0) 75*7538SRichard.Morris@Sun.COM return (cb->cb_types & ZFS_TYPE_SNAPSHOT); 76*7538SRichard.Morris@Sun.COM 77*7538SRichard.Morris@Sun.COM zph = zfs_get_pool_handle(zhp); 78*7538SRichard.Morris@Sun.COM return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL)); 79*7538SRichard.Morris@Sun.COM } 80*7538SRichard.Morris@Sun.COM 81*7538SRichard.Morris@Sun.COM /* 82*7538SRichard.Morris@Sun.COM * Called for each dataset. If the object is of an appropriate type, 83789Sahrens * add it to the avl tree and recurse over any children as necessary. 84789Sahrens */ 855367Sahrens static int 86789Sahrens zfs_callback(zfs_handle_t *zhp, void *data) 87789Sahrens { 88789Sahrens callback_data_t *cb = data; 89789Sahrens int dontclose = 0; 90*7538SRichard.Morris@Sun.COM int include_snaps = zfs_include_snapshots(zhp, cb); 91789Sahrens 92*7538SRichard.Morris@Sun.COM if ((zfs_get_type(zhp) & cb->cb_types) || 93*7538SRichard.Morris@Sun.COM ((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) { 94789Sahrens uu_avl_index_t idx; 95789Sahrens zfs_node_t *node = safe_malloc(sizeof (zfs_node_t)); 96789Sahrens 97789Sahrens node->zn_handle = zhp; 98789Sahrens uu_avl_node_init(node, &node->zn_avlnode, avl_pool); 992379Ssjelinek if (uu_avl_find(cb->cb_avl, node, cb->cb_sortcol, 1002379Ssjelinek &idx) == NULL) { 1012676Seschrock if (cb->cb_proplist && 1022676Seschrock zfs_expand_proplist(zhp, cb->cb_proplist) != 0) { 1032676Seschrock free(node); 1042676Seschrock return (-1); 1052676Seschrock } 106789Sahrens uu_avl_insert(cb->cb_avl, node, idx); 107789Sahrens dontclose = 1; 108789Sahrens } else { 109789Sahrens free(node); 110789Sahrens } 111789Sahrens } 112789Sahrens 113789Sahrens /* 1141356Seschrock * Recurse if necessary. 115789Sahrens */ 116*7538SRichard.Morris@Sun.COM if (cb->cb_flags & ZFS_ITER_RECURSE) { 1175367Sahrens if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) 1185367Sahrens (void) zfs_iter_filesystems(zhp, zfs_callback, data); 119*7538SRichard.Morris@Sun.COM if ((zfs_get_type(zhp) != ZFS_TYPE_SNAPSHOT) && include_snaps) 1205367Sahrens (void) zfs_iter_snapshots(zhp, zfs_callback, data); 1215367Sahrens } 122789Sahrens 123789Sahrens if (!dontclose) 124789Sahrens zfs_close(zhp); 125789Sahrens 126789Sahrens return (0); 127789Sahrens } 128789Sahrens 1292676Seschrock int 1302676Seschrock zfs_add_sort_column(zfs_sort_column_t **sc, const char *name, 1312379Ssjelinek boolean_t reverse) 1322379Ssjelinek { 1332379Ssjelinek zfs_sort_column_t *col; 1342676Seschrock zfs_prop_t prop; 1352676Seschrock 1365094Slling if ((prop = zfs_name_to_prop(name)) == ZPROP_INVAL && 1372676Seschrock !zfs_prop_user(name)) 1382676Seschrock return (-1); 1392379Ssjelinek 1402379Ssjelinek col = safe_malloc(sizeof (zfs_sort_column_t)); 1412379Ssjelinek 1422379Ssjelinek col->sc_prop = prop; 1432379Ssjelinek col->sc_reverse = reverse; 1445094Slling if (prop == ZPROP_INVAL) { 1452676Seschrock col->sc_user_prop = safe_malloc(strlen(name) + 1); 1462676Seschrock (void) strcpy(col->sc_user_prop, name); 1472676Seschrock } 1482379Ssjelinek 1492379Ssjelinek if (*sc == NULL) { 1502379Ssjelinek col->sc_last = col; 1512379Ssjelinek *sc = col; 1522379Ssjelinek } else { 1532379Ssjelinek (*sc)->sc_last->sc_next = col; 1542379Ssjelinek (*sc)->sc_last = col; 1552379Ssjelinek } 1562676Seschrock 1572676Seschrock return (0); 1582379Ssjelinek } 1592379Ssjelinek 1602379Ssjelinek void 1612379Ssjelinek zfs_free_sort_columns(zfs_sort_column_t *sc) 1622379Ssjelinek { 1632379Ssjelinek zfs_sort_column_t *col; 1642379Ssjelinek 1652379Ssjelinek while (sc != NULL) { 1662379Ssjelinek col = sc->sc_next; 1672676Seschrock free(sc->sc_user_prop); 1682379Ssjelinek free(sc); 1692379Ssjelinek sc = col; 1702379Ssjelinek } 1712379Ssjelinek } 1722379Ssjelinek 173789Sahrens /* ARGSUSED */ 174789Sahrens static int 175789Sahrens zfs_compare(const void *larg, const void *rarg, void *unused) 176789Sahrens { 177789Sahrens zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 178789Sahrens zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 179789Sahrens const char *lname = zfs_get_name(l); 180789Sahrens const char *rname = zfs_get_name(r); 181789Sahrens char *lat, *rat; 182789Sahrens uint64_t lcreate, rcreate; 183789Sahrens int ret; 184789Sahrens 185789Sahrens lat = (char *)strchr(lname, '@'); 186789Sahrens rat = (char *)strchr(rname, '@'); 187789Sahrens 188789Sahrens if (lat != NULL) 189789Sahrens *lat = '\0'; 190789Sahrens if (rat != NULL) 191789Sahrens *rat = '\0'; 192789Sahrens 193789Sahrens ret = strcmp(lname, rname); 194789Sahrens if (ret == 0) { 195789Sahrens /* 196789Sahrens * If we're comparing a dataset to one of its snapshots, we 197789Sahrens * always make the full dataset first. 198789Sahrens */ 199789Sahrens if (lat == NULL) { 200789Sahrens ret = -1; 201789Sahrens } else if (rat == NULL) { 202789Sahrens ret = 1; 203789Sahrens } else { 204789Sahrens /* 205789Sahrens * If we have two snapshots from the same dataset, then 206789Sahrens * we want to sort them according to creation time. We 207789Sahrens * use the hidden CREATETXG property to get an absolute 208789Sahrens * ordering of snapshots. 209789Sahrens */ 210789Sahrens lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG); 211789Sahrens rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG); 212789Sahrens 213789Sahrens if (lcreate < rcreate) 214789Sahrens ret = -1; 215789Sahrens else if (lcreate > rcreate) 216789Sahrens ret = 1; 217789Sahrens } 218789Sahrens } 219789Sahrens 220789Sahrens if (lat != NULL) 221789Sahrens *lat = '@'; 222789Sahrens if (rat != NULL) 223789Sahrens *rat = '@'; 224789Sahrens 225789Sahrens return (ret); 226789Sahrens } 227789Sahrens 2282379Ssjelinek /* 2292379Ssjelinek * Sort datasets by specified columns. 2302379Ssjelinek * 2312379Ssjelinek * o Numeric types sort in ascending order. 2322379Ssjelinek * o String types sort in alphabetical order. 2332379Ssjelinek * o Types inappropriate for a row sort that row to the literal 2342379Ssjelinek * bottom, regardless of the specified ordering. 2352379Ssjelinek * 2362379Ssjelinek * If no sort columns are specified, or two datasets compare equally 2372379Ssjelinek * across all specified columns, they are sorted alphabetically by name 2382379Ssjelinek * with snapshots grouped under their parents. 2392379Ssjelinek */ 2402379Ssjelinek static int 2412379Ssjelinek zfs_sort(const void *larg, const void *rarg, void *data) 2422379Ssjelinek { 2432379Ssjelinek zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle; 2442379Ssjelinek zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle; 2452379Ssjelinek zfs_sort_column_t *sc = (zfs_sort_column_t *)data; 2462379Ssjelinek zfs_sort_column_t *psc; 2472379Ssjelinek 2482379Ssjelinek for (psc = sc; psc != NULL; psc = psc->sc_next) { 2492676Seschrock char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN]; 2502676Seschrock char *lstr, *rstr; 2512379Ssjelinek uint64_t lnum, rnum; 2522676Seschrock boolean_t lvalid, rvalid; 2532379Ssjelinek int ret = 0; 2542379Ssjelinek 2552676Seschrock /* 2562676Seschrock * We group the checks below the generic code. If 'lstr' and 2572676Seschrock * 'rstr' are non-NULL, then we do a string based comparison. 2582676Seschrock * Otherwise, we compare 'lnum' and 'rnum'. 2592676Seschrock */ 2602676Seschrock lstr = rstr = NULL; 2615094Slling if (psc->sc_prop == ZPROP_INVAL) { 2622676Seschrock nvlist_t *luser, *ruser; 2632676Seschrock nvlist_t *lval, *rval; 2642676Seschrock 2652676Seschrock luser = zfs_get_user_props(l); 2662676Seschrock ruser = zfs_get_user_props(r); 2672379Ssjelinek 2682676Seschrock lvalid = (nvlist_lookup_nvlist(luser, 2692676Seschrock psc->sc_user_prop, &lval) == 0); 2702676Seschrock rvalid = (nvlist_lookup_nvlist(ruser, 2712676Seschrock psc->sc_user_prop, &rval) == 0); 2722379Ssjelinek 2732676Seschrock if (lvalid) 2742676Seschrock verify(nvlist_lookup_string(lval, 2755094Slling ZPROP_VALUE, &lstr) == 0); 2762676Seschrock if (rvalid) 2772676Seschrock verify(nvlist_lookup_string(rval, 2785094Slling ZPROP_VALUE, &rstr) == 0); 2792676Seschrock 2802676Seschrock } else if (zfs_prop_is_string(psc->sc_prop)) { 2812676Seschrock lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf, 2822676Seschrock sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0); 2832676Seschrock rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf, 2842676Seschrock sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0); 2852676Seschrock 2862676Seschrock lstr = lbuf; 2872676Seschrock rstr = rbuf; 2882379Ssjelinek } else { 2892379Ssjelinek lvalid = zfs_prop_valid_for_type(psc->sc_prop, 2902379Ssjelinek zfs_get_type(l)); 2912379Ssjelinek rvalid = zfs_prop_valid_for_type(psc->sc_prop, 2922379Ssjelinek zfs_get_type(r)); 2932379Ssjelinek 2942676Seschrock if (lvalid) 2952676Seschrock (void) zfs_prop_get_numeric(l, psc->sc_prop, 2962676Seschrock &lnum, NULL, NULL, 0); 2972676Seschrock if (rvalid) 2982676Seschrock (void) zfs_prop_get_numeric(r, psc->sc_prop, 2992676Seschrock &rnum, NULL, NULL, 0); 3002676Seschrock } 3012379Ssjelinek 3022676Seschrock if (!lvalid && !rvalid) 3032676Seschrock continue; 3042676Seschrock else if (!lvalid) 3052676Seschrock return (1); 3062676Seschrock else if (!rvalid) 3072676Seschrock return (-1); 3082379Ssjelinek 3092676Seschrock if (lstr) 3102676Seschrock ret = strcmp(lstr, rstr); 3117060Srm160521 else if (lnum < rnum) 3122676Seschrock ret = -1; 3132676Seschrock else if (lnum > rnum) 3142676Seschrock ret = 1; 3152379Ssjelinek 3162379Ssjelinek if (ret != 0) { 3172379Ssjelinek if (psc->sc_reverse == B_TRUE) 3182379Ssjelinek ret = (ret < 0) ? 1 : -1; 3192379Ssjelinek return (ret); 3202379Ssjelinek } 3212379Ssjelinek } 3222379Ssjelinek 3232379Ssjelinek return (zfs_compare(larg, rarg, NULL)); 3242379Ssjelinek } 3252379Ssjelinek 326789Sahrens int 327*7538SRichard.Morris@Sun.COM zfs_for_each(int argc, char **argv, int flags, zfs_type_t types, 328*7538SRichard.Morris@Sun.COM zfs_sort_column_t *sortcol, zprop_list_t **proplist, 329*7538SRichard.Morris@Sun.COM zfs_iter_f callback, void *data) 330789Sahrens { 331789Sahrens callback_data_t cb; 332789Sahrens int ret = 0; 333789Sahrens zfs_node_t *node; 334789Sahrens uu_avl_walk_t *walk; 335789Sahrens 336789Sahrens avl_pool = uu_avl_pool_create("zfs_pool", sizeof (zfs_node_t), 3372379Ssjelinek offsetof(zfs_node_t, zn_avlnode), zfs_sort, UU_DEFAULT); 338789Sahrens 339789Sahrens if (avl_pool == NULL) { 340789Sahrens (void) fprintf(stderr, 341789Sahrens gettext("internal error: out of memory\n")); 342789Sahrens exit(1); 343789Sahrens } 344789Sahrens 3452379Ssjelinek cb.cb_sortcol = sortcol; 346*7538SRichard.Morris@Sun.COM cb.cb_flags = flags; 3472676Seschrock cb.cb_proplist = proplist; 348789Sahrens cb.cb_types = types; 349789Sahrens if ((cb.cb_avl = uu_avl_create(avl_pool, NULL, UU_DEFAULT)) == NULL) { 350789Sahrens (void) fprintf(stderr, 351789Sahrens gettext("internal error: out of memory\n")); 352789Sahrens exit(1); 353789Sahrens } 354789Sahrens 355789Sahrens if (argc == 0) { 356789Sahrens /* 357789Sahrens * If given no arguments, iterate over all datasets. 358789Sahrens */ 359*7538SRichard.Morris@Sun.COM cb.cb_flags |= ZFS_ITER_RECURSE; 3602082Seschrock ret = zfs_iter_root(g_zfs, zfs_callback, &cb); 361789Sahrens } else { 362789Sahrens int i; 363789Sahrens zfs_handle_t *zhp; 364789Sahrens zfs_type_t argtype; 365789Sahrens 366789Sahrens /* 367789Sahrens * If we're recursive, then we always allow filesystems as 368789Sahrens * arguments. If we also are interested in snapshots, then we 369789Sahrens * can take volumes as well. 370789Sahrens */ 371789Sahrens argtype = types; 372*7538SRichard.Morris@Sun.COM if (flags & ZFS_ITER_RECURSE) { 373789Sahrens argtype |= ZFS_TYPE_FILESYSTEM; 374789Sahrens if (types & ZFS_TYPE_SNAPSHOT) 375789Sahrens argtype |= ZFS_TYPE_VOLUME; 376789Sahrens } 377789Sahrens 378789Sahrens for (i = 0; i < argc; i++) { 379*7538SRichard.Morris@Sun.COM if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) { 3803635Sck153898 zhp = zfs_path_to_zhandle(g_zfs, argv[i], 3813635Sck153898 argtype); 3823635Sck153898 } else { 3833635Sck153898 zhp = zfs_open(g_zfs, argv[i], argtype); 3843635Sck153898 } 3853635Sck153898 if (zhp != NULL) 3862082Seschrock ret |= zfs_callback(zhp, &cb); 387789Sahrens else 388789Sahrens ret = 1; 389789Sahrens } 390789Sahrens } 391789Sahrens 392789Sahrens /* 393789Sahrens * At this point we've got our AVL tree full of zfs handles, so iterate 394789Sahrens * over each one and execute the real user callback. 395789Sahrens */ 396789Sahrens for (node = uu_avl_first(cb.cb_avl); node != NULL; 397789Sahrens node = uu_avl_next(cb.cb_avl, node)) 398789Sahrens ret |= callback(node->zn_handle, data); 399789Sahrens 400789Sahrens /* 401789Sahrens * Finally, clean up the AVL tree. 402789Sahrens */ 403789Sahrens if ((walk = uu_avl_walk_start(cb.cb_avl, UU_WALK_ROBUST)) == NULL) { 404789Sahrens (void) fprintf(stderr, 405789Sahrens gettext("internal error: out of memory")); 406789Sahrens exit(1); 407789Sahrens } 408789Sahrens 409789Sahrens while ((node = uu_avl_walk_next(walk)) != NULL) { 410789Sahrens uu_avl_remove(cb.cb_avl, node); 411789Sahrens zfs_close(node->zn_handle); 412789Sahrens free(node); 413789Sahrens } 414789Sahrens 415789Sahrens uu_avl_walk_end(walk); 416789Sahrens uu_avl_destroy(cb.cb_avl); 417789Sahrens uu_avl_pool_destroy(avl_pool); 418789Sahrens 419789Sahrens return (ret); 420789Sahrens } 421