138451Smsmith/* 238451Smsmith * Copyright (c) 1998 Michael Smith. 338451Smsmith * All rights reserved. 438451Smsmith * 538451Smsmith * Redistribution and use in source and binary forms, with or without 638451Smsmith * modification, are permitted provided that the following conditions 738451Smsmith * are met: 838451Smsmith * 1. Redistributions of source code must retain the above copyright 938451Smsmith * notice, this list of conditions and the following disclaimer. 1038451Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1138451Smsmith * notice, this list of conditions and the following disclaimer in the 1238451Smsmith * documentation and/or other materials provided with the distribution. 1338451Smsmith * 1438451Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1538451Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1638451Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1738451Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1838451Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1938451Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2038451Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2138451Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2238451Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2338451Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2438451Smsmith * SUCH DAMAGE. 2538451Smsmith */ 2638451Smsmith 2784221Sdillon#include <sys/cdefs.h> 2884221Sdillon__FBSDID("$FreeBSD$"); 2984221Sdillon 3038451Smsmith#include "stand.h" 3138451Smsmith 3238451Smsmith#include <sys/stat.h> 3338451Smsmith#include <string.h> 3438451Smsmith#include <zlib.h> 3538451Smsmith 3638451Smsmith#define Z_BUFSIZE 2048 /* XXX larger? */ 3738451Smsmith 3838451Smsmithstruct z_file 3938451Smsmith{ 4038451Smsmith int zf_rawfd; 41123392Sgreen off_t zf_dataoffset; 4238451Smsmith z_stream zf_zstream; 4338451Smsmith char zf_buf[Z_BUFSIZE]; 44174741Ssobomax int zf_endseen; 4538451Smsmith}; 4638451Smsmith 4738451Smsmithstatic int zf_fill(struct z_file *z); 4839468Smsmithstatic int zf_open(const char *path, struct open_file *f); 4938451Smsmithstatic int zf_close(struct open_file *f); 5038451Smsmithstatic int zf_read(struct open_file *f, void *buf, size_t size, size_t *resid); 5138451Smsmithstatic off_t zf_seek(struct open_file *f, off_t offset, int where); 5238451Smsmithstatic int zf_stat(struct open_file *f, struct stat *sb); 5338451Smsmith 54108100Sjakestruct fs_ops gzipfs_fsops = { 5538451Smsmith "zip", 5638451Smsmith zf_open, 5738451Smsmith zf_close, 5838451Smsmith zf_read, 5938451Smsmith null_write, 6038451Smsmith zf_seek, 6159766Sjlemon zf_stat, 6259766Sjlemon null_readdir 6338451Smsmith}; 6438451Smsmith 6538451Smsmithstatic int 6638451Smsmithzf_fill(struct z_file *zf) 6738451Smsmith{ 6838451Smsmith int result; 6938451Smsmith int req; 7038451Smsmith 7138451Smsmith req = Z_BUFSIZE - zf->zf_zstream.avail_in; 7238451Smsmith result = 0; 7338451Smsmith 7438451Smsmith /* If we need more */ 7538451Smsmith if (req > 0) { 7638451Smsmith /* move old data to bottom of buffer */ 7738451Smsmith if (req < Z_BUFSIZE) 7838451Smsmith bcopy(zf->zf_buf + req, zf->zf_buf, Z_BUFSIZE - req); 7938451Smsmith 8038451Smsmith /* read to fill buffer and update availibility data */ 8138451Smsmith result = read(zf->zf_rawfd, zf->zf_buf + zf->zf_zstream.avail_in, req); 8238451Smsmith zf->zf_zstream.next_in = zf->zf_buf; 8338451Smsmith if (result >= 0) 8438451Smsmith zf->zf_zstream.avail_in += result; 8538451Smsmith } 8638451Smsmith return(result); 8738451Smsmith} 8838451Smsmith 8938451Smsmith/* 9038451Smsmith * Adapted from get_byte/check_header in libz 9138451Smsmith * 9238451Smsmith * Returns 0 if the header is OK, nonzero if not. 9338451Smsmith */ 9438451Smsmithstatic int 95123392Sgreenget_byte(struct z_file *zf, off_t *curoffp) 9638451Smsmith{ 9738451Smsmith if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) 9838451Smsmith return(-1); 9938451Smsmith zf->zf_zstream.avail_in--; 100123392Sgreen ++*curoffp; 10138451Smsmith return(*(zf->zf_zstream.next_in)++); 10238451Smsmith} 10338451Smsmith 10438451Smsmithstatic int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 10538451Smsmith 10638451Smsmith/* gzip flag byte */ 10738451Smsmith#define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 10838451Smsmith#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 10938451Smsmith#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 11038451Smsmith#define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 11138451Smsmith#define COMMENT 0x10 /* bit 4 set: file comment present */ 11238451Smsmith#define RESERVED 0xE0 /* bits 5..7: reserved */ 11338451Smsmith 11438451Smsmithstatic int 11538451Smsmithcheck_header(struct z_file *zf) 11638451Smsmith{ 11738451Smsmith int method; /* method byte */ 11838451Smsmith int flags; /* flags byte */ 11938451Smsmith uInt len; 12038451Smsmith int c; 12138451Smsmith 122123392Sgreen zf->zf_dataoffset = 0; 12338451Smsmith /* Check the gzip magic header */ 12438451Smsmith for (len = 0; len < 2; len++) { 125123392Sgreen c = get_byte(zf, &zf->zf_dataoffset); 12638451Smsmith if (c != gz_magic[len]) { 12738451Smsmith return(1); 12838451Smsmith } 12938451Smsmith } 130123392Sgreen method = get_byte(zf, &zf->zf_dataoffset); 131123392Sgreen flags = get_byte(zf, &zf->zf_dataoffset); 13238451Smsmith if (method != Z_DEFLATED || (flags & RESERVED) != 0) { 13338451Smsmith return(1); 13438451Smsmith } 13538451Smsmith 13638451Smsmith /* Discard time, xflags and OS code: */ 137123392Sgreen for (len = 0; len < 6; len++) (void)get_byte(zf, &zf->zf_dataoffset); 13838451Smsmith 13938451Smsmith if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ 140123392Sgreen len = (uInt)get_byte(zf, &zf->zf_dataoffset); 141123392Sgreen len += ((uInt)get_byte(zf, &zf->zf_dataoffset))<<8; 14238451Smsmith /* len is garbage if EOF but the loop below will quit anyway */ 143123392Sgreen while (len-- != 0 && get_byte(zf, &zf->zf_dataoffset) != -1) ; 14438451Smsmith } 14538451Smsmith if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ 146123392Sgreen while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ; 14738451Smsmith } 14838451Smsmith if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ 149123392Sgreen while ((c = get_byte(zf, &zf->zf_dataoffset)) != 0 && c != -1) ; 15038451Smsmith } 15138451Smsmith if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ 152123392Sgreen for (len = 0; len < 2; len++) c = get_byte(zf, &zf->zf_dataoffset); 15338451Smsmith } 15438451Smsmith /* if there's data left, we're in business */ 15538451Smsmith return((c == -1) ? 1 : 0); 15638451Smsmith} 15738451Smsmith 15838451Smsmithstatic int 15939468Smsmithzf_open(const char *fname, struct open_file *f) 16038451Smsmith{ 16138451Smsmith static char *zfname; 16238451Smsmith int rawfd; 16338451Smsmith struct z_file *zf; 16438451Smsmith char *cp; 16538451Smsmith int error; 16638451Smsmith struct stat sb; 16738451Smsmith 16838451Smsmith /* Have to be in "just read it" mode */ 16938451Smsmith if (f->f_flags != F_READ) 17038451Smsmith return(EPERM); 17138451Smsmith 17283610Ssobomax /* If the name already ends in .gz or .bz2, ignore it */ 17383610Ssobomax if ((cp = strrchr(fname, '.')) && (!strcmp(cp, ".gz") 17492494Ssobomax || !strcmp(cp, ".bz2") || !strcmp(cp, ".split"))) 17538451Smsmith return(ENOENT); 17638451Smsmith 17738451Smsmith /* Construct new name */ 17839665Smsmith zfname = malloc(strlen(fname) + 4); 17982852Skris if (zfname == NULL) 18082852Skris return(ENOMEM); 18138451Smsmith sprintf(zfname, "%s.gz", fname); 18238451Smsmith 18338451Smsmith /* Try to open the compressed datafile */ 18438451Smsmith rawfd = open(zfname, O_RDONLY); 18538451Smsmith free(zfname); 18638451Smsmith if (rawfd == -1) 18738451Smsmith return(ENOENT); 18838451Smsmith 18938451Smsmith if (fstat(rawfd, &sb) < 0) { 19038451Smsmith printf("zf_open: stat failed\n"); 19138451Smsmith close(rawfd); 19238451Smsmith return(ENOENT); 19338451Smsmith } 19438451Smsmith if (!S_ISREG(sb.st_mode)) { 19538451Smsmith printf("zf_open: not a file\n"); 19638451Smsmith close(rawfd); 19738451Smsmith return(EISDIR); /* best guess */ 19838451Smsmith } 19938451Smsmith 20038451Smsmith /* Allocate a z_file structure, populate it */ 20138451Smsmith zf = malloc(sizeof(struct z_file)); 20282852Skris if (zf == NULL) 20382852Skris return(ENOMEM); 20438451Smsmith bzero(zf, sizeof(struct z_file)); 20538451Smsmith zf->zf_rawfd = rawfd; 20638451Smsmith 207200919Sjhb /* Verify that the file is gzipped */ 20838451Smsmith if (check_header(zf)) { 20938451Smsmith close(zf->zf_rawfd); 21038451Smsmith free(zf); 21138451Smsmith return(EFTYPE); 21238451Smsmith } 21338451Smsmith 21438451Smsmith /* Initialise the inflation engine */ 21538451Smsmith if ((error = inflateInit2(&(zf->zf_zstream), -15)) != Z_OK) { 21638451Smsmith printf("zf_open: inflateInit returned %d : %s\n", error, zf->zf_zstream.msg); 21738451Smsmith close(zf->zf_rawfd); 21838451Smsmith free(zf); 21938451Smsmith return(EIO); 22038451Smsmith } 22138451Smsmith 22238451Smsmith /* Looks OK, we'll take it */ 22338451Smsmith f->f_fsdata = zf; 22438451Smsmith return(0); 22538451Smsmith} 22638451Smsmith 22738451Smsmithstatic int 22838451Smsmithzf_close(struct open_file *f) 22938451Smsmith{ 23038451Smsmith struct z_file *zf = (struct z_file *)f->f_fsdata; 23138451Smsmith 23238451Smsmith inflateEnd(&(zf->zf_zstream)); 23338451Smsmith close(zf->zf_rawfd); 23438451Smsmith free(zf); 23538451Smsmith return(0); 23638451Smsmith} 23738451Smsmith 23838451Smsmithstatic int 23938451Smsmithzf_read(struct open_file *f, void *buf, size_t size, size_t *resid) 24038451Smsmith{ 24138451Smsmith struct z_file *zf = (struct z_file *)f->f_fsdata; 24238451Smsmith int error; 24338451Smsmith 24438451Smsmith zf->zf_zstream.next_out = buf; /* where and how much */ 24538451Smsmith zf->zf_zstream.avail_out = size; 24638451Smsmith 247174741Ssobomax while (zf->zf_zstream.avail_out && zf->zf_endseen == 0) { 24838451Smsmith if ((zf->zf_zstream.avail_in == 0) && (zf_fill(zf) == -1)) { 24938451Smsmith printf("zf_read: fill error\n"); 250124811Sjhb return(EIO); 25138451Smsmith } 25238451Smsmith if (zf->zf_zstream.avail_in == 0) { /* oops, unexpected EOF */ 25338451Smsmith printf("zf_read: unexpected EOF\n"); 254124811Sjhb if (zf->zf_zstream.avail_out == size) 255200919Sjhb return(EIO); 25638451Smsmith break; 25738451Smsmith } 25838451Smsmith 25938451Smsmith error = inflate(&zf->zf_zstream, Z_SYNC_FLUSH); /* decompression pass */ 26038451Smsmith if (error == Z_STREAM_END) { /* EOF, all done */ 261174741Ssobomax zf->zf_endseen = 1; 26238451Smsmith break; 26338451Smsmith } 26438451Smsmith if (error != Z_OK) { /* argh, decompression error */ 26538451Smsmith printf("inflate: %s\n", zf->zf_zstream.msg); 266124811Sjhb return(EIO); 26738451Smsmith } 26838451Smsmith } 26938451Smsmith if (resid != NULL) 27038451Smsmith *resid = zf->zf_zstream.avail_out; 27138451Smsmith return(0); 27238451Smsmith} 27338451Smsmith 274123392Sgreenstatic int 275123392Sgreenzf_rewind(struct open_file *f) 276123392Sgreen{ 277123392Sgreen struct z_file *zf = (struct z_file *)f->f_fsdata; 278123392Sgreen 279123392Sgreen if (lseek(zf->zf_rawfd, zf->zf_dataoffset, SEEK_SET) == -1) 280200919Sjhb return(-1); 281123392Sgreen zf->zf_zstream.avail_in = 0; 282123392Sgreen zf->zf_zstream.next_in = NULL; 283200919Sjhb zf->zf_endseen = 0; 284123392Sgreen (void)inflateReset(&zf->zf_zstream); 285123392Sgreen 286200919Sjhb return(0); 287123392Sgreen} 288123392Sgreen 28938451Smsmithstatic off_t 29038451Smsmithzf_seek(struct open_file *f, off_t offset, int where) 29138451Smsmith{ 29238451Smsmith struct z_file *zf = (struct z_file *)f->f_fsdata; 29338451Smsmith off_t target; 29438451Smsmith char discard[16]; 29538451Smsmith 29638451Smsmith switch (where) { 29738451Smsmith case SEEK_SET: 29838451Smsmith target = offset; 29938451Smsmith break; 30038451Smsmith case SEEK_CUR: 30138451Smsmith target = offset + zf->zf_zstream.total_out; 30238451Smsmith break; 303124811Sjhb case SEEK_END: 304124811Sjhb target = -1; 30538451Smsmith default: 306124811Sjhb errno = EINVAL; 307200919Sjhb return(-1); 30838451Smsmith } 30938451Smsmith 310123392Sgreen /* rewind if required */ 311123392Sgreen if (target < zf->zf_zstream.total_out && zf_rewind(f) != 0) 312200919Sjhb return(-1); 31338451Smsmith 31438451Smsmith /* skip forwards if required */ 31538451Smsmith while (target > zf->zf_zstream.total_out) { 316124811Sjhb errno = zf_read(f, discard, min(sizeof(discard), 317124811Sjhb target - zf->zf_zstream.total_out), NULL); 318124811Sjhb if (errno) 31938451Smsmith return(-1); 32038451Smsmith } 32138451Smsmith /* This is where we are (be honest if we overshot) */ 322200919Sjhb return(zf->zf_zstream.total_out); 32338451Smsmith} 32438451Smsmith 32538451Smsmith 32638451Smsmithstatic int 32738451Smsmithzf_stat(struct open_file *f, struct stat *sb) 32838451Smsmith{ 32938451Smsmith struct z_file *zf = (struct z_file *)f->f_fsdata; 33038451Smsmith int result; 33138451Smsmith 33238451Smsmith /* stat as normal, but indicate that size is unknown */ 33338451Smsmith if ((result = fstat(zf->zf_rawfd, sb)) == 0) 33438451Smsmith sb->st_size = -1; 33538451Smsmith return(result); 33638451Smsmith} 33738451Smsmith 33838451Smsmith 33938451Smsmith 340