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