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