cread.c revision 1.26
1/* $NetBSD: cread.c,v 1.26 2013/10/13 20:09:02 joerg 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 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 284errout: 285 if (s != 0) 286 dealloc(s, sizeof(struct sd)); 287 ss[fd] = NULL; 288 oclose(fd); 289 return -1; 290} 291 292int 293close(int fd) 294{ 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 304 s = ss[fd]; 305 306 if (s != NULL) { 307 inflateEnd(&(s->stream)); 308 309 dealloc(s->inbuf, Z_BUFSIZE); 310 dealloc(s, sizeof(struct sd)); 311 } 312 313 return oclose(fd); 314} 315 316ssize_t 317read(int fd, void *buf, size_t len) 318{ 319 struct sd *s; 320 unsigned char *start = buf; /* starting point for crc computation */ 321 322 s = ss[fd]; 323 324 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) 325 return -1; 326 if (s->z_err == Z_STREAM_END) 327 return 0; /* EOF */ 328 329 s->stream.next_out = buf; 330 s->stream.avail_out = len; 331 332 while (s->stream.avail_out != 0) { 333 334 if (s->compressed == 0) { 335 /* Copy first the lookahead bytes: */ 336 unsigned int n = s->stream.avail_in; 337 if (n > s->stream.avail_out) 338 n = s->stream.avail_out; 339 if (n > 0) { 340 zmemcpy(s->stream.next_out, 341 s->stream.next_in, n); 342 s->stream.next_out += n; 343 s->stream.next_in += n; 344 s->stream.avail_out -= n; 345 s->stream.avail_in -= n; 346 } 347 if (s->stream.avail_out > 0) { 348 int got; 349 got = oread(s->fd, s->stream.next_out, 350 s->stream.avail_out); 351 if (got == -1) 352 return got; 353 s->stream.avail_out -= got; 354 } 355 return (int)(len - s->stream.avail_out); 356 } 357 358 if (s->stream.avail_in == 0 && !s->z_eof) { 359 int got; 360 errno = 0; 361 got = oread(fd, s->inbuf, Z_BUFSIZE); 362 if (got <= 0) { 363 s->z_eof = 1; 364 if (errno) { 365 s->z_err = Z_ERRNO; 366 break; 367 } 368 } 369 s->stream.avail_in = got; 370 s->stream.next_in = s->inbuf; 371 } 372 373 s->z_err = inflate(&(s->stream), Z_NO_FLUSH); 374 375 if (s->z_err == Z_STREAM_END) { 376 /* Check CRC and original size */ 377 s->crc = crc32(s->crc, start, (unsigned int) 378 (s->stream.next_out - start)); 379 start = s->stream.next_out; 380 381 if (getLong(s) != s->crc || 382 getLong(s) != s->stream.total_out) { 383 384 s->z_err = Z_DATA_ERROR; 385 } else { 386 /* Check for concatenated .gz files: */ 387 check_header(s); 388 if (s->z_err == Z_OK) { 389 inflateReset(&(s->stream)); 390 s->crc = crc32(0L, Z_NULL, 0); 391 } 392 } 393 } 394 if (s->z_err != Z_OK || s->z_eof) 395 break; 396 } 397 398 s->crc = crc32(s->crc, start, 399 (unsigned int)(s->stream.next_out - start)); 400 401 return (int)(len - s->stream.avail_out); 402} 403 404off_t 405lseek(int fd, off_t offset, int where) 406{ 407 struct open_file *f; 408 struct sd *s; 409 410#if !defined(LIBSA_NO_FD_CHECKING) 411 if ((unsigned int)fd >= SOPEN_MAX) { 412 errno = EBADF; 413 return -1; 414 } 415#endif 416 f = &files[fd]; 417 418 if ((f->f_flags & F_READ) == 0) 419 return olseek(fd, offset, where); 420 421 s = ss[fd]; 422 423 if(s->compressed == 0) { 424 off_t res = olseek(fd, offset, where); 425 if (res != (off_t)-1) { 426 /* make sure the lookahead buffer is invalid */ 427 s->stream.avail_in = 0; 428 } 429 return res; 430 } 431 432 switch(where) { 433 case SEEK_CUR: 434 offset += s->stream.total_out; 435 case SEEK_SET: 436 /* if seek backwards, simply start from the beginning */ 437 if (offset < s->stream.total_out) { 438 off_t res; 439 void *sav_inbuf; 440 441 res = olseek(fd, 0, SEEK_SET); 442 if(res == (off_t)-1) 443 return res; 444 /* ??? perhaps fallback to close / open */ 445 446 inflateEnd(&(s->stream)); 447 448 sav_inbuf = s->inbuf; /* don't allocate again */ 449 (void)memset(s, 0, sizeof(struct sd)); 450 /* this resets total_out to 0! */ 451 452 inflateInit2(&(s->stream), -15); 453 s->stream.next_in = s->inbuf = sav_inbuf; 454 455 s->fd = fd; 456 check_header(s); /* skip the .gz header */ 457 } 458 459 /* to seek forwards, throw away data */ 460 if (offset > s->stream.total_out) { 461 off_t toskip = offset - s->stream.total_out; 462 463 while (toskip > 0) { 464#define DUMMYBUFSIZE 256 465 char dummybuf[DUMMYBUFSIZE]; 466 off_t len = toskip; 467 468 if (len > DUMMYBUFSIZE) 469 len = DUMMYBUFSIZE; 470 if (read(fd, dummybuf, len) != len) { 471 errno = EOFFSET; 472 return (off_t)-1; 473 } 474 toskip -= len; 475 } 476 } 477#ifdef DEBUG 478 if (offset != s->stream.total_out) 479 panic("lseek compressed"); 480#endif 481 return offset; 482 case SEEK_END: 483 errno = EOFFSET; 484 break; 485 default: 486 errno = EINVAL; 487 break; 488 } 489 490 return (off_t)-1; 491} 492