Home | History | Annotate | Line # | Download | only in libsa
cread.c revision 1.6
      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