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