1dbd5678dSMartin Matuska /* 2dbd5678dSMartin Matuska * CDDL HEADER START 3dbd5678dSMartin Matuska * 4dbd5678dSMartin Matuska * The contents of this file are subject to the terms of the 5dbd5678dSMartin Matuska * Common Development and Distribution License (the "License"). 6dbd5678dSMartin Matuska * You may not use this file except in compliance with the License. 7dbd5678dSMartin Matuska * 8dbd5678dSMartin Matuska * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9dbd5678dSMartin Matuska * or https://opensource.org/licenses/CDDL-1.0. 10dbd5678dSMartin Matuska * See the License for the specific language governing permissions 11dbd5678dSMartin Matuska * and limitations under the License. 12dbd5678dSMartin Matuska * 13dbd5678dSMartin Matuska * When distributing Covered Code, include this CDDL HEADER in each 14dbd5678dSMartin Matuska * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15dbd5678dSMartin Matuska * If applicable, add the following below this CDDL HEADER, with the 16dbd5678dSMartin Matuska * fields enclosed by brackets "[]" replaced with your own identifying 17dbd5678dSMartin Matuska * information: Portions Copyright [yyyy] [name of copyright owner] 18dbd5678dSMartin Matuska * 19dbd5678dSMartin Matuska * CDDL HEADER END 20dbd5678dSMartin Matuska */ 21dbd5678dSMartin Matuska 22dbd5678dSMartin Matuska /* 23dbd5678dSMartin Matuska * Copyright 2022 Axcient. All rights reserved. 24dbd5678dSMartin Matuska * Use is subject to license terms. 25dbd5678dSMartin Matuska */ 26dbd5678dSMartin Matuska 27dbd5678dSMartin Matuska /* 28dbd5678dSMartin Matuska * Copyright (c) 2022 by Delphix. All rights reserved. 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 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 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; 75dbd5678dSMartin Matuska int level = -1; 76dbd5678dSMartin Matuska 77dbd5678dSMartin Matuska while ((c = getopt(argc, argv, "l:")) != -1) { 78dbd5678dSMartin Matuska switch (c) { 79dbd5678dSMartin Matuska case 'l': 80dbd5678dSMartin Matuska if (sscanf(optarg, "%d", &level) != 0) { 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(); 100dbd5678dSMartin Matuska int type = 0; 101dbd5678dSMartin Matuska zio_compress_info_t *cinfo = NULL; 102dbd5678dSMartin Matuska if (0 == strcmp(argv[0], "off")) { 103dbd5678dSMartin Matuska type = ZIO_COMPRESS_OFF; 104dbd5678dSMartin Matuska cinfo = &zio_compress_table[type]; 105dbd5678dSMartin Matuska } else if (0 == strcmp(argv[0], "inherit") || 106dbd5678dSMartin Matuska 0 == strcmp(argv[0], "empty") || 107dbd5678dSMartin Matuska 0 == strcmp(argv[0], "on")) { 108dbd5678dSMartin Matuska // Fall through to invalid compression type case 109dbd5678dSMartin Matuska } else { 110dbd5678dSMartin Matuska for (int i = 0; i < ZIO_COMPRESS_FUNCTIONS; i++) { 111dbd5678dSMartin Matuska if (0 == strcmp(zio_compress_table[i].ci_name, 112dbd5678dSMartin Matuska argv[0])) { 113dbd5678dSMartin Matuska cinfo = &zio_compress_table[i]; 114dbd5678dSMartin Matuska type = i; 115dbd5678dSMartin Matuska break; 116dbd5678dSMartin Matuska } 117dbd5678dSMartin Matuska } 118dbd5678dSMartin Matuska } 119dbd5678dSMartin Matuska if (cinfo == NULL) { 120dbd5678dSMartin Matuska fprintf(stderr, "Invalid compression type %s.\n", 121dbd5678dSMartin Matuska argv[0]); 122dbd5678dSMartin Matuska exit(2); 123dbd5678dSMartin Matuska } 124dbd5678dSMartin Matuska 125dbd5678dSMartin Matuska if (cinfo->ci_compress == NULL) { 126dbd5678dSMartin Matuska type = 0; 127dbd5678dSMartin Matuska cinfo = &zio_compress_table[0]; 128dbd5678dSMartin Matuska } 129dbd5678dSMartin Matuska 130dbd5678dSMartin Matuska if (isatty(STDIN_FILENO)) { 131dbd5678dSMartin Matuska (void) fprintf(stderr, 132dbd5678dSMartin Matuska "Error: The send stream is a binary format " 133dbd5678dSMartin Matuska "and can not be read from a\n" 134dbd5678dSMartin Matuska "terminal. Standard input must be redirected.\n"); 135dbd5678dSMartin Matuska exit(1); 136dbd5678dSMartin Matuska } 137dbd5678dSMartin Matuska 138dbd5678dSMartin Matuska fletcher_4_init(); 139dbd5678dSMartin Matuska zio_init(); 140dbd5678dSMartin Matuska zstd_init(); 141*15f0b8c3SMartin Matuska int begin = 0; 142*15f0b8c3SMartin Matuska boolean_t seen = B_FALSE; 143dbd5678dSMartin Matuska while (sfread(drr, sizeof (*drr), stdin) != 0) { 144dbd5678dSMartin Matuska struct drr_write *drrw; 145dbd5678dSMartin Matuska uint64_t payload_size = 0; 146dbd5678dSMartin Matuska 147dbd5678dSMartin Matuska /* 148dbd5678dSMartin Matuska * We need to regenerate the checksum. 149dbd5678dSMartin Matuska */ 150dbd5678dSMartin Matuska if (drr->drr_type != DRR_BEGIN) { 151dbd5678dSMartin Matuska memset(&drr->drr_u.drr_checksum.drr_checksum, 0, 152dbd5678dSMartin Matuska sizeof (drr->drr_u.drr_checksum.drr_checksum)); 153dbd5678dSMartin Matuska } 154dbd5678dSMartin Matuska 155dbd5678dSMartin Matuska 156dbd5678dSMartin Matuska switch (drr->drr_type) { 157dbd5678dSMartin Matuska case DRR_BEGIN: 158dbd5678dSMartin Matuska { 159dbd5678dSMartin Matuska ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0); 160*15f0b8c3SMartin Matuska VERIFY0(begin++); 161*15f0b8c3SMartin Matuska seen = B_TRUE; 162dbd5678dSMartin Matuska 163*15f0b8c3SMartin Matuska uint32_t sz = drr->drr_payloadlen; 164*15f0b8c3SMartin Matuska 165*15f0b8c3SMartin Matuska VERIFY3U(sz, <=, 1U << 28); 166*15f0b8c3SMartin Matuska 167dbd5678dSMartin Matuska if (sz != 0) { 168dbd5678dSMartin Matuska if (sz > bufsz) { 169dbd5678dSMartin Matuska buf = realloc(buf, sz); 170dbd5678dSMartin Matuska if (buf == NULL) 171dbd5678dSMartin Matuska err(1, "realloc"); 172dbd5678dSMartin Matuska bufsz = sz; 173dbd5678dSMartin Matuska } 174dbd5678dSMartin Matuska (void) sfread(buf, sz, stdin); 175dbd5678dSMartin Matuska } 176dbd5678dSMartin Matuska payload_size = sz; 177dbd5678dSMartin Matuska break; 178dbd5678dSMartin Matuska } 179dbd5678dSMartin Matuska case DRR_END: 180dbd5678dSMartin Matuska { 181dbd5678dSMartin Matuska struct drr_end *drre = &drr->drr_u.drr_end; 182dbd5678dSMartin Matuska /* 183*15f0b8c3SMartin Matuska * We would prefer to just check --begin == 0, but 184*15f0b8c3SMartin Matuska * replication streams have an end of stream END 185*15f0b8c3SMartin Matuska * record, so we must avoid tripping it. 186*15f0b8c3SMartin Matuska */ 187*15f0b8c3SMartin Matuska VERIFY3B(seen, ==, B_TRUE); 188*15f0b8c3SMartin Matuska begin--; 189*15f0b8c3SMartin Matuska /* 190dbd5678dSMartin Matuska * Use the recalculated checksum, unless this is 191dbd5678dSMartin Matuska * the END record of a stream package, which has 192dbd5678dSMartin Matuska * no checksum. 193dbd5678dSMartin Matuska */ 194dbd5678dSMartin Matuska if (!ZIO_CHECKSUM_IS_ZERO(&drre->drr_checksum)) 195dbd5678dSMartin Matuska drre->drr_checksum = stream_cksum; 196dbd5678dSMartin Matuska break; 197dbd5678dSMartin Matuska } 198dbd5678dSMartin Matuska 199dbd5678dSMartin Matuska case DRR_OBJECT: 200dbd5678dSMartin Matuska { 201dbd5678dSMartin Matuska struct drr_object *drro = &drr->drr_u.drr_object; 202*15f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 203dbd5678dSMartin Matuska 204dbd5678dSMartin Matuska if (drro->drr_bonuslen > 0) { 205dbd5678dSMartin Matuska payload_size = DRR_OBJECT_PAYLOAD_SIZE(drro); 206dbd5678dSMartin Matuska (void) sfread(buf, payload_size, stdin); 207dbd5678dSMartin Matuska } 208dbd5678dSMartin Matuska break; 209dbd5678dSMartin Matuska } 210dbd5678dSMartin Matuska 211dbd5678dSMartin Matuska case DRR_SPILL: 212dbd5678dSMartin Matuska { 213dbd5678dSMartin Matuska struct drr_spill *drrs = &drr->drr_u.drr_spill; 214*15f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 215dbd5678dSMartin Matuska payload_size = DRR_SPILL_PAYLOAD_SIZE(drrs); 216dbd5678dSMartin Matuska (void) sfread(buf, payload_size, stdin); 217dbd5678dSMartin Matuska break; 218dbd5678dSMartin Matuska } 219dbd5678dSMartin Matuska 220dbd5678dSMartin Matuska case DRR_WRITE_BYREF: 221*15f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 222dbd5678dSMartin Matuska fprintf(stderr, 223dbd5678dSMartin Matuska "Deduplicated streams are not supported\n"); 224dbd5678dSMartin Matuska exit(1); 225dbd5678dSMartin Matuska break; 226dbd5678dSMartin Matuska 227dbd5678dSMartin Matuska case DRR_WRITE: 228dbd5678dSMartin Matuska { 229*15f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 230dbd5678dSMartin Matuska drrw = &thedrr.drr_u.drr_write; 231dbd5678dSMartin Matuska payload_size = DRR_WRITE_PAYLOAD_SIZE(drrw); 232dbd5678dSMartin Matuska /* 233dbd5678dSMartin Matuska * In order to recompress an encrypted block, you have 234dbd5678dSMartin Matuska * to decrypt, decompress, recompress, and 235dbd5678dSMartin Matuska * re-encrypt. That can be a future enhancement (along 236dbd5678dSMartin Matuska * with decryption or re-encryption), but for now we 237dbd5678dSMartin Matuska * skip encrypted blocks. 238dbd5678dSMartin Matuska */ 239dbd5678dSMartin Matuska boolean_t encrypted = B_FALSE; 240dbd5678dSMartin Matuska for (int i = 0; i < ZIO_DATA_SALT_LEN; i++) { 241dbd5678dSMartin Matuska if (drrw->drr_salt[i] != 0) { 242dbd5678dSMartin Matuska encrypted = B_TRUE; 243dbd5678dSMartin Matuska break; 244dbd5678dSMartin Matuska } 245dbd5678dSMartin Matuska } 246dbd5678dSMartin Matuska if (encrypted) { 247dbd5678dSMartin Matuska (void) sfread(buf, payload_size, stdin); 248dbd5678dSMartin Matuska break; 249dbd5678dSMartin Matuska } 250dbd5678dSMartin Matuska if (drrw->drr_compressiontype >= 251dbd5678dSMartin Matuska ZIO_COMPRESS_FUNCTIONS) { 252dbd5678dSMartin Matuska fprintf(stderr, "Invalid compression type in " 253dbd5678dSMartin Matuska "stream: %d\n", drrw->drr_compressiontype); 254dbd5678dSMartin Matuska exit(3); 255dbd5678dSMartin Matuska } 256dbd5678dSMartin Matuska zio_compress_info_t *dinfo = 257dbd5678dSMartin Matuska &zio_compress_table[drrw->drr_compressiontype]; 258dbd5678dSMartin Matuska 259dbd5678dSMartin Matuska /* Set up buffers to minimize memcpys */ 260dbd5678dSMartin Matuska char *cbuf, *dbuf; 261dbd5678dSMartin Matuska if (cinfo->ci_compress == NULL) 262dbd5678dSMartin Matuska dbuf = buf; 263dbd5678dSMartin Matuska else 264dbd5678dSMartin Matuska dbuf = safe_calloc(bufsz); 265dbd5678dSMartin Matuska 266dbd5678dSMartin Matuska if (dinfo->ci_decompress == NULL) 267dbd5678dSMartin Matuska cbuf = dbuf; 268dbd5678dSMartin Matuska else 269dbd5678dSMartin Matuska cbuf = safe_calloc(payload_size); 270dbd5678dSMartin Matuska 271dbd5678dSMartin Matuska /* Read and decompress the payload */ 272dbd5678dSMartin Matuska (void) sfread(cbuf, payload_size, stdin); 273dbd5678dSMartin Matuska if (dinfo->ci_decompress != NULL) { 274dbd5678dSMartin Matuska if (0 != dinfo->ci_decompress(cbuf, dbuf, 275dbd5678dSMartin Matuska payload_size, MIN(bufsz, 276dbd5678dSMartin Matuska drrw->drr_logical_size), dinfo->ci_level)) { 277dbd5678dSMartin Matuska warnx("decompression type %d failed " 278dbd5678dSMartin Matuska "for ino %llu offset %llu", 279dbd5678dSMartin Matuska type, 280dbd5678dSMartin Matuska (u_longlong_t)drrw->drr_object, 281dbd5678dSMartin Matuska (u_longlong_t)drrw->drr_offset); 282dbd5678dSMartin Matuska exit(4); 283dbd5678dSMartin Matuska } 284dbd5678dSMartin Matuska payload_size = drrw->drr_logical_size; 285dbd5678dSMartin Matuska free(cbuf); 286dbd5678dSMartin Matuska } 287dbd5678dSMartin Matuska 288dbd5678dSMartin Matuska /* Recompress the payload */ 289dbd5678dSMartin Matuska if (cinfo->ci_compress != NULL) { 290dbd5678dSMartin Matuska payload_size = P2ROUNDUP(cinfo->ci_compress( 291dbd5678dSMartin Matuska dbuf, buf, drrw->drr_logical_size, 292dbd5678dSMartin Matuska MIN(payload_size, bufsz), (level == -1 ? 293dbd5678dSMartin Matuska cinfo->ci_level : level)), 294dbd5678dSMartin Matuska SPA_MINBLOCKSIZE); 295dbd5678dSMartin Matuska if (payload_size != drrw->drr_logical_size) { 296dbd5678dSMartin Matuska drrw->drr_compressiontype = type; 297dbd5678dSMartin Matuska drrw->drr_compressed_size = 298dbd5678dSMartin Matuska payload_size; 299dbd5678dSMartin Matuska } else { 300dbd5678dSMartin Matuska memcpy(buf, dbuf, payload_size); 301dbd5678dSMartin Matuska drrw->drr_compressiontype = 0; 302dbd5678dSMartin Matuska drrw->drr_compressed_size = 0; 303dbd5678dSMartin Matuska } 304dbd5678dSMartin Matuska free(dbuf); 305dbd5678dSMartin Matuska } else { 306dbd5678dSMartin Matuska drrw->drr_compressiontype = type; 307dbd5678dSMartin Matuska drrw->drr_compressed_size = 0; 308dbd5678dSMartin Matuska } 309dbd5678dSMartin Matuska break; 310dbd5678dSMartin Matuska } 311dbd5678dSMartin Matuska 312dbd5678dSMartin Matuska case DRR_WRITE_EMBEDDED: 313dbd5678dSMartin Matuska { 314dbd5678dSMartin Matuska struct drr_write_embedded *drrwe = 315dbd5678dSMartin Matuska &drr->drr_u.drr_write_embedded; 316*15f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 317dbd5678dSMartin Matuska payload_size = 318dbd5678dSMartin Matuska P2ROUNDUP((uint64_t)drrwe->drr_psize, 8); 319dbd5678dSMartin Matuska (void) sfread(buf, payload_size, stdin); 320dbd5678dSMartin Matuska break; 321dbd5678dSMartin Matuska } 322dbd5678dSMartin Matuska 323dbd5678dSMartin Matuska case DRR_FREEOBJECTS: 324dbd5678dSMartin Matuska case DRR_FREE: 325dbd5678dSMartin Matuska case DRR_OBJECT_RANGE: 326*15f0b8c3SMartin Matuska VERIFY3S(begin, ==, 1); 327dbd5678dSMartin Matuska break; 328dbd5678dSMartin Matuska 329dbd5678dSMartin Matuska default: 330dbd5678dSMartin Matuska (void) fprintf(stderr, "INVALID record type 0x%x\n", 331dbd5678dSMartin Matuska drr->drr_type); 332dbd5678dSMartin Matuska /* should never happen, so assert */ 333dbd5678dSMartin Matuska assert(B_FALSE); 334dbd5678dSMartin Matuska } 335dbd5678dSMartin Matuska 336dbd5678dSMartin Matuska if (feof(stdout)) { 337dbd5678dSMartin Matuska fprintf(stderr, "Error: unexpected end-of-file\n"); 338dbd5678dSMartin Matuska exit(1); 339dbd5678dSMartin Matuska } 340dbd5678dSMartin Matuska if (ferror(stdout)) { 341dbd5678dSMartin Matuska fprintf(stderr, "Error while reading file: %s\n", 342dbd5678dSMartin Matuska strerror(errno)); 343dbd5678dSMartin Matuska exit(1); 344dbd5678dSMartin Matuska } 345dbd5678dSMartin Matuska 346dbd5678dSMartin Matuska /* 347dbd5678dSMartin Matuska * We need to recalculate the checksum, and it needs to be 348dbd5678dSMartin Matuska * initially zero to do that. BEGIN records don't have 349dbd5678dSMartin Matuska * a checksum. 350dbd5678dSMartin Matuska */ 351dbd5678dSMartin Matuska if (drr->drr_type != DRR_BEGIN) { 352dbd5678dSMartin Matuska memset(&drr->drr_u.drr_checksum.drr_checksum, 0, 353dbd5678dSMartin Matuska sizeof (drr->drr_u.drr_checksum.drr_checksum)); 354dbd5678dSMartin Matuska } 355dbd5678dSMartin Matuska if (dump_record(drr, buf, payload_size, 356dbd5678dSMartin Matuska &stream_cksum, STDOUT_FILENO) != 0) 357dbd5678dSMartin Matuska break; 358dbd5678dSMartin Matuska if (drr->drr_type == DRR_END) { 359dbd5678dSMartin Matuska /* 360dbd5678dSMartin Matuska * Typically the END record is either the last 361dbd5678dSMartin Matuska * thing in the stream, or it is followed 362dbd5678dSMartin Matuska * by a BEGIN record (which also zeros the checksum). 363dbd5678dSMartin Matuska * However, a stream package ends with two END 364dbd5678dSMartin Matuska * records. The last END record's checksum starts 365dbd5678dSMartin Matuska * from zero. 366dbd5678dSMartin Matuska */ 367dbd5678dSMartin Matuska ZIO_SET_CHECKSUM(&stream_cksum, 0, 0, 0, 0); 368dbd5678dSMartin Matuska } 369dbd5678dSMartin Matuska } 370dbd5678dSMartin Matuska free(buf); 371dbd5678dSMartin Matuska fletcher_4_fini(); 372dbd5678dSMartin Matuska zio_fini(); 373dbd5678dSMartin Matuska zstd_fini(); 374dbd5678dSMartin Matuska 375dbd5678dSMartin Matuska return (0); 376dbd5678dSMartin Matuska } 377