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