1*479ab7f0SSascha Wildner /*
2*479ab7f0SSascha Wildner * Copyright (c) 1998 Michael Smith.
3*479ab7f0SSascha Wildner * Copyright (c) 2000 Maxim Sobolev
4*479ab7f0SSascha Wildner * All rights reserved.
5*479ab7f0SSascha Wildner *
6*479ab7f0SSascha Wildner * Redistribution and use in source and binary forms, with or without
7*479ab7f0SSascha Wildner * modification, are permitted provided that the following conditions
8*479ab7f0SSascha Wildner * are met:
9*479ab7f0SSascha Wildner * 1. Redistributions of source code must retain the above copyright
10*479ab7f0SSascha Wildner * notice, this list of conditions and the following disclaimer.
11*479ab7f0SSascha Wildner * 2. Redistributions in binary form must reproduce the above copyright
12*479ab7f0SSascha Wildner * notice, this list of conditions and the following disclaimer in the
13*479ab7f0SSascha Wildner * documentation and/or other materials provided with the distribution.
14*479ab7f0SSascha Wildner *
15*479ab7f0SSascha Wildner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16*479ab7f0SSascha Wildner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17*479ab7f0SSascha Wildner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18*479ab7f0SSascha Wildner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19*479ab7f0SSascha Wildner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20*479ab7f0SSascha Wildner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21*479ab7f0SSascha Wildner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22*479ab7f0SSascha Wildner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23*479ab7f0SSascha Wildner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24*479ab7f0SSascha Wildner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25*479ab7f0SSascha Wildner * SUCH DAMAGE.
26*479ab7f0SSascha Wildner *
27*479ab7f0SSascha Wildner * $FreeBSD: src/lib/libstand/bzipfs.c,v 1.2.2.3 2002/04/08 13:50:09 sobomax Exp $
28*479ab7f0SSascha Wildner * $DragonFly: src/lib/libstand/bzipfs.c,v 1.4 2007/05/13 18:33:56 swildner Exp $
29*479ab7f0SSascha Wildner *
30*479ab7f0SSascha Wildner */
31*479ab7f0SSascha Wildner
32*479ab7f0SSascha Wildner #include "stand.h"
33*479ab7f0SSascha Wildner
34*479ab7f0SSascha Wildner #include <sys/stat.h>
35*479ab7f0SSascha Wildner #include <string.h>
36*479ab7f0SSascha Wildner #include <bzlib.h>
37*479ab7f0SSascha Wildner
38*479ab7f0SSascha Wildner #define BZ_BUFSIZE 2048 /* XXX larger? */
39*479ab7f0SSascha Wildner
40*479ab7f0SSascha Wildner struct bz_file
41*479ab7f0SSascha Wildner {
42*479ab7f0SSascha Wildner int bzf_rawfd;
43*479ab7f0SSascha Wildner bz_stream bzf_bzstream;
44*479ab7f0SSascha Wildner char bzf_buf[BZ_BUFSIZE];
45*479ab7f0SSascha Wildner };
46*479ab7f0SSascha Wildner
47*479ab7f0SSascha Wildner static int bzf_fill(struct bz_file *z);
48*479ab7f0SSascha Wildner static int bzf_open(const char *path, struct open_file *f);
49*479ab7f0SSascha Wildner static int bzf_close(struct open_file *f);
50*479ab7f0SSascha Wildner static int bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid);
51*479ab7f0SSascha Wildner static off_t bzf_seek(struct open_file *f, off_t offset, int where);
52*479ab7f0SSascha Wildner static int bzf_stat(struct open_file *f, struct stat *sb);
53*479ab7f0SSascha Wildner
54*479ab7f0SSascha Wildner struct fs_ops bzipfs_fsops = {
55*479ab7f0SSascha Wildner "bzip",
56*479ab7f0SSascha Wildner bzf_open,
57*479ab7f0SSascha Wildner bzf_close,
58*479ab7f0SSascha Wildner bzf_read,
59*479ab7f0SSascha Wildner null_write,
60*479ab7f0SSascha Wildner bzf_seek,
61*479ab7f0SSascha Wildner bzf_stat,
62*479ab7f0SSascha Wildner null_readdir
63*479ab7f0SSascha Wildner };
64*479ab7f0SSascha Wildner
65*479ab7f0SSascha Wildner #if 0
66*479ab7f0SSascha Wildner void *
67*479ab7f0SSascha Wildner calloc(int items, size_t size)
68*479ab7f0SSascha Wildner {
69*479ab7f0SSascha Wildner return(malloc(items * size));
70*479ab7f0SSascha Wildner }
71*479ab7f0SSascha Wildner #endif
72*479ab7f0SSascha Wildner
73*479ab7f0SSascha Wildner static int
bzf_fill(struct bz_file * bzf)74*479ab7f0SSascha Wildner bzf_fill(struct bz_file *bzf)
75*479ab7f0SSascha Wildner {
76*479ab7f0SSascha Wildner int result;
77*479ab7f0SSascha Wildner int req;
78*479ab7f0SSascha Wildner
79*479ab7f0SSascha Wildner req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
80*479ab7f0SSascha Wildner result = 0;
81*479ab7f0SSascha Wildner
82*479ab7f0SSascha Wildner /* If we need more */
83*479ab7f0SSascha Wildner if (req > 0) {
84*479ab7f0SSascha Wildner /* move old data to bottom of buffer */
85*479ab7f0SSascha Wildner if (req < BZ_BUFSIZE)
86*479ab7f0SSascha Wildner bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
87*479ab7f0SSascha Wildner
88*479ab7f0SSascha Wildner /* read to fill buffer and update availibility data */
89*479ab7f0SSascha Wildner result = read(bzf->bzf_rawfd, bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
90*479ab7f0SSascha Wildner bzf->bzf_bzstream.next_in = bzf->bzf_buf;
91*479ab7f0SSascha Wildner if (result >= 0)
92*479ab7f0SSascha Wildner bzf->bzf_bzstream.avail_in += result;
93*479ab7f0SSascha Wildner }
94*479ab7f0SSascha Wildner return(result);
95*479ab7f0SSascha Wildner }
96*479ab7f0SSascha Wildner
97*479ab7f0SSascha Wildner /*
98*479ab7f0SSascha Wildner * Adapted from get_byte/check_header in libz
99*479ab7f0SSascha Wildner *
100*479ab7f0SSascha Wildner * Returns 0 if the header is OK, nonzero if not.
101*479ab7f0SSascha Wildner */
102*479ab7f0SSascha Wildner static int
get_byte(struct bz_file * bzf)103*479ab7f0SSascha Wildner get_byte(struct bz_file *bzf)
104*479ab7f0SSascha Wildner {
105*479ab7f0SSascha Wildner if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
106*479ab7f0SSascha Wildner return(-1);
107*479ab7f0SSascha Wildner bzf->bzf_bzstream.avail_in--;
108*479ab7f0SSascha Wildner return(*(bzf->bzf_bzstream.next_in)++);
109*479ab7f0SSascha Wildner }
110*479ab7f0SSascha Wildner
111*479ab7f0SSascha Wildner static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
112*479ab7f0SSascha Wildner
113*479ab7f0SSascha Wildner static int
check_header(struct bz_file * bzf)114*479ab7f0SSascha Wildner check_header(struct bz_file *bzf)
115*479ab7f0SSascha Wildner {
116*479ab7f0SSascha Wildner unsigned int len;
117*479ab7f0SSascha Wildner int c;
118*479ab7f0SSascha Wildner
119*479ab7f0SSascha Wildner /* Check the bzip2 magic header */
120*479ab7f0SSascha Wildner for (len = 0; len < 3; len++) {
121*479ab7f0SSascha Wildner c = get_byte(bzf);
122*479ab7f0SSascha Wildner if (c != bz_magic[len]) {
123*479ab7f0SSascha Wildner return(1);
124*479ab7f0SSascha Wildner }
125*479ab7f0SSascha Wildner }
126*479ab7f0SSascha Wildner /* Check that the block size is valid */
127*479ab7f0SSascha Wildner c = get_byte(bzf);
128*479ab7f0SSascha Wildner if (c < '1' || c > '9')
129*479ab7f0SSascha Wildner return(1);
130*479ab7f0SSascha Wildner
131*479ab7f0SSascha Wildner /* Put back bytes that we've took from the input stream */
132*479ab7f0SSascha Wildner bzf->bzf_bzstream.next_in -= 4;
133*479ab7f0SSascha Wildner bzf->bzf_bzstream.avail_in += 4;
134*479ab7f0SSascha Wildner
135*479ab7f0SSascha Wildner return(0);
136*479ab7f0SSascha Wildner }
137*479ab7f0SSascha Wildner
138*479ab7f0SSascha Wildner static int
bzf_open(const char * fname,struct open_file * f)139*479ab7f0SSascha Wildner bzf_open(const char *fname, struct open_file *f)
140*479ab7f0SSascha Wildner {
141*479ab7f0SSascha Wildner static char *bzfname;
142*479ab7f0SSascha Wildner int rawfd;
143*479ab7f0SSascha Wildner struct bz_file *bzf;
144*479ab7f0SSascha Wildner char *cp;
145*479ab7f0SSascha Wildner int error;
146*479ab7f0SSascha Wildner struct stat sb;
147*479ab7f0SSascha Wildner
148*479ab7f0SSascha Wildner /* Have to be in "just read it" mode */
149*479ab7f0SSascha Wildner if ((f->f_flags & (F_READ | F_WRITE)) != F_READ)
150*479ab7f0SSascha Wildner return(EPERM);
151*479ab7f0SSascha Wildner
152*479ab7f0SSascha Wildner /* If the name already ends in .gz or .bz2, ignore it */
153*479ab7f0SSascha Wildner if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz")
154*479ab7f0SSascha Wildner || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
155*479ab7f0SSascha Wildner return(ENOENT);
156*479ab7f0SSascha Wildner
157*479ab7f0SSascha Wildner /* Construct new name */
158*479ab7f0SSascha Wildner bzfname = malloc(strlen(fname) + 5);
159*479ab7f0SSascha Wildner sprintf(bzfname, "%s.bz2", fname);
160*479ab7f0SSascha Wildner
161*479ab7f0SSascha Wildner /* Try to open the compressed datafile */
162*479ab7f0SSascha Wildner rawfd = open(bzfname, O_RDONLY);
163*479ab7f0SSascha Wildner free(bzfname);
164*479ab7f0SSascha Wildner if (rawfd == -1)
165*479ab7f0SSascha Wildner return(ENOENT);
166*479ab7f0SSascha Wildner
167*479ab7f0SSascha Wildner if (fstat(rawfd, &sb) < 0) {
168*479ab7f0SSascha Wildner printf("bzf_open: stat failed\n");
169*479ab7f0SSascha Wildner close(rawfd);
170*479ab7f0SSascha Wildner return(ENOENT);
171*479ab7f0SSascha Wildner }
172*479ab7f0SSascha Wildner if (!S_ISREG(sb.st_mode)) {
173*479ab7f0SSascha Wildner printf("bzf_open: not a file\n");
174*479ab7f0SSascha Wildner close(rawfd);
175*479ab7f0SSascha Wildner return(EISDIR); /* best guess */
176*479ab7f0SSascha Wildner }
177*479ab7f0SSascha Wildner
178*479ab7f0SSascha Wildner /* Allocate a bz_file structure, populate it */
179*479ab7f0SSascha Wildner bzf = malloc(sizeof(struct bz_file));
180*479ab7f0SSascha Wildner bzero(bzf, sizeof(struct bz_file));
181*479ab7f0SSascha Wildner bzf->bzf_rawfd = rawfd;
182*479ab7f0SSascha Wildner
183*479ab7f0SSascha Wildner /* Verify that the file is bzipped (XXX why do this afterwards?) */
184*479ab7f0SSascha Wildner if (check_header(bzf)) {
185*479ab7f0SSascha Wildner close(bzf->bzf_rawfd);
186*479ab7f0SSascha Wildner BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
187*479ab7f0SSascha Wildner free(bzf);
188*479ab7f0SSascha Wildner return(EFTYPE);
189*479ab7f0SSascha Wildner }
190*479ab7f0SSascha Wildner
191*479ab7f0SSascha Wildner /* Initialise the inflation engine */
192*479ab7f0SSascha Wildner if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
193*479ab7f0SSascha Wildner printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
194*479ab7f0SSascha Wildner close(bzf->bzf_rawfd);
195*479ab7f0SSascha Wildner free(bzf);
196*479ab7f0SSascha Wildner return(EIO);
197*479ab7f0SSascha Wildner }
198*479ab7f0SSascha Wildner
199*479ab7f0SSascha Wildner /* Looks OK, we'll take it */
200*479ab7f0SSascha Wildner f->f_fsdata = bzf;
201*479ab7f0SSascha Wildner return(0);
202*479ab7f0SSascha Wildner }
203*479ab7f0SSascha Wildner
204*479ab7f0SSascha Wildner static int
bzf_close(struct open_file * f)205*479ab7f0SSascha Wildner bzf_close(struct open_file *f)
206*479ab7f0SSascha Wildner {
207*479ab7f0SSascha Wildner struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
208*479ab7f0SSascha Wildner
209*479ab7f0SSascha Wildner f->f_fsdata = NULL;
210*479ab7f0SSascha Wildner if (bzf) {
211*479ab7f0SSascha Wildner BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
212*479ab7f0SSascha Wildner close(bzf->bzf_rawfd);
213*479ab7f0SSascha Wildner free(bzf);
214*479ab7f0SSascha Wildner }
215*479ab7f0SSascha Wildner return(0);
216*479ab7f0SSascha Wildner }
217*479ab7f0SSascha Wildner
218*479ab7f0SSascha Wildner static int
bzf_read(struct open_file * f,void * buf,size_t size,size_t * resid)219*479ab7f0SSascha Wildner bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
220*479ab7f0SSascha Wildner {
221*479ab7f0SSascha Wildner struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
222*479ab7f0SSascha Wildner int error;
223*479ab7f0SSascha Wildner
224*479ab7f0SSascha Wildner bzf->bzf_bzstream.next_out = buf; /* where and how much */
225*479ab7f0SSascha Wildner bzf->bzf_bzstream.avail_out = size;
226*479ab7f0SSascha Wildner
227*479ab7f0SSascha Wildner while (bzf->bzf_bzstream.avail_out) {
228*479ab7f0SSascha Wildner if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
229*479ab7f0SSascha Wildner printf("bzf_read: fill error\n");
230*479ab7f0SSascha Wildner return(-1);
231*479ab7f0SSascha Wildner }
232*479ab7f0SSascha Wildner if (bzf->bzf_bzstream.avail_in == 0) { /* oops, unexpected EOF */
233*479ab7f0SSascha Wildner printf("bzf_read: unexpected EOF\n");
234*479ab7f0SSascha Wildner break;
235*479ab7f0SSascha Wildner }
236*479ab7f0SSascha Wildner
237*479ab7f0SSascha Wildner error = BZ2_bzDecompress(&bzf->bzf_bzstream); /* decompression pass */
238*479ab7f0SSascha Wildner if (error == BZ_STREAM_END) { /* EOF, all done */
239*479ab7f0SSascha Wildner break;
240*479ab7f0SSascha Wildner }
241*479ab7f0SSascha Wildner if (error != BZ_OK) { /* argh, decompression error */
242*479ab7f0SSascha Wildner printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
243*479ab7f0SSascha Wildner errno = EIO;
244*479ab7f0SSascha Wildner return(-1);
245*479ab7f0SSascha Wildner }
246*479ab7f0SSascha Wildner }
247*479ab7f0SSascha Wildner if (resid != NULL)
248*479ab7f0SSascha Wildner *resid = bzf->bzf_bzstream.avail_out;
249*479ab7f0SSascha Wildner return(0);
250*479ab7f0SSascha Wildner }
251*479ab7f0SSascha Wildner
252*479ab7f0SSascha Wildner static off_t
bzf_seek(struct open_file * f,off_t offset,int where)253*479ab7f0SSascha Wildner bzf_seek(struct open_file *f, off_t offset, int where)
254*479ab7f0SSascha Wildner {
255*479ab7f0SSascha Wildner struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
256*479ab7f0SSascha Wildner off_t target;
257*479ab7f0SSascha Wildner char discard[16];
258*479ab7f0SSascha Wildner
259*479ab7f0SSascha Wildner switch (where) {
260*479ab7f0SSascha Wildner case SEEK_SET:
261*479ab7f0SSascha Wildner target = offset;
262*479ab7f0SSascha Wildner break;
263*479ab7f0SSascha Wildner case SEEK_CUR:
264*479ab7f0SSascha Wildner target = offset + bzf->bzf_bzstream.total_out_lo32;
265*479ab7f0SSascha Wildner break;
266*479ab7f0SSascha Wildner default:
267*479ab7f0SSascha Wildner target = -1;
268*479ab7f0SSascha Wildner }
269*479ab7f0SSascha Wildner
270*479ab7f0SSascha Wildner /* Can we get there from here? */
271*479ab7f0SSascha Wildner if (target < bzf->bzf_bzstream.total_out_lo32) {
272*479ab7f0SSascha Wildner errno = EOFFSET;
273*479ab7f0SSascha Wildner return -1;
274*479ab7f0SSascha Wildner }
275*479ab7f0SSascha Wildner
276*479ab7f0SSascha Wildner /* skip forwards if required */
277*479ab7f0SSascha Wildner while (target > bzf->bzf_bzstream.total_out_lo32) {
278*479ab7f0SSascha Wildner if (bzf_read(f, discard, min(sizeof(discard), target - bzf->bzf_bzstream.total_out_lo32), NULL) == -1)
279*479ab7f0SSascha Wildner return(-1);
280*479ab7f0SSascha Wildner }
281*479ab7f0SSascha Wildner /* This is where we are (be honest if we overshot) */
282*479ab7f0SSascha Wildner return (bzf->bzf_bzstream.total_out_lo32);
283*479ab7f0SSascha Wildner }
284*479ab7f0SSascha Wildner
285*479ab7f0SSascha Wildner static int
bzf_stat(struct open_file * f,struct stat * sb)286*479ab7f0SSascha Wildner bzf_stat(struct open_file *f, struct stat *sb)
287*479ab7f0SSascha Wildner {
288*479ab7f0SSascha Wildner struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
289*479ab7f0SSascha Wildner int result;
290*479ab7f0SSascha Wildner
291*479ab7f0SSascha Wildner /* stat as normal, but indicate that size is unknown */
292*479ab7f0SSascha Wildner if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
293*479ab7f0SSascha Wildner sb->st_size = -1;
294*479ab7f0SSascha Wildner return(result);
295*479ab7f0SSascha Wildner }
296*479ab7f0SSascha Wildner
297*479ab7f0SSascha Wildner void
bz_internal_error(int errorcode)298*479ab7f0SSascha Wildner bz_internal_error(int errorcode)
299*479ab7f0SSascha Wildner {
300*479ab7f0SSascha Wildner panic("bzipfs: critical error %d in bzip2 library occurred\n", errorcode);
301*479ab7f0SSascha Wildner }
302