cread.c revision 1.3
1/*	$NetBSD: cread.c,v 1.3 1997/06/13 14:28:52 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/* support for compressed bootfiles
36 (only read)
37 replaces open(), close(), read(), lseek().
38 original libsa open(), close(), read(), lseek() are called
39 as oopen(), oclose(), oread() resp. olseek().
40 compression parts stripped from zlib:gzio.c
41 */
42
43/* gzio.c -- IO on .gz files
44 * Copyright (C) 1995-1996 Jean-loup Gailly.
45 * For conditions of distribution and use, see copyright notice in zlib.h
46 */
47
48#include <lib/libkern/libkern.h>
49#include "stand.h"
50#include <lib/libz/zlib.h>
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 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      transparent; /* 1 if input file is not a .gz file */
78} *ss[SOPEN_MAX];
79
80/*
81 * compression utilities
82 */
83
84void *zcalloc (opaque, items, size)
85void *opaque;
86unsigned items;
87unsigned size;
88{
89  return(alloc(items * size));
90}
91
92void  zcfree (opaque, ptr)
93void *opaque;
94void *ptr;
95{
96  free(ptr, 0); /* XXX works only with modified allocator */
97}
98
99void zmemcpy(dest, source, len)
100unsigned char *dest;
101unsigned char *source;
102unsigned int len;
103{
104  bcopy(source, dest, len);
105}
106
107static int get_byte(s)
108    struct sd *s;
109{
110    if (s->z_eof) return EOF;
111    if (s->stream.avail_in == 0) {
112	errno = 0;
113	s->stream.avail_in = oread(s->fd, s->inbuf, Z_BUFSIZE);
114	if (s->stream.avail_in == 0) {
115	    s->z_eof = 1;
116	    if (errno) s->z_err = Z_ERRNO;
117	    return EOF;
118	}
119	s->stream.next_in = s->inbuf;
120    }
121    s->stream.avail_in--;
122    return *(s->stream.next_in)++;
123}
124
125static unsigned long getLong (s)
126    struct sd *s;
127{
128    unsigned long x = (unsigned long)get_byte(s);
129    int c;
130
131    x += ((unsigned long)get_byte(s))<<8;
132    x += ((unsigned long)get_byte(s))<<16;
133    c = get_byte(s);
134    if (c == EOF) s->z_err = Z_DATA_ERROR;
135    x += ((unsigned long)c)<<24;
136    return x;
137}
138
139static void check_header(s)
140    struct sd *s;
141{
142    int method; /* method byte */
143    int flags;  /* flags byte */
144    unsigned int len;
145    int c;
146
147    /* Check the gzip magic header */
148    for (len = 0; len < 2; len++) {
149	c = get_byte(s);
150	if (c != gz_magic[len]) {
151	    s->transparent = 1;
152	    if (c != EOF) s->stream.avail_in++, s->stream.next_in--;
153	    s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
154	    return;
155	}
156    }
157    method = get_byte(s);
158    flags = get_byte(s);
159    if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
160	s->z_err = Z_DATA_ERROR;
161	return;
162    }
163
164    /* Discard time, xflags and OS code: */
165    for (len = 0; len < 6; len++) (void)get_byte(s);
166
167    if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
168	len  =  (unsigned int)get_byte(s);
169	len += ((unsigned int)get_byte(s))<<8;
170	/* len is garbage if EOF but the loop below will quit anyway */
171	while (len-- != 0 && get_byte(s) != EOF) ;
172    }
173    if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
174	while ((c = get_byte(s)) != 0 && c != EOF) ;
175    }
176    if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
177	while ((c = get_byte(s)) != 0 && c != EOF) ;
178    }
179    if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
180	for (len = 0; len < 2; len++) (void)get_byte(s);
181    }
182    s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
183}
184
185/*
186 * new open(), close(), read(), lseek()
187 */
188
189int
190open(fname, mode)
191	const char *fname;
192	int mode;
193{
194	  int fd;
195	  struct sd *s = 0;
196
197	  if(((fd = oopen(fname, mode)) == -1)
198	     || (mode != 0)) /* compression only for read */
199	  return(fd);
200
201	  ss[fd] = s = alloc(sizeof(struct sd));
202	  if(!s) goto errout;
203	  bzero(s, sizeof(struct sd));
204
205	  if(inflateInit2(&(s->stream), -15) != Z_OK)
206	      goto errout;
207
208	  s->stream.next_in  = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
209	  if(!s->inbuf) {
210	      inflateEnd(&(s->stream));
211	      goto errout;
212	  }
213
214	  s->fd = fd;
215	  check_header(s); /* skip the .gz header */
216	  return(fd);
217
218errout:
219          if(s) free(s, sizeof(struct sd));
220          oclose(fd);
221	  return(-1);
222}
223
224int
225close(fd)
226	int fd;
227{
228	struct open_file *f;
229        struct sd *s;
230
231	if ((unsigned)fd >= SOPEN_MAX) {
232		errno = EBADF;
233		return (-1);
234	}
235	f = &files[fd];
236
237	if(!(f->f_flags & F_READ))
238		return(oclose(fd));
239
240	s = ss[fd];
241
242	inflateEnd(&(s->stream));
243
244	free(s->inbuf, Z_BUFSIZE);
245	free(s, sizeof(struct sd));
246
247	return(oclose(fd));
248}
249
250ssize_t
251read(fd, buf, len)
252	int fd;
253	void *buf;
254	size_t len;
255{
256	  struct sd *s;
257	  unsigned char *start = buf; /* starting point for crc computation */
258
259	  s = ss[fd];
260
261	  if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
262	  if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
263
264	  s->stream.next_out = buf;
265	  s->stream.avail_out = len;
266
267	  while (s->stream.avail_out != 0) {
268
269	    if (s->transparent) {
270	      /* Copy first the lookahead bytes: */
271	      unsigned int n = s->stream.avail_in;
272	      if (n > s->stream.avail_out) n = s->stream.avail_out;
273	      if (n > 0) {
274		zmemcpy(s->stream.next_out, s->stream.next_in, n);
275		s->stream.next_out += n;
276		s->stream.next_in   += n;
277		s->stream.avail_out -= n;
278		s->stream.avail_in  -= n;
279	      }
280	      if (s->stream.avail_out > 0) {
281		s->stream.avail_out -= oread(s->fd, s->stream.next_out, s->stream.avail_out);
282	      }
283	      return (int)(len - s->stream.avail_out);
284	    }
285
286	    if (s->stream.avail_in == 0 && !s->z_eof) {
287
288	      errno = 0;
289	      s->stream.avail_in = oread(fd, s->inbuf, Z_BUFSIZE);
290	      if (s->stream.avail_in == 0) {
291		s->z_eof = 1;
292		if (errno) {
293		  s->z_err = Z_ERRNO;
294		  break;
295		}
296	      }
297	      s->stream.next_in = s->inbuf;
298	    }
299	    s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
300
301	    if (s->z_err == Z_STREAM_END) {
302	      /* Check CRC and original size */
303	      s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
304	      start = s->stream.next_out;
305
306	      if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
307		s->z_err = Z_DATA_ERROR;
308	      } else {
309		/* Check for concatenated .gz files: */
310		check_header(s);
311		if (s->z_err == Z_OK) {
312		  inflateReset(&(s->stream));
313		  s->crc = crc32(0L, Z_NULL, 0);
314		}
315	      }
316	    }
317	    if (s->z_err != Z_OK || s->z_eof) break;
318	  }
319	  s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
320
321	  return (int)(len - s->stream.avail_out);
322}
323
324off_t
325lseek(fd, offset, where)
326	int fd;
327	off_t offset;
328	int where;
329{
330	    register struct open_file *f;
331	    struct sd *s;
332
333	    if ((unsigned)fd >= SOPEN_MAX) {
334		errno = EBADF;
335		return (-1);
336	    }
337	    f = &files[fd];;
338
339	    if(!(f->f_flags & F_READ))
340		return(olseek(fd, offset, where));
341
342	    s = ss[fd];
343
344	    if(s->transparent) {
345		off_t res = olseek(fd, offset, where);
346		if(res != (off_t)-1) {
347		    /* make sure the lookahead buffer is invalid */
348		    s->stream.avail_in = 0;
349		}
350		return(res);
351	    }
352
353	    switch(where) {
354		case SEEK_CUR:
355		    offset += s->stream.total_out;
356		case SEEK_SET:
357
358		    /* if seek backwards, simply start from
359		     the beginning */
360		    if(offset < s->stream.total_out) {
361			off_t res;
362			void *sav_inbuf;
363
364			res = olseek(fd, 0, SEEK_SET);
365			if(res == (off_t)-1)
366			    return(res);
367			/* ??? perhaps fallback to close / open */
368
369			inflateEnd(&(s->stream));
370
371			sav_inbuf = s->inbuf; /* don't allocate again */
372			bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
373
374			inflateInit2(&(s->stream), -15);
375			s->stream.next_in = s->inbuf = sav_inbuf;
376
377			s->fd = fd;
378			check_header(s); /* skip the .gz header */
379		    }
380
381		    /* to seek forwards, throw away data */
382		    if(offset > s->stream.total_out) {
383			off_t toskip = offset - s->stream.total_out;
384
385			while(toskip > 0) {
386#define DUMMYBUFSIZE 256
387			    char dummybuf[DUMMYBUFSIZE];
388			    off_t len = toskip;
389			    if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
390			    if(read(fd, dummybuf, len) != len) {
391				errno = EOFFSET;
392				return((off_t)-1);
393			    }
394			    toskip -= len;
395			}
396		    }
397#ifdef DEBUG
398		    if(offset != s->stream.total_out)
399			panic("lseek compressed");
400#endif
401		    return(offset);
402		case SEEK_END:
403		    errno = EOFFSET;
404		    break;
405		default:
406		    errno = EINVAL;
407	    }
408	    return((off_t)-1);
409}
410