Home | History | Annotate | Line # | Download | only in libsa
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 
     60 static 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 
     70 static 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 
     84 void *zcalloc (opaque, items, size)
     85 void *opaque;
     86 unsigned items;
     87 unsigned size;
     88 {
     89   return(alloc(items * size));
     90 }
     91 
     92 void  zcfree (opaque, ptr)
     93 void *opaque;
     94 void *ptr;
     95 {
     96   free(ptr, 0); /* XXX works only with modified allocator */
     97 }
     98 
     99 void zmemcpy(dest, source, len)
    100 unsigned char *dest;
    101 unsigned char *source;
    102 unsigned int len;
    103 {
    104   bcopy(source, dest, len);
    105 }
    106 
    107 static 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 
    125 static 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 
    139 static 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 
    189 int
    190 open(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 
    218 errout:
    219           if(s) free(s, sizeof(struct sd));
    220           oclose(fd);
    221 	  return(-1);
    222 }
    223 
    224 int
    225 close(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 
    250 ssize_t
    251 read(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 
    324 off_t
    325 lseek(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