xref: /minix3/sys/lib/libsa/cread.c (revision 0a6a1f1d05b60e214de2f05a7310ddd1f0e590e7)
1 /*	$NetBSD: cread.c,v 1.27 2015/07/25 07:06:11 isaki 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
crc32(uint32_t crc,const uint8_t * const buf,size_t len)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 *
zcalloc(void * opaque,unsigned int items,unsigned int size)124 zcalloc(void *opaque, unsigned int items, unsigned int size)
125 {
126 
127 	return alloc(items * size);
128 }
129 
130 void
zcfree(void * opaque,void * ptr)131 zcfree(void *opaque, void *ptr)
132 {
133 
134 	dealloc(ptr, 0); /* XXX works only with modified allocator */
135 }
136 
137 void
zmemcpy(unsigned char * dest,unsigned char * source,unsigned int len)138 zmemcpy(unsigned char *dest, unsigned char *source, unsigned int len)
139 {
140 
141 	memcpy(dest, source, len);
142 }
143 
144 static int
get_byte(struct sd * s)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
getLong(struct sd * s)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
check_header(struct sd * s)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
open(const char * fname,int mode)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 	ss[fd] = NULL;
288 	oclose(fd);
289 	return -1;
290 }
291 
292 int
close(int fd)293 close(int fd)
294 {
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 
304 	s = ss[fd];
305 
306 	if (s != NULL) {
307 		inflateEnd(&(s->stream));
308 
309 		dealloc(s->inbuf, Z_BUFSIZE);
310 		dealloc(s, sizeof(struct sd));
311 	}
312 
313 	return oclose(fd);
314 }
315 
316 ssize_t
read(int fd,void * buf,size_t len)317 read(int fd, void *buf, size_t len)
318 {
319 	struct sd *s;
320 	unsigned char *start = buf; /* starting point for crc computation */
321 
322 	s = ss[fd];
323 
324 	if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
325 		return -1;
326 	if (s->z_err == Z_STREAM_END)
327 		return 0;  /* EOF */
328 
329 	s->stream.next_out = buf;
330 	s->stream.avail_out = len;
331 
332 	while (s->stream.avail_out != 0) {
333 
334 		if (s->compressed == 0) {
335 			/* Copy first the lookahead bytes: */
336 			unsigned int n = s->stream.avail_in;
337 			if (n > s->stream.avail_out)
338 				n = s->stream.avail_out;
339 			if (n > 0) {
340 				zmemcpy(s->stream.next_out,
341 					s->stream.next_in, n);
342 				s->stream.next_out  += n;
343 				s->stream.next_in   += n;
344 				s->stream.avail_out -= n;
345 				s->stream.avail_in  -= n;
346 			}
347 			if (s->stream.avail_out > 0) {
348 				int got;
349 				got = oread(s->fd, s->stream.next_out,
350 				            s->stream.avail_out);
351 				if (got == -1)
352 					return got;
353 				s->stream.avail_out -= got;
354 			}
355 			return (int)(len - s->stream.avail_out);
356 		}
357 
358 		if (s->stream.avail_in == 0 && !s->z_eof) {
359 			int got;
360 			errno = 0;
361 			got = oread(fd, s->inbuf, Z_BUFSIZE);
362 			if (got <= 0) {
363 				s->z_eof = 1;
364 				if (errno) {
365 					s->z_err = Z_ERRNO;
366 					break;
367 				}
368 			}
369 			s->stream.avail_in = got;
370 			s->stream.next_in = s->inbuf;
371 		}
372 
373 		s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
374 
375 		if (s->z_err == Z_STREAM_END) {
376 			/* Check CRC and original size */
377 			s->crc = crc32(s->crc, start, (unsigned int)
378 					(s->stream.next_out - start));
379 			start = s->stream.next_out;
380 
381 			if (getLong(s) != s->crc ||
382 			    getLong(s) != s->stream.total_out) {
383 
384 				s->z_err = Z_DATA_ERROR;
385 			} else {
386 				/* Check for concatenated .gz files: */
387 				check_header(s);
388 				if (s->z_err == Z_OK) {
389 					inflateReset(&(s->stream));
390 					s->crc = crc32(0L, Z_NULL, 0);
391 				}
392 			}
393 		}
394 		if (s->z_err != Z_OK || s->z_eof)
395 			break;
396 	}
397 
398 	s->crc = crc32(s->crc, start,
399 	               (unsigned int)(s->stream.next_out - start));
400 
401 	return (int)(len - s->stream.avail_out);
402 }
403 
404 off_t
lseek(int fd,off_t offset,int where)405 lseek(int fd, off_t offset, int where)
406 {
407 	struct open_file *f;
408 	struct sd *s;
409 
410 #if !defined(LIBSA_NO_FD_CHECKING)
411 	if ((unsigned int)fd >= SOPEN_MAX) {
412 		errno = EBADF;
413 		return -1;
414 	}
415 #endif
416 	f = &files[fd];
417 
418 	if ((f->f_flags & F_READ) == 0)
419 		return olseek(fd, offset, where);
420 
421 	s = ss[fd];
422 
423 	if(s->compressed == 0) {
424 		off_t res = olseek(fd, offset, where);
425 		if (res != (off_t)-1) {
426 			/* make sure the lookahead buffer is invalid */
427 			s->stream.avail_in = 0;
428 		}
429 		return res;
430 	}
431 
432 	switch(where) {
433 	case SEEK_CUR:
434 		offset += s->stream.total_out;
435 	case SEEK_SET:
436 		/* if seek backwards, simply start from the beginning */
437 		if (offset < s->stream.total_out) {
438 			off_t res;
439 			void *sav_inbuf;
440 
441 			res = olseek(fd, 0, SEEK_SET);
442 			if(res == (off_t)-1)
443 				return res;
444 			/* ??? perhaps fallback to close / open */
445 
446 			inflateEnd(&(s->stream));
447 
448 			sav_inbuf = s->inbuf; /* don't allocate again */
449 			(void)memset(s, 0, sizeof(struct sd));
450 			/* this resets total_out to 0! */
451 
452 			inflateInit2(&(s->stream), -15);
453 			s->stream.next_in = s->inbuf = sav_inbuf;
454 
455 			s->fd = fd;
456 			check_header(s); /* skip the .gz header */
457 		}
458 
459 		/* to seek forwards, throw away data */
460 		if (offset > s->stream.total_out) {
461 			off_t toskip = offset - s->stream.total_out;
462 
463 			while (toskip > 0) {
464 #define DUMMYBUFSIZE 256
465 				char dummybuf[DUMMYBUFSIZE];
466 				off_t len = toskip;
467 
468 				if (len > DUMMYBUFSIZE)
469 					len = DUMMYBUFSIZE;
470 				if (read(fd, dummybuf, len) != len) {
471 					errno = EOFFSET;
472 					return (off_t)-1;
473 				}
474 				toskip -= len;
475 			}
476 		}
477 #ifdef DEBUG
478 		if (offset != s->stream.total_out)
479 			panic("lseek compressed");
480 #endif
481 		return offset;
482 	case SEEK_END:
483 		errno = EOFFSET;
484 		break;
485 	default:
486 		errno = EINVAL;
487 		break;
488 	}
489 
490 	return (off_t)-1;
491 }
492