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