cread.c revision 1.10
1/*	$NetBSD: cread.c,v 1.10 1999/11/11 20:23:16 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 1996
5 *	Matthias Drochner.  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 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed for the NetBSD Project
18 *	by Matthias Drochner.
19 * 4. The name of the author may not be used to endorse or promote products
20 *    derived from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35/*
36 * Support for compressed bootfiles  (only read)
37 *
38 * - replaces open(), close(), read(), lseek().
39 * - original libsa open(), close(), read(), lseek() are called
40 *   as oopen(), oclose(), oread() resp. olseek().
41 * - compression parts stripped from zlib:gzio.c
42 */
43
44/* gzio.c -- IO on .gz files
45 * Copyright (C) 1995-1996 Jean-loup Gailly.
46 * For conditions of distribution and use, see copyright notice in zlib.h
47 */
48
49#include "stand.h"
50#ifdef _STANDALONE
51#include <lib/libz/zlib.h>
52#else
53#include <string.h>
54#include <zlib.h>
55#endif
56
57#define EOF (-1) /* needed by compression code */
58
59#ifdef SAVE_MEMORY
60#define Z_BUFSIZE 1024
61#else
62#define Z_BUFSIZE 4096
63#endif
64
65static int gz_magic[2] = {0x1f, 0x8b};	/* gzip magic header */
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
75static struct sd {
76	z_stream	stream;
77	int		z_err;	/* error code for last stream operation */
78	int		z_eof;	/* set if end of input file */
79	int		fd;
80	unsigned char	*inbuf;	/* input buffer */
81	unsigned long	crc;	/* crc32 of uncompressed data */
82	int		compressed;	/* 1 if input file is a .gz file */
83} *ss[SOPEN_MAX];
84
85static int		get_byte __P((struct sd *));
86static unsigned long	getLong __P((struct sd *));
87static void		check_header __P((struct sd *));
88
89/* XXX - find suitable headerf ile for these: */
90void	*zcalloc __P((void *, unsigned int, unsigned int));
91void	zcfree __P((void *, void *));
92void	zmemcpy __P((unsigned char *, unsigned char *, unsigned int));
93
94
95/*
96 * compression utilities
97 */
98
99void *
100zcalloc (opaque, items, size)
101	void *opaque;
102	unsigned items;
103	unsigned size;
104{
105	return(alloc(items * size));
106}
107
108void
109zcfree (opaque, ptr)
110	void *opaque;
111	void *ptr;
112{
113	free(ptr, 0); /* XXX works only with modified allocator */
114}
115
116void
117zmemcpy(dest, source, len)
118	unsigned char *dest;
119	unsigned char *source;
120	unsigned int len;
121{
122	bcopy(source, dest, len);
123}
124
125static int
126get_byte(s)
127	struct sd *s;
128{
129	if (s->z_eof)
130		return (EOF);
131
132	if (s->stream.avail_in == 0) {
133		int got;
134
135		errno = 0;
136		got = oread(s->fd, s->inbuf, Z_BUFSIZE);
137		if (got <= 0) {
138			s->z_eof = 1;
139			if (errno) s->z_err = Z_ERRNO;
140			return EOF;
141		}
142		s->stream.avail_in = got;
143		s->stream.next_in = s->inbuf;
144	}
145	s->stream.avail_in--;
146	return *(s->stream.next_in)++;
147}
148
149static unsigned long
150getLong (s)
151    struct sd *s;
152{
153	unsigned long x = (unsigned long)get_byte(s);
154	int c;
155
156	x += ((unsigned long)get_byte(s)) << 8;
157	x += ((unsigned long)get_byte(s)) << 16;
158	c = get_byte(s);
159	if (c == EOF)
160		s->z_err = Z_DATA_ERROR;
161	x += ((unsigned long)c)<<24;
162	return x;
163}
164
165static void
166check_header(s)
167	struct sd *s;
168{
169	int method; /* method byte */
170	int flags;  /* flags byte */
171	unsigned int len;
172	int c;
173
174	/* Check the gzip magic header */
175	for (len = 0; len < 2; len++) {
176		c = get_byte(s);
177		if (c == gz_magic[len])
178			continue;
179		if ((c == EOF) && (len == 0))  {
180			/*
181			 * We must not change s->compressed if we are at EOF;
182			 * we may have come to the end of a gzipped file and be
183			 * check to see if another gzipped file is concatenated
184			 * to this one. If one isn't, we still need to be able
185			 * to lseek on this file as a compressed file.
186			 */
187			return;
188		}
189		s->compressed = 0;
190		if (c != EOF) {
191			s->stream.avail_in++;
192			s->stream.next_in--;
193		}
194		s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
195		return;
196	}
197	s->compressed = 1;
198	method = get_byte(s);
199	flags = get_byte(s);
200	if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
201		s->z_err = Z_DATA_ERROR;
202		return;
203	}
204
205	/* Discard time, xflags and OS code: */
206	for (len = 0; len < 6; len++)
207		(void)get_byte(s);
208
209	if ((flags & EXTRA_FIELD) != 0) {
210		/* skip the extra field */
211		len  =  (unsigned int)get_byte(s);
212		len += ((unsigned int)get_byte(s)) << 8;
213		/* len is garbage if EOF but the loop below will quit anyway */
214		while (len-- != 0 && get_byte(s) != EOF) /*void*/;
215	}
216	if ((flags & ORIG_NAME) != 0) {
217		/* skip the original file name */
218		while ((c = get_byte(s)) != 0 && c != EOF) /*void*/;
219	}
220	if ((flags & COMMENT) != 0) {
221		/* skip the .gz file comment */
222		while ((c = get_byte(s)) != 0 && c != EOF) /*void*/;
223	}
224	if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
225		for (len = 0; len < 2; len++)
226			(void)get_byte(s);
227	}
228	s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
229}
230
231/*
232 * new open(), close(), read(), lseek()
233 */
234
235int
236open(fname, mode)
237	const char *fname;
238	int mode;
239{
240	int fd;
241	struct sd *s = 0;
242
243	if ( ((fd = oopen(fname, mode)) == -1) || (mode != 0) )
244		/* compression only for read */
245		return(fd);
246
247	ss[fd] = s = alloc(sizeof(struct sd));
248	if (s == 0)
249		goto errout;
250	bzero(s, sizeof(struct sd));
251
252	if (inflateInit2(&(s->stream), -15) != Z_OK)
253		goto errout;
254
255	s->stream.next_in  = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
256	if (s->inbuf == 0) {
257		inflateEnd(&(s->stream));
258		goto errout;
259	}
260
261	s->fd = fd;
262	check_header(s); /* skip the .gz header */
263	return(fd);
264
265errout:
266	if (s != 0)
267		free(s, sizeof(struct sd));
268	oclose(fd);
269	return (-1);
270}
271
272int
273close(fd)
274	int fd;
275{
276	struct open_file *f;
277	struct sd *s;
278
279#if !defined(LIBSA_NO_FD_CHECKING)
280	if ((unsigned)fd >= SOPEN_MAX) {
281		errno = EBADF;
282		return (-1);
283	}
284#endif
285	f = &files[fd];
286
287	if ((f->f_flags & F_READ) == 0)
288		return(oclose(fd));
289
290	s = ss[fd];
291
292	inflateEnd(&(s->stream));
293
294	free(s->inbuf, Z_BUFSIZE);
295	free(s, sizeof(struct sd));
296
297	return (oclose(fd));
298}
299
300ssize_t
301read(fd, buf, len)
302	int fd;
303	void *buf;
304	size_t len;
305{
306	struct sd *s;
307	unsigned char *start = buf; /* starting point for crc computation */
308
309	s = ss[fd];
310
311	if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
312		return (-1);
313	if (s->z_err == Z_STREAM_END)
314		return (0);  /* EOF */
315
316	s->stream.next_out = buf;
317	s->stream.avail_out = len;
318
319	while (s->stream.avail_out != 0) {
320
321		if (s->compressed == 0) {
322			/* Copy first the lookahead bytes: */
323			unsigned int n = s->stream.avail_in;
324			if (n > s->stream.avail_out)
325				n = s->stream.avail_out;
326			if (n > 0) {
327				zmemcpy(s->stream.next_out,
328					s->stream.next_in, n);
329				s->stream.next_out  += n;
330				s->stream.next_in   += n;
331				s->stream.avail_out -= n;
332				s->stream.avail_in  -= n;
333			}
334			if (s->stream.avail_out > 0) {
335				int got;
336				got = oread(s->fd, s->stream.next_out,
337					    s->stream.avail_out);
338				if (got == -1)
339					return (got);
340				s->stream.avail_out -= got;
341			}
342			return (int)(len - s->stream.avail_out);
343		}
344
345		if (s->stream.avail_in == 0 && !s->z_eof) {
346			int got;
347			errno = 0;
348			got = oread(fd, s->inbuf, Z_BUFSIZE);
349			if (got <= 0) {
350				s->z_eof = 1;
351				if (errno) {
352					s->z_err = Z_ERRNO;
353					break;
354				}
355			}
356			s->stream.avail_in = got;
357			s->stream.next_in = s->inbuf;
358		}
359
360		s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
361
362		if (s->z_err == Z_STREAM_END) {
363			/* Check CRC and original size */
364			s->crc = crc32(s->crc, start, (unsigned int)
365					(s->stream.next_out - start));
366			start = s->stream.next_out;
367
368			if (getLong(s) != s->crc ||
369			    getLong(s) != s->stream.total_out) {
370
371				s->z_err = Z_DATA_ERROR;
372			} else {
373				/* Check for concatenated .gz files: */
374				check_header(s);
375				if (s->z_err == Z_OK) {
376					inflateReset(&(s->stream));
377					s->crc = crc32(0L, Z_NULL, 0);
378				}
379			}
380		}
381		if (s->z_err != Z_OK || s->z_eof)
382			break;
383	}
384
385	s->crc = crc32(s->crc, start,
386		       (unsigned int)(s->stream.next_out - start));
387
388	return (int)(len - s->stream.avail_out);
389}
390
391off_t
392lseek(fd, offset, where)
393	int fd;
394	off_t offset;
395	int where;
396{
397	struct open_file *f;
398	struct sd *s;
399
400#if !defined(LIBSA_NO_FD_CHECKING)
401	if ((unsigned)fd >= SOPEN_MAX) {
402		errno = EBADF;
403		return (-1);
404	}
405#endif
406	f = &files[fd];;
407
408	if ((f->f_flags & F_READ) == 0)
409		return(olseek(fd, offset, where));
410
411	s = ss[fd];
412
413	if(s->compressed == 0) {
414		off_t res = olseek(fd, offset, where);
415		if (res != (off_t)-1) {
416			/* make sure the lookahead buffer is invalid */
417			s->stream.avail_in = 0;
418		}
419		return (res);
420	}
421
422	switch(where) {
423	case SEEK_CUR:
424		    offset += s->stream.total_out;
425	case SEEK_SET:
426		/* if seek backwards, simply start from the beginning */
427		if (offset < s->stream.total_out) {
428			off_t res;
429			void *sav_inbuf;
430
431			res = olseek(fd, 0, SEEK_SET);
432			if(res == (off_t)-1)
433			    return(res);
434			/* ??? perhaps fallback to close / open */
435
436			inflateEnd(&(s->stream));
437
438			sav_inbuf = s->inbuf; /* don't allocate again */
439			bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
440
441			inflateInit2(&(s->stream), -15);
442			s->stream.next_in = s->inbuf = sav_inbuf;
443
444			s->fd = fd;
445			check_header(s); /* skip the .gz header */
446		}
447
448		    /* to seek forwards, throw away data */
449		if (offset > s->stream.total_out) {
450			off_t toskip = offset - s->stream.total_out;
451
452			while (toskip > 0) {
453#define DUMMYBUFSIZE 256
454				char dummybuf[DUMMYBUFSIZE];
455				off_t len = toskip;
456				if (len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
457				if (read(fd, dummybuf, len) != len) {
458					errno = EOFFSET;
459					return ((off_t)-1);
460				}
461				toskip -= len;
462			}
463		}
464#ifdef DEBUG
465		if (offset != s->stream.total_out)
466			panic("lseek compressed");
467#endif
468		return (offset);
469	case SEEK_END:
470		errno = EOFFSET;
471		break;
472	default:
473		errno = EINVAL;
474	}
475
476	return((off_t)-1);
477}
478