Fix problem with zdb -d

zdb -d <pool>/<objset ID> does not work when
other command line arguments are included i.e.
zdb -U <cachefile> -d <pool>/<objset ID>
This change fixes the command line parsing
to handle this situation.  Also fix issue
where zdb -r <dataset> <file> does not handle
the root <dataset> of the pool. Introduce -N
option to force <objset ID> to be interpreted
as a numeric objsetID.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Rich Ercolani <rincebrain@gmail.com>
Reviewed-by: Tony Nguyen <tony.nguyen@delphix.com>
Signed-off-by: Paul Zuchowski <pzuchowski@datto.com>
Closes #12845
Closes #12944
This commit is contained in:
Paul Zuchowski 2022-01-20 12:28:55 -05:00 committed by GitHub
parent e1c720de7d
commit 5a4d282f55
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 146 additions and 35 deletions

View File

@ -8389,6 +8389,23 @@ zdb_embedded_block(char *thing)
free(buf); free(buf);
} }
/* check for valid hex or decimal numeric string */
static boolean_t
zdb_numeric(char *str)
{
int i = 0;
if (strlen(str) == 0)
return (B_FALSE);
if (strncmp(str, "0x", 2) == 0 || strncmp(str, "0X", 2) == 0)
i = 2;
for (; i < strlen(str); i++) {
if (!isxdigit(str[i]))
return (B_FALSE);
}
return (B_TRUE);
}
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -8452,6 +8469,7 @@ main(int argc, char **argv)
{"disable-leak-tracking", no_argument, NULL, 'L'}, {"disable-leak-tracking", no_argument, NULL, 'L'},
{"metaslabs", no_argument, NULL, 'm'}, {"metaslabs", no_argument, NULL, 'm'},
{"metaslab-groups", no_argument, NULL, 'M'}, {"metaslab-groups", no_argument, NULL, 'M'},
{"numeric", no_argument, NULL, 'N'},
{"option", required_argument, NULL, 'o'}, {"option", required_argument, NULL, 'o'},
{"object-lookups", no_argument, NULL, 'O'}, {"object-lookups", no_argument, NULL, 'O'},
{"path", required_argument, NULL, 'p'}, {"path", required_argument, NULL, 'p'},
@ -8475,7 +8493,7 @@ main(int argc, char **argv)
}; };
while ((c = getopt_long(argc, argv, while ((c = getopt_long(argc, argv,
"AbcCdDeEFGhiI:klLmMo:Op:PqrRsSt:uU:vVx:XYyZ", "AbcCdDeEFGhiI:klLmMNo:Op:PqrRsSt:uU:vVx:XYyZ",
long_options, NULL)) != -1) { long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'b': case 'b':
@ -8490,6 +8508,7 @@ main(int argc, char **argv)
case 'l': case 'l':
case 'm': case 'm':
case 'M': case 'M':
case 'N':
case 'O': case 'O':
case 'r': case 'r':
case 'R': case 'R':
@ -8581,32 +8600,6 @@ main(int argc, char **argv)
(void) fprintf(stderr, "-p option requires use of -e\n"); (void) fprintf(stderr, "-p option requires use of -e\n");
usage(); usage();
} }
if (dump_opt['d'] || dump_opt['r']) {
/* <pool>[/<dataset | objset id> is accepted */
if (argv[2] && (objset_str = strchr(argv[2], '/')) != NULL &&
objset_str++ != NULL) {
char *endptr;
errno = 0;
objset_id = strtoull(objset_str, &endptr, 0);
/* dataset 0 is the same as opening the pool */
if (errno == 0 && endptr != objset_str &&
objset_id != 0) {
target_is_spa = B_FALSE;
dataset_lookup = B_TRUE;
} else if (objset_id != 0) {
printf("failed to open objset %s "
"%llu %s", objset_str,
(u_longlong_t)objset_id,
strerror(errno));
exit(1);
}
/* normal dataset name not an objset ID */
if (endptr == objset_str) {
objset_id = -1;
}
}
}
#if defined(_LP64) #if defined(_LP64)
/* /*
* ZDB does not typically re-read blocks; therefore limit the ARC * ZDB does not typically re-read blocks; therefore limit the ARC
@ -8645,7 +8638,7 @@ main(int argc, char **argv)
verbose = MAX(verbose, 1); verbose = MAX(verbose, 1);
for (c = 0; c < 256; c++) { for (c = 0; c < 256; c++) {
if (dump_all && strchr("AeEFklLOPrRSXy", c) == NULL) if (dump_all && strchr("AeEFklLNOPrRSXy", c) == NULL)
dump_opt[c] = 1; dump_opt[c] = 1;
if (dump_opt[c]) if (dump_opt[c])
dump_opt[c] += verbose; dump_opt[c] += verbose;
@ -8684,6 +8677,7 @@ main(int argc, char **argv)
return (dump_path(argv[0], argv[1], NULL)); return (dump_path(argv[0], argv[1], NULL));
} }
if (dump_opt['r']) { if (dump_opt['r']) {
target_is_spa = B_FALSE;
if (argc != 3) if (argc != 3)
usage(); usage();
dump_opt['v'] = verbose; dump_opt['v'] = verbose;
@ -8694,6 +8688,10 @@ main(int argc, char **argv)
rewind = ZPOOL_DO_REWIND | rewind = ZPOOL_DO_REWIND |
(dump_opt['X'] ? ZPOOL_EXTREME_REWIND : 0); (dump_opt['X'] ? ZPOOL_EXTREME_REWIND : 0);
/* -N implies -d */
if (dump_opt['N'] && dump_opt['d'] == 0)
dump_opt['d'] = dump_opt['N'];
if (nvlist_alloc(&policy, NV_UNIQUE_NAME_TYPE, 0) != 0 || if (nvlist_alloc(&policy, NV_UNIQUE_NAME_TYPE, 0) != 0 ||
nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, max_txg) != 0 || nvlist_add_uint64(policy, ZPOOL_LOAD_REQUEST_TXG, max_txg) != 0 ||
nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, rewind) != 0) nvlist_add_uint32(policy, ZPOOL_LOAD_REWIND_POLICY, rewind) != 0)
@ -8712,6 +8710,35 @@ main(int argc, char **argv)
targetlen = strlen(target); targetlen = strlen(target);
if (targetlen && target[targetlen - 1] == '/') if (targetlen && target[targetlen - 1] == '/')
target[targetlen - 1] = '\0'; target[targetlen - 1] = '\0';
/*
* See if an objset ID was supplied (-d <pool>/<objset ID>).
* To disambiguate tank/100, consider the 100 as objsetID
* if -N was given, otherwise 100 is an objsetID iff
* tank/100 as a named dataset fails on lookup.
*/
objset_str = strchr(target, '/');
if (objset_str && strlen(objset_str) > 1 &&
zdb_numeric(objset_str + 1)) {
char *endptr;
errno = 0;
objset_str++;
objset_id = strtoull(objset_str, &endptr, 0);
/* dataset 0 is the same as opening the pool */
if (errno == 0 && endptr != objset_str &&
objset_id != 0) {
if (dump_opt['N'])
dataset_lookup = B_TRUE;
}
/* normal dataset name not an objset ID */
if (endptr == objset_str) {
objset_id = -1;
}
} else if (objset_str && !zdb_numeric(objset_str + 1) &&
dump_opt['N']) {
printf("Supply a numeric objset ID with -N\n");
exit(1);
}
} else { } else {
target_pool = target; target_pool = target;
} }
@ -8829,13 +8856,27 @@ main(int argc, char **argv)
} }
return (error); return (error);
} else { } else {
target_pool = strdup(target);
if (strpbrk(target, "/@") != NULL)
*strpbrk(target_pool, "/@") = '\0';
zdb_set_skip_mmp(target); zdb_set_skip_mmp(target);
/*
* If -N was supplied, the user has indicated that
* zdb -d <pool>/<objsetID> is in effect. Otherwise
* we first assume that the dataset string is the
* dataset name. If dmu_objset_hold fails with the
* dataset string, and we have an objset_id, retry the
* lookup with the objsetID.
*/
boolean_t retry = B_TRUE;
retry_lookup:
if (dataset_lookup == B_TRUE) { if (dataset_lookup == B_TRUE) {
/* /*
* Use the supplied id to get the name * Use the supplied id to get the name
* for open_objset. * for open_objset.
*/ */
error = spa_open(target, &spa, FTAG); error = spa_open(target_pool, &spa, FTAG);
if (error == 0) { if (error == 0) {
error = name_from_objset_id(spa, error = name_from_objset_id(spa,
objset_id, dsname); objset_id, dsname);
@ -8844,10 +8885,23 @@ main(int argc, char **argv)
target = dsname; target = dsname;
} }
} }
if (error == 0) if (error == 0) {
if (objset_id > 0 && retry) {
int err = dmu_objset_hold(target, FTAG,
&os);
if (err) {
dataset_lookup = B_TRUE;
retry = B_FALSE;
goto retry_lookup;
} else {
dmu_objset_rele(os, FTAG);
}
}
error = open_objset(target, FTAG, &os); error = open_objset(target, FTAG, &os);
}
if (error == 0) if (error == 0)
spa = dmu_objset_spa(os); spa = dmu_objset_spa(os);
free(target_pool);
} }
} }
nvlist_free(policy); nvlist_free(policy);

View File

@ -23,7 +23,7 @@
.Nd display ZFS storage pool debugging and consistency information .Nd display ZFS storage pool debugging and consistency information
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl AbcdDFGhikLMPsvXYy .Op Fl AbcdDFGhikLMNPsvXYy
.Op Fl e Oo Fl V Oc Oo Fl p Ar path Oc Ns .Op Fl e Oo Fl V Oc Oo Fl p Ar path Oc Ns
.Op Fl I Ar inflight I/Os .Op Fl I Ar inflight I/Os
.Oo Fl o Ar var Ns = Ns Ar value Oc Ns .Oo Fl o Ar var Ns = Ns Ar value Oc Ns
@ -137,6 +137,14 @@ also display the configuration that would be used were the pool to be imported.
Display information about datasets. Display information about datasets.
Specified once, displays basic dataset information: ID, create transaction, Specified once, displays basic dataset information: ID, create transaction,
size, and object count. size, and object count.
See
.Fl N
for determining if
.Op Ar poolname Ns Op / Ns Ar dataset | objset ID
is to use the specified
.Op Ar dataset | objset ID
as a string (dataset name) or a number (objset ID) when
datasets have numeric names.
.Pp .Pp
If specified multiple times provides greater and greater verbosity. If specified multiple times provides greater and greater verbosity.
.Pp .Pp
@ -271,6 +279,19 @@ and free space histogram, as well as overall pool fragmentation and histogram.
.It Fl MM .It Fl MM
"Special" vdevs are added to -M's normal output. "Special" vdevs are added to -M's normal output.
.It Fl O , -object-lookups Ns = Ns Ar dataset path .It Fl O , -object-lookups Ns = Ns Ar dataset path
Also display information about the maximum contiguous free space and the
percentage of free space in each space map.
.It Fl MMM
Display every spacemap record.
.It Fl N
Same as
.Fl d
but force zdb to interpret the
.Op Ar dataset | objset ID
in
.Op Ar poolname Ns Op / Ns Ar dataset | objset ID
as a numeric objset ID.
.It Fl O Ar dataset path
Look up the specified Look up the specified
.Ar path .Ar path
inside of the inside of the

View File

@ -58,7 +58,7 @@ set -A args "create" "add" "destroy" "import fakepool" \
"setvprop" "blah blah" "-%" "--?" "-*" "-=" \ "setvprop" "blah blah" "-%" "--?" "-*" "-=" \
"-a" "-f" "-g" "-j" "-n" "-o" "-p" "-p /tmp" \ "-a" "-f" "-g" "-j" "-n" "-o" "-p" "-p /tmp" \
"-t" "-w" "-z" "-E" "-H" "-I" "-J" "-K" \ "-t" "-w" "-z" "-E" "-H" "-I" "-J" "-K" \
"-N" "-Q" "-R" "-T" "-W" "-Q" "-R" "-T" "-W"
log_assert "Execute zdb using invalid parameters." log_assert "Execute zdb using invalid parameters."

View File

@ -30,10 +30,16 @@
# 6. Confirm names # 6. Confirm names
# 7. Run zdb -dddddd pool/objsetID objectID (hex) # 7. Run zdb -dddddd pool/objsetID objectID (hex)
# 8. Confirm names # 8. Confirm names
# 9. Obtain objsetID from /proc/spl/kstat/zfs/testpool/obset-0x<ID> # 9. Repeat with zdb -NNNNNN pool/objsetID objectID
# 10. Obtain objsetID from /proc/spl/kstat/zfs/testpool/obset-0x<ID>
# (linux only) # (linux only)
# 10. Run zdb -dddddd pool/objsetID (hex) # 11. Run zdb -dddddd pool/objsetID (hex)
# 11. Match name from zdb against proc entry # 12. Match name from zdb against proc entry
# 13. Create dataset with hex numeric name
# 14. Create dataset with decimal numeric name
# 15. zdb -d for numeric datasets succeeds
# 16. zdb -N for numeric datasets fails
# 17. zdb -dN for numeric datasets fails
# #
function cleanup function cleanup
@ -78,6 +84,17 @@ do
(( $? != 0 )) && log_fail \ (( $? != 0 )) && log_fail \
"zdb -dddddd $TESTPOOL/$id $obj failed $reason" "zdb -dddddd $TESTPOOL/$id $obj failed $reason"
obj=$(printf "0x%X" $obj) obj=$(printf "0x%X" $obj)
log_note "zdb -NNNNNN $TESTPOOL/$id $obj"
output=$(zdb -NNNNNN $TESTPOOL/$id $obj)
reason="($TESTPOOL/$TESTFS not in zdb output)"
echo $output |grep "$TESTPOOL/$TESTFS" > /dev/null
(( $? != 0 )) && log_fail \
"zdb -NNNNNN $TESTPOOL/$id $obj failed $reason"
reason="(file1 not in zdb output)"
echo $output |grep "file1" > /dev/null
(( $? != 0 )) && log_fail \
"zdb -NNNNNN $TESTPOOL/$id $obj failed $reason"
done done
if is_linux; then if is_linux; then
@ -94,4 +111,23 @@ if is_linux; then
"zdb -dddddd $TESTPOOL/$objset_hex failed $reason" "zdb -dddddd $TESTPOOL/$objset_hex failed $reason"
fi fi
log_must zfs create $TESTPOOL/0x400
log_must zfs create $TESTPOOL/100
output=$(zdb -d $TESTPOOL/0x400)
reason="($TESTPOOL/0x400 not in zdb output)"
echo $output |grep "$TESTPOOL/0x400" > /dev/null
(( $? != 0 )) && log_fail \
"zdb -d $TESTPOOL/0x400 failed $reason"
output=$(zdb -d $TESTPOOL/100)
reason="($TESTPOOL/100 not in zdb output)"
echo $output |grep "$TESTPOOL/100" > /dev/null
(( $? != 0 )) && log_fail \
"zdb -d $TESTPOOL/100 failed $reason"
# force numeric interpretation, should fail
log_mustnot zdb -N $TESTPOOL/0x400
log_mustnot zdb -N $TESTPOOL/100
log_mustnot zdb -Nd $TESTPOOL/0x400
log_mustnot zdb -Nd $TESTPOOL/100
log_pass "zdb -d <pool>/<objset ID> generates the correct names." log_pass "zdb -d <pool>/<objset ID> generates the correct names."