kern_gzio.c revision 231949
1/*
2 * $Id: kern_gzio.c,v 1.6 2008-10-18 22:54:45 lbazinet Exp $
3 *
4 * core_gzip.c -- gzip routines used in compressing user process cores
5 *
6 * This file is derived from src/lib/libz/gzio.c in FreeBSD.
7 */
8
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
12 *
13 */
14
15/* @(#) $FreeBSD: head/sys/kern/kern_gzio.c 231949 2012-02-21 01:05:12Z kib $ */
16
17#include <sys/param.h>
18#include <sys/proc.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>
25
26#include <sys/vnode.h>
27#include <sys/mount.h>
28
29#define GZ_HEADER_LEN	10
30
31#ifndef Z_BUFSIZE
32#  ifdef MAXSEG_64K
33#    define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
34#  else
35#    define Z_BUFSIZE 16384
36#  endif
37#endif
38#ifndef Z_PRINTF_BUFSIZE
39#  define Z_PRINTF_BUFSIZE 4096
40#endif
41
42#define ALLOC(size) malloc(size, M_TEMP, M_WAITOK | M_ZERO)
43#define TRYFREE(p) {if (p) free(p, M_TEMP);}
44
45static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
46
47/* gzip flag byte */
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 */
54
55typedef struct gz_stream {
56    z_stream 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 */
69    int	     flags;
70} gz_stream;
71
72
73local int do_flush        OF((gzFile file, int flush));
74local int    destroy      OF((gz_stream *s));
75local void   putU32      OF((gz_stream *file, uint32_t x));
76local void *gz_alloc      OF((void *notused, u_int items, u_int size));
77local void gz_free        OF((void *notused, void *ptr));
78
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).
87*/
88gzFile gz_open (path, mode, vp)
89    const char *path;
90    const char *mode;
91    struct vnode *vp;
92{
93    int err;
94    int level = Z_DEFAULT_COMPRESSION; /* compression level */
95    int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
96    const char *p = mode;
97    gz_stream *s;
98    char fmode[80]; /* copy of mode, without the compression level */
99    char *m = fmode;
100    ssize_t resid;
101    int error;
102    char buf[GZ_HEADER_LEN + 1];
103
104    if (!path || !mode) return Z_NULL;
105
106    s = (gz_stream *)ALLOC(sizeof(gz_stream));
107    if (!s) return Z_NULL;
108
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;
115    s->file = NULL;
116    s->z_err = Z_OK;
117    s->z_eof = 0;
118    s->crc = 0;
119    s->msg = NULL;
120    s->transparent = 0;
121    s->outoff = 0;
122    s->flags = 0;
123
124    s->path = (char*)ALLOC(strlen(path)+1);
125    if (s->path == NULL) {
126        return destroy(s), (gzFile)Z_NULL;
127    }
128    strcpy(s->path, path); /* do this early for debugging */
129
130    s->mode = '\0';
131    do {
132        if (*p == 'r') s->mode = 'r';
133        if (*p == 'w' || *p == 'a') s->mode = 'w';
134        if (*p >= '0' && *p <= '9') {
135	    level = *p - '0';
136	} else if (*p == 'f') {
137	  strategy = Z_FILTERED;
138	} else if (*p == 'h') {
139	  strategy = Z_HUFFMAN_ONLY;
140	} else {
141	    *m++ = *p; /* copy the mode */
142	}
143    } while (*p++ && m != fmode + sizeof(fmode));
144
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;
148    }
149
150    err = deflateInit2(&(s->stream), level,
151                       Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
152    /* windowBits is passed < 0 to suppress zlib header */
153
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;
157    }
158
159    s->stream.avail_out = Z_BUFSIZE;
160    s->file = vp;
161
162    /* Write a very simple .gz header:
163     */
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);
167
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;
173    }
174    s->outoff += GZ_HEADER_LEN;
175    s->startpos = 10L;
176
177    return (gzFile)s;
178}
179
180
181 /* ===========================================================================
182 * Cleanup then free the given gz_stream. Return a zlib error code.
183   Try freeing in the reverse order of allocations.
184 */
185local int destroy (s)
186    gz_stream *s;
187{
188    int err = Z_OK;
189
190    if (!s) return Z_STREAM_ERROR;
191
192    TRYFREE(s->msg);
193
194    if (s->stream.state != NULL) {
195	if (s->mode == 'w') {
196	    err = deflateEnd(&(s->stream));
197	}
198    }
199    if (s->z_err < 0) err = s->z_err;
200
201    TRYFREE(s->inbuf);
202    TRYFREE(s->outbuf);
203    TRYFREE(s->path);
204    TRYFREE(s);
205    return err;
206}
207
208
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).
212*/
213int ZEXPORT gzwrite (file, buf, len)
214    gzFile file;
215    const voidp buf;
216    unsigned len;
217{
218    gz_stream *s = (gz_stream*)file;
219    off_t curoff;
220    size_t resid;
221    int error;
222    int vfslocked;
223
224    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
225
226    s->stream.next_in = (Bytef*)buf;
227    s->stream.avail_in = len;
228
229    curoff = s->outoff;
230    while (s->stream.avail_in != 0) {
231
232        if (s->stream.avail_out == 0) {
233
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);
240            if (error) {
241                log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
242                curoff += Z_BUFSIZE - resid;
243                s->z_err = Z_ERRNO;
244                break;
245            }
246            curoff += Z_BUFSIZE;
247            s->stream.avail_out = Z_BUFSIZE;
248        }
249        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
250        if (s->z_err != Z_OK) {
251            log(LOG_ERR,
252                "gzwrite: deflate returned error %d\n", s->z_err);
253            break;
254        }
255    }
256
257    s->crc = ~crc32_raw(buf, len, ~s->crc);
258    s->outoff = curoff;
259
260    return (int)(len - s->stream.avail_in);
261}
262
263
264/* ===========================================================================
265     Flushes all pending output into the compressed file. The parameter
266   flush is as in the deflate() function.
267*/
268local int do_flush (file, flush)
269    gzFile file;
270    int flush;
271{
272    uInt len;
273    int done = 0;
274    gz_stream *s = (gz_stream*)file;
275    off_t curoff = s->outoff;
276    size_t resid;
277    int vfslocked = 0;
278    int error;
279
280    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
281
282    if (s->stream.avail_in) {
283        log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
284    }
285
286    s->stream.avail_in = 0; /* should be zero already anyway */
287
288    for (;;) {
289        len = Z_BUFSIZE - s->stream.avail_out;
290
291        if (len != 0) {
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);
297	    if (error) {
298                s->z_err = Z_ERRNO;
299                s->outoff = curoff + len - resid;
300                return Z_ERRNO;
301            }
302            s->stream.next_out = s->outbuf;
303            s->stream.avail_out = Z_BUFSIZE;
304            curoff += len;
305        }
306        if (done) break;
307        s->z_err = deflate(&(s->stream), flush);
308
309	/* Ignore the second of two consecutive flushes: */
310	if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
311
312        /* deflate has finished flushing only when it hasn't used up
313         * all the available space in the output buffer:
314         */
315        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
316
317        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
318    }
319    s->outoff = curoff;
320
321    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
322}
323
324int ZEXPORT gzflush (file, flush)
325     gzFile file;
326     int flush;
327{
328    gz_stream *s = (gz_stream*)file;
329    int err = do_flush (file, flush);
330
331    if (err) return err;
332    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
333}
334
335
336/* ===========================================================================
337   Outputs a long in LSB order to the given file
338*/
339local void putU32 (s, x)
340    gz_stream *s;
341    uint32_t x;
342{
343    uint32_t xx;
344    off_t curoff = s->outoff;
345    ssize_t resid;
346
347#if BYTE_ORDER == BIG_ENDIAN
348    xx = bswap32(x);
349#else
350    xx = x;
351#endif
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;
356}
357
358
359/* ===========================================================================
360     Flushes all pending output if necessary, closes the compressed file
361   and deallocates all the (de)compression state.
362*/
363int ZEXPORT gzclose (file)
364    gzFile file;
365{
366    int err;
367    gz_stream *s = (gz_stream*)file;
368
369    if (s == NULL) return Z_STREAM_ERROR;
370
371    if (s->mode == 'w') {
372        err = do_flush (file, Z_FINISH);
373        if (err != Z_OK) {
374            log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
375            return destroy((gz_stream*)file);
376        }
377#if 0
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));
381#endif
382        putU32 (s, s->crc);
383        putU32 (s, (uint32_t) s->stream.total_in);
384    }
385    return destroy((gz_stream*)file);
386}
387
388/*
389 * Space allocation and freeing routines for use by zlib routines when called
390 * from gzip modules.
391 */
392static void *
393gz_alloc(void *notused __unused, u_int items, u_int size)
394{
395    void *ptr;
396
397    MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
398    return ptr;
399}
400
401static void
402gz_free(void *opaque __unused, void *ptr)
403{
404    FREE(ptr, M_TEMP);
405}
406
407