Generalize the gzio API.

We currently use a set of subroutines in kern_gzio.c to perform
compression of user and kernel core dumps. In the interest of adding
support for other compression algorithms (zstd) in this role without
complicating the API consumers, add a simple compressor API which can be
used to select an algorithm.

Also change the (non-default) GZIO kernel option to not enable
compressed user cores by default. It's not clear that such a default
would be desirable with support for multiple algorithms implemented,
and it's inconsistent in that it isn't applied to kernel dumps.

Reviewed by:	cem
Differential Revision:	https://reviews.freebsd.org/D13632
This commit is contained in:
Mark Johnston 2018-01-08 21:27:41 +00:00
parent d3692a4dee
commit 78f57a9cde
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327707
10 changed files with 325 additions and 269 deletions

View File

@ -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 @@ generating it).
.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 @@ is included in the kernel configuration file:
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 @@ command can be used:
.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 ,

View File

@ -3767,7 +3767,6 @@ kern/kern_exit.c standard
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_bus_dma.c standard
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

View File

@ -454,8 +454,8 @@ textdump_dumpsys(struct dumperinfo *di)
/*
* 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 @@ textdump_dumpsys(struct dumperinfo *di)
/*
* Restore EKCD status.
*/
di->kdc = kdc;
di->kdcrypto = kdc;
}
/*-

View File

@ -36,13 +36,12 @@ __FBSDID("$FreeBSD$");
#include "opt_capsicum.h"
#include "opt_compat.h"
#include "opt_gzio.h"
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/compressor.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
#include <sys/gzio.h>
#include <sys/imgact.h>
#include <sys/imgact_elf.h>
#include <sys/jail.h>
@ -1185,9 +1184,12 @@ struct coredump_params {
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_rlimit(void *, struct sbuf *, size_t *);
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 @@ compress_chunk(struct coredump_params *p, char *base, char *buf, u_int len)
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 @@ compress_chunk(struct coredump_params *p, char *base, char *buf, u_int len)
}
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 @@ core_output(void *base, size_t len, off_t offset, struct coredump_params *p,
{
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 @@ sbuf_drain_core_output(void *arg, const char *data, int len)
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 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags)
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(&notelst);
@ -1391,7 +1382,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags)
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 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags)
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, &params);
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, &params);
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 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags)
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 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags)
}
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(&notelst)) != NULL) {
TAILQ_REMOVE(&notelst, ninfo, link);
free(ninfo, M_TEMP);

View File

@ -41,7 +41,6 @@ __FBSDID("$FreeBSD$");
#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 @@ __FBSDID("$FreeBSD$");
#include <sys/bio.h>
#include <sys/buf.h>
#include <sys/conf.h>
#include <sys/compressor.h>
#include <sys/cons.h>
#include <sys/eventhandler.h>
#include <sys/filedesc.h>
#include <sys/gzio.h>
#include <sys/jail.h>
#include <sys/kdb.h>
#include <sys/kernel.h>
@ -174,23 +173,21 @@ struct kerneldumpcrypto {
};
#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 @@ kerneldumpcrypto_dumpkeysize(const struct kerneldumpcrypto *kdc)
}
#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 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
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 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
}
if (compression != KERNELDUMP_COMP_NONE) {
#ifdef GZIO
/*
* We currently can't support simultaneous encryption and
* compression.
@ -1074,31 +1068,25 @@ set_dumper(struct dumperinfo *di, const char *devname, struct thread *td,
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 @@ dump_encrypted_write(struct dumperinfo *di, void *virtual,
int error;
size_t nbytes;
kdc = di->kdc;
kdc = di->kdcrypto;
while (length > 0) {
nbytes = MIN(length, sizeof(buf));
@ -1194,7 +1182,7 @@ dump_write_key(struct dumperinfo *di, off_t offset)
{
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 @@ dump_write_key(struct dumperinfo *di, off_t offset)
}
#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 @@ kerneldumpgz_write_cb(void *base, size_t length, off_t offset, void *arg)
}
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 @@ dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh)
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 @@ dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh)
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 @@ dump_start(struct dumperinfo *di, struct kerneldumpheader *kdh)
2 * di->blocksize - keysize;
kdh->dumpextent = htod64(dumpextent);
} else
#endif
return (E2BIG);
}
@ -1333,7 +1317,7 @@ _dump_append(struct dumperinfo *di, void *virtual, vm_offset_t physical,
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 @@ int
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 @@ dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh)
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 @@ dump_finish(struct dumperinfo *di, struct kerneldumpheader *kdh)
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 @@ dump_init_header(const struct dumperinfo *di, struct kerneldumpheader *kdh,
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 @@ dump_init_header(const struct dumperinfo *di, struct kerneldumpheader *kdh,
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);
}

View File

@ -40,7 +40,6 @@
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
#include "opt_gzio.h"
#include "opt_ktrace.h"
#include <sys/param.h>
@ -51,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/acct.h>
#include <sys/bus.h>
#include <sys/capsicum.h>
#include <sys/compressor.h>
#include <sys/condvar.h>
#include <sys/event.h>
#include <sys/fcntl.h>
@ -3255,17 +3255,30 @@ SYSCTL_PROC(_debug, OID_AUTO, ncores, CTLTYPE_INT|CTLFLAG_RW,
#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 @@ corefile_open(const char *comm, uid_t uid, pid_t pid, struct thread *td,
}
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 @@ coredump(struct thread *td)
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;
}

View File

@ -1,5 +1,7 @@
/*-
* Copyright (c) 2014 Mark Johnston <markj@FreeBSD.org>
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2014, 2017 Mark Johnston <markj@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -24,55 +26,87 @@
* SUCH DAMAGE.
*/
/*
* Subroutines used for writing compressed user process and kernel core dumps.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "opt_gzio.h"
#include <sys/param.h>
#include <sys/gzio.h>
#include <sys/compressor.h>
#include <sys/kernel.h>
#include <sys/linker_set.h>
#include <sys/malloc.h>
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 <sys/zutil.h>
#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 */
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 */
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);
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);
struct gzio_stream *
gzio_init(gzio_cb cb, enum gzio_mode mode, size_t bufsz, int level, void *arg)
static void *
gz_alloc(void *arg __unused, u_int n, u_int sz)
{
struct gzio_stream *s;
/*
* 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;
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_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;
@ -85,98 +119,57 @@ gzio_init(gzio_cb cb, enum gzio_mode mode, size_t bufsz, int level, void *arg)
if (error != 0)
goto fail;
gzio_reset(s);
gz_reset(s);
return (s);
fail:
gz_free(NULL, s->gz_buffer);
gz_free(NULL, s);
return (NULL);
}
void
gzio_reset(struct gzio_stream *s)
static void
gz_reset(void *stream)
{
struct gz_stream *s;
uint8_t *hdr;
const size_t hdrlen = 10;
(void)deflateReset(&s->gz_stream);
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, KERN_GZ_HDRLEN);
hdr[0] = KERN_GZ_MAGIC1;
hdr[1] = KERN_GZ_MAGIC2;
memset(hdr, 0, hdrlen);
hdr[0] = 0x1f;
hdr[1] = 0x8b;
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);
s->gz_stream.next_out += hdrlen;
s->gz_stream.avail_out -= hdrlen;
}
static int
gz_write(struct gzio_stream *s, void *buf, u_int len, int zflag)
gz_write(void *stream, void *data, size_t len, compressor_cb_t cb,
void *arg)
{
uint8_t trailer[KERN_GZ_TRAILERLEN];
struct gz_stream *s;
uint8_t trailer[8];
size_t room;
int error, zerror;
int error, zerror, zflag;
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));
s = stream;
zflag = data == NULL ? Z_FINISH : Z_NO_FLUSH;
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);
s->gz_stream.next_in = data;
s->gz_crc = crc32_raw(data, len, s->gz_crc);
} else
s->gz_crc ^= ~0U;
@ -202,14 +195,13 @@ gz_write(struct gzio_stream *s, void *buf, u_int len, int zflag)
((uint32_t *)trailer)[0] = s->gz_crc;
((uint32_t *)trailer)[1] =
s->gz_stream.total_in;
room = MIN(KERN_GZ_TRAILERLEN,
room = MIN(sizeof(trailer),
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);
error = cb(s->gz_buffer, len, s->gz_off, arg);
if (error != 0)
break;
@ -221,13 +213,103 @@ gz_write(struct gzio_stream *s, void *buf, u_int len, int zflag)
* 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);
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);
}

View File

@ -1,7 +1,7 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2014 Mark Johnston <markj@FreeBSD.org>
* Copyright (c) 2014, 2017 Mark Johnston <markj@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@ -28,25 +28,26 @@
* $FreeBSD$
*/
#ifndef _SYS__GZIO_H_
#define _SYS__GZIO_H_
#ifndef _SYS__COMPRESSOR_H_
#define _SYS__COMPRESSOR_H_
#ifdef _KERNEL
enum gzio_mode {
GZIO_DEFLATE,
};
/* Supported formats. */
#define COMPRESS_GZIP 1
typedef int (*gzio_cb)(void *, size_t, off_t, void *);
typedef int (*compressor_cb_t)(void *, size_t, off_t, void *);
struct gzio_stream;
struct compressor;
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 *);
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__GZIO_H_ */
#endif /* _SYS__COMPRESSOR_H_ */

View File

@ -339,8 +339,8 @@ struct dumperinfo {
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 */

View File

@ -96,8 +96,6 @@ struct sysentvec;
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 *);