1 /* $NetBSD: cread.c,v 1.5 1997/07/04 18:45:11 drochner 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 transparent; /* 1 if input file is not 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 s->transparent = 1; 159 if (c != EOF) s->stream.avail_in++, s->stream.next_in--; 160 s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END; 161 return; 162 } 163 } 164 method = get_byte(s); 165 flags = get_byte(s); 166 if (method != Z_DEFLATED || (flags & RESERVED) != 0) { 167 s->z_err = Z_DATA_ERROR; 168 return; 169 } 170 171 /* Discard time, xflags and OS code: */ 172 for (len = 0; len < 6; len++) (void)get_byte(s); 173 174 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ 175 len = (unsigned int)get_byte(s); 176 len += ((unsigned int)get_byte(s))<<8; 177 /* len is garbage if EOF but the loop below will quit anyway */ 178 while (len-- != 0 && get_byte(s) != EOF) ; 179 } 180 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ 181 while ((c = get_byte(s)) != 0 && c != EOF) ; 182 } 183 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ 184 while ((c = get_byte(s)) != 0 && c != EOF) ; 185 } 186 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ 187 for (len = 0; len < 2; len++) (void)get_byte(s); 188 } 189 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK; 190 } 191 192 /* 193 * new open(), close(), read(), lseek() 194 */ 195 196 int 197 open(fname, mode) 198 const char *fname; 199 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) goto errout; 210 bzero(s, sizeof(struct sd)); 211 212 if(inflateInit2(&(s->stream), -15) != Z_OK) 213 goto errout; 214 215 s->stream.next_in = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE); 216 if(!s->inbuf) { 217 inflateEnd(&(s->stream)); 218 goto errout; 219 } 220 221 s->fd = fd; 222 check_header(s); /* skip the .gz header */ 223 return(fd); 224 225 errout: 226 if(s) free(s, sizeof(struct sd)); 227 oclose(fd); 228 return(-1); 229 } 230 231 int 232 close(fd) 233 int fd; 234 { 235 struct open_file *f; 236 struct sd *s; 237 238 if ((unsigned)fd >= SOPEN_MAX) { 239 errno = EBADF; 240 return (-1); 241 } 242 f = &files[fd]; 243 244 if(!(f->f_flags & F_READ)) 245 return(oclose(fd)); 246 247 s = ss[fd]; 248 249 inflateEnd(&(s->stream)); 250 251 free(s->inbuf, Z_BUFSIZE); 252 free(s, sizeof(struct sd)); 253 254 return(oclose(fd)); 255 } 256 257 ssize_t 258 read(fd, buf, len) 259 int fd; 260 void *buf; 261 size_t len; 262 { 263 struct sd *s; 264 unsigned char *start = buf; /* starting point for crc computation */ 265 266 s = ss[fd]; 267 268 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1; 269 if (s->z_err == Z_STREAM_END) return 0; /* EOF */ 270 271 s->stream.next_out = buf; 272 s->stream.avail_out = len; 273 274 while (s->stream.avail_out != 0) { 275 276 if (s->transparent) { 277 /* Copy first the lookahead bytes: */ 278 unsigned int n = s->stream.avail_in; 279 if (n > s->stream.avail_out) n = s->stream.avail_out; 280 if (n > 0) { 281 zmemcpy(s->stream.next_out, s->stream.next_in, n); 282 s->stream.next_out += n; 283 s->stream.next_in += n; 284 s->stream.avail_out -= n; 285 s->stream.avail_in -= n; 286 } 287 if (s->stream.avail_out > 0) { 288 int got; 289 got = oread(s->fd, s->stream.next_out, s->stream.avail_out); 290 if(got == -1) 291 return(got); 292 s->stream.avail_out -= got; 293 } 294 return (int)(len - s->stream.avail_out); 295 } 296 297 if (s->stream.avail_in == 0 && !s->z_eof) { 298 int got; 299 errno = 0; 300 got = oread(fd, s->inbuf, Z_BUFSIZE); 301 if (got <= 0) { 302 s->z_eof = 1; 303 if (errno) { 304 s->z_err = Z_ERRNO; 305 break; 306 } 307 } 308 s->stream.avail_in = got; 309 s->stream.next_in = s->inbuf; 310 } 311 s->z_err = inflate(&(s->stream), Z_NO_FLUSH); 312 313 if (s->z_err == Z_STREAM_END) { 314 /* Check CRC and original size */ 315 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start)); 316 start = s->stream.next_out; 317 318 if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) { 319 s->z_err = Z_DATA_ERROR; 320 } else { 321 /* Check for concatenated .gz files: */ 322 check_header(s); 323 if (s->z_err == Z_OK) { 324 inflateReset(&(s->stream)); 325 s->crc = crc32(0L, Z_NULL, 0); 326 } 327 } 328 } 329 if (s->z_err != Z_OK || s->z_eof) break; 330 } 331 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start)); 332 333 return (int)(len - s->stream.avail_out); 334 } 335 336 off_t 337 lseek(fd, offset, where) 338 int fd; 339 off_t offset; 340 int where; 341 { 342 register struct open_file *f; 343 struct sd *s; 344 345 if ((unsigned)fd >= SOPEN_MAX) { 346 errno = EBADF; 347 return (-1); 348 } 349 f = &files[fd];; 350 351 if(!(f->f_flags & F_READ)) 352 return(olseek(fd, offset, where)); 353 354 s = ss[fd]; 355 356 if(s->transparent) { 357 off_t res = olseek(fd, offset, where); 358 if(res != (off_t)-1) { 359 /* make sure the lookahead buffer is invalid */ 360 s->stream.avail_in = 0; 361 } 362 return(res); 363 } 364 365 switch(where) { 366 case SEEK_CUR: 367 offset += s->stream.total_out; 368 case SEEK_SET: 369 370 /* if seek backwards, simply start from 371 the beginning */ 372 if(offset < s->stream.total_out) { 373 off_t res; 374 void *sav_inbuf; 375 376 res = olseek(fd, 0, SEEK_SET); 377 if(res == (off_t)-1) 378 return(res); 379 /* ??? perhaps fallback to close / open */ 380 381 inflateEnd(&(s->stream)); 382 383 sav_inbuf = s->inbuf; /* don't allocate again */ 384 bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */ 385 386 inflateInit2(&(s->stream), -15); 387 s->stream.next_in = s->inbuf = sav_inbuf; 388 389 s->fd = fd; 390 check_header(s); /* skip the .gz header */ 391 } 392 393 /* to seek forwards, throw away data */ 394 if(offset > s->stream.total_out) { 395 off_t toskip = offset - s->stream.total_out; 396 397 while(toskip > 0) { 398 #define DUMMYBUFSIZE 256 399 char dummybuf[DUMMYBUFSIZE]; 400 off_t len = toskip; 401 if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE; 402 if(read(fd, dummybuf, len) != len) { 403 errno = EOFFSET; 404 return((off_t)-1); 405 } 406 toskip -= len; 407 } 408 } 409 #ifdef DEBUG 410 if(offset != s->stream.total_out) 411 panic("lseek compressed"); 412 #endif 413 return(offset); 414 case SEEK_END: 415 errno = EOFFSET; 416 break; 417 default: 418 errno = EINVAL; 419 } 420 return((off_t)-1); 421 } 422