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/10.3/sys/kern/kern_gzio.c 241896 2012-10-22 17:50: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 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 223 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; 224 225 s->stream.next_in = (Bytef*)buf; 226 s->stream.avail_in = len; 227 228 curoff = s->outoff; 229 while (s->stream.avail_in != 0) { 230 231 if (s->stream.avail_out == 0) { 232 233 s->stream.next_out = s->outbuf; 234 error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, Z_BUFSIZE, 235 curoff, UIO_SYSSPACE, IO_UNIT, 236 curproc->p_ucred, NOCRED, &resid, curthread); 237 if (error) { 238 log(LOG_ERR, "gzwrite: vn_rdwr return %d\n", error); 239 curoff += Z_BUFSIZE - resid; 240 s->z_err = Z_ERRNO; 241 break; 242 } 243 curoff += Z_BUFSIZE; 244 s->stream.avail_out = Z_BUFSIZE; 245 } 246 s->z_err = deflate(&(s->stream), Z_NO_FLUSH); 247 if (s->z_err != Z_OK) { 248 log(LOG_ERR, 249 "gzwrite: deflate returned error %d\n", s->z_err); 250 break; 251 } 252 } 253 254 s->crc = ~crc32_raw(buf, len, ~s->crc); 255 s->outoff = curoff; 256 257 return (int)(len - s->stream.avail_in); 258} 259 260 261/* =========================================================================== 262 Flushes all pending output into the compressed file. The parameter 263 flush is as in the deflate() function. 264*/ 265local int do_flush (file, flush) 266 gzFile file; 267 int flush; 268{ 269 uInt len; 270 int done = 0; 271 gz_stream *s = (gz_stream*)file; 272 off_t curoff = s->outoff; 273 size_t resid; 274 int error; 275 276 if (s == NULL || s->mode != 'w') return Z_STREAM_ERROR; 277 278 if (s->stream.avail_in) { 279 log(LOG_WARNING, "do_flush: avail_in non-zero on entry\n"); 280 } 281 282 s->stream.avail_in = 0; /* should be zero already anyway */ 283 284 for (;;) { 285 len = Z_BUFSIZE - s->stream.avail_out; 286 287 if (len != 0) { 288 error = vn_rdwr_inchunks(UIO_WRITE, s->file, s->outbuf, len, curoff, 289 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred, 290 NOCRED, &resid, curthread); 291 if (error) { 292 s->z_err = Z_ERRNO; 293 s->outoff = curoff + len - resid; 294 return Z_ERRNO; 295 } 296 s->stream.next_out = s->outbuf; 297 s->stream.avail_out = Z_BUFSIZE; 298 curoff += len; 299 } 300 if (done) break; 301 s->z_err = deflate(&(s->stream), flush); 302 303 /* Ignore the second of two consecutive flushes: */ 304 if (len == 0 && s->z_err == Z_BUF_ERROR) s->z_err = Z_OK; 305 306 /* deflate has finished flushing only when it hasn't used up 307 * all the available space in the output buffer: 308 */ 309 done = (s->stream.avail_out != 0 || s->z_err == Z_STREAM_END); 310 311 if (s->z_err != Z_OK && s->z_err != Z_STREAM_END) break; 312 } 313 s->outoff = curoff; 314 315 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; 316} 317 318int ZEXPORT gzflush (file, flush) 319 gzFile file; 320 int flush; 321{ 322 gz_stream *s = (gz_stream*)file; 323 int err = do_flush (file, flush); 324 325 if (err) return err; 326 return s->z_err == Z_STREAM_END ? Z_OK : s->z_err; 327} 328 329 330/* =========================================================================== 331 Outputs a long in LSB order to the given file 332*/ 333local void putU32 (s, x) 334 gz_stream *s; 335 uint32_t x; 336{ 337 uint32_t xx; 338 off_t curoff = s->outoff; 339 ssize_t resid; 340 341#if BYTE_ORDER == BIG_ENDIAN 342 xx = bswap32(x); 343#else 344 xx = x; 345#endif 346 vn_rdwr(UIO_WRITE, s->file, (caddr_t)&xx, sizeof(xx), curoff, 347 UIO_SYSSPACE, IO_UNIT, curproc->p_ucred, 348 NOCRED, &resid, curthread); 349 s->outoff += sizeof(xx) - resid; 350} 351 352 353/* =========================================================================== 354 Flushes all pending output if necessary, closes the compressed file 355 and deallocates all the (de)compression state. 356*/ 357int ZEXPORT gzclose (file) 358 gzFile file; 359{ 360 int err; 361 gz_stream *s = (gz_stream*)file; 362 363 if (s == NULL) return Z_STREAM_ERROR; 364 365 if (s->mode == 'w') { 366 err = do_flush (file, Z_FINISH); 367 if (err != Z_OK) { 368 log(LOG_ERR, "gzclose: do_flush failed (err %d)\n", err); 369 return destroy((gz_stream*)file); 370 } 371#if 0 372 printf("gzclose: putting crc: %lld total: %lld\n", 373 (long long)s->crc, (long long)s->stream.total_in); 374 printf("sizeof uLong = %d\n", (int)sizeof(uLong)); 375#endif 376 putU32 (s, s->crc); 377 putU32 (s, (uint32_t) s->stream.total_in); 378 } 379 return destroy((gz_stream*)file); 380} 381 382/* 383 * Space allocation and freeing routines for use by zlib routines when called 384 * from gzip modules. 385 */ 386static void * 387gz_alloc(void *notused __unused, u_int items, u_int size) 388{ 389 void *ptr; 390 391 MALLOC(ptr, void *, items * size, M_TEMP, M_NOWAIT | M_ZERO); 392 return ptr; 393} 394 395static void 396gz_free(void *opaque __unused, void *ptr) 397{ 398 FREE(ptr, M_TEMP); 399} 400 401