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.
28 * Copyright (c) 2022 by Delphix. All rights reserved.
35 #include <sys/zfs_ioctl.h>
36 #include <sys/zio_checksum.h>
37 #include <sys/zstd/zstd.h>
38 #include "zfs_fletcher.h"
42 dump_record(dmu_replay_record_t
*drr
, void *payload
, int payload_len
,
43 zio_cksum_t
*zc
, int outfd
)
45 assert(offsetof(dmu_replay_record_t
, drr_u
.drr_checksum
.drr_checksum
)
46 == sizeof (dmu_replay_record_t
) - sizeof (zio_cksum_t
));
47 fletcher_4_incremental_native(drr
,
48 offsetof(dmu_replay_record_t
, drr_u
.drr_checksum
.drr_checksum
), zc
);
49 if (drr
->drr_type
!= DRR_BEGIN
) {
50 assert(ZIO_CHECKSUM_IS_ZERO(&drr
->drr_u
.
51 drr_checksum
.drr_checksum
));
52 drr
->drr_u
.drr_checksum
.drr_checksum
= *zc
;
54 fletcher_4_incremental_native(&drr
->drr_u
.drr_checksum
.drr_checksum
,
55 sizeof (zio_cksum_t
), zc
);
56 if (write(outfd
, drr
, sizeof (*drr
)) == -1)
58 if (payload_len
!= 0) {
59 fletcher_4_incremental_native(payload
, payload_len
, zc
);
60 if (write(outfd
, payload
, payload_len
) == -1)
67 zstream_do_recompress(int argc
, char *argv
[])
69 int bufsz
= SPA_MAXBLOCKSIZE
;
70 char *buf
= safe_malloc(bufsz
);
71 dmu_replay_record_t thedrr
;
72 dmu_replay_record_t
*drr
= &thedrr
;
73 zio_cksum_t stream_cksum
;
77 while ((c
= getopt(argc
, argv
, "l:")) != -1) {
80 if (sscanf(optarg
, "%d", &level
) != 0) {
82 "failed to parse level '%s'\n",
88 (void) fprintf(stderr
, "invalid option '%c'\n",
101 zio_compress_info_t
*cinfo
= NULL
;
102 if (0 == strcmp(argv
[0], "off")) {
103 type
= ZIO_COMPRESS_OFF
;
104 cinfo
= &zio_compress_table
[type
];
105 } else if (0 == strcmp(argv
[0], "inherit") ||
106 0 == strcmp(argv
[0], "empty") ||
107 0 == strcmp(argv
[0], "on")) {
108 // Fall through to invalid compression type case
110 for (int i
= 0; i
< ZIO_COMPRESS_FUNCTIONS
; i
++) {
111 if (0 == strcmp(zio_compress_table
[i
].ci_name
,
113 cinfo
= &zio_compress_table
[i
];
120 fprintf(stderr
, "Invalid compression type %s.\n",
125 if (cinfo
->ci_compress
== NULL
) {
127 cinfo
= &zio_compress_table
[0];
130 if (isatty(STDIN_FILENO
)) {
131 (void) fprintf(stderr
,
132 "Error: The send stream is a binary format "
133 "and can not be read from a\n"
134 "terminal. Standard input must be redirected.\n");
142 boolean_t seen
= B_FALSE
;
143 while (sfread(drr
, sizeof (*drr
), stdin
) != 0) {
144 struct drr_write
*drrw
;
145 uint64_t payload_size
= 0;
148 * We need to regenerate the checksum.
150 if (drr
->drr_type
!= DRR_BEGIN
) {
151 memset(&drr
->drr_u
.drr_checksum
.drr_checksum
, 0,
152 sizeof (drr
->drr_u
.drr_checksum
.drr_checksum
));
156 switch (drr
->drr_type
) {
159 ZIO_SET_CHECKSUM(&stream_cksum
, 0, 0, 0, 0);
163 uint32_t sz
= drr
->drr_payloadlen
;
165 VERIFY3U(sz
, <=, 1U << 28);
169 buf
= realloc(buf
, sz
);
174 (void) sfread(buf
, sz
, stdin
);
181 struct drr_end
*drre
= &drr
->drr_u
.drr_end
;
183 * We would prefer to just check --begin == 0, but
184 * replication streams have an end of stream END
185 * record, so we must avoid tripping it.
187 VERIFY3B(seen
, ==, B_TRUE
);
190 * Use the recalculated checksum, unless this is
191 * the END record of a stream package, which has
194 if (!ZIO_CHECKSUM_IS_ZERO(&drre
->drr_checksum
))
195 drre
->drr_checksum
= stream_cksum
;
201 struct drr_object
*drro
= &drr
->drr_u
.drr_object
;
202 VERIFY3S(begin
, ==, 1);
204 if (drro
->drr_bonuslen
> 0) {
205 payload_size
= DRR_OBJECT_PAYLOAD_SIZE(drro
);
206 (void) sfread(buf
, payload_size
, stdin
);
213 struct drr_spill
*drrs
= &drr
->drr_u
.drr_spill
;
214 VERIFY3S(begin
, ==, 1);
215 payload_size
= DRR_SPILL_PAYLOAD_SIZE(drrs
);
216 (void) sfread(buf
, payload_size
, stdin
);
220 case DRR_WRITE_BYREF
:
221 VERIFY3S(begin
, ==, 1);
223 "Deduplicated streams are not supported\n");
229 VERIFY3S(begin
, ==, 1);
230 drrw
= &thedrr
.drr_u
.drr_write
;
231 payload_size
= DRR_WRITE_PAYLOAD_SIZE(drrw
);
233 * In order to recompress an encrypted block, you have
234 * to decrypt, decompress, recompress, and
235 * re-encrypt. That can be a future enhancement (along
236 * with decryption or re-encryption), but for now we
237 * skip encrypted blocks.
239 boolean_t encrypted
= B_FALSE
;
240 for (int i
= 0; i
< ZIO_DATA_SALT_LEN
; i
++) {
241 if (drrw
->drr_salt
[i
] != 0) {
247 (void) sfread(buf
, payload_size
, stdin
);
250 if (drrw
->drr_compressiontype
>=
251 ZIO_COMPRESS_FUNCTIONS
) {
252 fprintf(stderr
, "Invalid compression type in "
253 "stream: %d\n", drrw
->drr_compressiontype
);
256 zio_compress_info_t
*dinfo
=
257 &zio_compress_table
[drrw
->drr_compressiontype
];
259 /* Set up buffers to minimize memcpys */
261 if (cinfo
->ci_compress
== NULL
)
264 dbuf
= safe_calloc(bufsz
);
266 if (dinfo
->ci_decompress
== NULL
)
269 cbuf
= safe_calloc(payload_size
);
271 /* Read and decompress the payload */
272 (void) sfread(cbuf
, payload_size
, stdin
);
273 if (dinfo
->ci_decompress
!= NULL
) {
274 if (0 != dinfo
->ci_decompress(cbuf
, dbuf
,
275 payload_size
, MIN(bufsz
,
276 drrw
->drr_logical_size
), dinfo
->ci_level
)) {
277 warnx("decompression type %d failed "
278 "for ino %llu offset %llu",
280 (u_longlong_t
)drrw
->drr_object
,
281 (u_longlong_t
)drrw
->drr_offset
);
284 payload_size
= drrw
->drr_logical_size
;
288 /* Recompress the payload */
289 if (cinfo
->ci_compress
!= NULL
) {
290 payload_size
= P2ROUNDUP(cinfo
->ci_compress(
291 dbuf
, buf
, drrw
->drr_logical_size
,
292 MIN(payload_size
, bufsz
), (level
== -1 ?
293 cinfo
->ci_level
: level
)),
295 if (payload_size
!= drrw
->drr_logical_size
) {
296 drrw
->drr_compressiontype
= type
;
297 drrw
->drr_compressed_size
=
300 memcpy(buf
, dbuf
, payload_size
);
301 drrw
->drr_compressiontype
= 0;
302 drrw
->drr_compressed_size
= 0;
306 drrw
->drr_compressiontype
= type
;
307 drrw
->drr_compressed_size
= 0;
312 case DRR_WRITE_EMBEDDED
:
314 struct drr_write_embedded
*drrwe
=
315 &drr
->drr_u
.drr_write_embedded
;
316 VERIFY3S(begin
, ==, 1);
318 P2ROUNDUP((uint64_t)drrwe
->drr_psize
, 8);
319 (void) sfread(buf
, payload_size
, stdin
);
323 case DRR_FREEOBJECTS
:
325 case DRR_OBJECT_RANGE
:
326 VERIFY3S(begin
, ==, 1);
330 (void) fprintf(stderr
, "INVALID record type 0x%x\n",
332 /* should never happen, so assert */
337 fprintf(stderr
, "Error: unexpected end-of-file\n");
340 if (ferror(stdout
)) {
341 fprintf(stderr
, "Error while reading file: %s\n",
347 * We need to recalculate the checksum, and it needs to be
348 * initially zero to do that. BEGIN records don't have
351 if (drr
->drr_type
!= DRR_BEGIN
) {
352 memset(&drr
->drr_u
.drr_checksum
.drr_checksum
, 0,
353 sizeof (drr
->drr_u
.drr_checksum
.drr_checksum
));
355 if (dump_record(drr
, buf
, payload_size
,
356 &stream_cksum
, STDOUT_FILENO
) != 0)
358 if (drr
->drr_type
== DRR_END
) {
360 * Typically the END record is either the last
361 * thing in the stream, or it is followed
362 * by a BEGIN record (which also zeros the checksum).
363 * However, a stream package ends with two END
364 * records. The last END record's checksum starts
367 ZIO_SET_CHECKSUM(&stream_cksum
, 0, 0, 0, 0);