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