kern_gzio.c revision 207415
1204552Salfred/* 2204552Salfred * $Id: kern_gzio.c,v 1.6 2008-10-18 22:54:45 lbazinet Exp $ 3204552Salfred * 4204552Salfred * core_gzip.c -- gzip routines used in compressing user process cores 5204552Salfred * 6204552Salfred * This file is derived from src/lib/libz/gzio.c in FreeBSD. 7204552Salfred */ 8204552Salfred 9204552Salfred/* gzio.c -- IO on .gz files 10204552Salfred * Copyright (C) 1995-1998 Jean-loup Gailly. 11204552Salfred * For conditions of distribution and use, see copyright notice in zlib.h 12204552Salfred * 13204552Salfred */ 14204552Salfred 15204552Salfred/* @(#) $FreeBSD: head/sys/kern/kern_gzio.c 207415 2010-04-30 03:10:53Z alfred $ */ 16204552Salfred 17204552Salfred#include <sys/param.h> 18204552Salfred#include <sys/proc.h> 19204552Salfred#include <sys/malloc.h> 20204552Salfred#include <sys/vnode.h> 21204552Salfred#include <sys/syslog.h> 22204552Salfred#include <sys/endian.h> 23204552Salfred#include <net/zutil.h> 24204552Salfred#include <sys/libkern.h> 25204552Salfred 26204552Salfred#include <sys/vnode.h> 27204552Salfred#include <sys/mount.h> 28204552Salfred 29204552Salfred#define GZ_HEADER_LEN 10 30204552Salfred 31204552Salfred#ifndef Z_BUFSIZE 32204552Salfred# ifdef MAXSEG_64K 33204552Salfred# define Z_BUFSIZE 4096 /* minimize memory usage for 16-bit DOS */ 34204552Salfred# else 35204552Salfred# define Z_BUFSIZE 16384 36204552Salfred# endif 37204552Salfred#endif 38204552Salfred#ifndef Z_PRINTF_BUFSIZE 39204552Salfred# define Z_PRINTF_BUFSIZE 4096 40204552Salfred#endif 41204552Salfred 42204552Salfred#define ALLOC(size) malloc(size, M_TEMP, M_WAITOK | M_ZERO) 43204552Salfred#define TRYFREE(p) {if (p) free(p, M_TEMP);} 44204552Salfred 45204552Salfredstatic int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 46204552Salfred 47204552Salfred/* gzip flag byte */ 48204552Salfred#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 49204552Salfred#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 50204552Salfred#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 51204552Salfred#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 52204552Salfred#define COMMENT 0x10 /* bit 4 set: file comment present */ 53204552Salfred#define RESERVED 0xE0 /* bits 5..7: reserved */ 54204552Salfred 55204552Salfredtypedef struct gz_stream { 56204552Salfred z_stream stream; 57204552Salfred int z_err; /* error code for last stream operation */ 58204552Salfred int z_eof; /* set if end of input file */ 59204552Salfred struct vnode *file; /* vnode pointer of .gz file */ 60204552Salfred Byte *inbuf; /* input buffer */ 61204552Salfred Byte *outbuf; /* output buffer */ 62204552Salfred uLong crc; /* crc32 of uncompressed data */ 63204552Salfred char *msg; /* error message */ 64204552Salfred char *path; /* path name for debugging only */ 65204552Salfred int transparent; /* 1 if input file is not a .gz file */ 66204552Salfred char mode; /* 'w' or 'r' */ 67204552Salfred long startpos; /* start of compressed data in file (header skipped) */ 68204552Salfred off_t outoff; /* current offset in output file */ 69204552Salfred int flags; 70204552Salfred} gz_stream; 71204552Salfred 72204552Salfred 73204552Salfredlocal int do_flush OF((gzFile file, int flush)); 74204552Salfredlocal int destroy OF((gz_stream *s)); 75204552Salfredlocal void putU32 OF((gz_stream *file, uint32_t x)); 76204552Salfredlocal void *gz_alloc OF((void *notused, u_int items, u_int size)); 77204552Salfredlocal void gz_free OF((void *notused, void *ptr)); 78204552Salfred 79204552Salfred/* =========================================================================== 80204552Salfred Opens a gzip (.gz) file for reading or writing. The mode parameter 81204552Salfred is as in fopen ("rb" or "wb"). The file is given either by file descriptor 82204552Salfred or path name (if fd == -1). 83204552Salfred gz_open return NULL if the file could not be opened or if there was 84204552Salfred insufficient memory to allocate the (de)compression state; errno 85204552Salfred can be checked to distinguish the two cases (if errno is zero, the 86204552Salfred zlib error is Z_MEM_ERROR). 87204552Salfred*/ 88204552SalfredgzFile gz_open (path, mode, vp) 89204552Salfred const char *path; 90204552Salfred const char *mode; 91204552Salfred struct vnode *vp; 92204552Salfred{ 93204552Salfred int err; 94204552Salfred int level = Z_DEFAULT_COMPRESSION; /* compression level */ 95204552Salfred int strategy = Z_DEFAULT_STRATEGY; /* compression strategy */ 96204552Salfred const char *p = mode; 97204552Salfred gz_stream *s; 98204552Salfred char fmode[80]; /* copy of mode, without the compression level */ 99204552Salfred char *m = fmode; 100204552Salfred int resid; 101204552Salfred int error; 102204552Salfred char buf[GZ_HEADER_LEN + 1]; 103204552Salfred 104204552Salfred if (!path || !mode) return Z_NULL; 105204552Salfred 106204552Salfred s = (gz_stream *)ALLOC(sizeof(gz_stream)); 107204552Salfred if (!s) return Z_NULL; 108204552Salfred 109204552Salfred s->stream.zalloc = (alloc_func)gz_alloc; 110204552Salfred s->stream.zfree = (free_func)gz_free; 111204552Salfred s->stream.opaque = (voidpf)0; 112204552Salfred s->stream.next_in = s->inbuf = Z_NULL; 113204552Salfred s->stream.next_out = s->outbuf = Z_NULL; 114204552Salfred s->stream.avail_in = s->stream.avail_out = 0; 115204552Salfred s->file = NULL; 116204552Salfred s->z_err = Z_OK; 117204552Salfred s->z_eof = 0; 118204552Salfred s->crc = 0; 119204552Salfred s->msg = NULL; 120204552Salfred s->transparent = 0; 121204552Salfred s->outoff = 0; 122204552Salfred s->flags = 0; 123204552Salfred 124204552Salfred s->path = (char*)ALLOC(strlen(path)+1); 125204552Salfred if (s->path == NULL) { 126204552Salfred return destroy(s), (gzFile)Z_NULL; 127204552Salfred } 128204552Salfred strcpy(s->path, path); /* do this early for debugging */ 129204552Salfred 130204552Salfred s->mode = '\0'; 131204552Salfred do { 132204552Salfred if (*p == 'r') s->mode = 'r'; 133204552Salfred if (*p == 'w' || *p == 'a') s->mode = 'w'; 134204552Salfred if (*p >= '0' && *p <= '9') { 135204552Salfred level = *p - '0'; 136204552Salfred } else if (*p == 'f') { 137204552Salfred strategy = Z_FILTERED; 138204552Salfred } else if (*p == 'h') { 139204552Salfred strategy = Z_HUFFMAN_ONLY; 140204552Salfred } else { 141204552Salfred *m++ = *p; /* copy the mode */ 142204552Salfred } 143204552Salfred } while (*p++ && m != fmode + sizeof(fmode)); 144204552Salfred 145204552Salfred if (s->mode != 'w') { 146204552Salfred log(LOG_ERR, "gz_open: mode is not w (%c)\n", s->mode); 147204552Salfred return destroy(s), (gzFile)Z_NULL; 148204552Salfred } 149204552Salfred 150204552Salfred err = deflateInit2(&(s->stream), level, 151204552Salfred Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, strategy); 152204552Salfred /* windowBits is passed < 0 to suppress zlib header */ 153204552Salfred 154204552Salfred s->stream.next_out = s->outbuf = (Byte*)ALLOC(Z_BUFSIZE); 155204552Salfred if (err != Z_OK || s->outbuf == Z_NULL) { 156204552Salfred return destroy(s), (gzFile)Z_NULL; 157204552Salfred } 158204552Salfred 159204552Salfred s->stream.avail_out = Z_BUFSIZE; 160204552Salfred s->file = vp; 161204552Salfred 162204552Salfred /* Write a very simple .gz header: 163204552Salfred */ 164204552Salfred snprintf(buf, sizeof(buf), "%c%c%c%c%c%c%c%c%c%c", gz_magic[0], 165204552Salfred gz_magic[1], Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/, 166204552Salfred 0 /*xflags*/, OS_CODE); 167204552Salfred 168204552Salfred if ((error = vn_rdwr(UIO_WRITE, s->file, buf, GZ_HEADER_LEN, s->outoff, 169207415Salfred UIO_SYSSPACE, IO_UNIT, curproc->p_ucred, 170204552Salfred NOCRED, &resid, curthread))) { 171204552Salfred s->outoff += GZ_HEADER_LEN - resid; 172204552Salfred return destroy(s), (gzFile)Z_NULL; 173204552Salfred } 174204552Salfred s->outoff += GZ_HEADER_LEN; 175204552Salfred s->startpos = 10L; 176204552Salfred 177204552Salfred return (gzFile)s; 178204552Salfred} 179204552Salfred 180204552Salfred 181204552Salfred /* =========================================================================== 182204552Salfred * Cleanup then free the given gz_stream. Return a zlib error code. 183204552Salfred Try freeing in the reverse order of allocations. 184204552Salfred */ 185204552Salfredlocal int destroy (s) 186204552Salfred gz_stream *s; 187204552Salfred{ 188204552Salfred int err = Z_OK; 189204552Salfred 190204552Salfred if (!s) return Z_STREAM_ERROR; 191204552Salfred 192204552Salfred TRYFREE(s->msg); 193204552Salfred 194204552Salfred if (s->stream.state != NULL) { 195204552Salfred if (s->mode == 'w') { 196204552Salfred err = deflateEnd(&(s->stream)); 197204552Salfred } 198204552Salfred } 199204552Salfred if (s->z_err < 0) err = s->z_err; 200204552Salfred 201204552Salfred TRYFREE(s->inbuf); 202204552Salfred TRYFREE(s->outbuf); 203204552Salfred TRYFREE(s->path); 204204552Salfred TRYFREE(s); 205204552Salfred return err; 206204552Salfred} 207204552Salfred 208204552Salfred 209204552Salfred/* =========================================================================== 210204552Salfred Writes the given number of uncompressed bytes into the compressed file. 211204552Salfred gzwrite returns the number of bytes actually written (0 in case of error). 212204552Salfred*/ 213204552Salfredint ZEXPORT gzwrite (file, buf, len) 214204552Salfred gzFile file; 215204552Salfred const voidp buf; 216204552Salfred unsigned len; 217204552Salfred{ 218204552Salfred gz_stream *s = (gz_stream*)file; 219204552Salfred off_t curoff; 220204552Salfred size_t resid; 221204552Salfred int error; 222204552Salfred int vfslocked; 223204552Salfred 224204552Salfred if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; 225204552Salfred 226204552Salfred s->stream.next_in = (Bytef*)buf; 227204552Salfred s->stream.avail_in = len; 228204552Salfred 229204552Salfred curoff = s->outoff; 230204552Salfred while (s->stream.avail_in != 0) { 231204552Salfred 232204552Salfred if (s->stream.avail_out == 0) { 233204552Salfred 234204552Salfred s->stream.next_out = s->outbuf; 235204552Salfred vfslocked = VFS_LOCK_GIANT(s->file->v_mount); 236204552Salfred error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE, 237207415Salfred curoff, UIO_SYSSPACE, IO_UNIT, 238204552Salfred curproc->p_ucred, NOCRED, &resid, curthread); 239204552Salfred VFS_UNLOCK_GIANT(vfslocked); 240204552Salfred if (error) { 241204552Salfred log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error); 242204552Salfred curoff += Z_BUFSIZE - resid; 243204552Salfred s->z_err = Z_ERRNO; 244204552Salfred break; 245204552Salfred } 246204552Salfred curoff += Z_BUFSIZE; 247204552Salfred s->stream.avail_out = Z_BUFSIZE; 248204552Salfred } 249204552Salfred s->z_err = deflate(&(s->stream), Z_NO_FLUSH); 250204552Salfred if (s->z_err != Z_OK) { 251204552Salfred log(LOG_ERR, 252204552Salfred "gzwrite: deflate returned error %d\n", s->z_err); 253204552Salfred break; 254204552Salfred } 255204552Salfred } 256204552Salfred 257204552Salfred s->crc = ~crc32_raw(buf, len, ~s->crc); 258204552Salfred s->outoff = curoff; 259204552Salfred 260204552Salfred return (int)(len - s->stream.avail_in); 261204552Salfred} 262204552Salfred 263204552Salfred 264204552Salfred/* =========================================================================== 265204552Salfred Flushes all pending output into the compressed file. The parameter 266204552Salfred flush is as in the deflate() function. 267204552Salfred*/ 268204552Salfredlocal int do_flush (file, flush) 269204552Salfred gzFile file; 270204552Salfred int flush; 271204552Salfred{ 272204552Salfred uInt len; 273204552Salfred int done = 0; 274204552Salfred gz_stream *s = (gz_stream*)file; 275204552Salfred off_t curoff = s->outoff; 276204552Salfred size_t resid; 277204552Salfred int vfslocked = 0; 278204552Salfred int error; 279204552Salfred 280204552Salfred if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; 281204552Salfred 282204552Salfred if (s->stream.avail_in) { 283204552Salfred log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n"); 284204552Salfred } 285204552Salfred 286204552Salfred s->stream.avail_in = 0; /* should be zero already anyway */ 287204552Salfred 288204552Salfred for (;;) { 289204552Salfred len = Z_BUFSIZE - s->stream.avail_out; 290204552Salfred 291204552Salfred if (len != 0) { 292204552Salfred vfslocked = VFS_LOCK_GIANT(s->file->v_mount); 293204552Salfred error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff, 294207415Salfred UIO_SYSSPACE, IO_UNIT, curproc->p_ucred, 295204552Salfred NOCRED, &resid, curthread); 296204552Salfred VFS_UNLOCK_GIANT(vfslocked); 297204552Salfred if (error) { 298204552Salfred s->z_err = Z_ERRNO; 299204552Salfred s->outoff = curoff + len - resid; 300204552Salfred return Z_ERRNO; 301204552Salfred } 302204552Salfred s->stream.next_out = s->outbuf; 303204552Salfred s->stream.avail_out = Z_BUFSIZE; 304204552Salfred curoff += len; 305204552Salfred } 306204552Salfred if (done) break; 307204552Salfred s->z_err = deflate(&(s->stream), flush); 308204552Salfred 309204552Salfred /* Ignore the second of two consecutive flushes: */ 310204552Salfred if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; 311204552Salfred 312204552Salfred /* deflate has finished flushing only when it hasn't used up 313204552Salfred * all the available space in the output buffer: 314204552Salfred */ 315204552Salfred done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); 316204552Salfred 317204552Salfred if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; 318204552Salfred } 319204552Salfred s->outoff = curoff; 320204552Salfred 321204552Salfred return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; 322204552Salfred} 323204552Salfred 324204552Salfredint ZEXPORT gzflush (file, flush) 325204552Salfred gzFile file; 326204552Salfred int flush; 327204552Salfred{ 328204552Salfred gz_stream *s = (gz_stream*)file; 329204552Salfred int err = do_flush (file, flush); 330204552Salfred 331204552Salfred if (err) return err; 332204552Salfred return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; 333204552Salfred} 334204552Salfred 335204552Salfred 336204552Salfred/* =========================================================================== 337204552Salfred Outputs a long in LSB order to the given file 338204552Salfred*/ 339204552Salfredlocal void putU32 (s, x) 340204552Salfred gz_stream *s; 341204552Salfred uint32_t x; 342204552Salfred{ 343204552Salfred uint32_t xx; 344204552Salfred off_t curoff = s->outoff; 345204552Salfred int resid; 346204552Salfred 347204552Salfred#if BYTE_ORDER == BIG_ENDIAN 348204552Salfred xx = bswap32(x); 349204552Salfred#else 350204552Salfred xx = x; 351204552Salfred#endif 352204552Salfred vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff, 353207415Salfred UIO_SYSSPACE, IO_UNIT, curproc->p_ucred, 354204552Salfred NOCRED, &resid, curthread); 355204552Salfred s->outoff += sizeof(xx) - resid; 356204552Salfred} 357204552Salfred 358204552Salfred 359204552Salfred/* =========================================================================== 360204552Salfred Flushes all pending output if necessary, closes the compressed file 361204552Salfred and deallocates all the (de)compression state. 362204552Salfred*/ 363204552Salfredint ZEXPORT gzclose (file) 364204552Salfred gzFile file; 365204552Salfred{ 366204552Salfred int err; 367204552Salfred gz_stream *s = (gz_stream*)file; 368204552Salfred 369204552Salfred if (s == NULL) return Z_STREAM_ERROR; 370204552Salfred 371204552Salfred if (s->mode == 'w') { 372204552Salfred err = do_flush (file, Z_FINISH); 373204552Salfred if (err != Z_OK) { 374204552Salfred log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err); 375204552Salfred return destroy((gz_stream*)file); 376204552Salfred } 377204552Salfred#if 0 378204552Salfred printf("gzclose: putting crc: %lld total: %lld\n", 379204552Salfred (long long)s->crc, (long long)s->stream.total_in); 380204552Salfred printf("sizeof uLong = %d\n", (int)sizeof(uLong)); 381204552Salfred#endif 382204552Salfred putU32 (s, s->crc); 383204552Salfred putU32 (s, (uint32_t) s->stream.total_in); 384204552Salfred } 385204552Salfred return destroy((gz_stream*)file); 386204552Salfred} 387204552Salfred 388204552Salfred/* 389204552Salfred * Space allocation and freeing routines for use by zlib routines when called 390204552Salfred * from gzip modules. 391204552Salfred */ 392204552Salfredstatic void * 393204552Salfredgz_alloc(void *notused __unused, u_int items, u_int size) 394204552Salfred{ 395204552Salfred void *ptr; 396204552Salfred 397204552Salfred MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO); 398204552Salfred return ptr; 399204552Salfred} 400204552Salfred 401204552Salfredstatic void 402204552Salfredgz_free(void *opaque __unused, void *ptr) 403204552Salfred{ 404204552Salfred FREE(ptr, M_TEMP); 405204552Salfred} 406204552Salfred 407