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: stable/9/sys/kern/kern_gzio.c 233353 2012-03-23 11:26: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 
45 static 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 
55 typedef 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 
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));
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 */
gz_open(path,mode,vp)88 gzFile 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  */
destroy(s)185 local 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 */
gzwrite(file,buf,len)213 int 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 */
do_flush(file,flush)268 local 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 
gzflush(file,flush)324 int 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 */
putU32(s,x)339 local 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 */
gzclose(file)363 int 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  */
392 static void *
gz_alloc(void * notused __unused,u_int items,u_int size)393 gz_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 
401 static void
gz_free(void * opaque __unused,void * ptr)402 gz_free(void *opaque __unused, void *ptr)
403 {
404     FREE(ptr, M_TEMP);
405 }
406 
407