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 */ 21ae115bc7Smrj /* 22ae115bc7Smrj * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23ae115bc7Smrj * Use is subject to license terms. 24ae115bc7Smrj */ 25ae115bc7Smrj 26ae115bc7Smrj #pragma ident "%Z%%M% %I% %E% SMI" 27ae115bc7Smrj 28ae115bc7Smrj /* 29ae115bc7Smrj * Decompression module for stand alone file systems. 30ae115bc7Smrj */ 31ae115bc7Smrj 32ae115bc7Smrj #include <sys/param.h> 33ae115bc7Smrj #include <sys/sysmacros.h> 34ae115bc7Smrj #include <sys/vnode.h> 35ae115bc7Smrj #include <sys/bootvfs.h> 36ae115bc7Smrj #include <sys/filep.h> 37ae115bc7Smrj #include <zmod/zlib.h> 38ae115bc7Smrj 39ae115bc7Smrj #ifdef _BOOT 40ae115bc7Smrj #include "../common/util.h" 41ae115bc7Smrj #else 42ae115bc7Smrj #include <sys/sunddi.h> 43ae115bc7Smrj #endif 44ae115bc7Smrj 45ae115bc7Smrj #define MAX_DECOMP_BUFS 8 46ae115bc7Smrj #define GZIP_ID_BYTE_1 0x1f 47ae115bc7Smrj #define GZIP_ID_BYTE_2 0x8b 48ae115bc7Smrj #define GZIP_CM_DEFLATE 0x08 49ae115bc7Smrj #define SEEKBUFSIZE 8192 50ae115bc7Smrj 51ae115bc7Smrj #ifdef _BOOT 52ae115bc7Smrj #define dprintf if (cf_debug) printf 53ae115bc7Smrj #else 54ae115bc7Smrj #define dprintf if (cf_debug) printf 55ae115bc7Smrj 56ae115bc7Smrj #endif 57ae115bc7Smrj 58ae115bc7Smrj extern int bootrd_debug; 59ae115bc7Smrj extern void *bkmem_alloc(size_t); 60ae115bc7Smrj extern void bkmem_free(void *, size_t); 61ae115bc7Smrj 62ae115bc7Smrj caddr_t scratch_bufs[MAX_DECOMP_BUFS]; /* array of free scratch mem bufs */ 63ae115bc7Smrj int decomp_bufcnt; /* total no, of allocated decomp bufs */ 64ae115bc7Smrj int free_dcomp_bufs; /* no. of free decomp bufs */ 65ae115bc7Smrj char seek_scrbuf[SEEKBUFSIZE]; /* buffer for seeking */ 66ae115bc7Smrj int cf_debug; /* non-zero enables debug prints */ 67ae115bc7Smrj 68ae115bc7Smrj void * 69ae115bc7Smrj cf_alloc(void *opaque, unsigned int items, unsigned int size) 70ae115bc7Smrj { 71ae115bc7Smrj fileid_t *filep; 72ae115bc7Smrj unsigned int nbytes; 73ae115bc7Smrj caddr_t ptr; 74ae115bc7Smrj 75ae115bc7Smrj filep = (fileid_t *)opaque; 76ae115bc7Smrj nbytes = roundup(items * size, sizeof (long)); 77ae115bc7Smrj if (nbytes > (DECOMP_BUFSIZE - filep->fi_dcscrused)) { 78ae115bc7Smrj ptr = bkmem_alloc(nbytes); 79ae115bc7Smrj } else { 80ae115bc7Smrj ptr = &filep->fi_dcscrbuf[filep->fi_dcscrused]; 81ae115bc7Smrj filep->fi_dcscrused += nbytes; 82ae115bc7Smrj } 83ae115bc7Smrj bzero(ptr, nbytes); 84ae115bc7Smrj return (ptr); 85ae115bc7Smrj } 86ae115bc7Smrj 87ae115bc7Smrj /* 88ae115bc7Smrj * Decompression scratch memory free routine, does nothing since we free 89ae115bc7Smrj * the entire scratch area all at once on file close. 90ae115bc7Smrj */ 91ae115bc7Smrj /* ARGSUSED */ 92ae115bc7Smrj void 93ae115bc7Smrj cf_free(void *opaque, void *addr) 94ae115bc7Smrj { 95ae115bc7Smrj } 96ae115bc7Smrj 97ae115bc7Smrj /* 98ae115bc7Smrj * Read the first block of the file described by filep and determine if 99ae115bc7Smrj * the file is gzip-compressed. If so, the compressed flag will be set 100ae115bc7Smrj * in the fileid_t struct pointed to by filep and it will be initialized 101ae115bc7Smrj * for doing decompression on reads to the file. 102ae115bc7Smrj */ 103ae115bc7Smrj int 104ae115bc7Smrj cf_check_compressed(fileid_t *filep) 105ae115bc7Smrj { 106ae115bc7Smrj unsigned char *filebytes; 107ae115bc7Smrj z_stream *zsp; 108ae115bc7Smrj 1099e6e1504Ssmaybe /* 1109e6e1504Ssmaybe * If the file is not long enough to check for a decompression header 1119e6e1504Ssmaybe * then return not compressed. 1129e6e1504Ssmaybe */ 1139e6e1504Ssmaybe if (filep->fi_inode->i_size < 3) 1149e6e1504Ssmaybe return (0); 115ae115bc7Smrj filep->fi_offset = 0; 116ae115bc7Smrj if ((filep->fi_getblock)(filep) == -1) 117ae115bc7Smrj return (-1); 118ae115bc7Smrj filep->fi_offset = 0; 119ae115bc7Smrj filep->fi_count = 0; 120ae115bc7Smrj filep->fi_cfoff = 0; 121ae115bc7Smrj filebytes = (unsigned char *)filep->fi_memp; 122ae115bc7Smrj if (filebytes[0] != GZIP_ID_BYTE_1 || filebytes[1] != GZIP_ID_BYTE_2 || 123ae115bc7Smrj filebytes[2] != GZIP_CM_DEFLATE) 124ae115bc7Smrj return (0); /* not compressed */ 125ae115bc7Smrj filep->fi_flags |= FI_COMPRESSED; 126ae115bc7Smrj 127ae115bc7Smrj dprintf("file %s is compressed\n", filep->fi_path); 128ae115bc7Smrj 129ae115bc7Smrj /* 130ae115bc7Smrj * Allocate decompress scratch buffer 131ae115bc7Smrj */ 132ae115bc7Smrj if (free_dcomp_bufs) { 133ae115bc7Smrj filep->fi_dcscrbuf = scratch_bufs[--free_dcomp_bufs]; 134ae115bc7Smrj } else { 135ae115bc7Smrj filep->fi_dcscrbuf = bkmem_alloc(DECOMP_BUFSIZE); 136ae115bc7Smrj decomp_bufcnt++; 137ae115bc7Smrj } 138ae115bc7Smrj filep->fi_dcscrused = 0; 139ae115bc7Smrj zsp = bkmem_alloc(sizeof (*zsp)); 140ae115bc7Smrj filep->fi_dcstream = zsp; 141ae115bc7Smrj /* 142*c9431fa1Sahl * Initialize the decompression stream. Adding 16 to the window size 143*c9431fa1Sahl * indicates that zlib should expect a gzip header. 144ae115bc7Smrj */ 145ae115bc7Smrj bzero(zsp, sizeof (*zsp)); 146ae115bc7Smrj zsp->opaque = filep; 147ae115bc7Smrj zsp->zalloc = cf_alloc; 148ae115bc7Smrj zsp->zfree = cf_free; 149ae115bc7Smrj zsp->avail_in = 0; 150ae115bc7Smrj zsp->next_in = NULL; 151ae115bc7Smrj zsp->avail_out = 0; 152ae115bc7Smrj zsp->next_out = NULL; 153*c9431fa1Sahl if (inflateInit2(zsp, MAX_WBITS + 16) != Z_OK) { 154*c9431fa1Sahl dprintf("inflateInit2() failed\n"); 155ae115bc7Smrj return (-1); 156*c9431fa1Sahl } 157ae115bc7Smrj return (0); 158ae115bc7Smrj } 159ae115bc7Smrj 160ae115bc7Smrj /* 161ae115bc7Smrj * If the file described by fileid_t struct at *filep is compressed 162ae115bc7Smrj * free any resources associated with the decompression. (decompression 163ae115bc7Smrj * buffer, etc.). 164ae115bc7Smrj */ 165ae115bc7Smrj void 166ae115bc7Smrj cf_close(fileid_t *filep) 167ae115bc7Smrj { 168ae115bc7Smrj if ((filep->fi_flags & FI_COMPRESSED) == 0) 169ae115bc7Smrj return; 170ae115bc7Smrj dprintf("cf_close: %s\n", filep->fi_path); 171ae115bc7Smrj (void) inflateEnd(filep->fi_dcstream); 172ae115bc7Smrj bkmem_free(filep->fi_dcstream, sizeof (z_stream)); 173ae115bc7Smrj if (free_dcomp_bufs == MAX_DECOMP_BUFS) { 174ae115bc7Smrj bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE); 175ae115bc7Smrj } else { 176ae115bc7Smrj scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf; 177ae115bc7Smrj } 178ae115bc7Smrj } 179ae115bc7Smrj 180ae115bc7Smrj void 181ae115bc7Smrj cf_rewind(fileid_t *filep) 182ae115bc7Smrj { 183ae115bc7Smrj z_stream *zsp; 184ae115bc7Smrj 185ae115bc7Smrj dprintf("cf_rewind: %s\n", filep->fi_path); 186ae115bc7Smrj zsp = filep->fi_dcstream; 187ae115bc7Smrj zsp->avail_in = 0; 188ae115bc7Smrj zsp->next_in = NULL; 189ae115bc7Smrj (void) inflateReset(zsp); 190ae115bc7Smrj filep->fi_cfoff = 0; 191ae115bc7Smrj } 192ae115bc7Smrj 193ae115bc7Smrj #define FLG_FHCRC 0x02 /* crc field present */ 194ae115bc7Smrj #define FLG_FEXTRA 0x04 /* "extra" field present */ 195ae115bc7Smrj #define FLG_FNAME 0x08 /* file name field present */ 196ae115bc7Smrj #define FLG_FCOMMENT 0x10 /* comment field present */ 197ae115bc7Smrj 198ae115bc7Smrj /* 199ae115bc7Smrj * Read at the current uncompressed offset from the compressed file described 200ae115bc7Smrj * by *filep. Will return decompressed data. 201ae115bc7Smrj */ 202ae115bc7Smrj int 203ae115bc7Smrj cf_read(fileid_t *filep, caddr_t buf, size_t count) 204ae115bc7Smrj { 205ae115bc7Smrj z_stream *zsp; 206ae115bc7Smrj struct inode *ip; 207ae115bc7Smrj int err = Z_OK; 208*c9431fa1Sahl int infbytes; 209ae115bc7Smrj off_t soff; 210ae115bc7Smrj caddr_t smemp; 211ae115bc7Smrj 212ae115bc7Smrj dprintf("cf_read: %s ", filep->fi_path); 213ae115bc7Smrj dprintf("%lx bytes\n", count); 214ae115bc7Smrj zsp = filep->fi_dcstream; 215ae115bc7Smrj ip = filep->fi_inode; 216ae115bc7Smrj dprintf(" reading at offset %lx\n", zsp->total_out); 217ae115bc7Smrj zsp->next_out = (unsigned char *)buf; 218ae115bc7Smrj zsp->avail_out = count; 219ae115bc7Smrj while (zsp->avail_out != 0) { 220ae115bc7Smrj if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) { 221ae115bc7Smrj /* 222ae115bc7Smrj * read a block of the file to inflate 223ae115bc7Smrj */ 224ae115bc7Smrj soff = filep->fi_offset; 225ae115bc7Smrj smemp = filep->fi_memp; 226ae115bc7Smrj filep->fi_memp = NULL; 227ae115bc7Smrj filep->fi_offset = filep->fi_cfoff; 228ae115bc7Smrj filep->fi_count = 0; 229ae115bc7Smrj if ((*filep->fi_getblock)(filep) == -1) 230ae115bc7Smrj return (-1); 231ae115bc7Smrj filep->fi_offset = soff; 232ae115bc7Smrj zsp->next_in = (unsigned char *)filep->fi_memp; 233ae115bc7Smrj zsp->avail_in = filep->fi_count; 234ae115bc7Smrj filep->fi_memp = smemp; 235ae115bc7Smrj filep->fi_cfoff += filep->fi_count; 236ae115bc7Smrj } 237ae115bc7Smrj infbytes = zsp->avail_out; 238ae115bc7Smrj dprintf("attempting inflate of %x bytes to buf at: %lx\n", 239ae115bc7Smrj zsp->avail_out, (unsigned long)zsp->next_out); 240ae115bc7Smrj err = inflate(zsp, Z_NO_FLUSH); 241ae115bc7Smrj infbytes -= zsp->avail_out; 242ae115bc7Smrj dprintf("inflated %x bytes, errcode=%d\n", infbytes, err); 243ae115bc7Smrj /* 244ae115bc7Smrj * break out if we hit end of the compressed file 245ae115bc7Smrj * or the end of the compressed byte stream 246ae115bc7Smrj */ 247ae115bc7Smrj if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END) 248ae115bc7Smrj break; 249ae115bc7Smrj } 250ae115bc7Smrj dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out); 251ae115bc7Smrj return (count - zsp->avail_out); 252ae115bc7Smrj } 253ae115bc7Smrj 254ae115bc7Smrj /* 255ae115bc7Smrj * Seek to the location specified by addr 256ae115bc7Smrj */ 257ae115bc7Smrj void 258ae115bc7Smrj cf_seek(fileid_t *filep, off_t addr, int whence) 259ae115bc7Smrj { 260ae115bc7Smrj z_stream *zsp; 261ae115bc7Smrj int readsz; 262ae115bc7Smrj 263ae115bc7Smrj dprintf("cf_seek: %s ", filep->fi_path); 264ae115bc7Smrj dprintf("to %lx\n", addr); 265ae115bc7Smrj zsp = filep->fi_dcstream; 266ae115bc7Smrj if (whence == SEEK_CUR) 267ae115bc7Smrj addr += zsp->total_out; 268ae115bc7Smrj /* 269ae115bc7Smrj * To seek backwards, must rewind and seek forwards 270ae115bc7Smrj */ 271ae115bc7Smrj if (addr < zsp->total_out) { 272ae115bc7Smrj cf_rewind(filep); 273ae115bc7Smrj filep->fi_offset = 0; 274ae115bc7Smrj } else { 275ae115bc7Smrj addr -= zsp->total_out; 276ae115bc7Smrj } 277ae115bc7Smrj while (addr > 0) { 278ae115bc7Smrj readsz = MIN(addr, SEEKBUFSIZE); 279ae115bc7Smrj (void) cf_read(filep, seek_scrbuf, readsz); 280ae115bc7Smrj addr -= readsz; 281ae115bc7Smrj } 282ae115bc7Smrj } 283