]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Fix zdb -R with 'b' flag
authorPaul Zuchowski <31706010+PaulZ-98@users.noreply.github.com>
Mon, 10 Feb 2020 22:00:05 +0000 (18:00 -0400)
committerGitHub <noreply@github.com>
Mon, 10 Feb 2020 22:00:05 +0000 (14:00 -0800)
zdb -R :b fails due to the indirect block being compressed,
and the 'b' and 'd' flag not working in tandem when specified.
Fix the flag parsing code and create a zfs test for zdb -R
block display.  Also fix the zio flags where the dotted notation
for the vdev portion of DVA (i.e. 0.0:offset:length) fails.

Reviewed-by: Ryan Moeller <ryan@iXsystems.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Signed-off-by: Paul Zuchowski <pzuchowski@datto.com>
Closes #9640
Closes #9729

cmd/zdb/zdb.c
include/sys/zio.h
man/man8/zdb.8
module/zfs/zio.c
tests/runfiles/common.run
tests/zfs-tests/tests/functional/cli_root/zdb/Makefile.am
tests/zfs-tests/tests/functional/cli_root/zdb/zdb_display_block.ksh [new file with mode: 0755]

index f00df5d57a92b4447d7f2a3784c36b759e8cee02..d6d8b91a9e2be8b7c2f7a415a2486cb4fc7ca3bf 100644 (file)
@@ -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);
 }
 
index f78b02fd50d3fabf7d2a8d9bee662fa8fc175e9b..368d8c14951c724c5969b3259eecfbc1b8252029 100644 (file)
@@ -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.
  */
index a7ab6b8ba42bc11eef84a47bb1bc1de7a3b3dc1a..8506d54780ff87c14c6e86ae8d0e354a4f28e8ab 100644 (file)
@@ -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
index ebd7098d69f7c7e8d48bd006978da952ca5383b9..9dc3c830a0223dc543f046da2375864c02c6d021 100644 (file)
@@ -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));
index 72745bfa7b210cf8cfa3702fe86fb6b78132d2ee..81cca7eed5a0fa52cf979e9271febc580856c91c 100644 (file)
@@ -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']
index e4679ae9faa736b32014143f9a55df19dbb74255..71fe684367a6b32f4b3dfeaae81915ef6d3c1e83 100644 (file)
@@ -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 (executable)
index 0000000..c8a52de
--- /dev/null
@@ -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"