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