Annotation of src/usr.bin/compress/gzopen.c, Revision 1.34
1.34 ! tedu 1: /* $OpenBSD: gzopen.c,v 1.33 2016/09/03 11:41:10 tedu Exp $ */
1.1 mickey 2:
3: /*
4: * Copyright (c) 1997 Michael Shalayeff
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
1.5 mickey 16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1.1 mickey 18: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26: * SUCH DAMAGE.
27: *
28: */
29: /* this is partially derived from the zlib's gzio.c file, so the notice: */
30: /*
31: zlib.h -- interface of the 'zlib' general purpose compression library
32: version 1.0.4, Jul 24th, 1996.
33:
34: Copyright (C) 1995-1996 Jean-loup Gailly and Mark Adler
35:
36: This software is provided 'as-is', without any express or implied
37: warranty. In no event will the authors be held liable for any damages
38: arising from the use of this software.
39:
40: Permission is granted to anyone to use this software for any purpose,
41: including commercial applications, and to alter it and redistribute it
42: freely, subject to the following restrictions:
43:
44: 1. The origin of this software must not be misrepresented; you must not
45: claim that you wrote the original software. If you use this software
46: in a product, an acknowledgment in the product documentation would be
47: appreciated but is not required.
48: 2. Altered source versions must be plainly marked as such, and must not be
49: misrepresented as being the original software.
50: 3. This notice may not be removed or altered from any source distribution.
51:
52: Jean-loup Gailly Mark Adler
53: gzip@prep.ai.mit.edu madler@alumni.caltech.edu
54:
55:
56: The data format used by the zlib library is described by RFCs (Request for
57: Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
58: (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
59: */
1.5 mickey 60:
1.1 mickey 61: #include <sys/stat.h>
1.13 millert 62: #include <sys/uio.h>
1.1 mickey 63: #include <stdio.h>
64: #include <stdlib.h>
1.11 david 65: #include <string.h>
1.1 mickey 66: #include <errno.h>
67: #include <unistd.h>
1.28 deraadt 68: #include <limits.h>
1.1 mickey 69: #include <zlib.h>
70: #include "compress.h"
71:
72: /* gzip flag byte */
73: #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
74: #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
75: #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
76: #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
77: #define COMMENT 0x10 /* bit 4 set: file comment present */
78: #define RESERVED 0xE0 /* bits 5..7: reserved */
79:
80: #define DEF_MEM_LEVEL 8
81: #define OS_CODE 0x03 /* unix */
82:
83: typedef
84: struct gz_stream {
85: int z_fd; /* .gz file */
1.30 millert 86: int z_eof; /* set if end of input file */
1.1 mickey 87: z_stream z_stream; /* libz stream */
88: u_char z_buf[Z_BUFSIZE]; /* i/o buffer */
1.30 millert 89: char z_mode; /* 'w' or 'r' */
1.13 millert 90: u_int32_t z_time; /* timestamp (mtime) */
1.30 millert 91: u_int32_t z_crc; /* crc32 of uncompressed data */
1.13 millert 92: u_int32_t z_hlen; /* length of the gz header */
1.30 millert 93: u_int64_t z_total_in; /* # bytes in */
94: u_int64_t z_total_out; /* # bytes out */
1.1 mickey 95: } gz_stream;
96:
1.5 mickey 97: static const u_char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
1.1 mickey 98:
1.3 millert 99: static u_int32_t get_int32(gz_stream *);
1.13 millert 100: static int get_header(gz_stream *, char *, int);
1.3 millert 101: static int get_byte(gz_stream *);
1.1 mickey 102:
103: void *
1.33 tedu 104: gz_ropen(int fd, char *name, int gotmagic)
105: {
106: gz_stream *s;
107:
108: if (fd < 0)
109: return NULL;
110:
111: if ((s = calloc(1, sizeof(gz_stream))) == NULL)
112: return NULL;
113:
114: s->z_stream.zalloc = (alloc_func)0;
115: s->z_stream.zfree = (free_func)0;
116: s->z_stream.opaque = (voidpf)0;
117: s->z_stream.next_in = Z_NULL;
118: s->z_stream.next_out = Z_NULL;
119: s->z_stream.avail_in = s->z_stream.avail_out = 0;
120: s->z_fd = 0;
121: s->z_eof = 0;
122: s->z_time = 0;
123: s->z_hlen = 0;
124: s->z_total_in = 0;
125: s->z_total_out = 0;
126: s->z_crc = crc32(0L, Z_NULL, 0);
127: s->z_mode = 'r';
128:
129: if (inflateInit2(&(s->z_stream), -MAX_WBITS) != Z_OK) {
130: free (s);
131: return NULL;
1.1 mickey 132: }
1.33 tedu 133: s->z_stream.next_in = s->z_buf;
1.1 mickey 134: s->z_stream.avail_out = Z_BUFSIZE;
135:
136: errno = 0;
137: s->z_fd = fd;
138:
1.33 tedu 139: /* read the .gz header */
140: if (get_header(s, name, gotmagic) != 0) {
141: gz_close(s, NULL, NULL, NULL);
142: s = NULL;
1.1 mickey 143: }
144:
145: return s;
146: }
147:
148: static int
1.7 deraadt 149: get_byte(gz_stream *s)
1.1 mickey 150: {
151: if (s->z_eof)
152: return EOF;
1.5 mickey 153:
1.1 mickey 154: if (s->z_stream.avail_in == 0) {
155: errno = 0;
156: s->z_stream.avail_in = read(s->z_fd, s->z_buf, Z_BUFSIZE);
1.24 pedro 157: if ((int)s->z_stream.avail_in <= 0) {
1.1 mickey 158: s->z_eof = 1;
159: return EOF;
160: }
161: s->z_stream.next_in = s->z_buf;
162: }
163: s->z_stream.avail_in--;
164: return *s->z_stream.next_in++;
165: }
166:
1.5 mickey 167: static u_int32_t
1.7 deraadt 168: get_int32(gz_stream *s)
1.1 mickey 169: {
1.2 mpech 170: u_int32_t x;
1.1 mickey 171:
172: x = ((u_int32_t)(get_byte(s) & 0xff));
173: x |= ((u_int32_t)(get_byte(s) & 0xff))<<8;
174: x |= ((u_int32_t)(get_byte(s) & 0xff))<<16;
175: x |= ((u_int32_t)(get_byte(s) & 0xff))<<24;
176: return x;
177: }
178:
179: static int
1.13 millert 180: get_header(gz_stream *s, char *name, int gotmagic)
1.1 mickey 181: {
182: int method; /* method byte */
183: int flags; /* flags byte */
1.13 millert 184: char *ep;
1.1 mickey 185: uInt len;
186: int c;
187:
188: /* Check the gzip magic header */
1.12 millert 189: if (!gotmagic) {
190: for (len = 0; len < 2; len++) {
191: c = get_byte(s);
192: if (c != gz_magic[len]) {
193: errno = EFTYPE;
194: return -1;
195: }
1.1 mickey 196: }
197: }
198:
199: method = get_byte(s);
200: flags = get_byte(s);
201: if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
202: errno = EFTYPE;
203: return -1;
204: }
205:
1.13 millert 206: /* Stash timestamp (mtime) */
207: s->z_time = get_int32(s);
208:
209: /* Discard xflags and OS code */
210: (void)get_byte(s);
211: (void)get_byte(s);
1.1 mickey 212:
1.30 millert 213: s->z_hlen += 10; /* magic, method, flags, time, xflags, OS code */
1.1 mickey 214: if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
215: len = (uInt)get_byte(s);
216: len += ((uInt)get_byte(s))<<8;
1.13 millert 217: s->z_hlen += 2;
1.1 mickey 218: /* len is garbage if EOF but the loop below will quit anyway */
219: while (len-- != 0 && get_byte(s) != EOF)
1.13 millert 220: s->z_hlen++;
1.1 mickey 221: }
222:
1.13 millert 223: if ((flags & ORIG_NAME) != 0) { /* read/save the original file name */
224: if ((ep = name) != NULL)
1.28 deraadt 225: ep += PATH_MAX - 1;
1.13 millert 226: while ((c = get_byte(s)) != EOF) {
227: s->z_hlen++;
228: if (c == '\0')
229: break;
230: if (name < ep)
231: *name++ = c;
232: }
233: if (name != NULL)
234: *name = '\0';
1.1 mickey 235: }
236:
237: if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
1.13 millert 238: while ((c = get_byte(s)) != EOF) {
239: s->z_hlen++;
240: if (c == '\0')
241: break;
242: }
1.1 mickey 243: }
244:
245: if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
1.14 mickey 246: (void)get_byte(s);
247: (void)get_byte(s);
1.13 millert 248: s->z_hlen += 2;
1.1 mickey 249: }
250:
251: if (s->z_eof) {
252: errno = EFTYPE;
253: return -1;
254: }
255:
256: return 0;
257: }
258:
259: int
1.7 deraadt 260: gz_read(void *cookie, char *buf, int len)
1.1 mickey 261: {
1.2 mpech 262: gz_stream *s = (gz_stream*)cookie;
1.1 mickey 263: u_char *start = buf; /* starting point for crc computation */
1.17 millert 264: int error = Z_OK;
1.1 mickey 265:
266: s->z_stream.next_out = buf;
267: s->z_stream.avail_out = len;
268:
1.17 millert 269: while (error == Z_OK && !s->z_eof && s->z_stream.avail_out != 0) {
1.1 mickey 270:
271: if (s->z_stream.avail_in == 0) {
272:
273: errno = 0;
1.24 pedro 274: s->z_stream.avail_in = read(s->z_fd, s->z_buf,
275: Z_BUFSIZE);
276: if ((int)s->z_stream.avail_in <= 0)
1.1 mickey 277: s->z_eof = 1;
278: s->z_stream.next_in = s->z_buf;
279: }
280:
1.17 millert 281: error = inflate(&(s->z_stream), Z_NO_FLUSH);
1.25 mpf 282:
283: if (error == Z_DATA_ERROR) {
284: errno = EINVAL;
1.30 millert 285: goto bad;
1.25 mpf 286: }
287: if (error == Z_BUF_ERROR) {
288: errno = EIO;
1.30 millert 289: goto bad;
1.25 mpf 290: }
1.17 millert 291: if (error == Z_STREAM_END) {
1.1 mickey 292: /* Check CRC and original size */
293: s->z_crc = crc32(s->z_crc, start,
1.7 deraadt 294: (uInt)(s->z_stream.next_out - start));
1.1 mickey 295: start = s->z_stream.next_out;
296:
1.9 millert 297: if (get_int32(s) != s->z_crc) {
298: errno = EINVAL;
1.30 millert 299: goto bad;
1.9 millert 300: }
1.18 henning 301: if (get_int32(s) != (u_int32_t)s->z_stream.total_out) {
1.7 deraadt 302: errno = EIO;
1.1 mickey 303: return -1;
304: }
1.13 millert 305: s->z_hlen += 2 * sizeof(int32_t);
1.30 millert 306:
307: /* Add byte counts from the finished stream. */
308: s->z_total_in += s->z_stream.total_in;
309: s->z_total_out += s->z_stream.total_out;
310:
1.16 millert 311: /* Check for the existence of an appended file. */
312: if (get_header(s, NULL, 0) != 0) {
313: s->z_eof = 1;
314: break;
315: }
316: inflateReset(&(s->z_stream));
317: s->z_crc = crc32(0L, Z_NULL, 0);
1.17 millert 318: error = Z_OK;
1.1 mickey 319: }
320: }
321: s->z_crc = crc32(s->z_crc, start,
1.7 deraadt 322: (uInt)(s->z_stream.next_out - start));
1.15 millert 323: len -= s->z_stream.avail_out;
324:
325: return (len);
1.30 millert 326: bad:
327: /* Add byte counts from the finished stream. */
328: s->z_total_in += s->z_stream.total_in;
329: s->z_total_out += s->z_stream.total_out;
330: return (-1);
1.1 mickey 331: }
332:
1.34 ! tedu 333: #ifndef SMALL
! 334: static int
! 335: put_int32(gz_stream *s, u_int32_t x)
! 336: {
! 337: u_int32_t y = htole32(x);
! 338:
! 339: if (write(s->z_fd, &y, sizeof(y)) != sizeof(y))
! 340: return Z_ERRNO;
! 341: return 0;
! 342: }
! 343:
! 344: static int
! 345: put_header(gz_stream *s, char *name, u_int32_t mtime, int bits)
! 346: {
! 347: struct iovec iov[2];
! 348: u_char buf[10];
! 349:
! 350: buf[0] = gz_magic[0];
! 351: buf[1] = gz_magic[1];
! 352: buf[2] = Z_DEFLATED;
! 353: buf[3] = name ? ORIG_NAME : 0;
! 354: buf[4] = mtime & 0xff;
! 355: buf[5] = (mtime >> 8) & 0xff;
! 356: buf[6] = (mtime >> 16) & 0xff;
! 357: buf[7] = (mtime >> 24) & 0xff;
! 358: buf[8] = bits == 1 ? 4 : bits == 9 ? 2 : 0; /* xflags */
! 359: buf[9] = OS_CODE;
! 360: iov[0].iov_base = buf;
! 361: iov[0].iov_len = sizeof(buf);
! 362: s->z_hlen = sizeof(buf);
! 363:
! 364: if (name != NULL) {
! 365: iov[1].iov_base = name;
! 366: iov[1].iov_len = strlen(name) + 1;
! 367: s->z_hlen += iov[1].iov_len;
! 368: }
! 369: if (writev(s->z_fd, iov, name ? 2 : 1) == -1)
! 370: return (-1);
! 371: return (0);
! 372: }
! 373:
! 374: void *
! 375: gz_wopen(int fd, char *name, int bits, u_int32_t mtime)
! 376: {
! 377: gz_stream *s;
! 378:
! 379: if (fd < 0)
! 380: return NULL;
! 381:
! 382: if (bits < 0 || bits > Z_BEST_COMPRESSION) {
! 383: errno = EINVAL;
! 384: return NULL;
! 385: }
! 386: if ((s = calloc(1, sizeof(gz_stream))) == NULL)
! 387: return NULL;
! 388:
! 389: s->z_stream.zalloc = (alloc_func)0;
! 390: s->z_stream.zfree = (free_func)0;
! 391: s->z_stream.opaque = (voidpf)0;
! 392: s->z_stream.next_in = Z_NULL;
! 393: s->z_stream.next_out = Z_NULL;
! 394: s->z_stream.avail_in = s->z_stream.avail_out = 0;
! 395: s->z_fd = 0;
! 396: s->z_eof = 0;
! 397: s->z_time = 0;
! 398: s->z_hlen = 0;
! 399: s->z_total_in = 0;
! 400: s->z_total_out = 0;
! 401: s->z_crc = crc32(0L, Z_NULL, 0);
! 402: s->z_mode = 'w';
! 403:
! 404: /* windowBits is passed < 0 to suppress zlib header */
! 405: if (deflateInit2(&(s->z_stream), bits, Z_DEFLATED,
! 406: -MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK) {
! 407: free (s);
! 408: return NULL;
! 409: }
! 410: s->z_stream.next_out = s->z_buf;
! 411: s->z_stream.avail_out = Z_BUFSIZE;
! 412:
! 413: errno = 0;
! 414: s->z_fd = fd;
! 415:
! 416: /* write the .gz header */
! 417: if (put_header(s, name, mtime, bits) != 0) {
! 418: gz_close(s, NULL, NULL, NULL);
! 419: s = NULL;
! 420: }
! 421:
! 422: return s;
! 423: }
1.1 mickey 424: int
1.7 deraadt 425: gz_write(void *cookie, const char *buf, int len)
1.1 mickey 426: {
1.2 mpech 427: gz_stream *s = (gz_stream*)cookie;
1.1 mickey 428:
429: s->z_stream.next_in = (char *)buf;
430: s->z_stream.avail_in = len;
431:
432: while (s->z_stream.avail_in != 0) {
433: if (s->z_stream.avail_out == 0) {
434: if (write(s->z_fd, s->z_buf, Z_BUFSIZE) != Z_BUFSIZE)
435: break;
436: s->z_stream.next_out = s->z_buf;
437: s->z_stream.avail_out = Z_BUFSIZE;
438: }
439: if (deflate(&(s->z_stream), Z_NO_FLUSH) != Z_OK)
440: break;
441: }
442: s->z_crc = crc32(s->z_crc, buf, len);
443:
444: return (int)(len - s->z_stream.avail_in);
1.34 ! tedu 445: }
! 446:
! 447: int
! 448: gz_flush(void *cookie, int flush)
! 449: {
! 450: gz_stream *s = (gz_stream*)cookie;
! 451: size_t len;
! 452: int done = 0;
! 453: int err;
! 454:
! 455: if (s == NULL || s->z_mode != 'w') {
! 456: errno = EBADF;
! 457: return Z_ERRNO;
! 458: }
! 459:
! 460: s->z_stream.avail_in = 0; /* should be zero already anyway */
! 461:
! 462: for (;;) {
! 463: len = Z_BUFSIZE - s->z_stream.avail_out;
! 464:
! 465: if (len != 0) {
! 466: if (write(s->z_fd, s->z_buf, len) != len)
! 467: return Z_ERRNO;
! 468: s->z_stream.next_out = s->z_buf;
! 469: s->z_stream.avail_out = Z_BUFSIZE;
! 470: }
! 471: if (done)
! 472: break;
! 473: if ((err = deflate(&(s->z_stream), flush)) != Z_OK &&
! 474: err != Z_STREAM_END)
! 475: return err;
! 476:
! 477: /* deflate has finished flushing only when it hasn't
! 478: * used up all the available space in the output buffer
! 479: */
! 480: done = (s->z_stream.avail_out != 0 || err == Z_STREAM_END);
! 481: }
! 482: return 0;
! 483: }
1.20 henning 484: #endif
1.34 ! tedu 485:
! 486: int
! 487: gz_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
! 488: {
! 489: gz_stream *s = (gz_stream*)cookie;
! 490: int err = 0;
! 491:
! 492: if (s == NULL)
! 493: return -1;
! 494:
! 495: #ifndef SMALL
! 496: if (s->z_mode == 'w' && (err = gz_flush (s, Z_FINISH)) == Z_OK) {
! 497: if ((err = put_int32 (s, s->z_crc)) == Z_OK) {
! 498: s->z_hlen += sizeof(int32_t);
! 499: if ((err = put_int32 (s, s->z_stream.total_in)) == Z_OK)
! 500: s->z_hlen += sizeof(int32_t);
! 501: }
! 502: }
! 503: #endif
! 504: if (!err && s->z_stream.state != NULL) {
! 505: if (s->z_mode == 'w')
! 506: #ifndef SMALL
! 507: err = deflateEnd(&s->z_stream);
! 508: #else
! 509: err = -1;
! 510: #endif
! 511: else if (s->z_mode == 'r')
! 512: err = inflateEnd(&s->z_stream);
! 513: }
! 514:
! 515: if (info != NULL) {
! 516: info->mtime = s->z_time;
! 517: info->crc = s->z_crc;
! 518: info->hlen = s->z_hlen;
! 519: if (s->z_mode == 'r') {
! 520: info->total_in = s->z_total_in;
! 521: info->total_out = s->z_total_out;
! 522: } else {
! 523: info->total_in = s->z_stream.total_in;
! 524: info->total_out = s->z_stream.total_out;
! 525: }
! 526:
! 527: }
! 528:
! 529: setfile(name, s->z_fd, sb);
! 530: if (!err)
! 531: err = close(s->z_fd);
! 532: else
! 533: (void)close(s->z_fd);
! 534:
! 535: free(s);
! 536:
! 537: return err;
1.1 mickey 538: }
1.34 ! tedu 539: