xref: /freebsd/sys/kern/subr_compressor.c (revision b37f6c9805edb4b89f0a8c2b78f78a3dcfc0647b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014, 2017 Mark Johnston <markj@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in
13  *    the documentation and/or other materials provided with the
14  *    distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * Subroutines used for writing compressed user process and kernel core dumps.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include "opt_gzio.h"
37 
38 #include <sys/param.h>
39 
40 #include <sys/compressor.h>
41 #include <sys/kernel.h>
42 #include <sys/linker_set.h>
43 #include <sys/malloc.h>
44 
45 MALLOC_DEFINE(M_COMPRESS, "compressor", "kernel compression subroutines");
46 
47 struct compressor_methods {
48 	int format;
49 	void *(* const init)(size_t, int);
50 	void (* const reset)(void *);
51 	int (* const write)(void *, void *, size_t, compressor_cb_t, void *);
52 	void (* const fini)(void *);
53 };
54 
55 struct compressor {
56 	const struct compressor_methods *methods;
57 	compressor_cb_t cb;
58 	void *priv;
59 	void *arg;
60 };
61 
62 SET_DECLARE(compressors, struct compressor_methods);
63 
64 #ifdef GZIO
65 
66 #include <sys/zutil.h>
67 
68 struct gz_stream {
69 	uint8_t		*gz_buffer;	/* output buffer */
70 	size_t		gz_bufsz;	/* output buffer size */
71 	off_t		gz_off;		/* offset into the output stream */
72 	uint32_t	gz_crc;		/* stream CRC32 */
73 	z_stream	gz_stream;	/* zlib state */
74 };
75 
76 static void 	*gz_init(size_t maxiosize, int level);
77 static void	gz_reset(void *stream);
78 static int	gz_write(void *stream, void *data, size_t len, compressor_cb_t,
79 		    void *);
80 static void	gz_fini(void *stream);
81 
82 static void *
83 gz_alloc(void *arg __unused, u_int n, u_int sz)
84 {
85 
86 	/*
87 	 * Memory for zlib state is allocated using M_NODUMP since it may be
88 	 * used to compress a kernel dump, and we don't want zlib to attempt to
89 	 * compress its own state.
90 	 */
91 	return (malloc(n * sz, M_COMPRESS, M_WAITOK | M_ZERO | M_NODUMP));
92 }
93 
94 static void
95 gz_free(void *arg __unused, void *ptr)
96 {
97 
98 	free(ptr, M_COMPRESS);
99 }
100 
101 static void *
102 gz_init(size_t maxiosize, int level)
103 {
104 	struct gz_stream *s;
105 	int error;
106 
107 	s = gz_alloc(NULL, 1, roundup2(sizeof(*s), PAGE_SIZE));
108 	s->gz_buffer = gz_alloc(NULL, 1, maxiosize);
109 	s->gz_bufsz = maxiosize;
110 
111 	s->gz_stream.zalloc = gz_alloc;
112 	s->gz_stream.zfree = gz_free;
113 	s->gz_stream.opaque = NULL;
114 	s->gz_stream.next_in = Z_NULL;
115 	s->gz_stream.avail_in = 0;
116 
117 	error = deflateInit2(&s->gz_stream, level, Z_DEFLATED, -MAX_WBITS,
118 	    DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
119 	if (error != 0)
120 		goto fail;
121 
122 	gz_reset(s);
123 
124 	return (s);
125 
126 fail:
127 	gz_free(NULL, s);
128 	return (NULL);
129 }
130 
131 static void
132 gz_reset(void *stream)
133 {
134 	struct gz_stream *s;
135 	uint8_t *hdr;
136 	const size_t hdrlen = 10;
137 
138 	s = stream;
139 	s->gz_off = 0;
140 	s->gz_crc = ~0U;
141 
142 	(void)deflateReset(&s->gz_stream);
143 	s->gz_stream.avail_out = s->gz_bufsz;
144 	s->gz_stream.next_out = s->gz_buffer;
145 
146 	/* Write the gzip header to the output buffer. */
147 	hdr = s->gz_buffer;
148 	memset(hdr, 0, hdrlen);
149 	hdr[0] = 0x1f;
150 	hdr[1] = 0x8b;
151 	hdr[2] = Z_DEFLATED;
152 	hdr[9] = OS_CODE;
153 	s->gz_stream.next_out += hdrlen;
154 	s->gz_stream.avail_out -= hdrlen;
155 }
156 
157 static int
158 gz_write(void *stream, void *data, size_t len, compressor_cb_t cb,
159     void *arg)
160 {
161 	struct gz_stream *s;
162 	uint8_t trailer[8];
163 	size_t room;
164 	int error, zerror, zflag;
165 
166 	s = stream;
167 	zflag = data == NULL ? Z_FINISH : Z_NO_FLUSH;
168 
169 	if (len > 0) {
170 		s->gz_stream.avail_in = len;
171 		s->gz_stream.next_in = data;
172 		s->gz_crc = crc32_raw(data, len, s->gz_crc);
173 	} else
174 		s->gz_crc ^= ~0U;
175 
176 	error = 0;
177 	do {
178 		zerror = deflate(&s->gz_stream, zflag);
179 		if (zerror != Z_OK && zerror != Z_STREAM_END) {
180 			error = EIO;
181 			break;
182 		}
183 
184 		if (s->gz_stream.avail_out == 0 || zerror == Z_STREAM_END) {
185 			/*
186 			 * Our output buffer is full or there's nothing left
187 			 * to produce, so we're flushing the buffer.
188 			 */
189 			len = s->gz_bufsz - s->gz_stream.avail_out;
190 			if (zerror == Z_STREAM_END) {
191 				/*
192 				 * Try to pack as much of the trailer into the
193 				 * output buffer as we can.
194 				 */
195 				((uint32_t *)trailer)[0] = s->gz_crc;
196 				((uint32_t *)trailer)[1] =
197 				    s->gz_stream.total_in;
198 				room = MIN(sizeof(trailer),
199 				    s->gz_bufsz - len);
200 				memcpy(s->gz_buffer + len, trailer, room);
201 				len += room;
202 			}
203 
204 			error = cb(s->gz_buffer, len, s->gz_off, arg);
205 			if (error != 0)
206 				break;
207 
208 			s->gz_off += len;
209 			s->gz_stream.next_out = s->gz_buffer;
210 			s->gz_stream.avail_out = s->gz_bufsz;
211 
212 			/*
213 			 * If we couldn't pack the trailer into the output
214 			 * buffer, write it out now.
215 			 */
216 			if (zerror == Z_STREAM_END && room < sizeof(trailer))
217 				error = cb(trailer + room,
218 				    sizeof(trailer) - room, s->gz_off, arg);
219 		}
220 	} while (zerror != Z_STREAM_END &&
221 	    (zflag == Z_FINISH || s->gz_stream.avail_in > 0));
222 
223 	return (error);
224 }
225 
226 static void
227 gz_fini(void *stream)
228 {
229 	struct gz_stream *s;
230 
231 	s = stream;
232 	(void)deflateEnd(&s->gz_stream);
233 	gz_free(NULL, s->gz_buffer);
234 	gz_free(NULL, s);
235 }
236 
237 struct compressor_methods gzip_methods = {
238 	.format = COMPRESS_GZIP,
239 	.init = gz_init,
240 	.reset = gz_reset,
241 	.write = gz_write,
242 	.fini = gz_fini,
243 };
244 DATA_SET(compressors, gzip_methods);
245 
246 #endif /* GZIO */
247 
248 bool
249 compressor_avail(int format)
250 {
251 	struct compressor_methods **iter;
252 
253 	SET_FOREACH(iter, compressors) {
254 		if ((*iter)->format == format)
255 			return (true);
256 	}
257 	return (false);
258 }
259 
260 struct compressor *
261 compressor_init(compressor_cb_t cb, int format, size_t maxiosize, int level,
262     void *arg)
263 {
264 	struct compressor_methods **iter;
265 	struct compressor *s;
266 	void *priv;
267 
268 	SET_FOREACH(iter, compressors) {
269 		if ((*iter)->format == format)
270 			break;
271 	}
272 	if (iter == NULL)
273 		return (NULL);
274 
275 	priv = (*iter)->init(maxiosize, level);
276 	if (priv == NULL)
277 		return (NULL);
278 
279 	s = malloc(sizeof(*s), M_COMPRESS, M_WAITOK | M_ZERO);
280 	s->methods = (*iter);
281 	s->priv = priv;
282 	s->cb = cb;
283 	s->arg = arg;
284 	return (s);
285 }
286 
287 void
288 compressor_reset(struct compressor *stream)
289 {
290 
291 	stream->methods->reset(stream->priv);
292 }
293 
294 int
295 compressor_write(struct compressor *stream, void *data, size_t len)
296 {
297 
298 	return (stream->methods->write(stream->priv, data, len, stream->cb,
299 	    stream->arg));
300 }
301 
302 int
303 compressor_flush(struct compressor *stream)
304 {
305 
306 	return (stream->methods->write(stream->priv, NULL, 0, stream->cb,
307 	    stream->arg));
308 }
309 
310 void
311 compressor_fini(struct compressor *stream)
312 {
313 
314 	stream->methods->fini(stream->priv);
315 }
316