2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2014, 2017 Mark Johnston <markj@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
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
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
30 * Subroutines used for writing compressed user process and kernel core dumps.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
38 #include <sys/param.h>
40 #include <sys/compressor.h>
41 #include <sys/kernel.h>
42 #include <sys/linker_set.h>
43 #include <sys/malloc.h>
45 MALLOC_DEFINE(M_COMPRESS, "compressor", "kernel compression subroutines");
47 struct compressor_methods {
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 *);
56 const struct compressor_methods *methods;
62 SET_DECLARE(compressors, struct compressor_methods);
66 #include <sys/zutil.h>
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 */
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,
80 static void gz_fini(void *stream);
83 gz_alloc(void *arg __unused, u_int n, u_int sz)
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.
91 return (malloc(n * sz, M_COMPRESS, M_WAITOK | M_ZERO | M_NODUMP));
95 gz_free(void *arg __unused, void *ptr)
98 free(ptr, M_COMPRESS);
102 gz_init(size_t maxiosize, int level)
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;
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;
117 error = deflateInit2(&s->gz_stream, level, Z_DEFLATED, -MAX_WBITS,
118 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
132 gz_reset(void *stream)
136 const size_t hdrlen = 10;
142 (void)deflateReset(&s->gz_stream);
143 s->gz_stream.avail_out = s->gz_bufsz;
144 s->gz_stream.next_out = s->gz_buffer;
146 /* Write the gzip header to the output buffer. */
148 memset(hdr, 0, hdrlen);
153 s->gz_stream.next_out += hdrlen;
154 s->gz_stream.avail_out -= hdrlen;
158 gz_write(void *stream, void *data, size_t len, compressor_cb_t cb,
164 int error, zerror, zflag;
167 zflag = data == NULL ? Z_FINISH : Z_NO_FLUSH;
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);
178 zerror = deflate(&s->gz_stream, zflag);
179 if (zerror != Z_OK && zerror != Z_STREAM_END) {
184 if (s->gz_stream.avail_out == 0 || zerror == Z_STREAM_END) {
186 * Our output buffer is full or there's nothing left
187 * to produce, so we're flushing the buffer.
189 len = s->gz_bufsz - s->gz_stream.avail_out;
190 if (zerror == Z_STREAM_END) {
192 * Try to pack as much of the trailer into the
193 * output buffer as we can.
195 ((uint32_t *)trailer)[0] = s->gz_crc;
196 ((uint32_t *)trailer)[1] =
197 s->gz_stream.total_in;
198 room = MIN(sizeof(trailer),
200 memcpy(s->gz_buffer + len, trailer, room);
204 error = cb(s->gz_buffer, len, s->gz_off, arg);
209 s->gz_stream.next_out = s->gz_buffer;
210 s->gz_stream.avail_out = s->gz_bufsz;
213 * If we couldn't pack the trailer into the output
214 * buffer, write it out now.
216 if (zerror == Z_STREAM_END && room < sizeof(trailer))
217 error = cb(trailer + room,
218 sizeof(trailer) - room, s->gz_off, arg);
220 } while (zerror != Z_STREAM_END &&
221 (zflag == Z_FINISH || s->gz_stream.avail_in > 0));
227 gz_fini(void *stream)
232 (void)deflateEnd(&s->gz_stream);
233 gz_free(NULL, s->gz_buffer);
237 struct compressor_methods gzip_methods = {
238 .format = COMPRESS_GZIP,
244 DATA_SET(compressors, gzip_methods);
249 compressor_avail(int format)
251 struct compressor_methods **iter;
253 SET_FOREACH(iter, compressors) {
254 if ((*iter)->format == format)
261 compressor_init(compressor_cb_t cb, int format, size_t maxiosize, int level,
264 struct compressor_methods **iter;
265 struct compressor *s;
268 SET_FOREACH(iter, compressors) {
269 if ((*iter)->format == format)
275 priv = (*iter)->init(maxiosize, level);
279 s = malloc(sizeof(*s), M_COMPRESS, M_WAITOK | M_ZERO);
280 s->methods = (*iter);
288 compressor_reset(struct compressor *stream)
291 stream->methods->reset(stream->priv);
295 compressor_write(struct compressor *stream, void *data, size_t len)
298 return (stream->methods->write(stream->priv, data, len, stream->cb,
303 compressor_flush(struct compressor *stream)
306 return (stream->methods->write(stream->priv, NULL, 0, stream->cb,
311 compressor_fini(struct compressor *stream)
314 stream->methods->fini(stream->priv);