1a0b956f5SMartin Matuska /* 2a0b956f5SMartin Matuska * CDDL HEADER START 3a0b956f5SMartin Matuska * 4a0b956f5SMartin Matuska * The contents of this file are subject to the terms of the 5a0b956f5SMartin Matuska * Common Development and Distribution License (the "License"). 6a0b956f5SMartin Matuska * You may not use this file except in compliance with the License. 7a0b956f5SMartin Matuska * 8a0b956f5SMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9271171e0SMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10a0b956f5SMartin Matuska * See the License for the specific language governing permissions 11a0b956f5SMartin Matuska * and limitations under the License. 12a0b956f5SMartin Matuska * 13a0b956f5SMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each 14a0b956f5SMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15a0b956f5SMartin Matuska * If applicable, add the following below this CDDL HEADER, with the 16a0b956f5SMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying 17a0b956f5SMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner] 18a0b956f5SMartin Matuska * 19a0b956f5SMartin Matuska * CDDL HEADER END 20a0b956f5SMartin Matuska */ 21a0b956f5SMartin Matuska 22a0b956f5SMartin Matuska /* 23a0b956f5SMartin Matuska * Copyright 2022 Axcient. All rights reserved. 24a0b956f5SMartin Matuska * Use is subject to license terms. 25*e2df9bb4SMartin Matuska * 26*e2df9bb4SMartin Matuska * Copyright (c) 2024, Klara, Inc. 27a0b956f5SMartin Matuska */ 28a0b956f5SMartin Matuska 29a0b956f5SMartin Matuska #include <err.h> 30a0b956f5SMartin Matuska #include <search.h> 31a0b956f5SMartin Matuska #include <stdio.h> 32a0b956f5SMartin Matuska #include <stdlib.h> 33a0b956f5SMartin Matuska #include <unistd.h> 34a0b956f5SMartin Matuska #include <sys/zfs_ioctl.h> 35a0b956f5SMartin Matuska #include <sys/zio_checksum.h> 36a0b956f5SMartin Matuska #include <sys/zstd/zstd.h> 37a0b956f5SMartin Matuska #include "zfs_fletcher.h" 38a0b956f5SMartin Matuska #include "zstream.h" 39a0b956f5SMartin Matuska 40a0b956f5SMartin Matuska static int 41a0b956f5SMartin Matuska dump_record(dmu_replay_record_t *drr, void *payload, int payload_len, 42a0b956f5SMartin Matuska zio_cksum_t *zc, int outfd) 43a0b956f5SMartin Matuska { 44a0b956f5SMartin Matuska assert(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum) 45a0b956f5SMartin Matuska == sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t)); 46a0b956f5SMartin Matuska fletcher_4_incremental_native(drr, 47a0b956f5SMartin Matuska offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc); 48a0b956f5SMartin Matuska if (drr->drr_type != DRR_BEGIN) { 49a0b956f5SMartin Matuska assert(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u. 50a0b956f5SMartin Matuska drr_checksum.drr_checksum)); 51a0b956f5SMartin Matuska drr->drr_u.drr_checksum.drr_checksum = *zc; 52a0b956f5SMartin Matuska } 53a0b956f5SMartin Matuska fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum, 54a0b956f5SMartin Matuska sizeof (zio_cksum_t), zc); 55a0b956f5SMartin Matuska if (write(outfd, drr, sizeof (*drr)) == -1) 56a0b956f5SMartin Matuska return (errno); 57a0b956f5SMartin Matuska if (payload_len != 0) { 58a0b956f5SMartin Matuska fletcher_4_incremental_native(payload, payload_len, zc); 59a0b956f5SMartin Matuska if (write(outfd, payload, payload_len) == -1) 60a0b956f5SMartin Matuska return (errno); 61a0b956f5SMartin Matuska } 62a0b956f5SMartin Matuska return (0); 63a0b956f5SMartin Matuska } 64a0b956f5SMartin Matuska 65a0b956f5SMartin Matuska int 66a0b956f5SMartin Matuska zstream_do_decompress(int argc, char *argv[]) 67a0b956f5SMartin Matuska { 68a0b956f5SMartin Matuska const int KEYSIZE = 64; 69a0b956f5SMartin Matuska int bufsz = SPA_MAXBLOCKSIZE; 70a0b956f5SMartin Matuska char *buf = safe_malloc(bufsz); 71a0b956f5SMartin Matuska dmu_replay_record_t thedrr; 72a0b956f5SMartin Matuska dmu_replay_record_t *drr = &thedrr; 73a0b956f5SMartin Matuska zio_cksum_t stream_cksum; 74a0b956f5SMartin Matuska int c; 75a0b956f5SMartin Matuska boolean_t verbose = B_FALSE; 76a0b956f5SMartin Matuska 77a0b956f5SMartin Matuska while ((c = getopt(argc, argv, "v")) != -1) { 78a0b956f5SMartin Matuska switch (c) { 79a0b956f5SMartin Matuska case 'v': 80a0b956f5SMartin Matuska verbose = B_TRUE; 81a0b956f5SMartin Matuska break; 82a0b956f5SMartin Matuska case '?': 83a0b956f5SMartin Matuska (void) fprintf(stderr, "invalid option '%c'\n", 84a0b956f5SMartin Matuska optopt); 85a0b956f5SMartin Matuska zstream_usage(); 86a0b956f5SMartin Matuska break; 87a0b956f5SMartin Matuska } 88a0b956f5SMartin Matuska } 89a0b956f5SMartin Matuska 90a0b956f5SMartin Matuska argc -= optind; 91a0b956f5SMartin Matuska argv += optind; 92a0b956f5SMartin Matuska 93a0b956f5SMartin Matuska if (argc < 0) 94a0b956f5SMartin Matuska zstream_usage(); 95a0b956f5SMartin Matuska 96a0b956f5SMartin Matuska if (hcreate(argc) == 0) 97a0b956f5SMartin Matuska errx(1, "hcreate"); 98a0b956f5SMartin Matuska for (int i = 0; i < argc; i++) { 99a0b956f5SMartin Matuska uint64_t object, offset; 100a0b956f5SMartin Matuska char *obj_str; 101a0b956f5SMartin Matuska char *offset_str; 102a0b956f5SMartin Matuska char *key; 103a0b956f5SMartin Matuska char *end; 104a0b956f5SMartin Matuska enum zio_compress type = ZIO_COMPRESS_LZ4; 105a0b956f5SMartin Matuska 106a0b956f5SMartin Matuska obj_str = strsep(&argv[i], ","); 107a0b956f5SMartin Matuska if (argv[i] == NULL) { 108a0b956f5SMartin Matuska zstream_usage(); 109a0b956f5SMartin Matuska exit(2); 110a0b956f5SMartin Matuska } 111a0b956f5SMartin Matuska errno = 0; 112a0b956f5SMartin Matuska object = strtoull(obj_str, &end, 0); 113a0b956f5SMartin Matuska if (errno || *end != '\0') 114a0b956f5SMartin Matuska errx(1, "invalid value for object"); 115a0b956f5SMartin Matuska offset_str = strsep(&argv[i], ","); 116a0b956f5SMartin Matuska offset = strtoull(offset_str, &end, 0); 117a0b956f5SMartin Matuska if (errno || *end != '\0') 118a0b956f5SMartin Matuska errx(1, "invalid value for offset"); 119a0b956f5SMartin Matuska if (argv[i]) { 120dbd5678dSMartin Matuska if (0 == strcmp("off", argv[i])) 121dbd5678dSMartin Matuska type = ZIO_COMPRESS_OFF; 122dbd5678dSMartin Matuska else if (0 == strcmp("lz4", argv[i])) 123a0b956f5SMartin Matuska type = ZIO_COMPRESS_LZ4; 124a0b956f5SMartin Matuska else if (0 == strcmp("lzjb", argv[i])) 125a0b956f5SMartin Matuska type = ZIO_COMPRESS_LZJB; 126a0b956f5SMartin Matuska else if (0 == strcmp("gzip", argv[i])) 127a0b956f5SMartin Matuska type = ZIO_COMPRESS_GZIP_1; 128a0b956f5SMartin Matuska else if (0 == strcmp("zle", argv[i])) 129a0b956f5SMartin Matuska type = ZIO_COMPRESS_ZLE; 130a0b956f5SMartin Matuska else if (0 == strcmp("zstd", argv[i])) 131a0b956f5SMartin Matuska type = ZIO_COMPRESS_ZSTD; 132a0b956f5SMartin Matuska else { 133a0b956f5SMartin Matuska fprintf(stderr, "Invalid compression type %s.\n" 134dbd5678dSMartin Matuska "Supported types are off, lz4, lzjb, gzip, " 135dbd5678dSMartin Matuska "zle, and zstd\n", 136a0b956f5SMartin Matuska argv[i]); 137a0b956f5SMartin Matuska exit(2); 138a0b956f5SMartin Matuska } 139a0b956f5SMartin Matuska } 140a0b956f5SMartin Matuska 141a0b956f5SMartin Matuska if (asprintf(&key, "%llu,%llu", (u_longlong_t)object, 142a0b956f5SMartin Matuska (u_longlong_t)offset) < 0) { 143a0b956f5SMartin Matuska err(1, "asprintf"); 144a0b956f5SMartin Matuska } 145a0b956f5SMartin Matuska ENTRY e = {.key = key}; 146a0b956f5SMartin Matuska ENTRY *p; 147a0b956f5SMartin Matuska 148a0b956f5SMartin Matuska p = hsearch(e, ENTER); 149a0b956f5SMartin Matuska if (p == NULL) 150a0b956f5SMartin Matuska errx(1, "hsearch"); 151dbd5678dSMartin Matuska p->data = (void*)(intptr_t)type; 152a0b956f5SMartin Matuska } 153a0b956f5SMartin Matuska 154a0b956f5SMartin Matuska if (isatty(STDIN_FILENO)) { 155a0b956f5SMartin Matuska (void) fprintf(stderr, 156a0b956f5SMartin Matuska "Error: The send stream is a binary format " 157a0b956f5SMartin Matuska "and can not be read from a\n" 158a0b956f5SMartin Matuska "terminal. Standard input must be redirected.\n"); 159a0b956f5SMartin Matuska exit(1); 160a0b956f5SMartin Matuska } 161a0b956f5SMartin Matuska 162a0b956f5SMartin Matuska fletcher_4_init(); 16315f0b8c3SMartin Matuska int begin = 0; 16415f0b8c3SMartin Matuska boolean_t seen = B_FALSE; 165a0b956f5SMartin Matuska while (sfread(drr, sizeof (*drr), stdin) != 0) { 166a0b956f5SMartin Matuska struct drr_write *drrw; 167a0b956f5SMartin Matuska uint64_t payload_size = 0; 168a0b956f5SMartin Matuska 169a0b956f5SMartin Matuska /* 170a0b956f5SMartin Matuska * We need to regenerate the checksum. 171a0b956f5SMartin Matuska */ 172a0b956f5SMartin Matuska if (drr->drr_type != DRR_BEGIN) { 173a0b956f5SMartin Matuska memset(&drr->drr_u.drr_checksum.drr_checksum, 0, 174a0b956f5SMartin Matuska sizeof (drr->drr_u.drr_checksum.drr_checksum)); 175a0b956f5SMartin Matuska } 176a0b956f5SMartin Matuska 177a0b956f5SMartin Matuska switch (drr->drr_type) { 178a0b956f5SMartin Matuska case DRR_BEGIN: 179a0b956f5SMartin Matuska { 180a0b956f5SMartin Matuska ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0); 18115f0b8c3SMartin Matuska VERIFY0(begin++); 18215f0b8c3SMartin Matuska seen = B_TRUE; 183a0b956f5SMartin Matuska 18415f0b8c3SMartin Matuska uint32_t sz = drr->drr_payloadlen; 18515f0b8c3SMartin Matuska 18615f0b8c3SMartin Matuska VERIFY3U(sz, <=, 1U << 28); 18715f0b8c3SMartin Matuska 188a0b956f5SMartin Matuska if (sz != 0) { 189a0b956f5SMartin Matuska if (sz > bufsz) { 190a0b956f5SMartin Matuska buf = realloc(buf, sz); 191a0b956f5SMartin Matuska if (buf == NULL) 192a0b956f5SMartin Matuska err(1, "realloc"); 193a0b956f5SMartin Matuska bufsz = sz; 194a0b956f5SMartin Matuska } 195a0b956f5SMartin Matuska (void) sfread(buf, sz, stdin); 196a0b956f5SMartin Matuska } 197a0b956f5SMartin Matuska payload_size = sz; 198a0b956f5SMartin Matuska break; 199a0b956f5SMartin Matuska } 200a0b956f5SMartin Matuska case DRR_END: 201a0b956f5SMartin Matuska { 202a0b956f5SMartin Matuska struct drr_end *drre = &drr->drr_u.drr_end; 203a0b956f5SMartin Matuska /* 20415f0b8c3SMartin Matuska * We would prefer to just check --begin == 0, but 20515f0b8c3SMartin Matuska * replication streams have an end of stream END 20615f0b8c3SMartin Matuska * record, so we must avoid tripping it. 20715f0b8c3SMartin Matuska */ 20815f0b8c3SMartin Matuska VERIFY3B(seen, ==, B_TRUE); 20915f0b8c3SMartin Matuska begin--; 21015f0b8c3SMartin Matuska /* 211a0b956f5SMartin Matuska * Use the recalculated checksum, unless this is 212a0b956f5SMartin Matuska * the END record of a stream package, which has 213a0b956f5SMartin Matuska * no checksum. 214a0b956f5SMartin Matuska */ 215a0b956f5SMartin Matuska if (!ZIO_CHECKSUM_IS_ZERO(&drre->drr_checksum)) 216a0b956f5SMartin Matuska drre->drr_checksum = stream_cksum; 217a0b956f5SMartin Matuska break; 218a0b956f5SMartin Matuska } 219a0b956f5SMartin Matuska 220a0b956f5SMartin Matuska case DRR_OBJECT: 221a0b956f5SMartin Matuska { 222a0b956f5SMartin Matuska struct drr_object *drro = &drr->drr_u.drr_object; 22315f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 224a0b956f5SMartin Matuska 225a0b956f5SMartin Matuska if (drro->drr_bonuslen > 0) { 226a0b956f5SMartin Matuska payload_size = DRR_OBJECT_PAYLOAD_SIZE(drro); 227a0b956f5SMartin Matuska (void) sfread(buf, payload_size, stdin); 228a0b956f5SMartin Matuska } 229a0b956f5SMartin Matuska break; 230a0b956f5SMartin Matuska } 231a0b956f5SMartin Matuska 232a0b956f5SMartin Matuska case DRR_SPILL: 233a0b956f5SMartin Matuska { 234a0b956f5SMartin Matuska struct drr_spill *drrs = &drr->drr_u.drr_spill; 23515f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 236a0b956f5SMartin Matuska payload_size = DRR_SPILL_PAYLOAD_SIZE(drrs); 237a0b956f5SMartin Matuska (void) sfread(buf, payload_size, stdin); 238a0b956f5SMartin Matuska break; 239a0b956f5SMartin Matuska } 240a0b956f5SMartin Matuska 241a0b956f5SMartin Matuska case DRR_WRITE_BYREF: 24215f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 243a0b956f5SMartin Matuska fprintf(stderr, 244a0b956f5SMartin Matuska "Deduplicated streams are not supported\n"); 245a0b956f5SMartin Matuska exit(1); 246a0b956f5SMartin Matuska break; 247a0b956f5SMartin Matuska 248a0b956f5SMartin Matuska case DRR_WRITE: 249a0b956f5SMartin Matuska { 25015f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 251a0b956f5SMartin Matuska drrw = &thedrr.drr_u.drr_write; 252a0b956f5SMartin Matuska payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw); 253a0b956f5SMartin Matuska ENTRY *p; 254a0b956f5SMartin Matuska char key[KEYSIZE]; 255a0b956f5SMartin Matuska 256a0b956f5SMartin Matuska snprintf(key, KEYSIZE, "%llu,%llu", 257a0b956f5SMartin Matuska (u_longlong_t)drrw->drr_object, 258a0b956f5SMartin Matuska (u_longlong_t)drrw->drr_offset); 259a0b956f5SMartin Matuska ENTRY e = {.key = key}; 260a0b956f5SMartin Matuska 261a0b956f5SMartin Matuska p = hsearch(e, FIND); 262*e2df9bb4SMartin Matuska if (p == NULL) { 263*e2df9bb4SMartin Matuska /* 264*e2df9bb4SMartin Matuska * Read the contents of the block unaltered 265*e2df9bb4SMartin Matuska */ 266*e2df9bb4SMartin Matuska (void) sfread(buf, payload_size, stdin); 267dbd5678dSMartin Matuska break; 268a0b956f5SMartin Matuska } 269a0b956f5SMartin Matuska 270a0b956f5SMartin Matuska /* 271a0b956f5SMartin Matuska * Read and decompress the block 272a0b956f5SMartin Matuska */ 273*e2df9bb4SMartin Matuska enum zio_compress c = 274*e2df9bb4SMartin Matuska (enum zio_compress)(intptr_t)p->data; 275*e2df9bb4SMartin Matuska 276*e2df9bb4SMartin Matuska if (c == ZIO_COMPRESS_OFF) { 277*e2df9bb4SMartin Matuska (void) sfread(buf, payload_size, stdin); 278*e2df9bb4SMartin Matuska drrw->drr_compressiontype = 0; 279*e2df9bb4SMartin Matuska drrw->drr_compressed_size = 0; 280*e2df9bb4SMartin Matuska if (verbose) 281*e2df9bb4SMartin Matuska fprintf(stderr, 282*e2df9bb4SMartin Matuska "Resetting compression type to " 283*e2df9bb4SMartin Matuska "off for ino %llu offset %llu\n", 284*e2df9bb4SMartin Matuska (u_longlong_t)drrw->drr_object, 285*e2df9bb4SMartin Matuska (u_longlong_t)drrw->drr_offset); 286*e2df9bb4SMartin Matuska break; 287*e2df9bb4SMartin Matuska } 288*e2df9bb4SMartin Matuska 289*e2df9bb4SMartin Matuska uint64_t lsize = drrw->drr_logical_size; 290*e2df9bb4SMartin Matuska ASSERT3U(payload_size, <=, lsize); 291*e2df9bb4SMartin Matuska 292a0b956f5SMartin Matuska char *lzbuf = safe_calloc(payload_size); 293a0b956f5SMartin Matuska (void) sfread(lzbuf, payload_size, stdin); 294*e2df9bb4SMartin Matuska 295*e2df9bb4SMartin Matuska abd_t sabd, dabd; 296*e2df9bb4SMartin Matuska abd_get_from_buf_struct(&sabd, lzbuf, payload_size); 297*e2df9bb4SMartin Matuska abd_get_from_buf_struct(&dabd, buf, lsize); 298*e2df9bb4SMartin Matuska int err = zio_decompress_data(c, &sabd, &dabd, 299*e2df9bb4SMartin Matuska payload_size, lsize, NULL); 300*e2df9bb4SMartin Matuska abd_free(&dabd); 301*e2df9bb4SMartin Matuska abd_free(&sabd); 302*e2df9bb4SMartin Matuska 303*e2df9bb4SMartin Matuska if (err == 0) { 304*e2df9bb4SMartin Matuska drrw->drr_compressiontype = 0; 305*e2df9bb4SMartin Matuska drrw->drr_compressed_size = 0; 306*e2df9bb4SMartin Matuska payload_size = lsize; 307*e2df9bb4SMartin Matuska if (verbose) { 308*e2df9bb4SMartin Matuska fprintf(stderr, 309*e2df9bb4SMartin Matuska "successfully decompressed " 310*e2df9bb4SMartin Matuska "ino %llu offset %llu\n", 311*e2df9bb4SMartin Matuska (u_longlong_t)drrw->drr_object, 312*e2df9bb4SMartin Matuska (u_longlong_t)drrw->drr_offset); 313*e2df9bb4SMartin Matuska } 314*e2df9bb4SMartin Matuska } else { 315a0b956f5SMartin Matuska /* 316*e2df9bb4SMartin Matuska * The block must not be compressed, at least 317*e2df9bb4SMartin Matuska * not with this compression type, possibly 318*e2df9bb4SMartin Matuska * because it gets written multiple times in 319*e2df9bb4SMartin Matuska * this stream. 320a0b956f5SMartin Matuska */ 321a0b956f5SMartin Matuska warnx("decompression failed for " 322a0b956f5SMartin Matuska "ino %llu offset %llu", 323a0b956f5SMartin Matuska (u_longlong_t)drrw->drr_object, 324a0b956f5SMartin Matuska (u_longlong_t)drrw->drr_offset); 325a0b956f5SMartin Matuska memcpy(buf, lzbuf, payload_size); 326a0b956f5SMartin Matuska } 327*e2df9bb4SMartin Matuska 328a0b956f5SMartin Matuska free(lzbuf); 329a0b956f5SMartin Matuska break; 330a0b956f5SMartin Matuska } 331a0b956f5SMartin Matuska 332a0b956f5SMartin Matuska case DRR_WRITE_EMBEDDED: 333a0b956f5SMartin Matuska { 33415f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 335a0b956f5SMartin Matuska struct drr_write_embedded *drrwe = 336a0b956f5SMartin Matuska &drr->drr_u.drr_write_embedded; 337a0b956f5SMartin Matuska payload_size = 338a0b956f5SMartin Matuska P2ROUNDUP((uint64_t)drrwe->drr_psize, 8); 339a0b956f5SMartin Matuska (void) sfread(buf, payload_size, stdin); 340a0b956f5SMartin Matuska break; 341a0b956f5SMartin Matuska } 342a0b956f5SMartin Matuska 343a0b956f5SMartin Matuska case DRR_FREEOBJECTS: 344a0b956f5SMartin Matuska case DRR_FREE: 345a0b956f5SMartin Matuska case DRR_OBJECT_RANGE: 34615f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 347a0b956f5SMartin Matuska break; 348a0b956f5SMartin Matuska 349a0b956f5SMartin Matuska default: 350a0b956f5SMartin Matuska (void) fprintf(stderr, "INVALID record type 0x%x\n", 351a0b956f5SMartin Matuska drr->drr_type); 352a0b956f5SMartin Matuska /* should never happen, so assert */ 353a0b956f5SMartin Matuska assert(B_FALSE); 354a0b956f5SMartin Matuska } 355a0b956f5SMartin Matuska 356a0b956f5SMartin Matuska if (feof(stdout)) { 357a0b956f5SMartin Matuska fprintf(stderr, "Error: unexpected end-of-file\n"); 358a0b956f5SMartin Matuska exit(1); 359a0b956f5SMartin Matuska } 360a0b956f5SMartin Matuska if (ferror(stdout)) { 361a0b956f5SMartin Matuska fprintf(stderr, "Error while reading file: %s\n", 362a0b956f5SMartin Matuska strerror(errno)); 363a0b956f5SMartin Matuska exit(1); 364a0b956f5SMartin Matuska } 365a0b956f5SMartin Matuska 366a0b956f5SMartin Matuska /* 367a0b956f5SMartin Matuska * We need to recalculate the checksum, and it needs to be 368a0b956f5SMartin Matuska * initially zero to do that. BEGIN records don't have 369a0b956f5SMartin Matuska * a checksum. 370a0b956f5SMartin Matuska */ 371a0b956f5SMartin Matuska if (drr->drr_type != DRR_BEGIN) { 372a0b956f5SMartin Matuska memset(&drr->drr_u.drr_checksum.drr_checksum, 0, 373a0b956f5SMartin Matuska sizeof (drr->drr_u.drr_checksum.drr_checksum)); 374a0b956f5SMartin Matuska } 375a0b956f5SMartin Matuska if (dump_record(drr, buf, payload_size, 376a0b956f5SMartin Matuska &stream_cksum, STDOUT_FILENO) != 0) 377a0b956f5SMartin Matuska break; 378a0b956f5SMartin Matuska if (drr->drr_type == DRR_END) { 379a0b956f5SMartin Matuska /* 380a0b956f5SMartin Matuska * Typically the END record is either the last 381a0b956f5SMartin Matuska * thing in the stream, or it is followed 382a0b956f5SMartin Matuska * by a BEGIN record (which also zeros the checksum). 383a0b956f5SMartin Matuska * However, a stream package ends with two END 384a0b956f5SMartin Matuska * records. The last END record's checksum starts 385a0b956f5SMartin Matuska * from zero. 386a0b956f5SMartin Matuska */ 387a0b956f5SMartin Matuska ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0); 388a0b956f5SMartin Matuska } 389a0b956f5SMartin Matuska } 390a0b956f5SMartin Matuska free(buf); 391a0b956f5SMartin Matuska fletcher_4_fini(); 392a0b956f5SMartin Matuska hdestroy(); 393a0b956f5SMartin Matuska 394a0b956f5SMartin Matuska return (0); 395a0b956f5SMartin Matuska } 396