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