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