cread.c revision 1.4 1 /* $NetBSD: cread.c,v 1.4 1997/06/26 19:11:34 drochner 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 transparent; /* 1 if input file is not 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 errno = 0;
118 s->stream.avail_in = oread(s->fd, s->inbuf, Z_BUFSIZE);
119 if (s->stream.avail_in == 0) {
120 s->z_eof = 1;
121 if (errno) s->z_err = Z_ERRNO;
122 return EOF;
123 }
124 s->stream.next_in = s->inbuf;
125 }
126 s->stream.avail_in--;
127 return *(s->stream.next_in)++;
128 }
129
130 static unsigned long getLong (s)
131 struct sd *s;
132 {
133 unsigned long x = (unsigned long)get_byte(s);
134 int c;
135
136 x += ((unsigned long)get_byte(s))<<8;
137 x += ((unsigned long)get_byte(s))<<16;
138 c = get_byte(s);
139 if (c == EOF) s->z_err = Z_DATA_ERROR;
140 x += ((unsigned long)c)<<24;
141 return x;
142 }
143
144 static void check_header(s)
145 struct sd *s;
146 {
147 int method; /* method byte */
148 int flags; /* flags byte */
149 unsigned int len;
150 int c;
151
152 /* Check the gzip magic header */
153 for (len = 0; len < 2; len++) {
154 c = get_byte(s);
155 if (c != gz_magic[len]) {
156 s->transparent = 1;
157 if (c != EOF) s->stream.avail_in++, s->stream.next_in--;
158 s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
159 return;
160 }
161 }
162 method = get_byte(s);
163 flags = get_byte(s);
164 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
165 s->z_err = Z_DATA_ERROR;
166 return;
167 }
168
169 /* Discard time, xflags and OS code: */
170 for (len = 0; len < 6; len++) (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 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
179 while ((c = get_byte(s)) != 0 && c != EOF) ;
180 }
181 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
182 while ((c = get_byte(s)) != 0 && c != EOF) ;
183 }
184 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
185 for (len = 0; len < 2; len++) (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(fname, mode)
196 const char *fname;
197 int mode;
198 {
199 int fd;
200 struct sd *s = 0;
201
202 if(((fd = oopen(fname, mode)) == -1)
203 || (mode != 0)) /* compression only for read */
204 return(fd);
205
206 ss[fd] = s = alloc(sizeof(struct sd));
207 if(!s) goto errout;
208 bzero(s, sizeof(struct sd));
209
210 if(inflateInit2(&(s->stream), -15) != Z_OK)
211 goto errout;
212
213 s->stream.next_in = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
214 if(!s->inbuf) {
215 inflateEnd(&(s->stream));
216 goto errout;
217 }
218
219 s->fd = fd;
220 check_header(s); /* skip the .gz header */
221 return(fd);
222
223 errout:
224 if(s) free(s, sizeof(struct sd));
225 oclose(fd);
226 return(-1);
227 }
228
229 int
230 close(fd)
231 int fd;
232 {
233 struct open_file *f;
234 struct sd *s;
235
236 if ((unsigned)fd >= SOPEN_MAX) {
237 errno = EBADF;
238 return (-1);
239 }
240 f = &files[fd];
241
242 if(!(f->f_flags & F_READ))
243 return(oclose(fd));
244
245 s = ss[fd];
246
247 inflateEnd(&(s->stream));
248
249 free(s->inbuf, Z_BUFSIZE);
250 free(s, sizeof(struct sd));
251
252 return(oclose(fd));
253 }
254
255 ssize_t
256 read(fd, buf, len)
257 int fd;
258 void *buf;
259 size_t len;
260 {
261 struct sd *s;
262 unsigned char *start = buf; /* starting point for crc computation */
263
264 s = ss[fd];
265
266 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
267 if (s->z_err == Z_STREAM_END) return 0; /* EOF */
268
269 s->stream.next_out = buf;
270 s->stream.avail_out = len;
271
272 while (s->stream.avail_out != 0) {
273
274 if (s->transparent) {
275 /* Copy first the lookahead bytes: */
276 unsigned int n = s->stream.avail_in;
277 if (n > s->stream.avail_out) n = s->stream.avail_out;
278 if (n > 0) {
279 zmemcpy(s->stream.next_out, s->stream.next_in, n);
280 s->stream.next_out += n;
281 s->stream.next_in += n;
282 s->stream.avail_out -= n;
283 s->stream.avail_in -= n;
284 }
285 if (s->stream.avail_out > 0) {
286 s->stream.avail_out -= oread(s->fd, s->stream.next_out, s->stream.avail_out);
287 }
288 return (int)(len - s->stream.avail_out);
289 }
290
291 if (s->stream.avail_in == 0 && !s->z_eof) {
292
293 errno = 0;
294 s->stream.avail_in = oread(fd, s->inbuf, Z_BUFSIZE);
295 if (s->stream.avail_in == 0) {
296 s->z_eof = 1;
297 if (errno) {
298 s->z_err = Z_ERRNO;
299 break;
300 }
301 }
302 s->stream.next_in = s->inbuf;
303 }
304 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
305
306 if (s->z_err == Z_STREAM_END) {
307 /* Check CRC and original size */
308 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
309 start = s->stream.next_out;
310
311 if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
312 s->z_err = Z_DATA_ERROR;
313 } else {
314 /* Check for concatenated .gz files: */
315 check_header(s);
316 if (s->z_err == Z_OK) {
317 inflateReset(&(s->stream));
318 s->crc = crc32(0L, Z_NULL, 0);
319 }
320 }
321 }
322 if (s->z_err != Z_OK || s->z_eof) break;
323 }
324 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
325
326 return (int)(len - s->stream.avail_out);
327 }
328
329 off_t
330 lseek(fd, offset, where)
331 int fd;
332 off_t offset;
333 int where;
334 {
335 register struct open_file *f;
336 struct sd *s;
337
338 if ((unsigned)fd >= SOPEN_MAX) {
339 errno = EBADF;
340 return (-1);
341 }
342 f = &files[fd];;
343
344 if(!(f->f_flags & F_READ))
345 return(olseek(fd, offset, where));
346
347 s = ss[fd];
348
349 if(s->transparent) {
350 off_t res = olseek(fd, offset, where);
351 if(res != (off_t)-1) {
352 /* make sure the lookahead buffer is invalid */
353 s->stream.avail_in = 0;
354 }
355 return(res);
356 }
357
358 switch(where) {
359 case SEEK_CUR:
360 offset += s->stream.total_out;
361 case SEEK_SET:
362
363 /* if seek backwards, simply start from
364 the beginning */
365 if(offset < s->stream.total_out) {
366 off_t res;
367 void *sav_inbuf;
368
369 res = olseek(fd, 0, SEEK_SET);
370 if(res == (off_t)-1)
371 return(res);
372 /* ??? perhaps fallback to close / open */
373
374 inflateEnd(&(s->stream));
375
376 sav_inbuf = s->inbuf; /* don't allocate again */
377 bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
378
379 inflateInit2(&(s->stream), -15);
380 s->stream.next_in = s->inbuf = sav_inbuf;
381
382 s->fd = fd;
383 check_header(s); /* skip the .gz header */
384 }
385
386 /* to seek forwards, throw away data */
387 if(offset > s->stream.total_out) {
388 off_t toskip = offset - s->stream.total_out;
389
390 while(toskip > 0) {
391 #define DUMMYBUFSIZE 256
392 char dummybuf[DUMMYBUFSIZE];
393 off_t len = toskip;
394 if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
395 if(read(fd, dummybuf, len) != len) {
396 errno = EOFFSET;
397 return((off_t)-1);
398 }
399 toskip -= len;
400 }
401 }
402 #ifdef DEBUG
403 if(offset != s->stream.total_out)
404 panic("lseek compressed");
405 #endif
406 return(offset);
407 case SEEK_END:
408 errno = EOFFSET;
409 break;
410 default:
411 errno = EINVAL;
412 }
413 return((off_t)-1);
414 }
415