2 * $Id: kern_gzio.c,v 1.6 2008-10-18 22:54:45 lbazinet Exp $
4 * core_gzip.c -- gzip routines used in compressing user process cores
6 * This file is derived from src/lib/libz/gzio.c in FreeBSD.
9 /* gzio.c -- IO on .gz files
10 * Copyright (C) 1995-1998 Jean-loup Gailly.
11 * For conditions of distribution and use, see copyright notice in zlib.h
17 #include <sys/param.h>
19 #include <sys/malloc.h>
20 #include <sys/vnode.h>
21 #include <sys/syslog.h>
22 #include <sys/endian.h>
23 #include <net/zutil.h>
24 #include <sys/libkern.h>
26 #include <sys/vnode.h>
27 #include <sys/mount.h>
29 #define GZ_HEADER_LEN 10
33 # define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
35 # define Z_BUFSIZE 16384
38 #ifndef Z_PRINTF_BUFSIZE
39 # define Z_PRINTF_BUFSIZE 4096
42 #define ALLOC(size) malloc(size, M_TEMP, M_WAITOK | M_ZERO)
43 #define TRYFREE(p) {if (p) free(p, M_TEMP);}
45 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
48 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
49 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
50 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
51 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
52 #define COMMENT 0x10 /* bit 4 set: file comment present */
53 #define RESERVED 0xE0 /* bits 5..7: reserved */
55 typedef struct gz_stream {
57 int z_err; /* error code for last stream operation */
58 int z_eof; /* set if end of input file */
59 struct vnode *file; /* vnode pointer of .gz file */
60 Byte *inbuf; /* input buffer */
61 Byte *outbuf; /* output buffer */
62 uLong crc; /* crc32 of uncompressed data */
63 char *msg; /* error message */
64 char *path; /* path name for debugging only */
65 int transparent; /* 1 if input file is not a .gz file */
66 char mode; /* 'w' or 'r' */
67 long startpos; /* start of compressed data in file (header skipped) */
68 off_t outoff; /* current offset in output file */
73 local int do_flush OF((gzFile file, int flush));
74 local int destroy OF((gz_stream *s));
75 local void putU32 OF((gz_stream *file, uint32_t x));
76 local void *gz_alloc OF((void *notused, u_int items, u_int size));
77 local void gz_free OF((void *notused, void *ptr));
79 /* ===========================================================================
80 Opens a gzip (.gz) file for reading or writing. The mode parameter
81 is as in fopen ("rb" or "wb"). The file is given either by file descriptor
82 or path name (if fd == -1).
83 gz_open return NULL if the file could not be opened or if there was
84 insufficient memory to allocate the (de)compression state; errno
85 can be checked to distinguish the two cases (if errno is zero, the
86 zlib error is Z_MEM_ERROR).
88 gzFile gz_open (path, mode, vp)
94 int level = Z_DEFAULT_COMPRESSION; /* compression level */
95 int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
98 char fmode[80]; /* copy of mode, without the compression level */
102 char buf[GZ_HEADER_LEN + 1];
104 if (!path || !mode) return Z_NULL;
106 s = (gz_stream *)ALLOC(sizeof(gz_stream));
107 if (!s) return Z_NULL;
109 s->stream.zalloc = (alloc_func)gz_alloc;
110 s->stream.zfree = (free_func)gz_free;
111 s->stream.opaque = (voidpf)0;
112 s->stream.next_in = s->inbuf = Z_NULL;
113 s->stream.next_out = s->outbuf = Z_NULL;
114 s->stream.avail_in = s->stream.avail_out = 0;
124 s->path = (char*)ALLOC(strlen(path)+1);
125 if (s->path == NULL) {
126 return destroy(s), (gzFile)Z_NULL;
128 strcpy(s->path, path); /* do this early for debugging */
132 if (*p == 'r') s->mode = 'r';
133 if (*p == 'w' || *p == 'a') s->mode = 'w';
134 if (*p >= '0' && *p <= '9') {
136 } else if (*p == 'f') {
137 strategy = Z_FILTERED;
138 } else if (*p == 'h') {
139 strategy = Z_HUFFMAN_ONLY;
141 *m++ = *p; /* copy the mode */
143 } while (*p++ && m != fmode + sizeof(fmode));
145 if (s->mode != 'w') {
146 log(LOG_ERR, "gz_open: mode is not w (%c)\n", s->mode);
147 return destroy(s), (gzFile)Z_NULL;
150 err = deflateInit2(&(s->stream), level,
151 Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
152 /* windowBits is passed < 0 to suppress zlib header */
154 s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
155 if (err != Z_OK || s->outbuf == Z_NULL) {
156 return destroy(s), (gzFile)Z_NULL;
159 s->stream.avail_out = Z_BUFSIZE;
162 /* Write a very simple .gz header:
164 snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c%c%c%c", gz_magic[0],
165 gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/,
166 0 /*xflags*/, OS_CODE);
168 if ((error = vn_rdwr(UIO_WRITE, s->file, buf, GZ_HEADER_LEN, s->outoff,
169 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
170 NOCRED, &resid, curthread))) {
171 s->outoff += GZ_HEADER_LEN - resid;
172 return destroy(s), (gzFile)Z_NULL;
174 s->outoff += GZ_HEADER_LEN;
181 /* ===========================================================================
182 * Cleanup then free the given gz_stream. Return a zlib error code.
183 Try freeing in the reverse order of allocations.
185 local int destroy (s)
190 if (!s) return Z_STREAM_ERROR;
194 if (s->stream.state != NULL) {
195 if (s->mode == 'w') {
196 err = deflateEnd(&(s->stream));
199 if (s->z_err < 0) err = s->z_err;
209 /* ===========================================================================
210 Writes the given number of uncompressed bytes into the compressed file.
211 gzwrite returns the number of bytes actually written (0 in case of error).
213 int ZEXPORT gzwrite (file, buf, len)
218 gz_stream *s = (gz_stream*)file;
223 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
225 s->stream.next_in = (Bytef*)buf;
226 s->stream.avail_in = len;
229 while (s->stream.avail_in != 0) {
231 if (s->stream.avail_out == 0) {
233 s->stream.next_out = s->outbuf;
234 error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE,
235 curoff, UIO_SYSSPACE, IO_UNIT,
236 curproc->p_ucred, NOCRED, &resid, curthread);
238 log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
239 curoff += Z_BUFSIZE - resid;
244 s->stream.avail_out = Z_BUFSIZE;
246 s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
247 if (s->z_err != Z_OK) {
249 "gzwrite: deflate returned error %d\n", s->z_err);
254 s->crc = ~crc32_raw(buf, len, ~s->crc);
257 return (int)(len - s->stream.avail_in);
261 /* ===========================================================================
262 Flushes all pending output into the compressed file. The parameter
263 flush is as in the deflate() function.
265 local int do_flush (file, flush)
271 gz_stream *s = (gz_stream*)file;
272 off_t curoff = s->outoff;
276 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
278 if (s->stream.avail_in) {
279 log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
282 s->stream.avail_in = 0; /* should be zero already anyway */
285 len = Z_BUFSIZE - s->stream.avail_out;
288 error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff,
289 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
290 NOCRED, &resid, curthread);
293 s->outoff = curoff + len - resid;
296 s->stream.next_out = s->outbuf;
297 s->stream.avail_out = Z_BUFSIZE;
301 s->z_err = deflate(&(s->stream), flush);
303 /* Ignore the second of two consecutive flushes: */
304 if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
306 /* deflate has finished flushing only when it hasn't used up
307 * all the available space in the output buffer:
309 done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
311 if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
315 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
318 int ZEXPORT gzflush (file, flush)
322 gz_stream *s = (gz_stream*)file;
323 int err = do_flush (file, flush);
326 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
330 /* ===========================================================================
331 Outputs a long in LSB order to the given file
333 local void putU32 (s, x)
338 off_t curoff = s->outoff;
341 #if BYTE_ORDER == BIG_ENDIAN
346 vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff,
347 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
348 NOCRED, &resid, curthread);
349 s->outoff += sizeof(xx) - resid;
353 /* ===========================================================================
354 Flushes all pending output if necessary, closes the compressed file
355 and deallocates all the (de)compression state.
357 int ZEXPORT gzclose (file)
361 gz_stream *s = (gz_stream*)file;
363 if (s == NULL) return Z_STREAM_ERROR;
365 if (s->mode == 'w') {
366 err = do_flush (file, Z_FINISH);
368 log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
369 return destroy((gz_stream*)file);
372 printf("gzclose: putting crc: %lld total: %lld\n",
373 (long long)s->crc, (long long)s->stream.total_in);
374 printf("sizeof uLong = %d\n", (int)sizeof(uLong));
377 putU32 (s, (uint32_t) s->stream.total_in);
379 return destroy((gz_stream*)file);
383 * Space allocation and freeing routines for use by zlib routines when called
387 gz_alloc(void *notused __unused, u_int items, u_int size)
391 MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
396 gz_free(void *opaque __unused, void *ptr)