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