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