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