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