]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/subr_compressor.c
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
[FreeBSD/FreeBSD.git] / sys / kern / subr_compressor.c
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 }