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