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