kern_gzio.c revision 207415
1204552Salfred/*
2204552Salfred * $Id: kern_gzio.c,v 1.6 2008-10-18 22:54:45 lbazinet Exp $
3204552Salfred *
4204552Salfred * core_gzip.c -- gzip routines used in compressing user process cores
5204552Salfred *
6204552Salfred * This file is derived from src/lib/libz/gzio.c in FreeBSD.
7204552Salfred */
8204552Salfred
9204552Salfred/* gzio.c -- IO on .gz files
10204552Salfred * Copyright (C) 1995-1998 Jean-loup Gailly.
11204552Salfred * For conditions of distribution and use, see copyright notice in zlib.h
12204552Salfred *
13204552Salfred */
14204552Salfred
15204552Salfred/* @(#) $FreeBSD: head/sys/kern/kern_gzio.c 207415 2010-04-30 03:10:53Z alfred $ */
16204552Salfred
17204552Salfred#include <sys/param.h>
18204552Salfred#include <sys/proc.h>
19204552Salfred#include <sys/malloc.h>
20204552Salfred#include <sys/vnode.h>
21204552Salfred#include <sys/syslog.h>
22204552Salfred#include <sys/endian.h>
23204552Salfred#include <net/zutil.h>
24204552Salfred#include <sys/libkern.h>
25204552Salfred
26204552Salfred#include <sys/vnode.h>
27204552Salfred#include <sys/mount.h>
28204552Salfred
29204552Salfred#define GZ_HEADER_LEN	10
30204552Salfred
31204552Salfred#ifndef Z_BUFSIZE
32204552Salfred#  ifdef MAXSEG_64K
33204552Salfred#    define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */
34204552Salfred#  else
35204552Salfred#    define Z_BUFSIZE 16384
36204552Salfred#  endif
37204552Salfred#endif
38204552Salfred#ifndef Z_PRINTF_BUFSIZE
39204552Salfred#  define Z_PRINTF_BUFSIZE 4096
40204552Salfred#endif
41204552Salfred
42204552Salfred#define ALLOC(size) malloc(size, M_TEMP, M_WAITOK | M_ZERO)
43204552Salfred#define TRYFREE(p) {if (p) free(p, M_TEMP);}
44204552Salfred
45204552Salfredstatic int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
46204552Salfred
47204552Salfred/* gzip flag byte */
48204552Salfred#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
49204552Salfred#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
50204552Salfred#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
51204552Salfred#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
52204552Salfred#define COMMENT      0x10 /* bit 4 set: file comment present */
53204552Salfred#define RESERVED     0xE0 /* bits 5..7: reserved */
54204552Salfred
55204552Salfredtypedef struct gz_stream {
56204552Salfred    z_stream stream;
57204552Salfred    int      z_err;   /* error code for last stream operation */
58204552Salfred    int      z_eof;   /* set if end of input file */
59204552Salfred    struct vnode *file; /* vnode pointer of .gz file */
60204552Salfred    Byte     *inbuf;  /* input buffer */
61204552Salfred    Byte     *outbuf; /* output buffer */
62204552Salfred    uLong    crc;     /* crc32 of uncompressed data */
63204552Salfred    char     *msg;    /* error message */
64204552Salfred    char     *path;   /* path name for debugging only */
65204552Salfred    int      transparent; /* 1 if input file is not a .gz file */
66204552Salfred    char     mode;    /* 'w' or 'r' */
67204552Salfred    long     startpos; /* start of compressed data in file (header skipped) */
68204552Salfred    off_t    outoff;  /* current offset in output file */
69204552Salfred    int	     flags;
70204552Salfred} gz_stream;
71204552Salfred
72204552Salfred
73204552Salfredlocal int do_flush        OF((gzFile file, int flush));
74204552Salfredlocal int    destroy      OF((gz_stream *s));
75204552Salfredlocal void   putU32      OF((gz_stream *file, uint32_t x));
76204552Salfredlocal void *gz_alloc      OF((void *notused, u_int items, u_int size));
77204552Salfredlocal void gz_free        OF((void *notused, void *ptr));
78204552Salfred
79204552Salfred/* ===========================================================================
80204552Salfred     Opens a gzip (.gz) file for reading or writing. The mode parameter
81204552Salfred   is as in fopen ("rb" or "wb"). The file is given either by file descriptor
82204552Salfred   or path name (if fd == -1).
83204552Salfred     gz_open return NULL if the file could not be opened or if there was
84204552Salfred   insufficient memory to allocate the (de)compression state; errno
85204552Salfred   can be checked to distinguish the two cases (if errno is zero, the
86204552Salfred   zlib error is Z_MEM_ERROR).
87204552Salfred*/
88204552SalfredgzFile gz_open (path, mode, vp)
89204552Salfred    const char *path;
90204552Salfred    const char *mode;
91204552Salfred    struct vnode *vp;
92204552Salfred{
93204552Salfred    int err;
94204552Salfred    int level = Z_DEFAULT_COMPRESSION; /* compression level */
95204552Salfred    int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */
96204552Salfred    const char *p = mode;
97204552Salfred    gz_stream *s;
98204552Salfred    char fmode[80]; /* copy of mode, without the compression level */
99204552Salfred    char *m = fmode;
100204552Salfred    int resid;
101204552Salfred    int error;
102204552Salfred    char buf[GZ_HEADER_LEN + 1];
103204552Salfred
104204552Salfred    if (!path || !mode) return Z_NULL;
105204552Salfred
106204552Salfred    s = (gz_stream *)ALLOC(sizeof(gz_stream));
107204552Salfred    if (!s) return Z_NULL;
108204552Salfred
109204552Salfred    s->stream.zalloc = (alloc_func)gz_alloc;
110204552Salfred    s->stream.zfree = (free_func)gz_free;
111204552Salfred    s->stream.opaque = (voidpf)0;
112204552Salfred    s->stream.next_in = s->inbuf = Z_NULL;
113204552Salfred    s->stream.next_out = s->outbuf = Z_NULL;
114204552Salfred    s->stream.avail_in = s->stream.avail_out = 0;
115204552Salfred    s->file = NULL;
116204552Salfred    s->z_err = Z_OK;
117204552Salfred    s->z_eof = 0;
118204552Salfred    s->crc = 0;
119204552Salfred    s->msg = NULL;
120204552Salfred    s->transparent = 0;
121204552Salfred    s->outoff = 0;
122204552Salfred    s->flags = 0;
123204552Salfred
124204552Salfred    s->path = (char*)ALLOC(strlen(path)+1);
125204552Salfred    if (s->path == NULL) {
126204552Salfred        return destroy(s), (gzFile)Z_NULL;
127204552Salfred    }
128204552Salfred    strcpy(s->path, path); /* do this early for debugging */
129204552Salfred
130204552Salfred    s->mode = '\0';
131204552Salfred    do {
132204552Salfred        if (*p == 'r') s->mode = 'r';
133204552Salfred        if (*p == 'w' || *p == 'a') s->mode = 'w';
134204552Salfred        if (*p >= '0' && *p <= '9') {
135204552Salfred	    level = *p - '0';
136204552Salfred	} else if (*p == 'f') {
137204552Salfred	  strategy = Z_FILTERED;
138204552Salfred	} else if (*p == 'h') {
139204552Salfred	  strategy = Z_HUFFMAN_ONLY;
140204552Salfred	} else {
141204552Salfred	    *m++ = *p; /* copy the mode */
142204552Salfred	}
143204552Salfred    } while (*p++ && m != fmode + sizeof(fmode));
144204552Salfred
145204552Salfred    if (s->mode != 'w') {
146204552Salfred        log(LOG_ERR, "gz_open: mode is not w (%c)\n", s->mode);
147204552Salfred        return destroy(s), (gzFile)Z_NULL;
148204552Salfred    }
149204552Salfred
150204552Salfred    err = deflateInit2(&(s->stream), level,
151204552Salfred                       Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy);
152204552Salfred    /* windowBits is passed < 0 to suppress zlib header */
153204552Salfred
154204552Salfred    s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE);
155204552Salfred    if (err != Z_OK || s->outbuf == Z_NULL) {
156204552Salfred        return destroy(s), (gzFile)Z_NULL;
157204552Salfred    }
158204552Salfred
159204552Salfred    s->stream.avail_out = Z_BUFSIZE;
160204552Salfred    s->file = vp;
161204552Salfred
162204552Salfred    /* Write a very simple .gz header:
163204552Salfred     */
164204552Salfred    snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c%c%c%c", gz_magic[0],
165204552Salfred             gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/,
166204552Salfred             0 /*xflags*/, OS_CODE);
167204552Salfred
168204552Salfred    if ((error = vn_rdwr(UIO_WRITE, s->file, buf, GZ_HEADER_LEN, s->outoff,
169207415Salfred                         UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
170204552Salfred                         NOCRED, &resid, curthread))) {
171204552Salfred        s->outoff += GZ_HEADER_LEN - resid;
172204552Salfred        return destroy(s), (gzFile)Z_NULL;
173204552Salfred    }
174204552Salfred    s->outoff += GZ_HEADER_LEN;
175204552Salfred    s->startpos = 10L;
176204552Salfred
177204552Salfred    return (gzFile)s;
178204552Salfred}
179204552Salfred
180204552Salfred
181204552Salfred /* ===========================================================================
182204552Salfred * Cleanup then free the given gz_stream. Return a zlib error code.
183204552Salfred   Try freeing in the reverse order of allocations.
184204552Salfred */
185204552Salfredlocal int destroy (s)
186204552Salfred    gz_stream *s;
187204552Salfred{
188204552Salfred    int err = Z_OK;
189204552Salfred
190204552Salfred    if (!s) return Z_STREAM_ERROR;
191204552Salfred
192204552Salfred    TRYFREE(s->msg);
193204552Salfred
194204552Salfred    if (s->stream.state != NULL) {
195204552Salfred	if (s->mode == 'w') {
196204552Salfred	    err = deflateEnd(&(s->stream));
197204552Salfred	}
198204552Salfred    }
199204552Salfred    if (s->z_err < 0) err = s->z_err;
200204552Salfred
201204552Salfred    TRYFREE(s->inbuf);
202204552Salfred    TRYFREE(s->outbuf);
203204552Salfred    TRYFREE(s->path);
204204552Salfred    TRYFREE(s);
205204552Salfred    return err;
206204552Salfred}
207204552Salfred
208204552Salfred
209204552Salfred/* ===========================================================================
210204552Salfred     Writes the given number of uncompressed bytes into the compressed file.
211204552Salfred   gzwrite returns the number of bytes actually written (0 in case of error).
212204552Salfred*/
213204552Salfredint ZEXPORT gzwrite (file, buf, len)
214204552Salfred    gzFile file;
215204552Salfred    const voidp buf;
216204552Salfred    unsigned len;
217204552Salfred{
218204552Salfred    gz_stream *s = (gz_stream*)file;
219204552Salfred    off_t curoff;
220204552Salfred    size_t resid;
221204552Salfred    int error;
222204552Salfred    int vfslocked;
223204552Salfred
224204552Salfred    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
225204552Salfred
226204552Salfred    s->stream.next_in = (Bytef*)buf;
227204552Salfred    s->stream.avail_in = len;
228204552Salfred
229204552Salfred    curoff = s->outoff;
230204552Salfred    while (s->stream.avail_in != 0) {
231204552Salfred
232204552Salfred        if (s->stream.avail_out == 0) {
233204552Salfred
234204552Salfred            s->stream.next_out = s->outbuf;
235204552Salfred            vfslocked = VFS_LOCK_GIANT(s->file->v_mount);
236204552Salfred            error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE,
237207415Salfred                        curoff, UIO_SYSSPACE, IO_UNIT,
238204552Salfred                        curproc->p_ucred, NOCRED, &resid, curthread);
239204552Salfred            VFS_UNLOCK_GIANT(vfslocked);
240204552Salfred            if (error) {
241204552Salfred                log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
242204552Salfred                curoff += Z_BUFSIZE - resid;
243204552Salfred                s->z_err = Z_ERRNO;
244204552Salfred                break;
245204552Salfred            }
246204552Salfred            curoff += Z_BUFSIZE;
247204552Salfred            s->stream.avail_out = Z_BUFSIZE;
248204552Salfred        }
249204552Salfred        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
250204552Salfred        if (s->z_err != Z_OK) {
251204552Salfred            log(LOG_ERR,
252204552Salfred                "gzwrite: deflate returned error %d\n", s->z_err);
253204552Salfred            break;
254204552Salfred        }
255204552Salfred    }
256204552Salfred
257204552Salfred    s->crc = ~crc32_raw(buf, len, ~s->crc);
258204552Salfred    s->outoff = curoff;
259204552Salfred
260204552Salfred    return (int)(len - s->stream.avail_in);
261204552Salfred}
262204552Salfred
263204552Salfred
264204552Salfred/* ===========================================================================
265204552Salfred     Flushes all pending output into the compressed file. The parameter
266204552Salfred   flush is as in the deflate() function.
267204552Salfred*/
268204552Salfredlocal int do_flush (file, flush)
269204552Salfred    gzFile file;
270204552Salfred    int flush;
271204552Salfred{
272204552Salfred    uInt len;
273204552Salfred    int done = 0;
274204552Salfred    gz_stream *s = (gz_stream*)file;
275204552Salfred    off_t curoff = s->outoff;
276204552Salfred    size_t resid;
277204552Salfred    int vfslocked = 0;
278204552Salfred    int error;
279204552Salfred
280204552Salfred    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
281204552Salfred
282204552Salfred    if (s->stream.avail_in) {
283204552Salfred        log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
284204552Salfred    }
285204552Salfred
286204552Salfred    s->stream.avail_in = 0; /* should be zero already anyway */
287204552Salfred
288204552Salfred    for (;;) {
289204552Salfred        len = Z_BUFSIZE - s->stream.avail_out;
290204552Salfred
291204552Salfred        if (len != 0) {
292204552Salfred            vfslocked = VFS_LOCK_GIANT(s->file->v_mount);
293204552Salfred            error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff,
294207415Salfred                        UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
295204552Salfred                        NOCRED, &resid, curthread);
296204552Salfred            VFS_UNLOCK_GIANT(vfslocked);
297204552Salfred	    if (error) {
298204552Salfred                s->z_err = Z_ERRNO;
299204552Salfred                s->outoff = curoff + len - resid;
300204552Salfred                return Z_ERRNO;
301204552Salfred            }
302204552Salfred            s->stream.next_out = s->outbuf;
303204552Salfred            s->stream.avail_out = Z_BUFSIZE;
304204552Salfred            curoff += len;
305204552Salfred        }
306204552Salfred        if (done) break;
307204552Salfred        s->z_err = deflate(&(s->stream), flush);
308204552Salfred
309204552Salfred	/* Ignore the second of two consecutive flushes: */
310204552Salfred	if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
311204552Salfred
312204552Salfred        /* deflate has finished flushing only when it hasn't used up
313204552Salfred         * all the available space in the output buffer:
314204552Salfred         */
315204552Salfred        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
316204552Salfred
317204552Salfred        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
318204552Salfred    }
319204552Salfred    s->outoff = curoff;
320204552Salfred
321204552Salfred    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
322204552Salfred}
323204552Salfred
324204552Salfredint ZEXPORT gzflush (file, flush)
325204552Salfred     gzFile file;
326204552Salfred     int flush;
327204552Salfred{
328204552Salfred    gz_stream *s = (gz_stream*)file;
329204552Salfred    int err = do_flush (file, flush);
330204552Salfred
331204552Salfred    if (err) return err;
332204552Salfred    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
333204552Salfred}
334204552Salfred
335204552Salfred
336204552Salfred/* ===========================================================================
337204552Salfred   Outputs a long in LSB order to the given file
338204552Salfred*/
339204552Salfredlocal void putU32 (s, x)
340204552Salfred    gz_stream *s;
341204552Salfred    uint32_t x;
342204552Salfred{
343204552Salfred    uint32_t xx;
344204552Salfred    off_t curoff = s->outoff;
345204552Salfred    int resid;
346204552Salfred
347204552Salfred#if BYTE_ORDER == BIG_ENDIAN
348204552Salfred    xx = bswap32(x);
349204552Salfred#else
350204552Salfred    xx = x;
351204552Salfred#endif
352204552Salfred    vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff,
353207415Salfred            UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
354204552Salfred            NOCRED, &resid, curthread);
355204552Salfred    s->outoff += sizeof(xx) - resid;
356204552Salfred}
357204552Salfred
358204552Salfred
359204552Salfred/* ===========================================================================
360204552Salfred     Flushes all pending output if necessary, closes the compressed file
361204552Salfred   and deallocates all the (de)compression state.
362204552Salfred*/
363204552Salfredint ZEXPORT gzclose (file)
364204552Salfred    gzFile file;
365204552Salfred{
366204552Salfred    int err;
367204552Salfred    gz_stream *s = (gz_stream*)file;
368204552Salfred
369204552Salfred    if (s == NULL) return Z_STREAM_ERROR;
370204552Salfred
371204552Salfred    if (s->mode == 'w') {
372204552Salfred        err = do_flush (file, Z_FINISH);
373204552Salfred        if (err != Z_OK) {
374204552Salfred            log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
375204552Salfred            return destroy((gz_stream*)file);
376204552Salfred        }
377204552Salfred#if 0
378204552Salfred	printf("gzclose: putting crc: %lld total: %lld\n",
379204552Salfred	    (long long)s->crc, (long long)s->stream.total_in);
380204552Salfred	printf("sizeof uLong = %d\n", (int)sizeof(uLong));
381204552Salfred#endif
382204552Salfred        putU32 (s, s->crc);
383204552Salfred        putU32 (s, (uint32_t) s->stream.total_in);
384204552Salfred    }
385204552Salfred    return destroy((gz_stream*)file);
386204552Salfred}
387204552Salfred
388204552Salfred/*
389204552Salfred * Space allocation and freeing routines for use by zlib routines when called
390204552Salfred * from gzip modules.
391204552Salfred */
392204552Salfredstatic void *
393204552Salfredgz_alloc(void *notused __unused, u_int items, u_int size)
394204552Salfred{
395204552Salfred    void *ptr;
396204552Salfred
397204552Salfred    MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
398204552Salfred    return ptr;
399204552Salfred}
400204552Salfred
401204552Salfredstatic void
402204552Salfredgz_free(void *opaque __unused, void *ptr)
403204552Salfred{
404204552Salfred    FREE(ptr, M_TEMP);
405204552Salfred}
406204552Salfred
407