xref: /freebsd/sys/contrib/openzfs/cmd/zstream/zstream_decompress.c (revision 2f10ffc003be396f3fc23cd2888023896560252b)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * This file and its contents are supplied under the terms of the
4  * Common Development and Distribution License ("CDDL"), version 1.0.
5  * You may only use this file in accordance with the terms of version
6  * 1.0 of the CDDL.
7  *
8  * A full copy of the text of the CDDL should have accompanied this
9  * source.  A copy of the CDDL is also available via the Internet at
10  * https://opensource.org/license/CDDL-1.0.
11  */
12 
13 /*
14  * Copyright 2022 Axcient.  All rights reserved.
15  * Use is subject to license terms.
16  *
17  * Copyright (c) 2024, Klara, Inc.
18  * Copyright (c) 2026 by Garth Snyder
19  */
20 
21 #include <err.h>
22 #include <errno.h>
23 #include <search.h>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/stdtypes.h>
29 #include <sys/zfs_ioctl.h>
30 #include <sys/zio_compress.h>
31 #include <unistd.h>
32 
33 #include "zstream.h"
34 #include "zstream_modules.h"
35 #include "zstream_util.h"
36 
37 #define	KEYSIZE 64
38 
39 static disposition_t
chain_decompress_named_writes(void * item_in,void * context)40 chain_decompress_named_writes(void *item_in, void *context)
41 {
42 	(void) context;
43 	drr_packet_t *item = (drr_packet_t *)item_in;
44 
45 	if (item == NULL) {
46 		return (D_OK);
47 	}
48 
49 	dmu_replay_record_t *drr = &item->dp_drr;
50 	struct drr_write *drrw = &drr->drr_u.drr_write;
51 	char key[KEYSIZE];
52 	uint8_t *dcbuff;
53 
54 	if (drr->drr_type != DRR_WRITE) {
55 		return (D_OK);
56 	}
57 
58 	snprintf(key, KEYSIZE, "%llu,%llu",
59 	    (u_longlong_t)drrw->drr_object, (u_longlong_t)drrw->drr_offset);
60 	ENTRY e = { .key = key };
61 	ENTRY *p = hsearch(e, FIND);
62 	if (p == NULL) {
63 		return (D_OK);
64 	}
65 
66 	enum zio_compress ctype = (enum zio_compress)(intptr_t)p->data;
67 	if (ctype == ZIO_COMPRESS_INHERIT) {
68 		/* Unspecified */
69 		ctype = drrw->drr_compressiontype;
70 	}
71 	if (ctype_is_uncompressed(ctype)) {
72 		drrw->drr_compressiontype = 0;
73 		drrw->drr_logical_size = drrw->drr_compressed_size;
74 		drrw->drr_compressed_size = 0;
75 		if (OPTION_ENABLED(CA_VERBOSE)) {
76 			fprintf(stderr,
77 			    "Resetting compression type to "
78 			    "off for ino %llu offset %llu\n",
79 			    (u_longlong_t)drrw->drr_object,
80 			    (u_longlong_t)drrw->drr_offset);
81 		}
82 		return (D_OK);
83 	}
84 
85 	if (write_is_encrypted(drrw)) {
86 		warnx("the write for ino %llu offset %llu is marked "
87 		    "as encrypted. Attempting decompression anyway...",
88 		    (u_longlong_t)drrw->drr_object,
89 		    (u_longlong_t)drrw->drr_offset);
90 	}
91 
92 	dcbuff = decompress_buffer(item->dp_payload, item->dp_payload_size,
93 	    drrw->drr_logical_size, ctype);
94 
95 	if (dcbuff == NULL) {
96 		/*
97 		 * The block must not be compressed, at least not with this
98 		 * compression type, possibly because it gets written
99 		 * multiple times in this stream.
100 		 */
101 		warnx("decompression failed for ino %llu offset %llu",
102 		    (u_longlong_t)drrw->drr_object,
103 		    (u_longlong_t)drrw->drr_offset);
104 	} else {
105 		set_payload(item, dcbuff, drrw->drr_logical_size);
106 		drrw->drr_compressiontype = 0;
107 		drrw->drr_compressed_size = 0;
108 		if (OPTION_ENABLED(CA_VERBOSE)) {
109 			fprintf(stderr,
110 			    "Successfully decompressed ino %llu offset %llu\n",
111 			    (u_longlong_t)drrw->drr_object,
112 			    (u_longlong_t)drrw->drr_offset);
113 		}
114 	}
115 	return (D_OK);
116 }
117 
118 static chain_step_t
serial_decompress_named_writes(void)119 serial_decompress_named_writes(void)
120 {
121 	chain_step_t step = {
122 		.cs_type = CS_SERIAL,
123 		.cs_in_size = sizeof (drr_packet_t),
124 		.cs_out_size = sizeof (drr_packet_t),
125 		.cs_context = NULL,
126 		.cs_serial = {
127 		    .process = chain_decompress_named_writes
128 		}
129 	};
130 	return (step);
131 }
132 
133 int
zstream_do_decompress(int argc,char * argv[])134 zstream_do_decompress(int argc, char *argv[])
135 {
136 	chain_attrs_t attrs = {0};
137 	int c;
138 
139 	while ((c = getopt(argc, argv, "v")) != -1) {
140 		switch (c) {
141 		case 'v':
142 			ENABLE_OPTION(&attrs, CA_VERBOSE);
143 			break;
144 		case '?':
145 			fprintf(stderr, "invalid option '%c'\n", optopt);
146 			zstream_usage();
147 		}
148 	}
149 
150 	argc -= optind;
151 	argv += optind;
152 
153 	if (argc < 0)
154 		zstream_usage();
155 	if (hcreate(argc) == 0)
156 		errx(1, "hcreate failed");
157 
158 	for (int i = 0; i < argc; i++) {
159 		uint64_t object, offset;
160 		char *obj_str;
161 		char *offset_str;
162 		char *key;
163 		char *end;
164 		enum zio_compress type = ZIO_COMPRESS_INHERIT;
165 
166 		obj_str = strsep(&argv[i], ",");
167 		if (argv[i] == NULL)
168 			zstream_usage();
169 		errno = 0;
170 		object = strtoull(obj_str, &end, 0);
171 		if (errno || *end != '\0')
172 			errx(1, "invalid value for object");
173 		offset_str = strsep(&argv[i], ",");
174 		offset = strtoull(offset_str, &end, 0);
175 		if (errno || *end != '\0')
176 			errx(1, "invalid value for offset");
177 		if (argv[i]) {
178 			if (0 == strcmp("off", argv[i]))
179 				type = ZIO_COMPRESS_OFF;
180 			else if (0 == strcmp("lz4", argv[i]))
181 				type = ZIO_COMPRESS_LZ4;
182 			else if (0 == strcmp("lzjb", argv[i]))
183 				type = ZIO_COMPRESS_LZJB;
184 			else if (0 == strcmp("gzip", argv[i]))
185 				type = ZIO_COMPRESS_GZIP_1;
186 			else if (0 == strcmp("zle", argv[i]))
187 				type = ZIO_COMPRESS_ZLE;
188 			else if (0 == strcmp("zstd", argv[i]))
189 				type = ZIO_COMPRESS_ZSTD;
190 			else {
191 				errx(2, "invalid compression type %s. "
192 				    "Supported types are off, lz4, lzjb, gzip, "
193 				    "zle, and zstd", argv[i]);
194 			}
195 		}
196 
197 		int n_chars = asprintf(&key, "%llu,%llu", (u_longlong_t)object,
198 		    (u_longlong_t)offset);
199 		if (n_chars < 0)
200 			err(1, "asprintf");
201 		ENTRY e = { .key = key };
202 		ENTRY *p;
203 		p = hsearch(e, ENTER);
204 		if (p == NULL)
205 			errx(1, "hsearch failed");
206 		p->data = (void *)(intptr_t)type;
207 	}
208 
209 	ENABLE_OPTION(&attrs, CA_FORBID_DEDUP);
210 
211 	zstream_chain_t decompress_chain = {
212 		STANDARD_INPUT_STACK(NULL),
213 		serial_decompress_named_writes(),
214 		STANDARD_OUTPUT_STACK(NULL)
215 	};
216 	zstream_chain_exec(decompress_chain, &attrs);
217 
218 	hdestroy();
219 	return (0);
220 }
221