1*ae115bc7Smrj /* 2*ae115bc7Smrj * CDDL HEADER START 3*ae115bc7Smrj * 4*ae115bc7Smrj * The contents of this file are subject to the terms of the 5*ae115bc7Smrj * Common Development and Distribution License (the "License"). 6*ae115bc7Smrj * You may not use this file except in compliance with the License. 7*ae115bc7Smrj * 8*ae115bc7Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*ae115bc7Smrj * or http://www.opensolaris.org/os/licensing. 10*ae115bc7Smrj * See the License for the specific language governing permissions 11*ae115bc7Smrj * and limitations under the License. 12*ae115bc7Smrj * 13*ae115bc7Smrj * When distributing Covered Code, include this CDDL HEADER in each 14*ae115bc7Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*ae115bc7Smrj * If applicable, add the following below this CDDL HEADER, with the 16*ae115bc7Smrj * fields enclosed by brackets "[]" replaced with your own identifying 17*ae115bc7Smrj * information: Portions Copyright [yyyy] [name of copyright owner] 18*ae115bc7Smrj * 19*ae115bc7Smrj * CDDL HEADER END 20*ae115bc7Smrj */ 21*ae115bc7Smrj /* 22*ae115bc7Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*ae115bc7Smrj * Use is subject to license terms. 24*ae115bc7Smrj */ 25*ae115bc7Smrj 26*ae115bc7Smrj #pragma ident "%Z%%M% %I% %E% SMI" 27*ae115bc7Smrj 28*ae115bc7Smrj /* 29*ae115bc7Smrj * Decompression module for stand alone file systems. 30*ae115bc7Smrj */ 31*ae115bc7Smrj 32*ae115bc7Smrj #include <sys/param.h> 33*ae115bc7Smrj #include <sys/sysmacros.h> 34*ae115bc7Smrj #include <sys/vnode.h> 35*ae115bc7Smrj #include <sys/bootvfs.h> 36*ae115bc7Smrj #include <sys/filep.h> 37*ae115bc7Smrj #include <zmod/zlib.h> 38*ae115bc7Smrj 39*ae115bc7Smrj #ifdef _BOOT 40*ae115bc7Smrj #include "../common/util.h" 41*ae115bc7Smrj #else 42*ae115bc7Smrj #include <sys/sunddi.h> 43*ae115bc7Smrj #endif 44*ae115bc7Smrj 45*ae115bc7Smrj #define MAX_DECOMP_BUFS 8 46*ae115bc7Smrj #define GZIP_ID_BYTE_1 0x1f 47*ae115bc7Smrj #define GZIP_ID_BYTE_2 0x8b 48*ae115bc7Smrj #define GZIP_CM_DEFLATE 0x08 49*ae115bc7Smrj #define SEEKBUFSIZE 8192 50*ae115bc7Smrj 51*ae115bc7Smrj #ifdef _BOOT 52*ae115bc7Smrj #define dprintf if (cf_debug) printf 53*ae115bc7Smrj #else 54*ae115bc7Smrj #define dprintf if (cf_debug) printf 55*ae115bc7Smrj 56*ae115bc7Smrj #endif 57*ae115bc7Smrj 58*ae115bc7Smrj extern int bootrd_debug; 59*ae115bc7Smrj extern void *bkmem_alloc(size_t); 60*ae115bc7Smrj extern void bkmem_free(void *, size_t); 61*ae115bc7Smrj 62*ae115bc7Smrj caddr_t scratch_bufs[MAX_DECOMP_BUFS]; /* array of free scratch mem bufs */ 63*ae115bc7Smrj int decomp_bufcnt; /* total no, of allocated decomp bufs */ 64*ae115bc7Smrj int free_dcomp_bufs; /* no. of free decomp bufs */ 65*ae115bc7Smrj char seek_scrbuf[SEEKBUFSIZE]; /* buffer for seeking */ 66*ae115bc7Smrj int cf_debug; /* non-zero enables debug prints */ 67*ae115bc7Smrj 68*ae115bc7Smrj void * 69*ae115bc7Smrj cf_alloc(void *opaque, unsigned int items, unsigned int size) 70*ae115bc7Smrj { 71*ae115bc7Smrj fileid_t *filep; 72*ae115bc7Smrj unsigned int nbytes; 73*ae115bc7Smrj caddr_t ptr; 74*ae115bc7Smrj 75*ae115bc7Smrj filep = (fileid_t *)opaque; 76*ae115bc7Smrj nbytes = roundup(items * size, sizeof (long)); 77*ae115bc7Smrj if (nbytes > (DECOMP_BUFSIZE - filep->fi_dcscrused)) { 78*ae115bc7Smrj ptr = bkmem_alloc(nbytes); 79*ae115bc7Smrj } else { 80*ae115bc7Smrj ptr = &filep->fi_dcscrbuf[filep->fi_dcscrused]; 81*ae115bc7Smrj filep->fi_dcscrused += nbytes; 82*ae115bc7Smrj } 83*ae115bc7Smrj bzero(ptr, nbytes); 84*ae115bc7Smrj return (ptr); 85*ae115bc7Smrj } 86*ae115bc7Smrj 87*ae115bc7Smrj /* 88*ae115bc7Smrj * Decompression scratch memory free routine, does nothing since we free 89*ae115bc7Smrj * the entire scratch area all at once on file close. 90*ae115bc7Smrj */ 91*ae115bc7Smrj /* ARGSUSED */ 92*ae115bc7Smrj void 93*ae115bc7Smrj cf_free(void *opaque, void *addr) 94*ae115bc7Smrj { 95*ae115bc7Smrj } 96*ae115bc7Smrj 97*ae115bc7Smrj /* 98*ae115bc7Smrj * Read the first block of the file described by filep and determine if 99*ae115bc7Smrj * the file is gzip-compressed. If so, the compressed flag will be set 100*ae115bc7Smrj * in the fileid_t struct pointed to by filep and it will be initialized 101*ae115bc7Smrj * for doing decompression on reads to the file. 102*ae115bc7Smrj */ 103*ae115bc7Smrj int 104*ae115bc7Smrj cf_check_compressed(fileid_t *filep) 105*ae115bc7Smrj { 106*ae115bc7Smrj unsigned char *filebytes; 107*ae115bc7Smrj z_stream *zsp; 108*ae115bc7Smrj 109*ae115bc7Smrj filep->fi_offset = 0; 110*ae115bc7Smrj if ((filep->fi_getblock)(filep) == -1) 111*ae115bc7Smrj return (-1); 112*ae115bc7Smrj filep->fi_offset = 0; 113*ae115bc7Smrj filep->fi_count = 0; 114*ae115bc7Smrj filep->fi_cfoff = 0; 115*ae115bc7Smrj filebytes = (unsigned char *)filep->fi_memp; 116*ae115bc7Smrj if (filebytes[0] != GZIP_ID_BYTE_1 || filebytes[1] != GZIP_ID_BYTE_2 || 117*ae115bc7Smrj filebytes[2] != GZIP_CM_DEFLATE) 118*ae115bc7Smrj return (0); /* not compressed */ 119*ae115bc7Smrj filep->fi_flags |= FI_COMPRESSED; 120*ae115bc7Smrj 121*ae115bc7Smrj dprintf("file %s is compressed\n", filep->fi_path); 122*ae115bc7Smrj 123*ae115bc7Smrj /* 124*ae115bc7Smrj * Allocate decompress scratch buffer 125*ae115bc7Smrj */ 126*ae115bc7Smrj if (free_dcomp_bufs) { 127*ae115bc7Smrj filep->fi_dcscrbuf = scratch_bufs[--free_dcomp_bufs]; 128*ae115bc7Smrj } else { 129*ae115bc7Smrj filep->fi_dcscrbuf = bkmem_alloc(DECOMP_BUFSIZE); 130*ae115bc7Smrj decomp_bufcnt++; 131*ae115bc7Smrj } 132*ae115bc7Smrj filep->fi_dcscrused = 0; 133*ae115bc7Smrj zsp = bkmem_alloc(sizeof (*zsp)); 134*ae115bc7Smrj filep->fi_dcstream = zsp; 135*ae115bc7Smrj /* 136*ae115bc7Smrj * Initialize the decompression stream 137*ae115bc7Smrj */ 138*ae115bc7Smrj bzero(zsp, sizeof (*zsp)); 139*ae115bc7Smrj zsp->opaque = filep; 140*ae115bc7Smrj zsp->zalloc = cf_alloc; 141*ae115bc7Smrj zsp->zfree = cf_free; 142*ae115bc7Smrj zsp->avail_in = 0; 143*ae115bc7Smrj zsp->next_in = NULL; 144*ae115bc7Smrj zsp->avail_out = 0; 145*ae115bc7Smrj zsp->next_out = NULL; 146*ae115bc7Smrj if (inflateInit2(zsp, -MAX_WBITS) != Z_OK) 147*ae115bc7Smrj return (-1); 148*ae115bc7Smrj return (0); 149*ae115bc7Smrj } 150*ae115bc7Smrj 151*ae115bc7Smrj /* 152*ae115bc7Smrj * If the file described by fileid_t struct at *filep is compressed 153*ae115bc7Smrj * free any resources associated with the decompression. (decompression 154*ae115bc7Smrj * buffer, etc.). 155*ae115bc7Smrj */ 156*ae115bc7Smrj void 157*ae115bc7Smrj cf_close(fileid_t *filep) 158*ae115bc7Smrj { 159*ae115bc7Smrj if ((filep->fi_flags & FI_COMPRESSED) == 0) 160*ae115bc7Smrj return; 161*ae115bc7Smrj dprintf("cf_close: %s\n", filep->fi_path); 162*ae115bc7Smrj (void) inflateEnd(filep->fi_dcstream); 163*ae115bc7Smrj bkmem_free(filep->fi_dcstream, sizeof (z_stream)); 164*ae115bc7Smrj if (free_dcomp_bufs == MAX_DECOMP_BUFS) { 165*ae115bc7Smrj bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE); 166*ae115bc7Smrj } else { 167*ae115bc7Smrj scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf; 168*ae115bc7Smrj } 169*ae115bc7Smrj } 170*ae115bc7Smrj 171*ae115bc7Smrj void 172*ae115bc7Smrj cf_rewind(fileid_t *filep) 173*ae115bc7Smrj { 174*ae115bc7Smrj z_stream *zsp; 175*ae115bc7Smrj 176*ae115bc7Smrj dprintf("cf_rewind: %s\n", filep->fi_path); 177*ae115bc7Smrj zsp = filep->fi_dcstream; 178*ae115bc7Smrj zsp->avail_in = 0; 179*ae115bc7Smrj zsp->next_in = NULL; 180*ae115bc7Smrj (void) inflateReset(zsp); 181*ae115bc7Smrj filep->fi_cfoff = 0; 182*ae115bc7Smrj } 183*ae115bc7Smrj 184*ae115bc7Smrj #define FLG_FHCRC 0x02 /* crc field present */ 185*ae115bc7Smrj #define FLG_FEXTRA 0x04 /* "extra" field present */ 186*ae115bc7Smrj #define FLG_FNAME 0x08 /* file name field present */ 187*ae115bc7Smrj #define FLG_FCOMMENT 0x10 /* comment field present */ 188*ae115bc7Smrj 189*ae115bc7Smrj /* 190*ae115bc7Smrj * Calculate the header length of a gzip compressed file 191*ae115bc7Smrj */ 192*ae115bc7Smrj static int 193*ae115bc7Smrj cf_headerlen(unsigned char *hp) 194*ae115bc7Smrj { 195*ae115bc7Smrj unsigned char flag; 196*ae115bc7Smrj int xlen, hlen = 0; 197*ae115bc7Smrj 198*ae115bc7Smrj hlen += 3; /* skip ID bytes and compression method */ 199*ae115bc7Smrj flag = hp[hlen]; 200*ae115bc7Smrj hlen += 7; /* skip flag, mtime(4 bytes), xfl, and os */ 201*ae115bc7Smrj if (flag & FLG_FEXTRA) { 202*ae115bc7Smrj xlen = hp[hlen++]; 203*ae115bc7Smrj xlen += hp[hlen++] << 8; 204*ae115bc7Smrj hlen += xlen; /* skip extra field */ 205*ae115bc7Smrj } 206*ae115bc7Smrj if (flag & FLG_FNAME) 207*ae115bc7Smrj hlen += strlen((char *)&hp[hlen]) + 1; /* skip file name */ 208*ae115bc7Smrj if (flag & FLG_FCOMMENT) 209*ae115bc7Smrj hlen += strlen((char *)&hp[hlen]) + 1; /* skip comment */ 210*ae115bc7Smrj if (flag & FLG_FHCRC) 211*ae115bc7Smrj hlen += 2; /* skip crc */ 212*ae115bc7Smrj return (hlen); 213*ae115bc7Smrj } 214*ae115bc7Smrj 215*ae115bc7Smrj /* 216*ae115bc7Smrj * Read at the current uncompressed offset from the compressed file described 217*ae115bc7Smrj * by *filep. Will return decompressed data. 218*ae115bc7Smrj */ 219*ae115bc7Smrj int 220*ae115bc7Smrj cf_read(fileid_t *filep, caddr_t buf, size_t count) 221*ae115bc7Smrj { 222*ae115bc7Smrj z_stream *zsp; 223*ae115bc7Smrj struct inode *ip; 224*ae115bc7Smrj int err = Z_OK; 225*ae115bc7Smrj int hlen, infbytes; 226*ae115bc7Smrj off_t soff; 227*ae115bc7Smrj caddr_t smemp; 228*ae115bc7Smrj 229*ae115bc7Smrj dprintf("cf_read: %s ", filep->fi_path); 230*ae115bc7Smrj dprintf("%lx bytes\n", count); 231*ae115bc7Smrj zsp = filep->fi_dcstream; 232*ae115bc7Smrj ip = filep->fi_inode; 233*ae115bc7Smrj dprintf(" reading at offset %lx\n", zsp->total_out); 234*ae115bc7Smrj zsp->next_out = (unsigned char *)buf; 235*ae115bc7Smrj zsp->avail_out = count; 236*ae115bc7Smrj while (zsp->avail_out != 0) { 237*ae115bc7Smrj if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) { 238*ae115bc7Smrj /* 239*ae115bc7Smrj * read a block of the file to inflate 240*ae115bc7Smrj */ 241*ae115bc7Smrj soff = filep->fi_offset; 242*ae115bc7Smrj smemp = filep->fi_memp; 243*ae115bc7Smrj filep->fi_memp = NULL; 244*ae115bc7Smrj filep->fi_offset = filep->fi_cfoff; 245*ae115bc7Smrj filep->fi_count = 0; 246*ae115bc7Smrj if ((*filep->fi_getblock)(filep) == -1) 247*ae115bc7Smrj return (-1); 248*ae115bc7Smrj filep->fi_offset = soff; 249*ae115bc7Smrj zsp->next_in = (unsigned char *)filep->fi_memp; 250*ae115bc7Smrj zsp->avail_in = filep->fi_count; 251*ae115bc7Smrj filep->fi_memp = smemp; 252*ae115bc7Smrj /* 253*ae115bc7Smrj * If we are reading the first block of the file, we 254*ae115bc7Smrj * need to skip over the header bytes before inflating 255*ae115bc7Smrj */ 256*ae115bc7Smrj if (filep->fi_cfoff == 0) { 257*ae115bc7Smrj hlen = cf_headerlen(zsp->next_in); 258*ae115bc7Smrj zsp->next_in += hlen; 259*ae115bc7Smrj zsp->avail_in -= hlen; 260*ae115bc7Smrj } 261*ae115bc7Smrj filep->fi_cfoff += filep->fi_count; 262*ae115bc7Smrj } 263*ae115bc7Smrj infbytes = zsp->avail_out; 264*ae115bc7Smrj dprintf("attempting inflate of %x bytes to buf at: %lx\n", 265*ae115bc7Smrj zsp->avail_out, (unsigned long)zsp->next_out); 266*ae115bc7Smrj err = inflate(zsp, Z_NO_FLUSH); 267*ae115bc7Smrj infbytes -= zsp->avail_out; 268*ae115bc7Smrj dprintf("inflated %x bytes, errcode=%d\n", infbytes, err); 269*ae115bc7Smrj /* 270*ae115bc7Smrj * break out if we hit end of the compressed file 271*ae115bc7Smrj * or the end of the compressed byte stream 272*ae115bc7Smrj */ 273*ae115bc7Smrj if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END) 274*ae115bc7Smrj break; 275*ae115bc7Smrj } 276*ae115bc7Smrj dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out); 277*ae115bc7Smrj return (count - zsp->avail_out); 278*ae115bc7Smrj } 279*ae115bc7Smrj 280*ae115bc7Smrj /* 281*ae115bc7Smrj * Seek to the location specified by addr 282*ae115bc7Smrj */ 283*ae115bc7Smrj void 284*ae115bc7Smrj cf_seek(fileid_t *filep, off_t addr, int whence) 285*ae115bc7Smrj { 286*ae115bc7Smrj z_stream *zsp; 287*ae115bc7Smrj int readsz; 288*ae115bc7Smrj 289*ae115bc7Smrj dprintf("cf_seek: %s ", filep->fi_path); 290*ae115bc7Smrj dprintf("to %lx\n", addr); 291*ae115bc7Smrj zsp = filep->fi_dcstream; 292*ae115bc7Smrj if (whence == SEEK_CUR) 293*ae115bc7Smrj addr += zsp->total_out; 294*ae115bc7Smrj /* 295*ae115bc7Smrj * To seek backwards, must rewind and seek forwards 296*ae115bc7Smrj */ 297*ae115bc7Smrj if (addr < zsp->total_out) { 298*ae115bc7Smrj cf_rewind(filep); 299*ae115bc7Smrj filep->fi_offset = 0; 300*ae115bc7Smrj } else { 301*ae115bc7Smrj addr -= zsp->total_out; 302*ae115bc7Smrj } 303*ae115bc7Smrj while (addr > 0) { 304*ae115bc7Smrj readsz = MIN(addr, SEEKBUFSIZE); 305*ae115bc7Smrj (void) cf_read(filep, seek_scrbuf, readsz); 306*ae115bc7Smrj addr -= readsz; 307*ae115bc7Smrj } 308*ae115bc7Smrj } 309