2 * Copyright (c) 1998 Michael Smith.
3 * Copyright (c) 2000 Maxim Sobolev
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
28 #include <sys/cdefs.h>
34 #include <sys/errno.h>
35 #include <sys/fcntl.h>
36 #include <sys/types.h>
37 #include <sys/unistd.h>
40 int f_flags
; /* see F_* below */
41 void *f_fsdata
; /* file system specific data */
43 #define F_READ 0x0001 /* file opened for reading */
44 #define EOFFSET (ELAST+8) /* relative seek not supported */
45 static inline u_int
min(u_int a
, u_int b
) { return(a
< b
? a
: b
); }
46 #define panic(x, y) abort()
53 #define BZ_BUFSIZE 2048 /* XXX larger? */
58 bz_stream bzf_bzstream
;
59 char bzf_buf
[BZ_BUFSIZE
];
63 static int bzf_fill(struct bz_file
*z
);
64 static int bzf_open(const char *path
, struct open_file
*f
);
65 static int bzf_close(struct open_file
*f
);
66 static int bzf_read(struct open_file
*f
, void *buf
, size_t size
, size_t *resid
);
67 static off_t
bzf_seek(struct open_file
*f
, off_t offset
, int where
);
68 static int bzf_stat(struct open_file
*f
, struct stat
*sb
);
71 struct fs_ops bzipfs_fsops
= {
84 bzf_fill(struct bz_file
*bzf
)
89 req
= BZ_BUFSIZE
- bzf
->bzf_bzstream
.avail_in
;
94 /* move old data to bottom of buffer */
96 bcopy(bzf
->bzf_buf
+ req
, bzf
->bzf_buf
, BZ_BUFSIZE
- req
);
98 /* read to fill buffer and update availibility data */
99 result
= read(bzf
->bzf_rawfd
, bzf
->bzf_buf
+ bzf
->bzf_bzstream
.avail_in
, req
);
100 bzf
->bzf_bzstream
.next_in
= bzf
->bzf_buf
;
102 bzf
->bzf_bzstream
.avail_in
+= result
;
108 * Adapted from get_byte/check_header in libz
110 * Returns 0 if the header is OK, nonzero if not.
113 get_byte(struct bz_file
*bzf
)
115 if ((bzf
->bzf_bzstream
.avail_in
== 0) && (bzf_fill(bzf
) == -1))
117 bzf
->bzf_bzstream
.avail_in
--;
118 return(*(bzf
->bzf_bzstream
.next_in
)++);
121 static int bz_magic
[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */
124 check_header(struct bz_file
*bzf
)
129 /* Check the bzip2 magic header */
130 for (len
= 0; len
< 3; len
++) {
132 if (c
!= bz_magic
[len
]) {
136 /* Check that the block size is valid */
138 if (c
< '1' || c
> '9')
141 /* Put back bytes that we've took from the input stream */
142 bzf
->bzf_bzstream
.next_in
-= 4;
143 bzf
->bzf_bzstream
.avail_in
+= 4;
149 bzf_open(const char *fname
, struct open_file
*f
)
151 static char *bzfname
;
158 /* Have to be in "just read it" mode */
159 if (f
->f_flags
!= F_READ
)
162 /* If the name already ends in .gz or .bz2, ignore it */
163 if ((cp
= strrchr(fname
, '.')) && (!strcmp(cp
, ".gz")
164 || !strcmp(cp
, ".bz2") || !strcmp(cp
, ".split")))
167 /* Construct new name */
168 bzfname
= malloc(strlen(fname
) + 5);
171 sprintf(bzfname
, "%s.bz2", fname
);
173 /* Try to open the compressed datafile */
174 rawfd
= open(bzfname
, O_RDONLY
);
179 if (fstat(rawfd
, &sb
) < 0) {
180 printf("bzf_open: stat failed\n");
184 if (!S_ISREG(sb
.st_mode
)) {
185 printf("bzf_open: not a file\n");
187 return(EISDIR
); /* best guess */
190 /* Allocate a bz_file structure, populate it */
191 bzf
= malloc(sizeof(struct bz_file
));
194 bzero(bzf
, sizeof(struct bz_file
));
195 bzf
->bzf_rawfd
= rawfd
;
197 /* Verify that the file is bzipped */
198 if (check_header(bzf
)) {
199 close(bzf
->bzf_rawfd
);
204 /* Initialise the inflation engine */
205 if ((error
= BZ2_bzDecompressInit(&(bzf
->bzf_bzstream
), 0, 1)) != BZ_OK
) {
206 printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error
);
207 close(bzf
->bzf_rawfd
);
212 /* Looks OK, we'll take it */
218 bzf_close(struct open_file
*f
)
220 struct bz_file
*bzf
= (struct bz_file
*)f
->f_fsdata
;
222 BZ2_bzDecompressEnd(&(bzf
->bzf_bzstream
));
223 close(bzf
->bzf_rawfd
);
229 bzf_read(struct open_file
*f
, void *buf
, size_t size
, size_t *resid
)
231 struct bz_file
*bzf
= (struct bz_file
*)f
->f_fsdata
;
234 bzf
->bzf_bzstream
.next_out
= buf
; /* where and how much */
235 bzf
->bzf_bzstream
.avail_out
= size
;
237 while (bzf
->bzf_bzstream
.avail_out
&& bzf
->bzf_endseen
== 0) {
238 if ((bzf
->bzf_bzstream
.avail_in
== 0) && (bzf_fill(bzf
) == -1)) {
239 printf("bzf_read: fill error\n");
242 if (bzf
->bzf_bzstream
.avail_in
== 0) { /* oops, unexpected EOF */
243 printf("bzf_read: unexpected EOF\n");
244 if (bzf
->bzf_bzstream
.avail_out
== size
)
249 error
= BZ2_bzDecompress(&bzf
->bzf_bzstream
); /* decompression pass */
250 if (error
== BZ_STREAM_END
) { /* EOF, all done */
251 bzf
->bzf_endseen
= 1;
254 if (error
!= BZ_OK
) { /* argh, decompression error */
255 printf("bzf_read: BZ2_bzDecompress returned %d\n", error
);
260 *resid
= bzf
->bzf_bzstream
.avail_out
;
265 bzf_rewind(struct open_file
*f
)
267 struct bz_file
*bzf
= (struct bz_file
*)f
->f_fsdata
;
268 struct bz_file
*bzf_tmp
;
271 * Since bzip2 does not have an equivalent inflateReset function a crude
272 * one needs to be provided. The functions all called in such a way that
273 * at any time an error occurs a roll back can be done (effectively making
274 * this rewind 'atomic', either the reset occurs successfully or not at all,
275 * with no 'undefined' state happening).
278 /* Allocate a bz_file structure, populate it */
279 bzf_tmp
= malloc(sizeof(struct bz_file
));
282 bzero(bzf_tmp
, sizeof(struct bz_file
));
283 bzf_tmp
->bzf_rawfd
= bzf
->bzf_rawfd
;
285 /* Initialise the inflation engine */
286 if (BZ2_bzDecompressInit(&(bzf_tmp
->bzf_bzstream
), 0, 1) != BZ_OK
) {
291 /* Seek back to the beginning of the file */
292 if (lseek(bzf
->bzf_rawfd
, 0, SEEK_SET
) == -1) {
293 BZ2_bzDecompressEnd(&(bzf_tmp
->bzf_bzstream
));
298 /* Free old bz_file data */
299 BZ2_bzDecompressEnd(&(bzf
->bzf_bzstream
));
302 /* Use the new bz_file data */
303 f
->f_fsdata
= bzf_tmp
;
309 bzf_seek(struct open_file
*f
, off_t offset
, int where
)
311 struct bz_file
*bzf
= (struct bz_file
*)f
->f_fsdata
;
320 target
= offset
+ bzf
->bzf_bzstream
.total_out_lo32
;
327 /* Can we get there from here? */
328 if (target
< bzf
->bzf_bzstream
.total_out_lo32
&& bzf_rewind(f
) != 0) {
333 /* if bzf_rewind was called then bzf has changed */
334 bzf
= (struct bz_file
*)f
->f_fsdata
;
336 /* skip forwards if required */
337 while (target
> bzf
->bzf_bzstream
.total_out_lo32
) {
338 errno
= bzf_read(f
, discard
, min(sizeof(discard
),
339 target
- bzf
->bzf_bzstream
.total_out_lo32
), NULL
);
343 /* This is where we are (be honest if we overshot) */
344 return(bzf
->bzf_bzstream
.total_out_lo32
);
348 bzf_stat(struct open_file
*f
, struct stat
*sb
)
350 struct bz_file
*bzf
= (struct bz_file
*)f
->f_fsdata
;
353 /* stat as normal, but indicate that size is unknown */
354 if ((result
= fstat(bzf
->bzf_rawfd
, sb
)) == 0)
360 bz_internal_error(int errorcode
)
362 panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode
);
366 /* Small test case, open and decompress test.bz2 */
374 memset(&f
, '\0', sizeof(f
));
376 err
= bzf_open("test", &f
);
380 err
= bzf_read(&f
, buf
, sizeof(buf
), &resid
);
381 } while (err
== 0 && resid
!= sizeof(buf
));