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