diff options
-rw-r--r-- | cmd/zdb/zdb.c | 255 | ||||
-rw-r--r-- | include/sys/zio.h | 9 | ||||
-rw-r--r-- | man/man8/zdb.8 | 2 | ||||
-rw-r--r-- | module/zfs/zio.c | 91 | ||||
-rw-r--r-- | tests/runfiles/common.run | 3 | ||||
-rw-r--r-- | tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am | 5 | ||||
-rwxr-xr-x | tests/zfs-tests/tests/functional/cli_root/zdb/zdb_display_block.ksh | 128 |
7 files changed, 376 insertions, 117 deletions
diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index f00df5d57..d6d8b91a9 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -6410,6 +6410,18 @@ 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 char flagbitstr[16]; + static void zdb_print_blkptr(blkptr_t *bp, int flags) { @@ -6576,6 +6588,83 @@ zdb_parse_block_sizes(char *sizes, uint64_t *lsize, uint64_t *psize) #define ZIO_COMPRESS_MASK(alg) (1ULL << (ZIO_COMPRESS_##alg)) +static boolean_t +zdb_decompress_block(abd_t *pabd, void *buf, void *lbuf, uint64_t lsize, + uint64_t psize, int flags) +{ + boolean_t exceeded = B_FALSE; + /* + * We don't know how the data was compressed, so just try + * every decompress function at every inflated blocksize. + */ + 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; + + /* + * 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. User can specify 'v' flag + * to see the progression. + */ + 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[*cfuncp].\ + ci_name); + } + + /* + * We randomize lbuf2, and decompress to both + * lbuf and lbuf2. This way, we will know if + * decompression fill exactly to lsize. + */ + VERIFY0(random_get_pseudo_bytes(lbuf2, lsize)); + + if (zio_decompress_data(*cfuncp, pabd, + lbuf, psize, lsize) == 0 && + zio_decompress_data(*cfuncp, pabd, + lbuf2, psize, lsize) == 0 && + bcmp(lbuf, lbuf2, lsize) == 0) + break; + } + if (*cfuncp != 0) + break; + } + umem_free(lbuf2, SPA_MAXBLOCKSIZE); + + if (lsize > maxlsize) { + exceeded = B_TRUE; + } + buf = lbuf; + 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"); + } + + return (exceeded); +} + /* * Read a block from a pool and print it out. The syntax of the * block descriptor is: @@ -6610,7 +6699,7 @@ zdb_read_block(char *thing, spa_t *spa) void *lbuf, *buf; char *s, *p, *dup, *vdev, *flagstr, *sizes; int i, error; - boolean_t borrowed = B_FALSE; + boolean_t borrowed = B_FALSE, found = B_FALSE; dup = strdup(thing); s = strtok(dup, ":"); @@ -6630,41 +6719,57 @@ zdb_read_block(char *thing, spa_t *spa) s = "offset must be a multiple of sector size"; if (s) { (void) printf("Invalid block specifier: %s - %s\n", thing, s); - free(flagstr); - free(dup); - return; + goto done; } for (s = strtok(flagstr, ":"); s; s = strtok(NULL, ":")) { - for (i = 0; flagstr[i]; i++) { + for (i = 0; i < strlen(flagstr); i++) { int bit = flagbits[(uchar_t)flagstr[i]]; if (bit == 0) { - (void) printf("***Invalid flag: %c\n", - flagstr[i]); + (void) printf("***Ignoring flag: %c\n", + (uchar_t)flagstr[i]); continue; } + found = B_TRUE; flags |= bit; - /* If it's not something with an argument, keep going */ - if ((bit & (ZDB_FLAG_CHECKSUM | - ZDB_FLAG_PRINT_BLKPTR)) == 0) - continue; - p = &flagstr[i + 1]; - if (bit == ZDB_FLAG_PRINT_BLKPTR) { - blkptr_offset = strtoull(p, &p, 16); - i = p - &flagstr[i + 1]; - } if (*p != ':' && *p != '\0') { - (void) printf("***Invalid flag arg: '%s'\n", s); - free(flagstr); - free(dup); - return; + int j = 0, nextbit = flagbits[(uchar_t)*p]; + char *end, offstr[8] = { 0 }; + if ((bit == ZDB_FLAG_PRINT_BLKPTR) && + (nextbit == 0)) { + /* look ahead to isolate the offset */ + while (nextbit == 0 && + strchr(flagbitstr, *p) == NULL) { + offstr[j] = *p; + j++; + if (i + j > strlen(flagstr)) + break; + p++; + nextbit = flagbits[(uchar_t)*p]; + } + blkptr_offset = strtoull(offstr, &end, + 16); + i += j; + } else if (nextbit == 0) { + (void) printf("***Ignoring flag arg:" + " '%c'\n", (uchar_t)*p); + } } } } - free(flagstr); + if (blkptr_offset % sizeof (blkptr_t)) { + printf("Block pointer offset 0x%llx " + "must be divisible by 0x%x\n", + (longlong_t)blkptr_offset, (int)sizeof (blkptr_t)); + goto done; + } + if (found == B_FALSE && strlen(flagstr) > 0) { + printf("Invalid flag arg: '%s'\n", flagstr); + goto done; + } vd = zdb_vdev_lookup(spa->spa_root_vdev, vdev); if (vd == NULL) { @@ -6717,10 +6822,9 @@ zdb_read_block(char *thing, spa_t *spa) */ zio_nowait(zio_vdev_child_io(zio, bp, vd, offset, pabd, psize, ZIO_TYPE_READ, ZIO_PRIORITY_SYNC_READ, - ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_QUEUE | - ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY | - ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW | ZIO_FLAG_OPTIONAL, - NULL, NULL)); + ZIO_FLAG_DONT_CACHE | ZIO_FLAG_DONT_PROPAGATE | + ZIO_FLAG_DONT_RETRY | ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW | + ZIO_FLAG_OPTIONAL, NULL, NULL)); } error = zio_wait(zio); @@ -6731,80 +6835,43 @@ zdb_read_block(char *thing, spa_t *spa) goto out; } + uint64_t orig_lsize = lsize; + buf = lbuf; if (flags & ZDB_FLAG_DECOMPRESS) { - /* - * We don't know how the data was compressed, so just try - * every decompress function at every inflated blocksize. - */ - 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; - - /* - * 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. User can specify 'v' flag - * to see the progression. - */ - 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[*cfuncp].\ - ci_name); - } - - /* - * We randomize lbuf2, and decompress to both - * lbuf and lbuf2. This way, we will know if - * decompression fill exactly to lsize. - */ - VERIFY0(random_get_pseudo_bytes(lbuf2, lsize)); - - if (zio_decompress_data(*cfuncp, pabd, - lbuf, psize, lsize) == 0 && - zio_decompress_data(*cfuncp, pabd, - lbuf2, psize, lsize) == 0 && - bcmp(lbuf, lbuf2, lsize) == 0) - break; - } - if (*cfuncp != 0) - break; - } - umem_free(lbuf2, SPA_MAXBLOCKSIZE); - - if (lsize > maxlsize) { + boolean_t failed = zdb_decompress_block(pabd, buf, lbuf, + lsize, psize, flags); + if (failed) { (void) printf("Decompress of %s failed\n", thing); goto out; } - buf = lbuf; - 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 { buf = abd_borrow_buf_copy(pabd, lsize); borrowed = B_TRUE; } + /* + * Try to detect invalid block pointer. If invalid, try + * decompressing. + */ + if ((flags & ZDB_FLAG_PRINT_BLKPTR || flags & ZDB_FLAG_INDIRECT) && + !(flags & ZDB_FLAG_DECOMPRESS)) { + const blkptr_t *b = (const blkptr_t *)(void *) + ((uintptr_t)buf + (uintptr_t)blkptr_offset); + if (zfs_blkptr_verify(spa, b, B_FALSE, BLK_VERIFY_ONLY) == + B_FALSE) { + abd_return_buf_copy(pabd, buf, lsize); + borrowed = B_FALSE; + buf = lbuf; + boolean_t failed = zdb_decompress_block(pabd, buf, + lbuf, lsize, psize, flags); + b = (const blkptr_t *)(void *) + ((uintptr_t)buf + (uintptr_t)blkptr_offset); + if (failed || zfs_blkptr_verify(spa, b, B_FALSE, + BLK_VERIFY_LOG) == B_FALSE) { + printf("invalid block pointer at this DVA\n"); + goto out; + } + } + } if (flags & ZDB_FLAG_PRINT_BLKPTR) zdb_print_blkptr((blkptr_t *)(void *) @@ -6812,8 +6879,8 @@ zdb_read_block(char *thing, spa_t *spa) else if (flags & ZDB_FLAG_RAW) zdb_dump_block_raw(buf, lsize, flags); else if (flags & ZDB_FLAG_INDIRECT) - zdb_dump_indirect((blkptr_t *)buf, lsize / sizeof (blkptr_t), - flags); + zdb_dump_indirect((blkptr_t *)buf, + orig_lsize / sizeof (blkptr_t), flags); else if (flags & ZDB_FLAG_GBH) zdb_dump_gbh(buf, flags); else @@ -6886,6 +6953,8 @@ zdb_read_block(char *thing, spa_t *spa) out: abd_free(pabd); umem_free(lbuf, SPA_MAXBLOCKSIZE); +done: + free(flagstr); free(dup); } diff --git a/include/sys/zio.h b/include/sys/zio.h index f78b02fd5..368d8c149 100644 --- a/include/sys/zio.h +++ b/include/sys/zio.h @@ -516,6 +516,12 @@ struct zio { taskq_ent_t io_tqent; }; +enum blk_verify_flag { + BLK_VERIFY_ONLY, + BLK_VERIFY_LOG, + BLK_VERIFY_HALT +}; + extern int zio_bookmark_compare(const void *, const void *); extern zio_t *zio_null(zio_t *pio, spa_t *spa, vdev_t *vd, @@ -626,6 +632,9 @@ extern void zio_suspend(spa_t *spa, zio_t *zio, zio_suspend_reason_t); extern int zio_resume(spa_t *spa); extern void zio_resume_wait(spa_t *spa); +extern boolean_t zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, + boolean_t config_held, enum blk_verify_flag blk_verify); + /* * Initial setup and teardown. */ diff --git a/man/man8/zdb.8 b/man/man8/zdb.8 index a7ab6b8ba..8506d5478 100644 --- a/man/man8/zdb.8 +++ b/man/man8/zdb.8 @@ -286,7 +286,7 @@ of the block to read and, optionally, .Pp .Bl -tag -compact -width "b offset" .It Sy b Ar offset -Print block pointer +Print block pointer at hex offset .It Sy c Calculate and display checksums .It Sy d diff --git a/module/zfs/zio.c b/module/zfs/zio.c index ebd7098d6..9dc3c830a 100644 --- a/module/zfs/zio.c +++ b/module/zfs/zio.c @@ -890,35 +890,82 @@ zio_root(spa_t *spa, zio_done_func_t *done, void *private, enum zio_flag flags) return (zio_null(NULL, spa, NULL, done, private, flags)); } -static void -zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held) +static int +zfs_blkptr_verify_log(spa_t *spa, const blkptr_t *bp, + enum blk_verify_flag blk_verify, const char *fmt, ...) +{ + va_list adx; + char buf[256]; + + va_start(adx, fmt); + (void) vsnprintf(buf, sizeof (buf), fmt, adx); + va_end(adx); + + switch (blk_verify) { + case BLK_VERIFY_HALT: + zfs_panic_recover("%s: %s", spa_name(spa), buf); + break; + case BLK_VERIFY_LOG: + zfs_dbgmsg("%s: %s", spa_name(spa), buf); + break; + case BLK_VERIFY_ONLY: + break; + } + + return (1); +} + +/* + * Verify the block pointer fields contain reasonable values. This means + * it only contains known object types, checksum/compression identifiers, + * block sizes within the maximum allowed limits, valid DVAs, etc. + * + * If everything checks out B_TRUE is returned. The zfs_blkptr_verify + * argument controls the behavior when an invalid field is detected. + * + * Modes for zfs_blkptr_verify: + * 1) BLK_VERIFY_ONLY (evaluate the block) + * 2) BLK_VERIFY_LOG (evaluate the block and log problems) + * 3) BLK_VERIFY_HALT (call zfs_panic_recover on error) + */ +boolean_t +zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held, + enum blk_verify_flag blk_verify) { + int errors = 0; + if (!DMU_OT_IS_VALID(BP_GET_TYPE(bp))) { - zfs_panic_recover("blkptr at %p has invalid TYPE %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p has invalid TYPE %llu", bp, (longlong_t)BP_GET_TYPE(bp)); } if (BP_GET_CHECKSUM(bp) >= ZIO_CHECKSUM_FUNCTIONS || BP_GET_CHECKSUM(bp) <= ZIO_CHECKSUM_ON) { - zfs_panic_recover("blkptr at %p has invalid CHECKSUM %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p has invalid CHECKSUM %llu", bp, (longlong_t)BP_GET_CHECKSUM(bp)); } if (BP_GET_COMPRESS(bp) >= ZIO_COMPRESS_FUNCTIONS || BP_GET_COMPRESS(bp) <= ZIO_COMPRESS_ON) { - zfs_panic_recover("blkptr at %p has invalid COMPRESS %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p has invalid COMPRESS %llu", bp, (longlong_t)BP_GET_COMPRESS(bp)); } if (BP_GET_LSIZE(bp) > SPA_MAXBLOCKSIZE) { - zfs_panic_recover("blkptr at %p has invalid LSIZE %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p has invalid LSIZE %llu", bp, (longlong_t)BP_GET_LSIZE(bp)); } if (BP_GET_PSIZE(bp) > SPA_MAXBLOCKSIZE) { - zfs_panic_recover("blkptr at %p has invalid PSIZE %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p has invalid PSIZE %llu", bp, (longlong_t)BP_GET_PSIZE(bp)); } if (BP_IS_EMBEDDED(bp)) { if (BPE_GET_ETYPE(bp) >= NUM_BP_EMBEDDED_TYPES) { - zfs_panic_recover("blkptr at %p has invalid ETYPE %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p has invalid ETYPE %llu", bp, (longlong_t)BPE_GET_ETYPE(bp)); } } @@ -928,7 +975,7 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held) * will be done once the zio is executed in vdev_mirror_map_alloc. */ if (!spa->spa_trust_config) - return; + return (B_TRUE); if (!config_held) spa_config_enter(spa, SCL_VDEV, bp, RW_READER); @@ -946,21 +993,21 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held) uint64_t vdevid = DVA_GET_VDEV(&bp->blk_dva[i]); if (vdevid >= spa->spa_root_vdev->vdev_children) { - zfs_panic_recover("blkptr at %p DVA %u has invalid " - "VDEV %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p DVA %u has invalid VDEV %llu", bp, i, (longlong_t)vdevid); continue; } vdev_t *vd = spa->spa_root_vdev->vdev_child[vdevid]; if (vd == NULL) { - zfs_panic_recover("blkptr at %p DVA %u has invalid " - "VDEV %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p DVA %u has invalid VDEV %llu", bp, i, (longlong_t)vdevid); continue; } if (vd->vdev_ops == &vdev_hole_ops) { - zfs_panic_recover("blkptr at %p DVA %u has hole " - "VDEV %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p DVA %u has hole VDEV %llu", bp, i, (longlong_t)vdevid); continue; } @@ -977,13 +1024,15 @@ zfs_blkptr_verify(spa_t *spa, const blkptr_t *bp, boolean_t config_held) if (BP_IS_GANG(bp)) asize = vdev_psize_to_asize(vd, SPA_GANGBLOCKSIZE); if (offset + asize > vd->vdev_asize) { - zfs_panic_recover("blkptr at %p DVA %u has invalid " - "OFFSET %llu", + errors += zfs_blkptr_verify_log(spa, bp, blk_verify, + "blkptr at %p DVA %u has invalid OFFSET %llu", bp, i, (longlong_t)offset); } } if (!config_held) spa_config_exit(spa, SCL_VDEV, bp); + + return (errors == 0); } boolean_t @@ -1023,7 +1072,8 @@ zio_read(zio_t *pio, spa_t *spa, const blkptr_t *bp, { zio_t *zio; - zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER); + (void) zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER, + BLK_VERIFY_HALT); zio = zio_create(pio, spa, BP_PHYSICAL_BIRTH(bp), bp, data, size, size, done, private, @@ -1116,7 +1166,7 @@ void zio_free(spa_t *spa, uint64_t txg, const blkptr_t *bp) { - zfs_blkptr_verify(spa, bp, B_FALSE); + (void) zfs_blkptr_verify(spa, bp, B_FALSE, BLK_VERIFY_HALT); /* * The check for EMBEDDED is a performance optimization. We @@ -1186,7 +1236,8 @@ zio_claim(zio_t *pio, spa_t *spa, uint64_t txg, const blkptr_t *bp, { zio_t *zio; - zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER); + (void) zfs_blkptr_verify(spa, bp, flags & ZIO_FLAG_CONFIG_WRITER, + BLK_VERIFY_HALT); if (BP_IS_EMBEDDED(bp)) return (zio_null(pio, spa, NULL, NULL, NULL, 0)); diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 72745bfa7..81cca7eed 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -101,7 +101,8 @@ 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_object_range_neg', 'zdb_object_range_pos', 'zdb_objset_id'] + 'zdb_object_range_neg', 'zdb_object_range_pos', 'zdb_display_block', + 'zdb_objset_id'] pre = post = tags = ['functional', 'cli_root', 'zdb'] diff --git a/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am b/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am index e4679ae9f..71fe68436 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am +++ b/tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am @@ -8,6 +8,7 @@ dist_pkgdata_SCRIPTS = \ zdb_006_pos.ksh \ zdb_checksum.ksh \ zdb_decompress.ksh \ - zdb_objset_id.ksh \ zdb_object_range_neg.ksh \ - zdb_object_range_pos.ksh + zdb_object_range_pos.ksh \ + zdb_display_block.ksh \ + zdb_objset_id.ksh diff --git a/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_display_block.ksh b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_display_block.ksh new file mode 100755 index 000000000..c8a52def4 --- /dev/null +++ b/tests/zfs-tests/tests/functional/cli_root/zdb/zdb_display_block.ksh @@ -0,0 +1,128 @@ +#!/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>:b will display the block +# +# 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 of the first L1 block; +# record the first L0 block display; and +# record the 2nd L0 block display. +# 5. Run zdb -R with :bd displays first L0 +# 6. Run zdb -R with :b80d displays 2nd L0 +# 7. Run zdb -R with :db80 displays 2nd L0 +# 8. Run zdb -R with :id flag displays indirect block +# (similar to zdb -ddddddbbbbbb output) +# 9. Run zdb -R with :id flag and .0 vdev +# + + +function cleanup +{ + datasetexists $TESTPOOL && destroy_pool $TESTPOOL +} + +log_assert "Verify zdb -R :b flag (block display) works as expected" +log_onexit cleanup +init_data=$TESTDIR/file1 +write_count=256 +blksize=4096 + +# only read 256 128 byte block pointers in L1 (:i flag) +# 256 x 128 = 32k / 0x8000 +l1_read_size="8000" + +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 + +file_write -d R -o create -w -f $init_data -b $blksize -c $write_count +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 "L1 DVA" |head -n1) +dva=$(sed -Ene 's/^.+DVA\[0\]=<([^>]+)>.*/\1/p' <<< "$output") +log_note "first L1 block $init_data has a DVA of $dva" +output=$(zdb -ddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \ + |grep -m 1 "L0 DVA" |head -n1) +blk_out0=${output##*>} +blk_out0=${blk_out0##+([[:space:]])} + +output=$(zdb -ddddddbbbbbb $TESTPOOL/$TESTFS $obj 2> /dev/null \ + |grep -m 1 "1000 L0 DVA" |head -n1) +blk_out1=${output##*>} +blk_out1=${blk_out1##+([[:space:]])} + +output=$(export ZDB_NO_ZLE=\"true\"; zdb -R $TESTPOOL $dva:bd\ + 2> /dev/null) +output=${output##*>} +output=${output##+([[:space:]])} +if [ "$output" != "$blk_out0" ]; then + log_fail "zdb -R :bd (block 0 display/decompress) failed" +fi + +output=$(export ZDB_NO_ZLE=\"true\"; zdb -R $TESTPOOL $dva:db80\ + 2> /dev/null) +output=${output##*>} +output=${output##+([[:space:]])} +if [ "$output" != "$blk_out1" ]; then + log_fail "zdb -R :db80 (block 1 display/decompress) failed" +fi + +output=$(export ZDB_NO_ZLE=\"true\"; zdb -R $TESTPOOL $dva:b80d\ + 2> /dev/null) +output=${output##*>} +output=${output##+([[:space:]])} +if [ "$output" != "$blk_out1" ]; then + log_fail "zdb -R :b80d (block 1 display/decompress) failed" +fi + +vdev=$(echo "$dva" |awk '{split($0,array,":")} END{print array[1]}') +offset=$(echo "$dva" |awk '{split($0,array,":")} END{print array[2]}') +output=$(export ZDB_NO_ZLE=\"true\";\ + zdb -R $TESTPOOL $vdev:$offset:$l1_read_size:id 2> /dev/null) +block_cnt=$(echo "$output" | grep 'L0' | wc -l) +if [ "$block_cnt" != "$write_count" ]; then + log_fail "zdb -R :id (indirect block display) failed" +fi + +# read from specific half of mirror +vdev="$vdev.0" +log_note "Reading from DVA $vdev:$offset:$l1_read_size" +output=$(export ZDB_NO_ZLE=\"true\";\ + zdb -R $TESTPOOL $vdev:$offset:$l1_read_size:id 2> /dev/null) +block_cnt=$(echo "$output" | grep 'L0' | wc -l) +if [ "$block_cnt" != "$write_count" ]; then + log_fail "zdb -R 0.0:offset:length:id (indirect block display) failed" +fi + +log_pass "zdb -R :b flag (block display) works as expected" |