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