]> CyberLeo.Net >> Repos - FreeBSD/releng/8.0.git/blob - lib/libstand/bzipfs.c
Adjust to reflect 8.0-RELEASE.
[FreeBSD/releng/8.0.git] / lib / libstand / bzipfs.c
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 __FBSDID("$FreeBSD$");
30
31 #ifndef REGRESSION
32 #include "stand.h"
33 #else
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 static inline u_int min(u_int a, u_int b) { return (a < b ? a : b); }
46 #define panic(x, y) abort()
47 #endif
48
49 #include <sys/stat.h>
50 #include <string.h>
51 #include <bzlib.h>
52
53 #define BZ_BUFSIZE 2048 /* XXX larger? */
54
55 struct bz_file
56 {
57     int                 bzf_rawfd;
58     bz_stream           bzf_bzstream;
59     char                bzf_buf[BZ_BUFSIZE];
60     int                 bzf_endseen;
61 };
62
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);
69
70 #ifndef REGRESSION
71 struct fs_ops bzipfs_fsops = {
72     "bzip",
73     bzf_open, 
74     bzf_close, 
75     bzf_read,
76     null_write,
77     bzf_seek,
78     bzf_stat,
79     null_readdir
80 };
81 #endif
82
83 #if 0
84 void *
85 calloc(int items, size_t size)
86 {
87     return(malloc(items * size));
88 }
89 #endif
90
91 static int
92 bzf_fill(struct bz_file *bzf)
93 {
94     int         result;
95     int         req;
96     
97     req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in;
98     result = 0;
99     
100     /* If we need more */
101     if (req > 0) {
102         /* move old data to bottom of buffer */
103         if (req < BZ_BUFSIZE)
104             bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req);
105         
106         /* read to fill buffer and update availibility data */
107         result = read(bzf->bzf_rawfd, 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
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
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
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, '.')) && (!strcmp(cp, ".gz")
172             || !strcmp(cp, ".bz2") || !strcmp(cp, ".split")))
173         return(ENOENT);
174
175     /* Construct new name */
176     bzfname = malloc(strlen(fname) + 5);
177     sprintf(bzfname, "%s.bz2", fname);
178
179     /* Try to open the compressed datafile */
180     rawfd = open(bzfname, O_RDONLY);
181     free(bzfname);
182     if (rawfd == -1)
183         return(ENOENT);
184
185     if (fstat(rawfd, &sb) < 0) {
186         printf("bzf_open: stat failed\n");
187         close(rawfd);
188         return(ENOENT);
189     }
190     if (!S_ISREG(sb.st_mode)) {
191         printf("bzf_open: not a file\n");
192         close(rawfd);
193         return(EISDIR);                 /* best guess */
194     }
195
196     /* Allocate a bz_file structure, populate it */
197     bzf = malloc(sizeof(struct bz_file));
198     bzero(bzf, sizeof(struct bz_file));
199     bzf->bzf_rawfd = rawfd;
200
201     /* Verify that the file is bzipped (XXX why do this afterwards?) */
202     if (check_header(bzf)) {
203         close(bzf->bzf_rawfd);
204         BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
205         free(bzf);
206         return(EFTYPE);
207     }
208
209     /* Initialise the inflation engine */
210     if ((error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1)) != BZ_OK) {
211         printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error);
212         close(bzf->bzf_rawfd);
213         free(bzf);
214         return(EIO);
215     }
216
217     /* Looks OK, we'll take it */
218     f->f_fsdata = bzf;
219     return(0);
220 }
221
222 static int
223 bzf_close(struct open_file *f)
224 {
225     struct bz_file      *bzf = (struct bz_file *)f->f_fsdata;
226     
227     BZ2_bzDecompressEnd(&(bzf->bzf_bzstream));
228     close(bzf->bzf_rawfd);
229     free(bzf);
230     return(0);
231 }
232  
233 static int 
234 bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid)
235 {
236     struct bz_file      *bzf = (struct bz_file *)f->f_fsdata;
237     int                 error;
238
239     bzf->bzf_bzstream.next_out = buf;                   /* where and how much */
240     bzf->bzf_bzstream.avail_out = size;
241
242     while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) {
243         if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) {
244             printf("bzf_read: fill error\n");
245             return(EIO);
246         }
247         if (bzf->bzf_bzstream.avail_in == 0) {          /* oops, unexpected EOF */
248             printf("bzf_read: unexpected EOF\n");
249             if (bzf->bzf_bzstream.avail_out == size)
250                 return (EIO);
251             break;
252         }
253
254         error = BZ2_bzDecompress(&bzf->bzf_bzstream);   /* decompression pass */
255         if (error == BZ_STREAM_END) {                   /* EOF, all done */
256             bzf->bzf_endseen = 1;
257             break;
258         }
259         if (error != BZ_OK) {                           /* argh, decompression error */
260             printf("bzf_read: BZ2_bzDecompress returned %d\n", error);
261             return(EIO);
262         }
263     }
264     if (resid != NULL)
265         *resid = bzf->bzf_bzstream.avail_out;
266     return(0);
267 }
268
269 static off_t
270 bzf_seek(struct open_file *f, off_t offset, int where)
271 {
272     struct bz_file      *bzf = (struct bz_file *)f->f_fsdata;
273     off_t               target;
274     char                discard[16];
275     
276     switch (where) {
277     case SEEK_SET:
278         target = offset;
279         break;
280     case SEEK_CUR:
281         target = offset + bzf->bzf_bzstream.total_out_lo32;
282         break;
283     case SEEK_END:
284         target = -1;
285     default:
286         errno = EINVAL;
287         return (-1);
288     }
289
290     /* Can we get there from here? */
291     if (target < bzf->bzf_bzstream.total_out_lo32) {
292         errno = EOFFSET;
293         return -1;
294     } 
295
296     /* skip forwards if required */
297     while (target > bzf->bzf_bzstream.total_out_lo32) {
298         errno = bzf_read(f, discard, min(sizeof(discard),
299             target - bzf->bzf_bzstream.total_out_lo32), NULL);
300         if (errno)
301             return(-1);
302     }
303     /* This is where we are (be honest if we overshot) */
304     return (bzf->bzf_bzstream.total_out_lo32);
305 }
306
307 static int
308 bzf_stat(struct open_file *f, struct stat *sb)
309 {
310     struct bz_file      *bzf = (struct bz_file *)f->f_fsdata;
311     int                 result;
312
313     /* stat as normal, but indicate that size is unknown */
314     if ((result = fstat(bzf->bzf_rawfd, sb)) == 0)
315         sb->st_size = -1;
316     return(result);
317 }
318
319 void
320 bz_internal_error(int errorcode)
321 {
322     panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode);
323 }
324
325 #ifdef REGRESSION
326 /* Small test case, open and decompress test.bz2 */
327 int main()
328 {
329     struct open_file f;
330     char buf[1024];
331     size_t resid;
332     int err;
333
334     memset(&f, '\0', sizeof(f));
335     f.f_flags = F_READ;
336     err = bzf_open("test", &f);
337     if (err != 0)
338         exit(1);
339     do {
340         err = bzf_read(&f, buf, sizeof(buf), &resid);
341     } while (err == 0 && resid != sizeof(buf));
342
343     if (err != 0)
344         exit(2);
345     exit(0);
346 }
347 #endif