Index: head/share/man/man5/core.5 =================================================================== --- head/share/man/man5/core.5 +++ head/share/man/man5/core.5 @@ -28,7 +28,7 @@ .\" @(#)core.5 8.3 (Berkeley) 12/11/93 .\" $FreeBSD$ .\" -.Dd October 5, 2015 +.Dd January 8, 2018 .Dt CORE 5 .Os .Sh NAME @@ -67,8 +67,8 @@ .Pp The following format specifiers may be used in the .Va kern.corefile -sysctl to insert additional information into the resulting core file -name: +sysctl to insert additional information into the resulting core +filename: .Bl -tag -width "1234567890" -compact -offset "12345" .It Em \&%H Machine hostname. @@ -108,17 +108,17 @@ GZIO .El .Pp -When the GZIO option is included, the following sysctls control whether core -files will be compressed: -.Bl -tag -width "kern.compress_user_cores_gzlevel" -compact -offset "12345" -.It Em kern.compress_user_cores_gzlevel -Gzip compression level. -Defaults to 6. +The following sysctl control core file compression: +.Bl -tag -width "kern.compress_user_cores_level" -compact -offset "12345" .It Em kern.compress_user_cores -Actually compress user cores. -Compressed core files will have a suffix of +Enable compression of user cores. +A value of 1 configures gzip compression. +gzip-compressed core files will have a suffix of .Ql .gz -appended to them. +appended to their filenames. +.It Em kern.compress_user_cores_level +Compression level. +Defaults to 6. .El .Sh NOTES Corefiles are written with open file descriptor information as an ELF note. @@ -153,6 +153,7 @@ .Dl sysctl kern.corefile=/var/coredumps/\&%U/\&%N.core .Sh SEE ALSO .Xr gdb 1 , +.Xr gzip 1 , .Xr kgdb 1 , .Xr setrlimit 2 , .Xr sigaction 2 , Index: head/sys/conf/files =================================================================== --- head/sys/conf/files +++ head/sys/conf/files @@ -3767,7 +3767,6 @@ kern/kern_fail.c standard kern/kern_ffclock.c standard kern/kern_fork.c standard -kern/kern_gzio.c optional gzio kern/kern_hhook.c standard kern/kern_idle.c standard kern/kern_intr.c standard @@ -3843,6 +3842,7 @@ kern/subr_bufring.c standard kern/subr_capability.c standard kern/subr_clock.c standard +kern/subr_compressor.c standard kern/subr_counter.c standard kern/subr_devstat.c standard kern/subr_disk.c standard Index: head/sys/ddb/db_textdump.c =================================================================== --- head/sys/ddb/db_textdump.c +++ head/sys/ddb/db_textdump.c @@ -454,8 +454,8 @@ /* * Disable EKCD because we don't provide encrypted textdumps. */ - kdc = di->kdc; - di->kdc = NULL; + kdc = di->kdcrypto; + di->kdcrypto = NULL; /* * Position the start of the dump so that we'll write the kernel dump @@ -512,7 +512,7 @@ /* * Restore EKCD status. */ - di->kdc = kdc; + di->kdcrypto = kdc; } /*- Index: head/sys/kern/imgact_elf.c =================================================================== --- head/sys/kern/imgact_elf.c +++ head/sys/kern/imgact_elf.c @@ -36,13 +36,12 @@ #include "opt_capsicum.h" #include "opt_compat.h" -#include "opt_gzio.h" #include #include +#include #include #include -#include #include #include #include @@ -1185,9 +1184,12 @@ struct ucred *file_cred; struct thread *td; struct vnode *vp; - struct gzio_stream *gzs; + struct compressor *comp; }; +extern int compress_user_cores; +extern int compress_user_cores_level; + static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static int core_write(struct coredump_params *, const void *, size_t, off_t, @@ -1219,9 +1221,6 @@ static void note_procstat_umask(void *, struct sbuf *, size_t *); static void note_procstat_vmmap(void *, struct sbuf *, size_t *); -#ifdef GZIO -extern int compress_user_cores_gzlevel; - /* * Write out a core segment to the compression stream. */ @@ -1241,7 +1240,7 @@ error = copyin(base, buf, chunk_len); if (error != 0) bzero(buf, chunk_len); - error = gzio_write(p->gzs, buf, chunk_len); + error = compressor_write(p->comp, buf, chunk_len); if (error != 0) break; base += chunk_len; @@ -1251,13 +1250,12 @@ } static int -core_gz_write(void *base, size_t len, off_t offset, void *arg) +core_compressed_write(void *base, size_t len, off_t offset, void *arg) { return (core_write((struct coredump_params *)arg, base, len, offset, UIO_SYSSPACE)); } -#endif /* GZIO */ static int core_write(struct coredump_params *p, const void *base, size_t len, @@ -1275,10 +1273,9 @@ { int error; -#ifdef GZIO - if (p->gzs != NULL) + if (p->comp != NULL) return (compress_chunk(p, base, tmpbuf, len)); -#endif + /* * EFAULT is a non-fatal error that we can get, for example, * if the segment is backed by a file but extends beyond its @@ -1323,11 +1320,9 @@ locked = PROC_LOCKED(p->td->td_proc); if (locked) PROC_UNLOCK(p->td->td_proc); -#ifdef GZIO - if (p->gzs != NULL) - error = gzio_write(p->gzs, __DECONST(char *, data), len); + if (p->comp != NULL) + error = compressor_write(p->comp, __DECONST(char *, data), len); else -#endif error = core_write(p, __DECONST(void *, data), len, p->offset, UIO_SYSSPACE); if (locked) @@ -1362,11 +1357,7 @@ struct note_info *ninfo; void *hdr, *tmpbuf; size_t hdrsize, notesz, coresize; -#ifdef GZIO - boolean_t compress; - compress = (flags & IMGACT_CORE_COMPRESS) != 0; -#endif hdr = NULL; tmpbuf = NULL; TAILQ_INIT(¬elst); @@ -1391,7 +1382,7 @@ params.file_cred = NOCRED; params.td = td; params.vp = vp; - params.gzs = NULL; + params.comp = NULL; #ifdef RACCT if (racct_enable) { @@ -1409,18 +1400,17 @@ goto done; } -#ifdef GZIO /* Create a compression stream if necessary. */ - if (compress) { - params.gzs = gzio_init(core_gz_write, GZIO_DEFLATE, - CORE_BUF_SIZE, compress_user_cores_gzlevel, ¶ms); - if (params.gzs == NULL) { + if (compress_user_cores != 0) { + params.comp = compressor_init(core_compressed_write, + compress_user_cores, CORE_BUF_SIZE, + compress_user_cores_level, ¶ms); + if (params.comp == NULL) { error = EFAULT; goto done; } tmpbuf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO); } -#endif /* * Allocate memory for building the header, fill it up, @@ -1446,10 +1436,8 @@ offset += php->p_filesz; php++; } -#ifdef GZIO - if (error == 0 && compress) - error = gzio_flush(params.gzs); -#endif + if (error == 0 && params.comp != NULL) + error = compressor_flush(params.comp); } if (error) { log(LOG_WARNING, @@ -1458,13 +1446,9 @@ } done: -#ifdef GZIO - if (compress) { - free(tmpbuf, M_TEMP); - if (params.gzs != NULL) - gzio_fini(params.gzs); - } -#endif + free(tmpbuf, M_TEMP); + if (params.comp != NULL) + compressor_fini(params.comp); while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { TAILQ_REMOVE(¬elst, ninfo, link); free(ninfo, M_TEMP); Index: head/sys/kern/kern_gzio.c =================================================================== --- head/sys/kern/kern_gzio.c +++ head/sys/kern/kern_gzio.c @@ -1,233 +0,0 @@ -/*- - * Copyright (c) 2014 Mark Johnston - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include - -#include -#include -#include -#include - -#define KERN_GZ_HDRLEN 10 /* gzip header length */ -#define KERN_GZ_TRAILERLEN 8 /* gzip trailer length */ -#define KERN_GZ_MAGIC1 0x1f /* first magic byte */ -#define KERN_GZ_MAGIC2 0x8b /* second magic byte */ - -MALLOC_DEFINE(M_GZIO, "gzio", "zlib state"); - -struct gzio_stream { - uint8_t * gz_buffer; /* output buffer */ - size_t gz_bufsz; /* total buffer size */ - off_t gz_off; /* offset into the output stream */ - enum gzio_mode gz_mode; /* stream mode */ - uint32_t gz_crc; /* stream CRC32 */ - gzio_cb gz_cb; /* output callback */ - void * gz_arg; /* private callback arg */ - z_stream gz_stream; /* zlib state */ -}; - -static void * gz_alloc(void *, u_int, u_int); -static void gz_free(void *, void *); -static int gz_write(struct gzio_stream *, void *, u_int, int); - -struct gzio_stream * -gzio_init(gzio_cb cb, enum gzio_mode mode, size_t bufsz, int level, void *arg) -{ - struct gzio_stream *s; - int error; - - if (bufsz < KERN_GZ_HDRLEN) - return (NULL); - if (mode != GZIO_DEFLATE) - return (NULL); - - s = gz_alloc(NULL, 1, sizeof(*s)); - s->gz_bufsz = bufsz; - s->gz_buffer = gz_alloc(NULL, 1, s->gz_bufsz); - s->gz_mode = mode; - s->gz_cb = cb; - s->gz_arg = arg; - - s->gz_stream.zalloc = gz_alloc; - s->gz_stream.zfree = gz_free; - s->gz_stream.opaque = NULL; - s->gz_stream.next_in = Z_NULL; - s->gz_stream.avail_in = 0; - - error = deflateInit2(&s->gz_stream, level, Z_DEFLATED, -MAX_WBITS, - DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (error != 0) - goto fail; - - gzio_reset(s); - - return (s); - -fail: - gz_free(NULL, s->gz_buffer); - gz_free(NULL, s); - return (NULL); -} - -void -gzio_reset(struct gzio_stream *s) -{ - uint8_t *hdr; - - (void)deflateReset(&s->gz_stream); - - s->gz_off = 0; - s->gz_crc = ~0U; - - s->gz_stream.avail_out = s->gz_bufsz; - s->gz_stream.next_out = s->gz_buffer; - - /* Write the gzip header to the output buffer. */ - hdr = s->gz_buffer; - memset(hdr, 0, KERN_GZ_HDRLEN); - hdr[0] = KERN_GZ_MAGIC1; - hdr[1] = KERN_GZ_MAGIC2; - hdr[2] = Z_DEFLATED; - hdr[9] = OS_CODE; - s->gz_stream.next_out += KERN_GZ_HDRLEN; - s->gz_stream.avail_out -= KERN_GZ_HDRLEN; -} - -int -gzio_write(struct gzio_stream *s, void *data, u_int len) -{ - - return (gz_write(s, data, len, Z_NO_FLUSH)); -} - -int -gzio_flush(struct gzio_stream *s) -{ - - return (gz_write(s, NULL, 0, Z_FINISH)); -} - -void -gzio_fini(struct gzio_stream *s) -{ - - (void)deflateEnd(&s->gz_stream); - gz_free(NULL, s->gz_buffer); - gz_free(NULL, s); -} - -static void * -gz_alloc(void *arg __unused, u_int n, u_int sz) -{ - - /* - * Memory for zlib state is allocated using M_NODUMP since it may be - * used to compress a kernel dump, and we don't want zlib to attempt to - * compress its own state. - */ - return (malloc(n * sz, M_GZIO, M_WAITOK | M_ZERO | M_NODUMP)); -} - -static void -gz_free(void *arg __unused, void *ptr) -{ - - free(ptr, M_GZIO); -} - -static int -gz_write(struct gzio_stream *s, void *buf, u_int len, int zflag) -{ - uint8_t trailer[KERN_GZ_TRAILERLEN]; - size_t room; - int error, zerror; - - KASSERT(zflag == Z_FINISH || zflag == Z_NO_FLUSH, - ("unexpected flag %d", zflag)); - KASSERT(s->gz_mode == GZIO_DEFLATE, - ("invalid stream mode %d", s->gz_mode)); - - if (len > 0) { - s->gz_stream.avail_in = len; - s->gz_stream.next_in = buf; - s->gz_crc = crc32_raw(buf, len, s->gz_crc); - } else - s->gz_crc ^= ~0U; - - error = 0; - do { - zerror = deflate(&s->gz_stream, zflag); - if (zerror != Z_OK && zerror != Z_STREAM_END) { - error = EIO; - break; - } - - if (s->gz_stream.avail_out == 0 || zerror == Z_STREAM_END) { - /* - * Our output buffer is full or there's nothing left - * to produce, so we're flushing the buffer. - */ - len = s->gz_bufsz - s->gz_stream.avail_out; - if (zerror == Z_STREAM_END) { - /* - * Try to pack as much of the trailer into the - * output buffer as we can. - */ - ((uint32_t *)trailer)[0] = s->gz_crc; - ((uint32_t *)trailer)[1] = - s->gz_stream.total_in; - room = MIN(KERN_GZ_TRAILERLEN, - s->gz_bufsz - len); - memcpy(s->gz_buffer + len, trailer, room); - len += room; - } - - error = s->gz_cb(s->gz_buffer, len, s->gz_off, - s->gz_arg); - if (error != 0) - break; - - s->gz_off += len; - s->gz_stream.next_out = s->gz_buffer; - s->gz_stream.avail_out = s->gz_bufsz; - - /* - * If we couldn't pack the trailer into the output - * buffer, write it out now. - */ - if (zerror == Z_STREAM_END && room < KERN_GZ_TRAILERLEN) - error = s->gz_cb(trailer + room, - KERN_GZ_TRAILERLEN - room, s->gz_off, - s->gz_arg); - } - } while (zerror != Z_STREAM_END && - (zflag == Z_FINISH || s->gz_stream.avail_in > 0)); - - return (error); -} Index: head/sys/kern/kern_shutdown.c =================================================================== --- head/sys/kern/kern_shutdown.c +++ head/sys/kern/kern_shutdown.c @@ -41,7 +41,6 @@ #include "opt_ddb.h" #include "opt_ekcd.h" -#include "opt_gzio.h" #include "opt_kdb.h" #include "opt_panic.h" #include "opt_sched.h" @@ -52,10 +51,10 @@ #include #include #include +#include #include #include #include -#include #include #include #include @@ -174,23 +173,21 @@ }; #endif -#ifdef GZIO -struct kerneldumpgz { - struct gzio_stream *kdgz_stream; - uint8_t *kdgz_buf; - size_t kdgz_resid; +struct kerneldumpcomp { + struct compressor *kdc_stream; + uint8_t *kdc_buf; + size_t kdc_resid; }; -static struct kerneldumpgz *kerneldumpgz_create(struct dumperinfo *di, +static struct kerneldumpcomp *kerneldumpcomp_create(struct dumperinfo *di, uint8_t compression); -static void kerneldumpgz_destroy(struct dumperinfo *di); -static int kerneldumpgz_write_cb(void *cb, size_t len, off_t off, void *arg); +static void kerneldumpcomp_destroy(struct dumperinfo *di); +static int kerneldumpcomp_write_cb(void *base, size_t len, off_t off, void *arg); static int kerneldump_gzlevel = 6; SYSCTL_INT(_kern, OID_AUTO, kerneldump_gzlevel, CTLFLAG_RWTUN, &kerneldump_gzlevel, 0, - "Kernel crash dump gzip compression level"); -#endif /* GZIO */ + "Kernel crash dump compression level"); /* * Variable panicstr contains argument to first call to panic; used as flag @@ -986,39 +983,37 @@ } #endif /* EKCD */ -#ifdef GZIO -static struct kerneldumpgz * -kerneldumpgz_create(struct dumperinfo *di, uint8_t compression) +static struct kerneldumpcomp * +kerneldumpcomp_create(struct dumperinfo *di, uint8_t compression) { - struct kerneldumpgz *kdgz; + struct kerneldumpcomp *kdcomp; if (compression != KERNELDUMP_COMP_GZIP) return (NULL); - kdgz = malloc(sizeof(*kdgz), M_DUMPER, M_WAITOK | M_ZERO); - kdgz->kdgz_stream = gzio_init(kerneldumpgz_write_cb, GZIO_DEFLATE, - di->maxiosize, kerneldump_gzlevel, di); - if (kdgz->kdgz_stream == NULL) { - free(kdgz, M_DUMPER); + kdcomp = malloc(sizeof(*kdcomp), M_DUMPER, M_WAITOK | M_ZERO); + kdcomp->kdc_stream = compressor_init(kerneldumpcomp_write_cb, + COMPRESS_GZIP, di->maxiosize, kerneldump_gzlevel, di); + if (kdcomp->kdc_stream == NULL) { + free(kdcomp, M_DUMPER); return (NULL); } - kdgz->kdgz_buf = malloc(di->maxiosize, M_DUMPER, M_WAITOK | M_NODUMP); - return (kdgz); + kdcomp->kdc_buf = malloc(di->maxiosize, M_DUMPER, M_WAITOK | M_NODUMP); + return (kdcomp); } static void -kerneldumpgz_destroy(struct dumperinfo *di) +kerneldumpcomp_destroy(struct dumperinfo *di) { - struct kerneldumpgz *kdgz; + struct kerneldumpcomp *kdcomp; - kdgz = di->kdgz; - if (kdgz == NULL) + kdcomp = di->kdcomp; + if (kdcomp == NULL) return; - gzio_fini(kdgz->kdgz_stream); - explicit_bzero(kdgz->kdgz_buf, di->maxiosize); - free(kdgz->kdgz_buf, M_DUMPER); - free(kdgz, M_DUMPER); + compressor_fini(kdcomp->kdc_stream); + explicit_bzero(kdcomp->kdc_buf, di->maxiosize); + free(kdcomp->kdc_buf, M_DUMPER); + free(kdcomp, M_DUMPER); } -#endif /* GZIO */ /* Registration of dumpers */ int @@ -1041,14 +1036,14 @@ return (EBUSY); dumper = *di; dumper.blockbuf = NULL; - dumper.kdc = NULL; - dumper.kdgz = NULL; + dumper.kdcrypto = NULL; + dumper.kdcomp = NULL; if (encryption != KERNELDUMP_ENC_NONE) { #ifdef EKCD - dumper.kdc = kerneldumpcrypto_create(di->blocksize, encryption, - key, encryptedkeysize, encryptedkey); - if (dumper.kdc == NULL) { + dumper.kdcrypto = kerneldumpcrypto_create(di->blocksize, + encryption, key, encryptedkeysize, encryptedkey); + if (dumper.kdcrypto == NULL) { error = EINVAL; goto cleanup; } @@ -1065,7 +1060,6 @@ } if (compression != KERNELDUMP_COMP_NONE) { -#ifdef GZIO /* * We currently can't support simultaneous encryption and * compression. @@ -1074,31 +1068,25 @@ error = EOPNOTSUPP; goto cleanup; } - dumper.kdgz = kerneldumpgz_create(&dumper, compression); - if (dumper.kdgz == NULL) { + dumper.kdcomp = kerneldumpcomp_create(&dumper, compression); + if (dumper.kdcomp == NULL) { error = EINVAL; goto cleanup; } -#else - error = EOPNOTSUPP; - goto cleanup; -#endif } dumper.blockbuf = malloc(di->blocksize, M_DUMPER, M_WAITOK | M_ZERO); return (0); cleanup: #ifdef EKCD - if (dumper.kdc != NULL) { - explicit_bzero(dumper.kdc, sizeof(*dumper.kdc) + - dumper.kdc->kdc_dumpkeysize); - free(dumper.kdc, M_EKCD); + if (dumper.kdcrypto != NULL) { + explicit_bzero(dumper.kdcrypto, sizeof(*dumper.kdcrypto) + + dumper.kdcrypto->kdc_dumpkeysize); + free(dumper.kdcrypto, M_EKCD); } #endif -#ifdef GZIO - kerneldumpgz_destroy(&dumper); -#endif + kerneldumpcomp_destroy(&dumper); if (dumper.blockbuf != NULL) { explicit_bzero(dumper.blockbuf, dumper.blocksize); @@ -1168,7 +1156,7 @@ int error; size_t nbytes; - kdc = di->kdc; + kdc = di->kdcrypto; while (length > 0) { nbytes = MIN(length, sizeof(buf)); @@ -1194,7 +1182,7 @@ { struct kerneldumpcrypto *kdc; - kdc = di->kdc; + kdc = di->kdcrypto; if (kdc == NULL) return (0); return (dump_write(di, kdc->kdc_dumpkey, 0, offset, @@ -1202,9 +1190,8 @@ } #endif /* EKCD */ -#ifdef GZIO static int -kerneldumpgz_write_cb(void *base, size_t length, off_t offset, void *arg) +kerneldumpcomp_write_cb(void *base, size_t length, off_t offset, void *arg) { struct dumperinfo *di; size_t resid, rlength; @@ -1227,12 +1214,11 @@ } resid = length - rlength; memmove(di->blockbuf, (uint8_t *)base + rlength, resid); - di->kdgz->kdgz_resid = resid; + di->kdcomp->kdc_resid = resid; return (EAGAIN); } return (_dump_append(di, base, 0, length)); } -#endif /* GZIO */ /* * Write a kerneldumpheader at the specified offset. The header structure is 512 @@ -1290,10 +1276,10 @@ uint32_t keysize; #ifdef EKCD - int error = kerneldumpcrypto_init(di->kdc); + int error = kerneldumpcrypto_init(di->kdcrypto); if (error != 0) return (error); - keysize = kerneldumpcrypto_dumpkeysize(di->kdc); + keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto); #else keysize = 0; #endif @@ -1301,8 +1287,7 @@ dumpextent = dtoh64(kdh->dumpextent); if (di->mediasize < SIZEOF_METADATA + dumpextent + 2 * di->blocksize + keysize) { -#ifdef GZIO - if (di->kdgz != NULL) { + if (di->kdcomp != NULL) { /* * We don't yet know how much space the compressed dump * will occupy, so try to use the whole swap partition @@ -1315,7 +1300,6 @@ 2 * di->blocksize - keysize; kdh->dumpextent = htod64(dumpextent); } else -#endif return (E2BIG); } @@ -1333,7 +1317,7 @@ int error; #ifdef EKCD - if (di->kdc != NULL) + if (di->kdcrypto != NULL) error = dump_encrypted_write(di, virtual, physical, di->dumpoff, length); else @@ -1353,18 +1337,16 @@ dump_append(struct dumperinfo *di, void *virtual, vm_offset_t physical, size_t length) { -#ifdef GZIO void *buf; - if (di->kdgz != NULL) { - /* Bounce through a buffer to avoid gzip CRC errors. */ + if (di->kdcomp != NULL) { + /* Bounce through a buffer to avoid CRC errors. */ if (length > di->maxiosize) return (EINVAL); - buf = di->kdgz->kdgz_buf; + buf = di->kdcomp->kdc_buf; memmove(buf, virtual, length); - return (gzio_write(di->kdgz->kdgz_stream, buf, length)); + return (compressor_write(di->kdcomp->kdc_stream, buf, length)); } -#endif return (_dump_append(di, virtual, physical, length)); } @@ -1399,20 +1381,19 @@ extent = dtoh64(kdh->dumpextent); #ifdef EKCD - keysize = kerneldumpcrypto_dumpkeysize(di->kdc); + keysize = kerneldumpcrypto_dumpkeysize(di->kdcrypto); #else keysize = 0; #endif -#ifdef GZIO - if (di->kdgz != NULL) { - error = gzio_flush(di->kdgz->kdgz_stream); + if (di->kdcomp != NULL) { + error = compressor_flush(di->kdcomp->kdc_stream); if (error == EAGAIN) { /* We have residual data in di->blockbuf. */ error = dump_write(di, di->blockbuf, 0, di->dumpoff, di->blocksize); - di->dumpoff += di->kdgz->kdgz_resid; - di->kdgz->kdgz_resid = 0; + di->dumpoff += di->kdcomp->kdc_resid; + di->kdcomp->kdc_resid = 0; } if (error != 0) return (error); @@ -1426,9 +1407,8 @@ kdh->parity = 0; kdh->parity = kerneldump_parity(kdh); - gzio_reset(di->kdgz->kdgz_stream); + compressor_reset(di->kdcomp->kdc_stream); } -#endif /* * Write kerneldump headers at the beginning and end of the dump extent. @@ -1471,7 +1451,7 @@ kdh->dumpextent = kdh->dumplength; kdh->dumptime = htod64(time_second); #ifdef EKCD - kdh->dumpkeysize = htod32(kerneldumpcrypto_dumpkeysize(di->kdc)); + kdh->dumpkeysize = htod32(kerneldumpcrypto_dumpkeysize(di->kdcrypto)); #else kdh->dumpkeysize = 0; #endif @@ -1482,10 +1462,8 @@ kdh->versionstring[dstsize - 2] = '\n'; if (panicstr != NULL) strlcpy(kdh->panicstring, panicstr, sizeof(kdh->panicstring)); -#ifdef GZIO - if (di->kdgz != NULL) + if (di->kdcomp != NULL) kdh->compression = KERNELDUMP_COMP_GZIP; -#endif kdh->parity = kerneldump_parity(kdh); } Index: head/sys/kern/kern_sig.c =================================================================== --- head/sys/kern/kern_sig.c +++ head/sys/kern/kern_sig.c @@ -40,7 +40,6 @@ __FBSDID("$FreeBSD$"); #include "opt_compat.h" -#include "opt_gzio.h" #include "opt_ktrace.h" #include @@ -51,6 +50,7 @@ #include #include #include +#include #include #include #include @@ -3255,18 +3255,31 @@ #define GZ_SUFFIX ".gz" -#ifdef GZIO -static int compress_user_cores = 1; -SYSCTL_INT(_kern, OID_AUTO, compress_user_cores, CTLFLAG_RWTUN, - &compress_user_cores, 0, "Compression of user corefiles"); +int compress_user_cores = 0; -int compress_user_cores_gzlevel = 6; -SYSCTL_INT(_kern, OID_AUTO, compress_user_cores_gzlevel, CTLFLAG_RWTUN, - &compress_user_cores_gzlevel, 0, "Corefile gzip compression level"); -#else -static int compress_user_cores = 0; -#endif +static int +sysctl_compress_user_cores(SYSCTL_HANDLER_ARGS) +{ + int error, val; + val = compress_user_cores; + error = sysctl_handle_int(oidp, &val, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (val != 0 && !compressor_avail(val)) + return (EINVAL); + compress_user_cores = val; + return (error); +} +SYSCTL_PROC(_kern, OID_AUTO, compress_user_cores, CTLTYPE_INT | CTLFLAG_RWTUN, + 0, sizeof(int), sysctl_compress_user_cores, "I", + "Enable compression of user corefiles (" __XSTRING(COMPRESS_GZIP) " = gzip)"); + +int compress_user_cores_level = 6; +SYSCTL_INT(_kern, OID_AUTO, compress_user_cores_level, CTLFLAG_RWTUN, + &compress_user_cores_level, 0, + "Corefile compression level"); + /* * Protect the access to corefilename[] by allproc_lock. */ @@ -3363,7 +3376,7 @@ } sx_sunlock(&corefilename_lock); free(hostname, M_TEMP); - if (compress) + if (compress == COMPRESS_GZIP) sbuf_printf(&sb, GZ_SUFFIX); if (sbuf_error(&sb) != 0) { log(LOG_ERR, "pid %ld (%s), uid (%lu): corename is too " @@ -3529,8 +3542,7 @@ PROC_UNLOCK(p); if (p->p_sysent->sv_coredump != NULL) { - error = p->p_sysent->sv_coredump(td, vp, limit, - compress_user_cores ? IMGACT_CORE_COMPRESS : 0); + error = p->p_sysent->sv_coredump(td, vp, limit, 0); } else { error = ENOSYS; } Index: head/sys/kern/subr_compressor.c =================================================================== --- head/sys/kern/subr_compressor.c +++ head/sys/kern/subr_compressor.c @@ -0,0 +1,315 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2014, 2017 Mark Johnston + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Subroutines used for writing compressed user process and kernel core dumps. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_gzio.h" + +#include + +#include +#include +#include +#include + +MALLOC_DEFINE(M_COMPRESS, "compressor", "kernel compression subroutines"); + +struct compressor_methods { + int format; + void *(* const init)(size_t, int); + void (* const reset)(void *); + int (* const write)(void *, void *, size_t, compressor_cb_t, void *); + void (* const fini)(void *); +}; + +struct compressor { + const struct compressor_methods *methods; + compressor_cb_t cb; + void *priv; + void *arg; +}; + +SET_DECLARE(compressors, struct compressor_methods); + +#ifdef GZIO + +#include + +struct gz_stream { + uint8_t *gz_buffer; /* output buffer */ + size_t gz_bufsz; /* output buffer size */ + off_t gz_off; /* offset into the output stream */ + uint32_t gz_crc; /* stream CRC32 */ + z_stream gz_stream; /* zlib state */ +}; + +static void *gz_init(size_t maxiosize, int level); +static void gz_reset(void *stream); +static int gz_write(void *stream, void *data, size_t len, compressor_cb_t, + void *); +static void gz_fini(void *stream); + +static void * +gz_alloc(void *arg __unused, u_int n, u_int sz) +{ + + /* + * Memory for zlib state is allocated using M_NODUMP since it may be + * used to compress a kernel dump, and we don't want zlib to attempt to + * compress its own state. + */ + return (malloc(n * sz, M_COMPRESS, M_WAITOK | M_ZERO | M_NODUMP)); +} + +static void +gz_free(void *arg __unused, void *ptr) +{ + + free(ptr, M_COMPRESS); +} + +static void * +gz_init(size_t maxiosize, int level) +{ + struct gz_stream *s; + int error; + + s = gz_alloc(NULL, 1, roundup2(sizeof(*s), PAGE_SIZE)); + s->gz_buffer = gz_alloc(NULL, 1, maxiosize); + s->gz_bufsz = maxiosize; + + s->gz_stream.zalloc = gz_alloc; + s->gz_stream.zfree = gz_free; + s->gz_stream.opaque = NULL; + s->gz_stream.next_in = Z_NULL; + s->gz_stream.avail_in = 0; + + error = deflateInit2(&s->gz_stream, level, Z_DEFLATED, -MAX_WBITS, + DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); + if (error != 0) + goto fail; + + gz_reset(s); + + return (s); + +fail: + gz_free(NULL, s); + return (NULL); +} + +static void +gz_reset(void *stream) +{ + struct gz_stream *s; + uint8_t *hdr; + const size_t hdrlen = 10; + + s = stream; + s->gz_off = 0; + s->gz_crc = ~0U; + + (void)deflateReset(&s->gz_stream); + s->gz_stream.avail_out = s->gz_bufsz; + s->gz_stream.next_out = s->gz_buffer; + + /* Write the gzip header to the output buffer. */ + hdr = s->gz_buffer; + memset(hdr, 0, hdrlen); + hdr[0] = 0x1f; + hdr[1] = 0x8b; + hdr[2] = Z_DEFLATED; + hdr[9] = OS_CODE; + s->gz_stream.next_out += hdrlen; + s->gz_stream.avail_out -= hdrlen; +} + +static int +gz_write(void *stream, void *data, size_t len, compressor_cb_t cb, + void *arg) +{ + struct gz_stream *s; + uint8_t trailer[8]; + size_t room; + int error, zerror, zflag; + + s = stream; + zflag = data == NULL ? Z_FINISH : Z_NO_FLUSH; + + if (len > 0) { + s->gz_stream.avail_in = len; + s->gz_stream.next_in = data; + s->gz_crc = crc32_raw(data, len, s->gz_crc); + } else + s->gz_crc ^= ~0U; + + error = 0; + do { + zerror = deflate(&s->gz_stream, zflag); + if (zerror != Z_OK && zerror != Z_STREAM_END) { + error = EIO; + break; + } + + if (s->gz_stream.avail_out == 0 || zerror == Z_STREAM_END) { + /* + * Our output buffer is full or there's nothing left + * to produce, so we're flushing the buffer. + */ + len = s->gz_bufsz - s->gz_stream.avail_out; + if (zerror == Z_STREAM_END) { + /* + * Try to pack as much of the trailer into the + * output buffer as we can. + */ + ((uint32_t *)trailer)[0] = s->gz_crc; + ((uint32_t *)trailer)[1] = + s->gz_stream.total_in; + room = MIN(sizeof(trailer), + s->gz_bufsz - len); + memcpy(s->gz_buffer + len, trailer, room); + len += room; + } + + error = cb(s->gz_buffer, len, s->gz_off, arg); + if (error != 0) + break; + + s->gz_off += len; + s->gz_stream.next_out = s->gz_buffer; + s->gz_stream.avail_out = s->gz_bufsz; + + /* + * If we couldn't pack the trailer into the output + * buffer, write it out now. + */ + if (zerror == Z_STREAM_END && room < sizeof(trailer)) + error = cb(trailer + room, + sizeof(trailer) - room, s->gz_off, arg); + } + } while (zerror != Z_STREAM_END && + (zflag == Z_FINISH || s->gz_stream.avail_in > 0)); + + return (error); +} + +static void +gz_fini(void *stream) +{ + struct gz_stream *s; + + s = stream; + (void)deflateEnd(&s->gz_stream); + gz_free(NULL, s->gz_buffer); + gz_free(NULL, s); +} + +struct compressor_methods gzip_methods = { + .format = COMPRESS_GZIP, + .init = gz_init, + .reset = gz_reset, + .write = gz_write, + .fini = gz_fini, +}; +DATA_SET(compressors, gzip_methods); + +#endif /* GZIO */ + +bool +compressor_avail(int format) +{ + struct compressor_methods **iter; + + SET_FOREACH(iter, compressors) { + if ((*iter)->format == format) + return (true); + } + return (false); +} + +struct compressor * +compressor_init(compressor_cb_t cb, int format, size_t maxiosize, int level, + void *arg) +{ + struct compressor_methods **iter; + struct compressor *s; + void *priv; + + SET_FOREACH(iter, compressors) { + if ((*iter)->format == format) + break; + } + if (iter == NULL) + return (NULL); + + priv = (*iter)->init(maxiosize, level); + if (priv == NULL) + return (NULL); + + s = malloc(sizeof(*s), M_COMPRESS, M_WAITOK | M_ZERO); + s->methods = (*iter); + s->priv = priv; + s->cb = cb; + s->arg = arg; + return (s); +} + +void +compressor_reset(struct compressor *stream) +{ + + stream->methods->reset(stream->priv); +} + +int +compressor_write(struct compressor *stream, void *data, size_t len) +{ + + return (stream->methods->write(stream->priv, data, len, stream->cb, + stream->arg)); +} + +int +compressor_flush(struct compressor *stream) +{ + + return (stream->methods->write(stream->priv, NULL, 0, stream->cb, + stream->arg)); +} + +void +compressor_fini(struct compressor *stream) +{ + + stream->methods->fini(stream->priv); +} Index: head/sys/sys/compressor.h =================================================================== --- head/sys/sys/compressor.h +++ head/sys/sys/compressor.h @@ -0,0 +1,53 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2014, 2017 Mark Johnston + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _SYS__COMPRESSOR_H_ +#define _SYS__COMPRESSOR_H_ + +#ifdef _KERNEL + +/* Supported formats. */ +#define COMPRESS_GZIP 1 + +typedef int (*compressor_cb_t)(void *, size_t, off_t, void *); + +struct compressor; + +bool compressor_avail(int format); +struct compressor *compressor_init(compressor_cb_t cb, int format, + size_t maxiosize, int level, void *arg); +void compressor_reset(struct compressor *stream); +int compressor_write(struct compressor *stream, void *data, + size_t len); +int compressor_flush(struct compressor *stream); +void compressor_fini(struct compressor *stream); + +#endif /* _KERNEL */ +#endif /* _SYS__COMPRESSOR_H_ */ Index: head/sys/sys/conf.h =================================================================== --- head/sys/sys/conf.h +++ head/sys/sys/conf.h @@ -339,8 +339,8 @@ off_t mediasize; /* Space available in bytes. */ void *blockbuf; /* Buffer for padding shorter dump blocks */ off_t dumpoff; /* Offset of ongoing kernel dump. */ - struct kerneldumpcrypto *kdc; /* Kernel dump crypto. */ - struct kerneldumpgz *kdgz; /* Kernel dump compression. */ + struct kerneldumpcrypto *kdcrypto; /* Kernel dump crypto. */ + struct kerneldumpcomp *kdcomp; /* Kernel dump compression. */ }; extern int dumping; /* system is dumping */ Index: head/sys/sys/gzio.h =================================================================== --- head/sys/sys/gzio.h +++ head/sys/sys/gzio.h @@ -1,52 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2014 Mark Johnston - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD$ - */ - -#ifndef _SYS__GZIO_H_ -#define _SYS__GZIO_H_ - -#ifdef _KERNEL - -enum gzio_mode { - GZIO_DEFLATE, -}; - -typedef int (*gzio_cb)(void *, size_t, off_t, void *); - -struct gzio_stream; - -struct gzio_stream *gzio_init(gzio_cb cb, enum gzio_mode, size_t, int, void *); -void gzio_reset(struct gzio_stream *); -int gzio_write(struct gzio_stream *, void *, u_int); -int gzio_flush(struct gzio_stream *); -void gzio_fini(struct gzio_stream *); - -#endif /* _KERNEL */ - -#endif /* _SYS__GZIO_H_ */ Index: head/sys/sys/imgact.h =================================================================== --- head/sys/sys/imgact.h +++ head/sys/sys/imgact.h @@ -96,8 +96,6 @@ struct thread; struct vmspace; -#define IMGACT_CORE_COMPRESS 0x01 - int exec_alloc_args(struct image_args *); int exec_check_permissions(struct image_params *); register_t *exec_copyout_strings(struct image_params *);