kern_gzio.c revision 231949
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: head/sys/kern/kern_gzio.c 231949 2012-02-21 01:05:12Z 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 45static 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 55typedef 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 73local int do_flush OF((gzFile file, int flush)); 74local int destroy OF((gz_stream *s)); 75local void putU32 OF((gz_stream *file, uint32_t x)); 76local void *gz_alloc OF((void *notused, u_int items, u_int size)); 77local 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*/ 88gzFile 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 */ 185local 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*/ 213int 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*/ 268local 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 324int 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*/ 339local 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*/ 363int 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 */ 392static void * 393gz_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 401static void 402gz_free(void *opaque __unused, void *ptr) 403{ 404 FREE(ptr, M_TEMP); 405} 406 407