zdb: add support for object ranges for zdb -d

Allow a range of object identifiers to dump with -d. This may
be useful when dumping a large dataset and you want to break
it up into multiple phases, or to resume where a previous scan
left off. Object type selection flags are supported to reduce
the performance overhead of verbosely dumping unwanted objects,
and to reduce the amount of post-processing work needed to
filter out unwanted objects from zdb output.

This change extends existing syntax in a backward-compatible
way. That is, the base case of a range is to specify a single
object identifier to dump. Ranges and object identifiers can
be intermixed as command line parameters.

Usage synopsis:

    Object ranges take the form <start>:<end>[:<flags>]
        start    Starting object number
        end      Ending object number, or -1 for no upper bound
        flags    Optional flags to select object types:
         A    All objects (this is the default)
         d    ZFS directories
         f    ZFS files
         m    SPA space maps
         z    ZAPs
         -    Negate effect of next flag

Examples:

 # Dump all file objects
 zdb -dd tank/fish 0👎f

 # Dump all file and directory objects
 zdb -dd tank/fish 0👎fd

 # Dump all types except file and directory objects
 zdb -dd tank/fish 0👎A-f-d

 # Dump object IDs in a specific range
 zdb -dd tank/fish 1000:2000

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Ryan Moeller <ryan@ixsystems.com>
Reviewed-by: Paul Zuchowski <pzuchowski@datto.com>
Signed-off-by: Ned Bass <bass6@llnl.gov>
Closes #9832
This commit is contained in:
Ned Bass 2020-01-24 11:00:46 -08:00 committed by Brian Behlendorf
parent 8e9e90bba3
commit a3403164d7
6 changed files with 555 additions and 50 deletions

View File

@ -110,8 +110,38 @@ uint8_t dump_opt[256];
typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
uint64_t *zopt_object = NULL;
static unsigned zopt_objects = 0;
uint64_t *zopt_metaslab = NULL;
static unsigned zopt_metaslab_args = 0;
typedef struct zopt_object_range {
uint64_t zor_obj_start;
uint64_t zor_obj_end;
uint64_t zor_flags;
} zopt_object_range_t;
zopt_object_range_t *zopt_object_ranges = NULL;
static unsigned zopt_object_args = 0;
static int flagbits[256];
#define ZOR_FLAG_PLAIN_FILE 0x0001
#define ZOR_FLAG_DIRECTORY 0x0002
#define ZOR_FLAG_SPACE_MAP 0x0004
#define ZOR_FLAG_ZAP 0x0008
#define ZOR_FLAG_ALL_TYPES -1
#define ZOR_SUPPORTED_FLAGS (ZOR_FLAG_PLAIN_FILE | \
ZOR_FLAG_DIRECTORY | \
ZOR_FLAG_SPACE_MAP | \
ZOR_FLAG_ZAP)
#define ZDB_FLAG_CHECKSUM 0x0001
#define ZDB_FLAG_DECOMPRESS 0x0002
#define ZDB_FLAG_BSWAP 0x0004
#define ZDB_FLAG_GBH 0x0008
#define ZDB_FLAG_INDIRECT 0x0010
#define ZDB_FLAG_RAW 0x0020
#define ZDB_FLAG_PRINT_BLKPTR 0x0040
#define ZDB_FLAG_VERBOSE 0x0080
uint64_t max_inflight_bytes = 256 * 1024 * 1024; /* 256MB */
static int leaked_objects = 0;
static range_tree_t *mos_refd_objs;
@ -144,9 +174,9 @@ usage(void)
"Usage:\t%s [-AbcdDFGhikLMPsvX] [-e [-V] [-p <path> ...]] "
"[-I <inflight I/Os>]\n"
"\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\n"
"\t\t[<poolname>[/<dataset | objset id>] [<object> ...]]\n"
"\t\t[<poolname>[/<dataset | objset id>] [<object | range> ...]]\n"
"\t%s [-AdiPv] [-e [-V] [-p <path> ...]] [-U <cache>]\n"
"\t\t[<poolname>[/<dataset | objset id>] [<object> ...]\n"
"\t\t[<poolname>[/<dataset | objset id>] [<object | range> ...]\n"
"\t%s [-v] <bookmark>\n"
"\t%s -C [-A] [-U <cache>]\n"
"\t%s -l [-Aqu] <device>\n"
@ -165,8 +195,20 @@ usage(void)
"separator character '/' or '@'\n");
(void) fprintf(stderr, " If dataset name is specified, only that "
"dataset is dumped\n");
(void) fprintf(stderr, " If object numbers are specified, only "
"those objects are dumped\n\n");
(void) fprintf(stderr, " If object numbers or object number "
"ranges are specified, only those\n"
" objects or ranges are dumped.\n\n");
(void) fprintf(stderr,
" Object ranges take the form <start>:<end>[:<flags>]\n"
" start Starting object number\n"
" end Ending object number, or -1 for no upper bound\n"
" flags Optional flags to select object types:\n"
" A All objects (this is the default)\n"
" d ZFS directories\n"
" f ZFS files \n"
" m SPA space maps\n"
" z ZAPs\n"
" - Negate effect of next flag\n\n");
(void) fprintf(stderr, " Options to control amount of output:\n");
(void) fprintf(stderr, " -b block statistics\n");
(void) fprintf(stderr, " -c checksum all metadata (twice for "
@ -1172,24 +1214,24 @@ dump_metaslabs(spa_t *spa)
(void) printf("\nMetaslabs:\n");
if (!dump_opt['d'] && zopt_objects > 0) {
c = zopt_object[0];
if (!dump_opt['d'] && zopt_metaslab_args > 0) {
c = zopt_metaslab[0];
if (c >= children)
(void) fatal("bad vdev id: %llu", (u_longlong_t)c);
if (zopt_objects > 1) {
if (zopt_metaslab_args > 1) {
vd = rvd->vdev_child[c];
print_vdev_metaslab_header(vd);
for (m = 1; m < zopt_objects; m++) {
if (zopt_object[m] < vd->vdev_ms_count)
for (m = 1; m < zopt_metaslab_args; m++) {
if (zopt_metaslab[m] < vd->vdev_ms_count)
dump_metaslab(
vd->vdev_ms[zopt_object[m]]);
vd->vdev_ms[zopt_metaslab[m]]);
else
(void) fprintf(stderr, "bad metaslab "
"number %llu\n",
(u_longlong_t)zopt_object[m]);
(u_longlong_t)zopt_metaslab[m]);
}
(void) printf("\n");
return;
@ -2539,9 +2581,49 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = {
dump_unknown, /* Unknown type, must be last */
};
static boolean_t
match_object_type(dmu_object_type_t obj_type, uint64_t flags)
{
boolean_t match = B_TRUE;
switch (obj_type) {
case DMU_OT_DIRECTORY_CONTENTS:
if (!(flags & ZOR_FLAG_DIRECTORY))
match = B_FALSE;
break;
case DMU_OT_PLAIN_FILE_CONTENTS:
if (!(flags & ZOR_FLAG_PLAIN_FILE))
match = B_FALSE;
break;
case DMU_OT_SPACE_MAP:
if (!(flags & ZOR_FLAG_SPACE_MAP))
match = B_FALSE;
break;
default:
if (strcmp(zdb_ot_name(obj_type), "zap") == 0) {
if (!(flags & ZOR_FLAG_ZAP))
match = B_FALSE;
break;
}
/*
* If all bits except some of the supported flags are
* set, the user combined the all-types flag (A) with
* a negated flag to exclude some types (e.g. A-f to
* show all object types except plain files).
*/
if ((flags | ZOR_SUPPORTED_FLAGS) != ZOR_FLAG_ALL_TYPES)
match = B_FALSE;
break;
}
return (match);
}
static void
dump_object(objset_t *os, uint64_t object, int verbosity,
boolean_t *print_header, uint64_t *dnode_slots_used)
boolean_t *print_header, uint64_t *dnode_slots_used, uint64_t flags)
{
dmu_buf_t *db = NULL;
dmu_object_info_t doi;
@ -2598,6 +2680,13 @@ dump_object(objset_t *os, uint64_t object, int verbosity,
}
}
/*
* Default to showing all object types if no flags were specified.
*/
if (flags != 0 && flags != ZOR_FLAG_ALL_TYPES &&
!match_object_type(doi.doi_type, flags))
goto out;
if (dnode_slots_used)
*dnode_slots_used = doi.doi_dnodesize / DNODE_MIN_SIZE;
@ -2701,6 +2790,7 @@ dump_object(objset_t *os, uint64_t object, int verbosity,
}
}
out:
if (db != NULL)
dmu_buf_rele(db, FTAG);
if (dnode_held)
@ -2741,6 +2831,110 @@ count_ds_mos_objects(dsl_dataset_t *ds)
static const char *objset_types[DMU_OST_NUMTYPES] = {
"NONE", "META", "ZPL", "ZVOL", "OTHER", "ANY" };
/*
* Parse a string denoting a range of object IDs of the form
* <start>[:<end>[:flags]], and store the results in zor.
* Return 0 on success. On error, return 1 and update the msg
* pointer to point to a descriptive error message.
*/
static int
parse_object_range(char *range, zopt_object_range_t *zor, char **msg)
{
uint64_t flags = 0;
char *p, *s, *dup, *flagstr;
size_t len;
int i;
int rc = 0;
if (strchr(range, ':') == NULL) {
zor->zor_obj_start = strtoull(range, &p, 0);
if (*p != '\0') {
*msg = "Invalid characters in object ID";
rc = 1;
}
zor->zor_obj_end = zor->zor_obj_start;
return (rc);
}
if (strchr(range, ':') == range) {
*msg = "Invalid leading colon";
rc = 1;
return (rc);
}
len = strlen(range);
if (range[len - 1] == ':') {
*msg = "Invalid trailing colon";
rc = 1;
return (rc);
}
dup = strdup(range);
s = strtok(dup, ":");
zor->zor_obj_start = strtoull(s, &p, 0);
if (*p != '\0') {
*msg = "Invalid characters in start object ID";
rc = 1;
goto out;
}
s = strtok(NULL, ":");
zor->zor_obj_end = strtoull(s, &p, 0);
if (*p != '\0') {
*msg = "Invalid characters in end object ID";
rc = 1;
goto out;
}
if (zor->zor_obj_start > zor->zor_obj_end) {
*msg = "Start object ID may not exceed end object ID";
rc = 1;
goto out;
}
s = strtok(NULL, ":");
if (s == NULL) {
zor->zor_flags = ZOR_FLAG_ALL_TYPES;
goto out;
} else if (strtok(NULL, ":") != NULL) {
*msg = "Invalid colon-delimited field after flags";
rc = 1;
goto out;
}
flagstr = s;
for (i = 0; flagstr[i]; i++) {
int bit;
boolean_t negation = (flagstr[i] == '-');
if (negation) {
i++;
if (flagstr[i] == '\0') {
*msg = "Invalid trailing negation operator";
rc = 1;
goto out;
}
}
bit = flagbits[(uchar_t)flagstr[i]];
if (bit == 0) {
*msg = "Invalid flag";
rc = 1;
goto out;
}
if (negation)
flags &= ~bit;
else
flags |= bit;
}
zor->zor_flags = flags;
out:
free(dup);
return (rc);
}
static void
dump_objset(objset_t *os)
{
@ -2758,6 +2952,9 @@ dump_objset(objset_t *os)
uint64_t total_slots_used = 0;
uint64_t max_slot_used = 0;
uint64_t dnode_slots;
uint64_t obj_start;
uint64_t obj_end;
uint64_t flags;
/* make sure nicenum has enough space */
CTASSERT(sizeof (numbuf) >= NN_NUMBUF_SZ);
@ -2801,11 +2998,26 @@ dump_objset(objset_t *os)
numbuf, (u_longlong_t)usedobjs, blkbuf,
(dds.dds_inconsistent) ? " (inconsistent)" : "");
if (zopt_objects != 0) {
for (i = 0; i < zopt_objects; i++) {
dump_object(os, zopt_object[i], verbosity,
&print_header, NULL);
for (i = 0; i < zopt_object_args; i++) {
obj_start = zopt_object_ranges[i].zor_obj_start;
obj_end = zopt_object_ranges[i].zor_obj_end;
flags = zopt_object_ranges[i].zor_flags;
object = obj_start;
if (object == 0 || obj_start == obj_end)
dump_object(os, object, verbosity, &print_header, NULL,
flags);
else
object--;
while ((dmu_object_next(os, &object, B_FALSE, 0) == 0) &&
object <= obj_end) {
dump_object(os, object, verbosity, &print_header, NULL,
flags);
}
}
if (zopt_object_args > 0) {
(void) printf("\n");
return;
}
@ -2839,24 +3051,25 @@ dump_objset(objset_t *os)
if (BP_IS_HOLE(os->os_rootbp))
return;
dump_object(os, 0, verbosity, &print_header, NULL);
dump_object(os, 0, verbosity, &print_header, NULL, 0);
object_count = 0;
if (DMU_USERUSED_DNODE(os) != NULL &&
DMU_USERUSED_DNODE(os)->dn_type != 0) {
dump_object(os, DMU_USERUSED_OBJECT, verbosity, &print_header,
NULL);
NULL, 0);
dump_object(os, DMU_GROUPUSED_OBJECT, verbosity, &print_header,
NULL);
NULL, 0);
}
if (DMU_PROJECTUSED_DNODE(os) != NULL &&
DMU_PROJECTUSED_DNODE(os)->dn_type != 0)
dump_object(os, DMU_PROJECTUSED_OBJECT, verbosity,
&print_header, NULL);
&print_header, NULL, 0);
object = 0;
while ((error = dmu_object_next(os, &object, B_FALSE, 0)) == 0) {
dump_object(os, object, verbosity, &print_header, &dnode_slots);
dump_object(os, object, verbosity, &print_header, &dnode_slots,
0);
object_count++;
total_slots_used += dnode_slots;
max_slot_used = object + dnode_slots - 1;
@ -3360,7 +3573,7 @@ dump_path_impl(objset_t *os, uint64_t obj, char *name)
return (dump_path_impl(os, child_obj, s + 1));
/*FALLTHROUGH*/
case DMU_OT_PLAIN_FILE_CONTENTS:
dump_object(os, child_obj, dump_opt['v'], &header, NULL);
dump_object(os, child_obj, dump_opt['v'], &header, NULL, 0);
return (0);
default:
(void) fprintf(stderr, "object %llu has non-file/directory "
@ -6197,17 +6410,6 @@ dump_zpool(spa_t *spa)
}
}
#define ZDB_FLAG_CHECKSUM 0x0001
#define ZDB_FLAG_DECOMPRESS 0x0002
#define ZDB_FLAG_BSWAP 0x0004
#define ZDB_FLAG_GBH 0x0008
#define ZDB_FLAG_INDIRECT 0x0010
#define ZDB_FLAG_RAW 0x0020
#define ZDB_FLAG_PRINT_BLKPTR 0x0040
#define ZDB_FLAG_VERBOSE 0x0080
static int flagbits[256];
static void
zdb_print_blkptr(blkptr_t *bp, int flags)
{
@ -7130,20 +7332,41 @@ main(int argc, char **argv)
argv++;
argc--;
if (!dump_opt['R']) {
if (argc > 0) {
zopt_objects = argc;
zopt_object = calloc(zopt_objects, sizeof (uint64_t));
for (unsigned i = 0; i < zopt_objects; i++) {
flagbits['d'] = ZOR_FLAG_DIRECTORY;
flagbits['f'] = ZOR_FLAG_PLAIN_FILE;
flagbits['m'] = ZOR_FLAG_SPACE_MAP;
flagbits['z'] = ZOR_FLAG_ZAP;
flagbits['A'] = ZOR_FLAG_ALL_TYPES;
if (argc > 0 && dump_opt['d']) {
zopt_object_args = argc;
zopt_object_ranges = calloc(zopt_object_args,
sizeof (zopt_object_range_t));
for (unsigned i = 0; i < zopt_object_args; i++) {
int err;
char *msg = NULL;
err = parse_object_range(argv[i],
&zopt_object_ranges[i], &msg);
if (err != 0)
fatal("Bad object or range: '%s': %s\n",
argv[i], msg ? msg : "");
}
} else if (argc > 0 && dump_opt['m']) {
zopt_metaslab_args = argc;
zopt_metaslab = calloc(zopt_metaslab_args,
sizeof (uint64_t));
for (unsigned i = 0; i < zopt_metaslab_args; i++) {
errno = 0;
zopt_object[i] = strtoull(argv[i], NULL, 0);
if (zopt_object[i] == 0 && errno != 0)
fatal("bad number %s: %s",
argv[i], strerror(errno));
zopt_metaslab[i] = strtoull(argv[i], NULL, 0);
if (zopt_metaslab[i] == 0 && errno != 0)
fatal("bad number %s: %s", argv[i],
strerror(errno));
}
}
if (os != NULL) {
dump_objset(os);
} else if (zopt_objects > 0 && !dump_opt['m']) {
} else if (zopt_object_args > 0 && !dump_opt['m']) {
dump_objset(spa->spa_meta_objset);
} else {
dump_zpool(spa);

View File

@ -31,12 +31,12 @@
.Op Fl U Ar cache
.Op Fl x Ar dumpdir
.Op Ar poolname[/dataset | objset ID]
.Op Ar object ...
.Op Ar object | range ...
.Nm
.Op Fl AdiPv
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
.Op Fl U Ar cache
.Ar poolname[/dataset | objset ID] Op Ar object ...
.Ar poolname[/dataset | objset ID] Op Ar object | range ...
.Nm
.Fl C
.Op Fl A
@ -135,8 +135,45 @@ size, and object count.
.Pp
If specified multiple times provides greater and greater verbosity.
.Pp
If object IDs are specified, display information about those specific objects
only.
If object IDs or object ID ranges are specified, display information about
those specific objects or ranges only.
.Pp
An object ID range is specified in terms of a colon-separated tuple of
the form
.Ao start Ac Ns : Ns Ao end Ac Ns Op Ns : Ns Ao flags Ac Ns .
The fields
.Ar start
and
.Ar end
are integer object identfiers that denote the upper and lower bounds
of the range. An
.Ar end
value of -1 specifies a range with no upper bound. The
.Ar flags
field optionally specifies a set of flags, described below, that control
which object types are dumped. By default, all object types are dumped. A minus
sign
.Pq -
negates the effect of the flag that follows it and has no effect unless
preceded by the
.Ar A
flag. For example, the range 0:-1:A-d will dump all object types except
for directories.
.Pp
.Bl -tag -compact
.It Sy A
Dump all objects (this is the default)
.It Sy d
Dump ZFS directory objects
.It Sy f
Dump ZFS plain file objects
.It Sy m
Dump SPA space map objects
.It Sy z
Dump ZAP objects
.It Sy -
Negate the effect of next flag
.El
.It Fl D
Display deduplication statistics, including the deduplication ratio
.Pq Sy dedup ,

View File

@ -101,7 +101,7 @@ tags = ['functional', 'clean_mirror']
[tests/functional/cli_root/zdb]
tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos',
'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress',
'zdb_objset_id']
'zdb_object_range_neg', 'zdb_object_range_pos', 'zdb_objset_id']
pre =
post =
tags = ['functional', 'cli_root', 'zdb']

View File

@ -8,4 +8,6 @@ dist_pkgdata_SCRIPTS = \
zdb_006_pos.ksh \
zdb_checksum.ksh \
zdb_decompress.ksh \
zdb_objset_id.ksh
zdb_objset_id.ksh \
zdb_object_range_neg.ksh \
zdb_object_range_pos.ksh

View File

@ -0,0 +1,72 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#
# Copyright (c) 2020 Lawrence Livermore National Security, LLC.
. $STF_SUITE/include/libtest.shlib
#
# Description:
# A badly formed object range parameter passed to zdb -dd should
# return an error.
#
# Strategy:
# 1. Create a pool
# 2. Run zdb -dd with assorted invalid object range arguments and
# confirm it fails as expected
# 3. Run zdb -dd with an invalid object identifier and
# confirm it fails as expected
function cleanup
{
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
}
log_assert "Execute zdb using invalid object range parameters."
log_onexit cleanup
verify_runnable "both"
verify_disk_count "$DISKS" 2
default_mirror_setup_noexit $DISKS
log_must zpool sync
set -A bad_flags a b c e g h i j k l n o p q r s t u v w x y \
B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
0 1 2 3 4 5 6 7 8 9 _ - + % . , :
typeset -i i=0
while [[ $i -lt ${#bad_flags[*]} ]]; do
log_mustnot zdb -dd $TESTPOOL 0:1:${bad_flags[i]}
log_mustnot zdb -dd $TESTPOOL 0:1:A-${bad_flags[i]}
((i = i + 1))
done
set -A bad_ranges ":" "::" ":::" ":0" "0:" "0:1:" "0:1::" "0::f" "0a:1" \
"a0:1" "a:1" "0:a" "0:1a" "0:a1" "a:b0" "a:0b" "0:1:A-" "1:0" \
"0:1:f:f" "0:1:f:"
i=0
while [[ $i -lt ${#bad_ranges[*]} ]]; do
log_mustnot zdb -dd $TESTPOOL ${bad_ranges[i]}
((i = i + 1))
done
# Specifying a non-existent object identifier returns an error
obj_id_highest=$(zdb -P -dd $TESTPOOL/$TESTFS 2>/dev/null |
egrep "^ +-?([0-9]+ +){7}" | sort -n | tail -n 1 | awk '{print $1}')
obj_id_invalid=$(( $obj_id_highest + 1 ))
log_mustnot zdb -dd $TESTPOOL/$TESTFS $obj_id_invalid
log_pass "Badly formed zdb object range parameters fail as expected."

View File

@ -0,0 +1,171 @@
#!/bin/ksh -p
#
# CDDL HEADER START
#
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# http://www.illumos.org/license/CDDL.
#
# CDDL HEADER END
#
# Copyright (c) 2020 Lawrence Livermore National Security, LLC.
. $STF_SUITE/include/libtest.shlib
#
# Description:
# Object range parameters passed to zdb -dd work correctly.
#
# Strategy:
# 1. Create a pool
# 2. Create some files
# 3. Run zdb -dd with assorted object range arguments and verify output
function cleanup
{
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
}
#
# Print objects in @dataset with identifiers greater than or equal to
# @begin and less than or equal to @end, without using object range
# parameters.
#
function get_object_list_range
{
dataset=$1
begin=$2
end=$3
get_object_list $dataset |
while read line; do
obj=$(echo $line | awk '{print $1}')
if [[ $obj -ge $begin && $obj -le $end ]] ; then
echo "$line"
elif [[ $obj -gt $end ]] ; then
break
fi
done
}
#
# Print just the list of objects from 'zdb -dd' with leading whitespace
# trimmed, discarding other zdb output, sorted by object identifier.
# Caller must pass in the dataset argument at minimum.
#
function get_object_list
{
zdb -P -dd $@ 2>/dev/null |
egrep "^ +-?([0-9]+ +){7}" |
sed 's/^[[:space:]]*//' |
sort -n
}
log_assert "Verify zdb -dd object range arguments work correctly."
log_onexit cleanup
verify_runnable "both"
verify_disk_count "$DISKS" 2
default_mirror_setup_noexit $DISKS
for x in $(seq 0 7); do
touch $TESTDIR/file$x
mkdir $TESTDIR/dir$x
done
log_must zpool sync
# Get list of all objects, but filter out user/group objects which don't
# appear when using object or object range arguments
all_objects=$(get_object_list $TESTPOOL/$TESTFS | grep -v 'used$')
# Range 0:-1 gets all objects
expected=$all_objects
actual=$(get_object_list $TESTPOOL/$TESTFS 0:-1)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:A gets all objects
expected=$all_objects
actual=$(get_object_list $TESTPOOL/$TESTFS 0:-1:A)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:f must output all file objects
expected=$(grep "ZFS plain file" <<< $all_objects)
actual=$(get_object_list $TESTPOOL/$TESTFS 0:-1:f)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:d must output all directory objects
expected=$(grep "ZFS directory" <<< $all_objects)
actual=$(get_object_list $TESTPOOL/$TESTFS 0:-1:d)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:df must output all directory and file objects
expected=$(grep -e "ZFS directory" -e "ZFS plain file" <<< $all_objects)
actual=$(get_object_list $TESTPOOL/$TESTFS 0:-1:df)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:A-f-d must output all non-files and non-directories
expected=$(grep -v -e "ZFS plain file" -e "ZFS directory" <<< $all_objects)
actual=$(get_object_list $TESTPOOL/$TESTFS 0:-1:A-f-d)
log_must test "\n$actual\n" == "\n$expected\n"
# Specifying multiple ranges works
set -A obj_ids $(ls -i $TESTDIR | awk '{print $1}' | sort -n)
start1=${obj_ids[0]}
end1=${obj_ids[5]}
start2=${obj_ids[8]}
end2=${obj_ids[13]}
expected=$(get_object_list_range $TESTPOOL/$TESTFS $start1 $end1;
get_object_list_range $TESTPOOL/$TESTFS $start2 $end2)
actual=$(get_object_list $TESTPOOL/$TESTFS $start1:$end1 $start2:$end2)
log_must test "\n$actual\n" == "\n$expected\n"
# Combining ranges with individual object IDs works
expected=$(get_object_list_range $TESTPOOL/$TESTFS $start1 $end1;
get_object_list $TESTPOOL/$TESTFS $start2 $end2)
actual=$(get_object_list $TESTPOOL/$TESTFS $start1:$end1 $start2 $end2)
log_must test "\n$actual\n" == "\n$expected\n"
# Hex conversion must work for ranges and individual object identifiers
# (this test uses expected result from previous test).
start1_hex=$(printf "0x%x" $start1)
end1_hex=$(printf "0x%x" $end1)
start2_hex=$(printf "0x%x" $start2)
end2_hex=$(printf "0x%x" $end2)
actual=$(get_object_list $TESTPOOL/$TESTFS $start1_hex:$end1_hex \
$start2_hex $end2_hex)
log_must test "\n$actual\n" == "\n$expected\n"
# Specifying individual object IDs works
objects="$start1 $end1 $start2 $end2"
expected="$objects"
actual=$(get_object_list $TESTPOOL/$TESTFS $objects | awk '{print $1}' | xargs)
log_must test "$actual" == "$expected"
# Get all objects in the meta-objset to test m (spacemap) and z (zap) flags
all_mos_objects=$(get_object_list $TESTPOOL 0:-1)
# Range 0:-1:m must output all space map objects
expected=$(grep "SPA space map" <<< $all_mos_objects)
actual=$(get_object_list $TESTPOOL 0:-1:m)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:z must output all zap objects
expected=$(grep "zap" <<< $all_mos_objects)
actual=$(get_object_list $TESTPOOL 0:-1:z)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:A-m-z must output all non-space maps and non-zaps
expected=$(grep -v -e "zap" -e "SPA space map" <<< $all_mos_objects)
actual=$(get_object_list $TESTPOOL 0:-1:A-m-z)
log_must test "\n$actual\n" == "\n$expected\n"
# Range 0:-1:mz must output all space maps and zaps
expected=$(grep -e "SPA space map" -e "zap" <<< $all_mos_objects)
actual=$(get_object_list $TESTPOOL 0:-1:mz)
log_must test "\n$actual\n" == "\n$expected\n"
log_pass "zdb -dd object range arguments work correctly"