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