/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Decompression module for stand alone file systems. */ #include #include #include #include #include #include #ifdef _BOOT #include "../common/util.h" #else #include #endif #define MAX_DECOMP_BUFS 8 #define GZIP_ID_BYTE_1 0x1f #define GZIP_ID_BYTE_2 0x8b #define GZIP_CM_DEFLATE 0x08 #define SEEKBUFSIZE 8192 extern void prom_printf(const char *fmt, ...); #ifdef _BOOT #define dprintf if (cf_debug) prom_printf #else #define dprintf if (cf_debug) prom_printf #endif extern int bootrd_debug; extern void *bkmem_alloc(size_t); extern void bkmem_free(void *, size_t); caddr_t scratch_bufs[MAX_DECOMP_BUFS]; /* array of free scratch mem bufs */ int decomp_bufcnt; /* total no, of allocated decomp bufs */ int free_dcomp_bufs; /* no. of free decomp bufs */ char seek_scrbuf[SEEKBUFSIZE]; /* buffer for seeking */ int cf_debug = 0; /* non-zero enables debug prints */ void * cf_alloc(void *opaque, unsigned int items, unsigned int size) { fileid_t *filep; unsigned int nbytes; caddr_t ptr; filep = (fileid_t *)opaque; nbytes = roundup(items * size, sizeof (long)); if (nbytes > (DECOMP_BUFSIZE - filep->fi_dcscrused)) { ptr = bkmem_alloc(nbytes); } else { ptr = &filep->fi_dcscrbuf[filep->fi_dcscrused]; filep->fi_dcscrused += nbytes; } bzero(ptr, nbytes); return (ptr); } /* * Decompression scratch memory free routine, does nothing since we free * the entire scratch area all at once on file close. */ /* ARGSUSED */ void cf_free(void *opaque, void *addr) { } /* * Read the first block of the file described by filep and determine if * the file is gzip-compressed. If so, the compressed flag will be set * in the fileid_t struct pointed to by filep and it will be initialized * for doing decompression on reads to the file. */ int cf_check_compressed(fileid_t *filep) { unsigned char *filebytes; z_stream *zsp; /* * checking for a dcfs compressed file first would involve: * * if (filep->fi_inode->i_cflags & ICOMPRESS) * filep->fi_flags |= FI_COMPRESSED; */ /* * If the file is not long enough to check for a * decompression header then return not compressed. */ if (filep->fi_inode->i_size < 3) return (0); filep->fi_offset = 0; if ((filep->fi_getblock)(filep) == -1) return (-1); filep->fi_offset = 0; filep->fi_count = 0; filep->fi_cfoff = 0; filebytes = (unsigned char *)filep->fi_memp; if (filebytes[0] != GZIP_ID_BYTE_1 || filebytes[1] != GZIP_ID_BYTE_2 || filebytes[2] != GZIP_CM_DEFLATE) return (0); /* not compressed */ filep->fi_flags |= FI_COMPRESSED; dprintf("file %s is compressed\n", filep->fi_path); /* * Allocate decompress scratch buffer */ if (free_dcomp_bufs) { filep->fi_dcscrbuf = scratch_bufs[--free_dcomp_bufs]; } else { filep->fi_dcscrbuf = bkmem_alloc(DECOMP_BUFSIZE); decomp_bufcnt++; } filep->fi_dcscrused = 0; zsp = bkmem_alloc(sizeof (*zsp)); filep->fi_dcstream = zsp; /* * Initialize the decompression stream. Adding 16 to the window size * indicates that zlib should expect a gzip header. */ bzero(zsp, sizeof (*zsp)); zsp->opaque = filep; zsp->zalloc = cf_alloc; zsp->zfree = cf_free; zsp->avail_in = 0; zsp->next_in = NULL; zsp->avail_out = 0; zsp->next_out = NULL; if (inflateInit2(zsp, MAX_WBITS | 0x20) != Z_OK) { dprintf("inflateInit2() failed\n"); return (-1); } return (0); } /* * If the file described by fileid_t struct at *filep is compressed * free any resources associated with the decompression. (decompression * buffer, etc.). */ void cf_close(fileid_t *filep) { if ((filep->fi_flags & FI_COMPRESSED) == 0) return; dprintf("cf_close: %s\n", filep->fi_path); (void) inflateEnd(filep->fi_dcstream); bkmem_free(filep->fi_dcstream, sizeof (z_stream)); if (free_dcomp_bufs == MAX_DECOMP_BUFS) { bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE); } else { scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf; } } void cf_rewind(fileid_t *filep) { z_stream *zsp; dprintf("cf_rewind: %s\n", filep->fi_path); zsp = filep->fi_dcstream; zsp->avail_in = 0; zsp->next_in = NULL; (void) inflateReset(zsp); filep->fi_cfoff = 0; } #define FLG_FHCRC 0x02 /* crc field present */ #define FLG_FEXTRA 0x04 /* "extra" field present */ #define FLG_FNAME 0x08 /* file name field present */ #define FLG_FCOMMENT 0x10 /* comment field present */ /* * Read at the current uncompressed offset from the compressed file described * by *filep. Will return decompressed data. */ int cf_read(fileid_t *filep, caddr_t buf, size_t count) { z_stream *zsp; struct inode *ip; int err = Z_OK; int infbytes; off_t soff; caddr_t smemp; dprintf("cf_read: %s ", filep->fi_path); dprintf("%lx bytes\n", count); zsp = filep->fi_dcstream; ip = filep->fi_inode; dprintf(" reading at offset %lx\n", zsp->total_out); zsp->next_out = (unsigned char *)buf; zsp->avail_out = count; while (zsp->avail_out != 0) { if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) { /* * read a block of the file to inflate */ soff = filep->fi_offset; smemp = filep->fi_memp; filep->fi_memp = NULL; filep->fi_offset = filep->fi_cfoff; filep->fi_count = 0; if ((*filep->fi_getblock)(filep) == -1) return (-1); filep->fi_offset = soff; zsp->next_in = (unsigned char *)filep->fi_memp; zsp->avail_in = filep->fi_count; filep->fi_memp = smemp; filep->fi_cfoff += filep->fi_count; } infbytes = zsp->avail_out; dprintf("attempting inflate of %x bytes to buf at: %lx\n", zsp->avail_out, (unsigned long)zsp->next_out); err = inflate(zsp, Z_NO_FLUSH); infbytes -= zsp->avail_out; dprintf("inflated %x bytes, errcode=%d\n", infbytes, err); /* * break out if we hit end of the compressed file * or the end of the compressed byte stream */ if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END) break; } dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out); return (count - zsp->avail_out); } /* * Seek to the location specified by addr */ void cf_seek(fileid_t *filep, off_t addr, int whence) { z_stream *zsp; int readsz; dprintf("cf_seek: %s ", filep->fi_path); dprintf("to %lx\n", addr); zsp = filep->fi_dcstream; if (whence == SEEK_CUR) addr += zsp->total_out; /* * To seek backwards, must rewind and seek forwards */ if (addr < zsp->total_out) { cf_rewind(filep); filep->fi_offset = 0; } else { addr -= zsp->total_out; } while (addr > 0) { readsz = MIN(addr, SEEKBUFSIZE); (void) cf_read(filep, seek_scrbuf, readsz); addr -= readsz; } }