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 #define panic(x, y) abort() 46 47 static inline uint_t 48 min(uint_t a, uint_t b) 49 { 50 return (a < b ? a : b); 51 } 52 #endif 53 54 #include <sys/stat.h> 55 #include <string.h> 56 #include <bzlib.h> 57 58 #define BZ_BUFSIZE 2048 /* XXX larger? */ 59 60 struct bz_file 61 { 62 int bzf_rawfd; 63 bz_stream bzf_bzstream; 64 char bzf_buf[BZ_BUFSIZE]; 65 int bzf_endseen; 66 }; 67 68 static int bzf_fill(struct bz_file *); 69 static int bzf_open(const char *, struct open_file *); 70 static int bzf_close(struct open_file *); 71 static int bzf_read(struct open_file *, void *, size_t, size_t *); 72 static off_t bzf_seek(struct open_file *, off_t, int); 73 static int bzf_stat(struct open_file *, struct stat *); 74 75 #ifndef REGRESSION 76 struct fs_ops bzipfs_fsops = { 77 .fs_name = "bzip", 78 .fo_open = bzf_open, 79 .fo_close = bzf_close, 80 .fo_read = bzf_read, 81 .fo_write = null_write, 82 .fo_seek = bzf_seek, 83 .fo_stat = bzf_stat, 84 .fo_readdir = null_readdir 85 }; 86 #endif 87 88 static int 89 bzf_fill(struct bz_file *bzf) 90 { 91 int result; 92 int req; 93 94 req = BZ_BUFSIZE - bzf->bzf_bzstream.avail_in; 95 result = 0; 96 97 /* If we need more */ 98 if (req > 0) { 99 /* move old data to bottom of buffer */ 100 if (req < BZ_BUFSIZE) { 101 bcopy(bzf->bzf_buf + req, bzf->bzf_buf, 102 BZ_BUFSIZE - req); 103 } 104 105 /* read to fill buffer and update availibility data */ 106 result = read(bzf->bzf_rawfd, 107 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, '.')) && 172 ((strcmp(cp, ".gz") == 0) || 173 (strcmp(cp, ".bz2") == 0) || 174 (strcmp(cp, ".split") == 0))) 175 return (ENOENT); 176 177 /* Construct new name */ 178 bzfname = malloc(strlen(fname) + 5); 179 if (bzfname == NULL) 180 return (ENOMEM); 181 sprintf(bzfname, "%s.bz2", fname); 182 183 /* Try to open the compressed datafile */ 184 rawfd = open(bzfname, O_RDONLY); 185 free(bzfname); 186 if (rawfd == -1) 187 return (ENOENT); 188 189 if (fstat(rawfd, &sb) < 0) { 190 printf("bzf_open: stat failed\n"); 191 close(rawfd); 192 return (ENOENT); 193 } 194 if (!S_ISREG(sb.st_mode)) { 195 printf("bzf_open: not a file\n"); 196 close(rawfd); 197 return (EISDIR); /* best guess */ 198 } 199 200 /* Allocate a bz_file structure, populate it */ 201 bzf = malloc(sizeof (struct bz_file)); 202 if (bzf == NULL) 203 return (ENOMEM); 204 bzero(bzf, sizeof (struct bz_file)); 205 bzf->bzf_rawfd = rawfd; 206 207 /* Verify that the file is bzipped */ 208 if (check_header(bzf)) { 209 close(bzf->bzf_rawfd); 210 free(bzf); 211 return (EFTYPE); 212 } 213 214 /* Initialise the inflation engine */ 215 error = BZ2_bzDecompressInit(&(bzf->bzf_bzstream), 0, 1); 216 if (error != BZ_OK) { 217 printf("bzf_open: BZ2_bzDecompressInit returned %d\n", error); 218 close(bzf->bzf_rawfd); 219 free(bzf); 220 return (EIO); 221 } 222 223 /* Looks OK, we'll take it */ 224 f->f_fsdata = bzf; 225 return (0); 226 } 227 228 static int 229 bzf_close(struct open_file *f) 230 { 231 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 232 233 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream)); 234 close(bzf->bzf_rawfd); 235 free(bzf); 236 return (0); 237 } 238 239 static int 240 bzf_read(struct open_file *f, void *buf, size_t size, size_t *resid) 241 { 242 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 243 int error; 244 245 bzf->bzf_bzstream.next_out = buf; /* where and how much */ 246 bzf->bzf_bzstream.avail_out = size; 247 248 while (bzf->bzf_bzstream.avail_out && bzf->bzf_endseen == 0) { 249 if ((bzf->bzf_bzstream.avail_in == 0) && 250 (bzf_fill(bzf) == -1)) { 251 printf("bzf_read: fill error\n"); 252 return (EIO); 253 } 254 if (bzf->bzf_bzstream.avail_in == 0) { 255 /* oops, unexpected EOF */ 256 printf("bzf_read: unexpected EOF\n"); 257 if (bzf->bzf_bzstream.avail_out == size) 258 return (EIO); 259 break; 260 } 261 262 /* decompression pass */ 263 error = BZ2_bzDecompress(&bzf->bzf_bzstream); 264 if (error == BZ_STREAM_END) { /* EOF, all done */ 265 bzf->bzf_endseen = 1; 266 break; 267 } 268 if (error != BZ_OK) { /* argh, decompression error */ 269 printf("bzf_read: BZ2_bzDecompress returned %d\n", 270 error); 271 return (EIO); 272 } 273 } 274 if (resid != NULL) 275 *resid = bzf->bzf_bzstream.avail_out; 276 return (0); 277 } 278 279 static int 280 bzf_rewind(struct open_file *f) 281 { 282 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 283 struct bz_file *bzf_tmp; 284 285 /* 286 * Since bzip2 does not have an equivalent inflateReset function a crude 287 * one needs to be provided. The functions all called in such a way that 288 * at any time an error occurs a roll back can be done (effectively 289 * making this rewind 'atomic', either the reset occurs successfully 290 * or not at all, with no 'undefined' state happening). 291 */ 292 293 /* Allocate a bz_file structure, populate it */ 294 bzf_tmp = malloc(sizeof (struct bz_file)); 295 if (bzf_tmp == NULL) 296 return (-1); 297 bzero(bzf_tmp, sizeof (struct bz_file)); 298 bzf_tmp->bzf_rawfd = bzf->bzf_rawfd; 299 300 /* Initialise the inflation engine */ 301 if (BZ2_bzDecompressInit(&(bzf_tmp->bzf_bzstream), 0, 1) != BZ_OK) { 302 free(bzf_tmp); 303 return (-1); 304 } 305 306 /* Seek back to the beginning of the file */ 307 if (lseek(bzf->bzf_rawfd, 0, SEEK_SET) == -1) { 308 BZ2_bzDecompressEnd(&(bzf_tmp->bzf_bzstream)); 309 free(bzf_tmp); 310 return (-1); 311 } 312 313 /* Free old bz_file data */ 314 BZ2_bzDecompressEnd(&(bzf->bzf_bzstream)); 315 free(bzf); 316 317 /* Use the new bz_file data */ 318 f->f_fsdata = bzf_tmp; 319 320 return (0); 321 } 322 323 static off_t 324 bzf_seek(struct open_file *f, off_t offset, int where) 325 { 326 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 327 off_t target; 328 char discard[16]; 329 330 switch (where) { 331 case SEEK_SET: 332 target = offset; 333 break; 334 case SEEK_CUR: 335 target = offset + bzf->bzf_bzstream.total_out_lo32; 336 break; 337 default: 338 errno = EINVAL; 339 return (-1); 340 } 341 342 /* Can we get there from here? */ 343 if (target < bzf->bzf_bzstream.total_out_lo32 && bzf_rewind(f) != 0) { 344 errno = EOFFSET; 345 return (-1); 346 } 347 348 /* if bzf_rewind was called then bzf has changed */ 349 bzf = (struct bz_file *)f->f_fsdata; 350 351 /* skip forwards if required */ 352 while (target > bzf->bzf_bzstream.total_out_lo32) { 353 errno = bzf_read(f, discard, min(sizeof (discard), 354 target - bzf->bzf_bzstream.total_out_lo32), NULL); 355 if (errno) 356 return (-1); 357 } 358 /* This is where we are (be honest if we overshot) */ 359 return (bzf->bzf_bzstream.total_out_lo32); 360 } 361 362 static int 363 bzf_stat(struct open_file *f, struct stat *sb) 364 { 365 struct bz_file *bzf = (struct bz_file *)f->f_fsdata; 366 int result; 367 368 /* stat as normal, but indicate that size is unknown */ 369 if ((result = fstat(bzf->bzf_rawfd, sb)) == 0) 370 sb->st_size = -1; 371 return (result); 372 } 373 374 void 375 bz_internal_error(int errorcode) 376 { 377 panic("bzipfs: critical error %d in bzip2 library occured", 378 errorcode); 379 } 380 381 #ifdef REGRESSION 382 /* Small test case, open and decompress test.bz2 */ 383 int 384 main() 385 { 386 struct open_file f; 387 char buf[1024]; 388 size_t resid; 389 int err; 390 391 memset(&f, '\0', sizeof (f)); 392 f.f_flags = F_READ; 393 err = bzf_open("test", &f); 394 if (err != 0) 395 exit(1); 396 do { 397 err = bzf_read(&f, buf, sizeof (buf), &resid); 398 } while (err == 0 && resid != sizeof (buf)); 399 400 if (err != 0) 401 exit(2); 402 exit(0); 403 } 404 #endif 405