1 /* $OpenBSD: gzopen.c,v 1.25 2008/08/20 09:22:02 mpf Exp $ */
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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
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 /* zlib.h -- interface of the 'zlib' general purpose compression library
31
32 Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler
33
34 This software is provided 'as-is', without any express or implied
35 warranty. In no event will the authors be held liable for any damages
36 arising from the use of this software.
37
38 Permission is granted to anyone to use this software for any purpose,
39 including commercial applications, and to alter it and redistribute it
40 freely, subject to the following restrictions:
41
42 1. The origin of this software must not be misrepresented; you must not
43 claim that you wrote the original software. If you use this software
44 in a product, an acknowledgment in the product documentation would be
45 appreciated but is not required.
46 2. Altered source versions must be plainly marked as such, and must not be
47 misrepresented as being the original software.
48 3. This notice may not be removed or altered from any source distribution.
49
50 Jean-loup Gailly Mark Adler
51 jloup@gzip.org madler@alumni.caltech.edu
52 */
53
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <sys/uio.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <errno.h>
61 #include <unistd.h>
62 #include <zlib.h>
63 #include "compress.h"
64
65 __RCSID("$MirOS: src/usr.bin/compress/gzopen.c,v 1.7 2009/10/27 19:27:15 tg Exp $");
66
67 /* gzip flag byte */
68 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
69 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
70 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
71 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
72 #define COMMENT 0x10 /* bit 4 set: file comment present */
73 #define RESERVED 0xE0 /* bits 5..7: reserved */
74
75 #define DEF_MEM_LEVEL 8
76 #define OS_CODE 0x03 /* unix */
77
78 typedef
79 struct gz_stream {
80 int z_fd; /* .gz file */
81 z_stream z_stream; /* libz stream */
82 int z_eof; /* set if end of input file */
83 u_char z_buf[Z_BUFSIZE]; /* i/o buffer */
84 u_int32_t z_time; /* timestamp (mtime) */
85 u_int32_t z_hlen; /* length of the gz header */
86 u_int32_t z_crc; /* crc32 of uncompressed data */
87 char z_mode; /* 'w' or 'r' */
88
89 } gz_stream;
90
91 static const u_char gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
92
93 static int put_int32(gz_stream *, u_int32_t);
94 static u_int32_t get_int32(gz_stream *);
95 static int get_header(gz_stream *, char *, int);
96 static int put_header(gz_stream *, char *, u_int32_t, int);
97 static int get_byte(gz_stream *);
98
99 void *
gz_open(int fd,const char * mode,char * name,int bits,u_int32_t mtime,int gotmagic)100 gz_open(int fd, const char *mode, char *name, int bits,
101 u_int32_t mtime, int gotmagic)
102 {
103 gz_stream *s;
104
105 if (fd < 0 || !mode)
106 return NULL;
107
108 if ((mode[0] != 'r' && mode[0] != 'w') || mode[1] != '\0' ||
109 bits < 0 || bits > Z_BEST_COMPRESSION) {
110 errno = EINVAL;
111 return NULL;
112 }
113 if ((s = (gz_stream *)calloc(1, sizeof(gz_stream))) == NULL)
114 return NULL;
115
116 s->z_stream.zalloc = (alloc_func)0;
117 s->z_stream.zfree = (free_func)0;
118 s->z_stream.opaque = (voidpf)0;
119 s->z_stream.next_in = Z_NULL;
120 s->z_stream.next_out = Z_NULL;
121 s->z_stream.avail_in = s->z_stream.avail_out = 0;
122 s->z_fd = 0;
123 s->z_eof = 0;
124 s->z_time = 0;
125 s->z_hlen = 0;
126 s->z_crc = crc32(0L, Z_NULL, 0);
127 s->z_mode = mode[0];
128
129 if (s->z_mode == 'w') {
130 #ifndef SMALL
131 /* windowBits is passed < 0 to suppress zlib header */
132 if (deflateInit2(&(s->z_stream), bits, Z_DEFLATED,
133 -MAX_WBITS, DEF_MEM_LEVEL, 0) != Z_OK) {
134 free (s);
135 return NULL;
136 }
137 s->z_stream.next_out = s->z_buf;
138 #else
139 return (NULL);
140 #endif
141 } else {
142 if (inflateInit2(&(s->z_stream), -MAX_WBITS) != Z_OK) {
143 free (s);
144 return NULL;
145 }
146 s->z_stream.next_in = s->z_buf;
147 }
148 s->z_stream.avail_out = Z_BUFSIZE;
149
150 errno = 0;
151 s->z_fd = fd;
152
153 if (s->z_mode == 'w') {
154 /* write the .gz header */
155 if (put_header(s, name, mtime, bits) != 0) {
156 gz_close(s, NULL, NULL, NULL);
157 s = NULL;
158 }
159 } else {
160 /* read the .gz header */
161 if (get_header(s, name, gotmagic) != 0) {
162 gz_close(s, NULL, NULL, NULL);
163 s = NULL;
164 }
165 }
166
167 return s;
168 }
169
170 int
gz_close(void * cookie,struct z_info * info,const char * name,struct stat * sb)171 gz_close(void *cookie, struct z_info *info, const char *name, struct stat *sb)
172 {
173 gz_stream *s = (gz_stream*)cookie;
174 int err = 0;
175
176 if (s == NULL)
177 return -1;
178
179 #ifndef SMALL
180 if (s->z_mode == 'w' && (err = gz_flush (s, Z_FINISH)) == Z_OK) {
181 if ((err = put_int32 (s, s->z_crc)) == Z_OK) {
182 s->z_hlen += sizeof(int32_t);
183 if ((err = put_int32 (s, s->z_stream.total_in)) == Z_OK)
184 s->z_hlen += sizeof(int32_t);
185 }
186 }
187 #endif
188 if (!err && s->z_stream.state != NULL) {
189 if (s->z_mode == 'w')
190 #ifndef SMALL
191 err = deflateEnd(&s->z_stream);
192 #else
193 err = -1;
194 #endif
195 else if (s->z_mode == 'r')
196 err = inflateEnd(&s->z_stream);
197 }
198
199 if (info != NULL) {
200 info->mtime = s->z_time;
201 info->crc = s->z_crc;
202 info->hlen = s->z_hlen;
203 info->total_in = (off_t)s->z_stream.total_in;
204 info->total_out = (off_t)s->z_stream.total_out;
205 }
206
207 setfile(name, s->z_fd, sb);
208 if (!err)
209 err = close(s->z_fd);
210 else
211 (void)close(s->z_fd);
212
213 free(s);
214
215 return err;
216 }
217
218 #ifndef SMALL
219 int
gz_flush(void * cookie,int flush)220 gz_flush(void *cookie, int flush)
221 {
222 gz_stream *s = (gz_stream*)cookie;
223 size_t len;
224 int done = 0;
225 int err;
226
227 if (s == NULL || s->z_mode != 'w') {
228 errno = EBADF;
229 return Z_ERRNO;
230 }
231
232 s->z_stream.avail_in = 0; /* should be zero already anyway */
233
234 for (;;) {
235 len = Z_BUFSIZE - s->z_stream.avail_out;
236
237 if (len != 0) {
238 if ((size_t)write(s->z_fd, s->z_buf, len) != len)
239 return Z_ERRNO;
240 s->z_stream.next_out = s->z_buf;
241 s->z_stream.avail_out = Z_BUFSIZE;
242 }
243 if (done)
244 break;
245 if ((err = deflate(&(s->z_stream), flush)) != Z_OK &&
246 err != Z_STREAM_END)
247 return err;
248
249 /* deflate has finished flushing only when it hasn't
250 * used up all the available space in the output buffer
251 */
252 done = (s->z_stream.avail_out != 0 || err == Z_STREAM_END);
253 }
254 return 0;
255 }
256 #endif
257
258 static int
put_int32(gz_stream * s,u_int32_t x)259 put_int32(gz_stream *s, u_int32_t x)
260 {
261 u_int32_t y = htole32(x);
262
263 if (write(s->z_fd, &y, sizeof(y)) != sizeof(y))
264 return Z_ERRNO;
265 return 0;
266 }
267
268 static int
get_byte(gz_stream * s)269 get_byte(gz_stream *s)
270 {
271 if (s->z_eof)
272 return EOF;
273
274 if (s->z_stream.avail_in == 0) {
275 errno = 0;
276 s->z_stream.avail_in = read(s->z_fd, s->z_buf, Z_BUFSIZE);
277 if ((int)s->z_stream.avail_in <= 0) {
278 s->z_eof = 1;
279 return EOF;
280 }
281 s->z_stream.next_in = s->z_buf;
282 }
283 s->z_stream.avail_in--;
284 return *s->z_stream.next_in++;
285 }
286
287 static u_int32_t
get_int32(gz_stream * s)288 get_int32(gz_stream *s)
289 {
290 u_int32_t x;
291
292 x = ((u_int32_t)(get_byte(s) & 0xff));
293 x |= ((u_int32_t)(get_byte(s) & 0xff))<<8;
294 x |= ((u_int32_t)(get_byte(s) & 0xff))<<16;
295 x |= ((u_int32_t)(get_byte(s) & 0xff))<<24;
296 return x;
297 }
298
299 static int
get_header(gz_stream * s,char * name,int gotmagic)300 get_header(gz_stream *s, char *name, int gotmagic)
301 {
302 int method; /* method byte */
303 int flags; /* flags byte */
304 char *ep;
305 uInt len;
306 int c;
307
308 /* Check the gzip magic header */
309 if (!gotmagic) {
310 for (len = 0; len < 2; len++) {
311 c = get_byte(s);
312 if (c != gz_magic[len]) {
313 errno = EFTYPE;
314 return -1;
315 }
316 }
317 }
318
319 method = get_byte(s);
320 flags = get_byte(s);
321 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
322 errno = EFTYPE;
323 return -1;
324 }
325
326 /* Stash timestamp (mtime) */
327 s->z_time = get_int32(s);
328
329 /* Discard xflags and OS code */
330 (void)get_byte(s);
331 (void)get_byte(s);
332
333 s->z_hlen = 10; /* magic, method, flags, time, xflags, OS code */
334 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
335 len = (uInt)get_byte(s);
336 len += ((uInt)get_byte(s))<<8;
337 s->z_hlen += 2;
338 /* len is garbage if EOF but the loop below will quit anyway */
339 while (len-- != 0 && get_byte(s) != EOF)
340 s->z_hlen++;
341 }
342
343 if ((flags & ORIG_NAME) != 0) { /* read/save the original file name */
344 if ((ep = name) != NULL)
345 ep += MAXPATHLEN - 1;
346 while ((c = get_byte(s)) != EOF) {
347 s->z_hlen++;
348 if (c == '\0')
349 break;
350 if (name < ep)
351 *name++ = c;
352 }
353 if (name != NULL)
354 *name = '\0';
355 }
356
357 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
358 while ((c = get_byte(s)) != EOF) {
359 s->z_hlen++;
360 if (c == '\0')
361 break;
362 }
363 }
364
365 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
366 (void)get_byte(s);
367 (void)get_byte(s);
368 s->z_hlen += 2;
369 }
370
371 if (s->z_eof) {
372 errno = EFTYPE;
373 return -1;
374 }
375
376 return 0;
377 }
378
379 static int
put_header(gz_stream * s,char * name,u_int32_t mtime,int bits)380 put_header(gz_stream *s, char *name, u_int32_t mtime, int bits)
381 {
382 struct iovec iov[2];
383 u_char buf[10];
384
385 buf[0] = gz_magic[0];
386 buf[1] = gz_magic[1];
387 buf[2] = Z_DEFLATED;
388 buf[3] = name ? ORIG_NAME : 0;
389 buf[4] = mtime & 0xff;
390 buf[5] = (mtime >> 8) & 0xff;
391 buf[6] = (mtime >> 16) & 0xff;
392 buf[7] = (mtime >> 24) & 0xff;
393 buf[8] = bits == 1 ? 4 : bits == 9 ? 2 : 0; /* xflags */
394 buf[9] = OS_CODE;
395 iov[0].iov_base = buf;
396 iov[0].iov_len = sizeof(buf);
397 s->z_hlen = sizeof(buf);
398
399 if (name != NULL) {
400 iov[1].iov_base = name;
401 iov[1].iov_len = strlen(name) + 1;
402 s->z_hlen += iov[1].iov_len;
403 }
404 if (writev(s->z_fd, iov, name ? 2 : 1) == -1)
405 return (-1);
406 return (0);
407 }
408
409 int
gz_read(void * cookie,char * buf,int len)410 gz_read(void *cookie, char *buf, int len)
411 {
412 gz_stream *s = (gz_stream*)cookie;
413 u_char *start = (u_char *)buf; /* starting point for crc computation */
414 int error = Z_OK;
415
416 s->z_stream.next_out = (u_char *)buf;
417 s->z_stream.avail_out = len;
418
419 while (error == Z_OK && !s->z_eof && s->z_stream.avail_out != 0) {
420
421 if (s->z_stream.avail_in == 0) {
422
423 errno = 0;
424 s->z_stream.avail_in = read(s->z_fd, s->z_buf,
425 Z_BUFSIZE);
426 if ((int)s->z_stream.avail_in <= 0)
427 s->z_eof = 1;
428 s->z_stream.next_in = s->z_buf;
429 }
430
431 error = inflate(&(s->z_stream), Z_NO_FLUSH);
432
433 if (error == Z_DATA_ERROR) {
434 errno = EINVAL;
435 return -1;
436 }
437 if (error == Z_BUF_ERROR) {
438 errno = EIO;
439 return -1;
440 }
441 if (error == Z_STREAM_END) {
442 /* Check CRC and original size */
443 s->z_crc = crc32(s->z_crc, start,
444 (uInt)(s->z_stream.next_out - start));
445 start = s->z_stream.next_out;
446
447 if (get_int32(s) != s->z_crc) {
448 errno = EINVAL;
449 return -1;
450 }
451 if (get_int32(s) != (u_int32_t)s->z_stream.total_out) {
452 errno = EIO;
453 return -1;
454 }
455 s->z_hlen += 2 * sizeof(int32_t);
456 /* Check for the existence of an appended file. */
457 if (get_header(s, NULL, 0) != 0) {
458 s->z_eof = 1;
459 break;
460 }
461 inflateReset(&(s->z_stream));
462 s->z_crc = crc32(0L, Z_NULL, 0);
463 error = Z_OK;
464 }
465 }
466 s->z_crc = crc32(s->z_crc, start,
467 (uInt)(s->z_stream.next_out - start));
468 len -= s->z_stream.avail_out;
469
470 return (len);
471 }
472
473 int
gz_write(void * cookie,const char * buf,int len)474 gz_write(void *cookie, const char *buf, int len)
475 {
476 #ifndef SMALL
477 gz_stream *s = (gz_stream*)cookie;
478
479 s->z_stream.next_in = (const u_char *)buf;
480 s->z_stream.avail_in = len;
481
482 while (s->z_stream.avail_in != 0) {
483 if (s->z_stream.avail_out == 0) {
484 if (write(s->z_fd, s->z_buf, Z_BUFSIZE) != Z_BUFSIZE)
485 break;
486 s->z_stream.next_out = s->z_buf;
487 s->z_stream.avail_out = Z_BUFSIZE;
488 }
489 if (deflate(&(s->z_stream), Z_NO_FLUSH) != Z_OK)
490 break;
491 }
492 s->z_crc = crc32(s->z_crc, (const u_char *)buf, len);
493
494 return (int)(len - s->z_stream.avail_in);
495 #endif
496 }
497