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 http://www.opensolaris.org/os/licensing.
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("lz4", argv
[i
]))
119 type
= ZIO_COMPRESS_LZ4
;
120 else if (0 == strcmp("lzjb", argv
[i
]))
121 type
= ZIO_COMPRESS_LZJB
;
122 else if (0 == strcmp("gzip", argv
[i
]))
123 type
= ZIO_COMPRESS_GZIP_1
;
124 else if (0 == strcmp("zle", argv
[i
]))
125 type
= ZIO_COMPRESS_ZLE
;
126 else if (0 == strcmp("zstd", argv
[i
]))
127 type
= ZIO_COMPRESS_ZSTD
;
129 fprintf(stderr
, "Invalid compression type %s.\n"
130 "Supported types are lz4, lzjb, gzip, zle, "
137 if (asprintf(&key
, "%llu,%llu", (u_longlong_t
)object
,
138 (u_longlong_t
)offset
) < 0) {
141 ENTRY e
= {.key
= key
};
144 p
= hsearch(e
, ENTER
);
147 p
->data
= (void*)type
;
150 if (isatty(STDIN_FILENO
)) {
151 (void) fprintf(stderr
,
152 "Error: The send stream is a binary format "
153 "and can not be read from a\n"
154 "terminal. Standard input must be redirected.\n");
159 while (sfread(drr
, sizeof (*drr
), stdin
) != 0) {
160 struct drr_write
*drrw
;
161 uint64_t payload_size
= 0;
164 * We need to regenerate the checksum.
166 if (drr
->drr_type
!= DRR_BEGIN
) {
167 memset(&drr
->drr_u
.drr_checksum
.drr_checksum
, 0,
168 sizeof (drr
->drr_u
.drr_checksum
.drr_checksum
));
171 switch (drr
->drr_type
) {
174 ZIO_SET_CHECKSUM(&stream_cksum
, 0, 0, 0, 0);
176 int sz
= drr
->drr_payloadlen
;
179 buf
= realloc(buf
, sz
);
184 (void) sfread(buf
, sz
, stdin
);
191 struct drr_end
*drre
= &drr
->drr_u
.drr_end
;
193 * Use the recalculated checksum, unless this is
194 * the END record of a stream package, which has
197 if (!ZIO_CHECKSUM_IS_ZERO(&drre
->drr_checksum
))
198 drre
->drr_checksum
= stream_cksum
;
204 struct drr_object
*drro
= &drr
->drr_u
.drr_object
;
206 if (drro
->drr_bonuslen
> 0) {
207 payload_size
= DRR_OBJECT_PAYLOAD_SIZE(drro
);
208 (void) sfread(buf
, payload_size
, stdin
);
215 struct drr_spill
*drrs
= &drr
->drr_u
.drr_spill
;
216 payload_size
= DRR_SPILL_PAYLOAD_SIZE(drrs
);
217 (void) sfread(buf
, payload_size
, stdin
);
221 case DRR_WRITE_BYREF
:
223 "Deduplicated streams are not supported\n");
229 drrw
= &thedrr
.drr_u
.drr_write
;
230 payload_size
= DRR_WRITE_PAYLOAD_SIZE(drrw
);
234 snprintf(key
, KEYSIZE
, "%llu,%llu",
235 (u_longlong_t
)drrw
->drr_object
,
236 (u_longlong_t
)drrw
->drr_offset
);
237 ENTRY e
= {.key
= key
};
239 p
= hsearch(e
, FIND
);
241 zio_decompress_func_t
*xfunc
= NULL
;
242 switch ((enum zio_compress
)(intptr_t)p
->data
) {
243 case ZIO_COMPRESS_LZJB
:
244 xfunc
= lzjb_decompress
;
246 case ZIO_COMPRESS_GZIP_1
:
247 xfunc
= gzip_decompress
;
249 case ZIO_COMPRESS_ZLE
:
250 xfunc
= zle_decompress
;
252 case ZIO_COMPRESS_LZ4
:
253 xfunc
= lz4_decompress_zfs
;
255 case ZIO_COMPRESS_ZSTD
:
256 xfunc
= zfs_zstd_decompress
;
261 assert(xfunc
!= NULL
);
265 * Read and decompress the block
267 char *lzbuf
= safe_calloc(payload_size
);
268 (void) sfread(lzbuf
, payload_size
, stdin
);
269 if (0 != xfunc(lzbuf
, buf
,
270 payload_size
, payload_size
, 0)) {
272 * The block must not be compressed,
273 * possibly because it gets written
274 * multiple times in this stream.
276 warnx("decompression failed for "
277 "ino %llu offset %llu",
278 (u_longlong_t
)drrw
->drr_object
,
279 (u_longlong_t
)drrw
->drr_offset
);
280 memcpy(buf
, lzbuf
, payload_size
);
281 } else if (verbose
) {
282 fprintf(stderr
, "successfully "
283 "decompressed ino %llu "
285 (u_longlong_t
)drrw
->drr_object
,
286 (u_longlong_t
)drrw
->drr_offset
);
291 * Read the contents of the block unaltered
293 (void) sfread(buf
, payload_size
, stdin
);
298 case DRR_WRITE_EMBEDDED
:
300 struct drr_write_embedded
*drrwe
=
301 &drr
->drr_u
.drr_write_embedded
;
303 P2ROUNDUP((uint64_t)drrwe
->drr_psize
, 8);
304 (void) sfread(buf
, payload_size
, stdin
);
308 case DRR_FREEOBJECTS
:
310 case DRR_OBJECT_RANGE
:
314 (void) fprintf(stderr
, "INVALID record type 0x%x\n",
316 /* should never happen, so assert */
321 fprintf(stderr
, "Error: unexpected end-of-file\n");
324 if (ferror(stdout
)) {
325 fprintf(stderr
, "Error while reading file: %s\n",
331 * We need to recalculate the checksum, and it needs to be
332 * initially zero to do that. BEGIN records don't have
335 if (drr
->drr_type
!= DRR_BEGIN
) {
336 memset(&drr
->drr_u
.drr_checksum
.drr_checksum
, 0,
337 sizeof (drr
->drr_u
.drr_checksum
.drr_checksum
));
339 if (dump_record(drr
, buf
, payload_size
,
340 &stream_cksum
, STDOUT_FILENO
) != 0)
342 if (drr
->drr_type
== DRR_END
) {
344 * Typically the END record is either the last
345 * thing in the stream, or it is followed
346 * by a BEGIN record (which also zeros the checksum).
347 * However, a stream package ends with two END
348 * records. The last END record's checksum starts
351 ZIO_SET_CHECKSUM(&stream_cksum
, 0, 0, 0, 0);