1 /* $NetBSD: cread.c,v 1.6 1997/10/18 22:27:15 cjs 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 /* support for compressed bootfiles 36 (only read) 37 replaces open(), close(), read(), lseek(). 38 original libsa open(), close(), read(), lseek() are called 39 as oopen(), oclose(), oread() resp. olseek(). 40 compression parts stripped from zlib:gzio.c 41 */ 42 43 /* gzio.c -- IO on .gz files 44 * Copyright (C) 1995-1996 Jean-loup Gailly. 45 * For conditions of distribution and use, see copyright notice in zlib.h 46 */ 47 48 #include "stand.h" 49 #ifdef _STANDALONE 50 #include <lib/libkern/libkern.h> 51 #include <lib/libz/zlib.h> 52 #else 53 #include <string.h> 54 #include <zlib.h> 55 #endif 56 57 #define EOF (-1) /* needed by compression code */ 58 59 #ifdef SAVE_MEMORY 60 #define Z_BUFSIZE 1024 61 #else 62 #define Z_BUFSIZE 4096 63 #endif 64 65 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 66 67 /* gzip flag byte */ 68 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 69 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 70 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 71 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 72 #define COMMENT 0x10 /* bit 4 set: file comment present */ 73 #define RESERVED 0xE0 /* bits 5..7: reserved */ 74 75 static struct sd { 76 z_stream stream; 77 int z_err; /* error code for last stream operation */ 78 int z_eof; /* set if end of input file */ 79 int fd; 80 unsigned char *inbuf; /* input buffer */ 81 unsigned long crc; /* crc32 of uncompressed data */ 82 int compressed; /* 1 if input file is a .gz file */ 83 } *ss[SOPEN_MAX]; 84 85 /* 86 * compression utilities 87 */ 88 89 void *zcalloc (opaque, items, size) 90 void *opaque; 91 unsigned items; 92 unsigned size; 93 { 94 return(alloc(items * size)); 95 } 96 97 void zcfree (opaque, ptr) 98 void *opaque; 99 void *ptr; 100 { 101 free(ptr, 0); /* XXX works only with modified allocator */ 102 } 103 104 void zmemcpy(dest, source, len) 105 unsigned char *dest; 106 unsigned char *source; 107 unsigned int len; 108 { 109 bcopy(source, dest, len); 110 } 111 112 static int get_byte(s) 113 struct sd *s; 114 { 115 if (s->z_eof) return EOF; 116 if (s->stream.avail_in == 0) { 117 int got; 118 errno = 0; 119 got = oread(s->fd, s->inbuf, Z_BUFSIZE); 120 if (got <= 0) { 121 s->z_eof = 1; 122 if (errno) s->z_err = Z_ERRNO; 123 return EOF; 124 } 125 s->stream.avail_in = got; 126 s->stream.next_in = s->inbuf; 127 } 128 s->stream.avail_in--; 129 return *(s->stream.next_in)++; 130 } 131 132 static unsigned long getLong (s) 133 struct sd *s; 134 { 135 unsigned long x = (unsigned long)get_byte(s); 136 int c; 137 138 x += ((unsigned long)get_byte(s))<<8; 139 x += ((unsigned long)get_byte(s))<<16; 140 c = get_byte(s); 141 if (c == EOF) s->z_err = Z_DATA_ERROR; 142 x += ((unsigned long)c)<<24; 143 return x; 144 } 145 146 static void check_header(s) 147 struct sd *s; 148 { 149 int method; /* method byte */ 150 int flags; /* flags byte */ 151 unsigned int len; 152 int c; 153 154 /* Check the gzip magic header */ 155 for (len = 0; len < 2; len++) { 156 c = get_byte(s); 157 if (c != gz_magic[len]) { 158 if ((c == EOF) && (len == 0)) { 159 /* 160 * We must not change s->compressed if we are at EOF; 161 * we may have come to the end of a gzipped file and be 162 * check to see if another gzipped file is concatenated 163 * to this one. If one isn't, we still need to be able 164 * to lseek on this file as a compressed file. 165 */ 166 return; 167 } 168 s->compressed = 0; 169 if (c != EOF) s->stream.avail_in++, s->stream.next_in--; 170 s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; 171 return; 172 } 173 } 174 s->compressed = 1; 175 method = get_byte(s); 176 flags = get_byte(s); 177 if (method != Z_DEFLATED || (flags & RESERVED) != 0) { 178 s->z_err = Z_DATA_ERROR; 179 return; 180 } 181 182 /* Discard time, xflags and OS code: */ 183 for (len = 0; len < 6; len++) (void)get_byte(s); 184 185 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ 186 len = (unsigned int)get_byte(s); 187 len += ((unsigned int)get_byte(s))<<8; 188 /* len is garbage if EOF but the loop below will quit anyway */ 189 while (len-- != 0 && get_byte(s) != EOF) ; 190 } 191 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ 192 while ((c = get_byte(s)) != 0 && c != EOF) ; 193 } 194 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ 195 while ((c = get_byte(s)) != 0 && c != EOF) ; 196 } 197 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ 198 for (len = 0; len < 2; len++) (void)get_byte(s); 199 } 200 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; 201 } 202 203 /* 204 * new open(), close(), read(), lseek() 205 */ 206 207 int 208 open(fname, mode) 209 const char *fname; 210 int mode; 211 { 212 int fd; 213 struct sd *s = 0; 214 215 if(((fd = oopen(fname, mode)) == -1) 216 || (mode != 0)) /* compression only for read */ 217 return(fd); 218 219 ss[fd] = s = alloc(sizeof(struct sd)); 220 if(!s) goto errout; 221 bzero(s, sizeof(struct sd)); 222 223 if(inflateInit2(&(s->stream), -15) != Z_OK) 224 goto errout; 225 226 s->stream.next_in = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE); 227 if(!s->inbuf) { 228 inflateEnd(&(s->stream)); 229 goto errout; 230 } 231 232 s->fd = fd; 233 check_header(s); /* skip the .gz header */ 234 return(fd); 235 236 errout: 237 if(s) free(s, sizeof(struct sd)); 238 oclose(fd); 239 return(-1); 240 } 241 242 int 243 close(fd) 244 int fd; 245 { 246 struct open_file *f; 247 struct sd *s; 248 249 if ((unsigned)fd >= SOPEN_MAX) { 250 errno = EBADF; 251 return (-1); 252 } 253 f = &files[fd]; 254 255 if(!(f->f_flags & F_READ)) 256 return(oclose(fd)); 257 258 s = ss[fd]; 259 260 inflateEnd(&(s->stream)); 261 262 free(s->inbuf, Z_BUFSIZE); 263 free(s, sizeof(struct sd)); 264 265 return(oclose(fd)); 266 } 267 268 ssize_t 269 read(fd, buf, len) 270 int fd; 271 void *buf; 272 size_t len; 273 { 274 struct sd *s; 275 unsigned char *start = buf; /* starting point for crc computation */ 276 277 s = ss[fd]; 278 279 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; 280 if (s->z_err == Z_STREAM_END) return 0; /* EOF */ 281 282 s->stream.next_out = buf; 283 s->stream.avail_out = len; 284 285 while (s->stream.avail_out != 0) { 286 287 if (s->compressed == 0) { 288 /* Copy first the lookahead bytes: */ 289 unsigned int n = s->stream.avail_in; 290 if (n > s->stream.avail_out) n = s->stream.avail_out; 291 if (n > 0) { 292 zmemcpy(s->stream.next_out, s->stream.next_in, n); 293 s->stream.next_out += n; 294 s->stream.next_in += n; 295 s->stream.avail_out -= n; 296 s->stream.avail_in -= n; 297 } 298 if (s->stream.avail_out > 0) { 299 int got; 300 got = oread(s->fd, s->stream.next_out, s->stream.avail_out); 301 if(got == -1) 302 return(got); 303 s->stream.avail_out -= got; 304 } 305 return (int)(len - s->stream.avail_out); 306 } 307 308 if (s->stream.avail_in == 0 && !s->z_eof) { 309 int got; 310 errno = 0; 311 got = oread(fd, s->inbuf, Z_BUFSIZE); 312 if (got <= 0) { 313 s->z_eof = 1; 314 if (errno) { 315 s->z_err = Z_ERRNO; 316 break; 317 } 318 } 319 s->stream.avail_in = got; 320 s->stream.next_in = s->inbuf; 321 } 322 s->z_err = inflate(&(s->stream), Z_NO_FLUSH); 323 324 if (s->z_err == Z_STREAM_END) { 325 /* Check CRC and original size */ 326 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start)); 327 start = s->stream.next_out; 328 329 if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) { 330 s->z_err = Z_DATA_ERROR; 331 } else { 332 /* Check for concatenated .gz files: */ 333 check_header(s); 334 if (s->z_err == Z_OK) { 335 inflateReset(&(s->stream)); 336 s->crc = crc32(0L, Z_NULL, 0); 337 } 338 } 339 } 340 if (s->z_err != Z_OK || s->z_eof) break; 341 } 342 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start)); 343 344 return (int)(len - s->stream.avail_out); 345 } 346 347 off_t 348 lseek(fd, offset, where) 349 int fd; 350 off_t offset; 351 int where; 352 { 353 register struct open_file *f; 354 struct sd *s; 355 356 if ((unsigned)fd >= SOPEN_MAX) { 357 errno = EBADF; 358 return (-1); 359 } 360 f = &files[fd];; 361 362 if(!(f->f_flags & F_READ)) 363 return(olseek(fd, offset, where)); 364 365 s = ss[fd]; 366 367 if(s->compressed == 0) { 368 off_t res = olseek(fd, offset, where); 369 if(res != (off_t)-1) { 370 /* make sure the lookahead buffer is invalid */ 371 s->stream.avail_in = 0; 372 } 373 return(res); 374 } 375 376 switch(where) { 377 case SEEK_CUR: 378 offset += s->stream.total_out; 379 case SEEK_SET: 380 381 /* if seek backwards, simply start from 382 the beginning */ 383 if(offset < s->stream.total_out) { 384 off_t res; 385 void *sav_inbuf; 386 387 res = olseek(fd, 0, SEEK_SET); 388 if(res == (off_t)-1) 389 return(res); 390 /* ??? perhaps fallback to close / open */ 391 392 inflateEnd(&(s->stream)); 393 394 sav_inbuf = s->inbuf; /* don't allocate again */ 395 bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */ 396 397 inflateInit2(&(s->stream), -15); 398 s->stream.next_in = s->inbuf = sav_inbuf; 399 400 s->fd = fd; 401 check_header(s); /* skip the .gz header */ 402 } 403 404 /* to seek forwards, throw away data */ 405 if(offset > s->stream.total_out) { 406 off_t toskip = offset - s->stream.total_out; 407 408 while(toskip > 0) { 409 #define DUMMYBUFSIZE 256 410 char dummybuf[DUMMYBUFSIZE]; 411 off_t len = toskip; 412 if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE; 413 if(read(fd, dummybuf, len) != len) { 414 errno = EOFFSET; 415 return((off_t)-1); 416 } 417 toskip -= len; 418 } 419 } 420 #ifdef DEBUG 421 if(offset != s->stream.total_out) 422 panic("lseek compressed"); 423 #endif 424 return(offset); 425 case SEEK_END: 426 errno = EOFFSET; 427 break; 428 default: 429 errno = EINVAL; 430 } 431 return((off_t)-1); 432 } 433