xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_decompress.c (revision 15f0b8c309dea1dcb14d3e374686576ff68ac43f)
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