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