xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_recompress.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
1*61145dc2SMartin Matuska // SPDX-License-Identifier: CDDL-1.0
2dbd5678dSMartin Matuska /*
3dbd5678dSMartin Matuska  * CDDL HEADER START
4dbd5678dSMartin Matuska  *
5dbd5678dSMartin Matuska  * The contents of this file are subject to the terms of the
6dbd5678dSMartin Matuska  * Common Development and Distribution License (the "License").
7dbd5678dSMartin Matuska  * You may not use this file except in compliance with the License.
8dbd5678dSMartin Matuska  *
9dbd5678dSMartin Matuska  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10dbd5678dSMartin Matuska  * or https://opensource.org/licenses/CDDL-1.0.
11dbd5678dSMartin Matuska  * See the License for the specific language governing permissions
12dbd5678dSMartin Matuska  * and limitations under the License.
13dbd5678dSMartin Matuska  *
14dbd5678dSMartin Matuska  * When distributing Covered Code, include this CDDL HEADER in each
15dbd5678dSMartin Matuska  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16dbd5678dSMartin Matuska  * If applicable, add the following below this CDDL HEADER, with the
17dbd5678dSMartin Matuska  * fields enclosed by brackets "[]" replaced with your own identifying
18dbd5678dSMartin Matuska  * information: Portions Copyright [yyyy] [name of copyright owner]
19dbd5678dSMartin Matuska  *
20dbd5678dSMartin Matuska  * CDDL HEADER END
21dbd5678dSMartin Matuska  */
22dbd5678dSMartin Matuska 
23dbd5678dSMartin Matuska /*
24dbd5678dSMartin Matuska  * Copyright 2022 Axcient.  All rights reserved.
25dbd5678dSMartin Matuska  * Use is subject to license terms.
26e2df9bb4SMartin Matuska  *
27dbd5678dSMartin Matuska  * Copyright (c) 2022 by Delphix. All rights reserved.
28e2df9bb4SMartin Matuska  * Copyright (c) 2024, Klara, Inc.
29dbd5678dSMartin Matuska  */
30dbd5678dSMartin Matuska 
31dbd5678dSMartin Matuska #include <err.h>
32dbd5678dSMartin Matuska #include <stdio.h>
33dbd5678dSMartin Matuska #include <stdlib.h>
34dbd5678dSMartin Matuska #include <unistd.h>
35dbd5678dSMartin Matuska #include <sys/zfs_ioctl.h>
36dbd5678dSMartin Matuska #include <sys/zio_checksum.h>
37dbd5678dSMartin Matuska #include <sys/zstd/zstd.h>
38dbd5678dSMartin Matuska #include "zfs_fletcher.h"
39dbd5678dSMartin Matuska #include "zstream.h"
40dbd5678dSMartin Matuska 
41dbd5678dSMartin Matuska static int
dump_record(dmu_replay_record_t * drr,void * payload,int payload_len,zio_cksum_t * zc,int outfd)42dbd5678dSMartin Matuska dump_record(dmu_replay_record_t *drr, void *payload, int payload_len,
43dbd5678dSMartin Matuska     zio_cksum_t *zc, int outfd)
44dbd5678dSMartin Matuska {
45dbd5678dSMartin Matuska 	assert(offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum)
46dbd5678dSMartin Matuska 	    == sizeof (dmu_replay_record_t) - sizeof (zio_cksum_t));
47dbd5678dSMartin Matuska 	fletcher_4_incremental_native(drr,
48dbd5678dSMartin Matuska 	    offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum), zc);
49dbd5678dSMartin Matuska 	if (drr->drr_type != DRR_BEGIN) {
50dbd5678dSMartin Matuska 		assert(ZIO_CHECKSUM_IS_ZERO(&drr->drr_u.
51dbd5678dSMartin Matuska 		    drr_checksum.drr_checksum));
52dbd5678dSMartin Matuska 		drr->drr_u.drr_checksum.drr_checksum = *zc;
53dbd5678dSMartin Matuska 	}
54dbd5678dSMartin Matuska 	fletcher_4_incremental_native(&drr->drr_u.drr_checksum.drr_checksum,
55dbd5678dSMartin Matuska 	    sizeof (zio_cksum_t), zc);
56dbd5678dSMartin Matuska 	if (write(outfd, drr, sizeof (*drr)) == -1)
57dbd5678dSMartin Matuska 		return (errno);
58dbd5678dSMartin Matuska 	if (payload_len != 0) {
59dbd5678dSMartin Matuska 		fletcher_4_incremental_native(payload, payload_len, zc);
60dbd5678dSMartin Matuska 		if (write(outfd, payload, payload_len) == -1)
61dbd5678dSMartin Matuska 			return (errno);
62dbd5678dSMartin Matuska 	}
63dbd5678dSMartin Matuska 	return (0);
64dbd5678dSMartin Matuska }
65dbd5678dSMartin Matuska 
66dbd5678dSMartin Matuska int
zstream_do_recompress(int argc,char * argv[])67dbd5678dSMartin Matuska zstream_do_recompress(int argc, char *argv[])
68dbd5678dSMartin Matuska {
69dbd5678dSMartin Matuska 	int bufsz = SPA_MAXBLOCKSIZE;
70dbd5678dSMartin Matuska 	char *buf = safe_malloc(bufsz);
71dbd5678dSMartin Matuska 	dmu_replay_record_t thedrr;
72dbd5678dSMartin Matuska 	dmu_replay_record_t *drr = &thedrr;
73dbd5678dSMartin Matuska 	zio_cksum_t stream_cksum;
74dbd5678dSMartin Matuska 	int c;
75e2df9bb4SMartin Matuska 	int level = 0;
76dbd5678dSMartin Matuska 
77dbd5678dSMartin Matuska 	while ((c = getopt(argc, argv, "l:")) != -1) {
78dbd5678dSMartin Matuska 		switch (c) {
79dbd5678dSMartin Matuska 		case 'l':
80aca928a5SMartin Matuska 			if (sscanf(optarg, "%d", &level) != 1) {
81dbd5678dSMartin Matuska 				fprintf(stderr,
82dbd5678dSMartin Matuska 				    "failed to parse level '%s'\n",
83dbd5678dSMartin Matuska 				    optarg);
84dbd5678dSMartin Matuska 				zstream_usage();
85dbd5678dSMartin Matuska 			}
86dbd5678dSMartin Matuska 			break;
87dbd5678dSMartin Matuska 		case '?':
88dbd5678dSMartin Matuska 			(void) fprintf(stderr, "invalid option '%c'\n",
89dbd5678dSMartin Matuska 			    optopt);
90dbd5678dSMartin Matuska 			zstream_usage();
91dbd5678dSMartin Matuska 			break;
92dbd5678dSMartin Matuska 		}
93dbd5678dSMartin Matuska 	}
94dbd5678dSMartin Matuska 
95dbd5678dSMartin Matuska 	argc -= optind;
96dbd5678dSMartin Matuska 	argv += optind;
97dbd5678dSMartin Matuska 
98dbd5678dSMartin Matuska 	if (argc != 1)
99dbd5678dSMartin Matuska 		zstream_usage();
100e2df9bb4SMartin Matuska 
101e2df9bb4SMartin Matuska 	enum zio_compress ctype;
102e2df9bb4SMartin Matuska 	if (strcmp(argv[0], "off") == 0) {
103e2df9bb4SMartin Matuska 		ctype = ZIO_COMPRESS_OFF;
104dbd5678dSMartin Matuska 	} else {
105e2df9bb4SMartin Matuska 		for (ctype = 0; ctype < ZIO_COMPRESS_FUNCTIONS; ctype++) {
106e2df9bb4SMartin Matuska 			if (strcmp(argv[0],
107e2df9bb4SMartin Matuska 			    zio_compress_table[ctype].ci_name) == 0)
108dbd5678dSMartin Matuska 				break;
109dbd5678dSMartin Matuska 		}
110e2df9bb4SMartin Matuska 		if (ctype == ZIO_COMPRESS_FUNCTIONS ||
111e2df9bb4SMartin Matuska 		    zio_compress_table[ctype].ci_compress == NULL) {
112dbd5678dSMartin Matuska 			fprintf(stderr, "Invalid compression type %s.\n",
113dbd5678dSMartin Matuska 			    argv[0]);
114dbd5678dSMartin Matuska 			exit(2);
115dbd5678dSMartin Matuska 		}
116dbd5678dSMartin Matuska 	}
117dbd5678dSMartin Matuska 
118dbd5678dSMartin Matuska 	if (isatty(STDIN_FILENO)) {
119dbd5678dSMartin Matuska 		(void) fprintf(stderr,
120dbd5678dSMartin Matuska 		    "Error: The send stream is a binary format "
121dbd5678dSMartin Matuska 		    "and can not be read from a\n"
122dbd5678dSMartin Matuska 		    "terminal.  Standard input must be redirected.\n");
123dbd5678dSMartin Matuska 		exit(1);
124dbd5678dSMartin Matuska 	}
125dbd5678dSMartin Matuska 
126e2df9bb4SMartin Matuska 	abd_init();
127dbd5678dSMartin Matuska 	fletcher_4_init();
128dbd5678dSMartin Matuska 	zio_init();
129dbd5678dSMartin Matuska 	zstd_init();
13015f0b8c3SMartin Matuska 	int begin = 0;
13115f0b8c3SMartin Matuska 	boolean_t seen = B_FALSE;
132dbd5678dSMartin Matuska 	while (sfread(drr, sizeof (*drr), stdin) != 0) {
133dbd5678dSMartin Matuska 		struct drr_write *drrw;
134dbd5678dSMartin Matuska 		uint64_t payload_size = 0;
135dbd5678dSMartin Matuska 
136dbd5678dSMartin Matuska 		/*
137dbd5678dSMartin Matuska 		 * We need to regenerate the checksum.
138dbd5678dSMartin Matuska 		 */
139dbd5678dSMartin Matuska 		if (drr->drr_type != DRR_BEGIN) {
140dbd5678dSMartin Matuska 			memset(&drr->drr_u.drr_checksum.drr_checksum, 0,
141dbd5678dSMartin Matuska 			    sizeof (drr->drr_u.drr_checksum.drr_checksum));
142dbd5678dSMartin Matuska 		}
143dbd5678dSMartin Matuska 
144dbd5678dSMartin Matuska 
145dbd5678dSMartin Matuska 		switch (drr->drr_type) {
146dbd5678dSMartin Matuska 		case DRR_BEGIN:
147dbd5678dSMartin Matuska 		{
148dbd5678dSMartin Matuska 			ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
14915f0b8c3SMartin Matuska 			VERIFY0(begin++);
15015f0b8c3SMartin Matuska 			seen = B_TRUE;
151dbd5678dSMartin Matuska 
15215f0b8c3SMartin Matuska 			uint32_t sz = drr->drr_payloadlen;
15315f0b8c3SMartin Matuska 
15415f0b8c3SMartin Matuska 			VERIFY3U(sz, <=, 1U << 28);
15515f0b8c3SMartin Matuska 
156dbd5678dSMartin Matuska 			if (sz != 0) {
157dbd5678dSMartin Matuska 				if (sz > bufsz) {
158dbd5678dSMartin Matuska 					buf = realloc(buf, sz);
159dbd5678dSMartin Matuska 					if (buf == NULL)
160dbd5678dSMartin Matuska 						err(1, "realloc");
161dbd5678dSMartin Matuska 					bufsz = sz;
162dbd5678dSMartin Matuska 				}
163dbd5678dSMartin Matuska 				(void) sfread(buf, sz, stdin);
164dbd5678dSMartin Matuska 			}
165dbd5678dSMartin Matuska 			payload_size = sz;
166dbd5678dSMartin Matuska 			break;
167dbd5678dSMartin Matuska 		}
168dbd5678dSMartin Matuska 		case DRR_END:
169dbd5678dSMartin Matuska 		{
170dbd5678dSMartin Matuska 			struct drr_end *drre = &drr->drr_u.drr_end;
171dbd5678dSMartin Matuska 			/*
17215f0b8c3SMartin Matuska 			 * We would prefer to just check --begin == 0, but
17315f0b8c3SMartin Matuska 			 * replication streams have an end of stream END
17415f0b8c3SMartin Matuska 			 * record, so we must avoid tripping it.
17515f0b8c3SMartin Matuska 			 */
17615f0b8c3SMartin Matuska 			VERIFY3B(seen, ==, B_TRUE);
17715f0b8c3SMartin Matuska 			begin--;
17815f0b8c3SMartin Matuska 			/*
179dbd5678dSMartin Matuska 			 * Use the recalculated checksum, unless this is
180dbd5678dSMartin Matuska 			 * the END record of a stream package, which has
181dbd5678dSMartin Matuska 			 * no checksum.
182dbd5678dSMartin Matuska 			 */
183dbd5678dSMartin Matuska 			if (!ZIO_CHECKSUM_IS_ZERO(&drre->drr_checksum))
184dbd5678dSMartin Matuska 				drre->drr_checksum = stream_cksum;
185dbd5678dSMartin Matuska 			break;
186dbd5678dSMartin Matuska 		}
187dbd5678dSMartin Matuska 
188dbd5678dSMartin Matuska 		case DRR_OBJECT:
189dbd5678dSMartin Matuska 		{
190dbd5678dSMartin Matuska 			struct drr_object *drro = &drr->drr_u.drr_object;
19115f0b8c3SMartin Matuska 			VERIFY3S(begin, ==, 1);
192dbd5678dSMartin Matuska 
193dbd5678dSMartin Matuska 			if (drro->drr_bonuslen > 0) {
194dbd5678dSMartin Matuska 				payload_size = DRR_OBJECT_PAYLOAD_SIZE(drro);
195dbd5678dSMartin Matuska 				(void) sfread(buf, payload_size, stdin);
196dbd5678dSMartin Matuska 			}
197dbd5678dSMartin Matuska 			break;
198dbd5678dSMartin Matuska 		}
199dbd5678dSMartin Matuska 
200dbd5678dSMartin Matuska 		case DRR_SPILL:
201dbd5678dSMartin Matuska 		{
202dbd5678dSMartin Matuska 			struct drr_spill *drrs = &drr->drr_u.drr_spill;
20315f0b8c3SMartin Matuska 			VERIFY3S(begin, ==, 1);
204dbd5678dSMartin Matuska 			payload_size = DRR_SPILL_PAYLOAD_SIZE(drrs);
205dbd5678dSMartin Matuska 			(void) sfread(buf, payload_size, stdin);
206dbd5678dSMartin Matuska 			break;
207dbd5678dSMartin Matuska 		}
208dbd5678dSMartin Matuska 
209dbd5678dSMartin Matuska 		case DRR_WRITE_BYREF:
21015f0b8c3SMartin Matuska 			VERIFY3S(begin, ==, 1);
211dbd5678dSMartin Matuska 			fprintf(stderr,
212dbd5678dSMartin Matuska 			    "Deduplicated streams are not supported\n");
213dbd5678dSMartin Matuska 			exit(1);
214dbd5678dSMartin Matuska 			break;
215dbd5678dSMartin Matuska 
216dbd5678dSMartin Matuska 		case DRR_WRITE:
217dbd5678dSMartin Matuska 		{
21815f0b8c3SMartin Matuska 			VERIFY3S(begin, ==, 1);
219dbd5678dSMartin Matuska 			drrw = &thedrr.drr_u.drr_write;
220dbd5678dSMartin Matuska 			payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw);
221dbd5678dSMartin Matuska 			/*
222dbd5678dSMartin Matuska 			 * In order to recompress an encrypted block, you have
223dbd5678dSMartin Matuska 			 * to decrypt, decompress, recompress, and
224dbd5678dSMartin Matuska 			 * re-encrypt. That can be a future enhancement (along
225dbd5678dSMartin Matuska 			 * with decryption or re-encryption), but for now we
226dbd5678dSMartin Matuska 			 * skip encrypted blocks.
227dbd5678dSMartin Matuska 			 */
228dbd5678dSMartin Matuska 			boolean_t encrypted = B_FALSE;
229dbd5678dSMartin Matuska 			for (int i = 0; i < ZIO_DATA_SALT_LEN; i++) {
230dbd5678dSMartin Matuska 				if (drrw->drr_salt[i] != 0) {
231dbd5678dSMartin Matuska 					encrypted = B_TRUE;
232dbd5678dSMartin Matuska 					break;
233dbd5678dSMartin Matuska 				}
234dbd5678dSMartin Matuska 			}
235dbd5678dSMartin Matuska 			if (encrypted) {
236dbd5678dSMartin Matuska 				(void) sfread(buf, payload_size, stdin);
237dbd5678dSMartin Matuska 				break;
238dbd5678dSMartin Matuska 			}
239e2df9bb4SMartin Matuska 			enum zio_compress dtype = drrw->drr_compressiontype;
240e2df9bb4SMartin Matuska 			if (dtype >= ZIO_COMPRESS_FUNCTIONS) {
241dbd5678dSMartin Matuska 				fprintf(stderr, "Invalid compression type in "
242e2df9bb4SMartin Matuska 				    "stream: %d\n", dtype);
243dbd5678dSMartin Matuska 				exit(3);
244dbd5678dSMartin Matuska 			}
245e2df9bb4SMartin Matuska 			if (zio_compress_table[dtype].ci_decompress == NULL)
246e2df9bb4SMartin Matuska 				dtype = ZIO_COMPRESS_OFF;
247dbd5678dSMartin Matuska 
248dbd5678dSMartin Matuska 			/* Set up buffers to minimize memcpys */
249dbd5678dSMartin Matuska 			char *cbuf, *dbuf;
250e2df9bb4SMartin Matuska 			if (ctype == ZIO_COMPRESS_OFF)
251dbd5678dSMartin Matuska 				dbuf = buf;
252dbd5678dSMartin Matuska 			else
253dbd5678dSMartin Matuska 				dbuf = safe_calloc(bufsz);
254dbd5678dSMartin Matuska 
255e2df9bb4SMartin Matuska 			if (dtype == ZIO_COMPRESS_OFF)
256dbd5678dSMartin Matuska 				cbuf = dbuf;
257dbd5678dSMartin Matuska 			else
258dbd5678dSMartin Matuska 				cbuf = safe_calloc(payload_size);
259dbd5678dSMartin Matuska 
260dbd5678dSMartin Matuska 			/* Read and decompress the payload */
261dbd5678dSMartin Matuska 			(void) sfread(cbuf, payload_size, stdin);
262e2df9bb4SMartin Matuska 			if (dtype != ZIO_COMPRESS_OFF) {
263e2df9bb4SMartin Matuska 				abd_t cabd, dabd;
264e2df9bb4SMartin Matuska 				abd_get_from_buf_struct(&cabd,
265e2df9bb4SMartin Matuska 				    cbuf, payload_size);
266e2df9bb4SMartin Matuska 				abd_get_from_buf_struct(&dabd, dbuf,
267e2df9bb4SMartin Matuska 				    MIN(bufsz, drrw->drr_logical_size));
268e2df9bb4SMartin Matuska 				if (zio_decompress_data(dtype, &cabd, &dabd,
269e2df9bb4SMartin Matuska 				    payload_size, abd_get_size(&dabd),
270e2df9bb4SMartin Matuska 				    NULL) != 0) {
271dbd5678dSMartin Matuska 					warnx("decompression type %d failed "
272dbd5678dSMartin Matuska 					    "for ino %llu offset %llu",
273e2df9bb4SMartin Matuska 					    dtype,
274dbd5678dSMartin Matuska 					    (u_longlong_t)drrw->drr_object,
275dbd5678dSMartin Matuska 					    (u_longlong_t)drrw->drr_offset);
276dbd5678dSMartin Matuska 					exit(4);
277dbd5678dSMartin Matuska 				}
278dbd5678dSMartin Matuska 				payload_size = drrw->drr_logical_size;
279e2df9bb4SMartin Matuska 				abd_free(&dabd);
280e2df9bb4SMartin Matuska 				abd_free(&cabd);
281dbd5678dSMartin Matuska 				free(cbuf);
282dbd5678dSMartin Matuska 			}
283dbd5678dSMartin Matuska 
284dbd5678dSMartin Matuska 			/* Recompress the payload */
285e2df9bb4SMartin Matuska 			if (ctype != ZIO_COMPRESS_OFF) {
286e2df9bb4SMartin Matuska 				abd_t dabd, abd;
287e2df9bb4SMartin Matuska 				abd_get_from_buf_struct(&dabd,
288e2df9bb4SMartin Matuska 				    dbuf, drrw->drr_logical_size);
289e2df9bb4SMartin Matuska 				abd_t *pabd =
290e2df9bb4SMartin Matuska 				    abd_get_from_buf_struct(&abd, buf, bufsz);
291e2df9bb4SMartin Matuska 				size_t csize = zio_compress_data(ctype, &dabd,
2927a7741afSMartin Matuska 				    &pabd, drrw->drr_logical_size,
2937a7741afSMartin Matuska 				    drrw->drr_logical_size, level);
294e2df9bb4SMartin Matuska 				size_t rounded =
295e2df9bb4SMartin Matuska 				    P2ROUNDUP(csize, SPA_MINBLOCKSIZE);
296e2df9bb4SMartin Matuska 				if (rounded >= drrw->drr_logical_size) {
297dbd5678dSMartin Matuska 					memcpy(buf, dbuf, payload_size);
298dbd5678dSMartin Matuska 					drrw->drr_compressiontype = 0;
299dbd5678dSMartin Matuska 					drrw->drr_compressed_size = 0;
300e2df9bb4SMartin Matuska 				} else {
301e2df9bb4SMartin Matuska 					abd_zero_off(pabd, csize,
302e2df9bb4SMartin Matuska 					    rounded - csize);
303e2df9bb4SMartin Matuska 					drrw->drr_compressiontype = ctype;
304e2df9bb4SMartin Matuska 					drrw->drr_compressed_size =
305e2df9bb4SMartin Matuska 					    payload_size = rounded;
306dbd5678dSMartin Matuska 				}
307e2df9bb4SMartin Matuska 				abd_free(&abd);
308e2df9bb4SMartin Matuska 				abd_free(&dabd);
309dbd5678dSMartin Matuska 				free(dbuf);
310dbd5678dSMartin Matuska 			} else {
311e2df9bb4SMartin Matuska 				drrw->drr_compressiontype = 0;
312dbd5678dSMartin Matuska 				drrw->drr_compressed_size = 0;
313dbd5678dSMartin Matuska 			}
314dbd5678dSMartin Matuska 			break;
315dbd5678dSMartin Matuska 		}
316dbd5678dSMartin Matuska 
317dbd5678dSMartin Matuska 		case DRR_WRITE_EMBEDDED:
318dbd5678dSMartin Matuska 		{
319dbd5678dSMartin Matuska 			struct drr_write_embedded *drrwe =
320dbd5678dSMartin Matuska 			    &drr->drr_u.drr_write_embedded;
32115f0b8c3SMartin Matuska 			VERIFY3S(begin, ==, 1);
322dbd5678dSMartin Matuska 			payload_size =
323dbd5678dSMartin Matuska 			    P2ROUNDUP((uint64_t)drrwe->drr_psize, 8);
324dbd5678dSMartin Matuska 			(void) sfread(buf, payload_size, stdin);
325dbd5678dSMartin Matuska 			break;
326dbd5678dSMartin Matuska 		}
327dbd5678dSMartin Matuska 
328dbd5678dSMartin Matuska 		case DRR_FREEOBJECTS:
329dbd5678dSMartin Matuska 		case DRR_FREE:
330dbd5678dSMartin Matuska 		case DRR_OBJECT_RANGE:
33115f0b8c3SMartin Matuska 			VERIFY3S(begin, ==, 1);
332dbd5678dSMartin Matuska 			break;
333dbd5678dSMartin Matuska 
334dbd5678dSMartin Matuska 		default:
335dbd5678dSMartin Matuska 			(void) fprintf(stderr, "INVALID record type 0x%x\n",
336dbd5678dSMartin Matuska 			    drr->drr_type);
337dbd5678dSMartin Matuska 			/* should never happen, so assert */
338dbd5678dSMartin Matuska 			assert(B_FALSE);
339dbd5678dSMartin Matuska 		}
340dbd5678dSMartin Matuska 
341dbd5678dSMartin Matuska 		if (feof(stdout)) {
342dbd5678dSMartin Matuska 			fprintf(stderr, "Error: unexpected end-of-file\n");
343dbd5678dSMartin Matuska 			exit(1);
344dbd5678dSMartin Matuska 		}
345dbd5678dSMartin Matuska 		if (ferror(stdout)) {
346dbd5678dSMartin Matuska 			fprintf(stderr, "Error while reading file: %s\n",
347dbd5678dSMartin Matuska 			    strerror(errno));
348dbd5678dSMartin Matuska 			exit(1);
349dbd5678dSMartin Matuska 		}
350dbd5678dSMartin Matuska 
351dbd5678dSMartin Matuska 		/*
352dbd5678dSMartin Matuska 		 * We need to recalculate the checksum, and it needs to be
353dbd5678dSMartin Matuska 		 * initially zero to do that.  BEGIN records don't have
354dbd5678dSMartin Matuska 		 * a checksum.
355dbd5678dSMartin Matuska 		 */
356dbd5678dSMartin Matuska 		if (drr->drr_type != DRR_BEGIN) {
357dbd5678dSMartin Matuska 			memset(&drr->drr_u.drr_checksum.drr_checksum, 0,
358dbd5678dSMartin Matuska 			    sizeof (drr->drr_u.drr_checksum.drr_checksum));
359dbd5678dSMartin Matuska 		}
360dbd5678dSMartin Matuska 		if (dump_record(drr, buf, payload_size,
361dbd5678dSMartin Matuska 		    &stream_cksum, STDOUT_FILENO) != 0)
362dbd5678dSMartin Matuska 			break;
363dbd5678dSMartin Matuska 		if (drr->drr_type == DRR_END) {
364dbd5678dSMartin Matuska 			/*
365dbd5678dSMartin Matuska 			 * Typically the END record is either the last
366dbd5678dSMartin Matuska 			 * thing in the stream, or it is followed
367dbd5678dSMartin Matuska 			 * by a BEGIN record (which also zeros the checksum).
368dbd5678dSMartin Matuska 			 * However, a stream package ends with two END
369dbd5678dSMartin Matuska 			 * records.  The last END record's checksum starts
370dbd5678dSMartin Matuska 			 * from zero.
371dbd5678dSMartin Matuska 			 */
372dbd5678dSMartin Matuska 			ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0);
373dbd5678dSMartin Matuska 		}
374dbd5678dSMartin Matuska 	}
375dbd5678dSMartin Matuska 	free(buf);
376dbd5678dSMartin Matuska 	fletcher_4_fini();
377dbd5678dSMartin Matuska 	zio_fini();
378dbd5678dSMartin Matuska 	zstd_fini();
379e2df9bb4SMartin Matuska 	abd_fini();
380dbd5678dSMartin Matuska 
381dbd5678dSMartin Matuska 	return (0);
382dbd5678dSMartin Matuska }
383