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