aboutsummaryrefslogtreecommitdiffstats
path: root/cmd/zstream/zstream_dump.c
diff options
context:
space:
mode:
authorMatthew Ahrens <[email protected]>2020-04-10 10:39:55 -0700
committerGitHub <[email protected]>2020-04-10 10:39:55 -0700
commitc618f87cd2e96438468a391246d63ba1803f35c8 (patch)
treebdd9beb37d34e04c17543d99e10e21980f0f760a /cmd/zstream/zstream_dump.c
parent77f6826b83b7e27f0996f6d192202c36f65e41fd (diff)
Add `zstream redup` command to convert deduplicated send streams
Deduplicated send and receive is deprecated. To ease migration to the new dedup-send-less world, the commit adds a `zstream redup` utility to convert deduplicated send streams to normal streams, so that they can continue to be received indefinitely. The new `zstream` command also replaces the functionality of `zstreamdump`, by way of the `zstream dump` subcommand. The `zstreamdump` command is replaced by a shell script which invokes `zstream dump`. The way that `zstream redup` works under the hood is that as we read the send stream, we build up a hash table which maps from `<GUID, object, offset> -> <file_offset>`. Whenever we see a WRITE record, we add a new entry to the hash table, which indicates where in the stream file to find the WRITE record for this block. (The key is `drr_toguid, drr_object, drr_offset`.) For entries other than WRITE_BYREF, we pass them through unchanged (except for the running checksum, which is recalculated). For WRITE_BYREF records, we change them to WRITE records. We find the referenced WRITE record by looking in the hash table (for the record with key `drr_refguid, drr_refobject, drr_refoffset`), and then reading the record header and payload from the specified offset in the stream file. This is why the stream can not be a pipe. The found WRITE record replaces the WRITE_BYREF record, with its `drr_toguid`, `drr_object`, and `drr_offset` fields changed to be the same as the WRITE_BYREF's (i.e. we are writing the same logical block, but with the data supplied by the previous WRITE record). This algorithm requires memory proportional to the number of WRITE records (same as `zfs send -D`), but the size per WRITE record is relatively low (40 bytes, vs. 72 for `zfs send -D`). A 1TB send stream with 8KB blocks (`recordsize=8k`) would use around 5GB of RAM to "redup". Reviewed-by: Jorgen Lundman <[email protected]> Reviewed-by: Paul Dagnelie <[email protected]> Reviewed-by: Brian Behlendorf <[email protected]> Signed-off-by: Matthew Ahrens <[email protected]> Closes #10124 Closes #10156
Diffstat (limited to 'cmd/zstream/zstream_dump.c')
-rw-r--r--cmd/zstream/zstream_dump.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/cmd/zstream/zstream_dump.c b/cmd/zstream/zstream_dump.c
new file mode 100644
index 000000000..62a1d8272
--- /dev/null
+++ b/cmd/zstream/zstream_dump.c
@@ -0,0 +1,797 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Portions Copyright 2012 Martin Matuska <[email protected]>
+ */
+
+/*
+ * Copyright (c) 2013, 2015 by Delphix. All rights reserved.
+ */
+
+#include <ctype.h>
+#include <libnvpair.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <stddef.h>
+
+#include <sys/dmu.h>
+#include <sys/zfs_ioctl.h>
+#include <sys/zio.h>
+#include <zfs_fletcher.h>
+#include "zstream.h"
+
+/*
+ * If dump mode is enabled, the number of bytes to print per line
+ */
+#define BYTES_PER_LINE 16
+/*
+ * If dump mode is enabled, the number of bytes to group together, separated
+ * by newlines or spaces
+ */
+#define DUMP_GROUPING 4
+
+uint64_t total_stream_len = 0;
+FILE *send_stream = 0;
+boolean_t do_byteswap = B_FALSE;
+boolean_t do_cksum = B_TRUE;
+
+static void *
+safe_malloc(size_t size)
+{
+ void *rv = malloc(size);
+ if (rv == NULL) {
+ (void) fprintf(stderr, "ERROR; failed to allocate %zu bytes\n",
+ size);
+ abort();
+ }
+ return (rv);
+}
+
+/*
+ * ssread - send stream read.
+ *
+ * Read while computing incremental checksum
+ */
+static size_t
+ssread(void *buf, size_t len, zio_cksum_t *cksum)
+{
+ size_t outlen;
+
+ if ((outlen = fread(buf, len, 1, send_stream)) == 0)
+ return (0);
+
+ if (do_cksum) {
+ if (do_byteswap)
+ fletcher_4_incremental_byteswap(buf, len, cksum);
+ else
+ fletcher_4_incremental_native(buf, len, cksum);
+ }
+ total_stream_len += len;
+ return (outlen);
+}
+
+static size_t
+read_hdr(dmu_replay_record_t *drr, zio_cksum_t *cksum)
+{
+ ASSERT3U(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum),
+ ==, sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
+ size_t r = ssread(drr, sizeof (*drr) - sizeof (zio_cksum_t), cksum);
+ if (r == 0)
+ return (0);
+ zio_cksum_t saved_cksum = *cksum;
+ r = ssread(&drr->drr_u.drr_checksum.drr_checksum,
+ sizeof (zio_cksum_t), cksum);
+ if (r == 0)
+ return (0);
+ if (do_cksum &&
+ !ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.drr_checksum.drr_checksum) &&
+ !ZIO_CHECKSUM_EQUAL(saved_cksum,
+ drr->drr_u.drr_checksum.drr_checksum)) {
+ fprintf(stderr, "invalid checksum\n");
+ (void) printf("Incorrect checksum in record header.\n");
+ (void) printf("Expected checksum = %llx/%llx/%llx/%llx\n",
+ (longlong_t)saved_cksum.zc_word[0],
+ (longlong_t)saved_cksum.zc_word[1],
+ (longlong_t)saved_cksum.zc_word[2],
+ (longlong_t)saved_cksum.zc_word[3]);
+ return (0);
+ }
+ return (sizeof (*drr));
+}
+
+/*
+ * Print part of a block in ASCII characters
+ */
+static void
+print_ascii_block(char *subbuf, int length)
+{
+ int i;
+
+ for (i = 0; i < length; i++) {
+ char char_print = isprint(subbuf[i]) ? subbuf[i] : '.';
+ if (i != 0 && i % DUMP_GROUPING == 0) {
+ (void) printf(" ");
+ }
+ (void) printf("%c", char_print);
+ }
+ (void) printf("\n");
+}
+
+/*
+ * print_block - Dump the contents of a modified block to STDOUT
+ *
+ * Assume that buf has capacity evenly divisible by BYTES_PER_LINE
+ */
+static void
+print_block(char *buf, int length)
+{
+ int i;
+ /*
+ * Start printing ASCII characters at a constant offset, after
+ * the hex prints. Leave 3 characters per byte on a line (2 digit
+ * hex number plus 1 space) plus spaces between characters and
+ * groupings.
+ */
+ int ascii_start = BYTES_PER_LINE * 3 +
+ BYTES_PER_LINE / DUMP_GROUPING + 2;
+
+ for (i = 0; i < length; i += BYTES_PER_LINE) {
+ int j;
+ int this_line_length = MIN(BYTES_PER_LINE, length - i);
+ int print_offset = 0;
+
+ for (j = 0; j < this_line_length; j++) {
+ int buf_offset = i + j;
+
+ /*
+ * Separate every DUMP_GROUPING bytes by a space.
+ */
+ if (buf_offset % DUMP_GROUPING == 0) {
+ print_offset += printf(" ");
+ }
+
+ /*
+ * Print the two-digit hex value for this byte.
+ */
+ unsigned char hex_print = buf[buf_offset];
+ print_offset += printf("%02x ", hex_print);
+ }
+
+ (void) printf("%*s", ascii_start - print_offset, " ");
+
+ print_ascii_block(buf + i, this_line_length);
+ }
+}
+
+/*
+ * Print an array of bytes to stdout as hexadecimal characters. str must
+ * have buf_len * 2 + 1 bytes of space.
+ */
+static void
+sprintf_bytes(char *str, uint8_t *buf, uint_t buf_len)
+{
+ int i, n;
+
+ for (i = 0; i < buf_len; i++) {
+ n = sprintf(str, "%02x", buf[i] & 0xff);
+ str += n;
+ }
+
+ str[0] = '\0';
+}
+
+int
+zstream_do_dump(int argc, char *argv[])
+{
+ char *buf = safe_malloc(SPA_MAXBLOCKSIZE);
+ uint64_t drr_record_count[DRR_NUMTYPES] = { 0 };
+ uint64_t total_payload_size = 0;
+ uint64_t total_overhead_size = 0;
+ uint64_t drr_byte_count[DRR_NUMTYPES] = { 0 };
+ char salt[ZIO_DATA_SALT_LEN * 2 + 1];
+ char iv[ZIO_DATA_IV_LEN * 2 + 1];
+ char mac[ZIO_DATA_MAC_LEN * 2 + 1];
+ uint64_t total_records = 0;
+ uint64_t payload_size;
+ dmu_replay_record_t thedrr;
+ dmu_replay_record_t *drr = &thedrr;
+ struct drr_begin *drrb = &thedrr.drr_u.drr_begin;
+ struct drr_end *drre = &thedrr.drr_u.drr_end;
+ struct drr_object *drro = &thedrr.drr_u.drr_object;
+ struct drr_freeobjects *drrfo = &thedrr.drr_u.drr_freeobjects;
+ struct drr_write *drrw = &thedrr.drr_u.drr_write;
+ struct drr_write_byref *drrwbr = &thedrr.drr_u.drr_write_byref;
+ struct drr_free *drrf = &thedrr.drr_u.drr_free;
+ struct drr_spill *drrs = &thedrr.drr_u.drr_spill;
+ struct drr_write_embedded *drrwe = &thedrr.drr_u.drr_write_embedded;
+ struct drr_object_range *drror = &thedrr.drr_u.drr_object_range;
+ struct drr_redact *drrr = &thedrr.drr_u.drr_redact;
+ struct drr_checksum *drrc = &thedrr.drr_u.drr_checksum;
+ int c;
+ boolean_t verbose = B_FALSE;
+ boolean_t very_verbose = B_FALSE;
+ boolean_t first = B_TRUE;
+ /*
+ * dump flag controls whether the contents of any modified data blocks
+ * are printed to the console during processing of the stream. Warning:
+ * for large streams, this can obviously lead to massive prints.
+ */
+ boolean_t dump = B_FALSE;
+ int err;
+ zio_cksum_t zc = { { 0 } };
+ zio_cksum_t pcksum = { { 0 } };
+
+ while ((c = getopt(argc, argv, ":vCd")) != -1) {
+ switch (c) {
+ case 'C':
+ do_cksum = B_FALSE;
+ break;
+ case 'v':
+ if (verbose)
+ very_verbose = B_TRUE;
+ verbose = B_TRUE;
+ break;
+ case 'd':
+ dump = B_TRUE;
+ verbose = B_TRUE;
+ very_verbose = B_TRUE;
+ break;
+ case ':':
+ (void) fprintf(stderr,
+ "missing argument for '%c' option\n", optopt);
+ zstream_usage();
+ break;
+ case '?':
+ (void) fprintf(stderr, "invalid option '%c'\n",
+ optopt);
+ zstream_usage();
+ break;
+ }
+ }
+
+ if (argc > optind) {
+ const char *filename = argv[optind];
+ send_stream = fopen(filename, "r");
+ if (send_stream == NULL) {
+ (void) fprintf(stderr,
+ "Error while opening file '%s': %s\n",
+ filename, strerror(errno));
+ exit(1);
+ }
+ } else {
+ if (isatty(STDIN_FILENO)) {
+ (void) fprintf(stderr,
+ "Error: The send stream is a binary format "
+ "and can not be read from a\n"
+ "terminal. Standard input must be redirected, "
+ "or a file must be\n"
+ "specified as a command-line argument.\n");
+ exit(1);
+ }
+ send_stream = stdin;
+ }
+
+ fletcher_4_init();
+ while (read_hdr(drr, &zc)) {
+
+ /*
+ * If this is the first DMU record being processed, check for
+ * the magic bytes and figure out the endian-ness based on them.
+ */
+ if (first) {
+ if (drrb->drr_magic == BSWAP_64(DMU_BACKUP_MAGIC)) {
+ do_byteswap = B_TRUE;
+ if (do_cksum) {
+ ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
+ /*
+ * recalculate header checksum now
+ * that we know it needs to be
+ * byteswapped.
+ */
+ fletcher_4_incremental_byteswap(drr,
+ sizeof (dmu_replay_record_t), &zc);
+ }
+ } else if (drrb->drr_magic != DMU_BACKUP_MAGIC) {
+ (void) fprintf(stderr, "Invalid stream "
+ "(bad magic number)\n");
+ exit(1);
+ }
+ first = B_FALSE;
+ }
+ if (do_byteswap) {
+ drr->drr_type = BSWAP_32(drr->drr_type);
+ drr->drr_payloadlen =
+ BSWAP_32(drr->drr_payloadlen);
+ }
+
+ /*
+ * At this point, the leading fields of the replay record
+ * (drr_type and drr_payloadlen) have been byte-swapped if
+ * necessary, but the rest of the data structure (the
+ * union of type-specific structures) is still in its
+ * original state.
+ */
+ if (drr->drr_type >= DRR_NUMTYPES) {
+ (void) printf("INVALID record found: type 0x%x\n",
+ drr->drr_type);
+ (void) printf("Aborting.\n");
+ exit(1);
+ }
+
+ drr_record_count[drr->drr_type]++;
+ total_overhead_size += sizeof (*drr);
+ total_records++;
+ payload_size = 0;
+
+ switch (drr->drr_type) {
+ case DRR_BEGIN:
+ if (do_byteswap) {
+ drrb->drr_magic = BSWAP_64(drrb->drr_magic);
+ drrb->drr_versioninfo =
+ BSWAP_64(drrb->drr_versioninfo);
+ drrb->drr_creation_time =
+ BSWAP_64(drrb->drr_creation_time);
+ drrb->drr_type = BSWAP_32(drrb->drr_type);
+ drrb->drr_flags = BSWAP_32(drrb->drr_flags);
+ drrb->drr_toguid = BSWAP_64(drrb->drr_toguid);
+ drrb->drr_fromguid =
+ BSWAP_64(drrb->drr_fromguid);
+ }
+
+ (void) printf("BEGIN record\n");
+ (void) printf("\thdrtype = %lld\n",
+ DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo));
+ (void) printf("\tfeatures = %llx\n",
+ DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo));
+ (void) printf("\tmagic = %llx\n",
+ (u_longlong_t)drrb->drr_magic);
+ (void) printf("\tcreation_time = %llx\n",
+ (u_longlong_t)drrb->drr_creation_time);
+ (void) printf("\ttype = %u\n", drrb->drr_type);
+ (void) printf("\tflags = 0x%x\n", drrb->drr_flags);
+ (void) printf("\ttoguid = %llx\n",
+ (u_longlong_t)drrb->drr_toguid);
+ (void) printf("\tfromguid = %llx\n",
+ (u_longlong_t)drrb->drr_fromguid);
+ (void) printf("\ttoname = %s\n", drrb->drr_toname);
+ if (verbose)
+ (void) printf("\n");
+
+ if (drr->drr_payloadlen != 0) {
+ nvlist_t *nv;
+ int sz = drr->drr_payloadlen;
+
+ if (sz > SPA_MAXBLOCKSIZE) {
+ free(buf);
+ buf = safe_malloc(sz);
+ }
+ (void) ssread(buf, sz, &zc);
+ if (ferror(send_stream))
+ perror("fread");
+ err = nvlist_unpack(buf, sz, &nv, 0);
+ if (err) {
+ perror(strerror(err));
+ } else {
+ nvlist_print(stdout, nv);
+ nvlist_free(nv);
+ }
+ payload_size = sz;
+ }
+ break;
+
+ case DRR_END:
+ if (do_byteswap) {
+ drre->drr_checksum.zc_word[0] =
+ BSWAP_64(drre->drr_checksum.zc_word[0]);
+ drre->drr_checksum.zc_word[1] =
+ BSWAP_64(drre->drr_checksum.zc_word[1]);
+ drre->drr_checksum.zc_word[2] =
+ BSWAP_64(drre->drr_checksum.zc_word[2]);
+ drre->drr_checksum.zc_word[3] =
+ BSWAP_64(drre->drr_checksum.zc_word[3]);
+ }
+ /*
+ * We compare against the *previous* checksum
+ * value, because the stored checksum is of
+ * everything before the DRR_END record.
+ */
+ if (do_cksum && !ZIO_CHECKSUM_EQUAL(drre->drr_checksum,
+ pcksum)) {
+ (void) printf("Expected checksum differs from "
+ "checksum in stream.\n");
+ (void) printf("Expected checksum = "
+ "%llx/%llx/%llx/%llx\n",
+ (long long unsigned int)pcksum.zc_word[0],
+ (long long unsigned int)pcksum.zc_word[1],
+ (long long unsigned int)pcksum.zc_word[2],
+ (long long unsigned int)pcksum.zc_word[3]);
+ }
+ (void) printf("END checksum = %llx/%llx/%llx/%llx\n",
+ (long long unsigned int)
+ drre->drr_checksum.zc_word[0],
+ (long long unsigned int)
+ drre->drr_checksum.zc_word[1],
+ (long long unsigned int)
+ drre->drr_checksum.zc_word[2],
+ (long long unsigned int)
+ drre->drr_checksum.zc_word[3]);
+
+ ZIO_SET_CHECKSUM(&zc, 0, 0, 0, 0);
+ break;
+
+ case DRR_OBJECT:
+ if (do_byteswap) {
+ drro->drr_object = BSWAP_64(drro->drr_object);
+ drro->drr_type = BSWAP_32(drro->drr_type);
+ drro->drr_bonustype =
+ BSWAP_32(drro->drr_bonustype);
+ drro->drr_blksz = BSWAP_32(drro->drr_blksz);
+ drro->drr_bonuslen =
+ BSWAP_32(drro->drr_bonuslen);
+ drro->drr_raw_bonuslen =
+ BSWAP_32(drro->drr_raw_bonuslen);
+ drro->drr_toguid = BSWAP_64(drro->drr_toguid);
+ drro->drr_maxblkid =
+ BSWAP_64(drro->drr_maxblkid);
+ }
+
+ payload_size = DRR_OBJECT_PAYLOAD_SIZE(drro);
+
+ if (verbose) {
+ (void) printf("OBJECT object = %llu type = %u "
+ "bonustype = %u blksz = %u bonuslen = %u "
+ "dn_slots = %u raw_bonuslen = %u "
+ "flags = %u maxblkid = %llu "
+ "indblkshift = %u nlevels = %u "
+ "nblkptr = %u\n",
+ (u_longlong_t)drro->drr_object,
+ drro->drr_type,
+ drro->drr_bonustype,
+ drro->drr_blksz,
+ drro->drr_bonuslen,
+ drro->drr_dn_slots,
+ drro->drr_raw_bonuslen,
+ drro->drr_flags,
+ (u_longlong_t)drro->drr_maxblkid,
+ drro->drr_indblkshift,
+ drro->drr_nlevels,
+ drro->drr_nblkptr);
+ }
+ if (drro->drr_bonuslen > 0) {
+ (void) ssread(buf, payload_size, &zc);
+ if (dump)
+ print_block(buf, payload_size);
+ }
+ break;
+
+ case DRR_FREEOBJECTS:
+ if (do_byteswap) {
+ drrfo->drr_firstobj =
+ BSWAP_64(drrfo->drr_firstobj);
+ drrfo->drr_numobjs =
+ BSWAP_64(drrfo->drr_numobjs);
+ drrfo->drr_toguid = BSWAP_64(drrfo->drr_toguid);
+ }
+ if (verbose) {
+ (void) printf("FREEOBJECTS firstobj = %llu "
+ "numobjs = %llu\n",
+ (u_longlong_t)drrfo->drr_firstobj,
+ (u_longlong_t)drrfo->drr_numobjs);
+ }
+ break;
+
+ case DRR_WRITE:
+ if (do_byteswap) {
+ drrw->drr_object = BSWAP_64(drrw->drr_object);
+ drrw->drr_type = BSWAP_32(drrw->drr_type);
+ drrw->drr_offset = BSWAP_64(drrw->drr_offset);
+ drrw->drr_logical_size =
+ BSWAP_64(drrw->drr_logical_size);
+ drrw->drr_toguid = BSWAP_64(drrw->drr_toguid);
+ drrw->drr_key.ddk_prop =
+ BSWAP_64(drrw->drr_key.ddk_prop);
+ drrw->drr_compressed_size =
+ BSWAP_64(drrw->drr_compressed_size);
+ }
+
+ payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
+
+ /*
+ * If this is verbose and/or dump output,
+ * print info on the modified block
+ */
+ if (verbose) {
+ sprintf_bytes(salt, drrw->drr_salt,
+ ZIO_DATA_SALT_LEN);
+ sprintf_bytes(iv, drrw->drr_iv,
+ ZIO_DATA_IV_LEN);
+ sprintf_bytes(mac, drrw->drr_mac,
+ ZIO_DATA_MAC_LEN);
+
+ (void) printf("WRITE object = %llu type = %u "
+ "checksum type = %u compression type = %u "
+ "flags = %u offset = %llu "
+ "logical_size = %llu "
+ "compressed_size = %llu "
+ "payload_size = %llu props = %llx "
+ "salt = %s iv = %s mac = %s\n",
+ (u_longlong_t)drrw->drr_object,
+ drrw->drr_type,
+ drrw->drr_checksumtype,
+ drrw->drr_compressiontype,
+ drrw->drr_flags,
+ (u_longlong_t)drrw->drr_offset,
+ (u_longlong_t)drrw->drr_logical_size,
+ (u_longlong_t)drrw->drr_compressed_size,
+ (u_longlong_t)payload_size,
+ (u_longlong_t)drrw->drr_key.ddk_prop,
+ salt,
+ iv,
+ mac);
+ }
+
+ /*
+ * Read the contents of the block in from STDIN to buf
+ */
+ (void) ssread(buf, payload_size, &zc);
+ /*
+ * If in dump mode
+ */
+ if (dump) {
+ print_block(buf, payload_size);
+ }
+ break;
+
+ case DRR_WRITE_BYREF:
+ if (do_byteswap) {
+ drrwbr->drr_object =
+ BSWAP_64(drrwbr->drr_object);
+ drrwbr->drr_offset =
+ BSWAP_64(drrwbr->drr_offset);
+ drrwbr->drr_length =
+ BSWAP_64(drrwbr->drr_length);
+ drrwbr->drr_toguid =
+ BSWAP_64(drrwbr->drr_toguid);
+ drrwbr->drr_refguid =
+ BSWAP_64(drrwbr->drr_refguid);
+ drrwbr->drr_refobject =
+ BSWAP_64(drrwbr->drr_refobject);
+ drrwbr->drr_refoffset =
+ BSWAP_64(drrwbr->drr_refoffset);
+ drrwbr->drr_key.ddk_prop =
+ BSWAP_64(drrwbr->drr_key.ddk_prop);
+ }
+ if (verbose) {
+ (void) printf("WRITE_BYREF object = %llu "
+ "checksum type = %u props = %llx "
+ "offset = %llu length = %llu "
+ "toguid = %llx refguid = %llx "
+ "refobject = %llu refoffset = %llu\n",
+ (u_longlong_t)drrwbr->drr_object,
+ drrwbr->drr_checksumtype,
+ (u_longlong_t)drrwbr->drr_key.ddk_prop,
+ (u_longlong_t)drrwbr->drr_offset,
+ (u_longlong_t)drrwbr->drr_length,
+ (u_longlong_t)drrwbr->drr_toguid,
+ (u_longlong_t)drrwbr->drr_refguid,
+ (u_longlong_t)drrwbr->drr_refobject,
+ (u_longlong_t)drrwbr->drr_refoffset);
+ }
+ break;
+
+ case DRR_FREE:
+ if (do_byteswap) {
+ drrf->drr_object = BSWAP_64(drrf->drr_object);
+ drrf->drr_offset = BSWAP_64(drrf->drr_offset);
+ drrf->drr_length = BSWAP_64(drrf->drr_length);
+ }
+ if (verbose) {
+ (void) printf("FREE object = %llu "
+ "offset = %llu length = %lld\n",
+ (u_longlong_t)drrf->drr_object,
+ (u_longlong_t)drrf->drr_offset,
+ (longlong_t)drrf->drr_length);
+ }
+ break;
+ case DRR_SPILL:
+ if (do_byteswap) {
+ drrs->drr_object = BSWAP_64(drrs->drr_object);
+ drrs->drr_length = BSWAP_64(drrs->drr_length);
+ drrs->drr_compressed_size =
+ BSWAP_64(drrs->drr_compressed_size);
+ drrs->drr_type = BSWAP_32(drrs->drr_type);
+ }
+
+ payload_size = DRR_SPILL_PAYLOAD_SIZE(drrs);
+
+ if (verbose) {
+ sprintf_bytes(salt, drrs->drr_salt,
+ ZIO_DATA_SALT_LEN);
+ sprintf_bytes(iv, drrs->drr_iv,
+ ZIO_DATA_IV_LEN);
+ sprintf_bytes(mac, drrs->drr_mac,
+ ZIO_DATA_MAC_LEN);
+
+ (void) printf("SPILL block for object = %llu "
+ "length = %llu flags = %u "
+ "compression type = %u "
+ "compressed_size = %llu "
+ "payload_size = %llu "
+ "salt = %s iv = %s mac = %s\n",
+ (u_longlong_t)drrs->drr_object,
+ (u_longlong_t)drrs->drr_length,
+ drrs->drr_flags,
+ drrs->drr_compressiontype,
+ (u_longlong_t)drrs->drr_compressed_size,
+ (u_longlong_t)payload_size,
+ salt,
+ iv,
+ mac);
+ }
+ (void) ssread(buf, payload_size, &zc);
+ if (dump) {
+ print_block(buf, payload_size);
+ }
+ break;
+ case DRR_WRITE_EMBEDDED:
+ if (do_byteswap) {
+ drrwe->drr_object =
+ BSWAP_64(drrwe->drr_object);
+ drrwe->drr_offset =
+ BSWAP_64(drrwe->drr_offset);
+ drrwe->drr_length =
+ BSWAP_64(drrwe->drr_length);
+ drrwe->drr_toguid =
+ BSWAP_64(drrwe->drr_toguid);
+ drrwe->drr_lsize =
+ BSWAP_32(drrwe->drr_lsize);
+ drrwe->drr_psize =
+ BSWAP_32(drrwe->drr_psize);
+ }
+ if (verbose) {
+ (void) printf("WRITE_EMBEDDED object = %llu "
+ "offset = %llu length = %llu "
+ "toguid = %llx comp = %u etype = %u "
+ "lsize = %u psize = %u\n",
+ (u_longlong_t)drrwe->drr_object,
+ (u_longlong_t)drrwe->drr_offset,
+ (u_longlong_t)drrwe->drr_length,
+ (u_longlong_t)drrwe->drr_toguid,
+ drrwe->drr_compression,
+ drrwe->drr_etype,
+ drrwe->drr_lsize,
+ drrwe->drr_psize);
+ }
+ (void) ssread(buf,
+ P2ROUNDUP(drrwe->drr_psize, 8), &zc);
+ if (dump) {
+ print_block(buf,
+ P2ROUNDUP(drrwe->drr_psize, 8));
+ }
+ payload_size = P2ROUNDUP(drrwe->drr_psize, 8);
+ break;
+ case DRR_OBJECT_RANGE:
+ if (do_byteswap) {
+ drror->drr_firstobj =
+ BSWAP_64(drror->drr_firstobj);
+ drror->drr_numslots =
+ BSWAP_64(drror->drr_numslots);
+ drror->drr_toguid = BSWAP_64(drror->drr_toguid);
+ }
+ if (verbose) {
+ sprintf_bytes(salt, drror->drr_salt,
+ ZIO_DATA_SALT_LEN);
+ sprintf_bytes(iv, drror->drr_iv,
+ ZIO_DATA_IV_LEN);
+ sprintf_bytes(mac, drror->drr_mac,
+ ZIO_DATA_MAC_LEN);
+
+ (void) printf("OBJECT_RANGE firstobj = %llu "
+ "numslots = %llu flags = %u "
+ "salt = %s iv = %s mac = %s\n",
+ (u_longlong_t)drror->drr_firstobj,
+ (u_longlong_t)drror->drr_numslots,
+ drror->drr_flags,
+ salt,
+ iv,
+ mac);
+ }
+ break;
+ case DRR_REDACT:
+ if (do_byteswap) {
+ drrr->drr_object = BSWAP_64(drrr->drr_object);
+ drrr->drr_offset = BSWAP_64(drrr->drr_offset);
+ drrr->drr_length = BSWAP_64(drrr->drr_length);
+ drrr->drr_toguid = BSWAP_64(drrr->drr_toguid);
+ }
+ if (verbose) {
+ (void) printf("REDACT object = %llu offset = "
+ "%llu length = %llu\n",
+ (u_longlong_t)drrr->drr_object,
+ (u_longlong_t)drrr->drr_offset,
+ (u_longlong_t)drrr->drr_length);
+ }
+ break;
+ case DRR_NUMTYPES:
+ /* should never be reached */
+ exit(1);
+ }
+ if (drr->drr_type != DRR_BEGIN && very_verbose) {
+ (void) printf(" checksum = %llx/%llx/%llx/%llx\n",
+ (longlong_t)drrc->drr_checksum.zc_word[0],
+ (longlong_t)drrc->drr_checksum.zc_word[1],
+ (longlong_t)drrc->drr_checksum.zc_word[2],
+ (longlong_t)drrc->drr_checksum.zc_word[3]);
+ }
+ pcksum = zc;
+ drr_byte_count[drr->drr_type] += payload_size;
+ total_payload_size += payload_size;
+ }
+ free(buf);
+ fletcher_4_fini();
+
+ /* Print final summary */
+
+ (void) printf("SUMMARY:\n");
+ (void) printf("\tTotal DRR_BEGIN records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_BEGIN],
+ (u_longlong_t)drr_byte_count[DRR_BEGIN]);
+ (void) printf("\tTotal DRR_END records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_END],
+ (u_longlong_t)drr_byte_count[DRR_END]);
+ (void) printf("\tTotal DRR_OBJECT records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_OBJECT],
+ (u_longlong_t)drr_byte_count[DRR_OBJECT]);
+ (void) printf("\tTotal DRR_FREEOBJECTS records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_FREEOBJECTS],
+ (u_longlong_t)drr_byte_count[DRR_FREEOBJECTS]);
+ (void) printf("\tTotal DRR_WRITE records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_WRITE],
+ (u_longlong_t)drr_byte_count[DRR_WRITE]);
+ (void) printf("\tTotal DRR_WRITE_BYREF records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_WRITE_BYREF],
+ (u_longlong_t)drr_byte_count[DRR_WRITE_BYREF]);
+ (void) printf("\tTotal DRR_WRITE_EMBEDDED records = %lld (%llu "
+ "bytes)\n", (u_longlong_t)drr_record_count[DRR_WRITE_EMBEDDED],
+ (u_longlong_t)drr_byte_count[DRR_WRITE_EMBEDDED]);
+ (void) printf("\tTotal DRR_FREE records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_FREE],
+ (u_longlong_t)drr_byte_count[DRR_FREE]);
+ (void) printf("\tTotal DRR_SPILL records = %lld (%llu bytes)\n",
+ (u_longlong_t)drr_record_count[DRR_SPILL],
+ (u_longlong_t)drr_byte_count[DRR_SPILL]);
+ (void) printf("\tTotal records = %lld\n",
+ (u_longlong_t)total_records);
+ (void) printf("\tTotal payload size = %lld (0x%llx)\n",
+ (u_longlong_t)total_payload_size, (u_longlong_t)total_payload_size);
+ (void) printf("\tTotal header overhead = %lld (0x%llx)\n",
+ (u_longlong_t)total_overhead_size,
+ (u_longlong_t)total_overhead_size);
+ (void) printf("\tTotal stream length = %lld (0x%llx)\n",
+ (u_longlong_t)total_stream_len, (u_longlong_t)total_stream_len);
+ return (0);
+}