xref: /netbsd-src/sys/lib/libsa/cread.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: cread.c,v 1.6 1997/10/18 22:27:15 cjs 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      compressed; /* 1 if input file is 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 	    if ((c == EOF) && (len == 0))  {
159 		/*
160 		 * We must not change s->compressed if we are at EOF;
161 		 * we may have come to the end of a gzipped file and be
162 		 * check to see if another gzipped file is concatenated
163 		 * to this one. If one isn't, we still need to be able
164 		 * to lseek on this file as a compressed file.
165 		 */
166 		return;
167 	    }
168 	    s->compressed = 0;
169 	    if (c != EOF) s->stream.avail_in++, s->stream.next_in--;
170 	    s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
171 	    return;
172 	}
173     }
174     s->compressed = 1;
175     method = get_byte(s);
176     flags = get_byte(s);
177     if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
178 	s->z_err = Z_DATA_ERROR;
179 	return;
180     }
181 
182     /* Discard time, xflags and OS code: */
183     for (len = 0; len < 6; len++) (void)get_byte(s);
184 
185     if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
186 	len  =  (unsigned int)get_byte(s);
187 	len += ((unsigned int)get_byte(s))<<8;
188 	/* len is garbage if EOF but the loop below will quit anyway */
189 	while (len-- != 0 && get_byte(s) != EOF) ;
190     }
191     if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
192 	while ((c = get_byte(s)) != 0 && c != EOF) ;
193     }
194     if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
195 	while ((c = get_byte(s)) != 0 && c != EOF) ;
196     }
197     if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
198 	for (len = 0; len < 2; len++) (void)get_byte(s);
199     }
200     s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
201 }
202 
203 /*
204  * new open(), close(), read(), lseek()
205  */
206 
207 int
208 open(fname, mode)
209 	const char *fname;
210 	int mode;
211 {
212 	  int fd;
213 	  struct sd *s = 0;
214 
215 	  if(((fd = oopen(fname, mode)) == -1)
216 	     || (mode != 0)) /* compression only for read */
217 	  return(fd);
218 
219 	  ss[fd] = s = alloc(sizeof(struct sd));
220 	  if(!s) goto errout;
221 	  bzero(s, sizeof(struct sd));
222 
223 	  if(inflateInit2(&(s->stream), -15) != Z_OK)
224 	      goto errout;
225 
226 	  s->stream.next_in  = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
227 	  if(!s->inbuf) {
228 	      inflateEnd(&(s->stream));
229 	      goto errout;
230 	  }
231 
232 	  s->fd = fd;
233 	  check_header(s); /* skip the .gz header */
234 	  return(fd);
235 
236 errout:
237           if(s) free(s, sizeof(struct sd));
238           oclose(fd);
239 	  return(-1);
240 }
241 
242 int
243 close(fd)
244 	int fd;
245 {
246 	struct open_file *f;
247         struct sd *s;
248 
249 	if ((unsigned)fd >= SOPEN_MAX) {
250 		errno = EBADF;
251 		return (-1);
252 	}
253 	f = &files[fd];
254 
255 	if(!(f->f_flags & F_READ))
256 		return(oclose(fd));
257 
258 	s = ss[fd];
259 
260 	inflateEnd(&(s->stream));
261 
262 	free(s->inbuf, Z_BUFSIZE);
263 	free(s, sizeof(struct sd));
264 
265 	return(oclose(fd));
266 }
267 
268 ssize_t
269 read(fd, buf, len)
270 	int fd;
271 	void *buf;
272 	size_t len;
273 {
274 	  struct sd *s;
275 	  unsigned char *start = buf; /* starting point for crc computation */
276 
277 	  s = ss[fd];
278 
279 	  if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
280 	  if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
281 
282 	  s->stream.next_out = buf;
283 	  s->stream.avail_out = len;
284 
285 	  while (s->stream.avail_out != 0) {
286 
287 	    if (s->compressed == 0) {
288 	      /* Copy first the lookahead bytes: */
289 	      unsigned int n = s->stream.avail_in;
290 	      if (n > s->stream.avail_out) n = s->stream.avail_out;
291 	      if (n > 0) {
292 		zmemcpy(s->stream.next_out, s->stream.next_in, n);
293 		s->stream.next_out += n;
294 		s->stream.next_in   += n;
295 		s->stream.avail_out -= n;
296 		s->stream.avail_in  -= n;
297 	      }
298 	      if (s->stream.avail_out > 0) {
299 		  int got;
300 		  got = oread(s->fd, s->stream.next_out, s->stream.avail_out);
301 		  if(got == -1)
302 			  return(got);
303 		  s->stream.avail_out -= got;
304 	      }
305 	      return (int)(len - s->stream.avail_out);
306 	    }
307 
308 	    if (s->stream.avail_in == 0 && !s->z_eof) {
309 	      int got;
310 	      errno = 0;
311 	      got = oread(fd, s->inbuf, Z_BUFSIZE);
312 	      if (got <= 0) {
313 		s->z_eof = 1;
314 		if (errno) {
315 		  s->z_err = Z_ERRNO;
316 		  break;
317 		}
318 	      }
319 	      s->stream.avail_in = got;
320 	      s->stream.next_in = s->inbuf;
321 	    }
322 	    s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
323 
324 	    if (s->z_err == Z_STREAM_END) {
325 	      /* Check CRC and original size */
326 	      s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
327 	      start = s->stream.next_out;
328 
329 	      if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
330 		s->z_err = Z_DATA_ERROR;
331 	      } else {
332 		/* Check for concatenated .gz files: */
333 		check_header(s);
334 		if (s->z_err == Z_OK) {
335 		  inflateReset(&(s->stream));
336 		  s->crc = crc32(0L, Z_NULL, 0);
337 		}
338 	      }
339 	    }
340 	    if (s->z_err != Z_OK || s->z_eof) break;
341 	  }
342 	  s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
343 
344 	  return (int)(len - s->stream.avail_out);
345 }
346 
347 off_t
348 lseek(fd, offset, where)
349 	int fd;
350 	off_t offset;
351 	int where;
352 {
353 	    register struct open_file *f;
354 	    struct sd *s;
355 
356 	    if ((unsigned)fd >= SOPEN_MAX) {
357 		errno = EBADF;
358 		return (-1);
359 	    }
360 	    f = &files[fd];;
361 
362 	    if(!(f->f_flags & F_READ))
363 		return(olseek(fd, offset, where));
364 
365 	    s = ss[fd];
366 
367 	    if(s->compressed == 0) {
368 		off_t res = olseek(fd, offset, where);
369 		if(res != (off_t)-1) {
370 		    /* make sure the lookahead buffer is invalid */
371 		    s->stream.avail_in = 0;
372 		}
373 		return(res);
374 	    }
375 
376 	    switch(where) {
377 		case SEEK_CUR:
378 		    offset += s->stream.total_out;
379 		case SEEK_SET:
380 
381 		    /* if seek backwards, simply start from
382 		     the beginning */
383 		    if(offset < s->stream.total_out) {
384 			off_t res;
385 			void *sav_inbuf;
386 
387 			res = olseek(fd, 0, SEEK_SET);
388 			if(res == (off_t)-1)
389 			    return(res);
390 			/* ??? perhaps fallback to close / open */
391 
392 			inflateEnd(&(s->stream));
393 
394 			sav_inbuf = s->inbuf; /* don't allocate again */
395 			bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
396 
397 			inflateInit2(&(s->stream), -15);
398 			s->stream.next_in = s->inbuf = sav_inbuf;
399 
400 			s->fd = fd;
401 			check_header(s); /* skip the .gz header */
402 		    }
403 
404 		    /* to seek forwards, throw away data */
405 		    if(offset > s->stream.total_out) {
406 			off_t toskip = offset - s->stream.total_out;
407 
408 			while(toskip > 0) {
409 #define DUMMYBUFSIZE 256
410 			    char dummybuf[DUMMYBUFSIZE];
411 			    off_t len = toskip;
412 			    if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
413 			    if(read(fd, dummybuf, len) != len) {
414 				errno = EOFFSET;
415 				return((off_t)-1);
416 			    }
417 			    toskip -= len;
418 			}
419 		    }
420 #ifdef DEBUG
421 		    if(offset != s->stream.total_out)
422 			panic("lseek compressed");
423 #endif
424 		    return(offset);
425 		case SEEK_END:
426 		    errno = EOFFSET;
427 		    break;
428 		default:
429 		    errno = EINVAL;
430 	    }
431 	    return((off_t)-1);
432 }
433