zio_decompress_data always ASSERTs successful decompression
This interferes with zdb_read_block trying all the decompression algorithms when the 'd' flag is specified, as some are expected to fail. Also control the output when guessing algorithms, try the more common compression types first, allow specifying lsize/psize, and fix an uninitialized variable. Reviewed-by: Ryan Moeller <ryan@ixsystems.com> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Paul Zuchowski <pzuchowski@datto.com> Closes #9612 Closes #9630
This commit is contained in:
parent
b119e2c6f1
commit
f0bf435176
125
cmd/zdb/zdb.c
125
cmd/zdb/zdb.c
|
@ -2738,7 +2738,7 @@ static const char *objset_types[DMU_OST_NUMTYPES] = {
|
|||
static void
|
||||
dump_objset(objset_t *os)
|
||||
{
|
||||
dmu_objset_stats_t dds;
|
||||
dmu_objset_stats_t dds = { 0 };
|
||||
uint64_t object, object_count;
|
||||
uint64_t refdbytes, usedobjs, scratch;
|
||||
char numbuf[32];
|
||||
|
@ -6196,9 +6196,9 @@ dump_zpool(spa_t *spa)
|
|||
#define ZDB_FLAG_BSWAP 0x0004
|
||||
#define ZDB_FLAG_GBH 0x0008
|
||||
#define ZDB_FLAG_INDIRECT 0x0010
|
||||
#define ZDB_FLAG_PHYS 0x0020
|
||||
#define ZDB_FLAG_RAW 0x0040
|
||||
#define ZDB_FLAG_PRINT_BLKPTR 0x0080
|
||||
#define ZDB_FLAG_RAW 0x0020
|
||||
#define ZDB_FLAG_PRINT_BLKPTR 0x0040
|
||||
#define ZDB_FLAG_VERBOSE 0x0080
|
||||
|
||||
static int flagbits[256];
|
||||
|
||||
|
@ -6329,11 +6329,30 @@ name:
|
|||
return (NULL);
|
||||
}
|
||||
|
||||
static boolean_t
|
||||
zdb_parse_block_sizes(char *sizes, uint64_t *lsize, uint64_t *psize)
|
||||
{
|
||||
char *s0, *s1;
|
||||
|
||||
if (sizes == NULL)
|
||||
return (B_FALSE);
|
||||
|
||||
s0 = strtok(sizes, "/");
|
||||
if (s0 == NULL)
|
||||
return (B_FALSE);
|
||||
s1 = strtok(NULL, "/");
|
||||
*lsize = strtoull(s0, NULL, 16);
|
||||
*psize = s1 ? strtoull(s1, NULL, 16) : *lsize;
|
||||
return (*lsize >= *psize && *psize > 0);
|
||||
}
|
||||
|
||||
#define ZIO_COMPRESS_MASK(alg) (1ULL << (ZIO_COMPRESS_##alg))
|
||||
|
||||
/*
|
||||
* Read a block from a pool and print it out. The syntax of the
|
||||
* block descriptor is:
|
||||
*
|
||||
* pool:vdev_specifier:offset:size[:flags]
|
||||
* pool:vdev_specifier:offset:[lsize/]psize[:flags]
|
||||
*
|
||||
* pool - The name of the pool you wish to read from
|
||||
* vdev_specifier - Which vdev (see comment for zdb_vdev_lookup)
|
||||
|
@ -6346,8 +6365,8 @@ name:
|
|||
* e: Byteswap data before dumping
|
||||
* g: Display data as a gang block header
|
||||
* i: Display as an indirect block
|
||||
* p: Do I/O to physical offset
|
||||
* r: Dump raw data to stdout
|
||||
* v: Verbose
|
||||
*
|
||||
*/
|
||||
static void
|
||||
|
@ -6356,13 +6375,12 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
blkptr_t blk, *bp = &blk;
|
||||
dva_t *dva = bp->blk_dva;
|
||||
int flags = 0;
|
||||
uint64_t offset = 0, size = 0, psize = 0, lsize = 0, blkptr_offset = 0;
|
||||
uint64_t offset = 0, psize = 0, lsize = 0, blkptr_offset = 0;
|
||||
zio_t *zio;
|
||||
vdev_t *vd;
|
||||
abd_t *pabd;
|
||||
void *lbuf, *buf;
|
||||
const char *s, *vdev;
|
||||
char *p, *dup, *flagstr;
|
||||
char *s, *p, *dup, *vdev, *flagstr, *sizes;
|
||||
int i, error;
|
||||
boolean_t borrowed = B_FALSE;
|
||||
|
||||
|
@ -6371,18 +6389,14 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
vdev = s ? s : "";
|
||||
s = strtok(NULL, ":");
|
||||
offset = strtoull(s ? s : "", NULL, 16);
|
||||
sizes = strtok(NULL, ":");
|
||||
s = strtok(NULL, ":");
|
||||
size = strtoull(s ? s : "", NULL, 16);
|
||||
s = strtok(NULL, ":");
|
||||
if (s)
|
||||
flagstr = strdup(s);
|
||||
else
|
||||
flagstr = strdup("");
|
||||
flagstr = strdup(s ? s : "");
|
||||
|
||||
s = NULL;
|
||||
if (size == 0)
|
||||
s = "size must not be zero";
|
||||
if (!IS_P2ALIGNED(size, DEV_BSIZE))
|
||||
if (!zdb_parse_block_sizes(sizes, &lsize, &psize))
|
||||
s = "invalid size(s)";
|
||||
if (!IS_P2ALIGNED(psize, DEV_BSIZE) || !IS_P2ALIGNED(lsize, DEV_BSIZE))
|
||||
s = "size must be a multiple of sector size";
|
||||
if (!IS_P2ALIGNED(offset, DEV_BSIZE))
|
||||
s = "offset must be a multiple of sector size";
|
||||
|
@ -6438,9 +6452,6 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
vd->vdev_ops->vdev_op_type);
|
||||
}
|
||||
|
||||
psize = size;
|
||||
lsize = size;
|
||||
|
||||
pabd = abd_alloc_for_io(SPA_MAXBLOCKSIZE, B_FALSE);
|
||||
lbuf = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
|
||||
|
||||
|
@ -6497,30 +6508,41 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
* We don't know how the data was compressed, so just try
|
||||
* every decompress function at every inflated blocksize.
|
||||
*/
|
||||
enum zio_compress c;
|
||||
void *lbuf2 = umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
|
||||
int cfuncs[ZIO_COMPRESS_FUNCTIONS] = { 0 };
|
||||
int *cfuncp = cfuncs;
|
||||
uint64_t maxlsize = SPA_MAXBLOCKSIZE;
|
||||
uint64_t mask = ZIO_COMPRESS_MASK(ON) | ZIO_COMPRESS_MASK(OFF) |
|
||||
ZIO_COMPRESS_MASK(INHERIT) | ZIO_COMPRESS_MASK(EMPTY) |
|
||||
(getenv("ZDB_NO_ZLE") ? ZIO_COMPRESS_MASK(ZLE) : 0);
|
||||
*cfuncp++ = ZIO_COMPRESS_LZ4;
|
||||
*cfuncp++ = ZIO_COMPRESS_LZJB;
|
||||
mask |= ZIO_COMPRESS_MASK(LZ4) | ZIO_COMPRESS_MASK(LZJB);
|
||||
for (int c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++)
|
||||
if (((1ULL << c) & mask) == 0)
|
||||
*cfuncp++ = c;
|
||||
|
||||
/*
|
||||
* XXX - On the one hand, with SPA_MAXBLOCKSIZE at 16MB,
|
||||
* this could take a while and we should let the user know
|
||||
* On the one hand, with SPA_MAXBLOCKSIZE at 16MB, this
|
||||
* could take a while and we should let the user know
|
||||
* we are not stuck. On the other hand, printing progress
|
||||
* info gets old after a while. What to do?
|
||||
* info gets old after a while. User can specify 'v' flag
|
||||
* to see the progression.
|
||||
*/
|
||||
for (lsize = psize + SPA_MINBLOCKSIZE;
|
||||
lsize <= SPA_MAXBLOCKSIZE; lsize += SPA_MINBLOCKSIZE) {
|
||||
for (c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++) {
|
||||
/*
|
||||
* ZLE can easily decompress non zle stream.
|
||||
* So have an option to disable it.
|
||||
*/
|
||||
if (c == ZIO_COMPRESS_ZLE &&
|
||||
getenv("ZDB_NO_ZLE"))
|
||||
continue;
|
||||
|
||||
if (lsize == psize)
|
||||
lsize += SPA_MINBLOCKSIZE;
|
||||
else
|
||||
maxlsize = lsize;
|
||||
for (; lsize <= maxlsize; lsize += SPA_MINBLOCKSIZE) {
|
||||
for (cfuncp = cfuncs; *cfuncp; cfuncp++) {
|
||||
if (flags & ZDB_FLAG_VERBOSE) {
|
||||
(void) fprintf(stderr,
|
||||
"Trying %05llx -> %05llx (%s)\n",
|
||||
(u_longlong_t)psize, (u_longlong_t)lsize,
|
||||
zio_compress_table[c].ci_name);
|
||||
(u_longlong_t)psize,
|
||||
(u_longlong_t)lsize,
|
||||
zio_compress_table[*cfuncp].\
|
||||
ci_name);
|
||||
}
|
||||
|
||||
/*
|
||||
* We randomize lbuf2, and decompress to both
|
||||
|
@ -6529,27 +6551,30 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
*/
|
||||
VERIFY0(random_get_pseudo_bytes(lbuf2, lsize));
|
||||
|
||||
if (zio_decompress_data(c, pabd,
|
||||
if (zio_decompress_data(*cfuncp, pabd,
|
||||
lbuf, psize, lsize) == 0 &&
|
||||
zio_decompress_data(c, pabd,
|
||||
zio_decompress_data(*cfuncp, pabd,
|
||||
lbuf2, psize, lsize) == 0 &&
|
||||
bcmp(lbuf, lbuf2, lsize) == 0)
|
||||
break;
|
||||
}
|
||||
if (c != ZIO_COMPRESS_FUNCTIONS)
|
||||
if (*cfuncp != 0)
|
||||
break;
|
||||
}
|
||||
umem_free(lbuf2, SPA_MAXBLOCKSIZE);
|
||||
|
||||
if (lsize > SPA_MAXBLOCKSIZE) {
|
||||
if (lsize > maxlsize) {
|
||||
(void) printf("Decompress of %s failed\n", thing);
|
||||
goto out;
|
||||
}
|
||||
buf = lbuf;
|
||||
size = lsize;
|
||||
if (*cfuncp == ZIO_COMPRESS_ZLE) {
|
||||
printf("\nZLE decompression was selected. If you "
|
||||
"suspect the results are wrong,\ntry avoiding ZLE "
|
||||
"by setting and exporting ZDB_NO_ZLE=\"true\"\n");
|
||||
}
|
||||
} else {
|
||||
size = psize;
|
||||
buf = abd_borrow_buf_copy(pabd, size);
|
||||
buf = abd_borrow_buf_copy(pabd, lsize);
|
||||
borrowed = B_TRUE;
|
||||
}
|
||||
|
||||
|
@ -6557,14 +6582,14 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
zdb_print_blkptr((blkptr_t *)(void *)
|
||||
((uintptr_t)buf + (uintptr_t)blkptr_offset), flags);
|
||||
else if (flags & ZDB_FLAG_RAW)
|
||||
zdb_dump_block_raw(buf, size, flags);
|
||||
zdb_dump_block_raw(buf, lsize, flags);
|
||||
else if (flags & ZDB_FLAG_INDIRECT)
|
||||
zdb_dump_indirect((blkptr_t *)buf, size / sizeof (blkptr_t),
|
||||
zdb_dump_indirect((blkptr_t *)buf, lsize / sizeof (blkptr_t),
|
||||
flags);
|
||||
else if (flags & ZDB_FLAG_GBH)
|
||||
zdb_dump_gbh(buf, flags);
|
||||
else
|
||||
zdb_dump_block(thing, buf, size, flags);
|
||||
zdb_dump_block(thing, buf, lsize, flags);
|
||||
|
||||
/*
|
||||
* If :c was specified, iterate through the checksum table to
|
||||
|
@ -6628,7 +6653,7 @@ zdb_read_block(char *thing, spa_t *spa)
|
|||
}
|
||||
|
||||
if (borrowed)
|
||||
abd_return_buf_copy(pabd, buf, size);
|
||||
abd_return_buf_copy(pabd, buf, lsize);
|
||||
|
||||
out:
|
||||
abd_free(pabd);
|
||||
|
@ -7064,8 +7089,8 @@ main(int argc, char **argv)
|
|||
flagbits['e'] = ZDB_FLAG_BSWAP;
|
||||
flagbits['g'] = ZDB_FLAG_GBH;
|
||||
flagbits['i'] = ZDB_FLAG_INDIRECT;
|
||||
flagbits['p'] = ZDB_FLAG_PHYS;
|
||||
flagbits['r'] = ZDB_FLAG_RAW;
|
||||
flagbits['v'] = ZDB_FLAG_VERBOSE;
|
||||
|
||||
for (int i = 0; i < argc; i++)
|
||||
zdb_read_block(argv[i], spa);
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
.Op Fl A
|
||||
.Op Fl e Oo Fl V Oc Op Fl p Ar path ...
|
||||
.Op Fl U Ar cache
|
||||
.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
|
||||
.Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar [<lsize>/]<psize> Ns Op : Ns Ar flags
|
||||
.Nm
|
||||
.Fl S
|
||||
.Op Fl AP
|
||||
|
@ -228,7 +228,7 @@ This option can be combined with
|
|||
.Fl v
|
||||
for increasing verbosity.
|
||||
.It Xo
|
||||
.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar size Ns Op : Ns Ar flags
|
||||
.Fl R Ar poolname vdev Ns \&: Ns Ar offset Ns \&: Ns Ar [<lsize>/]<psize> Ns Op : Ns Ar flags
|
||||
.Xc
|
||||
Read and display a block from the specified device.
|
||||
By default the block is displayed as a hex dump, but see the description of the
|
||||
|
@ -241,8 +241,8 @@ The block is specified in terms of a colon-separated tuple
|
|||
.Ar offset
|
||||
.Pq the offset within the vdev
|
||||
.Ar size
|
||||
.Pq the size of the block to read
|
||||
and, optionally,
|
||||
.Pq the physical size, or logical size / physical size
|
||||
of the block to read and, optionally,
|
||||
.Ar flags
|
||||
.Pq a set of flags, described below .
|
||||
.Pp
|
||||
|
@ -263,6 +263,8 @@ Dump gang block header
|
|||
Dump indirect block
|
||||
.It Sy r
|
||||
Dump raw uninterpreted block data
|
||||
.It Sy v
|
||||
Verbose output for guessing compression algorithm
|
||||
.El
|
||||
.It Fl s
|
||||
Report statistics on
|
||||
|
|
|
@ -159,7 +159,6 @@ zio_decompress_data(enum zio_compress c, abd_t *src, void *dst,
|
|||
* the checksum. However, for extra protection (e.g. against bitflips
|
||||
* in non-ECC RAM), we handle this error (and test it).
|
||||
*/
|
||||
ASSERT0(ret);
|
||||
if (zio_decompress_fail_fraction != 0 &&
|
||||
spa_get_random(zio_decompress_fail_fraction) == 0)
|
||||
ret = SET_ERROR(EINVAL);
|
||||
|
|
|
@ -94,7 +94,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_005_pos', 'zdb_006_pos', 'zdb_checksum', 'zdb_decompress']
|
||||
pre =
|
||||
post =
|
||||
tags = ['functional', 'cli_root', 'zdb']
|
||||
|
|
|
@ -6,4 +6,5 @@ dist_pkgdata_SCRIPTS = \
|
|||
zdb_004_pos.ksh \
|
||||
zdb_005_pos.ksh \
|
||||
zdb_006_pos.ksh \
|
||||
zdb_checksum.ksh
|
||||
zdb_checksum.ksh \
|
||||
zdb_decompress.ksh
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
#!/bin/ksh
|
||||
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
#
|
||||
# Copyright (c) 2019 by Datto, Inc. All rights reserved.
|
||||
#
|
||||
|
||||
. $STF_SUITE/include/libtest.shlib
|
||||
|
||||
#
|
||||
# Description:
|
||||
# zdb -R pool <DVA>:d will display the correct data and length
|
||||
#
|
||||
# Strategy:
|
||||
# 1. Create a pool, set compression to lzjb
|
||||
# 2. Write some identifiable data to a file
|
||||
# 3. Run zdb -ddddddbbbbbb against the file
|
||||
# 4. Record the DVA, lsize, and psize of L0 block 0
|
||||
# 5. Run zdb -R with :d flag and match the data
|
||||
# 6. Run zdb -R with :dr flags and match the lsize/psize
|
||||
# 7. Run zdb -R with :dr flags and match the lsize
|
||||
# 8. Run zdb -R with :dr flags and match the psize
|
||||
#
|
||||
|
||||
|
||||
function cleanup
|
||||
{
|
||||
datasetexists $TESTPOOL && destroy_pool $TESTPOOL
|
||||
}
|
||||
|
||||
log_assert "Verify zdb -R :d flag (decompress) works as expected"
|
||||
log_onexit cleanup
|
||||
init_data=$TESTDIR/file1
|
||||
write_count=256
|
||||
blksize=4096
|
||||
pattern="_match__pattern_"
|
||||
verify_runnable "global"
|
||||
verify_disk_count "$DISKS" 2
|
||||
|
||||
default_mirror_setup_noexit $DISKS
|
||||
log_must zfs set recordsize=$blksize $TESTPOOL/$TESTFS
|
||||
log_must zfs set compression=lzjb $TESTPOOL/$TESTFS
|
||||
|
||||
# 16 chars 256 times = 4k = block size
|
||||
typeset four_k=""
|
||||
for i in {1..$write_count}
|
||||
do
|
||||
four_k=$four_k$pattern
|
||||
done
|
||||
|
||||
# write the 4k block 256 times
|
||||
for i in {1..$write_count}
|
||||
do
|
||||
echo $four_k >> $init_data
|
||||
done
|
||||
|
||||
sync_pool $TESTPOOL true
|
||||
|
||||
# get object number of file
|
||||
listing=$(ls -i $init_data)
|
||||
set -A array $listing
|
||||
obj=${array[0]}
|
||||
log_note "file $init_data has object number $obj"
|
||||
|
||||
output=$(zdb -ddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \
|
||||
|grep -m 1 "L0 DVA" |head -n1)
|
||||
dva=$(grep -oP 'DVA\[0\]=<\K.*?(?=>)' <<< "$output")
|
||||
log_note "block 0 of $init_data has a DVA of $dva"
|
||||
|
||||
# use the length reported by zdb -ddddddbbbbbb
|
||||
size_str=$(grep -oP 'size=\K.*?(?= )' <<< "$output")
|
||||
log_note "block size $size_str"
|
||||
|
||||
vdev=$(echo "$dva" |awk '{split($0,array,":")} END{print array[1]}')
|
||||
offset=$(echo "$dva" |awk '{split($0,array,":")} END{print array[2]}')
|
||||
output=$(zdb -R $TESTPOOL $vdev:$offset:$size_str:d 2> /dev/null)
|
||||
echo $output |grep $pattern > /dev/null
|
||||
(( $? != 0 )) && log_fail "zdb -R :d failed to decompress the data properly"
|
||||
|
||||
output=$(zdb -R $TESTPOOL $vdev:$offset:$size_str:dr 2> /dev/null)
|
||||
echo $output |grep $four_k > /dev/null
|
||||
(( $? != 0 )) && log_fail "zdb -R :dr failed to decompress the data properly"
|
||||
|
||||
output=$(zdb -R $TESTPOOL $vdev:$offset:$size_str:dr 2> /dev/null)
|
||||
result=${#output}
|
||||
(( $result != $blksize)) && log_fail \
|
||||
"zdb -R failed to decompress the data to the length (${#output} != $size_str)"
|
||||
|
||||
# decompress using lsize
|
||||
lsize=$(echo $size_str |awk '{split($0,array,"/")} END{print array[1]}')
|
||||
psize=$(echo $size_str |awk '{split($0,array,"/")} END{print array[2]}')
|
||||
output=$(zdb -R $TESTPOOL $vdev:$offset:$lsize:dr 2> /dev/null)
|
||||
result=${#output}
|
||||
(( $result != $blksize)) && log_fail \
|
||||
"zdb -R failed to decompress the data (length ${#output} != $blksize)"
|
||||
|
||||
# Specifying psize will decompress successfully , but not always to full
|
||||
# lsize since zdb has to guess lsize incrementally.
|
||||
output=$(zdb -R $TESTPOOL $vdev:$offset:$psize:dr 2> /dev/null)
|
||||
result=${#output}
|
||||
# convert psize to decimal
|
||||
psize_orig=$psize
|
||||
psize=${psize%?}
|
||||
psize=$((16#$psize))
|
||||
(( $result < $psize)) && log_fail \
|
||||
"zdb -R failed to decompress the data with psize $psize_orig\
|
||||
(length ${#output} < $psize)"
|
||||
|
||||
log_pass "zdb -R :d flag (decompress) works as expected"
|
Loading…
Reference in New Issue