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$ */
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;
100231949Skib    ssize_t 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
223204552Salfred    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
224204552Salfred
225204552Salfred    s->stream.next_in = (Bytef*)buf;
226204552Salfred    s->stream.avail_in = len;
227204552Salfred
228204552Salfred    curoff = s->outoff;
229204552Salfred    while (s->stream.avail_in != 0) {
230204552Salfred
231204552Salfred        if (s->stream.avail_out == 0) {
232204552Salfred
233204552Salfred            s->stream.next_out = s->outbuf;
234204552Salfred            error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE,
235207415Salfred                        curoff, UIO_SYSSPACE, IO_UNIT,
236204552Salfred                        curproc->p_ucred, NOCRED, &resid, curthread);
237204552Salfred            if (error) {
238204552Salfred                log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
239204552Salfred                curoff += Z_BUFSIZE - resid;
240204552Salfred                s->z_err = Z_ERRNO;
241204552Salfred                break;
242204552Salfred            }
243204552Salfred            curoff += Z_BUFSIZE;
244204552Salfred            s->stream.avail_out = Z_BUFSIZE;
245204552Salfred        }
246204552Salfred        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
247204552Salfred        if (s->z_err != Z_OK) {
248204552Salfred            log(LOG_ERR,
249204552Salfred                "gzwrite: deflate returned error %d\n", s->z_err);
250204552Salfred            break;
251204552Salfred        }
252204552Salfred    }
253204552Salfred
254204552Salfred    s->crc = ~crc32_raw(buf, len, ~s->crc);
255204552Salfred    s->outoff = curoff;
256204552Salfred
257204552Salfred    return (int)(len - s->stream.avail_in);
258204552Salfred}
259204552Salfred
260204552Salfred
261204552Salfred/* ===========================================================================
262204552Salfred     Flushes all pending output into the compressed file. The parameter
263204552Salfred   flush is as in the deflate() function.
264204552Salfred*/
265204552Salfredlocal int do_flush (file, flush)
266204552Salfred    gzFile file;
267204552Salfred    int flush;
268204552Salfred{
269204552Salfred    uInt len;
270204552Salfred    int done = 0;
271204552Salfred    gz_stream *s = (gz_stream*)file;
272204552Salfred    off_t curoff = s->outoff;
273204552Salfred    size_t resid;
274204552Salfred    int error;
275204552Salfred
276204552Salfred    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
277204552Salfred
278204552Salfred    if (s->stream.avail_in) {
279204552Salfred        log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
280204552Salfred    }
281204552Salfred
282204552Salfred    s->stream.avail_in = 0; /* should be zero already anyway */
283204552Salfred
284204552Salfred    for (;;) {
285204552Salfred        len = Z_BUFSIZE - s->stream.avail_out;
286204552Salfred
287204552Salfred        if (len != 0) {
288204552Salfred            error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff,
289207415Salfred                        UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
290204552Salfred                        NOCRED, &resid, curthread);
291204552Salfred	    if (error) {
292204552Salfred                s->z_err = Z_ERRNO;
293204552Salfred                s->outoff = curoff + len - resid;
294204552Salfred                return Z_ERRNO;
295204552Salfred            }
296204552Salfred            s->stream.next_out = s->outbuf;
297204552Salfred            s->stream.avail_out = Z_BUFSIZE;
298204552Salfred            curoff += len;
299204552Salfred        }
300204552Salfred        if (done) break;
301204552Salfred        s->z_err = deflate(&(s->stream), flush);
302204552Salfred
303204552Salfred	/* Ignore the second of two consecutive flushes: */
304204552Salfred	if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
305204552Salfred
306204552Salfred        /* deflate has finished flushing only when it hasn't used up
307204552Salfred         * all the available space in the output buffer:
308204552Salfred         */
309204552Salfred        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
310204552Salfred
311204552Salfred        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
312204552Salfred    }
313204552Salfred    s->outoff = curoff;
314204552Salfred
315204552Salfred    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
316204552Salfred}
317204552Salfred
318204552Salfredint ZEXPORT gzflush (file, flush)
319204552Salfred     gzFile file;
320204552Salfred     int flush;
321204552Salfred{
322204552Salfred    gz_stream *s = (gz_stream*)file;
323204552Salfred    int err = do_flush (file, flush);
324204552Salfred
325204552Salfred    if (err) return err;
326204552Salfred    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
327204552Salfred}
328204552Salfred
329204552Salfred
330204552Salfred/* ===========================================================================
331204552Salfred   Outputs a long in LSB order to the given file
332204552Salfred*/
333204552Salfredlocal void putU32 (s, x)
334204552Salfred    gz_stream *s;
335204552Salfred    uint32_t x;
336204552Salfred{
337204552Salfred    uint32_t xx;
338204552Salfred    off_t curoff = s->outoff;
339231949Skib    ssize_t resid;
340204552Salfred
341204552Salfred#if BYTE_ORDER == BIG_ENDIAN
342204552Salfred    xx = bswap32(x);
343204552Salfred#else
344204552Salfred    xx = x;
345204552Salfred#endif
346204552Salfred    vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff,
347207415Salfred            UIO_SYSSPACE, IO_UNIT, curproc->p_ucred,
348204552Salfred            NOCRED, &resid, curthread);
349204552Salfred    s->outoff += sizeof(xx) - resid;
350204552Salfred}
351204552Salfred
352204552Salfred
353204552Salfred/* ===========================================================================
354204552Salfred     Flushes all pending output if necessary, closes the compressed file
355204552Salfred   and deallocates all the (de)compression state.
356204552Salfred*/
357204552Salfredint ZEXPORT gzclose (file)
358204552Salfred    gzFile file;
359204552Salfred{
360204552Salfred    int err;
361204552Salfred    gz_stream *s = (gz_stream*)file;
362204552Salfred
363204552Salfred    if (s == NULL) return Z_STREAM_ERROR;
364204552Salfred
365204552Salfred    if (s->mode == 'w') {
366204552Salfred        err = do_flush (file, Z_FINISH);
367204552Salfred        if (err != Z_OK) {
368204552Salfred            log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
369204552Salfred            return destroy((gz_stream*)file);
370204552Salfred        }
371204552Salfred#if 0
372204552Salfred	printf("gzclose: putting crc: %lld total: %lld\n",
373204552Salfred	    (long long)s->crc, (long long)s->stream.total_in);
374204552Salfred	printf("sizeof uLong = %d\n", (int)sizeof(uLong));
375204552Salfred#endif
376204552Salfred        putU32 (s, s->crc);
377204552Salfred        putU32 (s, (uint32_t) s->stream.total_in);
378204552Salfred    }
379204552Salfred    return destroy((gz_stream*)file);
380204552Salfred}
381204552Salfred
382204552Salfred/*
383204552Salfred * Space allocation and freeing routines for use by zlib routines when called
384204552Salfred * from gzip modules.
385204552Salfred */
386204552Salfredstatic void *
387204552Salfredgz_alloc(void *notused __unused, u_int items, u_int size)
388204552Salfred{
389204552Salfred    void *ptr;
390204552Salfred
391204552Salfred    MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
392204552Salfred    return ptr;
393204552Salfred}
394204552Salfred
395204552Salfredstatic void
396204552Salfredgz_free(void *opaque __unused, void *ptr)
397204552Salfred{
398204552Salfred    FREE(ptr, M_TEMP);
399204552Salfred}
400204552Salfred
401