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