xref: /titanic_52/usr/src/boot/lib/libstand/bzipfs.c (revision 6298d5285e2394ba869ed330bfa7d7ef65fd7f55)
1 /*
2  * Copyright (c) 1998 Michael Smith.
3  * Copyright (c) 2000 Maxim Sobolev
4  * All rights reserved.
5  *
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.
14  *
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.
26  */
27 
28 #include <sys/cdefs.h>
29 
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>
38 
39 struct open_file {
40     int                 f_flags;        /* see F_* below */
41     void                *f_fsdata;      /* file system specific data */
42 };
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
48 
49 #include <sys/stat.h>
50 #include <string.h>
51 #include <bzlib.h>
52 
53 #define BZ_BUFSIZE 2048	/* XXX larger? */
54 
55 struct bz_file
56 {
57     int			bzf_rawfd;
58     bz_stream		bzf_bzstream;
59     char		bzf_buf[BZ_BUFSIZE];
60     int			bzf_endseen;
61 };
62 
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);
69 
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
80 };
81 #endif
82 
83 static int
84 bzf_fill(struct bz_file *bzf)
85 {
86     int		result;
87     int		req;
88 
89     req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
90     result = 0;
91 
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);
97 
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;
103     }
104     return(result);
105 }
106 
107 /*
108  * Adapted from get_byte/check_header in libz
109  *
110  * Returns 0 if the header is OK, nonzero if not.
111  */
112 static int
113 get_byte(struct bz_file *bzf)
114 {
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)++);
119 }
120 
121 static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
122 
123 static int
124 check_header(struct bz_file *bzf)
125 {
126     unsigned int len;
127     int		 c;
128 
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);
134 	}
135     }
136     /* Check that the block size is valid */
137     c = get_byte(bzf);
138     if (c < '1' || c > '9')
139 	return(1);
140 
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;
144 
145     return(0);
146 }
147 
148 static int
149 bzf_open(const char *fname, struct open_file *f)
150 {
151     static char		*bzfname;
152     int			rawfd;
153     struct bz_file	*bzf;
154     char		*cp;
155     int			error;
156     struct stat		sb;
157 
158     /* Have to be in "just read it" mode */
159     if (f->f_flags != F_READ)
160 	return(EPERM);
161 
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);
166 
167     /* Construct new name */
168     bzfname = malloc(strlen(fname) + 5);
169     if (bzfname == NULL)
170 	return(ENOMEM);
171     sprintf(bzfname, "%s.bz2", fname);
172 
173     /* Try to open the compressed datafile */
174     rawfd = open(bzfname, O_RDONLY);
175     free(bzfname);
176     if (rawfd == -1)
177 	return(ENOENT);
178 
179     if (fstat(rawfd, &sb) < 0) {
180 	printf("bzf_open: stat failed\n");
181 	close(rawfd);
182 	return(ENOENT);
183     }
184     if (!S_ISREG(sb.st_mode)) {
185 	printf("bzf_open: not a file\n");
186 	close(rawfd);
187 	return(EISDIR);			/* best guess */
188     }
189 
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;
196 
197     /* Verify that the file is bzipped */
198     if (check_header(bzf)) {
199 	close(bzf->bzf_rawfd);
200 	free(bzf);
201 	return(EFTYPE);
202     }
203 
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);
210     }
211 
212     /* Looks OK, we'll take it */
213     f->f_fsdata = bzf;
214     return(0);
215 }
216 
217 static int
218 bzf_close(struct open_file *f)
219 {
220     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
221 
222     BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
223     close(bzf->bzf_rawfd);
224     free(bzf);
225     return(0);
226 }
227 
228 static int
229 bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
230 {
231     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
232     int			error;
233 
234     bzf->bzf_bzstream.next_out = buf;			/* where and how much */
235     bzf->bzf_bzstream.avail_out = size;
236 
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);
241 	}
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;
247 	}
248 
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;
253 	}
254 	if (error != BZ_OK) {				/* argh, decompression error */
255 	    printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
256 	    return(EIO);
257 	}
258     }
259     if (resid != NULL)
260 	*resid = bzf->bzf_bzstream.avail_out;
261     return(0);
262 }
263 
264 static int
265 bzf_rewind(struct open_file *f)
266 {
267     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
268     struct bz_file	*bzf_tmp;
269 
270     /*
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).
276      */
277 
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;
284 
285     /* Initialise the inflation engine */
286     if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) {
287 	free(bzf_tmp);
288 	return(-1);
289     }
290 
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);
296     }
297 
298     /* Free old bz_file data */
299     BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
300     free(bzf);
301 
302     /* Use the new bz_file data */
303     f->f_fsdata = bzf_tmp;
304 
305     return(0);
306 }
307 
308 static off_t
309 bzf_seek(struct open_file *f, off_t offset, int where)
310 {
311     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
312     off_t		target;
313     char		discard[16];
314 
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);
325     }
326 
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;
331     }
332 
333     /* if bzf_rewind was called then bzf has changed */
334     bzf = (struct bz_file *)f->f_fsdata;
335 
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);
342     }
343     /* This is where we are (be honest if we overshot) */
344     return(bzf->bzf_bzstream.total_out_lo32);
345 }
346 
347 static int
348 bzf_stat(struct open_file *f, struct stat *sb)
349 {
350     struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
351     int			result;
352 
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);
357 }
358 
359 void
360 bz_internal_error(int errorcode)
361 {
362     panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode);
363 }
364 
365 #ifdef REGRESSION
366 /* Small test case, open and decompress test.bz2 */
367 int main()
368 {
369     struct open_file f;
370     char buf[1024];
371     size_t resid;
372     int err;
373 
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));
382 
383     if (err != 0)
384 	exit(2);
385     exit(0);
386 }
387 #endif
388