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