Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F139557619
D1832.id3850.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
39 KB
Referenced Files
None
Subscribers
None
D1832.id3850.diff
View Options
Index: share/man/man5/core.5
===================================================================
--- share/man/man5/core.5
+++ share/man/man5/core.5
@@ -28,7 +28,7 @@
.\" @(#)core.5 8.3 (Berkeley) 12/11/93
.\" $FreeBSD$
.\"
-.Dd November 22, 2012
+.Dd November 10, 2014
.Dt CORE 5
.Os
.Sh NAME
@@ -101,25 +101,23 @@
.Va kern.sugid_coredump
to 1.
.Pp
-Corefiles can be compressed by the kernel if the following items
-are included in the kernel configuration file:
+Corefiles can be compressed by the kernel if the following item
+is included in the kernel configuration file:
.Bl -tag -width "1234567890" -compact -offset "12345"
.It options
-COMPRESS_USER_CORES
-.It devices
-gzio
+GZIO
.El
.Pp
-When COMPRESS_USER_CORES is included the following sysctls can control
-if core files will be compressed:
+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 -1.
+Defaults to 6.
.It Em kern.compress_user_cores
Actually compress user cores.
-Core files will have the suffix
-.Em .gz
+Compressed core files will have a suffix of
+.Ql .gz
appended to them.
.El
.Sh EXAMPLES
Index: share/man/man9/Makefile
===================================================================
--- share/man/man9/Makefile
+++ share/man/man9/Makefile
@@ -130,6 +130,7 @@
g_provider_by_name.9 \
groupmember.9 \
g_wither_geom.9 \
+ gzio.9 \
hash.9 \
hashinit.9 \
hexdump.9 \
@@ -736,6 +737,10 @@
MLINKS+=g_provider.9 g_destroy_provider.9 \
g_provider.9 g_error_provider.9 \
g_provider.9 g_new_providerf.9
+MLINKS+=gzio.9 gzio_init.9 \
+ gzio.9 gzio_write.9 \
+ gzio.9 gzio_flush.9 \
+ gzio.9 gzio_fini.9
MLINKS+=hash.9 hash32.9 \
hash.9 hash32_buf.9 \
hash.9 hash32_str.9 \
Index: share/man/man9/gzio.9
===================================================================
--- /dev/null
+++ share/man/man9/gzio.9
@@ -0,0 +1,118 @@
+.\" Copyright (c) 2015 Mark Johnston <markj@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" 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$
+.\"
+.Dd February 14, 2015
+.Dt GZIO 9
+.Os
+.Sh NAME
+.Nm gzio
+.Nd a callback interface for the zlib compression library
+.Sh SYNOPSIS
+.Cd "options GZIO"
+.Pp
+.In sys/gzio.h
+.Bd -literal
+typedef int (*gzio_cb)(void *base, size_t len, off_t off, void *arg);
+.Ed
+.Pp
+.Ft struct gzio_stream *
+.Fn gzio_init "gzio_cb cb" "enum gzio_mode mode" "size_t bufsz" "int level" "void *arg"
+.Ft int
+.Fn gzio_write "struct gzio_stream *s" "void *data" "u_int len"
+.Ft int
+.Fn gzio_flush "struct gzio_stream *s"
+.Ft void
+.Fn gzio_fini "struct gzio_stream *s"
+.Sh DESCRIPTION
+The
+.Nm
+interface is a simple wrapper for the zlib library.
+It currently only supports compression of a data stream.
+A
+.Ft struct gzio_stream
+stores state for the stream and is allocated and freed using
+.Fn gzio_init
+and
+.Fn gzio_fini
+respectively.
+As data is written to the stream using
+.Fn gzio_write ,
+the caller-provided callback is invoked, making the processed data available.
+The callback is only invoked when the stream's internal buffer is full; to
+force the processing of partially-buffered data, use
+.Fn gzio_flush .
+.Fn gzio_fini
+is used to free a flushed stream.
+.Pp
+The
+.Fn gzio_init
+function allocates a new stream with callback
+.Va cb ,
+and an internal buffer of size
+.Va bufsz .
+The
+.Va mode
+parameter must be
+.Dv GZIO_DEFLATE .
+The
+.Va level
+parameter is a zlib parameter which determines the compression level used;
+valid values are 0-9 inclusive, and -1, which tells zlib to use its default
+compression level.
+Larger values give better compression ratios at the cost of CPU time and
+memory usage.
+Note that all memory used by
+.Nm
+is allocated in
+.Fn gzio_init .
+In particular, memory will not be allocated while writing to a
+.Nm
+stream.
+Finally,
+.Va arg
+is an opaque argument that is passed to
+.Va cb .
+.Fn gzio_init
+will return
+.Dv NULL
+if an error occurs during stream initialization.
+.Pp
+The
+.Fn gzio_write
+and
+.Fn gzio_flush
+functions write data to be compressed to a
+.Dv GZIO_DEFLATE
+stream.
+If the callback returns a non-zero value, these functions will return it to
+the caller.
+If an error occurs during compression,
+.Dv EIO will be returned.
+.Fn gzio_flush
+should be called exactly once for a given stream.
+.Sh SEE ALSO
+.Xr zlib 3 ,
+.Xr core 5
Index: sys/conf/NOTES
===================================================================
--- sys/conf/NOTES
+++ sys/conf/NOTES
@@ -2889,11 +2889,6 @@
# a single process at one time.
options SHMSEG=9
-# Compress user core dumps.
-options COMPRESS_USER_CORES
-# required to compress file output from kernel for COMPRESS_USER_CORES.
-device gzio
-
# Set the amount of time (in seconds) the system will wait before
# rebooting automatically when a kernel panic occurs. If set to (-1),
# the system will wait indefinitely until a key is pressed on the
@@ -2983,3 +2978,7 @@
# Module to enable execution of application via emulators like QEMU
options IMAGACT_BINMISC
+
+# zlib I/O stream support
+# This enables support for compressed core dumps.
+options GZIO
Index: sys/conf/options
===================================================================
--- sys/conf/options
+++ sys/conf/options
@@ -87,13 +87,13 @@
COMPAT_FREEBSD10 opt_compat.h
COMPAT_LINUXAPI opt_compat.h
COMPILING_LINT opt_global.h
-COMPRESS_USER_CORES opt_core.h
CY_PCI_FASTINTR
DEADLKRES opt_watchdog.h
DIRECTIO
FILEMON opt_dontuse.h
FFCLOCK
FULL_PREEMPTION opt_sched.h
+GZIO opt_gzio.h
IMAGACT_BINMISC opt_dontuse.h
IPI_PREEMPTION opt_sched.h
GEOM_AES opt_geom.h
Index: sys/kern/imgact_elf.c
===================================================================
--- sys/kern/imgact_elf.c
+++ sys/kern/imgact_elf.c
@@ -33,12 +33,13 @@
#include "opt_capsicum.h"
#include "opt_compat.h"
-#include "opt_core.h"
+#include "opt_gzio.h"
#include <sys/param.h>
#include <sys/capsicum.h>
#include <sys/exec.h>
#include <sys/fcntl.h>
+#include <sys/gzio.h>
#include <sys/imgact.h>
#include <sys/imgact_elf.h>
#include <sys/kernel.h>
@@ -68,8 +69,6 @@
#include <sys/eventhandler.h>
#include <sys/user.h>
-#include <net/zlib.h>
-
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/vm_param.h>
@@ -104,11 +103,7 @@
SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0,
"");
-#ifdef COMPRESS_USER_CORES
-static int compress_core(gzFile, char *, char *, unsigned int,
- struct thread * td);
-#endif
-#define CORE_BUF_SIZE (16 * 1024)
+#define CORE_BUF_SIZE (16 * 1024)
int __elfN(fallback_brand) = -1;
SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO,
@@ -1065,11 +1060,21 @@
TAILQ_HEAD(note_info_list, note_info);
+/* Coredump output parameters. */
+struct coredump_params {
+ off_t offset;
+ struct ucred *active_cred;
+ struct ucred *file_cred;
+ struct thread *td;
+ struct vnode *vp;
+ struct gzio_stream *gzs;
+};
+
static void cb_put_phdr(vm_map_entry_t, void *);
static void cb_size_segment(vm_map_entry_t, void *);
static void each_writable_segment(struct thread *, segment_callback, void *);
-static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *,
- int, void *, size_t, struct note_info_list *, size_t, gzFile);
+static int __elfN(corehdr)(struct coredump_params *, int, void *, size_t,
+ struct note_info_list *, size_t);
static void __elfN(prepare_notes)(struct thread *, struct note_info_list *,
size_t *);
static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t);
@@ -1093,42 +1098,58 @@
static void note_procstat_umask(void *, struct sbuf *, size_t *);
static void note_procstat_vmmap(void *, struct sbuf *, size_t *);
-#ifdef COMPRESS_USER_CORES
+#ifdef GZIO
extern int compress_user_cores;
extern int compress_user_cores_gzlevel;
-#endif
+/*
+ * Compress and write out a core segment for a user process.
+ */
static int
-core_output(struct vnode *vp, void *base, size_t len, off_t offset,
- struct ucred *active_cred, struct ucred *file_cred,
- struct thread *td, char *core_buf, gzFile gzfile) {
-
+compress_chunk(char *base, char *buf, u_int len, struct coredump_params *p)
+{
+ u_int chunk_len;
int error;
- if (gzfile) {
-#ifdef COMPRESS_USER_CORES
- error = compress_core(gzfile, base, core_buf, len, td);
-#else
- panic("shouldn't be here");
-#endif
- } else {
- error = vn_rdwr_inchunks(UIO_WRITE, vp, base, len, offset,
- UIO_USERSPACE, IO_UNIT | IO_DIRECT | IO_RANGELOCKED,
- active_cred, file_cred, NULL, td);
+
+ while (len > 0) {
+ chunk_len = MIN(len, CORE_BUF_SIZE);
+ copyin(base, buf, chunk_len);
+ error = gzio_write(p->gzs, buf, chunk_len);
+ if (error != 0)
+ break;
+ base += chunk_len;
+ len -= chunk_len;
}
return (error);
}
+#endif /* GZIO */
-/* Coredump output parameters for sbuf drain routine. */
-struct sbuf_drain_core_params {
- off_t offset;
- struct ucred *active_cred;
- struct ucred *file_cred;
- struct thread *td;
- struct vnode *vp;
-#ifdef COMPRESS_USER_CORES
- gzFile gzfile;
+static int
+core_write(void *base, size_t len, off_t offset, void *arg)
+{
+ struct coredump_params *p;
+
+ p = (struct coredump_params *)arg;
+
+ return (vn_rdwr_inchunks(UIO_WRITE, p->vp, base, len, offset,
+ UIO_SYSSPACE, IO_UNIT | IO_DIRECT | IO_RANGELOCKED,
+ p->active_cred, p->file_cred, NULL, p->td));
+}
+
+static int
+core_output(void *base, size_t len, off_t offset, struct coredump_params *p,
+ void *tmpbuf)
+{
+ int error;
+
+#ifdef GZIO
+ if (p->gzs != NULL)
+ error = compress_chunk(base, tmpbuf, len, p);
+ else
#endif
-};
+ error = core_write(base, len, offset, p);
+ return (error);
+}
/*
* Drain into a core file.
@@ -1136,10 +1157,10 @@
static int
sbuf_drain_core_output(void *arg, const char *data, int len)
{
- struct sbuf_drain_core_params *p;
+ struct coredump_params *p;
int error, locked;
- p = (struct sbuf_drain_core_params *)arg;
+ p = (struct coredump_params *)arg;
/*
* Some kern_proc out routines that print to this sbuf may
@@ -1152,16 +1173,12 @@
locked = PROC_LOCKED(p->td->td_proc);
if (locked)
PROC_UNLOCK(p->td->td_proc);
-#ifdef COMPRESS_USER_CORES
- if (p->gzfile != Z_NULL)
- error = compress_core(p->gzfile, NULL, __DECONST(char *, data),
- len, p->td);
+#ifdef GZIO
+ if (p->gzs != NULL)
+ error = gzio_write(p->gzs, __DECONST(char *, data), len);
else
#endif
- error = vn_rdwr_inchunks(UIO_WRITE, p->vp,
- __DECONST(void *, data), len, p->offset, UIO_SYSSPACE,
- IO_UNIT | IO_DIRECT | IO_RANGELOCKED, p->active_cred,
- p->file_cred, NULL, p->td);
+ error = core_write(__DECONST(void *, data), len, p->offset, p);
if (locked)
PROC_LOCK(p->td->td_proc);
if (error != 0)
@@ -1190,42 +1207,16 @@
int error = 0;
struct sseg_closure seginfo;
struct note_info_list notelst;
+ struct coredump_params params;
struct note_info *ninfo;
- void *hdr;
+ void *hdr, *tmpbuf;
size_t hdrsize, notesz, coresize;
+ boolean_t compress;
- gzFile gzfile = Z_NULL;
- char *core_buf = NULL;
-#ifdef COMPRESS_USER_CORES
- char gzopen_flags[8];
- char *p;
- int doing_compress = flags & IMGACT_CORE_COMPRESS;
-#endif
-
+ compress = (flags & IMGACT_CORE_COMPRESS) != 0;
hdr = NULL;
TAILQ_INIT(¬elst);
-#ifdef COMPRESS_USER_CORES
- if (doing_compress) {
- p = gzopen_flags;
- *p++ = 'w';
- if (compress_user_cores_gzlevel >= 0 &&
- compress_user_cores_gzlevel <= 9)
- *p++ = '0' + compress_user_cores_gzlevel;
- *p = 0;
- gzfile = gz_open("", gzopen_flags, vp);
- if (gzfile == Z_NULL) {
- error = EFAULT;
- goto done;
- }
- core_buf = malloc(CORE_BUF_SIZE, M_TEMP, M_WAITOK | M_ZERO);
- if (!core_buf) {
- error = ENOMEM;
- goto done;
- }
- }
-#endif
-
/* Size the program segments. */
seginfo.count = 0;
seginfo.size = 0;
@@ -1252,6 +1243,28 @@
goto done;
}
+ /* Set up core dump parameters. */
+ params.offset = 0;
+ params.active_cred = cred;
+ params.file_cred = NOCRED;
+ params.td = td;
+ params.vp = vp;
+ params.gzs = NULL;
+
+ tmpbuf = NULL;
+#ifdef GZIO
+ /* Create a compression stream if necessary. */
+ if (compress) {
+ params.gzs = gzio_init(core_write, GZIO_DEFLATE, CORE_BUF_SIZE,
+ compress_user_cores_gzlevel, ¶ms);
+ if (params.gzs == 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,
* and write it out following the notes.
@@ -1261,8 +1274,8 @@
error = EINVAL;
goto done;
}
- error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize,
- ¬elst, notesz, gzfile);
+ error = __elfN(corehdr)(¶ms, seginfo.count, hdr, hdrsize, ¬elst,
+ notesz);
/* Write the contents of all of the writable segments. */
if (error == 0) {
@@ -1273,13 +1286,17 @@
php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1;
offset = round_page(hdrsize + notesz);
for (i = 0; i < seginfo.count; i++) {
- error = core_output(vp, (caddr_t)(uintptr_t)php->p_vaddr,
- php->p_filesz, offset, cred, NOCRED, curthread, core_buf, gzfile);
+ error = core_output((caddr_t)(uintptr_t)php->p_vaddr,
+ php->p_filesz, offset, ¶ms, tmpbuf);
if (error != 0)
break;
offset += php->p_filesz;
php++;
}
+#ifdef GZIO
+ if (error == 0 && compress)
+ error = gzio_flush(params.gzs);
+#endif
}
if (error) {
log(LOG_WARNING,
@@ -1288,11 +1305,11 @@
}
done:
-#ifdef COMPRESS_USER_CORES
- if (core_buf)
- free(core_buf, M_TEMP);
- if (gzfile)
- gzclose(gzfile);
+#ifdef GZIO
+ if (compress) {
+ free(tmpbuf, M_TEMP);
+ gzio_fini(params.gzs);
+ }
#endif
while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) {
TAILQ_REMOVE(¬elst, ninfo, link);
@@ -1416,29 +1433,19 @@
* the page boundary.
*/
static int
-__elfN(corehdr)(struct thread *td, struct vnode *vp, struct ucred *cred,
- int numsegs, void *hdr, size_t hdrsize, struct note_info_list *notelst,
- size_t notesz, gzFile gzfile)
+__elfN(corehdr)(struct coredump_params *p, int numsegs, void *hdr,
+ size_t hdrsize, struct note_info_list *notelst, size_t notesz)
{
- struct sbuf_drain_core_params params;
struct note_info *ninfo;
struct sbuf *sb;
int error;
/* Fill in the header. */
bzero(hdr, hdrsize);
- __elfN(puthdr)(td, hdr, hdrsize, numsegs, notesz);
+ __elfN(puthdr)(p->td, hdr, hdrsize, numsegs, notesz);
- params.offset = 0;
- params.active_cred = cred;
- params.file_cred = NOCRED;
- params.td = td;
- params.vp = vp;
-#ifdef COMPRESS_USER_CORES
- params.gzfile = gzfile;
-#endif
sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN);
- sbuf_set_drain(sb, sbuf_drain_core_output, ¶ms);
+ sbuf_set_drain(sb, sbuf_drain_core_output, p);
sbuf_start_section(sb, NULL);
sbuf_bcat(sb, hdr, hdrsize);
TAILQ_FOREACH(ninfo, notelst, link)
@@ -2105,58 +2112,6 @@
};
EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw));
-#ifdef COMPRESS_USER_CORES
-/*
- * Compress and write out a core segment for a user process.
- *
- * 'inbuf' is the starting address of a VM segment in the process' address
- * space that is to be compressed and written out to the core file. 'dest_buf'
- * is a buffer in the kernel's address space. The segment is copied from
- * 'inbuf' to 'dest_buf' first before being processed by the compression
- * routine gzwrite(). This copying is necessary because the content of the VM
- * segment may change between the compression pass and the crc-computation pass
- * in gzwrite(). This is because realtime threads may preempt the UNIX kernel.
- *
- * If inbuf is NULL it is assumed that data is already copied to 'dest_buf'.
- */
-static int
-compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len,
- struct thread *td)
-{
- int len_compressed;
- int error = 0;
- unsigned int chunk_len;
-
- while (len) {
- if (inbuf != NULL) {
- chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len;
- copyin(inbuf, dest_buf, chunk_len);
- inbuf += chunk_len;
- } else {
- chunk_len = len;
- }
- len_compressed = gzwrite(file, dest_buf, chunk_len);
-
- EVENTHANDLER_INVOKE(app_coredump_progress, td, len_compressed);
-
- if ((unsigned int)len_compressed != chunk_len) {
- log(LOG_WARNING,
- "compress_core: length mismatch (0x%x returned, "
- "0x%x expected)\n", len_compressed, chunk_len);
- EVENTHANDLER_INVOKE(app_coredump_error, td,
- "compress_core: length mismatch %x -> %x",
- chunk_len, len_compressed);
- error = EFAULT;
- break;
- }
- len -= chunk_len;
- maybe_yield();
- }
-
- return (error);
-}
-#endif /* COMPRESS_USER_CORES */
-
static vm_prot_t
__elfN(trans_prot)(Elf_Word flags)
{
Index: sys/kern/kern_gzio.c
===================================================================
--- sys/kern/kern_gzio.c
+++ sys/kern/kern_gzio.c
@@ -1,400 +1,223 @@
-/*
- * $Id: kern_gzio.c,v 1.6 2008-10-18 22:54:45 lbazinet Exp $
+/*-
+ * Copyright (c) 2014 Mark Johnston <markj@FreeBSD.org>
*
- * core_gzip.c -- gzip routines used in compressing user process cores
+ * 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 file is derived from src/lib/libz/gzio.c in FreeBSD.
+ * 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.
*/
-/* gzio.c -- IO on .gz files
- * Copyright (C) 1995-1998 Jean-loup Gailly.
- * For conditions of distribution and use, see copyright notice in zlib.h
- *
- */
-
-/* @(#) $FreeBSD$ */
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
#include <sys/param.h>
-#include <sys/proc.h>
+
+#include <sys/gzio.h>
+#include <sys/kernel.h>
#include <sys/malloc.h>
-#include <sys/vnode.h>
-#include <sys/syslog.h>
-#include <sys/endian.h>
-#include <net/zutil.h>
-#include <sys/libkern.h>
-
-#include <sys/vnode.h>
-#include <sys/mount.h>
-
-#define GZ_HEADER_LEN 10
-
-#ifndef Z_BUFSIZE
-# ifdef MAXSEG_64K
-# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
-# else
-# define Z_BUFSIZE 16384
-# endif
-#endif
-#ifndef Z_PRINTF_BUFSIZE
-# define Z_PRINTF_BUFSIZE 4096
-#endif
-
-#define ALLOC(size) malloc(size, M_TEMP, M_WAITOK | M_ZERO)
-#define TRYFREE(p) {if (p) free(p, M_TEMP);}
-
-static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
-
-/* gzip flag byte */
-#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
-#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
-#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
-#define ORIG_NAME 0x08 /* bit 3 set: original file name present */
-#define COMMENT 0x10 /* bit 4 set: file comment present */
-#define RESERVED 0xE0 /* bits 5..7: reserved */
-
-typedef struct gz_stream {
- z_stream stream;
- int z_err; /* error code for last stream operation */
- int z_eof; /* set if end of input file */
- struct vnode *file; /* vnode pointer of .gz file */
- Byte *inbuf; /* input buffer */
- Byte *outbuf; /* output buffer */
- uLong crc; /* crc32 of uncompressed data */
- char *msg; /* error message */
- char *path; /* path name for debugging only */
- int transparent; /* 1 if input file is not a .gz file */
- char mode; /* 'w' or 'r' */
- long startpos; /* start of compressed data in file (header skipped) */
- off_t outoff; /* current offset in output file */
- int flags;
-} gz_stream;
-
-
-local int do_flush OF((gzFile file, int flush));
-local int destroy OF((gz_stream *s));
-local void putU32 OF((gz_stream *file, uint32_t x));
-local void *gz_alloc OF((void *notused, u_int items, u_int size));
-local void gz_free OF((void *notused, void *ptr));
-
-/* ===========================================================================
- Opens a gzip (.gz) file for reading or writing. The mode parameter
- is as in fopen ("rb" or "wb"). The file is given either by file descriptor
- or path name (if fd == -1).
- gz_open return NULL if the file could not be opened or if there was
- insufficient memory to allocate the (de)compression state; errno
- can be checked to distinguish the two cases (if errno is zero, the
- zlib error is Z_MEM_ERROR).
-*/
-gzFile gz_open (path, mode, vp)
- const char *path;
- const char *mode;
- struct vnode *vp;
-{
- int err;
- int level = Z_DEFAULT_COMPRESSION; /* compression level */
- int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
- const char *p = mode;
- gz_stream *s;
- char fmode[80]; /* copy of mode, without the compression level */
- char *m = fmode;
- ssize_t resid;
- int error;
- char buf[GZ_HEADER_LEN + 1];
-
- if (!path || !mode) return Z_NULL;
-
- s = (gz_stream *)ALLOC(sizeof(gz_stream));
- if (!s) return Z_NULL;
-
- s->stream.zalloc = (alloc_func)gz_alloc;
- s->stream.zfree = (free_func)gz_free;
- s->stream.opaque = (voidpf)0;
- s->stream.next_in = s->inbuf = Z_NULL;
- s->stream.next_out = s->outbuf = Z_NULL;
- s->stream.avail_in = s->stream.avail_out = 0;
- s->file = NULL;
- s->z_err = Z_OK;
- s->z_eof = 0;
- s->crc = 0;
- s->msg = NULL;
- s->transparent = 0;
- s->outoff = 0;
- s->flags = 0;
-
- s->path = (char*)ALLOC(strlen(path)+1);
- if (s->path == NULL) {
- return destroy(s), (gzFile)Z_NULL;
- }
- strcpy(s->path, path); /* do this early for debugging */
-
- s->mode = '\0';
- do {
- if (*p == 'r') s->mode = 'r';
- if (*p == 'w' || *p == 'a') s->mode = 'w';
- if (*p >= '0' && *p <= '9') {
- level = *p - '0';
- } else if (*p == 'f') {
- strategy = Z_FILTERED;
- } else if (*p == 'h') {
- strategy = Z_HUFFMAN_ONLY;
- } else {
- *m++ = *p; /* copy the mode */
- }
- } while (*p++ && m != fmode + sizeof(fmode));
-
- if (s->mode != 'w') {
- log(LOG_ERR, "gz_open: mode is not w (%c)\n", s->mode);
- return destroy(s), (gzFile)Z_NULL;
- }
-
- err = deflateInit2(&(s->stream), level,
- Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
- /* windowBits is passed < 0 to suppress zlib header */
-
- s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
- if (err != Z_OK || s->outbuf == Z_NULL) {
- return destroy(s), (gzFile)Z_NULL;
- }
-
- s->stream.avail_out = Z_BUFSIZE;
- s->file = vp;
-
- /* Write a very simple .gz header:
- */
- snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c%c%c%c", gz_magic[0],
- gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/,
- 0 /*xflags*/, OS_CODE);
-
- if ((error = vn_rdwr(UIO_WRITE, s->file, buf, GZ_HEADER_LEN, s->outoff,
- UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
- NOCRED, &resid, curthread))) {
- s->outoff += GZ_HEADER_LEN - resid;
- return destroy(s), (gzFile)Z_NULL;
- }
- s->outoff += GZ_HEADER_LEN;
- s->startpos = 10L;
-
- return (gzFile)s;
-}
+#include <net/zutil.h>
- /* ===========================================================================
- * Cleanup then free the given gz_stream. Return a zlib error code.
- Try freeing in the reverse order of allocations.
- */
-local int destroy (s)
- gz_stream *s;
+#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)
{
- int err = Z_OK;
-
- if (!s) return Z_STREAM_ERROR;
-
- TRYFREE(s->msg);
-
- if (s->stream.state != NULL) {
- if (s->mode == 'w') {
- err = deflateEnd(&(s->stream));
- }
- }
- if (s->z_err < 0) err = s->z_err;
-
- TRYFREE(s->inbuf);
- TRYFREE(s->outbuf);
- TRYFREE(s->path);
- TRYFREE(s);
- return err;
+ struct gzio_stream *s;
+ uint8_t *hdr;
+ 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_crc = ~0U;
+ 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;
+
+ 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;
+
+ return (s);
+
+fail:
+ gz_free(NULL, s->gz_buffer);
+ gz_free(NULL, s);
+ return (NULL);
}
-
-/* ===========================================================================
- Writes the given number of uncompressed bytes into the compressed file.
- gzwrite returns the number of bytes actually written (0 in case of error).
-*/
-int ZEXPORT gzwrite (file, buf, len)
- gzFile file;
- const voidp buf;
- unsigned len;
+int
+gzio_write(struct gzio_stream *s, void *data, u_int len)
{
- gz_stream *s = (gz_stream*)file;
- off_t curoff;
- size_t resid;
- int error;
-
- if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
-
- s->stream.next_in = (Bytef*)buf;
- s->stream.avail_in = len;
-
- curoff = s->outoff;
- while (s->stream.avail_in != 0) {
-
- if (s->stream.avail_out == 0) {
-
- s->stream.next_out = s->outbuf;
- error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE,
- curoff, UIO_SYSSPACE, IO_UNIT,
- curproc->p_ucred, NOCRED, &resid, curthread);
- if (error) {
- log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
- curoff += Z_BUFSIZE - resid;
- s->z_err = Z_ERRNO;
- break;
- }
- curoff += Z_BUFSIZE;
- s->stream.avail_out = Z_BUFSIZE;
- }
- s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
- if (s->z_err != Z_OK) {
- log(LOG_ERR,
- "gzwrite: deflate returned error %d\n", s->z_err);
- break;
- }
- }
-
- s->crc = ~crc32_raw(buf, len, ~s->crc);
- s->outoff = curoff;
-
- return (int)(len - s->stream.avail_in);
-}
-
-/* ===========================================================================
- Flushes all pending output into the compressed file. The parameter
- flush is as in the deflate() function.
-*/
-local int do_flush (file, flush)
- gzFile file;
- int flush;
-{
- uInt len;
- int done = 0;
- gz_stream *s = (gz_stream*)file;
- off_t curoff = s->outoff;
- size_t resid;
- int error;
-
- if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
-
- if (s->stream.avail_in) {
- log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
- }
-
- s->stream.avail_in = 0; /* should be zero already anyway */
-
- for (;;) {
- len = Z_BUFSIZE - s->stream.avail_out;
-
- if (len != 0) {
- error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff,
- UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
- NOCRED, &resid, curthread);
- if (error) {
- s->z_err = Z_ERRNO;
- s->outoff = curoff + len - resid;
- return Z_ERRNO;
- }
- s->stream.next_out = s->outbuf;
- s->stream.avail_out = Z_BUFSIZE;
- curoff += len;
- }
- if (done) break;
- s->z_err = deflate(&(s->stream), flush);
-
- /* Ignore the second of two consecutive flushes: */
- if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
-
- /* deflate has finished flushing only when it hasn't used up
- * all the available space in the output buffer:
- */
- done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
-
- if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
- }
- s->outoff = curoff;
-
- return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+ return (gz_write(s, data, len, Z_NO_FLUSH));
}
-int ZEXPORT gzflush (file, flush)
- gzFile file;
- int flush;
+int
+gzio_flush(struct gzio_stream *s)
{
- gz_stream *s = (gz_stream*)file;
- int err = do_flush (file, flush);
- if (err) return err;
- return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+ return (gz_write(s, NULL, 0, Z_FINISH));
}
-
-/* ===========================================================================
- Outputs a long in LSB order to the given file
-*/
-local void putU32 (s, x)
- gz_stream *s;
- uint32_t x;
+void
+gzio_fini(struct gzio_stream *s)
{
- uint32_t xx;
- off_t curoff = s->outoff;
- ssize_t resid;
-
-#if BYTE_ORDER == BIG_ENDIAN
- xx = bswap32(x);
-#else
- xx = x;
-#endif
- vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff,
- UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
- NOCRED, &resid, curthread);
- s->outoff += sizeof(xx) - resid;
-}
-
-/* ===========================================================================
- Flushes all pending output if necessary, closes the compressed file
- and deallocates all the (de)compression state.
-*/
-int ZEXPORT gzclose (file)
- gzFile file;
-{
- int err;
- gz_stream *s = (gz_stream*)file;
-
- if (s == NULL) return Z_STREAM_ERROR;
-
- if (s->mode == 'w') {
- err = do_flush (file, Z_FINISH);
- if (err != Z_OK) {
- log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
- return destroy((gz_stream*)file);
- }
-#if 0
- printf("gzclose: putting crc: %lld total: %lld\n",
- (long long)s->crc, (long long)s->stream.total_in);
- printf("sizeof uLong = %d\n", (int)sizeof(uLong));
-#endif
- putU32 (s, s->crc);
- putU32 (s, (uint32_t) s->stream.total_in);
- }
- return destroy((gz_stream*)file);
+ (void)deflateEnd(&s->gz_stream);
+ gz_free(NULL, s->gz_buffer);
+ gz_free(NULL, s);
}
-/*
- * Space allocation and freeing routines for use by zlib routines when called
- * from gzip modules.
- */
static void *
-gz_alloc(void *notused __unused, u_int items, u_int size)
+gz_alloc(void *arg __unused, u_int n, u_int sz)
{
- void *ptr;
- MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
- return ptr;
+ /*
+ * 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 *opaque __unused, void *ptr)
+gz_free(void *arg __unused, void *ptr)
{
- FREE(ptr, M_TEMP);
+
+ 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: sys/kern/kern_sig.c
===================================================================
--- sys/kern/kern_sig.c
+++ sys/kern/kern_sig.c
@@ -38,8 +38,8 @@
__FBSDID("$FreeBSD$");
#include "opt_compat.h"
+#include "opt_gzio.h"
#include "opt_ktrace.h"
-#include "opt_core.h"
#include <sys/param.h>
#include <sys/ctype.h>
@@ -3075,18 +3075,17 @@
SYSCTL_PROC(_debug, OID_AUTO, ncores, CTLTYPE_INT|CTLFLAG_RW,
0, sizeof(int), sysctl_debug_num_cores_check, "I", "");
-#if defined(COMPRESS_USER_CORES)
+#ifdef GZIO
int compress_user_cores = 1;
SYSCTL_INT(_kern, OID_AUTO, compress_user_cores, CTLFLAG_RW,
&compress_user_cores, 0, "Compression of user corefiles");
-int compress_user_cores_gzlevel = -1; /* default level */
+int compress_user_cores_gzlevel = 6;
SYSCTL_INT(_kern, OID_AUTO, compress_user_cores_gzlevel, CTLFLAG_RW,
- &compress_user_cores_gzlevel, -1, "Corefile gzip compression level");
+ &compress_user_cores_gzlevel, 0, "Corefile gzip compression level");
-#define GZ_SUFFIX ".gz"
-#define GZ_SUFFIX_LEN 3
-#endif
+#define GZ_SUFFIX ".gz"
+#endif /* GZIO */
static char corefilename[MAXPATHLEN] = {"%N.core"};
SYSCTL_STRING(_kern, OID_AUTO, corefile, CTLFLAG_RWTUN, corefilename,
@@ -3162,7 +3161,7 @@
}
}
free(hostname, M_TEMP);
-#ifdef COMPRESS_USER_CORES
+#ifdef GZIO
if (compress)
sbuf_printf(&sb, GZ_SUFFIX);
#endif
@@ -3267,7 +3266,7 @@
static const char comm_name[] = "comm=";
static const char core_name[] = "core=";
-#ifdef COMPRESS_USER_CORES
+#ifdef GZIO
compress = compress_user_cores;
#else
compress = 0;
Index: sys/net/zlib.h
===================================================================
--- sys/net/zlib.h
+++ sys/net/zlib.h
@@ -1010,13 +1010,6 @@
uLongf *get_crc_table OF((void)); /* can be used by asm versions of crc32() */
-#ifdef _KERNEL
-struct vnode;
-extern gzFile gz_open OF((const char *path, const char *mode,
- struct vnode *vp));
-#endif
-
-
#ifdef __cplusplus
}
#endif
Index: sys/sys/gzio.h
===================================================================
--- /dev/null
+++ sys/sys/gzio.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2014 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
+ * 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 *);
+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_ */
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Dec 14, 11:50 AM (11 h, 3 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
26958315
Default Alt Text
D1832.id3850.diff (39 KB)
Attached To
Mode
D1832: add a kernel gzio interface
Attached
Detach File
Event Timeline
Log In to Comment