1 /* $NetBSD: cread.c,v 1.23 2009/03/25 18:41:06 tls 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 60 static 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 70 static 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 80 static int get_byte(struct sd *); 81 static unsigned long getLong(struct sd *); 82 static void check_header(struct sd *); 83 84 /* XXX - find suitable header file for these: */ 85 void *zcalloc(void *, unsigned int, unsigned int); 86 void zcfree(void *, void *); 87 void 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 98 uint32_t 99 crc32(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 123 void * 124 zcalloc(void *opaque, unsigned int items, unsigned int size) 125 { 126 127 return alloc(items * size); 128 } 129 130 void 131 zcfree(void *opaque, void *ptr) 132 { 133 134 dealloc(ptr, 0); /* XXX works only with modified allocator */ 135 } 136 137 void 138 zmemcpy(unsigned char *dest, unsigned char *source, unsigned int len) 139 { 140 141 memcpy(dest, source, len); 142 } 143 144 static int 145 get_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 168 static unsigned long 169 getLong(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 184 static void 185 check_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 256 int 257 open(const char *fname, int mode) 258 { 259 int fd; 260 struct sd *s = 0; 261 262 if (((fd = oopen(fname, mode)) == -1) || (mode != 0)) 263 /* compression only for read */ 264 return fd; 265 266 ss[fd] = s = alloc(sizeof(struct sd)); 267 if (s == 0) 268 goto errout; 269 (void)memset(s, 0, sizeof(struct sd)); 270 271 if (inflateInit2(&(s->stream), -15) != Z_OK) 272 goto errout; 273 274 s->stream.next_in = s->inbuf = (unsigned char *)alloc(Z_BUFSIZE); 275 if (s->inbuf == 0) { 276 inflateEnd(&(s->stream)); 277 goto errout; 278 } 279 280 s->fd = fd; 281 check_header(s); /* skip the .gz header */ 282 return fd; 283 284 errout: 285 if (s != 0) 286 dealloc(s, sizeof(struct sd)); 287 oclose(fd); 288 return -1; 289 } 290 291 int 292 close(int fd) 293 { 294 struct open_file *f; 295 struct sd *s; 296 297 #if !defined(LIBSA_NO_FD_CHECKING) 298 if ((unsigned int)fd >= SOPEN_MAX) { 299 errno = EBADF; 300 return -1; 301 } 302 #endif 303 f = &files[fd]; 304 305 if ((f->f_flags & F_READ) == 0) 306 return oclose(fd); 307 308 s = ss[fd]; 309 310 inflateEnd(&(s->stream)); 311 312 dealloc(s->inbuf, Z_BUFSIZE); 313 dealloc(s, sizeof(struct sd)); 314 315 return oclose(fd); 316 } 317 318 ssize_t 319 read(int fd, void *buf, size_t len) 320 { 321 struct sd *s; 322 unsigned char *start = buf; /* starting point for crc computation */ 323 324 s = ss[fd]; 325 326 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) 327 return -1; 328 if (s->z_err == Z_STREAM_END) 329 return 0; /* EOF */ 330 331 s->stream.next_out = buf; 332 s->stream.avail_out = len; 333 334 while (s->stream.avail_out != 0) { 335 336 if (s->compressed == 0) { 337 /* Copy first the lookahead bytes: */ 338 unsigned int n = s->stream.avail_in; 339 if (n > s->stream.avail_out) 340 n = s->stream.avail_out; 341 if (n > 0) { 342 zmemcpy(s->stream.next_out, 343 s->stream.next_in, n); 344 s->stream.next_out += n; 345 s->stream.next_in += n; 346 s->stream.avail_out -= n; 347 s->stream.avail_in -= n; 348 } 349 if (s->stream.avail_out > 0) { 350 int got; 351 got = oread(s->fd, s->stream.next_out, 352 s->stream.avail_out); 353 if (got == -1) 354 return got; 355 s->stream.avail_out -= got; 356 } 357 return (int)(len - s->stream.avail_out); 358 } 359 360 if (s->stream.avail_in == 0 && !s->z_eof) { 361 int got; 362 errno = 0; 363 got = oread(fd, s->inbuf, Z_BUFSIZE); 364 if (got <= 0) { 365 s->z_eof = 1; 366 if (errno) { 367 s->z_err = Z_ERRNO; 368 break; 369 } 370 } 371 s->stream.avail_in = got; 372 s->stream.next_in = s->inbuf; 373 } 374 375 s->z_err = inflate(&(s->stream), Z_NO_FLUSH); 376 377 if (s->z_err == Z_STREAM_END) { 378 /* Check CRC and original size */ 379 s->crc = crc32(s->crc, start, (unsigned int) 380 (s->stream.next_out - start)); 381 start = s->stream.next_out; 382 383 if (getLong(s) != s->crc || 384 getLong(s) != s->stream.total_out) { 385 386 s->z_err = Z_DATA_ERROR; 387 } else { 388 /* Check for concatenated .gz files: */ 389 check_header(s); 390 if (s->z_err == Z_OK) { 391 inflateReset(&(s->stream)); 392 s->crc = crc32(0L, Z_NULL, 0); 393 } 394 } 395 } 396 if (s->z_err != Z_OK || s->z_eof) 397 break; 398 } 399 400 s->crc = crc32(s->crc, start, 401 (unsigned int)(s->stream.next_out - start)); 402 403 return (int)(len - s->stream.avail_out); 404 } 405 406 off_t 407 lseek(int fd, off_t offset, int where) 408 { 409 struct open_file *f; 410 struct sd *s; 411 412 #if !defined(LIBSA_NO_FD_CHECKING) 413 if ((unsigned int)fd >= SOPEN_MAX) { 414 errno = EBADF; 415 return -1; 416 } 417 #endif 418 f = &files[fd]; 419 420 if ((f->f_flags & F_READ) == 0) 421 return olseek(fd, offset, where); 422 423 s = ss[fd]; 424 425 if(s->compressed == 0) { 426 off_t res = olseek(fd, offset, where); 427 if (res != (off_t)-1) { 428 /* make sure the lookahead buffer is invalid */ 429 s->stream.avail_in = 0; 430 } 431 return res; 432 } 433 434 switch(where) { 435 case SEEK_CUR: 436 offset += s->stream.total_out; 437 case SEEK_SET: 438 /* if seek backwards, simply start from the beginning */ 439 if (offset < s->stream.total_out) { 440 off_t res; 441 void *sav_inbuf; 442 443 res = olseek(fd, 0, SEEK_SET); 444 if(res == (off_t)-1) 445 return res; 446 /* ??? perhaps fallback to close / open */ 447 448 inflateEnd(&(s->stream)); 449 450 sav_inbuf = s->inbuf; /* don't allocate again */ 451 (void)memset(s, 0, sizeof(struct sd)); 452 /* this resets total_out to 0! */ 453 454 inflateInit2(&(s->stream), -15); 455 s->stream.next_in = s->inbuf = sav_inbuf; 456 457 s->fd = fd; 458 check_header(s); /* skip the .gz header */ 459 } 460 461 /* to seek forwards, throw away data */ 462 if (offset > s->stream.total_out) { 463 off_t toskip = offset - s->stream.total_out; 464 465 while (toskip > 0) { 466 #define DUMMYBUFSIZE 256 467 char dummybuf[DUMMYBUFSIZE]; 468 off_t len = toskip; 469 470 if (len > DUMMYBUFSIZE) 471 len = DUMMYBUFSIZE; 472 if (read(fd, dummybuf, len) != len) { 473 errno = EOFFSET; 474 return (off_t)-1; 475 } 476 toskip -= len; 477 } 478 } 479 #ifdef DEBUG 480 if (offset != s->stream.total_out) 481 panic("lseek compressed"); 482 #endif 483 return offset; 484 case SEEK_END: 485 errno = EOFFSET; 486 break; 487 default: 488 errno = EINVAL; 489 break; 490 } 491 492 return (off_t)-1; 493 } 494