cread.c revision 1.1 1 /* $NetBSD: cread.c,v 1.1 1997/01/22 00:40:07 cgd 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 sd *s;
228
229 s = ss[fd];
230
231 inflateEnd(&(s->stream));
232
233 free(s->inbuf, Z_BUFSIZE);
234 free(s, sizeof(struct sd));
235
236 return(oclose(fd));
237 }
238
239 ssize_t
240 read(fd, buf, len)
241 int fd;
242 void *buf;
243 size_t len;
244 {
245 struct sd *s;
246 unsigned char *start = buf; /* starting point for crc computation */
247
248 s = ss[fd];
249
250 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
251 if (s->z_err == Z_STREAM_END) return 0; /* EOF */
252
253 s->stream.next_out = buf;
254 s->stream.avail_out = len;
255
256 while (s->stream.avail_out != 0) {
257
258 if (s->transparent) {
259 /* Copy first the lookahead bytes: */
260 unsigned int n = s->stream.avail_in;
261 if (n > s->stream.avail_out) n = s->stream.avail_out;
262 if (n > 0) {
263 zmemcpy(s->stream.next_out, s->stream.next_in, n);
264 s->stream.next_out += n;
265 s->stream.next_in += n;
266 s->stream.avail_out -= n;
267 s->stream.avail_in -= n;
268 }
269 if (s->stream.avail_out > 0) {
270 s->stream.avail_out -= oread(s->fd, s->stream.next_out, s->stream.avail_out);
271 }
272 return (int)(len - s->stream.avail_out);
273 }
274
275 if (s->stream.avail_in == 0 && !s->z_eof) {
276
277 errno = 0;
278 s->stream.avail_in = oread(fd, s->inbuf, Z_BUFSIZE);
279 if (s->stream.avail_in == 0) {
280 s->z_eof = 1;
281 if (errno) {
282 s->z_err = Z_ERRNO;
283 break;
284 }
285 }
286 s->stream.next_in = s->inbuf;
287 }
288 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
289
290 if (s->z_err == Z_STREAM_END) {
291 /* Check CRC and original size */
292 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
293 start = s->stream.next_out;
294
295 if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
296 s->z_err = Z_DATA_ERROR;
297 } else {
298 /* Check for concatenated .gz files: */
299 check_header(s);
300 if (s->z_err == Z_OK) {
301 inflateReset(&(s->stream));
302 s->crc = crc32(0L, Z_NULL, 0);
303 }
304 }
305 }
306 if (s->z_err != Z_OK || s->z_eof) break;
307 }
308 s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
309
310 return (int)(len - s->stream.avail_out);
311 }
312
313 off_t
314 lseek(fd, offset, where)
315 int fd;
316 off_t offset;
317 int where;
318 {
319 register struct open_file *f;
320 struct sd *s;
321
322 if ((unsigned)fd >= SOPEN_MAX) {
323 errno = EBADF;
324 return (-1);
325 }
326 f = &files[fd];;
327
328 if(!(f->f_flags & F_READ))
329 return(olseek(fd, offset, where));
330
331 s = ss[fd];
332
333 if(s->transparent) {
334 off_t res = olseek(fd, offset, where);
335 if(res != (off_t)-1) {
336 /* make sure the lookahead buffer is invalid */
337 s->stream.avail_in = 0;
338 }
339 return(res);
340 }
341
342 switch(where) {
343 case SEEK_CUR:
344 offset += s->stream.total_out;
345 case SEEK_SET:
346
347 /* if seek backwards, simply start from
348 the beginning */
349 if(offset < s->stream.total_out) {
350 off_t res;
351 void *sav_inbuf;
352
353 res = olseek(fd, 0, SEEK_SET);
354 if(res == (off_t)-1)
355 return(res);
356 /* ??? perhaps fallback to close / open */
357
358 inflateEnd(&(s->stream));
359
360 sav_inbuf = s->inbuf; /* don't allocate again */
361 bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
362
363 inflateInit2(&(s->stream), -15);
364 s->stream.next_in = s->inbuf = sav_inbuf;
365
366 s->fd = fd;
367 check_header(s); /* skip the .gz header */
368 }
369
370 /* to seek forwards, throw away data */
371 if(offset > s->stream.total_out) {
372 off_t toskip = offset - s->stream.total_out;
373
374 while(toskip > 0) {
375 #define DUMMYBUFSIZE 256
376 char dummybuf[DUMMYBUFSIZE];
377 off_t len = toskip;
378 if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
379 if(read(fd, dummybuf, len) != len) {
380 errno = EOFFSET;
381 return((off_t)-1);
382 }
383 toskip -= len;
384 }
385 }
386 #ifdef DEBUG
387 if(offset != s->stream.total_out)
388 panic("lseek compressed");
389 #endif
390 return(offset);
391 case SEEK_END:
392 errno = EOFFSET;
393 break;
394 default:
395 errno = EINVAL;
396 }
397 return((off_t)-1);
398 }
399