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 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 static int 84 bzf_fill(struct bz_file *bzf) 85 { 86 int result; 87 int req; 88 89 req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in; 90 result = 0; 91 92 /* If we need more */ 93 if (req > 0) { 94 /* move old data to bottom of buffer */ 95 if (req < BZ_BUFSIZE) 96 bcopy(bzf->bzf_buf + req, bzf->bzf_buf, BZ_BUFSIZE - req); 97 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; 101 if (result >= 0) 102 bzf->bzf_bzstream.avail_in += result; 103 } 104 return(result); 105 } 106 107 /* 108 * Adapted from get_byte/check_header in libz 109 * 110 * Returns 0 if the header is OK, nonzero if not. 111 */ 112 static int 113 get_byte(struct bz_file *bzf) 114 { 115 if ((bzf->bzf_bzstream.avail_in == 0) && (bzf_fill(bzf) == -1)) 116 return(-1); 117 bzf->bzf_bzstream.avail_in--; 118 return(*(bzf->bzf_bzstream.next_in)++); 119 } 120 121 static int bz_magic[3] = {'B', 'Z', 'h'}; /* bzip2 magic header */ 122 123 static int 124 check_header(struct bz_file *bzf) 125 { 126 unsigned int len; 127 int c; 128 129 /* Check the bzip2 magic header */ 130 for (len = 0; len < 3; len++) { 131 c = get_byte(bzf); 132 if (c != bz_magic[len]) { 133 return(1); 134 } 135 } 136 /* Check that the block size is valid */ 137 c = get_byte(bzf); 138 if (c < '1' || c > '9') 139 return(1); 140 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; 144 145 return(0); 146 } 147 148 static int 149 bzf_open(const char *fname, struct open_file *f) 150 { 151 static char *bzfname; 152 int rawfd; 153 struct bz_file *bzf; 154 char *cp; 155 int error; 156 struct stat sb; 157 158 /* Have to be in "just read it" mode */ 159 if (f->f_flags != F_READ) 160 return(EPERM); 161 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"))) 165 return(ENOENT); 166 167 /* Construct new name */ 168 bzfname = malloc(strlen(fname) + 5); 169 if (bzfname == NULL) 170 return(ENOMEM); 171 sprintf(bzfname, "%s.bz2", fname); 172 173 /* Try to open the compressed datafile */ 174 rawfd = open(bzfname, O_RDONLY); 175 free(bzfname); 176 if (rawfd == -1) 177 return(ENOENT); 178 179 if (fstat(rawfd, &sb) < 0) { 180 printf("bzf_open: stat failed\n"); 181 close(rawfd); 182 return(ENOENT); 183 } 184 if (!S_ISREG(sb.st_mode)) { 185 printf("bzf_open: not a file\n"); 186 close(rawfd); 187 return(EISDIR); /* best guess */ 188 } 189 190 /* Allocate a bz_file structure, populate it */ 191 bzf = malloc(sizeof(struct bz_file)); 192 if (bzf == NULL) 193 return(ENOMEM); 194 bzero(bzf, sizeof(struct bz_file)); 195 bzf->bzf_rawfd = rawfd; 196 197 /* Verify that the file is bzipped */ 198 if (check_header(bzf)) { 199 close(bzf->bzf_rawfd); 200 free(bzf); 201 return(EFTYPE); 202 } 203 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); 208 free(bzf); 209 return(EIO); 210 } 211 212 /* Looks OK, we'll take it */ 213 f->f_fsdata = bzf; 214 return(0); 215 } 216 217 static int 218 bzf_close(struct open_file *f) 219 { 220 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 221 222 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream)); 223 close(bzf->bzf_rawfd); 224 free(bzf); 225 return(0); 226 } 227 228 static int 229 bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid) 230 { 231 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 232 int error; 233 234 bzf->bzf_bzstream.next_out = buf; /* where and how much */ 235 bzf->bzf_bzstream.avail_out = size; 236 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"); 240 return(EIO); 241 } 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) 245 return(EIO); 246 break; 247 } 248 249 error = BZ2_bzDecompress(&bzf->bzf_bzstream); /* decompression pass */ 250 if (error == BZ_STREAM_END) { /* EOF, all done */ 251 bzf->bzf_endseen = 1; 252 break; 253 } 254 if (error != BZ_OK) { /* argh, decompression error */ 255 printf("bzf_read: BZ2_bzDecompress returned %d\n", error); 256 return(EIO); 257 } 258 } 259 if (resid != NULL) 260 *resid = bzf->bzf_bzstream.avail_out; 261 return(0); 262 } 263 264 static int 265 bzf_rewind(struct open_file *f) 266 { 267 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 268 struct bz_file *bzf_tmp; 269 270 /* 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). 276 */ 277 278 /* Allocate a bz_file structure, populate it */ 279 bzf_tmp = malloc(sizeof(struct bz_file)); 280 if (bzf_tmp == NULL) 281 return(-1); 282 bzero(bzf_tmp, sizeof(struct bz_file)); 283 bzf_tmp->bzf_rawfd = bzf->bzf_rawfd; 284 285 /* Initialise the inflation engine */ 286 if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) { 287 free(bzf_tmp); 288 return(-1); 289 } 290 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)); 294 free(bzf_tmp); 295 return(-1); 296 } 297 298 /* Free old bz_file data */ 299 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream)); 300 free(bzf); 301 302 /* Use the new bz_file data */ 303 f->f_fsdata = bzf_tmp; 304 305 return(0); 306 } 307 308 static off_t 309 bzf_seek(struct open_file *f, off_t offset, int where) 310 { 311 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 312 off_t target; 313 char discard[16]; 314 315 switch (where) { 316 case SEEK_SET: 317 target = offset; 318 break; 319 case SEEK_CUR: 320 target = offset + bzf->bzf_bzstream.total_out_lo32; 321 break; 322 default: 323 errno = EINVAL; 324 return(-1); 325 } 326 327 /* Can we get there from here? */ 328 if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) { 329 errno = EOFFSET; 330 return -1; 331 } 332 333 /* if bzf_rewind was called then bzf has changed */ 334 bzf = (struct bz_file *)f->f_fsdata; 335 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); 340 if (errno) 341 return(-1); 342 } 343 /* This is where we are (be honest if we overshot) */ 344 return(bzf->bzf_bzstream.total_out_lo32); 345 } 346 347 static int 348 bzf_stat(struct open_file *f, struct stat *sb) 349 { 350 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 351 int result; 352 353 /* stat as normal, but indicate that size is unknown */ 354 if ((result = fstat(bzf->bzf_rawfd, sb)) == 0) 355 sb->st_size = -1; 356 return(result); 357 } 358 359 void 360 bz_internal_error(int errorcode) 361 { 362 panic("bzipfs: critical error %d in bzip2 library occured\n", errorcode); 363 } 364 365 #ifdef REGRESSION 366 /* Small test case, open and decompress test.bz2 */ 367 int main() 368 { 369 struct open_file f; 370 char buf[1024]; 371 size_t resid; 372 int err; 373 374 memset(&f, '\0', sizeof(f)); 375 f.f_flags = F_READ; 376 err = bzf_open("test", &f); 377 if (err != 0) 378 exit(1); 379 do { 380 err = bzf_read(&f, buf, sizeof(buf), &resid); 381 } while (err == 0 && resid != sizeof(buf)); 382 383 if (err != 0) 384 exit(2); 385 exit(0); 386 } 387 #endif 388