mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2025-04-06 17:49:11 +03:00
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:
parent
8e9e90bba3
commit
a3403164d7
311
cmd/zdb/zdb.c
311
cmd/zdb/zdb.c
@ -110,8 +110,38 @@ uint8_t dump_opt[256];
|
|||||||
|
|
||||||
typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
|
typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
|
||||||
|
|
||||||
uint64_t *zopt_object = NULL;
|
uint64_t *zopt_metaslab = NULL;
|
||||||
static unsigned zopt_objects = 0;
|
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 */
|
uint64_t max_inflight_bytes = 256 * 1024 * 1024; /* 256MB */
|
||||||
static int leaked_objects = 0;
|
static int leaked_objects = 0;
|
||||||
static range_tree_t *mos_refd_objs;
|
static range_tree_t *mos_refd_objs;
|
||||||
@ -144,9 +174,9 @@ usage(void)
|
|||||||
"Usage:\t%s [-AbcdDFGhikLMPsvX] [-e [-V] [-p <path> ...]] "
|
"Usage:\t%s [-AbcdDFGhikLMPsvX] [-e [-V] [-p <path> ...]] "
|
||||||
"[-I <inflight I/Os>]\n"
|
"[-I <inflight I/Os>]\n"
|
||||||
"\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\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%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 [-v] <bookmark>\n"
|
||||||
"\t%s -C [-A] [-U <cache>]\n"
|
"\t%s -C [-A] [-U <cache>]\n"
|
||||||
"\t%s -l [-Aqu] <device>\n"
|
"\t%s -l [-Aqu] <device>\n"
|
||||||
@ -165,8 +195,20 @@ usage(void)
|
|||||||
"separator character '/' or '@'\n");
|
"separator character '/' or '@'\n");
|
||||||
(void) fprintf(stderr, " If dataset name is specified, only that "
|
(void) fprintf(stderr, " If dataset name is specified, only that "
|
||||||
"dataset is dumped\n");
|
"dataset is dumped\n");
|
||||||
(void) fprintf(stderr, " If object numbers are specified, only "
|
(void) fprintf(stderr, " If object numbers or object number "
|
||||||
"those objects are dumped\n\n");
|
"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, " Options to control amount of output:\n");
|
||||||
(void) fprintf(stderr, " -b block statistics\n");
|
(void) fprintf(stderr, " -b block statistics\n");
|
||||||
(void) fprintf(stderr, " -c checksum all metadata (twice for "
|
(void) fprintf(stderr, " -c checksum all metadata (twice for "
|
||||||
@ -1172,24 +1214,24 @@ dump_metaslabs(spa_t *spa)
|
|||||||
|
|
||||||
(void) printf("\nMetaslabs:\n");
|
(void) printf("\nMetaslabs:\n");
|
||||||
|
|
||||||
if (!dump_opt['d'] && zopt_objects > 0) {
|
if (!dump_opt['d'] && zopt_metaslab_args > 0) {
|
||||||
c = zopt_object[0];
|
c = zopt_metaslab[0];
|
||||||
|
|
||||||
if (c >= children)
|
if (c >= children)
|
||||||
(void) fatal("bad vdev id: %llu", (u_longlong_t)c);
|
(void) fatal("bad vdev id: %llu", (u_longlong_t)c);
|
||||||
|
|
||||||
if (zopt_objects > 1) {
|
if (zopt_metaslab_args > 1) {
|
||||||
vd = rvd->vdev_child[c];
|
vd = rvd->vdev_child[c];
|
||||||
print_vdev_metaslab_header(vd);
|
print_vdev_metaslab_header(vd);
|
||||||
|
|
||||||
for (m = 1; m < zopt_objects; m++) {
|
for (m = 1; m < zopt_metaslab_args; m++) {
|
||||||
if (zopt_object[m] < vd->vdev_ms_count)
|
if (zopt_metaslab[m] < vd->vdev_ms_count)
|
||||||
dump_metaslab(
|
dump_metaslab(
|
||||||
vd->vdev_ms[zopt_object[m]]);
|
vd->vdev_ms[zopt_metaslab[m]]);
|
||||||
else
|
else
|
||||||
(void) fprintf(stderr, "bad metaslab "
|
(void) fprintf(stderr, "bad metaslab "
|
||||||
"number %llu\n",
|
"number %llu\n",
|
||||||
(u_longlong_t)zopt_object[m]);
|
(u_longlong_t)zopt_metaslab[m]);
|
||||||
}
|
}
|
||||||
(void) printf("\n");
|
(void) printf("\n");
|
||||||
return;
|
return;
|
||||||
@ -2539,9 +2581,49 @@ static object_viewer_t *object_viewer[DMU_OT_NUMTYPES + 1] = {
|
|||||||
dump_unknown, /* Unknown type, must be last */
|
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
|
static void
|
||||||
dump_object(objset_t *os, uint64_t object, int verbosity,
|
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_buf_t *db = NULL;
|
||||||
dmu_object_info_t doi;
|
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)
|
if (dnode_slots_used)
|
||||||
*dnode_slots_used = doi.doi_dnodesize / DNODE_MIN_SIZE;
|
*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)
|
if (db != NULL)
|
||||||
dmu_buf_rele(db, FTAG);
|
dmu_buf_rele(db, FTAG);
|
||||||
if (dnode_held)
|
if (dnode_held)
|
||||||
@ -2741,6 +2831,110 @@ count_ds_mos_objects(dsl_dataset_t *ds)
|
|||||||
static const char *objset_types[DMU_OST_NUMTYPES] = {
|
static const char *objset_types[DMU_OST_NUMTYPES] = {
|
||||||
"NONE", "META", "ZPL", "ZVOL", "OTHER", "ANY" };
|
"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
|
static void
|
||||||
dump_objset(objset_t *os)
|
dump_objset(objset_t *os)
|
||||||
{
|
{
|
||||||
@ -2758,6 +2952,9 @@ dump_objset(objset_t *os)
|
|||||||
uint64_t total_slots_used = 0;
|
uint64_t total_slots_used = 0;
|
||||||
uint64_t max_slot_used = 0;
|
uint64_t max_slot_used = 0;
|
||||||
uint64_t dnode_slots;
|
uint64_t dnode_slots;
|
||||||
|
uint64_t obj_start;
|
||||||
|
uint64_t obj_end;
|
||||||
|
uint64_t flags;
|
||||||
|
|
||||||
/* make sure nicenum has enough space */
|
/* make sure nicenum has enough space */
|
||||||
CTASSERT(sizeof (numbuf) >= NN_NUMBUF_SZ);
|
CTASSERT(sizeof (numbuf) >= NN_NUMBUF_SZ);
|
||||||
@ -2801,11 +2998,26 @@ dump_objset(objset_t *os)
|
|||||||
numbuf, (u_longlong_t)usedobjs, blkbuf,
|
numbuf, (u_longlong_t)usedobjs, blkbuf,
|
||||||
(dds.dds_inconsistent) ? " (inconsistent)" : "");
|
(dds.dds_inconsistent) ? " (inconsistent)" : "");
|
||||||
|
|
||||||
if (zopt_objects != 0) {
|
for (i = 0; i < zopt_object_args; i++) {
|
||||||
for (i = 0; i < zopt_objects; i++) {
|
obj_start = zopt_object_ranges[i].zor_obj_start;
|
||||||
dump_object(os, zopt_object[i], verbosity,
|
obj_end = zopt_object_ranges[i].zor_obj_end;
|
||||||
&print_header, NULL);
|
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");
|
(void) printf("\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2839,24 +3051,25 @@ dump_objset(objset_t *os)
|
|||||||
if (BP_IS_HOLE(os->os_rootbp))
|
if (BP_IS_HOLE(os->os_rootbp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
dump_object(os, 0, verbosity, &print_header, NULL);
|
dump_object(os, 0, verbosity, &print_header, NULL, 0);
|
||||||
object_count = 0;
|
object_count = 0;
|
||||||
if (DMU_USERUSED_DNODE(os) != NULL &&
|
if (DMU_USERUSED_DNODE(os) != NULL &&
|
||||||
DMU_USERUSED_DNODE(os)->dn_type != 0) {
|
DMU_USERUSED_DNODE(os)->dn_type != 0) {
|
||||||
dump_object(os, DMU_USERUSED_OBJECT, verbosity, &print_header,
|
dump_object(os, DMU_USERUSED_OBJECT, verbosity, &print_header,
|
||||||
NULL);
|
NULL, 0);
|
||||||
dump_object(os, DMU_GROUPUSED_OBJECT, verbosity, &print_header,
|
dump_object(os, DMU_GROUPUSED_OBJECT, verbosity, &print_header,
|
||||||
NULL);
|
NULL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DMU_PROJECTUSED_DNODE(os) != NULL &&
|
if (DMU_PROJECTUSED_DNODE(os) != NULL &&
|
||||||
DMU_PROJECTUSED_DNODE(os)->dn_type != 0)
|
DMU_PROJECTUSED_DNODE(os)->dn_type != 0)
|
||||||
dump_object(os, DMU_PROJECTUSED_OBJECT, verbosity,
|
dump_object(os, DMU_PROJECTUSED_OBJECT, verbosity,
|
||||||
&print_header, NULL);
|
&print_header, NULL, 0);
|
||||||
|
|
||||||
object = 0;
|
object = 0;
|
||||||
while ((error = dmu_object_next(os, &object, B_FALSE, 0)) == 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++;
|
object_count++;
|
||||||
total_slots_used += dnode_slots;
|
total_slots_used += dnode_slots;
|
||||||
max_slot_used = object + dnode_slots - 1;
|
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));
|
return (dump_path_impl(os, child_obj, s + 1));
|
||||||
/*FALLTHROUGH*/
|
/*FALLTHROUGH*/
|
||||||
case DMU_OT_PLAIN_FILE_CONTENTS:
|
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);
|
return (0);
|
||||||
default:
|
default:
|
||||||
(void) fprintf(stderr, "object %llu has non-file/directory "
|
(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
|
static void
|
||||||
zdb_print_blkptr(blkptr_t *bp, int flags)
|
zdb_print_blkptr(blkptr_t *bp, int flags)
|
||||||
{
|
{
|
||||||
@ -7130,20 +7332,41 @@ main(int argc, char **argv)
|
|||||||
argv++;
|
argv++;
|
||||||
argc--;
|
argc--;
|
||||||
if (!dump_opt['R']) {
|
if (!dump_opt['R']) {
|
||||||
if (argc > 0) {
|
flagbits['d'] = ZOR_FLAG_DIRECTORY;
|
||||||
zopt_objects = argc;
|
flagbits['f'] = ZOR_FLAG_PLAIN_FILE;
|
||||||
zopt_object = calloc(zopt_objects, sizeof (uint64_t));
|
flagbits['m'] = ZOR_FLAG_SPACE_MAP;
|
||||||
for (unsigned i = 0; i < zopt_objects; i++) {
|
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;
|
errno = 0;
|
||||||
zopt_object[i] = strtoull(argv[i], NULL, 0);
|
zopt_metaslab[i] = strtoull(argv[i], NULL, 0);
|
||||||
if (zopt_object[i] == 0 && errno != 0)
|
if (zopt_metaslab[i] == 0 && errno != 0)
|
||||||
fatal("bad number %s: %s",
|
fatal("bad number %s: %s", argv[i],
|
||||||
argv[i], strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (os != NULL) {
|
if (os != NULL) {
|
||||||
dump_objset(os);
|
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);
|
dump_objset(spa->spa_meta_objset);
|
||||||
} else {
|
} else {
|
||||||
dump_zpool(spa);
|
dump_zpool(spa);
|
||||||
|
@ -31,12 +31,12 @@
|
|||||||
.Op Fl U Ar cache
|
.Op Fl U Ar cache
|
||||||
.Op Fl x Ar dumpdir
|
.Op Fl x Ar dumpdir
|
||||||
.Op Ar poolname[/dataset | objset ID]
|
.Op Ar poolname[/dataset | objset ID]
|
||||||
.Op Ar object ...
|
.Op Ar object | range ...
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl AdiPv
|
.Op Fl AdiPv
|
||||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||||
.Op Fl U Ar cache
|
.Op Fl U Ar cache
|
||||||
.Ar poolname[/dataset | objset ID] Op Ar object ...
|
.Ar poolname[/dataset | objset ID] Op Ar object | range ...
|
||||||
.Nm
|
.Nm
|
||||||
.Fl C
|
.Fl C
|
||||||
.Op Fl A
|
.Op Fl A
|
||||||
@ -135,8 +135,45 @@ size, and object count.
|
|||||||
.Pp
|
.Pp
|
||||||
If specified multiple times provides greater and greater verbosity.
|
If specified multiple times provides greater and greater verbosity.
|
||||||
.Pp
|
.Pp
|
||||||
If object IDs are specified, display information about those specific objects
|
If object IDs or object ID ranges are specified, display information about
|
||||||
only.
|
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
|
.It Fl D
|
||||||
Display deduplication statistics, including the deduplication ratio
|
Display deduplication statistics, including the deduplication ratio
|
||||||
.Pq Sy dedup ,
|
.Pq Sy dedup ,
|
||||||
|
@ -101,7 +101,7 @@ tags = ['functional', 'clean_mirror']
|
|||||||
[tests/functional/cli_root/zdb]
|
[tests/functional/cli_root/zdb]
|
||||||
tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos',
|
tests = ['zdb_001_neg', 'zdb_002_pos', 'zdb_003_pos', 'zdb_004_pos',
|
||||||
'zdb_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress',
|
'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 =
|
pre =
|
||||||
post =
|
post =
|
||||||
tags = ['functional', 'cli_root', 'zdb']
|
tags = ['functional', 'cli_root', 'zdb']
|
||||||
|
@ -8,4 +8,6 @@ dist_pkgdata_SCRIPTS = \
|
|||||||
zdb_006_pos.ksh \
|
zdb_006_pos.ksh \
|
||||||
zdb_checksum.ksh \
|
zdb_checksum.ksh \
|
||||||
zdb_decompress.ksh \
|
zdb_decompress.ksh \
|
||||||
zdb_objset_id.ksh
|
zdb_objset_id.ksh \
|
||||||
|
zdb_object_range_neg.ksh \
|
||||||
|
zdb_object_range_pos.ksh
|
||||||
|
72
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_object_range_neg.ksh
Executable file
72
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_object_range_neg.ksh
Executable 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."
|
171
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_object_range_pos.ksh
Executable file
171
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_object_range_pos.ksh
Executable 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"
|
Loading…
Reference in New Issue
Block a user