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