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