4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or https://opensource.org/licenses/CDDL-1.0.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 2022 Axcient. All rights reserved.
24 * Use is subject to license terms.
32 #include <sys/zfs_ioctl.h>
33 #include <sys/zio_checksum.h>
34 #include <sys/zstd/zstd.h>
35 #include "zfs_fletcher.h"
39 dump_record(dmu_replay_record_t
*drr
, void *payload
, int payload_len
,
40 zio_cksum_t
*zc
, int outfd
)
42 assert(offsetof(dmu_replay_record_t
, drr_u
.drr_checksum
.drr_checksum
)
43 == sizeof (dmu_replay_record_t
) - sizeof (zio_cksum_t
));
44 fletcher_4_incremental_native(drr
,
45 offsetof(dmu_replay_record_t
, drr_u
.drr_checksum
.drr_checksum
), zc
);
46 if (drr
->drr_type
!= DRR_BEGIN
) {
47 assert(ZIO_CHECKSUM_IS_ZERO(&drr
->drr_u
.
48 drr_checksum
.drr_checksum
));
49 drr
->drr_u
.drr_checksum
.drr_checksum
= *zc
;
51 fletcher_4_incremental_native(&drr
->drr_u
.drr_checksum
.drr_checksum
,
52 sizeof (zio_cksum_t
), zc
);
53 if (write(outfd
, drr
, sizeof (*drr
)) == -1)
55 if (payload_len
!= 0) {
56 fletcher_4_incremental_native(payload
, payload_len
, zc
);
57 if (write(outfd
, payload
, payload_len
) == -1)
64 zstream_do_decompress(int argc
, char *argv
[])
66 const int KEYSIZE
= 64;
67 int bufsz
= SPA_MAXBLOCKSIZE
;
68 char *buf
= safe_malloc(bufsz
);
69 dmu_replay_record_t thedrr
;
70 dmu_replay_record_t
*drr
= &thedrr
;
71 zio_cksum_t stream_cksum
;
73 boolean_t verbose
= B_FALSE
;
75 while ((c
= getopt(argc
, argv
, "v")) != -1) {
81 (void) fprintf(stderr
, "invalid option '%c'\n",
94 if (hcreate(argc
) == 0)
96 for (int i
= 0; i
< argc
; i
++) {
97 uint64_t object
, offset
;
102 enum zio_compress type
= ZIO_COMPRESS_LZ4
;
104 obj_str
= strsep(&argv
[i
], ",");
105 if (argv
[i
] == NULL
) {
110 object
= strtoull(obj_str
, &end
, 0);
111 if (errno
|| *end
!= '\0')
112 errx(1, "invalid value for object");
113 offset_str
= strsep(&argv
[i
], ",");
114 offset
= strtoull(offset_str
, &end
, 0);
115 if (errno
|| *end
!= '\0')
116 errx(1, "invalid value for offset");
118 if (0 == strcmp("off", argv
[i
]))
119 type
= ZIO_COMPRESS_OFF
;
120 else if (0 == strcmp("lz4", argv
[i
]))
121 type
= ZIO_COMPRESS_LZ4
;
122 else if (0 == strcmp("lzjb", argv
[i
]))
123 type
= ZIO_COMPRESS_LZJB
;
124 else if (0 == strcmp("gzip", argv
[i
]))
125 type
= ZIO_COMPRESS_GZIP_1
;
126 else if (0 == strcmp("zle", argv
[i
]))
127 type
= ZIO_COMPRESS_ZLE
;
128 else if (0 == strcmp("zstd", argv
[i
]))
129 type
= ZIO_COMPRESS_ZSTD
;
131 fprintf(stderr
, "Invalid compression type %s.\n"
132 "Supported types are off, lz4, lzjb, gzip, "
139 if (asprintf(&key
, "%llu,%llu", (u_longlong_t
)object
,
140 (u_longlong_t
)offset
) < 0) {
143 ENTRY e
= {.key
= key
};
146 p
= hsearch(e
, ENTER
);
149 p
->data
= (void*)(intptr_t)type
;
152 if (isatty(STDIN_FILENO
)) {
153 (void) fprintf(stderr
,
154 "Error: The send stream is a binary format "
155 "and can not be read from a\n"
156 "terminal. Standard input must be redirected.\n");
162 boolean_t seen
= B_FALSE
;
163 while (sfread(drr
, sizeof (*drr
), stdin
) != 0) {
164 struct drr_write
*drrw
;
165 uint64_t payload_size
= 0;
168 * We need to regenerate the checksum.
170 if (drr
->drr_type
!= DRR_BEGIN
) {
171 memset(&drr
->drr_u
.drr_checksum
.drr_checksum
, 0,
172 sizeof (drr
->drr_u
.drr_checksum
.drr_checksum
));
175 switch (drr
->drr_type
) {
178 ZIO_SET_CHECKSUM(&stream_cksum
, 0, 0, 0, 0);
182 uint32_t sz
= drr
->drr_payloadlen
;
184 VERIFY3U(sz
, <=, 1U << 28);
188 buf
= realloc(buf
, sz
);
193 (void) sfread(buf
, sz
, stdin
);
200 struct drr_end
*drre
= &drr
->drr_u
.drr_end
;
202 * We would prefer to just check --begin == 0, but
203 * replication streams have an end of stream END
204 * record, so we must avoid tripping it.
206 VERIFY3B(seen
, ==, B_TRUE
);
209 * Use the recalculated checksum, unless this is
210 * the END record of a stream package, which has
213 if (!ZIO_CHECKSUM_IS_ZERO(&drre
->drr_checksum
))
214 drre
->drr_checksum
= stream_cksum
;
220 struct drr_object
*drro
= &drr
->drr_u
.drr_object
;
221 VERIFY3S(begin
, ==, 1);
223 if (drro
->drr_bonuslen
> 0) {
224 payload_size
= DRR_OBJECT_PAYLOAD_SIZE(drro
);
225 (void) sfread(buf
, payload_size
, stdin
);
232 struct drr_spill
*drrs
= &drr
->drr_u
.drr_spill
;
233 VERIFY3S(begin
, ==, 1);
234 payload_size
= DRR_SPILL_PAYLOAD_SIZE(drrs
);
235 (void) sfread(buf
, payload_size
, stdin
);
239 case DRR_WRITE_BYREF
:
240 VERIFY3S(begin
, ==, 1);
242 "Deduplicated streams are not supported\n");
248 VERIFY3S(begin
, ==, 1);
249 drrw
= &thedrr
.drr_u
.drr_write
;
250 payload_size
= DRR_WRITE_PAYLOAD_SIZE(drrw
);
254 snprintf(key
, KEYSIZE
, "%llu,%llu",
255 (u_longlong_t
)drrw
->drr_object
,
256 (u_longlong_t
)drrw
->drr_offset
);
257 ENTRY e
= {.key
= key
};
259 p
= hsearch(e
, FIND
);
261 zio_decompress_func_t
*xfunc
= NULL
;
262 switch ((enum zio_compress
)(intptr_t)p
->data
) {
263 case ZIO_COMPRESS_OFF
:
266 case ZIO_COMPRESS_LZJB
:
267 xfunc
= lzjb_decompress
;
269 case ZIO_COMPRESS_GZIP_1
:
270 xfunc
= gzip_decompress
;
272 case ZIO_COMPRESS_ZLE
:
273 xfunc
= zle_decompress
;
275 case ZIO_COMPRESS_LZ4
:
276 xfunc
= lz4_decompress_zfs
;
278 case ZIO_COMPRESS_ZSTD
:
279 xfunc
= zfs_zstd_decompress
;
287 * Read and decompress the block
289 char *lzbuf
= safe_calloc(payload_size
);
290 (void) sfread(lzbuf
, payload_size
, stdin
);
292 memcpy(buf
, lzbuf
, payload_size
);
293 drrw
->drr_compressiontype
=
296 fprintf(stderr
, "Resetting "
297 "compression type to off "
298 "for ino %llu offset "
304 } else if (0 != xfunc(lzbuf
, buf
,
305 payload_size
, payload_size
, 0)) {
307 * The block must not be compressed,
308 * at least not with this compression
309 * type, possibly because it gets
310 * written multiple times in this
313 warnx("decompression failed for "
314 "ino %llu offset %llu",
315 (u_longlong_t
)drrw
->drr_object
,
316 (u_longlong_t
)drrw
->drr_offset
);
317 memcpy(buf
, lzbuf
, payload_size
);
318 } else if (verbose
) {
319 drrw
->drr_compressiontype
=
321 fprintf(stderr
, "successfully "
322 "decompressed ino %llu "
324 (u_longlong_t
)drrw
->drr_object
,
325 (u_longlong_t
)drrw
->drr_offset
);
327 drrw
->drr_compressiontype
=
333 * Read the contents of the block unaltered
335 (void) sfread(buf
, payload_size
, stdin
);
340 case DRR_WRITE_EMBEDDED
:
342 VERIFY3S(begin
, ==, 1);
343 struct drr_write_embedded
*drrwe
=
344 &drr
->drr_u
.drr_write_embedded
;
346 P2ROUNDUP((uint64_t)drrwe
->drr_psize
, 8);
347 (void) sfread(buf
, payload_size
, stdin
);
351 case DRR_FREEOBJECTS
:
353 case DRR_OBJECT_RANGE
:
354 VERIFY3S(begin
, ==, 1);
358 (void) fprintf(stderr
, "INVALID record type 0x%x\n",
360 /* should never happen, so assert */
365 fprintf(stderr
, "Error: unexpected end-of-file\n");
368 if (ferror(stdout
)) {
369 fprintf(stderr
, "Error while reading file: %s\n",
375 * We need to recalculate the checksum, and it needs to be
376 * initially zero to do that. BEGIN records don't have
379 if (drr
->drr_type
!= DRR_BEGIN
) {
380 memset(&drr
->drr_u
.drr_checksum
.drr_checksum
, 0,
381 sizeof (drr
->drr_u
.drr_checksum
.drr_checksum
));
383 if (dump_record(drr
, buf
, payload_size
,
384 &stream_cksum
, STDOUT_FILENO
) != 0)
386 if (drr
->drr_type
== DRR_END
) {
388 * Typically the END record is either the last
389 * thing in the stream, or it is followed
390 * by a BEGIN record (which also zeros the checksum).
391 * However, a stream package ends with two END
392 * records. The last END record's checksum starts
395 ZIO_SET_CHECKSUM(&stream_cksum
, 0, 0, 0, 0);