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