import less(1)
[unleashed/tickless.git] / usr / src / boot / lib / libstand / bzipfs.c
blobd99014ade76484d7f87cd28fe081e0101061bffa
1 /*
2 * Copyright (c) 1998 Michael Smith.
3 * Copyright (c) 2000 Maxim Sobolev
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
28 #include <sys/cdefs.h>
30 #ifndef REGRESSION
31 #include "stand.h"
32 #else
33 #include <stdlib.h>
34 #include <sys/errno.h>
35 #include <sys/fcntl.h>
36 #include <sys/types.h>
37 #include <sys/unistd.h>
39 struct open_file {
40 int f_flags; /* see F_* below */
41 void *f_fsdata; /* file system specific data */
43 #define F_READ 0x0001 /* file opened for reading */
44 #define EOFFSET (ELAST+8) /* relative seek not supported */
45 static inline u_int min(u_int a, u_int b) { return(a < b ? a : b); }
46 #define panic(x, y) abort()
47 #endif
49 #include <sys/stat.h>
50 #include <string.h>
51 #include <bzlib.h>
53 #define BZ_BUFSIZE 2048 /* XXX larger? */
55 struct bz_file
57 int bzf_rawfd;
58 bz_stream bzf_bzstream;
59 char bzf_buf[BZ_BUFSIZE];
60 int bzf_endseen;
63 static int bzf_fill(struct bz_file *z);
64 static int bzf_open(const char *path, struct open_file *f);
65 static int bzf_close(struct open_file *f);
66 static int bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
67 static off_t bzf_seek(struct open_file *f, off_t offset, int where);
68 static int bzf_stat(struct open_file *f, struct stat *sb);
70 #ifndef REGRESSION
71 struct fs_ops bzipfs_fsops = {
72 "bzip",
73 bzf_open,
74 bzf_close,
75 bzf_read,
76 null_write,
77 bzf_seek,
78 bzf_stat,
79 null_readdir
81 #endif
83 static int
84 bzf_fill(struct bz_file *bzf)
86 int result;
87 int req;
89 req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
90 result = 0;
92 /* If we need more */
93 if (req > 0) {
94 /* move old data to bottom of buffer */
95 if (req < BZ_BUFSIZE)
96 bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
98 /* read to fill buffer and update availibility data */
99 result = read(bzf->bzf_rawfd, bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
100 bzf->bzf_bzstream.next_in = bzf->bzf_buf;
101 if (result >= 0)
102 bzf->bzf_bzstream.avail_in += result;
104 return(result);
108 * Adapted from get_byte/check_header in libz
110 * Returns 0 if the header is OK, nonzero if not.
112 static int
113 get_byte(struct bz_file *bzf)
115 if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
116 return(-1);
117 bzf->bzf_bzstream.avail_in--;
118 return(*(bzf->bzf_bzstream.next_in)++);
121 static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
123 static int
124 check_header(struct bz_file *bzf)
126 unsigned int len;
127 int c;
129 /* Check the bzip2 magic header */
130 for (len = 0; len < 3; len++) {
131 c = get_byte(bzf);
132 if (c != bz_magic[len]) {
133 return(1);
136 /* Check that the block size is valid */
137 c = get_byte(bzf);
138 if (c < '1' || c > '9')
139 return(1);
141 /* Put back bytes that we've took from the input stream */
142 bzf->bzf_bzstream.next_in -= 4;
143 bzf->bzf_bzstream.avail_in += 4;
145 return(0);
148 static int
149 bzf_open(const char *fname, struct open_file *f)
151 static char *bzfname;
152 int rawfd;
153 struct bz_file *bzf;
154 char *cp;
155 int error;
156 struct stat sb;
158 /* Have to be in "just read it" mode */
159 if (f->f_flags != F_READ)
160 return(EPERM);
162 /* If the name already ends in .gz or .bz2, ignore it */
163 if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
164 || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
165 return(ENOENT);
167 /* Construct new name */
168 bzfname = malloc(strlen(fname) + 5);
169 if (bzfname == NULL)
170 return(ENOMEM);
171 sprintf(bzfname, "%s.bz2", fname);
173 /* Try to open the compressed datafile */
174 rawfd = open(bzfname, O_RDONLY);
175 free(bzfname);
176 if (rawfd == -1)
177 return(ENOENT);
179 if (fstat(rawfd, &sb) < 0) {
180 printf("bzf_open: stat failed\n");
181 close(rawfd);
182 return(ENOENT);
184 if (!S_ISREG(sb.st_mode)) {
185 printf("bzf_open: not a file\n");
186 close(rawfd);
187 return(EISDIR); /* best guess */
190 /* Allocate a bz_file structure, populate it */
191 bzf = malloc(sizeof(struct bz_file));
192 if (bzf == NULL)
193 return(ENOMEM);
194 bzero(bzf, sizeof(struct bz_file));
195 bzf->bzf_rawfd = rawfd;
197 /* Verify that the file is bzipped */
198 if (check_header(bzf)) {
199 close(bzf->bzf_rawfd);
200 free(bzf);
201 return(EFTYPE);
204 /* Initialise the inflation engine */
205 if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
206 printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
207 close(bzf->bzf_rawfd);
208 free(bzf);
209 return(EIO);
212 /* Looks OK, we'll take it */
213 f->f_fsdata = bzf;
214 return(0);
217 static int
218 bzf_close(struct open_file *f)
220 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
222 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
223 close(bzf->bzf_rawfd);
224 free(bzf);
225 return(0);
228 static int
229 bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
231 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
232 int error;
234 bzf->bzf_bzstream.next_out = buf; /* where and how much */
235 bzf->bzf_bzstream.avail_out = size;
237 while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) {
238 if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
239 printf("bzf_read: fill error\n");
240 return(EIO);
242 if (bzf->bzf_bzstream.avail_in == 0) { /* oops, unexpected EOF */
243 printf("bzf_read: unexpected EOF\n");
244 if (bzf->bzf_bzstream.avail_out == size)
245 return(EIO);
246 break;
249 error = BZ2_bzDecompress(&bzf->bzf_bzstream); /* decompression pass */
250 if (error == BZ_STREAM_END) { /* EOF, all done */
251 bzf->bzf_endseen = 1;
252 break;
254 if (error != BZ_OK) { /* argh, decompression error */
255 printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
256 return(EIO);
259 if (resid != NULL)
260 *resid = bzf->bzf_bzstream.avail_out;
261 return(0);
264 static int
265 bzf_rewind(struct open_file *f)
267 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
268 struct bz_file *bzf_tmp;
271 * Since bzip2 does not have an equivalent inflateReset function a crude
272 * one needs to be provided. The functions all called in such a way that
273 * at any time an error occurs a roll back can be done (effectively making
274 * this rewind 'atomic', either the reset occurs successfully or not at all,
275 * with no 'undefined' state happening).
278 /* Allocate a bz_file structure, populate it */
279 bzf_tmp = malloc(sizeof(struct bz_file));
280 if (bzf_tmp == NULL)
281 return(-1);
282 bzero(bzf_tmp, sizeof(struct bz_file));
283 bzf_tmp->bzf_rawfd = bzf->bzf_rawfd;
285 /* Initialise the inflation engine */
286 if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) {
287 free(bzf_tmp);
288 return(-1);
291 /* Seek back to the beginning of the file */
292 if (lseek(bzf->bzf_rawfd, 0, SEEK_SET) == -1) {
293 BZ2_bzDecompressEnd(&(bzf_tmp->bzf_bzstream));
294 free(bzf_tmp);
295 return(-1);
298 /* Free old bz_file data */
299 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
300 free(bzf);
302 /* Use the new bz_file data */
303 f->f_fsdata = bzf_tmp;
305 return(0);
308 static off_t
309 bzf_seek(struct open_file *f, off_t offset, int where)
311 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
312 off_t target;
313 char discard[16];
315 switch (where) {
316 case SEEK_SET:
317 target = offset;
318 break;
319 case SEEK_CUR:
320 target = offset + bzf->bzf_bzstream.total_out_lo32;
321 break;
322 default:
323 errno = EINVAL;
324 return(-1);
327 /* Can we get there from here? */
328 if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) {
329 errno = EOFFSET;
330 return -1;
333 /* if bzf_rewind was called then bzf has changed */
334 bzf = (struct bz_file *)f->f_fsdata;
336 /* skip forwards if required */
337 while (target > bzf->bzf_bzstream.total_out_lo32) {
338 errno = bzf_read(f, discard, min(sizeof(discard),
339 target - bzf->bzf_bzstream.total_out_lo32), NULL);
340 if (errno)
341 return(-1);
343 /* This is where we are (be honest if we overshot) */
344 return(bzf->bzf_bzstream.total_out_lo32);
347 static int
348 bzf_stat(struct open_file *f, struct stat *sb)
350 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
351 int result;
353 /* stat as normal, but indicate that size is unknown */
354 if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
355 sb->st_size = -1;
356 return(result);
359 void
360 bz_internal_error(int errorcode)
362 panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode);
365 #ifdef REGRESSION
366 /* Small test case, open and decompress test.bz2 */
367 int main()
369 struct open_file f;
370 char buf[1024];
371 size_t resid;
372 int err;
374 memset(&f, '\0', sizeof(f));
375 f.f_flags = F_READ;
376 err = bzf_open("test", &f);
377 if (err != 0)
378 exit(1);
379 do {
380 err = bzf_read(&f, buf, sizeof(buf), &resid);
381 } while (err == 0 && resid != sizeof(buf));
383 if (err != 0)
384 exit(2);
385 exit(0);
387 #endif