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