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