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 #define panic(x, y) abort()
46
47 static inline uint_t
min(uint_t a,uint_t b)48 min(uint_t a, uint_t b)
49 {
50 return (a < b ? a : b);
51 }
52 #endif
53
54 #include <sys/stat.h>
55 #include <string.h>
56 #include <bzlib.h>
57
58 #define BZ_BUFSIZE 2048 /* XXX larger? */
59
60 struct bz_file
61 {
62 int bzf_rawfd;
63 bz_stream bzf_bzstream;
64 char bzf_buf[BZ_BUFSIZE];
65 int bzf_endseen;
66 };
67
68 static int bzf_fill(struct bz_file *);
69 static int bzf_open(const char *, struct open_file *);
70 static int bzf_close(struct open_file *);
71 static int bzf_read(struct open_file *, void *, size_t, size_t *);
72 static off_t bzf_seek(struct open_file *, off_t, int);
73 static int bzf_stat(struct open_file *, struct stat *);
74
75 #ifndef REGRESSION
76 struct fs_ops bzipfs_fsops = {
77 .fs_name = "bzip",
78 .fo_open = bzf_open,
79 .fo_close = bzf_close,
80 .fo_read = bzf_read,
81 .fo_write = null_write,
82 .fo_seek = bzf_seek,
83 .fo_stat = bzf_stat,
84 .fo_readdir = null_readdir
85 };
86 #endif
87
88 static int
bzf_fill(struct bz_file * bzf)89 bzf_fill(struct bz_file *bzf)
90 {
91 int result;
92 int req;
93
94 req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
95 result = 0;
96
97 /* If we need more */
98 if (req > 0) {
99 /* move old data to bottom of buffer */
100 if (req < BZ_BUFSIZE) {
101 bcopy(bzf->bzf_buf + req, bzf->bzf_buf,
102 BZ_BUFSIZE - req);
103 }
104
105 /* read to fill buffer and update availibility data */
106 result = read(bzf->bzf_rawfd,
107 bzf->bzf_buf + bzf->bzf_bzstream.avail_in, req);
108 bzf->bzf_bzstream.next_in = bzf->bzf_buf;
109 if (result >= 0)
110 bzf->bzf_bzstream.avail_in += result;
111 }
112 return (result);
113 }
114
115 /*
116 * Adapted from get_byte/check_header in libz
117 *
118 * Returns 0 if the header is OK, nonzero if not.
119 */
120 static int
get_byte(struct bz_file * bzf)121 get_byte(struct bz_file *bzf)
122 {
123 if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1))
124 return (-1);
125 bzf->bzf_bzstream.avail_in--;
126 return (*(bzf->bzf_bzstream.next_in)++);
127 }
128
129 static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
130
131 static int
check_header(struct bz_file * bzf)132 check_header(struct bz_file *bzf)
133 {
134 unsigned int len;
135 int c;
136
137 /* Check the bzip2 magic header */
138 for (len = 0; len < 3; len++) {
139 c = get_byte(bzf);
140 if (c != bz_magic[len]) {
141 return (1);
142 }
143 }
144 /* Check that the block size is valid */
145 c = get_byte(bzf);
146 if (c < '1' || c > '9')
147 return (1);
148
149 /* Put back bytes that we've took from the input stream */
150 bzf->bzf_bzstream.next_in -= 4;
151 bzf->bzf_bzstream.avail_in += 4;
152
153 return (0);
154 }
155
156 static int
bzf_open(const char * fname,struct open_file * f)157 bzf_open(const char *fname, struct open_file *f)
158 {
159 static char *bzfname;
160 int rawfd;
161 struct bz_file *bzf;
162 char *cp;
163 int error;
164 struct stat sb;
165
166 /* Have to be in "just read it" mode */
167 if (f->f_flags != F_READ)
168 return (EPERM);
169
170 /* If the name already ends in .gz or .bz2, ignore it */
171 if ((cp = strrchr(fname, '.')) &&
172 ((strcmp(cp, ".gz") == 0) ||
173 (strcmp(cp, ".bz2") == 0) ||
174 (strcmp(cp, ".split") == 0)))
175 return (ENOENT);
176
177 /* Construct new name */
178 bzfname = malloc(strlen(fname) + 5);
179 if (bzfname == NULL)
180 return (ENOMEM);
181 sprintf(bzfname, "%s.bz2", fname);
182
183 /* Try to open the compressed datafile */
184 rawfd = open(bzfname, O_RDONLY);
185 free(bzfname);
186 if (rawfd == -1)
187 return (ENOENT);
188
189 if (fstat(rawfd, &sb) < 0) {
190 printf("bzf_open: stat failed\n");
191 close(rawfd);
192 return (ENOENT);
193 }
194 if (!S_ISREG(sb.st_mode)) {
195 printf("bzf_open: not a file\n");
196 close(rawfd);
197 return (EISDIR); /* best guess */
198 }
199
200 /* Allocate a bz_file structure, populate it */
201 bzf = malloc(sizeof (struct bz_file));
202 if (bzf == NULL)
203 return (ENOMEM);
204 bzero(bzf, sizeof (struct bz_file));
205 bzf->bzf_rawfd = rawfd;
206
207 /* Verify that the file is bzipped */
208 if (check_header(bzf)) {
209 close(bzf->bzf_rawfd);
210 free(bzf);
211 return (EFTYPE);
212 }
213
214 /* Initialise the inflation engine */
215 error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1);
216 if (error != BZ_OK) {
217 printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
218 close(bzf->bzf_rawfd);
219 free(bzf);
220 return (EIO);
221 }
222
223 /* Looks OK, we'll take it */
224 f->f_fsdata = bzf;
225 return (0);
226 }
227
228 static int
bzf_close(struct open_file * f)229 bzf_close(struct open_file *f)
230 {
231 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
232
233 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
234 close(bzf->bzf_rawfd);
235 free(bzf);
236 return (0);
237 }
238
239 static int
bzf_read(struct open_file * f,void * buf,size_t size,size_t * resid)240 bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
241 {
242 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
243 int error;
244
245 bzf->bzf_bzstream.next_out = buf; /* where and how much */
246 bzf->bzf_bzstream.avail_out = size;
247
248 while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) {
249 if ((bzf->bzf_bzstream.avail_in == 0) &&
250 (bzf_fill(bzf) == -1)) {
251 printf("bzf_read: fill error\n");
252 return (EIO);
253 }
254 if (bzf->bzf_bzstream.avail_in == 0) {
255 /* oops, unexpected EOF */
256 printf("bzf_read: unexpected EOF\n");
257 if (bzf->bzf_bzstream.avail_out == size)
258 return (EIO);
259 break;
260 }
261
262 /* decompression pass */
263 error = BZ2_bzDecompress(&bzf->bzf_bzstream);
264 if (error == BZ_STREAM_END) { /* EOF, all done */
265 bzf->bzf_endseen = 1;
266 break;
267 }
268 if (error != BZ_OK) { /* argh, decompression error */
269 printf("bzf_read: BZ2_bzDecompress returned %d\n",
270 error);
271 return (EIO);
272 }
273 }
274 if (resid != NULL)
275 *resid = bzf->bzf_bzstream.avail_out;
276 return (0);
277 }
278
279 static int
bzf_rewind(struct open_file * f)280 bzf_rewind(struct open_file *f)
281 {
282 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
283 struct bz_file *bzf_tmp;
284
285 /*
286 * Since bzip2 does not have an equivalent inflateReset function a crude
287 * one needs to be provided. The functions all called in such a way that
288 * at any time an error occurs a roll back can be done (effectively
289 * making this rewind 'atomic', either the reset occurs successfully
290 * or not at all, with no 'undefined' state happening).
291 */
292
293 /* Allocate a bz_file structure, populate it */
294 bzf_tmp = malloc(sizeof (struct bz_file));
295 if (bzf_tmp == NULL)
296 return (-1);
297 bzero(bzf_tmp, sizeof (struct bz_file));
298 bzf_tmp->bzf_rawfd = bzf->bzf_rawfd;
299
300 /* Initialise the inflation engine */
301 if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) {
302 free(bzf_tmp);
303 return (-1);
304 }
305
306 /* Seek back to the beginning of the file */
307 if (lseek(bzf->bzf_rawfd, 0, SEEK_SET) == -1) {
308 BZ2_bzDecompressEnd(&(bzf_tmp->bzf_bzstream));
309 free(bzf_tmp);
310 return (-1);
311 }
312
313 /* Free old bz_file data */
314 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
315 free(bzf);
316
317 /* Use the new bz_file data */
318 f->f_fsdata = bzf_tmp;
319
320 return (0);
321 }
322
323 static off_t
bzf_seek(struct open_file * f,off_t offset,int where)324 bzf_seek(struct open_file *f, off_t offset, int where)
325 {
326 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
327 off_t target;
328 char discard[16];
329
330 switch (where) {
331 case SEEK_SET:
332 target = offset;
333 break;
334 case SEEK_CUR:
335 target = offset + bzf->bzf_bzstream.total_out_lo32;
336 break;
337 default:
338 errno = EINVAL;
339 return (-1);
340 }
341
342 /* Can we get there from here? */
343 if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) {
344 errno = EOFFSET;
345 return (-1);
346 }
347
348 /* if bzf_rewind was called then bzf has changed */
349 bzf = (struct bz_file *)f->f_fsdata;
350
351 /* skip forwards if required */
352 while (target > bzf->bzf_bzstream.total_out_lo32) {
353 errno = bzf_read(f, discard, min(sizeof (discard),
354 target - bzf->bzf_bzstream.total_out_lo32), NULL);
355 if (errno)
356 return (-1);
357 }
358 /* This is where we are (be honest if we overshot) */
359 return (bzf->bzf_bzstream.total_out_lo32);
360 }
361
362 static int
bzf_stat(struct open_file * f,struct stat * sb)363 bzf_stat(struct open_file *f, struct stat *sb)
364 {
365 struct bz_file *bzf = (struct bz_file *)f->f_fsdata;
366 int result;
367
368 /* stat as normal, but indicate that size is unknown */
369 if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
370 sb->st_size = -1;
371 return (result);
372 }
373
374 void
bz_internal_error(int errorcode)375 bz_internal_error(int errorcode)
376 {
377 panic("bzipfs: critical error %d in bzip2 library occured",
378 errorcode);
379 }
380
381 #ifdef REGRESSION
382 /* Small test case, open and decompress test.bz2 */
383 int
main()384 main()
385 {
386 struct open_file f;
387 char buf[1024];
388 size_t resid;
389 int err;
390
391 memset(&f, '\0', sizeof (f));
392 f.f_flags = F_READ;
393 err = bzf_open("test", &f);
394 if (err != 0)
395 exit(1);
396 do {
397 err = bzf_read(&f, buf, sizeof (buf), &resid);
398 } while (err == 0 && resid != sizeof (buf));
399
400 if (err != 0)
401 exit(2);
402 exit(0);
403 }
404 #endif
405