FreeBSD kernel kern code
kern_gzio.c
Go to the documentation of this file.
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 /* @(#) $BSDSUniX$ */
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 
Byte * inbuf
Definition: kern_gzio.c:60
struct gz_stream gz_stream
struct buf * buf
Definition: vfs_bio.c:97
static void gz_free(void *opaque __unused, void *ptr)
Definition: kern_gzio.c:402
char * path
int vn_rdwr_inchunks(enum uio_rw rw, struct vnode *vp, void *base, size_t len, off_t offset, enum uio_seg segflg, int ioflg, struct ucred *active_cred, struct ucred *file_cred, size_t *aresid, struct thread *td)
Definition: vfs_vnops.c:474
int snprintf(char *str, size_t size, const char *format,...)
Definition: subr_prf.c:509
int mode
char * msg
Definition: kern_gzio.c:63
local int destroy(gz_stream *s)
Definition: kern_gzio.c:185
#define TRYFREE(p)
Definition: kern_gzio.c:43
int z_err
Definition: kern_gzio.c:57
#define Z_BUFSIZE
Definition: kern_gzio.c:35
int transparent
Definition: kern_gzio.c:65
long startpos
Definition: kern_gzio.c:67
local int do_flush OF((gzFile file, int flush))
int z_eof
Definition: kern_gzio.c:58
int ZEXPORT gzwrite(gzFile file, const voidp buf, unsigned len)
Definition: kern_gzio.c:213
void * opaque
Definition: linker_if.m:63
gzFile gz_open(char *path, const char *mode, struct vnode *vp) const
Definition: kern_gzio.c:88
off_t outoff
Definition: kern_gzio.c:68
char mode
Definition: kern_gzio.c:66
int flags
Definition: kern_gzio.c:69
z_stream stream
Definition: kern_gzio.c:56
int vn_rdwr(enum uio_rw rw, struct vnode *vp, void *base, int len, off_t offset, enum uio_seg segflg, int ioflg, struct ucred *active_cred, struct ucred *file_cred, ssize_t *aresid, struct thread *td)
Definition: vfs_vnops.c:379
void log(int level, const char *fmt,...)
Definition: subr_prf.c:289
#define ALLOC(size)
Definition: kern_gzio.c:42
int printf(const char *fmt,...)
Definition: subr_prf.c:367
uLong crc
Definition: kern_gzio.c:62
static void * gz_alloc(void *notused __unused, u_int items, u_int size)
Definition: kern_gzio.c:393
local void putU32(gz_stream *s, uint32_t x)
Definition: kern_gzio.c:339
local int do_flush(gzFile file, int flush)
Definition: kern_gzio.c:268
int ZEXPORT gzflush(gzFile file, int flush)
Definition: kern_gzio.c:324
int ZEXPORT gzclose(gzFile file)
Definition: kern_gzio.c:363
static int gz_magic[2]
Definition: kern_gzio.c:45
#define GZ_HEADER_LEN
Definition: kern_gzio.c:29
struct vnode * file
Definition: kern_gzio.c:59
Byte * outbuf
Definition: kern_gzio.c:61
char * path
Definition: kern_gzio.c:64
const struct cf_level * level
Definition: cpufreq_if.m:45