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