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;
224 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
226 s->stream.next_in = (Bytef*)buf;
227 s->stream.avail_in = len;
230 while (s->stream.avail_in != 0) {
232 if (s->stream.avail_out == 0) {
234 s->stream.next_out = s->outbuf;
235 vfslocked = VFS_LOCK_GIANT(s->file->v_mount);
236 error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE,
237 curoff, UIO_SYSSPACE, IO_UNIT,
238 curproc->p_ucred, NOCRED, &resid, curthread);
239 VFS_UNLOCK_GIANT(vfslocked);
241 log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
242 curoff += Z_BUFSIZE - resid;
247 s->stream.avail_out = Z_BUFSIZE;
249 s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
250 if (s->z_err != Z_OK) {
252 "gzwrite: deflate returned error %d\n", s->z_err);
257 s->crc = ~crc32_raw(buf, len, ~s->crc);
260 return (int)(len - s->stream.avail_in);
264 /* ===========================================================================
265 Flushes all pending output into the compressed file. The parameter
266 flush is as in the deflate() function.
268 local int do_flush (file, flush)
274 gz_stream *s = (gz_stream*)file;
275 off_t curoff = s->outoff;
280 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
282 if (s->stream.avail_in) {
283 log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
286 s->stream.avail_in = 0; /* should be zero already anyway */
289 len = Z_BUFSIZE - s->stream.avail_out;
292 vfslocked = VFS_LOCK_GIANT(s->file->v_mount);
293 error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff,
294 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
295 NOCRED, &resid, curthread);
296 VFS_UNLOCK_GIANT(vfslocked);
299 s->outoff = curoff + len - resid;
302 s->stream.next_out = s->outbuf;
303 s->stream.avail_out = Z_BUFSIZE;
307 s->z_err = deflate(&(s->stream), flush);
309 /* Ignore the second of two consecutive flushes: */
310 if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
312 /* deflate has finished flushing only when it hasn't used up
313 * all the available space in the output buffer:
315 done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
317 if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
321 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
324 int ZEXPORT gzflush (file, flush)
328 gz_stream *s = (gz_stream*)file;
329 int err = do_flush (file, flush);
332 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
336 /* ===========================================================================
337 Outputs a long in LSB order to the given file
339 local void putU32 (s, x)
344 off_t curoff = s->outoff;
347 #if BYTE_ORDER == BIG_ENDIAN
352 vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff,
353 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
354 NOCRED, &resid, curthread);
355 s->outoff += sizeof(xx) - resid;
359 /* ===========================================================================
360 Flushes all pending output if necessary, closes the compressed file
361 and deallocates all the (de)compression state.
363 int ZEXPORT gzclose (file)
367 gz_stream *s = (gz_stream*)file;
369 if (s == NULL) return Z_STREAM_ERROR;
371 if (s->mode == 'w') {
372 err = do_flush (file, Z_FINISH);
374 log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
375 return destroy((gz_stream*)file);
378 printf("gzclose: putting crc: %lld total: %lld\n",
379 (long long)s->crc, (long long)s->stream.total_in);
380 printf("sizeof uLong = %d\n", (int)sizeof(uLong));
383 putU32 (s, (uint32_t) s->stream.total_in);
385 return destroy((gz_stream*)file);
389 * Space allocation and freeing routines for use by zlib routines when called
393 gz_alloc(void *notused __unused, u_int items, u_int size)
397 MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
402 gz_free(void *opaque __unused, void *ptr)