xref: /openbsd/sys/lib/libsa/cread.c (revision ba4b5c50)
1 /*	$OpenBSD: cread.c,v 1.15 2016/09/18 15:14:52 jsing Exp $	*/
2 /*	$NetBSD: cread.c,v 1.2 1997/02/04 18:38:20 thorpej Exp $	*/
3 
4 /*
5  * Copyright (c) 1996
6  *	Matthias Drochner.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  */
29 
30 /* support for compressed bootfiles
31  (only read)
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 #include "../libz/zlib.h"
45 
46 #define EOF (-1) /* needed by compression code */
47 
48 #define zmemcpy	memcpy
49 
50 #define Z_BUFSIZE 4096
51 
52 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
53 
54 /* gzip flag byte */
55 #define ASCII_FLAG	0x01 /* bit 0 set: file probably ascii text */
56 #define HEAD_CRC	0x02 /* bit 1 set: header CRC present */
57 #define EXTRA_FIELD	0x04 /* bit 2 set: extra field present */
58 #define ORIG_NAME	0x08 /* bit 3 set: original file name present */
59 #define COMMENT		0x10 /* bit 4 set: file comment present */
60 #define RESERVED	0xE0 /* bits 5..7: reserved */
61 
62 static struct sd {
63 	z_stream	stream;
64 	int		z_err;	/* error code for last stream operation */
65 	int		z_eof;	/* set if end of input file */
66 	int		fd;
67 	unsigned char	*inbuf;	/* input buffer */
68 	unsigned long	crc;	/* crc32 of uncompressed data */
69 	int		transparent; /* 1 if input file is not a .gz file */
70 } *ss[SOPEN_MAX];
71 
72 #ifdef DEBUG
73 int z_verbose = 0;
74 #endif
75 
76 /*
77  * compression utilities
78  */
79 
80 void	*zcalloc(void *, unsigned int, unsigned int);
81 void	zcfree(void *, void *);
82 
83 void *
zcalloc(void * opaque,unsigned int items,unsigned int size)84 zcalloc(void *opaque, unsigned int items, unsigned int size)
85 {
86 	return(alloc(items * size));
87 }
88 
89 void
zcfree(void * opaque,void * ptr)90 zcfree(void *opaque, void *ptr)
91 {
92 	free(ptr, 0); /* XXX works only with modified allocator */
93 }
94 
95 static int
get_byte(struct sd * s)96 get_byte(struct sd *s)
97 {
98 	if (s->z_eof)
99 		return EOF;
100 	if (s->stream.avail_in == 0) {
101 		errno = 0;
102 		s->stream.avail_in = oread(s->fd, s->inbuf, Z_BUFSIZE);
103 		if (s->stream.avail_in <= 0) {
104 			s->z_eof = 1;
105 			if (errno)
106 				s->z_err = Z_ERRNO;
107 			return EOF;
108 		}
109 		s->stream.next_in = s->inbuf;
110 	}
111 	s->stream.avail_in--;
112 	return *(s->stream.next_in)++;
113 }
114 
115 static unsigned long
getLong(struct sd * s)116 getLong(struct sd *s)
117 {
118 	unsigned long x = (unsigned long)get_byte(s);
119 	int c;
120 
121 	x += ((unsigned long)get_byte(s))<<8;
122 	x += ((unsigned long)get_byte(s))<<16;
123 	c = get_byte(s);
124 	if (c == EOF)
125 		s->z_err = Z_DATA_ERROR;
126 	x += ((unsigned long)c)<<24;
127 	return x;
128 }
129 
130 static void
check_header(struct sd * s)131 check_header(struct sd *s)
132 {
133 	int method; /* method byte */
134 	int flags;  /* flags byte */
135 	unsigned int len;
136 	int c;
137 
138 	/* Check the gzip magic header */
139 	for (len = 0; len < 2; len++) {
140 		c = get_byte(s);
141 		if (c != gz_magic[len]) {
142 			if (len != 0) {
143 				s->stream.avail_in++;
144 				s->stream.next_in--;
145 			}
146 			if (c != EOF) {
147 				s->stream.avail_in++;
148 				s->stream.next_in--;
149 				s->transparent = 1;
150 			}
151 
152 			s->z_err = s->stream.avail_in != 0 ? Z_OK :
153 			    Z_STREAM_END;
154 			return;
155 		}
156 	}
157 	method = get_byte(s);
158 	flags = get_byte(s);
159 	if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
160 		s->z_err = Z_DATA_ERROR;
161 		return;
162 	}
163 
164 	/* Discard time, xflags and OS code: */
165 	for (len = 0; len < 6; len++)
166 		(void)get_byte(s);
167 
168 	if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
169 		len  =  (unsigned int)get_byte(s);
170 		len += ((unsigned int)get_byte(s))<<8;
171 		/* len is garbage if EOF but the loop below will quit anyway */
172 		while (len-- != 0 && get_byte(s) != EOF)
173 			;
174 	}
175 	if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
176 		while ((c = get_byte(s)) != 0 && c != EOF)
177 			;
178 	}
179 	if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
180 		while ((c = get_byte(s)) != 0 && c != EOF)
181 			;
182 	}
183 	if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
184 		for (len = 0; len < 2; len++)
185 			(void)get_byte(s);
186 	}
187 	s->z_err = s->z_eof ? Z_DATA_ERROR : Z_OK;
188 }
189 
190 /*
191  * new open(), close(), read(), lseek()
192  */
193 
194 int
open(const char * fname,int mode)195 open(const char *fname, int mode)
196 {
197 	int fd;
198 	struct sd *s = 0;
199 
200 	if (((fd = oopen(fname, mode)) == -1) ||
201 	    (mode != 0)) /* compression only for read */
202 		return(fd);
203 
204 	ss[fd] = s = alloc(sizeof(struct sd));
205 	if (!s)
206 		goto errout;
207 	bzero(s, sizeof(struct sd));
208 
209 	if (inflateInit2(&(s->stream), -15) != Z_OK)
210 		goto errout;
211 
212 	s->stream.next_in  = s->inbuf = (unsigned char *)alloc(Z_BUFSIZE);
213 	if (!s->inbuf) {
214 		inflateEnd(&(s->stream));
215 		goto errout;
216 	}
217 
218 	s->fd = fd;
219 	check_header(s); /* skip the .gz header */
220 	return(fd);
221 
222 errout:
223 	if (s)
224 		free(s, sizeof(struct sd));
225 	oclose(fd);
226 	return(-1);
227 }
228 
229 int
close(int fd)230 close(int fd)
231 {
232 	struct open_file *f;
233 	struct sd *s;
234 
235 	if ((unsigned)fd >= SOPEN_MAX) {
236 		errno = EBADF;
237 		return (-1);
238 	}
239 	f = &files[fd];
240 
241 	if (!(f->f_flags & F_READ))
242 		return(oclose(fd));
243 
244 	s = ss[fd];
245 	if (s != NULL) {
246 		inflateEnd(&(s->stream));
247 
248 		free(s->inbuf, Z_BUFSIZE);
249 		free(s, sizeof(struct sd));
250 	}
251 
252 	return(oclose(fd));
253 }
254 
255 ssize_t
read(int fd,void * buf,size_t len)256 read(int fd, void *buf, size_t len)
257 {
258 	struct sd *s;
259 	unsigned char *start = buf; /* starting point for crc computation */
260 
261 	s = ss[fd];
262 
263 	if (s->z_err == Z_DATA_ERROR || s->z_err == Z_ERRNO)
264 		return -1;
265 	if (s->z_err == Z_STREAM_END)
266 		return 0;  /* EOF */
267 
268 	s->stream.next_out = buf;
269 	s->stream.avail_out = len;
270 
271 	while (s->stream.avail_out != 0) {
272 
273 		if (s->transparent) {
274 			/* Copy first the lookahead bytes: */
275 			unsigned int n = s->stream.avail_in;
276 
277 			if (n > s->stream.avail_out)
278 				n = s->stream.avail_out;
279 			if (n > 0) {
280 				zmemcpy(s->stream.next_out, s->stream.next_in, n);
281 				s->stream.next_out += n;
282 				s->stream.next_in   += n;
283 				s->stream.avail_out -= n;
284 				s->stream.avail_in  -= n;
285 			}
286 			if (s->stream.avail_out > 0) {
287 				int n;
288 
289 				n = oread(fd, s->stream.next_out,
290 				    s->stream.avail_out);
291 				if (n <= 0) {
292 					s->z_eof = 1;
293 					if (errno) {
294 						s->z_err = Z_ERRNO;
295 						break;
296 					}
297 				}
298 				s->stream.avail_out -= n;
299 			}
300 			len -= s->stream.avail_out;
301 			s->stream.total_in  += (unsigned long)len;
302 			s->stream.total_out += (unsigned long)len;
303 			if (len == 0)
304 				s->z_eof = 1;
305 			return (int)len;
306 		}
307 
308 		if (s->stream.avail_in == 0 && !s->z_eof) {
309 			errno = 0;
310 			s->stream.avail_in = oread(fd, s->inbuf, Z_BUFSIZE);
311 			if (s->stream.avail_in <= 0) {
312 				s->z_eof = 1;
313 				if (errno) {
314 					s->z_err = Z_ERRNO;
315 					break;
316 				}
317 			}
318 			s->stream.next_in = s->inbuf;
319 		}
320 		s->z_err = inflate(&(s->stream), Z_NO_FLUSH);
321 
322 		if (s->z_err == Z_STREAM_END) {
323 			/* Check CRC and original size */
324 			s->crc = crc32(s->crc, start,
325 			    (unsigned int)(s->stream.next_out - start));
326 			start = s->stream.next_out;
327 
328 			if (getLong(s) != s->crc) {
329 				s->z_err = Z_DATA_ERROR;
330 			} else {
331 				(void)getLong(s);
332 
333 				/* The uncompressed length returned by
334 				 * above getlong() may be different from
335 				 * s->stream.total_out in case of concatenated
336 				 * .gz files. Check for such files:
337 				 */
338 				check_header(s);
339 				if (s->z_err == Z_OK) {
340 					unsigned long total_in = s->stream.total_in;
341 					unsigned long total_out = s->stream.total_out;
342 
343 					inflateReset(&(s->stream));
344 					s->stream.total_in = total_in;
345 					s->stream.total_out = total_out;
346 					s->crc = crc32(0L, Z_NULL, 0);
347 				}
348 			}
349 		}
350 		if (s->z_err != Z_OK || s->z_eof)
351 			break;
352 	}
353 	s->crc = crc32(s->crc, start, (unsigned int)(s->stream.next_out - start));
354 
355 	return (int)(len - s->stream.avail_out);
356 }
357 
358 off_t
lseek(int fd,off_t offset,int where)359 lseek(int fd, off_t offset, int where)
360 {
361 	struct open_file *f;
362 	struct sd *s;
363 
364 	if ((unsigned)fd >= SOPEN_MAX) {
365 		errno = EBADF;
366 		return (-1);
367 	}
368 	f = &files[fd];
369 
370 	if (!(f->f_flags & F_READ))
371 		return(olseek(fd, offset, where));
372 
373 	s = ss[fd];
374 
375 	if (s->transparent) {
376 		off_t res = olseek(fd, offset, where);
377 		if (res != (off_t)-1) {
378 			/* make sure the lookahead buffer is invalid */
379 			s->stream.avail_in = 0;
380 		}
381 		return(res);
382 	}
383 
384 	switch(where) {
385 	case SEEK_CUR:
386 		offset += s->stream.total_out;
387 	case SEEK_SET:
388 
389 		/* if seek backwards, simply start from
390 		 the beginning */
391 		if (offset < s->stream.total_out) {
392 			off_t res;
393 			void *sav_inbuf;
394 
395 			res = olseek(fd, 0, SEEK_SET);
396 			if (res == (off_t)-1)
397 				return(res);
398 			/* ??? perhaps fallback to close / open */
399 
400 			inflateEnd(&(s->stream));
401 
402 			sav_inbuf = s->inbuf; /* don't allocate again */
403 			bzero(s, sizeof(struct sd)); /* this resets total_out to 0! */
404 
405 			inflateInit2(&(s->stream), -15);
406 			s->stream.next_in = s->inbuf = sav_inbuf;
407 
408 			s->fd = fd;
409 			check_header(s); /* skip the .gz header */
410 		}
411 
412 		/* to seek forwards, throw away data */
413 		if (offset > s->stream.total_out) {
414 			off_t toskip = offset - s->stream.total_out;
415 
416 			while(toskip > 0) {
417 #define DUMMYBUFSIZE 256
418 				char dummybuf[DUMMYBUFSIZE];
419 				size_t len = toskip;
420 				ssize_t n;
421 
422 				if (len > DUMMYBUFSIZE)
423 					len = DUMMYBUFSIZE;
424 				n = read(fd, dummybuf, len);
425 				if (n <= 0) {
426 					if (n == 0)
427 						errno = EINVAL;
428 					return((off_t)-1);
429 				}
430 				toskip -= n;
431 			}
432 		}
433 #ifdef DEBUG
434 		if (offset != s->stream.total_out)
435 			panic("lseek compressed");
436 #endif
437 		return(offset);
438 	case SEEK_END:
439 		errno = EOFFSET;
440 		break;
441 	default:
442 		errno = EINVAL;
443 	}
444 	return((off_t)-1);
445 }
446