xref: /dflybsd-src/stand/lib/bzipfs.c (revision 479ab7f0492f2a51b48e8537e4f1dc686fc6014b)
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