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