]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/kern/kern_gzio.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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     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 */
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
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 */
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 */
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 *
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
402 gz_free(void *opaque __unused, void *ptr)
403 {
404     FREE(ptr, M_TEMP);
405 }
406