bzipfs.c revision 83610
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 * $FreeBSD: head/lib/libstand/bzipfs.c 83610 2001-09-18 13:01:12Z sobomax $
28 *
29 */
30
31#include "stand.h"
32
33#include <sys/stat.h>
34#include <string.h>
35#include <bzlib.h>
36
37#define BZ_BUFSIZE 2048	/* XXX larger? */
38
39struct bz_file
40{
41    int			bzf_rawfd;
42    bz_stream		bzf_bzstream;
43    char		bzf_buf[BZ_BUFSIZE];
44};
45
46static int	bzf_fill(struct bz_file *z);
47static int	bzf_open(const char *path, struct open_file *f);
48static int	bzf_close(struct open_file *f);
49static int	bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
50static off_t	bzf_seek(struct open_file *f, off_t offset, int where);
51static int	bzf_stat(struct open_file *f, struct stat *sb);
52
53struct fs_ops bzipfs_fsops = {
54    "bzip",
55    bzf_open,
56    bzf_close,
57    bzf_read,
58    null_write,
59    bzf_seek,
60    bzf_stat,
61    null_readdir
62};
63
64#if 0
65void *
66calloc(int items, size_t size)
67{
68    return(malloc(items * size));
69}
70#endif
71
72static int
73bzf_fill(struct bz_file *bzf)
74{
75    int		result;
76    int		req;
77
78    req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
79    result = 0;
80
81    /* If we need more */
82    if (req > 0) {
83	/* move old data to bottom of buffer */
84	if (req < BZ_BUFSIZE)
85	    bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
86
87	/* read to fill buffer and update availibility data */
88	result = read(bzf->bzf_rawfd, bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
89	bzf->bzf_bzstream.next_in = bzf->bzf_buf;
90	if (result >= 0)
91	    bzf->bzf_bzstream.avail_in += result;
92    }
93    return(result);
94}
95
96/*
97 * Adapted from get_byte/check_header in libz
98 *
99 * Returns 0 if the header is OK, nonzero if not.
100 */
101static int
102get_byte(struct bz_file *bzf)
103{
104    if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
105	return(-1);
106    bzf->bzf_bzstream.avail_in--;
107    return(*(bzf->bzf_bzstream.next_in)++);
108}
109
110static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
111
112static int
113check_header(struct bz_file *bzf)
114{
115    unsigned int len;
116    int		 c;
117
118    /* Check the bzip2 magic header */
119    for (len = 0; len < 3; len++) {
120	c = get_byte(bzf);
121	if (c != bz_magic[len]) {
122	    return(1);
123	}
124    }
125    /* Check that the block size is valid */
126    c = get_byte(bzf);
127    if (c < '1' || c > '9')
128	return(1);
129
130    /* Put back bytes that we've took from the input stream */
131    bzf->bzf_bzstream.next_in -= 4;
132    bzf->bzf_bzstream.avail_in += 4;
133
134    return(0);
135}
136
137static int
138bzf_open(const char *fname, struct open_file *f)
139{
140    static char		*bzfname;
141    int			rawfd;
142    struct bz_file	*bzf;
143    char		*cp;
144    int			error;
145    struct stat		sb;
146
147    /* Have to be in "just read it" mode */
148    if (f->f_flags != F_READ)
149	return(EPERM);
150
151    /* If the name already ends in .gz or .bz2, ignore it */
152    if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
153	    || !strcmp(cp, ".bz2")))
154	return(ENOENT);
155
156    /* Construct new name */
157    bzfname = malloc(strlen(fname) + 5);
158    sprintf(bzfname, "%s.bz2", fname);
159
160    /* Try to open the compressed datafile */
161    rawfd = open(bzfname, O_RDONLY);
162    free(bzfname);
163    if (rawfd == -1)
164	return(ENOENT);
165
166    if (fstat(rawfd, &sb) < 0) {
167	printf("bzf_open: stat failed\n");
168	close(rawfd);
169	return(ENOENT);
170    }
171    if (!S_ISREG(sb.st_mode)) {
172	printf("bzf_open: not a file\n");
173	close(rawfd);
174	return(EISDIR);			/* best guess */
175    }
176
177    /* Allocate a bz_file structure, populate it */
178    bzf = malloc(sizeof(struct bz_file));
179    bzero(bzf, sizeof(struct bz_file));
180    bzf->bzf_rawfd = rawfd;
181
182    /* Verify that the file is bzipped (XXX why do this afterwards?) */
183    if (check_header(bzf)) {
184	close(bzf->bzf_rawfd);
185	BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
186	free(bzf);
187	return(EFTYPE);
188    }
189
190    /* Initialise the inflation engine */
191    if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
192	printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
193	close(bzf->bzf_rawfd);
194	free(bzf);
195	return(EIO);
196    }
197
198    /* Looks OK, we'll take it */
199    f->f_fsdata = bzf;
200    return(0);
201}
202
203static int
204bzf_close(struct open_file *f)
205{
206    struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
207
208    BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
209    close(bzf->bzf_rawfd);
210    free(bzf);
211    return(0);
212}
213
214static int
215bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
216{
217    struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
218    int			error;
219
220    bzf->bzf_bzstream.next_out = buf;			/* where and how much */
221    bzf->bzf_bzstream.avail_out = size;
222
223    while (bzf->bzf_bzstream.avail_out) {
224	if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
225	    printf("bzf_read: fill error\n");
226	    return(-1);
227	}
228	if (bzf->bzf_bzstream.avail_in == 0) {		/* oops, unexpected EOF */
229	    printf("bzf_read: unexpected EOF\n");
230	    break;
231	}
232
233	error = BZ2_bzDecompress(&bzf->bzf_bzstream);	/* decompression pass */
234	if (error == BZ_STREAM_END) {			/* EOF, all done */
235	    break;
236	}
237	if (error != BZ_OK) {				/* argh, decompression error */
238	    printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
239	    errno = EIO;
240	    return(-1);
241	}
242    }
243    if (resid != NULL)
244	*resid = bzf->bzf_bzstream.avail_out;
245    return(0);
246}
247
248static off_t
249bzf_seek(struct open_file *f, off_t offset, int where)
250{
251    struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
252    off_t		target;
253    char		discard[16];
254
255    switch (where) {
256    case SEEK_SET:
257	target = offset;
258	break;
259    case SEEK_CUR:
260	target = offset + bzf->bzf_bzstream.total_out_lo32;
261	break;
262    default:
263	target = -1;
264    }
265
266    /* Can we get there from here? */
267    if (target < bzf->bzf_bzstream.total_out_lo32) {
268	errno = EOFFSET;
269	return -1;
270    }
271
272    /* skip forwards if required */
273    while (target > bzf->bzf_bzstream.total_out_lo32) {
274	if (bzf_read(f, discard, min(sizeof(discard), target - bzf->bzf_bzstream.total_out_lo32), NULL) == -1)
275	    return(-1);
276    }
277    /* This is where we are (be honest if we overshot) */
278    return (bzf->bzf_bzstream.total_out_lo32);
279}
280
281static int
282bzf_stat(struct open_file *f, struct stat *sb)
283{
284    struct bz_file	*bzf = (struct bz_file *)f->f_fsdata;
285    int			result;
286
287    /* stat as normal, but indicate that size is unknown */
288    if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
289	sb->st_size = -1;
290    return(result);
291}
292
293void
294bz_internal_error(int errorcode)
295{
296    panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode);
297}
298