]> git.proxmox.com Git - mirror_zfs.git/blobdiff - cmd/zdb/zdb.c
ddt: compare keys, not entries
[mirror_zfs.git] / cmd / zdb / zdb.c
index a17793c2d90e7a92f0f5debb458952f6d328fe3f..35d71012f77f7636f514af6505dc8cfdc7dec3ea 100644 (file)
@@ -33,6 +33,8 @@
  *     under sponsorship from the FreeBSD Foundation.
  * Copyright (c) 2021 Allan Jude
  * Copyright (c) 2021 Toomas Soome <tsoome@me.com>
+ * Copyright (c) 2023, Klara Inc.
+ * Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
  */
 
 #include <stdio.h>
@@ -40,6 +42,7 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include <getopt.h>
+#include <openssl/evp.h>
 #include <sys/zfs_context.h>
 #include <sys/spa.h>
 #include <sys/spa_impl.h>
 #include <sys/dsl_crypt.h>
 #include <sys/dsl_scan.h>
 #include <sys/btree.h>
+#include <sys/brt.h>
+#include <sys/brt_impl.h>
 #include <zfs_comutil.h>
 #include <sys/zstd/zstd.h>
 
 #include <libnvpair.h>
 #include <libzutil.h>
 
+#include <libzdb.h>
+
 #include "zdb.h"
 
-#define        ZDB_COMPRESS_NAME(idx) ((idx) < ZIO_COMPRESS_FUNCTIONS ?        \
-       zio_compress_table[(idx)].ci_name : "UNKNOWN")
-#define        ZDB_CHECKSUM_NAME(idx) ((idx) < ZIO_CHECKSUM_FUNCTIONS ?        \
-       zio_checksum_table[(idx)].ci_name : "UNKNOWN")
-#define        ZDB_OT_TYPE(idx) ((idx) < DMU_OT_NUMTYPES ? (idx) :             \
-       (idx) == DMU_OTN_ZAP_DATA || (idx) == DMU_OTN_ZAP_METADATA ?    \
-       DMU_OT_ZAP_OTHER : \
-       (idx) == DMU_OTN_UINT64_DATA || (idx) == DMU_OTN_UINT64_METADATA ? \
-       DMU_OT_UINT64_OTHER : DMU_OT_NUMTYPES)
-
-/* Some platforms require part of inode IDs to be remapped */
-#ifdef __APPLE__
-#define        ZDB_MAP_OBJECT_ID(obj) INO_XNUTOZFS(obj, 2)
-#else
-#define        ZDB_MAP_OBJECT_ID(obj) (obj)
-#endif
-
-static const char *
-zdb_ot_name(dmu_object_type_t type)
-{
-       if (type < DMU_OT_NUMTYPES)
-               return (dmu_ot[type].ot_name);
-       else if ((type & DMU_OT_NEWTYPE) &&
-           ((type & DMU_OT_BYTESWAP_MASK) < DMU_BSWAP_NUMFUNCS))
-               return (dmu_ot_byteswap[type & DMU_OT_BYTESWAP_MASK].ob_name);
-       else
-               return ("UNKNOWN");
-}
 
 extern int reference_tracking_enable;
 extern int zfs_recover;
-extern unsigned long zfs_arc_meta_min, zfs_arc_meta_limit;
-extern int zfs_vdev_async_read_max_active;
+extern uint_t zfs_vdev_async_read_max_active;
 extern boolean_t spa_load_verify_dryrun;
 extern boolean_t spa_mode_readable_spacemaps;
-extern int zfs_reconstruct_indirect_combinations_max;
+extern uint_t zfs_reconstruct_indirect_combinations_max;
 extern uint_t zfs_btree_verify_intensity;
 
 static const char cmdname[] = "zdb";
@@ -128,39 +106,17 @@ uint8_t dump_opt[256];
 
 typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
 
-uint64_t *zopt_metaslab = NULL;
+static uint64_t *zopt_metaslab = NULL;
 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 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 */
+static uint64_t max_inflight_bytes = 256 * 1024 * 1024; /* 256MB */
 static int leaked_objects = 0;
 static range_tree_t *mos_refd_objs;
 
@@ -171,62 +127,7 @@ static void mos_obj_refd_multiple(uint64_t);
 static int dump_bpobj_cb(void *arg, const blkptr_t *bp, boolean_t free,
     dmu_tx_t *tx);
 
-typedef struct sublivelist_verify {
-       /* FREE's that haven't yet matched to an ALLOC, in one sub-livelist */
-       zfs_btree_t sv_pair;
-
-       /* ALLOC's without a matching FREE, accumulates across sub-livelists */
-       zfs_btree_t sv_leftover;
-} sublivelist_verify_t;
-
-static int
-livelist_compare(const void *larg, const void *rarg)
-{
-       const blkptr_t *l = larg;
-       const blkptr_t *r = rarg;
-
-       /* Sort them according to dva[0] */
-       uint64_t l_dva0_vdev, r_dva0_vdev;
-       l_dva0_vdev = DVA_GET_VDEV(&l->blk_dva[0]);
-       r_dva0_vdev = DVA_GET_VDEV(&r->blk_dva[0]);
-       if (l_dva0_vdev < r_dva0_vdev)
-               return (-1);
-       else if (l_dva0_vdev > r_dva0_vdev)
-               return (+1);
-
-       /* if vdevs are equal, sort by offsets. */
-       uint64_t l_dva0_offset;
-       uint64_t r_dva0_offset;
-       l_dva0_offset = DVA_GET_OFFSET(&l->blk_dva[0]);
-       r_dva0_offset = DVA_GET_OFFSET(&r->blk_dva[0]);
-       if (l_dva0_offset < r_dva0_offset) {
-               return (-1);
-       } else if (l_dva0_offset > r_dva0_offset) {
-               return (+1);
-       }
-
-       /*
-        * Since we're storing blkptrs without cancelling FREE/ALLOC pairs,
-        * it's possible the offsets are equal. In that case, sort by txg
-        */
-       if (l->blk_birth < r->blk_birth) {
-               return (-1);
-       } else if (l->blk_birth > r->blk_birth) {
-               return (+1);
-       }
-       return (0);
-}
-
-typedef struct sublivelist_verify_block {
-       dva_t svb_dva;
 
-       /*
-        * We need this to check if the block marked as allocated
-        * in the livelist was freed (and potentially reallocated)
-        * in the metaslab spacemaps at a later TXG.
-        */
-       uint64_t svb_allocated_txg;
-} sublivelist_verify_block_t;
 
 static void zdb_print_blkptr(const blkptr_t *bp, int flags);
 
@@ -325,7 +226,7 @@ sublivelist_verify_func(void *args, dsl_deadlist_entry_t *dle)
        int err;
        struct sublivelist_verify *sv = args;
 
-       zfs_btree_create(&sv->sv_pair, sublivelist_block_refcnt_compare,
+       zfs_btree_create(&sv->sv_pair, sublivelist_block_refcnt_compare, NULL,
            sizeof (sublivelist_verify_block_refcnt_t));
 
        err = bpobj_iterate_nofree(&dle->dle_bpobj, sublivelist_verify_blkptr,
@@ -389,7 +290,7 @@ sublivelist_verify_lightweight(void *args, dsl_deadlist_entry_t *dle)
 {
        (void) args;
        sublivelist_verify_t sv;
-       zfs_btree_create(&sv.sv_leftover, livelist_block_compare,
+       zfs_btree_create(&sv.sv_leftover, livelist_block_compare, NULL,
            sizeof (sublivelist_verify_block_t));
        int err = sublivelist_verify_func(&sv, dle);
        zfs_btree_clear(&sv.sv_leftover);
@@ -467,7 +368,7 @@ static void
 verify_livelist_allocs(metaslab_verify_t *mv, uint64_t txg,
     uint64_t offset, uint64_t size)
 {
-       sublivelist_verify_block_t svb;
+       sublivelist_verify_block_t svb = {{{0}}};
        DVA_SET_VDEV(&svb.svb_dva, mv->mv_vdid);
        DVA_SET_OFFSET(&svb.svb_dva, offset);
        DVA_SET_ASIZE(&svb.svb_dva, size);
@@ -681,7 +582,7 @@ livelist_metaslab_validate(spa_t *spa)
        (void) printf("Verifying deleted livelist entries\n");
 
        sublivelist_verify_t sv;
-       zfs_btree_create(&sv.sv_leftover, livelist_block_compare,
+       zfs_btree_create(&sv.sv_leftover, livelist_block_compare, NULL,
            sizeof (sublivelist_verify_block_t));
        iterate_deleted_livelists(spa, livelist_verify, &sv);
 
@@ -715,7 +616,7 @@ livelist_metaslab_validate(spa_t *spa)
                        mv.mv_start = m->ms_start;
                        mv.mv_end = m->ms_start + m->ms_size;
                        zfs_btree_create(&mv.mv_livelist_allocs,
-                           livelist_block_compare,
+                           livelist_block_compare, NULL,
                            sizeof (sublivelist_verify_block_t));
 
                        mv_populate_livelist_allocs(&mv, &sv);
@@ -784,23 +685,27 @@ usage(void)
            "Usage:\t%s [-AbcdDFGhikLMPsvXy] [-e [-V] [-p <path> ...]] "
            "[-I <inflight I/Os>]\n"
            "\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\n"
+           "\t\t[-K <key>]\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>] [-K <key>]\n"
            "\t\t[<poolname>[/<dataset | objset id>] [<object | range> ...]\n"
+           "\t%s -B [-e [-V] [-p <path> ...]] [-I <inflight I/Os>]\n"
+           "\t\t[-o <var>=<value>]... [-t <txg>] [-U <cache>] [-x <dumpdir>]\n"
+           "\t\t[-K <key>] <poolname>/<objset id> [<backupflags>]\n"
            "\t%s [-v] <bookmark>\n"
-           "\t%s -C [-A] [-U <cache>]\n"
+           "\t%s -C [-A] [-U <cache>] [<poolname>]\n"
            "\t%s -l [-Aqu] <device>\n"
            "\t%s -m [-AFLPX] [-e [-V] [-p <path> ...]] [-t <txg>] "
            "[-U <cache>]\n\t\t<poolname> [<vdev> [<metaslab> ...]]\n"
-           "\t%s -O <dataset> <path>\n"
-           "\t%s -r <dataset> <path> <destination>\n"
+           "\t%s -O [-K <key>] <dataset> <path>\n"
+           "\t%s -r [-K <key>] <dataset> <path> <destination>\n"
            "\t%s -R [-A] [-e [-V] [-p <path> ...]] [-U <cache>]\n"
            "\t\t<poolname> <vdev>:<offset>:<size>[:<flags>]\n"
            "\t%s -E [-A] word0:word1:...:word15\n"
            "\t%s -S [-AP] [-e [-V] [-p <path> ...]] [-U <cache>] "
            "<poolname>\n\n",
            cmdname, cmdname, cmdname, cmdname, cmdname, cmdname, cmdname,
-           cmdname, cmdname, cmdname, cmdname);
+           cmdname, cmdname, cmdname, cmdname, cmdname);
 
        (void) fprintf(stderr, "    Dataset name must include at least one "
            "separator character '/' or '@'\n");
@@ -823,6 +728,8 @@ usage(void)
        (void) fprintf(stderr, "    Options to control amount of output:\n");
        (void) fprintf(stderr, "        -b --block-stats             "
            "block statistics\n");
+       (void) fprintf(stderr, "        -B --backup                  "
+           "backup stream\n");
        (void) fprintf(stderr, "        -c --checksum                "
            "checksum all metadata (twice for all data) blocks\n");
        (void) fprintf(stderr, "        -C --config                  "
@@ -878,6 +785,8 @@ usage(void)
        (void) fprintf(stderr, "        -I --inflight=INTEGER        "
            "specify the maximum number of checksumming I/Os "
            "[default is 200]\n");
+       (void) fprintf(stderr, "        -K --key=KEY                 "
+           "decryption key for encrypted dataset\n");
        (void) fprintf(stderr, "        -o --option=\"OPTION=INTEGER\" "
            "set global variable to an unsigned 32-bit integer\n");
        (void) fprintf(stderr, "        -p --path==PATH              "
@@ -888,6 +797,8 @@ usage(void)
            "don't print label contents\n");
        (void) fprintf(stderr, "        -t --txg=INTEGER             "
            "highest txg to use when searching for uberblocks\n");
+       (void) fprintf(stderr, "        -T --brt-stats               "
+           "BRT statistics\n");
        (void) fprintf(stderr, "        -u --uberblock               "
            "uberblock\n");
        (void) fprintf(stderr, "        -U --cachefile=PATH          "
@@ -985,7 +896,16 @@ zdb_nicenum(uint64_t num, char *buf, size_t buflen)
        if (dump_opt['P'])
                (void) snprintf(buf, buflen, "%llu", (longlong_t)num);
        else
-               nicenum(num, buf, sizeof (buf));
+               nicenum(num, buf, buflen);
+}
+
+static void
+zdb_nicebytes(uint64_t bytes, char *buf, size_t buflen)
+{
+       if (dump_opt['P'])
+               (void) snprintf(buf, buflen, "%llu", (longlong_t)bytes);
+       else
+               zfs_nicebytes(bytes, buf, buflen);
 }
 
 static const char histo_stars[] = "****************************************";
@@ -1000,11 +920,13 @@ dump_histogram(const uint64_t *histo, int size, int offset)
        uint64_t max = 0;
 
        for (i = 0; i < size; i++) {
+               if (histo[i] == 0)
+                       continue;
                if (histo[i] > max)
                        max = histo[i];
-               if (histo[i] > 0 && i > maxidx)
+               if (i > maxidx)
                        maxidx = i;
-               if (histo[i] > 0 && i < minidx)
+               if (i < minidx)
                        minidx = i;
        }
 
@@ -1137,6 +1059,8 @@ dump_uint64(objset_t *os, uint64_t object, void *data, size_t size)
        }
 
        if (size == 0) {
+               if (data == NULL)
+                       kmem_free(arr, oursize);
                (void) printf("\t\t[]\n");
                return;
        }
@@ -2066,6 +1990,76 @@ dump_all_ddts(spa_t *spa)
        dump_dedup_ratio(&dds_total);
 }
 
+static void
+dump_brt(spa_t *spa)
+{
+       if (!spa_feature_is_enabled(spa, SPA_FEATURE_BLOCK_CLONING)) {
+               printf("BRT: unsupported on this pool\n");
+               return;
+       }
+
+       if (!spa_feature_is_active(spa, SPA_FEATURE_BLOCK_CLONING)) {
+               printf("BRT: empty\n");
+               return;
+       }
+
+       brt_t *brt = spa->spa_brt;
+       VERIFY(brt);
+
+       char count[32], used[32], saved[32];
+       zdb_nicebytes(brt_get_used(spa), used, sizeof (used));
+       zdb_nicebytes(brt_get_saved(spa), saved, sizeof (saved));
+       uint64_t ratio = brt_get_ratio(spa);
+       printf("BRT: used %s; saved %s; ratio %llu.%02llux\n", used, saved,
+           (u_longlong_t)(ratio / 100), (u_longlong_t)(ratio % 100));
+
+       if (dump_opt['T'] < 2)
+               return;
+
+       for (uint64_t vdevid = 0; vdevid < brt->brt_nvdevs; vdevid++) {
+               brt_vdev_t *brtvd = &brt->brt_vdevs[vdevid];
+               if (brtvd == NULL)
+                       continue;
+
+               if (!brtvd->bv_initiated) {
+                       printf("BRT: vdev %" PRIu64 ": empty\n", vdevid);
+                       continue;
+               }
+
+               zdb_nicenum(brtvd->bv_totalcount, count, sizeof (count));
+               zdb_nicebytes(brtvd->bv_usedspace, used, sizeof (used));
+               zdb_nicebytes(brtvd->bv_savedspace, saved, sizeof (saved));
+               printf("BRT: vdev %" PRIu64 ": refcnt %s; used %s; saved %s\n",
+                   vdevid, count, used, saved);
+       }
+
+       if (dump_opt['T'] < 3)
+               return;
+
+       char dva[64];
+       printf("\n%-16s %-10s\n", "DVA", "REFCNT");
+
+       for (uint64_t vdevid = 0; vdevid < brt->brt_nvdevs; vdevid++) {
+               brt_vdev_t *brtvd = &brt->brt_vdevs[vdevid];
+               if (brtvd == NULL || !brtvd->bv_initiated)
+                       continue;
+
+               zap_cursor_t zc;
+               zap_attribute_t za;
+               for (zap_cursor_init(&zc, brt->brt_mos, brtvd->bv_mos_entries);
+                   zap_cursor_retrieve(&zc, &za) == 0;
+                   zap_cursor_advance(&zc)) {
+                       uint64_t offset = *(uint64_t *)za.za_name;
+                       uint64_t refcnt = za.za_first_integer;
+
+                       snprintf(dva, sizeof (dva), "%" PRIu64 ":%llx", vdevid,
+                           (u_longlong_t)offset);
+                       printf("%-16s %-10llu\n", dva, (u_longlong_t)refcnt);
+               }
+               zap_cursor_fini(&zc);
+       }
+}
+
 static void
 dump_dtl_seg(void *arg, uint64_t start, uint64_t size)
 {
@@ -2262,7 +2256,7 @@ static void
 snprintf_zstd_header(spa_t *spa, char *blkbuf, size_t buflen,
     const blkptr_t *bp)
 {
-       abd_t *pabd;
+       static abd_t *pabd = NULL;
        void *buf;
        zio_t *zio;
        zfs_zstdhdr_t zstd_hdr;
@@ -2293,7 +2287,8 @@ snprintf_zstd_header(spa_t *spa, char *blkbuf, size_t buflen,
                return;
        }
 
-       pabd = abd_alloc_for_io(SPA_MAXBLOCKSIZE, B_FALSE);
+       if (!pabd)
+               pabd = abd_alloc_for_io(SPA_MAXBLOCKSIZE, B_FALSE);
        zio = zio_root(spa, NULL, NULL, 0);
 
        /* Decrypt but don't decompress so we can read the compression header */
@@ -2374,7 +2369,8 @@ snprintf_blkptr_compact(char *blkbuf, size_t buflen, const blkptr_t *bp,
                        (void) snprintf(blkbuf + strlen(blkbuf),
                            buflen - strlen(blkbuf), " %s", "FREE");
                (void) snprintf(blkbuf + strlen(blkbuf),
-                   buflen - strlen(blkbuf), " cksum=%llx:%llx:%llx:%llx",
+                   buflen - strlen(blkbuf),
+                   " cksum=%016llx:%016llx:%016llx:%016llx",
                    (u_longlong_t)bp->blk_cksum.zc_word[0],
                    (u_longlong_t)bp->blk_cksum.zc_word[1],
                    (u_longlong_t)bp->blk_cksum.zc_word[2],
@@ -2855,9 +2851,11 @@ dump_bookmarks(objset_t *os, int verbosity)
            zap_cursor_advance(&zc)) {
                char osname[ZFS_MAX_DATASET_NAME_LEN];
                char buf[ZFS_MAX_DATASET_NAME_LEN];
+               int len;
                dmu_objset_name(os, osname);
-               VERIFY3S(0, <=, snprintf(buf, sizeof (buf), "%s#%s", osname,
-                   attr.za_name));
+               len = snprintf(buf, sizeof (buf), "%s#%s", osname,
+                   attr.za_name);
+               VERIFY3S(len, <, ZFS_MAX_DATASET_NAME_LEN);
                (void) dump_bookmark(dp, buf, verbosity >= 5, verbosity >= 6);
        }
        zap_cursor_fini(&zc);
@@ -3017,6 +3015,118 @@ verify_dd_livelist(objset_t *os)
        return (0);
 }
 
+static char *key_material = NULL;
+
+static boolean_t
+zdb_derive_key(dsl_dir_t *dd, uint8_t *key_out)
+{
+       uint64_t keyformat, salt, iters;
+       int i;
+       unsigned char c;
+
+       VERIFY0(zap_lookup(dd->dd_pool->dp_meta_objset, dd->dd_crypto_obj,
+           zfs_prop_to_name(ZFS_PROP_KEYFORMAT), sizeof (uint64_t),
+           1, &keyformat));
+
+       switch (keyformat) {
+       case ZFS_KEYFORMAT_HEX:
+               for (i = 0; i < WRAPPING_KEY_LEN * 2; i += 2) {
+                       if (!isxdigit(key_material[i]) ||
+                           !isxdigit(key_material[i+1]))
+                               return (B_FALSE);
+                       if (sscanf(&key_material[i], "%02hhx", &c) != 1)
+                               return (B_FALSE);
+                       key_out[i / 2] = c;
+               }
+               break;
+
+       case ZFS_KEYFORMAT_PASSPHRASE:
+               VERIFY0(zap_lookup(dd->dd_pool->dp_meta_objset,
+                   dd->dd_crypto_obj, zfs_prop_to_name(ZFS_PROP_PBKDF2_SALT),
+                   sizeof (uint64_t), 1, &salt));
+               VERIFY0(zap_lookup(dd->dd_pool->dp_meta_objset,
+                   dd->dd_crypto_obj, zfs_prop_to_name(ZFS_PROP_PBKDF2_ITERS),
+                   sizeof (uint64_t), 1, &iters));
+
+               if (PKCS5_PBKDF2_HMAC_SHA1(key_material, strlen(key_material),
+                   ((uint8_t *)&salt), sizeof (uint64_t), iters,
+                   WRAPPING_KEY_LEN, key_out) != 1)
+                       return (B_FALSE);
+
+               break;
+
+       default:
+               fatal("no support for key format %u\n",
+                   (unsigned int) keyformat);
+       }
+
+       return (B_TRUE);
+}
+
+static char encroot[ZFS_MAX_DATASET_NAME_LEN];
+static boolean_t key_loaded = B_FALSE;
+
+static void
+zdb_load_key(objset_t *os)
+{
+       dsl_pool_t *dp;
+       dsl_dir_t *dd, *rdd;
+       uint8_t key[WRAPPING_KEY_LEN];
+       uint64_t rddobj;
+       int err;
+
+       dp = spa_get_dsl(os->os_spa);
+       dd = os->os_dsl_dataset->ds_dir;
+
+       dsl_pool_config_enter(dp, FTAG);
+       VERIFY0(zap_lookup(dd->dd_pool->dp_meta_objset, dd->dd_crypto_obj,
+           DSL_CRYPTO_KEY_ROOT_DDOBJ, sizeof (uint64_t), 1, &rddobj));
+       VERIFY0(dsl_dir_hold_obj(dd->dd_pool, rddobj, NULL, FTAG, &rdd));
+       dsl_dir_name(rdd, encroot);
+       dsl_dir_rele(rdd, FTAG);
+
+       if (!zdb_derive_key(dd, key))
+               fatal("couldn't derive encryption key");
+
+       dsl_pool_config_exit(dp, FTAG);
+
+       ASSERT3U(dsl_dataset_get_keystatus(dd), ==, ZFS_KEYSTATUS_UNAVAILABLE);
+
+       dsl_crypto_params_t *dcp;
+       nvlist_t *crypto_args;
+
+       crypto_args = fnvlist_alloc();
+       fnvlist_add_uint8_array(crypto_args, "wkeydata",
+           (uint8_t *)key, WRAPPING_KEY_LEN);
+       VERIFY0(dsl_crypto_params_create_nvlist(DCP_CMD_NONE,
+           NULL, crypto_args, &dcp));
+       err = spa_keystore_load_wkey(encroot, dcp, B_FALSE);
+
+       dsl_crypto_params_free(dcp, (err != 0));
+       fnvlist_free(crypto_args);
+
+       if (err != 0)
+               fatal(
+                   "couldn't load encryption key for %s: %s",
+                   encroot, err == ZFS_ERR_CRYPTO_NOTSUP ?
+                   "crypto params not supported" : strerror(err));
+
+       ASSERT3U(dsl_dataset_get_keystatus(dd), ==, ZFS_KEYSTATUS_AVAILABLE);
+
+       printf("Unlocked encryption root: %s\n", encroot);
+       key_loaded = B_TRUE;
+}
+
+static void
+zdb_unload_key(void)
+{
+       if (!key_loaded)
+               return;
+
+       VERIFY0(spa_keystore_unload_wkey(encroot));
+       key_loaded = B_FALSE;
+}
+
 static avl_tree_t idx_tree;
 static avl_tree_t domain_tree;
 static boolean_t fuid_table_loaded;
@@ -3031,12 +3141,36 @@ open_objset(const char *path, const void *tag, objset_t **osp)
        uint64_t version = 0;
 
        VERIFY3P(sa_os, ==, NULL);
+
        /*
         * We can't own an objset if it's redacted.  Therefore, we do this
         * dance: hold the objset, then acquire a long hold on its dataset, then
         * release the pool (which is held as part of holding the objset).
         */
-       err = dmu_objset_hold(path, tag, osp);
+
+       if (dump_opt['K']) {
+               /* decryption requested, try to load keys */
+               err = dmu_objset_hold(path, tag, osp);
+               if (err != 0) {
+                       (void) fprintf(stderr, "failed to hold dataset "
+                           "'%s': %s\n",
+                           path, strerror(err));
+                       return (err);
+               }
+               dsl_dataset_long_hold(dmu_objset_ds(*osp), tag);
+               dsl_pool_rele(dmu_objset_pool(*osp), tag);
+
+               /* succeeds or dies */
+               zdb_load_key(*osp);
+
+               /* release it all */
+               dsl_dataset_long_rele(dmu_objset_ds(*osp), tag);
+               dsl_dataset_rele(dmu_objset_ds(*osp), tag);
+       }
+
+       int ds_hold_flags = key_loaded ? DS_HOLD_FLAG_DECRYPT : 0;
+
+       err = dmu_objset_hold_flags(path, ds_hold_flags, tag, osp);
        if (err != 0) {
                (void) fprintf(stderr, "failed to hold dataset '%s': %s\n",
                    path, strerror(err));
@@ -3045,7 +3179,8 @@ open_objset(const char *path, const void *tag, objset_t **osp)
        dsl_dataset_long_hold(dmu_objset_ds(*osp), tag);
        dsl_pool_rele(dmu_objset_pool(*osp), tag);
 
-       if (dmu_objset_type(*osp) == DMU_OST_ZFS && !(*osp)->os_encrypted) {
+       if (dmu_objset_type(*osp) == DMU_OST_ZFS &&
+           (key_loaded || !(*osp)->os_encrypted)) {
                (void) zap_lookup(*osp, MASTER_NODE_OBJ, ZPL_VERSION_STR,
                    8, 1, &version);
                if (version >= ZPL_VERSION_SA) {
@@ -3058,13 +3193,14 @@ open_objset(const char *path, const void *tag, objset_t **osp)
                        (void) fprintf(stderr, "sa_setup failed: %s\n",
                            strerror(err));
                        dsl_dataset_long_rele(dmu_objset_ds(*osp), tag);
-                       dsl_dataset_rele(dmu_objset_ds(*osp), tag);
+                       dsl_dataset_rele_flags(dmu_objset_ds(*osp),
+                           ds_hold_flags, tag);
                        *osp = NULL;
                }
        }
        sa_os = *osp;
 
-       return (0);
+       return (err);
 }
 
 static void
@@ -3074,9 +3210,12 @@ close_objset(objset_t *os, const void *tag)
        if (os->os_sa != NULL)
                sa_tear_down(os);
        dsl_dataset_long_rele(dmu_objset_ds(os), tag);
-       dsl_dataset_rele(dmu_objset_ds(os), tag);
+       dsl_dataset_rele_flags(dmu_objset_ds(os),
+           key_loaded ? DS_HOLD_FLAG_DECRYPT : 0, tag);
        sa_attr_table = NULL;
        sa_os = NULL;
+
+       zdb_unload_key();
 }
 
 static void
@@ -3170,13 +3309,22 @@ dump_znode_sa_xattr(sa_handle_t *hdl)
        (void) printf("\tSA xattrs: %d bytes, %d entries\n\n",
            sa_xattr_size, sa_xattr_entries);
        while ((elem = nvlist_next_nvpair(sa_xattr, elem)) != NULL) {
+               boolean_t can_print = !dump_opt['P'];
                uchar_t *value;
                uint_t cnt, idx;
 
                (void) printf("\t\t%s = ", nvpair_name(elem));
                nvpair_value_byte_array(elem, &value, &cnt);
+
                for (idx = 0; idx < cnt; ++idx) {
-                       if (isprint(value[idx]))
+                       if (!isprint(value[idx])) {
+                               can_print = B_FALSE;
+                               break;
+                       }
+               }
+
+               for (idx = 0; idx < cnt; ++idx) {
+                       if (can_print)
                                (void) putchar(value[idx]);
                        else
                                (void) printf("\\%3.3o", value[idx]);
@@ -3458,7 +3606,7 @@ dump_object(objset_t *os, uint64_t object, int verbosity,
                if (error)
                        fatal("dmu_object_info() failed, errno %u", error);
 
-               if (os->os_encrypted &&
+               if (!key_loaded && os->os_encrypted &&
                    DMU_OT_IS_ENCRYPTED(doi.doi_bonus_type)) {
                        error = dnode_hold(os, object, FTAG, &dn);
                        if (error)
@@ -3491,9 +3639,9 @@ dump_object(objset_t *os, uint64_t object, int verbosity,
        zdb_nicenum(doi.doi_physical_blocks_512 << 9, asize, sizeof (asize));
        zdb_nicenum(doi.doi_bonus_size, bonus_size, sizeof (bonus_size));
        zdb_nicenum(doi.doi_dnodesize, dnsize, sizeof (dnsize));
-       (void) sprintf(fill, "%6.2f", 100.0 * doi.doi_fill_count *
-           doi.doi_data_block_size / (object == 0 ? DNODES_PER_BLOCK : 1) /
-           doi.doi_max_offset);
+       (void) snprintf(fill, sizeof (fill), "%6.2f", 100.0 *
+           doi.doi_fill_count * doi.doi_data_block_size / (object == 0 ?
+           DNODES_PER_BLOCK : 1) / doi.doi_max_offset);
 
        aux[0] = '\0';
 
@@ -3555,7 +3703,8 @@ dump_object(objset_t *os, uint64_t object, int verbosity,
                        (void) printf("\t\t(bonus encrypted)\n");
                }
 
-               if (!os->os_encrypted || !DMU_OT_IS_ENCRYPTED(doi.doi_type)) {
+               if (key_loaded ||
+                   (!os->os_encrypted || !DMU_OT_IS_ENCRYPTED(doi.doi_type))) {
                        object_viewer[ZDB_OT_TYPE(doi.doi_type)](os, object,
                            NULL, 0);
                } else {
@@ -3965,6 +4114,11 @@ dump_uberblock(uberblock_t *ub, const char *header, const char *footer)
        }
        (void) printf("\tcheckpoint_txg = %llu\n",
            (u_longlong_t)ub->ub_checkpoint_txg);
+
+       (void) printf("\traidz_reflow state=%u off=%llu\n",
+           (int)RRSS_GET_STATE(ub),
+           (u_longlong_t)RRSS_GET_OFFSET(ub));
+
        (void) printf("%s", footer ? footer : "");
 }
 
@@ -4056,7 +4210,7 @@ collect_nvlist_stats(nvlist_t *nvl, zdb_nvl_stats_t *stats)
 {
        nvlist_t *list, **array;
        nvpair_t *nvp = NULL;
-       char *name;
+       const char *name;
        uint_t i, items;
 
        stats->zns_list_count++;
@@ -4336,26 +4490,26 @@ dump_l2arc_log_entries(uint64_t log_entries,
 }
 
 static void
-dump_l2arc_log_blkptr(l2arc_log_blkptr_t lbps)
+dump_l2arc_log_blkptr(const l2arc_log_blkptr_t *lbps)
 {
-       (void) printf("|\t\tdaddr: %llu\n", (u_longlong_t)lbps.lbp_daddr);
+       (void) printf("|\t\tdaddr: %llu\n", (u_longlong_t)lbps->lbp_daddr);
        (void) printf("|\t\tpayload_asize: %llu\n",
-           (u_longlong_t)lbps.lbp_payload_asize);
+           (u_longlong_t)lbps->lbp_payload_asize);
        (void) printf("|\t\tpayload_start: %llu\n",
-           (u_longlong_t)lbps.lbp_payload_start);
+           (u_longlong_t)lbps->lbp_payload_start);
        (void) printf("|\t\tlsize: %llu\n",
-           (u_longlong_t)L2BLK_GET_LSIZE((&lbps)->lbp_prop));
+           (u_longlong_t)L2BLK_GET_LSIZE(lbps->lbp_prop));
        (void) printf("|\t\tasize: %llu\n",
-           (u_longlong_t)L2BLK_GET_PSIZE((&lbps)->lbp_prop));
+           (u_longlong_t)L2BLK_GET_PSIZE(lbps->lbp_prop));
        (void) printf("|\t\tcompralgo: %llu\n",
-           (u_longlong_t)L2BLK_GET_COMPRESS((&lbps)->lbp_prop));
+           (u_longlong_t)L2BLK_GET_COMPRESS(lbps->lbp_prop));
        (void) printf("|\t\tcksumalgo: %llu\n",
-           (u_longlong_t)L2BLK_GET_CHECKSUM((&lbps)->lbp_prop));
+           (u_longlong_t)L2BLK_GET_CHECKSUM(lbps->lbp_prop));
        (void) printf("|\n\n");
 }
 
 static void
-dump_l2arc_log_blocks(int fd, l2arc_dev_hdr_phys_t l2dhdr,
+dump_l2arc_log_blocks(int fd, const l2arc_dev_hdr_phys_t *l2dhdr,
     l2arc_dev_hdr_phys_t *rebuild)
 {
        l2arc_log_blk_phys_t this_lb;
@@ -4368,13 +4522,13 @@ dump_l2arc_log_blocks(int fd, l2arc_dev_hdr_phys_t l2dhdr,
 
        if (!dump_opt['q'])
                print_l2arc_log_blocks();
-       memcpy(lbps, l2dhdr.dh_start_lbps, sizeof (lbps));
+       memcpy(lbps, l2dhdr->dh_start_lbps, sizeof (lbps));
 
-       dev.l2ad_evict = l2dhdr.dh_evict;
-       dev.l2ad_start = l2dhdr.dh_start;
-       dev.l2ad_end = l2dhdr.dh_end;
+       dev.l2ad_evict = l2dhdr->dh_evict;
+       dev.l2ad_start = l2dhdr->dh_start;
+       dev.l2ad_end = l2dhdr->dh_end;
 
-       if (l2dhdr.dh_start_lbps[0].lbp_daddr == 0) {
+       if (l2dhdr->dh_start_lbps[0].lbp_daddr == 0) {
                /* no log blocks to read */
                if (!dump_opt['q']) {
                        (void) printf("No log blocks to read\n");
@@ -4386,7 +4540,7 @@ dump_l2arc_log_blocks(int fd, l2arc_dev_hdr_phys_t l2dhdr,
                    L2BLK_GET_PSIZE((&lbps[0])->lbp_prop);
        }
 
-       dev.l2ad_first = !!(l2dhdr.dh_flags & L2ARC_DEV_HDR_EVICT_FIRST);
+       dev.l2ad_first = !!(l2dhdr->dh_flags & L2ARC_DEV_HDR_EVICT_FIRST);
 
        for (;;) {
                if (!l2arc_log_blkptr_valid(&dev, &lbps[0]))
@@ -4407,7 +4561,7 @@ dump_l2arc_log_blocks(int fd, l2arc_dev_hdr_phys_t l2dhdr,
                        failed++;
                        if (!dump_opt['q']) {
                                (void) printf("Invalid cksum\n");
-                               dump_l2arc_log_blkptr(lbps[0]);
+                               dump_l2arc_log_blkptr(&lbps[0]);
                        }
                        break;
                }
@@ -4444,11 +4598,11 @@ dump_l2arc_log_blocks(int fd, l2arc_dev_hdr_phys_t l2dhdr,
                        (void) printf("lb[%4llu]\tmagic: %llu\n",
                            (u_longlong_t)rebuild->dh_lb_count,
                            (u_longlong_t)this_lb.lb_magic);
-                       dump_l2arc_log_blkptr(lbps[0]);
+                       dump_l2arc_log_blkptr(&lbps[0]);
                }
 
                if (dump_opt['l'] > 2 && !dump_opt['q'])
-                       dump_l2arc_log_entries(l2dhdr.dh_log_entries,
+                       dump_l2arc_log_entries(l2dhdr->dh_log_entries,
                            this_lb.lb_entries,
                            rebuild->dh_lb_count);
 
@@ -4526,7 +4680,7 @@ dump_l2arc_header(int fd)
                    (u_longlong_t)l2dhdr.dh_trim_state);
        }
 
-       dump_l2arc_log_blocks(fd, l2dhdr, &rebuild);
+       dump_l2arc_log_blocks(fd, &l2dhdr, &rebuild);
        /*
         * The total aligned size of log blocks and the number of log blocks
         * reported in the header of the device may be less than what zdb
@@ -4713,6 +4867,81 @@ dump_path(char *ds, char *path, uint64_t *retobj)
        return (err);
 }
 
+static int
+dump_backup_bytes(objset_t *os, void *buf, int len, void *arg)
+{
+       const char *p = (const char *)buf;
+       ssize_t nwritten;
+
+       (void) os;
+       (void) arg;
+
+       /* Write the data out, handling short writes and signals. */
+       while ((nwritten = write(STDOUT_FILENO, p, len)) < len) {
+               if (nwritten < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       return (errno);
+               }
+               p += nwritten;
+               len -= nwritten;
+       }
+
+       return (0);
+}
+
+static void
+dump_backup(const char *pool, uint64_t objset_id, const char *flagstr)
+{
+       boolean_t embed = B_FALSE;
+       boolean_t large_block = B_FALSE;
+       boolean_t compress = B_FALSE;
+       boolean_t raw = B_FALSE;
+
+       const char *c;
+       for (c = flagstr; c != NULL && *c != '\0'; c++) {
+               switch (*c) {
+                       case 'e':
+                               embed = B_TRUE;
+                               break;
+                       case 'L':
+                               large_block = B_TRUE;
+                               break;
+                       case 'c':
+                               compress = B_TRUE;
+                               break;
+                       case 'w':
+                               raw = B_TRUE;
+                               break;
+                       default:
+                               fprintf(stderr, "dump_backup: invalid flag "
+                                   "'%c'\n", *c);
+                               return;
+               }
+       }
+
+       if (isatty(STDOUT_FILENO)) {
+               fprintf(stderr, "dump_backup: stream cannot be written "
+                   "to a terminal\n");
+               return;
+       }
+
+       offset_t off = 0;
+       dmu_send_outparams_t out = {
+           .dso_outfunc = dump_backup_bytes,
+           .dso_dryrun  = B_FALSE,
+       };
+
+       int err = dmu_send_obj(pool, objset_id, /* fromsnap */0, embed,
+           large_block, compress, raw, /* saved */ B_FALSE, STDOUT_FILENO,
+           &off, &out);
+       if (err != 0) {
+               fprintf(stderr, "dump_backup: dmu_send_obj: %s\n",
+                   strerror(err));
+               return;
+       }
+}
+
 static int
 zdb_copy_object(objset_t *os, uint64_t srcobj, char *destfile)
 {
@@ -4935,7 +5164,7 @@ dump_label(const char *dev)
                        if (nvlist_size(config, &size, NV_ENCODE_XDR) != 0)
                                size = buflen;
 
-                       /* If the device is a cache device clear the header. */
+                       /* If the device is a cache device read the header. */
                        if (!read_l2arc_header) {
                                if (nvlist_lookup_uint64(config,
                                    ZPOOL_CONFIG_POOL_STATE, &l2cache) == 0 &&
@@ -5049,8 +5278,18 @@ dump_one_objset(const char *dsname, void *arg)
            avl_first(&dmu_objset_ds(os)->ds_bookmarks); dbn != NULL;
            dbn = AVL_NEXT(&dmu_objset_ds(os)->ds_bookmarks, dbn)) {
                mos_obj_refd(dbn->dbn_phys.zbm_redaction_obj);
-               if (dbn->dbn_phys.zbm_redaction_obj != 0)
-                       global_feature_count[SPA_FEATURE_REDACTION_BOOKMARKS]++;
+               if (dbn->dbn_phys.zbm_redaction_obj != 0) {
+                       global_feature_count[
+                           SPA_FEATURE_REDACTION_BOOKMARKS]++;
+                       objset_t *mos = os->os_spa->spa_meta_objset;
+                       dnode_t *rl;
+                       VERIFY0(dnode_hold(mos,
+                           dbn->dbn_phys.zbm_redaction_obj, FTAG, &rl));
+                       if (rl->dn_have_spill) {
+                               global_feature_count[
+                                   SPA_FEATURE_REDACTION_LIST_SPILL]++;
+                       }
+               }
                if (dbn->dbn_phys.zbm_flags & ZBM_FLAG_HAS_FBN)
                        global_feature_count[SPA_FEATURE_BOOKMARK_WRITTEN]++;
        }
@@ -5099,12 +5338,20 @@ static const char *zdb_ot_extname[] = {
 #define        ZB_TOTAL        DN_MAX_LEVELS
 #define        SPA_MAX_FOR_16M (SPA_MAXBLOCKSHIFT+1)
 
+typedef struct zdb_brt_entry {
+       dva_t           zbre_dva;
+       uint64_t        zbre_refcount;
+       avl_node_t      zbre_node;
+} zdb_brt_entry_t;
+
 typedef struct zdb_cb {
        zdb_blkstats_t  zcb_type[ZB_TOTAL + 1][ZDB_OT_TOTAL + 1];
        uint64_t        zcb_removing_size;
        uint64_t        zcb_checkpoint_size;
        uint64_t        zcb_dedup_asize;
        uint64_t        zcb_dedup_blocks;
+       uint64_t        zcb_clone_asize;
+       uint64_t        zcb_clone_blocks;
        uint64_t        zcb_psize_count[SPA_MAX_FOR_16M];
        uint64_t        zcb_lsize_count[SPA_MAX_FOR_16M];
        uint64_t        zcb_asize_count[SPA_MAX_FOR_16M];
@@ -5125,6 +5372,8 @@ typedef struct zdb_cb {
        int             zcb_haderrors;
        spa_t           *zcb_spa;
        uint32_t        **zcb_vd_obsolete_counts;
+       avl_tree_t      zcb_brt;
+       boolean_t       zcb_brt_is_active;
 } zdb_cb_t;
 
 /* test if two DVA offsets from same vdev are within the same metaslab */
@@ -5419,6 +5668,45 @@ zdb_count_block(zdb_cb_t *zcb, zilog_t *zilog, const blkptr_t *bp,
        zcb->zcb_asize_len[bin] += BP_GET_ASIZE(bp);
        zcb->zcb_asize_total += BP_GET_ASIZE(bp);
 
+       if (zcb->zcb_brt_is_active && brt_maybe_exists(zcb->zcb_spa, bp)) {
+               /*
+                * Cloned blocks are special. We need to count them, so we can
+                * later uncount them when reporting leaked space, and we must
+                * only claim them them once.
+                *
+                * To do this, we keep our own in-memory BRT. For each block
+                * we haven't seen before, we look it up in the real BRT and
+                * if its there, we note it and its refcount then proceed as
+                * normal. If we see the block again, we count it as a clone
+                * and then give it no further consideration.
+                */
+               zdb_brt_entry_t zbre_search, *zbre;
+               avl_index_t where;
+
+               zbre_search.zbre_dva = bp->blk_dva[0];
+               zbre = avl_find(&zcb->zcb_brt, &zbre_search, &where);
+               if (zbre != NULL) {
+                       zcb->zcb_clone_asize += BP_GET_ASIZE(bp);
+                       zcb->zcb_clone_blocks++;
+
+                       zbre->zbre_refcount--;
+                       if (zbre->zbre_refcount == 0) {
+                               avl_remove(&zcb->zcb_brt, zbre);
+                               umem_free(zbre, sizeof (zdb_brt_entry_t));
+                       }
+                       return;
+               }
+
+               uint64_t crefcnt = brt_entry_get_refcount(zcb->zcb_spa, bp);
+               if (crefcnt > 0) {
+                       zbre = umem_zalloc(sizeof (zdb_brt_entry_t),
+                           UMEM_NOFAIL);
+                       zbre->zbre_dva = bp->blk_dva[0];
+                       zbre->zbre_refcount = crefcnt;
+                       avl_insert(&zcb->zcb_brt, zbre, where);
+               }
+       }
+
        if (dump_opt['L'])
                return;
 
@@ -6421,6 +6709,20 @@ deleted_livelists_dump_mos(spa_t *spa)
        iterate_deleted_livelists(spa, dump_livelist_cb, NULL);
 }
 
+static int
+zdb_brt_entry_compare(const void *zcn1, const void *zcn2)
+{
+       const dva_t *dva1 = &((const zdb_brt_entry_t *)zcn1)->zbre_dva;
+       const dva_t *dva2 = &((const zdb_brt_entry_t *)zcn2)->zbre_dva;
+       int cmp;
+
+       cmp = TREE_CMP(DVA_GET_VDEV(dva1), DVA_GET_VDEV(dva2));
+       if (cmp == 0)
+               cmp = TREE_CMP(DVA_GET_OFFSET(dva1), DVA_GET_OFFSET(dva2));
+
+       return (cmp);
+}
+
 static int
 dump_block_stats(spa_t *spa)
 {
@@ -6435,6 +6737,13 @@ dump_block_stats(spa_t *spa)
 
        zcb = umem_zalloc(sizeof (zdb_cb_t), UMEM_NOFAIL);
 
+       if (spa_feature_is_active(spa, SPA_FEATURE_BLOCK_CLONING)) {
+               avl_create(&zcb->zcb_brt, zdb_brt_entry_compare,
+                   sizeof (zdb_brt_entry_t),
+                   offsetof(zdb_brt_entry_t, zbre_node));
+               zcb->zcb_brt_is_active = B_TRUE;
+       }
+
        (void) printf("\nTraversing all blocks %s%s%s%s%s...\n\n",
            (dump_opt['c'] || !dump_opt['L']) ? "to verify " : "",
            (dump_opt['c'] == 1) ? "metadata " : "",
@@ -6536,7 +6845,8 @@ dump_block_stats(spa_t *spa)
            metaslab_class_get_alloc(spa_special_class(spa)) +
            metaslab_class_get_alloc(spa_dedup_class(spa)) +
            get_unflushed_alloc_space(spa);
-       total_found = tzb->zb_asize - zcb->zcb_dedup_asize +
+       total_found =
+           tzb->zb_asize - zcb->zcb_dedup_asize - zcb->zcb_clone_asize +
            zcb->zcb_removing_size + zcb->zcb_checkpoint_size;
 
        if (total_found == total_alloc && !dump_opt['L']) {
@@ -6577,6 +6887,9 @@ dump_block_stats(spa_t *spa)
            "bp deduped:", (u_longlong_t)zcb->zcb_dedup_asize,
            (u_longlong_t)zcb->zcb_dedup_blocks,
            (double)zcb->zcb_dedup_asize / tzb->zb_asize + 1.0);
+       (void) printf("\t%-16s %14llu    count: %6llu\n",
+           "bp cloned:", (u_longlong_t)zcb->zcb_clone_asize,
+           (u_longlong_t)zcb->zcb_clone_blocks);
        (void) printf("\t%-16s %14llu     used: %5.2f%%\n", "Normal class:",
            (u_longlong_t)norm_alloc, 100.0 * norm_alloc / norm_space);
 
@@ -6659,12 +6972,15 @@ dump_block_stats(spa_t *spa)
 
        if (dump_opt['b'] >= 2) {
                int l, t, level;
+               char csize[32], lsize[32], psize[32], asize[32];
+               char avg[32], gang[32];
                (void) printf("\nBlocks\tLSIZE\tPSIZE\tASIZE"
                    "\t  avg\t comp\t%%Total\tType\n");
 
+               zfs_blkstat_t *mdstats = umem_zalloc(sizeof (zfs_blkstat_t),
+                   UMEM_NOFAIL);
+
                for (t = 0; t <= ZDB_OT_TOTAL; t++) {
-                       char csize[32], lsize[32], psize[32], asize[32];
-                       char avg[32], gang[32];
                        const char *typename;
 
                        /* make sure nicenum has enough space */
@@ -6707,6 +7023,15 @@ dump_block_stats(spa_t *spa)
                                if (zb->zb_asize == 0)
                                        continue;
 
+                               if (level != ZB_TOTAL && t < DMU_OT_NUMTYPES &&
+                                   (level > 0 || DMU_OT_IS_METADATA(t))) {
+                                       mdstats->zb_count += zb->zb_count;
+                                       mdstats->zb_lsize += zb->zb_lsize;
+                                       mdstats->zb_psize += zb->zb_psize;
+                                       mdstats->zb_asize += zb->zb_asize;
+                                       mdstats->zb_gangs += zb->zb_gangs;
+                               }
+
                                if (dump_opt['b'] < 3 && level != ZB_TOTAL)
                                        continue;
 
@@ -6752,11 +7077,31 @@ dump_block_stats(spa_t *spa)
                                }
                        }
                }
+               zdb_nicenum(mdstats->zb_count, csize,
+                   sizeof (csize));
+               zdb_nicenum(mdstats->zb_lsize, lsize,
+                   sizeof (lsize));
+               zdb_nicenum(mdstats->zb_psize, psize,
+                   sizeof (psize));
+               zdb_nicenum(mdstats->zb_asize, asize,
+                   sizeof (asize));
+               zdb_nicenum(mdstats->zb_asize / mdstats->zb_count, avg,
+                   sizeof (avg));
+               zdb_nicenum(mdstats->zb_gangs, gang, sizeof (gang));
+
+               (void) printf("%6s\t%5s\t%5s\t%5s\t%5s"
+                   "\t%5.2f\t%6.2f\t",
+                   csize, lsize, psize, asize, avg,
+                   (double)mdstats->zb_lsize / mdstats->zb_psize,
+                   100.0 * mdstats->zb_asize / tzb->zb_asize);
+               (void) printf("%s\n", "Metadata Total");
 
                /* Output a table summarizing block sizes in the pool */
                if (dump_opt['b'] >= 2) {
                        dump_size_histograms(zcb);
                }
+
+               umem_free(mdstats, sizeof (zfs_blkstat_t));
        }
 
        (void) printf("\n");
@@ -6776,6 +7121,7 @@ dump_block_stats(spa_t *spa)
 }
 
 typedef struct zdb_ddt_entry {
+       /* key must be first for ddt_key_compare */
        ddt_key_t       zdde_key;
        uint64_t        zdde_ref_blocks;
        uint64_t        zdde_ref_lsize;
@@ -6836,7 +7182,7 @@ dump_simulated_ddt(spa_t *spa)
        ddt_histogram_t ddh_total = {{{0}}};
        ddt_stat_t dds_total = {0};
 
-       avl_create(&t, ddt_entry_compare,
+       avl_create(&t, ddt_key_compare,
            sizeof (zdb_ddt_entry_t), offsetof(zdb_ddt_entry_t, zdde_node));
 
        spa_config_enter(spa, SCL_CONFIG, FTAG, RW_READER);
@@ -7062,8 +7408,11 @@ import_checkpointed_state(char *target, nvlist_t *cfg, char **new_path)
                freecfg = B_TRUE;
        }
 
-       if (asprintf(&bogus_name, "%s%s", poolname, BOGUS_SUFFIX) == -1)
+       if (asprintf(&bogus_name, "%s%s", poolname, BOGUS_SUFFIX) == -1) {
+               if (target != poolname)
+                       free(poolname);
                return (NULL);
+       }
        fnvlist_add_string(cfg, ZPOOL_CONFIG_POOL_NAME, bogus_name);
 
        error = spa_import(bogus_name, cfg, NULL,
@@ -7078,6 +7427,7 @@ import_checkpointed_state(char *target, nvlist_t *cfg, char **new_path)
 
        if (new_path != NULL && path_start != NULL) {
                if (asprintf(new_path, "%s%s", bogus_name, path_start) == -1) {
+                       free(bogus_name);
                        if (path_start != NULL)
                                free(poolname);
                        return (NULL);
@@ -7473,6 +7823,9 @@ mos_leak_vdev(vdev_t *vd)
                mos_obj_refd(space_map_object(ms->ms_sm));
        }
 
+       if (vd->vdev_root_zap != 0)
+               mos_obj_refd(vd->vdev_root_zap);
+
        if (vd->vdev_top_zap != 0) {
                mos_obj_refd(vd->vdev_top_zap);
                mos_leak_vdev_top_zap(vd);
@@ -7500,6 +7853,19 @@ mos_leak_log_spacemaps(spa_t *spa)
                mos_obj_refd(sls->sls_sm_obj);
 }
 
+static void
+errorlog_count_refd(objset_t *mos, uint64_t errlog)
+{
+       zap_cursor_t zc;
+       zap_attribute_t za;
+       for (zap_cursor_init(&zc, mos, errlog);
+           zap_cursor_retrieve(&zc, &za) == 0;
+           zap_cursor_advance(&zc)) {
+               mos_obj_refd(za.za_first_integer);
+       }
+       zap_cursor_fini(&zc);
+}
+
 static int
 dump_mos_leaks(spa_t *spa)
 {
@@ -7520,6 +7886,12 @@ dump_mos_leaks(spa_t *spa)
        mos_obj_refd(spa->spa_history);
        mos_obj_refd(spa->spa_errlog_last);
        mos_obj_refd(spa->spa_errlog_scrub);
+
+       if (spa_feature_is_enabled(spa, SPA_FEATURE_HEAD_ERRLOG)) {
+               errorlog_count_refd(mos, spa->spa_errlog_last);
+               errorlog_count_refd(mos, spa->spa_errlog_scrub);
+       }
+
        mos_obj_refd(spa->spa_all_vdev_zaps);
        mos_obj_refd(spa->spa_dsl_pool->dp_bptree_obj);
        mos_obj_refd(spa->spa_dsl_pool->dp_tmp_userrefs_obj);
@@ -7581,6 +7953,17 @@ dump_mos_leaks(spa_t *spa)
                }
        }
 
+       if (spa->spa_brt != NULL) {
+               brt_t *brt = spa->spa_brt;
+               for (uint64_t vdevid = 0; vdevid < brt->brt_nvdevs; vdevid++) {
+                       brt_vdev_t *brtvd = &brt->brt_vdevs[vdevid];
+                       if (brtvd != NULL && brtvd->bv_initiated) {
+                               mos_obj_refd(brtvd->bv_mos_brtvdev);
+                               mos_obj_refd(brtvd->bv_mos_entries);
+                       }
+               }
+       }
+
        /*
         * Visit all allocated objects and make sure they are referenced.
         */
@@ -7717,6 +8100,9 @@ dump_zpool(spa_t *spa)
        if (dump_opt['D'])
                dump_all_ddts(spa);
 
+       if (dump_opt['T'])
+               dump_brt(spa);
+
        if (dump_opt['d'] > 2 || dump_opt['m'])
                dump_metaslabs(spa);
        if (dump_opt['M'])
@@ -7759,6 +8145,7 @@ dump_zpool(spa_t *spa)
                for (spa_feature_t f = 0; f < SPA_FEATURES; f++)
                        global_feature_count[f] = UINT64_MAX;
                global_feature_count[SPA_FEATURE_REDACTION_BOOKMARKS] = 0;
+               global_feature_count[SPA_FEATURE_REDACTION_LIST_SPILL] = 0;
                global_feature_count[SPA_FEATURE_BOOKMARK_WRITTEN] = 0;
                global_feature_count[SPA_FEATURE_LIVELIST] = 0;
 
@@ -8009,11 +8396,45 @@ zdb_parse_block_sizes(char *sizes, uint64_t *lsize, uint64_t *psize)
 #define        ZIO_COMPRESS_MASK(alg)  (1ULL << (ZIO_COMPRESS_##alg))
 
 static boolean_t
+try_decompress_block(abd_t *pabd, uint64_t lsize, uint64_t psize,
+    int flags, int cfunc, void *lbuf, void *lbuf2)
+{
+       if (flags & ZDB_FLAG_VERBOSE) {
+               (void) fprintf(stderr,
+                   "Trying %05llx -> %05llx (%s)\n",
+                   (u_longlong_t)psize,
+                   (u_longlong_t)lsize,
+                   zio_compress_table[cfunc].ci_name);
+       }
+
+       /*
+        * We set lbuf to all zeros and lbuf2 to all
+        * ones, then decompress to both buffers and
+        * compare their contents. This way we can
+        * know if decompression filled exactly to
+        * lsize or if it left some bytes unwritten.
+        */
+
+       memset(lbuf, 0x00, lsize);
+       memset(lbuf2, 0xff, lsize);
+
+       if (zio_decompress_data(cfunc, pabd,
+           lbuf, psize, lsize, NULL) == 0 &&
+           zio_decompress_data(cfunc, pabd,
+           lbuf2, psize, lsize, NULL) == 0 &&
+           memcmp(lbuf, lbuf2, lsize) == 0)
+               return (B_TRUE);
+       return (B_FALSE);
+}
+
+static uint64_t
 zdb_decompress_block(abd_t *pabd, void *buf, void *lbuf, uint64_t lsize,
     uint64_t psize, int flags)
 {
        (void) buf;
-       boolean_t exceeded = B_FALSE;
+       uint64_t orig_lsize = lsize;
+       boolean_t tryzle = ((getenv("ZDB_NO_ZLE") == NULL));
+       boolean_t found = B_FALSE;
        /*
         * We don't know how the data was compressed, so just try
         * every decompress function at every inflated blocksize.
@@ -8024,10 +8445,18 @@ zdb_decompress_block(abd_t *pabd, void *buf, void *lbuf, uint64_t lsize,
        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);
+           ZIO_COMPRESS_MASK(ZLE);
        *cfuncp++ = ZIO_COMPRESS_LZ4;
        *cfuncp++ = ZIO_COMPRESS_LZJB;
        mask |= ZIO_COMPRESS_MASK(LZ4) | ZIO_COMPRESS_MASK(LZJB);
+       /*
+        * Every gzip level has the same decompressor, no need to
+        * run it 9 times per bruteforce attempt.
+        */
+       mask |= ZIO_COMPRESS_MASK(GZIP_2) | ZIO_COMPRESS_MASK(GZIP_3);
+       mask |= ZIO_COMPRESS_MASK(GZIP_4) | ZIO_COMPRESS_MASK(GZIP_5);
+       mask |= ZIO_COMPRESS_MASK(GZIP_6) | ZIO_COMPRESS_MASK(GZIP_7);
+       mask |= ZIO_COMPRESS_MASK(GZIP_8) | ZIO_COMPRESS_MASK(GZIP_9);
        for (int c = 0; c < ZIO_COMPRESS_FUNCTIONS; c++)
                if (((1ULL << c) & mask) == 0)
                        *cfuncp++ = c;
@@ -8043,46 +8472,38 @@ zdb_decompress_block(abd_t *pabd, void *buf, void *lbuf, uint64_t lsize,
                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, NULL) == 0 &&
-                           zio_decompress_data(*cfuncp, pabd,
-                           lbuf2, psize, lsize, NULL) == 0 &&
-                           memcmp(lbuf, lbuf2, lsize) == 0)
+                       if (try_decompress_block(pabd, lsize, psize, flags,
+                           *cfuncp, lbuf, lbuf2)) {
+                               found = B_TRUE;
                                break;
+                       }
                }
                if (*cfuncp != 0)
                        break;
        }
+       if (!found && tryzle) {
+               for (lsize = orig_lsize; lsize <= maxlsize;
+                   lsize += SPA_MINBLOCKSIZE) {
+                       if (try_decompress_block(pabd, lsize, psize, flags,
+                           ZIO_COMPRESS_ZLE, lbuf, lbuf2)) {
+                               *cfuncp = ZIO_COMPRESS_ZLE;
+                               found = B_TRUE;
+                               break;
+                       }
+               }
+       }
        umem_free(lbuf2, SPA_MAXBLOCKSIZE);
 
-       if (lsize > maxlsize) {
-               exceeded = B_TRUE;
-       }
        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);
+       return (lsize > maxlsize ? -1 : lsize);
 }
 
 /*
@@ -8198,8 +8619,7 @@ zdb_read_block(char *thing, spa_t *spa)
        vd = zdb_vdev_lookup(spa->spa_root_vdev, vdev);
        if (vd == NULL) {
                (void) printf("***Invalid vdev: %s\n", vdev);
-               free(dup);
-               return;
+               goto done;
        } else {
                if (vd->vdev_path)
                        (void) fprintf(stderr, "Found vdev: %s\n",
@@ -8246,9 +8666,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_PROPAGATE |
-                   ZIO_FLAG_DONT_RETRY | ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW |
-                   ZIO_FLAG_OPTIONAL, NULL, NULL));
+                   ZIO_FLAG_DONT_PROPAGATE | ZIO_FLAG_DONT_RETRY |
+                   ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW | ZIO_FLAG_OPTIONAL,
+                   NULL, NULL));
        }
 
        error = zio_wait(zio);
@@ -8262,9 +8682,9 @@ zdb_read_block(char *thing, spa_t *spa)
        uint64_t orig_lsize = lsize;
        buf = lbuf;
        if (flags & ZDB_FLAG_DECOMPRESS) {
-               boolean_t failed = zdb_decompress_block(pabd, buf, lbuf,
+               lsize = zdb_decompress_block(pabd, buf, lbuf,
                    lsize, psize, flags);
-               if (failed) {
+               if (lsize == -1) {
                        (void) printf("Decompress of %s failed\n", thing);
                        goto out;
                }
@@ -8280,17 +8700,17 @@ zdb_read_block(char *thing, spa_t *spa)
            !(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) {
+               if (zfs_blkptr_verify(spa, b,
+                   BLK_CONFIG_NEEDED, 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,
+                       lsize = 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) {
+                       if (lsize == -1 || zfs_blkptr_verify(spa, b,
+                           BLK_CONFIG_NEEDED, BLK_VERIFY_LOG) == B_FALSE) {
                                printf("invalid block pointer at this DVA\n");
                                goto out;
                        }
@@ -8330,8 +8750,6 @@ zdb_read_block(char *thing, spa_t *spa)
                        BP_SET_CHECKSUM(bp, ck);
                        spa_config_enter(spa, SCL_STATE, FTAG, RW_READER);
                        czio = zio_root(spa, NULL, NULL, ZIO_FLAG_CANFAIL);
-                       czio->io_bp = bp;
-
                        if (vd == vd->vdev_top) {
                                zio_nowait(zio_read(czio, spa, bp, pabd, psize,
                                    NULL, NULL,
@@ -8342,7 +8760,6 @@ zdb_read_block(char *thing, spa_t *spa)
                                zio_nowait(zio_vdev_child_io(czio, bp, vd,
                                    offset, pabd, psize, ZIO_TYPE_READ,
                                    ZIO_PRIORITY_SYNC_READ,
-                                   ZIO_FLAG_DONT_CACHE |
                                    ZIO_FLAG_DONT_PROPAGATE |
                                    ZIO_FLAG_DONT_RETRY |
                                    ZIO_FLAG_CANFAIL | ZIO_FLAG_RAW |
@@ -8351,12 +8768,15 @@ zdb_read_block(char *thing, spa_t *spa)
                        }
                        error = zio_wait(czio);
                        if (error == 0 || error == ECKSUM) {
-                               zio_t *ck_zio = zio_root(spa, NULL, NULL, 0);
+                               zio_t *ck_zio = zio_null(NULL, spa, NULL,
+                                   NULL, NULL, 0);
                                ck_zio->io_offset =
                                    DVA_GET_OFFSET(&bp->blk_dva[0]);
                                ck_zio->io_bp = bp;
                                zio_checksum_compute(ck_zio, ck, pabd, lsize);
-                               printf("%12s\tcksum=%llx:%llx:%llx:%llx\n",
+                               printf(
+                                   "%12s\t"
+                                   "cksum=%016llx:%016llx:%016llx:%016llx\n",
                                    zio_checksum_table[ck].ci_name,
                                    (u_longlong_t)bp->blk_cksum.zc_word[0],
                                    (u_longlong_t)bp->blk_cksum.zc_word[1],
@@ -8474,6 +8894,7 @@ main(int argc, char **argv)
        struct option long_options[] = {
                {"ignore-assertions",   no_argument,            NULL, 'A'},
                {"block-stats",         no_argument,            NULL, 'b'},
+               {"backup",              no_argument,            NULL, 'B'},
                {"checksum",            no_argument,            NULL, 'c'},
                {"config",              no_argument,            NULL, 'C'},
                {"datasets",            no_argument,            NULL, 'd'},
@@ -8486,6 +8907,7 @@ main(int argc, char **argv)
                {"intent-logs",         no_argument,            NULL, 'i'},
                {"inflight",            required_argument,      NULL, 'I'},
                {"checkpointed-state",  no_argument,            NULL, 'k'},
+               {"key",                 required_argument,      NULL, 'K'},
                {"label",               no_argument,            NULL, 'l'},
                {"disable-leak-tracking",       no_argument,    NULL, 'L'},
                {"metaslabs",           no_argument,            NULL, 'm'},
@@ -8501,6 +8923,7 @@ main(int argc, char **argv)
                {"io-stats",            no_argument,            NULL, 's'},
                {"simulate-dedup",      no_argument,            NULL, 'S'},
                {"txg",                 required_argument,      NULL, 't'},
+               {"brt-stats",           no_argument,            NULL, 'T'},
                {"uberblock",           no_argument,            NULL, 'u'},
                {"cachefile",           required_argument,      NULL, 'U'},
                {"verbose",             no_argument,            NULL, 'v'},
@@ -8514,10 +8937,11 @@ main(int argc, char **argv)
        };
 
        while ((c = getopt_long(argc, argv,
-           "AbcCdDeEFGhiI:klLmMNo:Op:PqrRsSt:uU:vVx:XYyZ",
+           "AbBcCdDeEFGhiI:kK:lLmMNo:Op:PqrRsSt:TuU:vVx:XYyZ",
            long_options, NULL)) != -1) {
                switch (c) {
                case 'b':
+               case 'B':
                case 'c':
                case 'C':
                case 'd':
@@ -8535,6 +8959,7 @@ main(int argc, char **argv)
                case 'R':
                case 's':
                case 'S':
+               case 'T':
                case 'u':
                case 'y':
                case 'Z':
@@ -8565,6 +8990,12 @@ main(int argc, char **argv)
                                usage();
                        }
                        break;
+               case 'K':
+                       dump_opt[c]++;
+                       key_material = strdup(optarg);
+                       /* redact key material in process table */
+                       while (*optarg != '\0') { *optarg++ = '*'; }
+                       break;
                case 'o':
                        error = set_global_var(optarg);
                        if (error != 0)
@@ -8626,8 +9057,8 @@ main(int argc, char **argv)
         * ZDB does not typically re-read blocks; therefore limit the ARC
         * to 256 MB, which can be used entirely for metadata.
         */
-       zfs_arc_min = zfs_arc_meta_min = 2ULL << SPA_MAXBLOCKSHIFT;
-       zfs_arc_max = zfs_arc_meta_limit = 256 * 1024 * 1024;
+       zfs_arc_min = 2ULL << SPA_MAXBLOCKSHIFT;
+       zfs_arc_max = 256 * 1024 * 1024;
 #endif
 
        /*
@@ -8659,7 +9090,7 @@ main(int argc, char **argv)
                verbose = MAX(verbose, 1);
 
        for (c = 0; c < 256; c++) {
-               if (dump_all && strchr("AeEFklLNOPrRSXy", c) == NULL)
+               if (dump_all && strchr("ABeEFkKlLNOPrRSXy", c) == NULL)
                        dump_opt[c] = 1;
                if (dump_opt[c])
                        dump_opt[c] += verbose;
@@ -8691,22 +9122,6 @@ main(int argc, char **argv)
        if (dump_opt['l'])
                return (dump_label(argv[0]));
 
-       if (dump_opt['O']) {
-               if (argc != 2)
-                       usage();
-               dump_opt['v'] = verbose + 3;
-               return (dump_path(argv[0], argv[1], NULL));
-       }
-       if (dump_opt['r']) {
-               target_is_spa = B_FALSE;
-               if (argc != 3)
-                       usage();
-               dump_opt['v'] = verbose;
-               error = dump_path(argv[0], argv[1], &object);
-               if (error != 0)
-                       fatal("internal error: %s", strerror(error));
-       }
-
        if (dump_opt['X'] || dump_opt['F'])
                rewind = ZPOOL_DO_REWIND |
                    (dump_opt['X'] ? ZPOOL_EXTREME_REWIND : 0);
@@ -8773,8 +9188,12 @@ main(int argc, char **argv)
                args.path = searchdirs;
                args.can_be_active = B_TRUE;
 
-               error = zpool_find_config(NULL, target_pool, &cfg, &args,
-                   &libzpool_config_ops);
+               libpc_handle_t lpch = {
+                       .lpc_lib_handle = NULL,
+                       .lpc_ops = &libzpool_config_ops,
+                       .lpc_printerr = B_TRUE
+               };
+               error = zpool_find_config(&lpch, target_pool, &cfg, &args);
 
                if (error == 0) {
 
@@ -8803,6 +9222,29 @@ main(int argc, char **argv)
                searchdirs = NULL;
        }
 
+       /*
+        * We need to make sure to process -O option or call
+        * dump_path after the -e option has been processed,
+        * which imports the pool to the namespace if it's
+        * not in the cachefile.
+        */
+       if (dump_opt['O']) {
+               if (argc != 2)
+                       usage();
+               dump_opt['v'] = verbose + 3;
+               return (dump_path(argv[0], argv[1], NULL));
+       }
+
+       if (dump_opt['r']) {
+               target_is_spa = B_FALSE;
+               if (argc != 3)
+                       usage();
+               dump_opt['v'] = verbose;
+               error = dump_path(argv[0], argv[1], &object);
+               if (error != 0)
+                       fatal("internal error: %s", strerror(error));
+       }
+
        /*
         * import_checkpointed_state makes the assumption that the
         * target pool that we pass it is already part of the spa
@@ -8841,7 +9283,8 @@ main(int argc, char **argv)
                                    checkpoint_pool, error);
                        }
 
-               } else if (target_is_spa || dump_opt['R'] || objset_id == 0) {
+               } else if (target_is_spa || dump_opt['R'] || dump_opt['B'] ||
+                   objset_id == 0) {
                        zdb_set_skip_mmp(target);
                        error = spa_open_rewind(target, &spa, FTAG, policy,
                            NULL);
@@ -8977,7 +9420,10 @@ retry_lookup:
                                            strerror(errno));
                        }
                }
-               if (os != NULL) {
+               if (dump_opt['B']) {
+                       dump_backup(target, objset_id,
+                           argc > 0 ? argv[0] : NULL);
+               } else if (os != NULL) {
                        dump_objset(os);
                } else if (zopt_object_args > 0 && !dump_opt['m']) {
                        dump_objset(spa->spa_meta_objset);