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: releng/10.3/sys/kern/kern_gzio.c 241896 2012-10-22 17:50:54Z 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
223    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
224
225    s->stream.next_in = (Bytef*)buf;
226    s->stream.avail_in = len;
227
228    curoff = s->outoff;
229    while (s->stream.avail_in != 0) {
230
231        if (s->stream.avail_out == 0) {
232
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);
237            if (error) {
238                log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error);
239                curoff += Z_BUFSIZE - resid;
240                s->z_err = Z_ERRNO;
241                break;
242            }
243            curoff += Z_BUFSIZE;
244            s->stream.avail_out = Z_BUFSIZE;
245        }
246        s->z_err = deflate(&(s->stream), Z_NO_FLUSH);
247        if (s->z_err != Z_OK) {
248            log(LOG_ERR,
249                "gzwrite: deflate returned error %d\n", s->z_err);
250            break;
251        }
252    }
253
254    s->crc = ~crc32_raw(buf, len, ~s->crc);
255    s->outoff = curoff;
256
257    return (int)(len - s->stream.avail_in);
258}
259
260
261/* ===========================================================================
262     Flushes all pending output into the compressed file. The parameter
263   flush is as in the deflate() function.
264*/
265local int do_flush (file, flush)
266    gzFile file;
267    int flush;
268{
269    uInt len;
270    int done = 0;
271    gz_stream *s = (gz_stream*)file;
272    off_t curoff = s->outoff;
273    size_t resid;
274    int error;
275
276    if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR;
277
278    if (s->stream.avail_in) {
279        log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n");
280    }
281
282    s->stream.avail_in = 0; /* should be zero already anyway */
283
284    for (;;) {
285        len = Z_BUFSIZE - s->stream.avail_out;
286
287        if (len != 0) {
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);
291	    if (error) {
292                s->z_err = Z_ERRNO;
293                s->outoff = curoff + len - resid;
294                return Z_ERRNO;
295            }
296            s->stream.next_out = s->outbuf;
297            s->stream.avail_out = Z_BUFSIZE;
298            curoff += len;
299        }
300        if (done) break;
301        s->z_err = deflate(&(s->stream), flush);
302
303	/* Ignore the second of two consecutive flushes: */
304	if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK;
305
306        /* deflate has finished flushing only when it hasn't used up
307         * all the available space in the output buffer:
308         */
309        done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END);
310
311        if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break;
312    }
313    s->outoff = curoff;
314
315    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
316}
317
318int ZEXPORT gzflush (file, flush)
319     gzFile file;
320     int flush;
321{
322    gz_stream *s = (gz_stream*)file;
323    int err = do_flush (file, flush);
324
325    if (err) return err;
326    return  s->z_err == Z_STREAM_END ? Z_OK : s->z_err;
327}
328
329
330/* ===========================================================================
331   Outputs a long in LSB order to the given file
332*/
333local void putU32 (s, x)
334    gz_stream *s;
335    uint32_t x;
336{
337    uint32_t xx;
338    off_t curoff = s->outoff;
339    ssize_t resid;
340
341#if BYTE_ORDER == BIG_ENDIAN
342    xx = bswap32(x);
343#else
344    xx = x;
345#endif
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;
350}
351
352
353/* ===========================================================================
354     Flushes all pending output if necessary, closes the compressed file
355   and deallocates all the (de)compression state.
356*/
357int ZEXPORT gzclose (file)
358    gzFile file;
359{
360    int err;
361    gz_stream *s = (gz_stream*)file;
362
363    if (s == NULL) return Z_STREAM_ERROR;
364
365    if (s->mode == 'w') {
366        err = do_flush (file, Z_FINISH);
367        if (err != Z_OK) {
368            log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err);
369            return destroy((gz_stream*)file);
370        }
371#if 0
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));
375#endif
376        putU32 (s, s->crc);
377        putU32 (s, (uint32_t) s->stream.total_in);
378    }
379    return destroy((gz_stream*)file);
380}
381
382/*
383 * Space allocation and freeing routines for use by zlib routines when called
384 * from gzip modules.
385 */
386static void *
387gz_alloc(void *notused __unused, u_int items, u_int size)
388{
389    void *ptr;
390
391    MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO);
392    return ptr;
393}
394
395static void
396gz_free(void *opaque __unused, void *ptr)
397{
398    FREE(ptr, M_TEMP);
399}
400
401