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