cread.c revision 1.4 1 1.4 drochner /* $NetBSD: cread.c,v 1.4 1997/06/26 19:11:34 drochner Exp $ */
2 1.1 cgd
3 1.1 cgd /*
4 1.1 cgd * Copyright (c) 1996
5 1.1 cgd * Matthias Drochner. All rights reserved.
6 1.1 cgd *
7 1.1 cgd * Redistribution and use in source and binary forms, with or without
8 1.1 cgd * modification, are permitted provided that the following conditions
9 1.1 cgd * are met:
10 1.1 cgd * 1. Redistributions of source code must retain the above copyright
11 1.1 cgd * notice, this list of conditions and the following disclaimer.
12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 cgd * notice, this list of conditions and the following disclaimer in the
14 1.1 cgd * documentation and/or other materials provided with the distribution.
15 1.1 cgd * 3. All advertising materials mentioning features or use of this software
16 1.1 cgd * must display the following acknowledgement:
17 1.1 cgd * This product includes software developed for the NetBSD Project
18 1.1 cgd * by Matthias Drochner.
19 1.1 cgd * 4. The name of the author may not be used to endorse or promote products
20 1.1 cgd * derived from this software without specific prior written permission.
21 1.1 cgd *
22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23 1.1 cgd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24 1.1 cgd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25 1.1 cgd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26 1.1 cgd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
27 1.1 cgd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 1.1 cgd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 1.1 cgd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 1.1 cgd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
31 1.1 cgd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 1.1 cgd *
33 1.1 cgd */
34 1.1 cgd
35 1.1 cgd /* support for compressed bootfiles
36 1.1 cgd (only read)
37 1.1 cgd replaces open(), close(), read(), lseek().
38 1.1 cgd original libsa open(), close(), read(), lseek() are called
39 1.1 cgd as oopen(), oclose(), oread() resp. olseek().
40 1.1 cgd compression parts stripped from zlib:gzio.c
41 1.1 cgd */
42 1.1 cgd
43 1.1 cgd /* gzio.c -- IO on .gz files
44 1.1 cgd * Copyright (C) 1995-1996 Jean-loup Gailly.
45 1.1 cgd * For conditions of distribution and use, see copyright notice in zlib.h
46 1.1 cgd */
47 1.1 cgd
48 1.4 drochner #include "stand.h"
49 1.4 drochner #ifdef _STANDALONE
50 1.3 drochner #include <lib/libkern/libkern.h>
51 1.3 drochner #include <lib/libz/zlib.h>
52 1.4 drochner #else
53 1.4 drochner #include <string.h>
54 1.4 drochner #include <zlib.h>
55 1.4 drochner #endif
56 1.1 cgd
57 1.1 cgd #define EOF (-1) /* needed by compression code */
58 1.1 cgd
59 1.1 cgd #ifdef SAVE_MEMORY
60 1.1 cgd #define Z_BUFSIZE 1024
61 1.1 cgd #else
62 1.1 cgd #define Z_BUFSIZE 4096
63 1.1 cgd #endif
64 1.1 cgd
65 1.1 cgd static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
66 1.1 cgd
67 1.1 cgd /* gzip flag byte */
68 1.1 cgd #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
69 1.1 cgd #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
70 1.1 cgd #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
71 1.1 cgd #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
72 1.1 cgd #define COMMENT 0x10 /* bit 4 set: file comment present */
73 1.1 cgd #define RESERVED 0xE0 /* bits 5..7: reserved */
74 1.1 cgd
75 1.1 cgd static struct sd {
76 1.1 cgd z_stream stream;
77 1.1 cgd int z_err; /* error code for last stream operation */
78 1.1 cgd int z_eof; /* set if end of input file */
79 1.1 cgd int fd;
80 1.1 cgd unsigned char *inbuf; /* input buffer */
81 1.1 cgd unsigned long crc; /* crc32 of uncompressed data */
82 1.1 cgd int transparent; /* 1 if input file is not a .gz file */
83 1.1 cgd } *ss[SOPEN_MAX];
84 1.1 cgd
85 1.1 cgd /*
86 1.1 cgd * compression utilities
87 1.1 cgd */
88 1.1 cgd
89 1.1 cgd void *zcalloc (opaque, items, size)
90 1.1 cgd void *opaque;
91 1.1 cgd unsigned items;
92 1.1 cgd unsigned size;
93 1.1 cgd {
94 1.1 cgd return(alloc(items * size));
95 1.1 cgd }
96 1.1 cgd
97 1.1 cgd void zcfree (opaque, ptr)
98 1.1 cgd void *opaque;
99 1.1 cgd void *ptr;
100 1.1 cgd {
101 1.1 cgd free(ptr, 0); /* XXX works only with modified allocator */
102 1.1 cgd }
103 1.1 cgd
104 1.1 cgd void zmemcpy(dest, source, len)
105 1.1 cgd unsigned char *dest;
106 1.1 cgd unsigned char *source;
107 1.1 cgd unsigned int len;
108 1.1 cgd {
109 1.1 cgd bcopy(source, dest, len);
110 1.1 cgd }
111 1.1 cgd
112 1.1 cgd static int get_byte(s)
113 1.1 cgd struct sd *s;
114 1.1 cgd {
115 1.1 cgd if (s->z_eof) return EOF;
116 1.1 cgd if (s->stream.avail_in == 0) {
117 1.1 cgd errno = 0;
118 1.1 cgd s->stream.avail_in = oread(s->fd, s->inbuf, Z_BUFSIZE);
119 1.1 cgd if (s->stream.avail_in == 0) {
120 1.1 cgd s->z_eof = 1;
121 1.1 cgd if (errno) s->z_err = Z_ERRNO;
122 1.1 cgd return EOF;
123 1.1 cgd }
124 1.1 cgd s->stream.next_in = s->inbuf;
125 1.1 cgd }
126 1.1 cgd s->stream.avail_in--;
127 1.1 cgd return *(s->stream.next_in)++;
128 1.1 cgd }
129 1.1 cgd
130 1.1 cgd static unsigned long getLong (s)
131 1.1 cgd struct sd *s;
132 1.1 cgd {
133 1.1 cgd unsigned long x = (unsigned long)get_byte(s);
134 1.1 cgd int c;
135 1.1 cgd
136 1.1 cgd x += ((unsigned long)get_byte(s))<<8;
137 1.1 cgd x += ((unsigned long)get_byte(s))<<16;
138 1.1 cgd c = get_byte(s);
139 1.1 cgd if (c == EOF) s->z_err = Z_DATA_ERROR;
140 1.1 cgd x += ((unsigned long)c)<<24;
141 1.1 cgd return x;
142 1.1 cgd }
143 1.1 cgd
144 1.1 cgd static void check_header(s)
145 1.1 cgd struct sd *s;
146 1.1 cgd {
147 1.1 cgd int method; /* method byte */
148 1.1 cgd int flags; /* flags byte */
149 1.1 cgd unsigned int len;
150 1.1 cgd int c;
151 1.1 cgd
152 1.1 cgd /* Check the gzip magic header */
153 1.1 cgd for (len = 0; len < 2; len++) {
154 1.1 cgd c = get_byte(s);
155 1.1 cgd if (c != gz_magic[len]) {
156 1.1 cgd s->transparent = 1;
157 1.1 cgd if (c != EOF) s->stream.avail_in++, s->stream.next_in--;
158 1.1 cgd s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
159 1.1 cgd return;
160 1.1 cgd }
161 1.1 cgd }
162 1.1 cgd method = get_byte(s);
163 1.1 cgd flags = get_byte(s);
164 1.1 cgd if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
165 1.1 cgd s->z_err = Z_DATA_ERROR;
166 1.1 cgd return;
167 1.1 cgd }
168 1.1 cgd
169 1.1 cgd /* Discard time, xflags and OS code: */
170 1.1 cgd for (len = 0; len < 6; len++) (void)get_byte(s);
171 1.1 cgd
172 1.1 cgd if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
173 1.1 cgd len = (unsigned int)get_byte(s);
174 1.1 cgd len += ((unsigned int)get_byte(s))<<8;
175 1.1 cgd /* len is garbage if EOF but the loop below will quit anyway */
176 1.1 cgd while (len-- != 0 && get_byte(s) != EOF) ;
177 1.1 cgd }
178 1.1 cgd if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
179 1.1 cgd while ((c = get_byte(s)) != 0 && c != EOF) ;
180 1.1 cgd }
181 1.1 cgd if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
182 1.1 cgd while ((c = get_byte(s)) != 0 && c != EOF) ;
183 1.1 cgd }
184 1.1 cgd if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
185 1.1 cgd for (len = 0; len < 2; len++) (void)get_byte(s);
186 1.1 cgd }
187 1.1 cgd s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
188 1.1 cgd }
189 1.1 cgd
190 1.1 cgd /*
191 1.1 cgd * new open(), close(), read(), lseek()
192 1.1 cgd */
193 1.1 cgd
194 1.1 cgd int
195 1.1 cgd open(fname, mode)
196 1.1 cgd const char *fname;
197 1.1 cgd int mode;
198 1.1 cgd {
199 1.1 cgd int fd;
200 1.1 cgd struct sd *s = 0;
201 1.1 cgd
202 1.1 cgd if(((fd = oopen(fname, mode)) == -1)
203 1.1 cgd || (mode != 0)) /* compression only for read */
204 1.1 cgd return(fd);
205 1.1 cgd
206 1.1 cgd ss[fd] = s = alloc(sizeof(struct sd));
207 1.1 cgd if(!s) goto errout;
208 1.1 cgd bzero(s, sizeof(struct sd));
209 1.1 cgd
210 1.1 cgd if(inflateInit2(&(s->stream), -15) != Z_OK)
211 1.1 cgd goto errout;
212 1.1 cgd
213 1.1 cgd s->stream.next_in = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
214 1.1 cgd if(!s->inbuf) {
215 1.1 cgd inflateEnd(&(s->stream));
216 1.1 cgd goto errout;
217 1.1 cgd }
218 1.1 cgd
219 1.1 cgd s->fd = fd;
220 1.1 cgd check_header(s); /* skip the .gz header */
221 1.1 cgd return(fd);
222 1.1 cgd
223 1.1 cgd errout:
224 1.1 cgd if(s) free(s, sizeof(struct sd));
225 1.1 cgd oclose(fd);
226 1.1 cgd return(-1);
227 1.1 cgd }
228 1.1 cgd
229 1.1 cgd int
230 1.1 cgd close(fd)
231 1.1 cgd int fd;
232 1.1 cgd {
233 1.2 thorpej struct open_file *f;
234 1.1 cgd struct sd *s;
235 1.2 thorpej
236 1.2 thorpej if ((unsigned)fd >= SOPEN_MAX) {
237 1.2 thorpej errno = EBADF;
238 1.2 thorpej return (-1);
239 1.2 thorpej }
240 1.2 thorpej f = &files[fd];
241 1.2 thorpej
242 1.2 thorpej if(!(f->f_flags & F_READ))
243 1.2 thorpej return(oclose(fd));
244 1.1 cgd
245 1.1 cgd s = ss[fd];
246 1.1 cgd
247 1.1 cgd inflateEnd(&(s->stream));
248 1.1 cgd
249 1.1 cgd free(s->inbuf, Z_BUFSIZE);
250 1.1 cgd free(s, sizeof(struct sd));
251 1.1 cgd
252 1.1 cgd return(oclose(fd));
253 1.1 cgd }
254 1.1 cgd
255 1.1 cgd ssize_t
256 1.1 cgd read(fd, buf, len)
257 1.1 cgd int fd;
258 1.1 cgd void *buf;
259 1.1 cgd size_t len;
260 1.1 cgd {
261 1.1 cgd struct sd *s;
262 1.1 cgd unsigned char *start = buf; /* starting point for crc computation */
263 1.1 cgd
264 1.1 cgd s = ss[fd];
265 1.1 cgd
266 1.1 cgd if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO) return -1;
267 1.1 cgd if (s->z_err == Z_STREAM_END) return 0; /* EOF */
268 1.1 cgd
269 1.1 cgd s->stream.next_out = buf;
270 1.1 cgd s->stream.avail_out = len;
271 1.1 cgd
272 1.1 cgd while (s->stream.avail_out != 0) {
273 1.1 cgd
274 1.1 cgd if (s->transparent) {
275 1.1 cgd /* Copy first the lookahead bytes: */
276 1.1 cgd unsigned int n = s->stream.avail_in;
277 1.1 cgd if (n > s->stream.avail_out) n = s->stream.avail_out;
278 1.1 cgd if (n > 0) {
279 1.1 cgd zmemcpy(s->stream.next_out, s->stream.next_in, n);
280 1.1 cgd s->stream.next_out += n;
281 1.1 cgd s->stream.next_in += n;
282 1.1 cgd s->stream.avail_out -= n;
283 1.1 cgd s->stream.avail_in -= n;
284 1.1 cgd }
285 1.1 cgd if (s->stream.avail_out > 0) {
286 1.1 cgd s->stream.avail_out -= oread(s->fd, s->stream.next_out, s->stream.avail_out);
287 1.1 cgd }
288 1.1 cgd return (int)(len - s->stream.avail_out);
289 1.1 cgd }
290 1.1 cgd
291 1.1 cgd if (s->stream.avail_in == 0 && !s->z_eof) {
292 1.1 cgd
293 1.1 cgd errno = 0;
294 1.1 cgd s->stream.avail_in = oread(fd, s->inbuf, Z_BUFSIZE);
295 1.1 cgd if (s->stream.avail_in == 0) {
296 1.1 cgd s->z_eof = 1;
297 1.1 cgd if (errno) {
298 1.1 cgd s->z_err = Z_ERRNO;
299 1.1 cgd break;
300 1.1 cgd }
301 1.1 cgd }
302 1.1 cgd s->stream.next_in = s->inbuf;
303 1.1 cgd }
304 1.1 cgd s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
305 1.1 cgd
306 1.1 cgd if (s->z_err == Z_STREAM_END) {
307 1.1 cgd /* Check CRC and original size */
308 1.1 cgd s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
309 1.1 cgd start = s->stream.next_out;
310 1.1 cgd
311 1.1 cgd if (getLong(s) != s->crc || getLong(s) != s->stream.total_out) {
312 1.1 cgd s->z_err = Z_DATA_ERROR;
313 1.1 cgd } else {
314 1.1 cgd /* Check for concatenated .gz files: */
315 1.1 cgd check_header(s);
316 1.1 cgd if (s->z_err == Z_OK) {
317 1.1 cgd inflateReset(&(s->stream));
318 1.1 cgd s->crc = crc32(0L, Z_NULL, 0);
319 1.1 cgd }
320 1.1 cgd }
321 1.1 cgd }
322 1.1 cgd if (s->z_err != Z_OK || s->z_eof) break;
323 1.1 cgd }
324 1.1 cgd s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
325 1.1 cgd
326 1.1 cgd return (int)(len - s->stream.avail_out);
327 1.1 cgd }
328 1.1 cgd
329 1.1 cgd off_t
330 1.1 cgd lseek(fd, offset, where)
331 1.1 cgd int fd;
332 1.1 cgd off_t offset;
333 1.1 cgd int where;
334 1.1 cgd {
335 1.1 cgd register struct open_file *f;
336 1.1 cgd struct sd *s;
337 1.1 cgd
338 1.1 cgd if ((unsigned)fd >= SOPEN_MAX) {
339 1.1 cgd errno = EBADF;
340 1.1 cgd return (-1);
341 1.1 cgd }
342 1.1 cgd f = &files[fd];;
343 1.1 cgd
344 1.1 cgd if(!(f->f_flags & F_READ))
345 1.1 cgd return(olseek(fd, offset, where));
346 1.1 cgd
347 1.1 cgd s = ss[fd];
348 1.1 cgd
349 1.1 cgd if(s->transparent) {
350 1.1 cgd off_t res = olseek(fd, offset, where);
351 1.1 cgd if(res != (off_t)-1) {
352 1.1 cgd /* make sure the lookahead buffer is invalid */
353 1.1 cgd s->stream.avail_in = 0;
354 1.1 cgd }
355 1.1 cgd return(res);
356 1.1 cgd }
357 1.1 cgd
358 1.1 cgd switch(where) {
359 1.1 cgd case SEEK_CUR:
360 1.1 cgd offset += s->stream.total_out;
361 1.1 cgd case SEEK_SET:
362 1.1 cgd
363 1.1 cgd /* if seek backwards, simply start from
364 1.1 cgd the beginning */
365 1.1 cgd if(offset < s->stream.total_out) {
366 1.1 cgd off_t res;
367 1.1 cgd void *sav_inbuf;
368 1.1 cgd
369 1.1 cgd res = olseek(fd, 0, SEEK_SET);
370 1.1 cgd if(res == (off_t)-1)
371 1.1 cgd return(res);
372 1.1 cgd /* ??? perhaps fallback to close / open */
373 1.1 cgd
374 1.1 cgd inflateEnd(&(s->stream));
375 1.1 cgd
376 1.1 cgd sav_inbuf = s->inbuf; /* don't allocate again */
377 1.1 cgd bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
378 1.1 cgd
379 1.1 cgd inflateInit2(&(s->stream), -15);
380 1.1 cgd s->stream.next_in = s->inbuf = sav_inbuf;
381 1.1 cgd
382 1.1 cgd s->fd = fd;
383 1.1 cgd check_header(s); /* skip the .gz header */
384 1.1 cgd }
385 1.1 cgd
386 1.1 cgd /* to seek forwards, throw away data */
387 1.1 cgd if(offset > s->stream.total_out) {
388 1.1 cgd off_t toskip = offset - s->stream.total_out;
389 1.1 cgd
390 1.1 cgd while(toskip > 0) {
391 1.1 cgd #define DUMMYBUFSIZE 256
392 1.1 cgd char dummybuf[DUMMYBUFSIZE];
393 1.1 cgd off_t len = toskip;
394 1.1 cgd if(len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
395 1.1 cgd if(read(fd, dummybuf, len) != len) {
396 1.1 cgd errno = EOFFSET;
397 1.1 cgd return((off_t)-1);
398 1.1 cgd }
399 1.1 cgd toskip -= len;
400 1.1 cgd }
401 1.1 cgd }
402 1.1 cgd #ifdef DEBUG
403 1.1 cgd if(offset != s->stream.total_out)
404 1.1 cgd panic("lseek compressed");
405 1.1 cgd #endif
406 1.1 cgd return(offset);
407 1.1 cgd case SEEK_END:
408 1.1 cgd errno = EOFFSET;
409 1.1 cgd break;
410 1.1 cgd default:
411 1.1 cgd errno = EINVAL;
412 1.1 cgd }
413 1.1 cgd return((off_t)-1);
414 1.1 cgd }
415