1*4a5d661aSToomas Soome /* 2*4a5d661aSToomas Soome * Copyright (c) 1998 Michael Smith. 3*4a5d661aSToomas Soome * All rights reserved. 4*4a5d661aSToomas Soome * 5*4a5d661aSToomas Soome * Redistribution and use in source and binary forms, with or without 6*4a5d661aSToomas Soome * modification, are permitted provided that the following conditions 7*4a5d661aSToomas Soome * are met: 8*4a5d661aSToomas Soome * 1. Redistributions of source code must retain the above copyright 9*4a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer. 10*4a5d661aSToomas Soome * 2. Redistributions in binary form must reproduce the above copyright 11*4a5d661aSToomas Soome * notice, this list of conditions and the following disclaimer in the 12*4a5d661aSToomas Soome * documentation and/or other materials provided with the distribution. 13*4a5d661aSToomas Soome * 14*4a5d661aSToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15*4a5d661aSToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16*4a5d661aSToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17*4a5d661aSToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18*4a5d661aSToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19*4a5d661aSToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20*4a5d661aSToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21*4a5d661aSToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22*4a5d661aSToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23*4a5d661aSToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24*4a5d661aSToomas Soome * SUCH DAMAGE. 25*4a5d661aSToomas Soome */ 26*4a5d661aSToomas Soome 27*4a5d661aSToomas Soome #include <sys/cdefs.h> 28*4a5d661aSToomas Soome __FBSDID("$FreeBSD$"); 29*4a5d661aSToomas Soome 30*4a5d661aSToomas Soome #include "stand.h" 31*4a5d661aSToomas Soome 32*4a5d661aSToomas Soome #include <sys/stat.h> 33*4a5d661aSToomas Soome #include <string.h> 34*4a5d661aSToomas Soome #include <zlib.h> 35*4a5d661aSToomas Soome 36*4a5d661aSToomas Soome #define Z_BUFSIZE 2048 /* XXX larger? */ 37*4a5d661aSToomas Soome 38*4a5d661aSToomas Soome struct z_file 39*4a5d661aSToomas Soome { 40*4a5d661aSToomas Soome int zf_rawfd; 41*4a5d661aSToomas Soome off_t zf_dataoffset; 42*4a5d661aSToomas Soome z_stream zf_zstream; 43*4a5d661aSToomas Soome char zf_buf[Z_BUFSIZE]; 44*4a5d661aSToomas Soome int zf_endseen; 45*4a5d661aSToomas Soome }; 46*4a5d661aSToomas Soome 47*4a5d661aSToomas Soome static int zf_fill(struct z_file *z); 48*4a5d661aSToomas Soome static int zf_open(const char *path, struct open_file *f); 49*4a5d661aSToomas Soome static int zf_close(struct open_file *f); 50*4a5d661aSToomas Soome static int zf_read(struct open_file *f, void *buf, size_t size, size_t *resid); 51*4a5d661aSToomas Soome static off_t zf_seek(struct open_file *f, off_t offset, int where); 52*4a5d661aSToomas Soome static int zf_stat(struct open_file *f, struct stat *sb); 53*4a5d661aSToomas Soome 54*4a5d661aSToomas Soome struct fs_ops gzipfs_fsops = { 55*4a5d661aSToomas Soome "zip", 56*4a5d661aSToomas Soome zf_open, 57*4a5d661aSToomas Soome zf_close, 58*4a5d661aSToomas Soome zf_read, 59*4a5d661aSToomas Soome null_write, 60*4a5d661aSToomas Soome zf_seek, 61*4a5d661aSToomas Soome zf_stat, 62*4a5d661aSToomas Soome null_readdir 63*4a5d661aSToomas Soome }; 64*4a5d661aSToomas Soome 65*4a5d661aSToomas Soome static int 66*4a5d661aSToomas Soome zf_fill(struct z_file *zf) 67*4a5d661aSToomas Soome { 68*4a5d661aSToomas Soome int result; 69*4a5d661aSToomas Soome int req; 70*4a5d661aSToomas Soome 71*4a5d661aSToomas Soome req = Z_BUFSIZE - zf->zf_zstream.avail_in; 72*4a5d661aSToomas Soome result = 0; 73*4a5d661aSToomas Soome 74*4a5d661aSToomas Soome /* If we need more */ 75*4a5d661aSToomas Soome if (req > 0) { 76*4a5d661aSToomas Soome /* move old data to bottom of buffer */ 77*4a5d661aSToomas Soome if (req < Z_BUFSIZE) 78*4a5d661aSToomas Soome bcopy(zf->zf_buf + req, zf->zf_buf, Z_BUFSIZE - req); 79*4a5d661aSToomas Soome 80*4a5d661aSToomas Soome /* read to fill buffer and update availibility data */ 81*4a5d661aSToomas Soome result = read(zf->zf_rawfd, zf->zf_buf + zf->zf_zstream.avail_in, req); 82*4a5d661aSToomas Soome zf->zf_zstream.next_in = zf->zf_buf; 83*4a5d661aSToomas Soome if (result >= 0) 84*4a5d661aSToomas Soome zf->zf_zstream.avail_in += result; 85*4a5d661aSToomas Soome } 86*4a5d661aSToomas Soome return(result); 87*4a5d661aSToomas Soome } 88*4a5d661aSToomas Soome 89*4a5d661aSToomas Soome /* 90*4a5d661aSToomas Soome * Adapted from get_byte/check_header in libz 91*4a5d661aSToomas Soome * 92*4a5d661aSToomas Soome * Returns 0 if the header is OK, nonzero if not. 93*4a5d661aSToomas Soome */ 94*4a5d661aSToomas Soome static int 95*4a5d661aSToomas Soome get_byte(struct z_file *zf, off_t *curoffp) 96*4a5d661aSToomas Soome { 97*4a5d661aSToomas Soome if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) 98*4a5d661aSToomas Soome return(-1); 99*4a5d661aSToomas Soome zf->zf_zstream.avail_in--; 100*4a5d661aSToomas Soome ++*curoffp; 101*4a5d661aSToomas Soome return(*(zf->zf_zstream.next_in)++); 102*4a5d661aSToomas Soome } 103*4a5d661aSToomas Soome 104*4a5d661aSToomas Soome static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 105*4a5d661aSToomas Soome 106*4a5d661aSToomas Soome /* gzip flag byte */ 107*4a5d661aSToomas Soome #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 108*4a5d661aSToomas Soome #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 109*4a5d661aSToomas Soome #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 110*4a5d661aSToomas Soome #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 111*4a5d661aSToomas Soome #define COMMENT 0x10 /* bit 4 set: file comment present */ 112*4a5d661aSToomas Soome #define RESERVED 0xE0 /* bits 5..7: reserved */ 113*4a5d661aSToomas Soome 114*4a5d661aSToomas Soome static int 115*4a5d661aSToomas Soome check_header(struct z_file *zf) 116*4a5d661aSToomas Soome { 117*4a5d661aSToomas Soome int method; /* method byte */ 118*4a5d661aSToomas Soome int flags; /* flags byte */ 119*4a5d661aSToomas Soome uInt len; 120*4a5d661aSToomas Soome int c; 121*4a5d661aSToomas Soome 122*4a5d661aSToomas Soome zf->zf_dataoffset = 0; 123*4a5d661aSToomas Soome /* Check the gzip magic header */ 124*4a5d661aSToomas Soome for (len = 0; len < 2; len++) { 125*4a5d661aSToomas Soome c = get_byte(zf, &zf->zf_dataoffset); 126*4a5d661aSToomas Soome if (c != gz_magic[len]) { 127*4a5d661aSToomas Soome return(1); 128*4a5d661aSToomas Soome } 129*4a5d661aSToomas Soome } 130*4a5d661aSToomas Soome method = get_byte(zf, &zf->zf_dataoffset); 131*4a5d661aSToomas Soome flags = get_byte(zf, &zf->zf_dataoffset); 132*4a5d661aSToomas Soome if (method != Z_DEFLATED || (flags & RESERVED) != 0) { 133*4a5d661aSToomas Soome return(1); 134*4a5d661aSToomas Soome } 135*4a5d661aSToomas Soome 136*4a5d661aSToomas Soome /* Discard time, xflags and OS code: */ 137*4a5d661aSToomas Soome for (len = 0; len < 6; len++) (void)get_byte(zf, &zf->zf_dataoffset); 138*4a5d661aSToomas Soome 139*4a5d661aSToomas Soome if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ 140*4a5d661aSToomas Soome len = (uInt)get_byte(zf, &zf->zf_dataoffset); 141*4a5d661aSToomas Soome len += ((uInt)get_byte(zf, &zf->zf_dataoffset))<<8; 142*4a5d661aSToomas Soome /* len is garbage if EOF but the loop below will quit anyway */ 143*4a5d661aSToomas Soome while (len-- != 0 && get_byte(zf, &zf->zf_dataoffset) != -1) ; 144*4a5d661aSToomas Soome } 145*4a5d661aSToomas Soome if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ 146*4a5d661aSToomas Soome while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ; 147*4a5d661aSToomas Soome } 148*4a5d661aSToomas Soome if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ 149*4a5d661aSToomas Soome while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ; 150*4a5d661aSToomas Soome } 151*4a5d661aSToomas Soome if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ 152*4a5d661aSToomas Soome for (len = 0; len < 2; len++) c = get_byte(zf, &zf->zf_dataoffset); 153*4a5d661aSToomas Soome } 154*4a5d661aSToomas Soome /* if there's data left, we're in business */ 155*4a5d661aSToomas Soome return((c == -1) ? 1 : 0); 156*4a5d661aSToomas Soome } 157*4a5d661aSToomas Soome 158*4a5d661aSToomas Soome static int 159*4a5d661aSToomas Soome zf_open(const char *fname, struct open_file *f) 160*4a5d661aSToomas Soome { 161*4a5d661aSToomas Soome char *zfname; 162*4a5d661aSToomas Soome int rawfd; 163*4a5d661aSToomas Soome struct z_file *zf; 164*4a5d661aSToomas Soome char *cp; 165*4a5d661aSToomas Soome int error; 166*4a5d661aSToomas Soome struct stat sb; 167*4a5d661aSToomas Soome 168*4a5d661aSToomas Soome /* Have to be in "just read it" mode */ 169*4a5d661aSToomas Soome if (f->f_flags != F_READ) 170*4a5d661aSToomas Soome return(EPERM); 171*4a5d661aSToomas Soome 172*4a5d661aSToomas Soome /* If the name already ends in .gz or .bz2, ignore it */ 173*4a5d661aSToomas Soome if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz") 174*4a5d661aSToomas Soome || !strcmp(cp, ".bz2") || !strcmp(cp, ".split"))) 175*4a5d661aSToomas Soome return(ENOENT); 176*4a5d661aSToomas Soome 177*4a5d661aSToomas Soome /* Try to open the compressed datafile */ 178*4a5d661aSToomas Soome rawfd = open(fname, O_RDONLY | F_GZIP); 179*4a5d661aSToomas Soome if (rawfd == -1) { 180*4a5d661aSToomas Soome /* add .gz sufix and try again */ 181*4a5d661aSToomas Soome zfname = malloc(strlen(fname) + 4); 182*4a5d661aSToomas Soome if (zfname == NULL) 183*4a5d661aSToomas Soome return(ENOMEM); 184*4a5d661aSToomas Soome sprintf(zfname, "%s.gz", fname); 185*4a5d661aSToomas Soome rawfd = open(zfname, O_RDONLY); 186*4a5d661aSToomas Soome free(zfname); 187*4a5d661aSToomas Soome if (rawfd == -1) 188*4a5d661aSToomas Soome return(ENOENT); 189*4a5d661aSToomas Soome } 190*4a5d661aSToomas Soome 191*4a5d661aSToomas Soome if (fstat(rawfd, &sb) < 0) { 192*4a5d661aSToomas Soome printf("zf_open: stat failed\n"); 193*4a5d661aSToomas Soome close(rawfd); 194*4a5d661aSToomas Soome return(ENOENT); 195*4a5d661aSToomas Soome } 196*4a5d661aSToomas Soome if (!S_ISREG(sb.st_mode)) { 197*4a5d661aSToomas Soome close(rawfd); 198*4a5d661aSToomas Soome return(EISDIR); /* best guess */ 199*4a5d661aSToomas Soome } 200*4a5d661aSToomas Soome 201*4a5d661aSToomas Soome /* Allocate a z_file structure, populate it */ 202*4a5d661aSToomas Soome zf = malloc(sizeof(struct z_file)); 203*4a5d661aSToomas Soome if (zf == NULL) 204*4a5d661aSToomas Soome return(ENOMEM); 205*4a5d661aSToomas Soome bzero(zf, sizeof(struct z_file)); 206*4a5d661aSToomas Soome zf->zf_rawfd = rawfd; 207*4a5d661aSToomas Soome 208*4a5d661aSToomas Soome /* Verify that the file is gzipped */ 209*4a5d661aSToomas Soome if (check_header(zf)) { 210*4a5d661aSToomas Soome close(zf->zf_rawfd); 211*4a5d661aSToomas Soome free(zf); 212*4a5d661aSToomas Soome return(EFTYPE); 213*4a5d661aSToomas Soome } 214*4a5d661aSToomas Soome 215*4a5d661aSToomas Soome /* Initialise the inflation engine */ 216*4a5d661aSToomas Soome if ((error = inflateInit2(&(zf->zf_zstream), -15)) != Z_OK) { 217*4a5d661aSToomas Soome printf("zf_open: inflateInit returned %d : %s\n", error, zf->zf_zstream.msg); 218*4a5d661aSToomas Soome close(zf->zf_rawfd); 219*4a5d661aSToomas Soome free(zf); 220*4a5d661aSToomas Soome return(EIO); 221*4a5d661aSToomas Soome } 222*4a5d661aSToomas Soome 223*4a5d661aSToomas Soome /* Looks OK, we'll take it */ 224*4a5d661aSToomas Soome f->f_fsdata = zf; 225*4a5d661aSToomas Soome return(0); 226*4a5d661aSToomas Soome } 227*4a5d661aSToomas Soome 228*4a5d661aSToomas Soome static int 229*4a5d661aSToomas Soome zf_close(struct open_file *f) 230*4a5d661aSToomas Soome { 231*4a5d661aSToomas Soome struct z_file *zf = (struct z_file *)f->f_fsdata; 232*4a5d661aSToomas Soome 233*4a5d661aSToomas Soome inflateEnd(&(zf->zf_zstream)); 234*4a5d661aSToomas Soome close(zf->zf_rawfd); 235*4a5d661aSToomas Soome free(zf); 236*4a5d661aSToomas Soome return(0); 237*4a5d661aSToomas Soome } 238*4a5d661aSToomas Soome 239*4a5d661aSToomas Soome static int 240*4a5d661aSToomas Soome zf_read(struct open_file *f, void *buf, size_t size, size_t *resid) 241*4a5d661aSToomas Soome { 242*4a5d661aSToomas Soome struct z_file *zf = (struct z_file *)f->f_fsdata; 243*4a5d661aSToomas Soome int error; 244*4a5d661aSToomas Soome 245*4a5d661aSToomas Soome zf->zf_zstream.next_out = buf; /* where and how much */ 246*4a5d661aSToomas Soome zf->zf_zstream.avail_out = size; 247*4a5d661aSToomas Soome 248*4a5d661aSToomas Soome while (zf->zf_zstream.avail_out && zf->zf_endseen == 0) { 249*4a5d661aSToomas Soome if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) { 250*4a5d661aSToomas Soome printf("zf_read: fill error\n"); 251*4a5d661aSToomas Soome return(EIO); 252*4a5d661aSToomas Soome } 253*4a5d661aSToomas Soome if (zf->zf_zstream.avail_in == 0) { /* oops, unexpected EOF */ 254*4a5d661aSToomas Soome printf("zf_read: unexpected EOF\n"); 255*4a5d661aSToomas Soome if (zf->zf_zstream.avail_out == size) 256*4a5d661aSToomas Soome return(EIO); 257*4a5d661aSToomas Soome break; 258*4a5d661aSToomas Soome } 259*4a5d661aSToomas Soome 260*4a5d661aSToomas Soome error = inflate(&zf->zf_zstream, Z_SYNC_FLUSH); /* decompression pass */ 261*4a5d661aSToomas Soome if (error == Z_STREAM_END) { /* EOF, all done */ 262*4a5d661aSToomas Soome zf->zf_endseen = 1; 263*4a5d661aSToomas Soome break; 264*4a5d661aSToomas Soome } 265*4a5d661aSToomas Soome if (error != Z_OK) { /* argh, decompression error */ 266*4a5d661aSToomas Soome printf("inflate: %s\n", zf->zf_zstream.msg); 267*4a5d661aSToomas Soome return(EIO); 268*4a5d661aSToomas Soome } 269*4a5d661aSToomas Soome } 270*4a5d661aSToomas Soome if (resid != NULL) 271*4a5d661aSToomas Soome *resid = zf->zf_zstream.avail_out; 272*4a5d661aSToomas Soome return(0); 273*4a5d661aSToomas Soome } 274*4a5d661aSToomas Soome 275*4a5d661aSToomas Soome static int 276*4a5d661aSToomas Soome zf_rewind(struct open_file *f) 277*4a5d661aSToomas Soome { 278*4a5d661aSToomas Soome struct z_file *zf = (struct z_file *)f->f_fsdata; 279*4a5d661aSToomas Soome 280*4a5d661aSToomas Soome if (lseek(zf->zf_rawfd, zf->zf_dataoffset, SEEK_SET) == -1) 281*4a5d661aSToomas Soome return(-1); 282*4a5d661aSToomas Soome zf->zf_zstream.avail_in = 0; 283*4a5d661aSToomas Soome zf->zf_zstream.next_in = NULL; 284*4a5d661aSToomas Soome zf->zf_endseen = 0; 285*4a5d661aSToomas Soome (void)inflateReset(&zf->zf_zstream); 286*4a5d661aSToomas Soome 287*4a5d661aSToomas Soome return(0); 288*4a5d661aSToomas Soome } 289*4a5d661aSToomas Soome 290*4a5d661aSToomas Soome static off_t 291*4a5d661aSToomas Soome zf_seek(struct open_file *f, off_t offset, int where) 292*4a5d661aSToomas Soome { 293*4a5d661aSToomas Soome struct z_file *zf = (struct z_file *)f->f_fsdata; 294*4a5d661aSToomas Soome off_t target; 295*4a5d661aSToomas Soome char discard[16]; 296*4a5d661aSToomas Soome 297*4a5d661aSToomas Soome switch (where) { 298*4a5d661aSToomas Soome case SEEK_SET: 299*4a5d661aSToomas Soome target = offset; 300*4a5d661aSToomas Soome break; 301*4a5d661aSToomas Soome case SEEK_CUR: 302*4a5d661aSToomas Soome target = offset + zf->zf_zstream.total_out; 303*4a5d661aSToomas Soome break; 304*4a5d661aSToomas Soome case SEEK_END: 305*4a5d661aSToomas Soome target = -1; 306*4a5d661aSToomas Soome default: 307*4a5d661aSToomas Soome errno = EINVAL; 308*4a5d661aSToomas Soome return(-1); 309*4a5d661aSToomas Soome } 310*4a5d661aSToomas Soome 311*4a5d661aSToomas Soome /* rewind if required */ 312*4a5d661aSToomas Soome if (target < zf->zf_zstream.total_out && zf_rewind(f) != 0) 313*4a5d661aSToomas Soome return(-1); 314*4a5d661aSToomas Soome 315*4a5d661aSToomas Soome /* skip forwards if required */ 316*4a5d661aSToomas Soome while (target > zf->zf_zstream.total_out) { 317*4a5d661aSToomas Soome errno = zf_read(f, discard, min(sizeof(discard), 318*4a5d661aSToomas Soome target - zf->zf_zstream.total_out), NULL); 319*4a5d661aSToomas Soome if (errno) 320*4a5d661aSToomas Soome return(-1); 321*4a5d661aSToomas Soome } 322*4a5d661aSToomas Soome /* This is where we are (be honest if we overshot) */ 323*4a5d661aSToomas Soome return(zf->zf_zstream.total_out); 324*4a5d661aSToomas Soome } 325*4a5d661aSToomas Soome 326*4a5d661aSToomas Soome 327*4a5d661aSToomas Soome static int 328*4a5d661aSToomas Soome zf_stat(struct open_file *f, struct stat *sb) 329*4a5d661aSToomas Soome { 330*4a5d661aSToomas Soome struct z_file *zf = (struct z_file *)f->f_fsdata; 331*4a5d661aSToomas Soome int result, res; 332*4a5d661aSToomas Soome off_t pos1, pos2; 333*4a5d661aSToomas Soome uint32_t size; 334*4a5d661aSToomas Soome 335*4a5d661aSToomas Soome /* stat as normal, but indicate that size is unknown */ 336*4a5d661aSToomas Soome if ((result = fstat(zf->zf_rawfd, sb)) == 0) { 337*4a5d661aSToomas Soome if (sb->st_size == -1) 338*4a5d661aSToomas Soome return (result); 339*4a5d661aSToomas Soome pos1 = lseek(zf->zf_rawfd, 0, SEEK_CUR); 340*4a5d661aSToomas Soome pos2 = lseek(zf->zf_rawfd, sb->st_size - 4, SEEK_SET); 341*4a5d661aSToomas Soome if (pos2 != -1) { 342*4a5d661aSToomas Soome if (read(zf->zf_rawfd, &size, 4) == 4) 343*4a5d661aSToomas Soome sb->st_size = (off_t) size; 344*4a5d661aSToomas Soome else 345*4a5d661aSToomas Soome sb->st_size = -1; 346*4a5d661aSToomas Soome } else 347*4a5d661aSToomas Soome sb->st_size = -1; 348*4a5d661aSToomas Soome 349*4a5d661aSToomas Soome pos1 = lseek(zf->zf_rawfd, pos1, SEEK_SET); 350*4a5d661aSToomas Soome } 351*4a5d661aSToomas Soome return(result); 352*4a5d661aSToomas Soome } 353