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