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