FreeBSD/Linux Kernel Cross Reference
sys/lib/libsa/cread.c
1 /* $NetBSD: cread.c,v 1.15 2004/03/24 17:29:14 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 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28
29 /*
30 * Support for compressed bootfiles (only read)
31 *
32 * - replaces open(), close(), read(), lseek().
33 * - original libsa open(), close(), read(), lseek() are called
34 * as oopen(), oclose(), oread() resp. olseek().
35 * - compression parts stripped from zlib:gzio.c
36 */
37
38 /* gzio.c -- IO on .gz files
39 * Copyright (C) 1995-1996 Jean-loup Gailly.
40 * For conditions of distribution and use, see copyright notice in zlib.h
41 */
42
43 #include "stand.h"
44 #ifdef _STANDALONE
45 #include <lib/libkern/libkern.h>
46 #include <lib/libz/zlib.h>
47 #else
48 #include <string.h>
49 #include <zlib.h>
50 #endif
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 const 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 compressed; /* 1 if input file is a .gz file */
78 } *ss[SOPEN_MAX];
79
80 static int get_byte __P((struct sd *));
81 static unsigned long getLong __P((struct sd *));
82 static void check_header __P((struct sd *));
83
84 /* XXX - find suitable header file for these: */
85 void *zcalloc __P((void *, unsigned int, unsigned int));
86 void zcfree __P((void *, void *));
87 void zmemcpy __P((unsigned char *, unsigned char *, unsigned int));
88
89
90 /*
91 * compression utilities
92 */
93
94 void *
95 zcalloc (opaque, items, size)
96 void *opaque;
97 unsigned items;
98 unsigned size;
99 {
100 return(alloc(items * size));
101 }
102
103 void
104 zcfree (opaque, ptr)
105 void *opaque;
106 void *ptr;
107 {
108 free(ptr, 0); /* XXX works only with modified allocator */
109 }
110
111 void
112 zmemcpy(dest, source, len)
113 unsigned char *dest;
114 unsigned char *source;
115 unsigned int len;
116 {
117 bcopy(source, dest, len);
118 }
119
120 static int
121 get_byte(s)
122 struct sd *s;
123 {
124 if (s->z_eof)
125 return (EOF);
126
127 if (s->stream.avail_in == 0) {
128 int got;
129
130 errno = 0;
131 got = oread(s->fd, s->inbuf, Z_BUFSIZE);
132 if (got <= 0) {
133 s->z_eof = 1;
134 if (errno) s->z_err = Z_ERRNO;
135 return EOF;
136 }
137 s->stream.avail_in = got;
138 s->stream.next_in = s->inbuf;
139 }
140 s->stream.avail_in--;
141 return *(s->stream.next_in)++;
142 }
143
144 static unsigned long
145 getLong (s)
146 struct sd *s;
147 {
148 unsigned long x = (unsigned long)get_byte(s);
149 int c;
150
151 x += ((unsigned long)get_byte(s)) << 8;
152 x += ((unsigned long)get_byte(s)) << 16;
153 c = get_byte(s);
154 if (c == EOF)
155 s->z_err = Z_DATA_ERROR;
156 x += ((unsigned long)c)<<24;
157 return x;
158 }
159
160 static void
161 check_header(s)
162 struct sd *s;
163 {
164 int method; /* method byte */
165 int flags; /* flags byte */
166 unsigned int len;
167 int c;
168
169 /* Check the gzip magic header */
170 for (len = 0; len < 2; len++) {
171 c = get_byte(s);
172 if (c == gz_magic[len])
173 continue;
174 if ((c == EOF) && (len == 0)) {
175 /*
176 * We must not change s->compressed if we are at EOF;
177 * we may have come to the end of a gzipped file and be
178 * check to see if another gzipped file is concatenated
179 * to this one. If one isn't, we still need to be able
180 * to lseek on this file as a compressed file.
181 */
182 return;
183 }
184 s->compressed = 0;
185 if (c != EOF) {
186 s->stream.avail_in++;
187 s->stream.next_in--;
188 }
189 s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
190 return;
191 }
192 s->compressed = 1;
193 method = get_byte(s);
194 flags = get_byte(s);
195 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
196 s->z_err = Z_DATA_ERROR;
197 return;
198 }
199
200 /* Discard time, xflags and OS code: */
201 for (len = 0; len < 6; len++)
202 (void)get_byte(s);
203
204 if ((flags & EXTRA_FIELD) != 0) {
205 /* skip the extra field */
206 len = (unsigned int)get_byte(s);
207 len += ((unsigned int)get_byte(s)) << 8;
208 /* len is garbage if EOF but the loop below will quit anyway */
209 while (len-- != 0 && get_byte(s) != EOF) /*void*/;
210 }
211 if ((flags & ORIG_NAME) != 0) {
212 /* skip the original file name */
213 while ((c = get_byte(s)) != 0 && c != EOF) /*void*/;
214 }
215 if ((flags & COMMENT) != 0) {
216 /* skip the .gz file comment */
217 while ((c = get_byte(s)) != 0 && c != EOF) /*void*/;
218 }
219 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
220 for (len = 0; len < 2; len++)
221 (void)get_byte(s);
222 }
223 s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
224 }
225
226 /*
227 * new open(), close(), read(), lseek()
228 */
229
230 int
231 open(fname, mode)
232 const char *fname;
233 int mode;
234 {
235 int fd;
236 struct sd *s = 0;
237
238 if (((fd = oopen(fname, mode)) == -1) || (mode != 0))
239 /* compression only for read */
240 return (fd);
241
242 ss[fd] = s = alloc(sizeof(struct sd));
243 if (s == 0)
244 goto errout;
245 bzero(s, sizeof(struct sd));
246
247 if (inflateInit2(&(s->stream), -15) != Z_OK)
248 goto errout;
249
250 s->stream.next_in = s->inbuf = (unsigned char*)alloc(Z_BUFSIZE);
251 if (s->inbuf == 0) {
252 inflateEnd(&(s->stream));
253 goto errout;
254 }
255
256 s->fd = fd;
257 check_header(s); /* skip the .gz header */
258 return (fd);
259
260 errout:
261 if (s != 0)
262 free(s, sizeof(struct sd));
263 oclose(fd);
264 return (-1);
265 }
266
267 int
268 close(fd)
269 int fd;
270 {
271 struct open_file *f;
272 struct sd *s;
273
274 #if !defined(LIBSA_NO_FD_CHECKING)
275 if ((unsigned)fd >= SOPEN_MAX) {
276 errno = EBADF;
277 return (-1);
278 }
279 #endif
280 f = &files[fd];
281
282 if ((f->f_flags & F_READ) == 0)
283 return (oclose(fd));
284
285 s = ss[fd];
286
287 inflateEnd(&(s->stream));
288
289 free(s->inbuf, Z_BUFSIZE);
290 free(s, sizeof(struct sd));
291
292 return (oclose(fd));
293 }
294
295 ssize_t
296 read(fd, buf, len)
297 int fd;
298 void *buf;
299 size_t len;
300 {
301 struct sd *s;
302 unsigned char *start = buf; /* starting point for crc computation */
303
304 s = ss[fd];
305
306 if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
307 return (-1);
308 if (s->z_err == Z_STREAM_END)
309 return (0); /* EOF */
310
311 s->stream.next_out = buf;
312 s->stream.avail_out = len;
313
314 while (s->stream.avail_out != 0) {
315
316 if (s->compressed == 0) {
317 /* Copy first the lookahead bytes: */
318 unsigned int n = s->stream.avail_in;
319 if (n > s->stream.avail_out)
320 n = s->stream.avail_out;
321 if (n > 0) {
322 zmemcpy(s->stream.next_out,
323 s->stream.next_in, n);
324 s->stream.next_out += n;
325 s->stream.next_in += n;
326 s->stream.avail_out -= n;
327 s->stream.avail_in -= n;
328 }
329 if (s->stream.avail_out > 0) {
330 int got;
331 got = oread(s->fd, s->stream.next_out,
332 s->stream.avail_out);
333 if (got == -1)
334 return (got);
335 s->stream.avail_out -= got;
336 }
337 return (int)(len - s->stream.avail_out);
338 }
339
340 if (s->stream.avail_in == 0 && !s->z_eof) {
341 int got;
342 errno = 0;
343 got = oread(fd, s->inbuf, Z_BUFSIZE);
344 if (got <= 0) {
345 s->z_eof = 1;
346 if (errno) {
347 s->z_err = Z_ERRNO;
348 break;
349 }
350 }
351 s->stream.avail_in = got;
352 s->stream.next_in = s->inbuf;
353 }
354
355 s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
356
357 if (s->z_err == Z_STREAM_END) {
358 /* Check CRC and original size */
359 s->crc = crc32(s->crc, start, (unsigned int)
360 (s->stream.next_out - start));
361 start = s->stream.next_out;
362
363 if (getLong(s) != s->crc ||
364 getLong(s) != s->stream.total_out) {
365
366 s->z_err = Z_DATA_ERROR;
367 } else {
368 /* Check for concatenated .gz files: */
369 check_header(s);
370 if (s->z_err == Z_OK) {
371 inflateReset(&(s->stream));
372 s->crc = crc32(0L, Z_NULL, 0);
373 }
374 }
375 }
376 if (s->z_err != Z_OK || s->z_eof)
377 break;
378 }
379
380 s->crc = crc32(s->crc, start,
381 (unsigned int)(s->stream.next_out - start));
382
383 return (int)(len - s->stream.avail_out);
384 }
385
386 off_t
387 lseek(fd, offset, where)
388 int fd;
389 off_t offset;
390 int where;
391 {
392 struct open_file *f;
393 struct sd *s;
394
395 #if !defined(LIBSA_NO_FD_CHECKING)
396 if ((unsigned)fd >= SOPEN_MAX) {
397 errno = EBADF;
398 return (-1);
399 }
400 #endif
401 f = &files[fd];
402
403 if ((f->f_flags & F_READ) == 0)
404 return (olseek(fd, offset, where));
405
406 s = ss[fd];
407
408 if(s->compressed == 0) {
409 off_t res = olseek(fd, offset, where);
410 if (res != (off_t)-1) {
411 /* make sure the lookahead buffer is invalid */
412 s->stream.avail_in = 0;
413 }
414 return (res);
415 }
416
417 switch(where) {
418 case SEEK_CUR:
419 offset += s->stream.total_out;
420 case SEEK_SET:
421 /* if seek backwards, simply start from the beginning */
422 if (offset < s->stream.total_out) {
423 off_t res;
424 void *sav_inbuf;
425
426 res = olseek(fd, 0, SEEK_SET);
427 if(res == (off_t)-1)
428 return(res);
429 /* ??? perhaps fallback to close / open */
430
431 inflateEnd(&(s->stream));
432
433 sav_inbuf = s->inbuf; /* don't allocate again */
434 bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
435
436 inflateInit2(&(s->stream), -15);
437 s->stream.next_in = s->inbuf = sav_inbuf;
438
439 s->fd = fd;
440 check_header(s); /* skip the .gz header */
441 }
442
443 /* to seek forwards, throw away data */
444 if (offset > s->stream.total_out) {
445 off_t toskip = offset - s->stream.total_out;
446
447 while (toskip > 0) {
448 #define DUMMYBUFSIZE 256
449 char dummybuf[DUMMYBUFSIZE];
450 off_t len = toskip;
451 if (len > DUMMYBUFSIZE) len = DUMMYBUFSIZE;
452 if (read(fd, dummybuf, len) != len) {
453 errno = EOFFSET;
454 return ((off_t)-1);
455 }
456 toskip -= len;
457 }
458 }
459 #ifdef DEBUG
460 if (offset != s->stream.total_out)
461 panic("lseek compressed");
462 #endif
463 return (offset);
464 case SEEK_END:
465 errno = EOFFSET;
466 break;
467 default:
468 errno = EINVAL;
469 }
470
471 return ((off_t)-1);
472 }
Cache object: 4a9bfc934a10c2d9c4a1183d46a7851c
|