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