xref: /netbsd/sys/lib/libsa/cread.c (revision 6550d01e)
1 /*	$NetBSD: cread.c,v 1.23 2009/03/25 18:41:06 tls 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/libz.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(struct sd *);
81 static unsigned long	getLong(struct sd *);
82 static void		check_header(struct sd *);
83 
84 /* XXX - find suitable header file for these: */
85 void	*zcalloc(void *, unsigned int, unsigned int);
86 void	zcfree(void *, void *);
87 void	zmemcpy(unsigned char *, unsigned char *, unsigned int);
88 
89 /*
90  * The libkern version of this function uses an 8K set of tables.
91  * This is the double-loop version of LE CRC32 from if_ethersubr,
92  * lightly modified -- it is 200 bytes smaller than the version using
93  * a 4-bit table and at least 8K smaller than the libkern version.
94  */
95 #ifndef ETHER_CRC_POLY_LE
96 #define ETHER_CRC_POLY_LE	0xedb88320
97 #endif
98 uint32_t
99 crc32(uint32_t crc, const uint8_t *const buf, size_t len)
100 {
101 	uint32_t c, carry;
102 	size_t i, j;
103 
104 	crc = 0xffffffffU ^ crc;
105 	for (i = 0; i < len; i++) {
106 	    c = buf[i];
107 	    for (j = 0; j < 8; j++) {
108 		carry = ((crc & 0x01) ? 1 : 0) ^ (c & 0x01);
109 		crc >>= 1;
110 		c >>= 1;
111 		if (carry) {
112 			crc = (crc ^ ETHER_CRC_POLY_LE);
113 		}
114 	    }
115 	}
116 	return (crc ^ 0xffffffffU);
117 }
118 
119 /*
120  * compression utilities
121  */
122 
123 void *
124 zcalloc(void *opaque, unsigned int items, unsigned int size)
125 {
126 
127 	return alloc(items * size);
128 }
129 
130 void
131 zcfree(void *opaque, void *ptr)
132 {
133 
134 	dealloc(ptr, 0); /* XXX works only with modified allocator */
135 }
136 
137 void
138 zmemcpy(unsigned char *dest, unsigned char *source, unsigned int len)
139 {
140 
141 	memcpy(dest, source, len);
142 }
143 
144 static int
145 get_byte(struct sd *s)
146 {
147 	if (s->z_eof)
148 		return EOF;
149 
150 	if (s->stream.avail_in == 0) {
151 		int got;
152 
153 		errno = 0;
154 		got = oread(s->fd, s->inbuf, Z_BUFSIZE);
155 		if (got <= 0) {
156 			s->z_eof = 1;
157 			if (errno)
158 				s->z_err = Z_ERRNO;
159 			return EOF;
160 		}
161 		s->stream.avail_in = got;
162 		s->stream.next_in = s->inbuf;
163 	}
164 	s->stream.avail_in--;
165 	return *(s->stream.next_in)++;
166 }
167 
168 static unsigned long
169 getLong(struct sd *s)
170 {
171 	unsigned long x;
172 	int c;
173 
174 	x  =  (unsigned long)get_byte(s);
175 	x += ((unsigned long)get_byte(s)) << 8;
176 	x += ((unsigned long)get_byte(s)) << 16;
177 	c = get_byte(s);
178 	if (c == EOF)
179 		s->z_err = Z_DATA_ERROR;
180 	x += ((unsigned long)c) << 24;
181 	return x;
182 }
183 
184 static void
185 check_header(struct sd *s)
186 {
187 	int method; /* method byte */
188 	int flags;  /* flags byte */
189 	unsigned int len;
190 	int c;
191 
192 	/* Check the gzip magic header */
193 	for (len = 0; len < 2; len++) {
194 		c = get_byte(s);
195 		if (c == gz_magic[len])
196 			continue;
197 		if ((c == EOF) && (len == 0))  {
198 			/*
199 			 * We must not change s->compressed if we are at EOF;
200 			 * we may have come to the end of a gzipped file and be
201 			 * check to see if another gzipped file is concatenated
202 			 * to this one. If one isn't, we still need to be able
203 			 * to lseek on this file as a compressed file.
204 			 */
205 			return;
206 		}
207 		s->compressed = 0;
208 		if (c != EOF) {
209 			s->stream.avail_in++;
210 			s->stream.next_in--;
211 		}
212 		s->z_err = s->stream.avail_in != 0 ? Z_OK : Z_STREAM_END;
213 		return;
214 	}
215 	s->compressed = 1;
216 	method = get_byte(s);
217 	flags = get_byte(s);
218 	if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
219 		s->z_err = Z_DATA_ERROR;
220 		return;
221 	}
222 
223 	/* Discard time, xflags and OS code: */
224 	for (len = 0; len < 6; len++)
225 		(void)get_byte(s);
226 
227 	if ((flags & EXTRA_FIELD) != 0) {
228 		/* skip the extra field */
229 		len  =  (unsigned int)get_byte(s);
230 		len += ((unsigned int)get_byte(s)) << 8;
231 		/* len is garbage if EOF but the loop below will quit anyway */
232 		while (len-- != 0 && get_byte(s) != EOF)
233 			/*void*/;
234 	}
235 	if ((flags & ORIG_NAME) != 0) {
236 		/* skip the original file name */
237 		while ((c = get_byte(s)) != 0 && c != EOF)
238 			/*void*/;
239 	}
240 	if ((flags & COMMENT) != 0) {
241 		/* skip the .gz file comment */
242 		while ((c = get_byte(s)) != 0 && c != EOF)
243 			/*void*/;
244 	}
245 	if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
246 		for (len = 0; len < 2; len++)
247 			(void)get_byte(s);
248 	}
249 	s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
250 }
251 
252 /*
253  * new open(), close(), read(), lseek()
254  */
255 
256 int
257 open(const char *fname, int mode)
258 {
259 	int fd;
260 	struct sd *s = 0;
261 
262 	if (((fd = oopen(fname, mode)) == -1) || (mode != 0))
263 		/* compression only for read */
264 		return fd;
265 
266 	ss[fd] = s = alloc(sizeof(struct sd));
267 	if (s == 0)
268 		goto errout;
269 	(void)memset(s, 0, sizeof(struct sd));
270 
271 	if (inflateInit2(&(s->stream), -15) != Z_OK)
272 		goto errout;
273 
274 	s->stream.next_in = s->inbuf = (unsigned char *)alloc(Z_BUFSIZE);
275 	if (s->inbuf == 0) {
276 		inflateEnd(&(s->stream));
277 		goto errout;
278 	}
279 
280 	s->fd = fd;
281 	check_header(s); /* skip the .gz header */
282 	return fd;
283 
284 errout:
285 	if (s != 0)
286 		dealloc(s, sizeof(struct sd));
287 	oclose(fd);
288 	return -1;
289 }
290 
291 int
292 close(int fd)
293 {
294 	struct open_file *f;
295 	struct sd *s;
296 
297 #if !defined(LIBSA_NO_FD_CHECKING)
298 	if ((unsigned int)fd >= SOPEN_MAX) {
299 		errno = EBADF;
300 		return -1;
301 	}
302 #endif
303 	f = &files[fd];
304 
305 	if ((f->f_flags & F_READ) == 0)
306 		return oclose(fd);
307 
308 	s = ss[fd];
309 
310 	inflateEnd(&(s->stream));
311 
312 	dealloc(s->inbuf, Z_BUFSIZE);
313 	dealloc(s, sizeof(struct sd));
314 
315 	return oclose(fd);
316 }
317 
318 ssize_t
319 read(int fd, void *buf, size_t len)
320 {
321 	struct sd *s;
322 	unsigned char *start = buf; /* starting point for crc computation */
323 
324 	s = ss[fd];
325 
326 	if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
327 		return -1;
328 	if (s->z_err == Z_STREAM_END)
329 		return 0;  /* EOF */
330 
331 	s->stream.next_out = buf;
332 	s->stream.avail_out = len;
333 
334 	while (s->stream.avail_out != 0) {
335 
336 		if (s->compressed == 0) {
337 			/* Copy first the lookahead bytes: */
338 			unsigned int n = s->stream.avail_in;
339 			if (n > s->stream.avail_out)
340 				n = s->stream.avail_out;
341 			if (n > 0) {
342 				zmemcpy(s->stream.next_out,
343 					s->stream.next_in, n);
344 				s->stream.next_out  += n;
345 				s->stream.next_in   += n;
346 				s->stream.avail_out -= n;
347 				s->stream.avail_in  -= n;
348 			}
349 			if (s->stream.avail_out > 0) {
350 				int got;
351 				got = oread(s->fd, s->stream.next_out,
352 				            s->stream.avail_out);
353 				if (got == -1)
354 					return got;
355 				s->stream.avail_out -= got;
356 			}
357 			return (int)(len - s->stream.avail_out);
358 		}
359 
360 		if (s->stream.avail_in == 0 && !s->z_eof) {
361 			int got;
362 			errno = 0;
363 			got = oread(fd, s->inbuf, Z_BUFSIZE);
364 			if (got <= 0) {
365 				s->z_eof = 1;
366 				if (errno) {
367 					s->z_err = Z_ERRNO;
368 					break;
369 				}
370 			}
371 			s->stream.avail_in = got;
372 			s->stream.next_in = s->inbuf;
373 		}
374 
375 		s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
376 
377 		if (s->z_err == Z_STREAM_END) {
378 			/* Check CRC and original size */
379 			s->crc = crc32(s->crc, start, (unsigned int)
380 					(s->stream.next_out - start));
381 			start = s->stream.next_out;
382 
383 			if (getLong(s) != s->crc ||
384 			    getLong(s) != s->stream.total_out) {
385 
386 				s->z_err = Z_DATA_ERROR;
387 			} else {
388 				/* Check for concatenated .gz files: */
389 				check_header(s);
390 				if (s->z_err == Z_OK) {
391 					inflateReset(&(s->stream));
392 					s->crc = crc32(0L, Z_NULL, 0);
393 				}
394 			}
395 		}
396 		if (s->z_err != Z_OK || s->z_eof)
397 			break;
398 	}
399 
400 	s->crc = crc32(s->crc, start,
401 	               (unsigned int)(s->stream.next_out - start));
402 
403 	return (int)(len - s->stream.avail_out);
404 }
405 
406 off_t
407 lseek(int fd, off_t offset, int where)
408 {
409 	struct open_file *f;
410 	struct sd *s;
411 
412 #if !defined(LIBSA_NO_FD_CHECKING)
413 	if ((unsigned int)fd >= SOPEN_MAX) {
414 		errno = EBADF;
415 		return -1;
416 	}
417 #endif
418 	f = &files[fd];
419 
420 	if ((f->f_flags & F_READ) == 0)
421 		return olseek(fd, offset, where);
422 
423 	s = ss[fd];
424 
425 	if(s->compressed == 0) {
426 		off_t res = olseek(fd, offset, where);
427 		if (res != (off_t)-1) {
428 			/* make sure the lookahead buffer is invalid */
429 			s->stream.avail_in = 0;
430 		}
431 		return res;
432 	}
433 
434 	switch(where) {
435 	case SEEK_CUR:
436 		offset += s->stream.total_out;
437 	case SEEK_SET:
438 		/* if seek backwards, simply start from the beginning */
439 		if (offset < s->stream.total_out) {
440 			off_t res;
441 			void *sav_inbuf;
442 
443 			res = olseek(fd, 0, SEEK_SET);
444 			if(res == (off_t)-1)
445 				return res;
446 			/* ??? perhaps fallback to close / open */
447 
448 			inflateEnd(&(s->stream));
449 
450 			sav_inbuf = s->inbuf; /* don't allocate again */
451 			(void)memset(s, 0, sizeof(struct sd));
452 			/* this resets total_out to 0! */
453 
454 			inflateInit2(&(s->stream), -15);
455 			s->stream.next_in = s->inbuf = sav_inbuf;
456 
457 			s->fd = fd;
458 			check_header(s); /* skip the .gz header */
459 		}
460 
461 		/* to seek forwards, throw away data */
462 		if (offset > s->stream.total_out) {
463 			off_t toskip = offset - s->stream.total_out;
464 
465 			while (toskip > 0) {
466 #define DUMMYBUFSIZE 256
467 				char dummybuf[DUMMYBUFSIZE];
468 				off_t len = toskip;
469 
470 				if (len > DUMMYBUFSIZE)
471 					len = DUMMYBUFSIZE;
472 				if (read(fd, dummybuf, len) != len) {
473 					errno = EOFFSET;
474 					return (off_t)-1;
475 				}
476 				toskip -= len;
477 			}
478 		}
479 #ifdef DEBUG
480 		if (offset != s->stream.total_out)
481 			panic("lseek compressed");
482 #endif
483 		return offset;
484 	case SEEK_END:
485 		errno = EOFFSET;
486 		break;
487 	default:
488 		errno = EINVAL;
489 		break;
490 	}
491 
492 	return (off_t)-1;
493 }
494