Path: blob/main/sys/contrib/openzfs/cmd/zfs/zfs_iter.c
108610 views
// SPDX-License-Identifier: CDDL-1.01/*2* CDDL HEADER START3*4* The contents of this file are subject to the terms of the5* Common Development and Distribution License (the "License").6* You may not use this file except in compliance with the License.7*8* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE9* or https://opensource.org/licenses/CDDL-1.0.10* See the License for the specific language governing permissions11* and limitations under the License.12*13* When distributing Covered Code, include this CDDL HEADER in each14* file and include the License file at usr/src/OPENSOLARIS.LICENSE.15* If applicable, add the following below this CDDL HEADER, with the16* fields enclosed by brackets "[]" replaced with your own identifying17* information: Portions Copyright [yyyy] [name of copyright owner]18*19* CDDL HEADER END20*/2122/*23* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.24* Copyright (c) 2012 Pawel Jakub Dawidek <[email protected]>.25* Copyright 2013 Nexenta Systems, Inc. All rights reserved.26* Copyright (c) 2013 by Delphix. All rights reserved.27*/2829#include <libintl.h>30#include <stddef.h>31#include <stdio.h>32#include <stdlib.h>33#include <string.h>3435#include <libzfs.h>3637#include "zfs_util.h"38#include "zfs_iter.h"3940/*41* This is a private interface used to gather up all the datasets specified on42* the command line so that we can iterate over them in order.43*44* First, we iterate over all filesystems, gathering them together into an45* AVL tree. We report errors for any explicitly specified datasets46* that we couldn't open.47*48* When finished, we have an AVL tree of ZFS handles. We go through and execute49* the provided callback for each one, passing whatever data the user supplied.50*/51typedef struct callback_data callback_data_t;5253typedef struct zfs_node {54zfs_handle_t *zn_handle;55callback_data_t *zn_callback;56avl_node_t zn_avlnode;57} zfs_node_t;5859struct callback_data {60avl_tree_t cb_avl;61int cb_flags;62zfs_type_t cb_types;63zfs_sort_column_t *cb_sortcol;64zprop_list_t **cb_proplist;65int cb_depth_limit;66int cb_depth;67uint8_t cb_props_table[ZFS_NUM_PROPS];68};6970/*71* Include snaps if they were requested or if this a zfs list where types72* were not specified and the "listsnapshots" property is set on this pool.73*/74static boolean_t75zfs_include_snapshots(zfs_handle_t *zhp, callback_data_t *cb)76{77zpool_handle_t *zph;7879if ((cb->cb_flags & ZFS_ITER_PROP_LISTSNAPS) == 0)80return (cb->cb_types & ZFS_TYPE_SNAPSHOT);8182zph = zfs_get_pool_handle(zhp);83return (zpool_get_prop_int(zph, ZPOOL_PROP_LISTSNAPS, NULL));84}8586/*87* Called for each dataset. If the object is of an appropriate type,88* add it to the avl tree and recurse over any children as necessary.89*/90static int91zfs_callback(zfs_handle_t *zhp, void *data)92{93callback_data_t *cb = data;94boolean_t should_close = B_TRUE;95boolean_t include_snaps = zfs_include_snapshots(zhp, cb);96boolean_t include_bmarks = (cb->cb_types & ZFS_TYPE_BOOKMARK);9798if ((zfs_get_type(zhp) & cb->cb_types) ||99((zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) && include_snaps)) {100avl_index_t idx;101zfs_node_t *node = safe_malloc(sizeof (zfs_node_t));102103node->zn_handle = zhp;104node->zn_callback = cb;105if (avl_find(&cb->cb_avl, node, &idx) == NULL) {106if (cb->cb_proplist) {107if ((*cb->cb_proplist) &&108!(*cb->cb_proplist)->pl_all)109zfs_prune_proplist(zhp,110cb->cb_props_table);111112if (zfs_expand_proplist(zhp, cb->cb_proplist,113(cb->cb_flags & ZFS_ITER_RECVD_PROPS),114(cb->cb_flags & ZFS_ITER_LITERAL_PROPS))115!= 0) {116free(node);117return (-1);118}119}120avl_insert(&cb->cb_avl, node, idx);121should_close = B_FALSE;122} else {123free(node);124}125}126127/*128* Recurse if necessary.129*/130if (cb->cb_flags & ZFS_ITER_RECURSE &&131((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||132cb->cb_depth < cb->cb_depth_limit)) {133cb->cb_depth++;134135/*136* If we are not looking for filesystems, we don't need to137* recurse into filesystems when we are at our depth limit.138*/139if ((cb->cb_depth < cb->cb_depth_limit ||140(cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||141(cb->cb_types &142(ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) &&143zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {144(void) zfs_iter_filesystems_v2(zhp, cb->cb_flags,145zfs_callback, data);146}147148if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |149ZFS_TYPE_BOOKMARK)) == 0) && include_snaps) {150(void) zfs_iter_snapshots_v2(zhp, cb->cb_flags,151zfs_callback, data, 0, 0);152}153154if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |155ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks) {156(void) zfs_iter_bookmarks_v2(zhp, cb->cb_flags,157zfs_callback, data);158}159160cb->cb_depth--;161}162163if (should_close)164zfs_close(zhp);165166return (0);167}168169int170zfs_add_sort_column(zfs_sort_column_t **sc, const char *name,171boolean_t reverse)172{173zfs_sort_column_t *col;174zfs_prop_t prop;175176if ((prop = zfs_name_to_prop(name)) == ZPROP_USERPROP &&177!zfs_prop_user(name))178return (-1);179180col = safe_malloc(sizeof (zfs_sort_column_t));181182col->sc_prop = prop;183col->sc_reverse = reverse;184if (prop == ZPROP_USERPROP) {185col->sc_user_prop = safe_malloc(strlen(name) + 1);186(void) strcpy(col->sc_user_prop, name);187}188189if (*sc == NULL) {190col->sc_last = col;191*sc = col;192} else {193(*sc)->sc_last->sc_next = col;194(*sc)->sc_last = col;195}196197return (0);198}199200void201zfs_free_sort_columns(zfs_sort_column_t *sc)202{203zfs_sort_column_t *col;204205while (sc != NULL) {206col = sc->sc_next;207free(sc->sc_user_prop);208free(sc);209sc = col;210}211}212213/*214* Return true if all of the properties to be sorted are populated by215* dsl_dataset_fast_stat(). Note that sc == NULL (no sort) means we216* don't need any extra properties, so returns true.217*/218boolean_t219zfs_sort_only_by_fast(const zfs_sort_column_t *sc)220{221while (sc != NULL) {222switch (sc->sc_prop) {223case ZFS_PROP_NAME:224case ZFS_PROP_GUID:225case ZFS_PROP_CREATETXG:226case ZFS_PROP_NUMCLONES:227case ZFS_PROP_INCONSISTENT:228case ZFS_PROP_REDACTED:229case ZFS_PROP_ORIGIN:230break;231default:232return (B_FALSE);233}234sc = sc->sc_next;235}236237return (B_TRUE);238}239240boolean_t241zfs_list_only_by_fast(const zprop_list_t *p)242{243if (p == NULL) {244/* NULL means 'all' so we can't use simple mode */245return (B_FALSE);246}247248while (p != NULL) {249switch (p->pl_prop) {250case ZFS_PROP_NAME:251case ZFS_PROP_GUID:252case ZFS_PROP_CREATETXG:253case ZFS_PROP_NUMCLONES:254case ZFS_PROP_INCONSISTENT:255case ZFS_PROP_REDACTED:256case ZFS_PROP_ORIGIN:257break;258default:259return (B_FALSE);260}261p = p->pl_next;262}263264return (B_TRUE);265}266267static int268zfs_compare(const void *larg, const void *rarg)269{270zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;271zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;272const char *lname = zfs_get_name(l);273const char *rname = zfs_get_name(r);274char *lat, *rat;275uint64_t lcreate, rcreate;276int ret;277278lat = (char *)strchr(lname, '@');279rat = (char *)strchr(rname, '@');280281if (lat != NULL)282*lat = '\0';283if (rat != NULL)284*rat = '\0';285286ret = TREE_ISIGN(strcmp(lname, rname));287if (ret == 0 && (lat != NULL || rat != NULL)) {288/*289* If we're comparing a dataset to one of its snapshots, we290* always make the full dataset first.291*/292if (lat == NULL) {293ret = -1;294} else if (rat == NULL) {295ret = 1;296} else {297/*298* If we have two snapshots from the same dataset, then299* we want to sort them according to creation time. We300* use the hidden CREATETXG property to get an absolute301* ordering of snapshots.302*/303lcreate = zfs_prop_get_int(l, ZFS_PROP_CREATETXG);304rcreate = zfs_prop_get_int(r, ZFS_PROP_CREATETXG);305306/*307* Both lcreate and rcreate being 0 means we don't have308* properties and we should compare full name.309*/310if (lcreate == 0 && rcreate == 0)311ret = strcmp(lat + 1, rat + 1);312else if (lcreate < rcreate)313ret = -1;314else if (lcreate > rcreate)315ret = 1;316}317}318319if (lat != NULL)320*lat = '@';321if (rat != NULL)322*rat = '@';323324return (ret);325}326327/*328* Sort datasets by specified columns.329*330* o Numeric types sort in ascending order.331* o String types sort in alphabetical order.332* o Types inappropriate for a row sort that row to the literal333* bottom, regardless of the specified ordering.334*335* If no sort columns are specified, or two datasets compare equally336* across all specified columns, they are sorted alphabetically by name337* with snapshots grouped under their parents.338*/339static int340zfs_sort(const void *larg, const void *rarg)341{342zfs_handle_t *l = ((zfs_node_t *)larg)->zn_handle;343zfs_handle_t *r = ((zfs_node_t *)rarg)->zn_handle;344zfs_sort_column_t *sc = ((zfs_node_t *)larg)->zn_callback->cb_sortcol;345zfs_sort_column_t *psc;346347for (psc = sc; psc != NULL; psc = psc->sc_next) {348char lbuf[ZFS_MAXPROPLEN], rbuf[ZFS_MAXPROPLEN];349const char *lstr, *rstr;350uint64_t lnum = 0, rnum = 0;351boolean_t lvalid, rvalid;352int ret = 0;353354/*355* We group the checks below the generic code. If 'lstr' and356* 'rstr' are non-NULL, then we do a string based comparison.357* Otherwise, we compare 'lnum' and 'rnum'.358*/359lstr = rstr = NULL;360if (psc->sc_prop == ZPROP_USERPROP) {361nvlist_t *luser, *ruser;362nvlist_t *lval, *rval;363364luser = zfs_get_user_props(l);365ruser = zfs_get_user_props(r);366367lvalid = (nvlist_lookup_nvlist(luser,368psc->sc_user_prop, &lval) == 0);369rvalid = (nvlist_lookup_nvlist(ruser,370psc->sc_user_prop, &rval) == 0);371372if (lvalid)373verify(nvlist_lookup_string(lval,374ZPROP_VALUE, &lstr) == 0);375if (rvalid)376verify(nvlist_lookup_string(rval,377ZPROP_VALUE, &rstr) == 0);378} else if (psc->sc_prop == ZFS_PROP_NAME) {379lvalid = rvalid = B_TRUE;380381(void) strlcpy(lbuf, zfs_get_name(l), sizeof (lbuf));382(void) strlcpy(rbuf, zfs_get_name(r), sizeof (rbuf));383384lstr = lbuf;385rstr = rbuf;386} else if (zfs_prop_is_string(psc->sc_prop)) {387lvalid = (zfs_prop_get(l, psc->sc_prop, lbuf,388sizeof (lbuf), NULL, NULL, 0, B_TRUE) == 0);389rvalid = (zfs_prop_get(r, psc->sc_prop, rbuf,390sizeof (rbuf), NULL, NULL, 0, B_TRUE) == 0);391392lstr = lbuf;393rstr = rbuf;394} else {395lvalid = zfs_prop_valid_for_type(psc->sc_prop,396zfs_get_type(l), B_FALSE);397rvalid = zfs_prop_valid_for_type(psc->sc_prop,398zfs_get_type(r), B_FALSE);399400if (lvalid)401lnum = zfs_prop_get_int(l, psc->sc_prop);402if (rvalid)403rnum = zfs_prop_get_int(r, psc->sc_prop);404}405406if (!lvalid && !rvalid)407continue;408else if (!lvalid)409return (1);410else if (!rvalid)411return (-1);412413if (lstr)414ret = TREE_ISIGN(strcmp(lstr, rstr));415else if (lnum < rnum)416ret = -1;417else if (lnum > rnum)418ret = 1;419420if (ret != 0) {421if (psc->sc_reverse == B_TRUE)422ret = (ret < 0) ? 1 : -1;423return (ret);424}425}426427return (zfs_compare(larg, rarg));428}429430int431zfs_for_each(int argc, char **argv, int flags, zfs_type_t types,432zfs_sort_column_t *sortcol, zprop_list_t **proplist, int limit,433zfs_iter_f callback, void *data)434{435callback_data_t cb = {0};436int ret = 0;437zfs_node_t *node;438439cb.cb_sortcol = sortcol;440cb.cb_flags = flags;441cb.cb_proplist = proplist;442cb.cb_types = types;443cb.cb_depth_limit = limit;444/*445* If cb_proplist is provided then in the zfs_handles created we446* retain only those properties listed in cb_proplist and sortcol.447* The rest are pruned. So, the caller should make sure that no other448* properties other than those listed in cb_proplist/sortcol are449* accessed.450*451* If cb_proplist is NULL then we retain all the properties. We452* always retain the zoned property, which some other properties453* need (userquota & friends), and the createtxg property, which454* we need to sort snapshots.455*/456if (cb.cb_proplist && *cb.cb_proplist) {457zprop_list_t *p = *cb.cb_proplist;458459while (p) {460if (p->pl_prop >= ZFS_PROP_TYPE &&461p->pl_prop < ZFS_NUM_PROPS) {462cb.cb_props_table[p->pl_prop] = B_TRUE;463}464p = p->pl_next;465}466467while (sortcol) {468if (sortcol->sc_prop >= ZFS_PROP_TYPE &&469sortcol->sc_prop < ZFS_NUM_PROPS) {470cb.cb_props_table[sortcol->sc_prop] = B_TRUE;471}472sortcol = sortcol->sc_next;473}474475cb.cb_props_table[ZFS_PROP_ZONED] = B_TRUE;476cb.cb_props_table[ZFS_PROP_CREATETXG] = B_TRUE;477} else {478(void) memset(cb.cb_props_table, B_TRUE,479sizeof (cb.cb_props_table));480}481482avl_create(&cb.cb_avl, zfs_sort,483sizeof (zfs_node_t), offsetof(zfs_node_t, zn_avlnode));484485if (argc == 0) {486/*487* If given no arguments, iterate over all datasets.488*/489cb.cb_flags |= ZFS_ITER_RECURSE;490ret = zfs_iter_root(g_zfs, zfs_callback, &cb);491} else {492zfs_handle_t *zhp = NULL;493zfs_type_t argtype = types;494495/*496* If we're recursive, then we always allow filesystems as497* arguments. If we also are interested in snapshots or498* bookmarks, then we can take volumes as well.499*/500if (flags & ZFS_ITER_RECURSE) {501argtype |= ZFS_TYPE_FILESYSTEM;502if (types & (ZFS_TYPE_SNAPSHOT | ZFS_TYPE_BOOKMARK))503argtype |= ZFS_TYPE_VOLUME;504}505506for (int i = 0; i < argc; i++) {507if (flags & ZFS_ITER_ARGS_CAN_BE_PATHS) {508zhp = zfs_path_to_zhandle(g_zfs, argv[i],509argtype);510} else {511zhp = zfs_open(g_zfs, argv[i], argtype);512}513if (zhp != NULL)514ret |= zfs_callback(zhp, &cb);515else516ret = 1;517}518}519520/*521* At this point we've got our AVL tree full of zfs handles, so iterate522* over each one and execute the real user callback.523*/524for (node = avl_first(&cb.cb_avl); node != NULL;525node = AVL_NEXT(&cb.cb_avl, node))526ret |= callback(node->zn_handle, data);527528/*529* Finally, clean up the AVL tree.530*/531void *cookie = NULL;532while ((node = avl_destroy_nodes(&cb.cb_avl, &cookie)) != NULL) {533zfs_close(node->zn_handle);534free(node);535}536537avl_destroy(&cb.cb_avl);538539return (ret);540}541542543