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