FreeBSD/Linux Kernel Cross Reference
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: releng/9.2/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 */
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 >= '' && *p <= '9') {
135 level = *p - '';
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
Cache object: 541e2286790d1c9be783591243a11da4
|