]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/kern/kern_gzio.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / kern / kern_gzio.c
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$ */
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 */
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  */
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 */
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
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 */
265 local 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
318 int 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 */
333 local 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 */
357 int 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  */
386 static void *
387 gz_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                                      
395 static void
396 gz_free(void *opaque __unused, void *ptr)
397 {
398     FREE(ptr, M_TEMP);
399 }
400