svn commit: r279801 - in head: share/man/man5 sys/conf sys/kern sys/net sys/sys

Mark Johnston markj at FreeBSD.org
Mon Mar 9 03:50:55 UTC 2015


Author: markj
Date: Mon Mar  9 03:50:53 2015
New Revision: 279801
URL: https://svnweb.freebsd.org/changeset/base/279801

Log:
  Reimplement support for userland core dump compression using a new interface
  in kern_gzio.c. The old gzio interface was somewhat inflexible and has not
  worked properly since r272535: currently, the gzio functions are called with
  a range lock held on the output vnode, but kern_gzio.c does not pass the
  IO_RANGELOCKED flag to vn_rdwr() calls, resulting in deadlock when vn_rdwr()
  attempts to reacquire the range lock. Moreover, the new gzio interface can
  be used to implement kernel core compression.
  
  This change also modifies the kernel configuration options needed to enable
  userland core dump compression support: gzio is now an option rather than a
  device, and the COMPRESS_USER_CORES option is removed. Core dump compression
  is enabled using the kern.compress_user_cores sysctl/tunable.
  
  Differential Revision:	https://reviews.freebsd.org/D1832
  Reviewed by:	rpaulo
  Discussed with:	kib

Added:
  head/sys/sys/gzio.h   (contents, props changed)
Modified:
  head/share/man/man5/core.5
  head/sys/conf/NOTES
  head/sys/conf/options
  head/sys/kern/imgact_elf.c
  head/sys/kern/kern_gzio.c
  head/sys/kern/kern_sig.c
  head/sys/net/zlib.h

Modified: head/share/man/man5/core.5
==============================================================================
--- head/share/man/man5/core.5	Mon Mar  9 03:31:26 2015	(r279800)
+++ head/share/man/man5/core.5	Mon Mar  9 03:50:53 2015	(r279801)
@@ -28,7 +28,7 @@
 .\"     @(#)core.5	8.3 (Berkeley) 12/11/93
 .\" $FreeBSD$
 .\"
-.Dd November 22, 2012
+.Dd March 8, 2015
 .Dt CORE 5
 .Os
 .Sh NAME
@@ -101,25 +101,23 @@ variable
 .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

Modified: head/sys/conf/NOTES
==============================================================================
--- head/sys/conf/NOTES	Mon Mar  9 03:31:26 2015	(r279800)
+++ head/sys/conf/NOTES	Mon Mar  9 03:50:53 2015	(r279801)
@@ -2889,11 +2889,6 @@ options 	SHMMNI=33
 # 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 @@ options 	RANDOM_DEBUG	# Debugging messag
 
 # 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

Modified: head/sys/conf/options
==============================================================================
--- head/sys/conf/options	Mon Mar  9 03:31:26 2015	(r279800)
+++ head/sys/conf/options	Mon Mar  9 03:50:53 2015	(r279801)
@@ -87,13 +87,13 @@ COMPAT_FREEBSD9	opt_compat.h
 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

Modified: head/sys/kern/imgact_elf.c
==============================================================================
--- head/sys/kern/imgact_elf.c	Mon Mar  9 03:31:26 2015	(r279800)
+++ head/sys/kern/imgact_elf.c	Mon Mar  9 03:50:53 2015	(r279801)
@@ -33,12 +33,13 @@ __FBSDID("$FreeBSD$");
 
 #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/jail.h>
@@ -69,8 +70,6 @@ __FBSDID("$FreeBSD$");
 #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>
@@ -105,11 +104,7 @@ static Elf_Word __elfN(untrans_prot)(vm_
 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,
@@ -1067,11 +1062,23 @@ struct note_info {
 
 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 int core_write(struct coredump_params *, void *, size_t, off_t,
+    enum uio_seg);
 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);
@@ -1095,42 +1102,60 @@ static void note_procstat_rlimit(void *,
 static void note_procstat_umask(void *, struct sbuf *, size_t *);
 static void note_procstat_vmmap(void *, struct sbuf *, size_t *);
 
-#ifdef COMPRESS_USER_CORES
-extern int compress_user_cores;
+#ifdef GZIO
 extern int compress_user_cores_gzlevel;
-#endif
 
+/*
+ * Write out a core segment to the compression stream.
+ */
 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(struct coredump_params *p, char *base, char *buf, u_int len)
+{
+	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);
 }
 
-/* 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_gz_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, void *base, size_t len, off_t offset,
+    enum uio_seg seg)
+{
+
+	return (vn_rdwr_inchunks(UIO_WRITE, p->vp, base, len, offset,
+	    seg, 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)
+{
+
+#ifdef GZIO
+	if (p->gzs != NULL)
+		return (compress_chunk(p, base, tmpbuf, len));
 #endif
-};
+	return (core_write(p, base, len, offset, UIO_USERSPACE));
+}
 
 /*
  * Drain into a core file.
@@ -1138,10 +1163,10 @@ struct sbuf_drain_core_params {
 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
@@ -1154,16 +1179,13 @@ sbuf_drain_core_output(void *arg, const 
 	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(p, __DECONST(void *, data), len, p->offset,
+		    UIO_SYSSPACE);
 	if (locked)
 		PROC_LOCK(p->td->td_proc);
 	if (error != 0)
@@ -1192,42 +1214,16 @@ __elfN(coredump)(struct thread *td, stru
 	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(&notelst);
 
-#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;
@@ -1254,6 +1250,28 @@ __elfN(coredump)(struct thread *td, stru
 		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_gz_write, GZIO_DEFLATE,
+		    CORE_BUF_SIZE, compress_user_cores_gzlevel, &params);
+		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.
@@ -1263,8 +1281,8 @@ __elfN(coredump)(struct thread *td, stru
 		error = EINVAL;
 		goto done;
 	}
-	error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize,
-	    &notelst, notesz, gzfile);
+	error = __elfN(corehdr)(&params, seginfo.count, hdr, hdrsize, &notelst,
+	    notesz);
 
 	/* Write the contents of all of the writable segments. */
 	if (error == 0) {
@@ -1275,13 +1293,17 @@ __elfN(coredump)(struct thread *td, stru
 		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, &params, 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,
@@ -1290,11 +1312,11 @@ __elfN(coredump)(struct thread *td, stru
 	}
 
 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(&notelst)) != NULL) {
 		TAILQ_REMOVE(&notelst, ninfo, link);
@@ -1419,29 +1441,19 @@ each_writable_segment(td, func, closure)
  * 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, &params);
+	sbuf_set_drain(sb, sbuf_drain_core_output, p);
 	sbuf_start_section(sb, NULL);
 	sbuf_bcat(sb, hdr, hdrsize);
 	TAILQ_FOREACH(ninfo, notelst, link)
@@ -2108,58 +2120,6 @@ static struct execsw __elfN(execsw) = {
 };
 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)
 {

Modified: head/sys/kern/kern_gzio.c
==============================================================================
--- head/sys/kern/kern_gzio.c	Mon Mar  9 03:31:26 2015	(r279800)
+++ head/sys/kern/kern_gzio.c	Mon Mar  9 03:50:53 2015	(r279801)
@@ -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 at 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	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 */
 
-#define GZ_HEADER_LEN	10
+MALLOC_DEFINE(M_GZIO, "gzio", "zlib state");
 
-#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;
-}
+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);
 
- /* ===========================================================================
- * 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;
+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;
+	struct gzio_stream *s;
+	uint8_t *hdr;
+	int 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;
-}
+	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;
 
-/* ===========================================================================
-     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;
-{
-    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->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;
 
-    s->crc = ~crc32_raw(buf, len, ~s->crc);
-    s->outoff = curoff;
+	error = deflateInit2(&s->gz_stream, level, Z_DEFLATED, -MAX_WBITS,
+	    DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+	if (error != 0)
+		goto fail;
 
-    return (int)(len - s->stream.avail_in);
-}
+	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;
 
-/* ===========================================================================
-     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);
 
-    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
+fail:
+	gz_free(NULL, s->gz_buffer);
+	gz_free(NULL, s);
+	return (NULL);
 }
 
-int ZEXPORT gzflush (file, flush)
-     gzFile file;
-     int flush;
+int
+gzio_write(struct gzio_stream *s, void *data, u_int len)
 {
-    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, data, len, Z_NO_FLUSH));
 }
 
-
-/* ===========================================================================
-   Outputs a long in LSB order to the given file
-*/
-local void putU32 (s, x)
-    gz_stream *s;
-    uint32_t x;
+int
+gzio_flush(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;
-}
 
+	return (gz_write(s, NULL, 0, Z_FINISH));
+}
 
-/* ===========================================================================
-     Flushes all pending output if necessary, closes the compressed file
-   and deallocates all the (de)compression state.
-*/
-int ZEXPORT gzclose (file)
-    gzFile file;
+void
+gzio_fini(struct gzio_stream *s)
 {
-    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);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-src-head mailing list