Home | History | Annotate | Line # | Download | only in libsa
cread.c revision 1.6
      1  1.6       cjs /*	$NetBSD: cread.c,v 1.6 1997/10/18 22:27:15 cjs Exp $	*/
      2  1.1       cgd 
      3  1.1       cgd /*
      4  1.1       cgd  * Copyright (c) 1996
      5  1.1       cgd  *	Matthias Drochner.  All rights reserved.
      6  1.1       cgd  *
      7  1.1       cgd  * Redistribution and use in source and binary forms, with or without
      8  1.1       cgd  * modification, are permitted provided that the following conditions
      9  1.1       cgd  * are met:
     10  1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     11  1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     12  1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     13  1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     14  1.1       cgd  *    documentation and/or other materials provided with the distribution.
     15  1.1       cgd  * 3. All advertising materials mentioning features or use of this software
     16  1.1       cgd  *    must display the following acknowledgement:
     17  1.1       cgd  *	This product includes software developed for the NetBSD Project
     18  1.1       cgd  *	by Matthias Drochner.
     19  1.1       cgd  * 4. The name of the author may not be used to endorse or promote products
     20  1.1       cgd  *    derived from this software without specific prior written permission.
     21  1.1       cgd  *
     22  1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     23  1.1       cgd  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     24  1.1       cgd  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     25  1.1       cgd  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     26  1.1       cgd  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     27  1.1       cgd  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     28  1.1       cgd  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     29  1.1       cgd  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     30  1.1       cgd  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     31  1.1       cgd  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     32  1.1       cgd  *
     33  1.1       cgd  */
     34  1.1       cgd 
     35  1.1       cgd /* support for compressed bootfiles
     36  1.1       cgd  (only read)
     37  1.1       cgd  replaces open(), close(), read(), lseek().
     38  1.1       cgd  original libsa open(), close(), read(), lseek() are called
     39  1.1       cgd  as oopen(), oclose(), oread() resp. olseek().
     40  1.1       cgd  compression parts stripped from zlib:gzio.c
     41  1.1       cgd  */
     42  1.1       cgd 
     43  1.1       cgd /* gzio.c -- IO on .gz files
     44  1.1       cgd  * Copyright (C) 1995-1996 Jean-loup Gailly.
     45  1.1       cgd  * For conditions of distribution and use, see copyright notice in zlib.h
     46  1.1       cgd  */
     47  1.1       cgd 
     48  1.4  drochner #include "stand.h"
     49  1.4  drochner #ifdef _STANDALONE
     50  1.3  drochner #include <lib/libkern/libkern.h>
     51  1.3  drochner #include <lib/libz/zlib.h>
     52  1.4  drochner #else
     53  1.4  drochner #include <string.h>
     54  1.4  drochner #include <zlib.h>
     55  1.4  drochner #endif
     56  1.1       cgd 
     57  1.1       cgd #define EOF (-1) /* needed by compression code */
     58  1.1       cgd 
     59  1.1       cgd #ifdef SAVE_MEMORY
     60  1.1       cgd #define Z_BUFSIZE 1024
     61  1.1       cgd #else
     62  1.1       cgd #define Z_BUFSIZE 4096
     63  1.1       cgd #endif
     64  1.1       cgd 
     65  1.1       cgd static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
     66  1.1       cgd 
     67  1.1       cgd /* gzip flag byte */
     68  1.1       cgd #define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
     69  1.1       cgd #define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
     70  1.1       cgd #define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
     71  1.1       cgd #define ORIG_NAME    0x08 /* bit 3 set: original file name present */
     72  1.1       cgd #define COMMENT      0x10 /* bit 4 set: file comment present */
     73  1.1       cgd #define RESERVED     0xE0 /* bits 5..7: reserved */
     74  1.1       cgd 
     75  1.1       cgd static struct sd {
     76  1.1       cgd   z_stream stream;
     77  1.1       cgd   int      z_err;   /* error code for last stream operation */
     78  1.1       cgd   int      z_eof;   /* set if end of input file */
     79  1.1       cgd   int fd;
     80  1.1       cgd   unsigned char     *inbuf;  /* input buffer */
     81  1.1       cgd   unsigned long    crc;     /* crc32 of uncompressed data */
     82  1.6       cjs   int      compressed; /* 1 if input file is a .gz file */
     83  1.1       cgd } *ss[SOPEN_MAX];
     84  1.1       cgd 
     85  1.1       cgd /*
     86  1.1       cgd  * compression utilities
     87  1.1       cgd  */
     88  1.1       cgd 
     89  1.1       cgd void *zcalloc (opaque, items, size)
     90  1.1       cgd void *opaque;
     91  1.1       cgd unsigned items;
     92  1.1       cgd unsigned size;
     93  1.1       cgd {
     94  1.1       cgd   return(alloc(items * size));
     95  1.1       cgd }
     96  1.1       cgd 
     97  1.1       cgd void  zcfree (opaque, ptr)
     98  1.1       cgd void *opaque;
     99  1.1       cgd void *ptr;
    100  1.1       cgd {
    101  1.1       cgd   free(ptr, 0); /* XXX works only with modified allocator */
    102  1.1       cgd }
    103  1.1       cgd 
    104  1.1       cgd void zmemcpy(dest, source, len)
    105  1.1       cgd unsigned char *dest;
    106  1.1       cgd unsigned char *source;
    107  1.1       cgd unsigned int len;
    108  1.1       cgd {
    109  1.1       cgd   bcopy(source, dest, len);
    110  1.1       cgd }
    111  1.1       cgd 
    112  1.1       cgd static int get_byte(s)
    113  1.1       cgd     struct sd *s;
    114  1.1       cgd {
    115  1.1       cgd     if (s->z_eof) return EOF;
    116  1.1       cgd     if (s->stream.avail_in == 0) {
    117  1.5  drochner 	int got;
    118  1.1       cgd 	errno = 0;
    119  1.5  drochner 	got = oread(s->fd, s->inbuf, Z_BUFSIZE);
    120  1.5  drochner 	if (got <= 0) {
    121  1.1       cgd 	    s->z_eof = 1;
    122  1.1       cgd 	    if (errno) s->z_err = Z_ERRNO;
    123  1.1       cgd 	    return EOF;
    124  1.1       cgd 	}
    125  1.5  drochner 	s->stream.avail_in = got;
    126  1.1       cgd 	s->stream.next_in = s->inbuf;
    127  1.1       cgd     }
    128  1.1       cgd     s->stream.avail_in--;
    129  1.1       cgd     return *(s->stream.next_in)++;
    130  1.1       cgd }
    131  1.1       cgd 
    132  1.1       cgd static unsigned long getLong (s)
    133  1.1       cgd     struct sd *s;
    134  1.1       cgd {
    135  1.1       cgd     unsigned long x = (unsigned long)get_byte(s);
    136  1.1       cgd     int c;
    137  1.1       cgd 
    138  1.1       cgd     x += ((unsigned long)get_byte(s))<<8;
    139  1.1       cgd     x += ((unsigned long)get_byte(s))<<16;
    140  1.1       cgd     c = get_byte(s);
    141  1.1       cgd     if (c == EOF) s->z_err = Z_DATA_ERROR;
    142  1.1       cgd     x += ((unsigned long)c)<<24;
    143  1.1       cgd     return x;
    144  1.1       cgd }
    145  1.1       cgd 
    146  1.1       cgd static void check_header(s)
    147  1.1       cgd     struct sd *s;
    148  1.1       cgd {
    149  1.1       cgd     int method; /* method byte */
    150  1.1       cgd     int flags;  /* flags byte */
    151  1.1       cgd     unsigned int len;
    152  1.1       cgd     int c;
    153  1.1       cgd 
    154  1.1       cgd     /* Check the gzip magic header */
    155  1.1       cgd     for (len = 0; len < 2; len++) {
    156  1.1       cgd 	c = get_byte(s);
    157  1.1       cgd 	if (c != gz_magic[len]) {
    158  1.6       cjs 	    if ((c == EOF) && (len == 0))  {
    159  1.6       cjs 		/*
    160  1.6       cjs 		 * We must not change s->compressed if we are at EOF;
    161  1.6       cjs 		 * we may have come to the end of a gzipped file and be
    162  1.6       cjs 		 * check to see if another gzipped file is concatenated
    163  1.6       cjs 		 * to this one. If one isn't, we still need to be able
    164  1.6       cjs 		 * to lseek on this file as a compressed file.
    165  1.6       cjs 		 */
    166  1.6       cjs 		return;
    167  1.6       cjs 	    }
    168  1.6       cjs 	    s->compressed = 0;
    169  1.1       cgd 	    if (c != EOF) s->stream.avail_in++, s->stream.next_in--;
    170  1.1       cgd 	    s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
    171  1.1       cgd 	    return;
    172  1.1       cgd 	}
    173  1.1       cgd     }
    174  1.6       cjs     s->compressed = 1;
    175  1.1       cgd     method = get_byte(s);
    176  1.1       cgd     flags = get_byte(s);
    177  1.1       cgd     if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
    178  1.1       cgd 	s->z_err = Z_DATA_ERROR;
    179  1.1       cgd 	return;
    180  1.1       cgd     }
    181  1.1       cgd 
    182  1.1       cgd     /* Discard time, xflags and OS code: */
    183  1.1       cgd     for (len = 0; len < 6; len++) (void)get_byte(s);
    184  1.1       cgd 
    185  1.1       cgd     if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
    186  1.1       cgd 	len  =  (unsigned int)get_byte(s);
    187  1.1       cgd 	len += ((unsigned int)get_byte(s))<<8;
    188  1.1       cgd 	/* len is garbage if EOF but the loop below will quit anyway */
    189  1.1       cgd 	while (len-- != 0 && get_byte(s) != EOF) ;
    190  1.1       cgd     }
    191  1.1       cgd     if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
    192  1.1       cgd 	while ((c = get_byte(s)) != 0 && c != EOF) ;
    193  1.1       cgd     }
    194  1.1       cgd     if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
    195  1.1       cgd 	while ((c = get_byte(s)) != 0 && c != EOF) ;
    196  1.1       cgd     }
    197  1.1       cgd     if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
    198  1.1       cgd 	for (len = 0; len < 2; len++) (void)get_byte(s);
    199  1.1       cgd     }
    200  1.1       cgd     s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
    201  1.1       cgd }
    202  1.1       cgd 
    203  1.1       cgd /*
    204  1.1       cgd  * new open(), close(), read(), lseek()
    205  1.1       cgd  */
    206  1.1       cgd 
    207  1.1       cgd int
    208  1.1       cgd open(fname, mode)
    209  1.1       cgd 	const char *fname;
    210  1.1       cgd 	int mode;
    211  1.1       cgd {
    212  1.1       cgd 	  int fd;
    213  1.1       cgd 	  struct sd *s = 0;
    214  1.1       cgd 
    215  1.1       cgd 	  if(((fd = oopen(fname, mode)) == -1)
    216  1.1       cgd 	     || (mode != 0)) /* compression only for read */
    217  1.1       cgd 	  return(fd);
    218  1.1       cgd 
    219  1.1       cgd 	  ss[fd] = s = alloc(sizeof(struct sd));
    220  1.1       cgd 	  if(!s) goto errout;
    221  1.1       cgd 	  bzero(s, sizeof(struct sd));
    222  1.1       cgd 
    223  1.1       cgd 	  if(inflateInit2(&(s->stream), -15) != Z_OK)
    224  1.1       cgd 	      goto errout;
    225  1.1       cgd 
    226  1.1       cgd 	  s->stream.next_in  = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
    227  1.1       cgd 	  if(!s->inbuf) {
    228  1.1       cgd 	      inflateEnd(&(s->stream));
    229  1.1       cgd 	      goto errout;
    230  1.1       cgd 	  }
    231  1.1       cgd 
    232  1.1       cgd 	  s->fd = fd;
    233  1.1       cgd 	  check_header(s); /* skip the .gz header */
    234  1.1       cgd 	  return(fd);
    235  1.1       cgd 
    236  1.1       cgd errout:
    237  1.1       cgd           if(s) free(s, sizeof(struct sd));
    238  1.1       cgd           oclose(fd);
    239  1.1       cgd 	  return(-1);
    240  1.1       cgd }
    241  1.1       cgd 
    242  1.1       cgd int
    243  1.1       cgd close(fd)
    244  1.1       cgd 	int fd;
    245  1.1       cgd {
    246  1.2   thorpej 	struct open_file *f;
    247  1.1       cgd         struct sd *s;
    248  1.2   thorpej 
    249  1.2   thorpej 	if ((unsigned)fd >= SOPEN_MAX) {
    250  1.2   thorpej 		errno = EBADF;
    251  1.2   thorpej 		return (-1);
    252  1.2   thorpej 	}
    253  1.2   thorpej 	f = &files[fd];
    254  1.2   thorpej 
    255  1.2   thorpej 	if(!(f->f_flags & F_READ))
    256  1.2   thorpej 		return(oclose(fd));
    257  1.1       cgd 
    258  1.1       cgd 	s = ss[fd];
    259  1.1       cgd 
    260  1.1       cgd 	inflateEnd(&(s->stream));
    261  1.1       cgd 
    262  1.1       cgd 	free(s->inbuf, Z_BUFSIZE);
    263  1.1       cgd 	free(s, sizeof(struct sd));
    264  1.1       cgd 
    265  1.1       cgd 	return(oclose(fd));
    266  1.1       cgd }
    267  1.1       cgd 
    268  1.1       cgd ssize_t
    269  1.1       cgd read(fd, buf, len)
    270  1.1       cgd 	int fd;
    271  1.1       cgd 	void *buf;
    272  1.1       cgd 	size_t len;
    273  1.1       cgd {
    274  1.1       cgd 	  struct sd *s;
    275  1.1       cgd 	  unsigned char *start = buf; /* starting point for crc computation */
    276  1.1       cgd 
    277  1.1       cgd 	  s = ss[fd];
    278  1.1       cgd 
    279  1.1       cgd 	  if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
    280  1.1       cgd 	  if (s->z_err == Z_STREAM_END) return 0;  /* EOF */
    281  1.1       cgd 
    282  1.1       cgd 	  s->stream.next_out = buf;
    283  1.1       cgd 	  s->stream.avail_out = len;
    284  1.1       cgd 
    285  1.1       cgd 	  while (s->stream.avail_out != 0) {
    286  1.1       cgd 
    287  1.6       cjs 	    if (s->compressed == 0) {
    288  1.1       cgd 	      /* Copy first the lookahead bytes: */
    289  1.1       cgd 	      unsigned int n = s->stream.avail_in;
    290  1.1       cgd 	      if (n > s->stream.avail_out) n = s->stream.avail_out;
    291  1.1       cgd 	      if (n > 0) {
    292  1.1       cgd 		zmemcpy(s->stream.next_out, s->stream.next_in, n);
    293  1.1       cgd 		s->stream.next_out += n;
    294  1.1       cgd 		s->stream.next_in   += n;
    295  1.1       cgd 		s->stream.avail_out -= n;
    296  1.1       cgd 		s->stream.avail_in  -= n;
    297  1.1       cgd 	      }
    298  1.1       cgd 	      if (s->stream.avail_out > 0) {
    299  1.5  drochner 		  int got;
    300  1.5  drochner 		  got = oread(s->fd, s->stream.next_out, s->stream.avail_out);
    301  1.5  drochner 		  if(got == -1)
    302  1.5  drochner 			  return(got);
    303  1.5  drochner 		  s->stream.avail_out -= got;
    304  1.1       cgd 	      }
    305  1.1       cgd 	      return (int)(len - s->stream.avail_out);
    306  1.1       cgd 	    }
    307  1.1       cgd 
    308  1.1       cgd 	    if (s->stream.avail_in == 0 && !s->z_eof) {
    309  1.5  drochner 	      int got;
    310  1.1       cgd 	      errno = 0;
    311  1.5  drochner 	      got = oread(fd, s->inbuf, Z_BUFSIZE);
    312  1.5  drochner 	      if (got <= 0) {
    313  1.1       cgd 		s->z_eof = 1;
    314  1.1       cgd 		if (errno) {
    315  1.1       cgd 		  s->z_err = Z_ERRNO;
    316  1.1       cgd 		  break;
    317  1.1       cgd 		}
    318  1.1       cgd 	      }
    319  1.5  drochner 	      s->stream.avail_in = got;
    320  1.1       cgd 	      s->stream.next_in = s->inbuf;
    321  1.1       cgd 	    }
    322  1.1       cgd 	    s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
    323  1.1       cgd 
    324  1.1       cgd 	    if (s->z_err == Z_STREAM_END) {
    325  1.1       cgd 	      /* Check CRC and original size */
    326  1.1       cgd 	      s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
    327  1.1       cgd 	      start = s->stream.next_out;
    328  1.1       cgd 
    329  1.1       cgd 	      if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
    330  1.1       cgd 		s->z_err = Z_DATA_ERROR;
    331  1.1       cgd 	      } else {
    332  1.1       cgd 		/* Check for concatenated .gz files: */
    333  1.1       cgd 		check_header(s);
    334  1.1       cgd 		if (s->z_err == Z_OK) {
    335  1.1       cgd 		  inflateReset(&(s->stream));
    336  1.1       cgd 		  s->crc = crc32(0L, Z_NULL, 0);
    337  1.1       cgd 		}
    338  1.1       cgd 	      }
    339  1.1       cgd 	    }
    340  1.1       cgd 	    if (s->z_err != Z_OK || s->z_eof) break;
    341  1.1       cgd 	  }
    342  1.1       cgd 	  s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
    343  1.1       cgd 
    344  1.1       cgd 	  return (int)(len - s->stream.avail_out);
    345  1.1       cgd }
    346  1.1       cgd 
    347  1.1       cgd off_t
    348  1.1       cgd lseek(fd, offset, where)
    349  1.1       cgd 	int fd;
    350  1.1       cgd 	off_t offset;
    351  1.1       cgd 	int where;
    352  1.1       cgd {
    353  1.1       cgd 	    register struct open_file *f;
    354  1.1       cgd 	    struct sd *s;
    355  1.1       cgd 
    356  1.1       cgd 	    if ((unsigned)fd >= SOPEN_MAX) {
    357  1.1       cgd 		errno = EBADF;
    358  1.1       cgd 		return (-1);
    359  1.1       cgd 	    }
    360  1.1       cgd 	    f = &files[fd];;
    361  1.1       cgd 
    362  1.1       cgd 	    if(!(f->f_flags & F_READ))
    363  1.1       cgd 		return(olseek(fd, offset, where));
    364  1.1       cgd 
    365  1.1       cgd 	    s = ss[fd];
    366  1.1       cgd 
    367  1.6       cjs 	    if(s->compressed == 0) {
    368  1.1       cgd 		off_t res = olseek(fd, offset, where);
    369  1.1       cgd 		if(res != (off_t)-1) {
    370  1.1       cgd 		    /* make sure the lookahead buffer is invalid */
    371  1.1       cgd 		    s->stream.avail_in = 0;
    372  1.1       cgd 		}
    373  1.1       cgd 		return(res);
    374  1.1       cgd 	    }
    375  1.1       cgd 
    376  1.1       cgd 	    switch(where) {
    377  1.1       cgd 		case SEEK_CUR:
    378  1.1       cgd 		    offset += s->stream.total_out;
    379  1.1       cgd 		case SEEK_SET:
    380  1.1       cgd 
    381  1.1       cgd 		    /* if seek backwards, simply start from
    382  1.1       cgd 		     the beginning */
    383  1.1       cgd 		    if(offset < s->stream.total_out) {
    384  1.1       cgd 			off_t res;
    385  1.1       cgd 			void *sav_inbuf;
    386  1.1       cgd 
    387  1.1       cgd 			res = olseek(fd, 0, SEEK_SET);
    388  1.1       cgd 			if(res == (off_t)-1)
    389  1.1       cgd 			    return(res);
    390  1.1       cgd 			/* ??? perhaps fallback to close / open */
    391  1.1       cgd 
    392  1.1       cgd 			inflateEnd(&(s->stream));
    393  1.1       cgd 
    394  1.1       cgd 			sav_inbuf = s->inbuf; /* don't allocate again */
    395  1.1       cgd 			bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
    396  1.1       cgd 
    397  1.1       cgd 			inflateInit2(&(s->stream), -15);
    398  1.1       cgd 			s->stream.next_in = s->inbuf = sav_inbuf;
    399  1.1       cgd 
    400  1.1       cgd 			s->fd = fd;
    401  1.1       cgd 			check_header(s); /* skip the .gz header */
    402  1.1       cgd 		    }
    403  1.1       cgd 
    404  1.1       cgd 		    /* to seek forwards, throw away data */
    405  1.1       cgd 		    if(offset > s->stream.total_out) {
    406  1.1       cgd 			off_t toskip = offset - s->stream.total_out;
    407  1.1       cgd 
    408  1.1       cgd 			while(toskip > 0) {
    409  1.1       cgd #define DUMMYBUFSIZE 256
    410  1.1       cgd 			    char dummybuf[DUMMYBUFSIZE];
    411  1.1       cgd 			    off_t len = toskip;
    412  1.1       cgd 			    if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
    413  1.1       cgd 			    if(read(fd, dummybuf, len) != len) {
    414  1.1       cgd 				errno = EOFFSET;
    415  1.1       cgd 				return((off_t)-1);
    416  1.1       cgd 			    }
    417  1.1       cgd 			    toskip -= len;
    418  1.1       cgd 			}
    419  1.1       cgd 		    }
    420  1.1       cgd #ifdef DEBUG
    421  1.1       cgd 		    if(offset != s->stream.total_out)
    422  1.1       cgd 			panic("lseek compressed");
    423  1.1       cgd #endif
    424  1.1       cgd 		    return(offset);
    425  1.1       cgd 		case SEEK_END:
    426  1.1       cgd 		    errno = EOFFSET;
    427  1.1       cgd 		    break;
    428  1.1       cgd 		default:
    429  1.1       cgd 		    errno = EINVAL;
    430  1.1       cgd 	    }
    431  1.1       cgd 	    return((off_t)-1);
    432  1.1       cgd }
    433