1 /* $OpenBSD: cread.c,v 1.12 2004/04/02 04:39:51 deraadt 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
254 inflateEnd(&(s->stream));
255
256 free(s->inbuf, Z_BUFSIZE);
257 free(s, sizeof(struct sd));
258
259 return(oclose(fd));
260 }
261
262 ssize_t
263 read(int fd, void *buf, size_t len)
264 {
265 struct sd *s;
266 unsigned char *start = buf; /* starting point for crc computation */
267
268 s = ss[fd];
269
270 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
271 return -1;
272 if (s->z_err == Z_STREAM_END)
273 return 0; /* EOF */
274
275 s->stream.next_out = buf;
276 s->stream.avail_out = len;
277
278 while (s->stream.avail_out != 0) {
279
280 if (s->transparent) {
281 /* Copy first the lookahead bytes: */
282 unsigned int n = s->stream.avail_in;
283
284 if (n > s->stream.avail_out)
285 n = s->stream.avail_out;
286 if (n > 0) {
287 zmemcpy(s->stream.next_out, s->stream.next_in, n);
288 s->stream.next_out += n;
289 s->stream.next_in += n;
290 s->stream.avail_out -= n;
291 s->stream.avail_in -= n;
292 }
293 if (s->stream.avail_out > 0) {
294 int n;
295
296 n = oread(fd, s->stream.next_out,
297 s->stream.avail_out);
298 if (n <= 0) {
299 s->z_eof = 1;
300 if (errno) {
301 s->z_err = Z_ERRNO;
302 break;
303 }
304 }
305 s->stream.avail_out -= n;
306 }
307 len -= s->stream.avail_out;
308 s->stream.total_in += (unsigned long)len;
309 s->stream.total_out += (unsigned long)len;
310 if (len == 0)
311 s->z_eof = 1;
312 return (int)len;
313 }
314
315 if (s->stream.avail_in == 0 && !s->z_eof) {
316 errno = 0;
317 s->stream.avail_in = oread(fd, s->inbuf, Z_BUFSIZE);
318 if (s->stream.avail_in <= 0) {
319 s->z_eof = 1;
320 if (errno) {
321 s->z_err = Z_ERRNO;
322 break;
323 }
324 }
325 s->stream.next_in = s->inbuf;
326 }
327 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
328
329 if (s->z_err == Z_STREAM_END) {
330 /* Check CRC and original size */
331 s->crc = crc32(s->crc, start,
332 (unsigned int)(s->stream.next_out - start));
333 start = s->stream.next_out;
334
335 if (getLong(s) != s->crc) {
336 s->z_err = Z_DATA_ERROR;
337 } else {
338 (void)getLong(s);
339
340 /* The uncompressed length returned by
341 * above getlong() may be different from
342 * s->stream.total_out in case of concatenated
343 * .gz files. Check for such files:
344 */
345 check_header(s);
346 if (s->z_err == Z_OK) {
347 unsigned long total_in = s->stream.total_in;
348 unsigned long total_out = s->stream.total_out;
349
350 inflateReset(&(s->stream));
351 s->stream.total_in = total_in;
352 s->stream.total_out = total_out;
353 s->crc = crc32(0L, Z_NULL, 0);
354 }
355 }
356 }
357 if (s->z_err != Z_OK || s->z_eof)
358 break;
359 }
360 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
361
362 return (int)(len - s->stream.avail_out);
363 }
364
365 off_t
366 lseek(int fd, off_t offset, int where)
367 {
368 struct open_file *f;
369 struct sd *s;
370
371 if ((unsigned)fd >= SOPEN_MAX) {
372 errno = EBADF;
373 return (-1);
374 }
375 f = &files[fd];
376
377 if (!(f->f_flags & F_READ))
378 return(olseek(fd, offset, where));
379
380 s = ss[fd];
381
382 if (s->transparent) {
383 off_t res = olseek(fd, offset, where);
384 if (res != (off_t)-1) {
385 /* make sure the lookahead buffer is invalid */
386 s->stream.avail_in = 0;
387 }
388 return(res);
389 }
390
391 switch(where) {
392 case SEEK_CUR:
393 offset += s->stream.total_out;
394 case SEEK_SET:
395
396 /* if seek backwards, simply start from
397 the beginning */
398 if (offset < s->stream.total_out) {
399 off_t res;
400 void *sav_inbuf;
401
402 res = olseek(fd, 0, SEEK_SET);
403 if (res == (off_t)-1)
404 return(res);
405 /* ??? perhaps fallback to close / open */
406
407 inflateEnd(&(s->stream));
408
409 sav_inbuf = s->inbuf; /* don't allocate again */
410 bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
411
412 inflateInit2(&(s->stream), -15);
413 s->stream.next_in = s->inbuf = sav_inbuf;
414
415 s->fd = fd;
416 check_header(s); /* skip the .gz header */
417 }
418
419 /* to seek forwards, throw away data */
420 if (offset > s->stream.total_out) {
421 off_t toskip = offset - s->stream.total_out;
422
423 while(toskip > 0) {
424 #define DUMMYBUFSIZE 256
425 char dummybuf[DUMMYBUFSIZE];
426 off_t len = toskip;
427
428 if (len > DUMMYBUFSIZE)
429 len = DUMMYBUFSIZE;
430 if (read(fd, dummybuf, len) != len) {
431 errno = EOFFSET;
432 return((off_t)-1);
433 }
434 toskip -= len;
435 }
436 }
437 #ifdef DEBUG
438 if (offset != s->stream.total_out)
439 panic("lseek compressed");
440 #endif
441 return(offset);
442 case SEEK_END:
443 errno = EOFFSET;
444 break;
445 default:
446 errno = EINVAL;
447 }
448 return((off_t)-1);
449 }