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