13446Smrj /*
23446Smrj * CDDL HEADER START
33446Smrj *
43446Smrj * The contents of this file are subject to the terms of the
53446Smrj * Common Development and Distribution License (the "License").
63446Smrj * You may not use this file except in compliance with the License.
73446Smrj *
83446Smrj * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93446Smrj * or http://www.opensolaris.org/os/licensing.
103446Smrj * See the License for the specific language governing permissions
113446Smrj * and limitations under the License.
123446Smrj *
133446Smrj * When distributing Covered Code, include this CDDL HEADER in each
143446Smrj * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153446Smrj * If applicable, add the following below this CDDL HEADER, with the
163446Smrj * fields enclosed by brackets "[]" replaced with your own identifying
173446Smrj * information: Portions Copyright [yyyy] [name of copyright owner]
183446Smrj *
193446Smrj * CDDL HEADER END
203446Smrj */
21*7563SPrasad.Singamsetty@Sun.COM
223446Smrj /*
23*7563SPrasad.Singamsetty@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
243446Smrj * Use is subject to license terms.
253446Smrj */
263446Smrj
273446Smrj /*
283446Smrj * Decompression module for stand alone file systems.
293446Smrj */
303446Smrj
313446Smrj #include <sys/param.h>
323446Smrj #include <sys/sysmacros.h>
333446Smrj #include <sys/vnode.h>
343446Smrj #include <sys/bootvfs.h>
353446Smrj #include <sys/filep.h>
363446Smrj #include <zmod/zlib.h>
373446Smrj
383446Smrj #ifdef _BOOT
393446Smrj #include "../common/util.h"
403446Smrj #else
413446Smrj #include <sys/sunddi.h>
423446Smrj #endif
433446Smrj
443446Smrj #define MAX_DECOMP_BUFS 8
453446Smrj #define GZIP_ID_BYTE_1 0x1f
463446Smrj #define GZIP_ID_BYTE_2 0x8b
473446Smrj #define GZIP_CM_DEFLATE 0x08
483446Smrj #define SEEKBUFSIZE 8192
493446Smrj
50*7563SPrasad.Singamsetty@Sun.COM extern void prom_printf(const char *fmt, ...);
51*7563SPrasad.Singamsetty@Sun.COM
523446Smrj #ifdef _BOOT
53*7563SPrasad.Singamsetty@Sun.COM #define dprintf if (cf_debug) prom_printf
543446Smrj #else
55*7563SPrasad.Singamsetty@Sun.COM #define dprintf if (cf_debug) prom_printf
563446Smrj
573446Smrj #endif
583446Smrj
593446Smrj extern int bootrd_debug;
603446Smrj extern void *bkmem_alloc(size_t);
613446Smrj extern void bkmem_free(void *, size_t);
623446Smrj
633446Smrj caddr_t scratch_bufs[MAX_DECOMP_BUFS]; /* array of free scratch mem bufs */
643446Smrj int decomp_bufcnt; /* total no, of allocated decomp bufs */
653446Smrj int free_dcomp_bufs; /* no. of free decomp bufs */
663446Smrj char seek_scrbuf[SEEKBUFSIZE]; /* buffer for seeking */
67*7563SPrasad.Singamsetty@Sun.COM int cf_debug = 0; /* non-zero enables debug prints */
683446Smrj
693446Smrj void *
cf_alloc(void * opaque,unsigned int items,unsigned int size)703446Smrj cf_alloc(void *opaque, unsigned int items, unsigned int size)
713446Smrj {
723446Smrj fileid_t *filep;
733446Smrj unsigned int nbytes;
743446Smrj caddr_t ptr;
753446Smrj
763446Smrj filep = (fileid_t *)opaque;
773446Smrj nbytes = roundup(items * size, sizeof (long));
783446Smrj if (nbytes > (DECOMP_BUFSIZE - filep->fi_dcscrused)) {
793446Smrj ptr = bkmem_alloc(nbytes);
803446Smrj } else {
813446Smrj ptr = &filep->fi_dcscrbuf[filep->fi_dcscrused];
823446Smrj filep->fi_dcscrused += nbytes;
833446Smrj }
843446Smrj bzero(ptr, nbytes);
853446Smrj return (ptr);
863446Smrj }
873446Smrj
883446Smrj /*
893446Smrj * Decompression scratch memory free routine, does nothing since we free
903446Smrj * the entire scratch area all at once on file close.
913446Smrj */
923446Smrj /* ARGSUSED */
933446Smrj void
cf_free(void * opaque,void * addr)943446Smrj cf_free(void *opaque, void *addr)
953446Smrj {
963446Smrj }
973446Smrj
983446Smrj /*
993446Smrj * Read the first block of the file described by filep and determine if
1003446Smrj * the file is gzip-compressed. If so, the compressed flag will be set
1013446Smrj * in the fileid_t struct pointed to by filep and it will be initialized
1023446Smrj * for doing decompression on reads to the file.
1033446Smrj */
1043446Smrj int
cf_check_compressed(fileid_t * filep)1053446Smrj cf_check_compressed(fileid_t *filep)
1063446Smrj {
1073446Smrj unsigned char *filebytes;
1083446Smrj z_stream *zsp;
1093446Smrj
1103603Ssmaybe /*
111*7563SPrasad.Singamsetty@Sun.COM * checking for a dcfs compressed file first would involve:
112*7563SPrasad.Singamsetty@Sun.COM *
113*7563SPrasad.Singamsetty@Sun.COM * if (filep->fi_inode->i_cflags & ICOMPRESS)
114*7563SPrasad.Singamsetty@Sun.COM * filep->fi_flags |= FI_COMPRESSED;
115*7563SPrasad.Singamsetty@Sun.COM */
116*7563SPrasad.Singamsetty@Sun.COM
117*7563SPrasad.Singamsetty@Sun.COM /*
118*7563SPrasad.Singamsetty@Sun.COM * If the file is not long enough to check for a
119*7563SPrasad.Singamsetty@Sun.COM * decompression header then return not compressed.
1203603Ssmaybe */
1213603Ssmaybe if (filep->fi_inode->i_size < 3)
1223603Ssmaybe return (0);
1233446Smrj filep->fi_offset = 0;
1243446Smrj if ((filep->fi_getblock)(filep) == -1)
1253446Smrj return (-1);
1263446Smrj filep->fi_offset = 0;
1273446Smrj filep->fi_count = 0;
1283446Smrj filep->fi_cfoff = 0;
1293446Smrj filebytes = (unsigned char *)filep->fi_memp;
130*7563SPrasad.Singamsetty@Sun.COM if (filebytes[0] != GZIP_ID_BYTE_1 ||
131*7563SPrasad.Singamsetty@Sun.COM filebytes[1] != GZIP_ID_BYTE_2 ||
1323446Smrj filebytes[2] != GZIP_CM_DEFLATE)
1333446Smrj return (0); /* not compressed */
1343446Smrj filep->fi_flags |= FI_COMPRESSED;
1353446Smrj
1363446Smrj dprintf("file %s is compressed\n", filep->fi_path);
1373446Smrj
1383446Smrj /*
1393446Smrj * Allocate decompress scratch buffer
1403446Smrj */
1413446Smrj if (free_dcomp_bufs) {
1423446Smrj filep->fi_dcscrbuf = scratch_bufs[--free_dcomp_bufs];
1433446Smrj } else {
1443446Smrj filep->fi_dcscrbuf = bkmem_alloc(DECOMP_BUFSIZE);
1453446Smrj decomp_bufcnt++;
1463446Smrj }
1473446Smrj filep->fi_dcscrused = 0;
1483446Smrj zsp = bkmem_alloc(sizeof (*zsp));
1493446Smrj filep->fi_dcstream = zsp;
1503446Smrj /*
1513886Sahl * Initialize the decompression stream. Adding 16 to the window size
1523886Sahl * indicates that zlib should expect a gzip header.
1533446Smrj */
1543446Smrj bzero(zsp, sizeof (*zsp));
1553446Smrj zsp->opaque = filep;
1563446Smrj zsp->zalloc = cf_alloc;
1573446Smrj zsp->zfree = cf_free;
1583446Smrj zsp->avail_in = 0;
1593446Smrj zsp->next_in = NULL;
1603446Smrj zsp->avail_out = 0;
1613446Smrj zsp->next_out = NULL;
162*7563SPrasad.Singamsetty@Sun.COM if (inflateInit2(zsp, MAX_WBITS | 0x20) != Z_OK) {
1633886Sahl dprintf("inflateInit2() failed\n");
1643446Smrj return (-1);
1653886Sahl }
1663446Smrj return (0);
1673446Smrj }
1683446Smrj
1693446Smrj /*
1703446Smrj * If the file described by fileid_t struct at *filep is compressed
1713446Smrj * free any resources associated with the decompression. (decompression
1723446Smrj * buffer, etc.).
1733446Smrj */
1743446Smrj void
cf_close(fileid_t * filep)1753446Smrj cf_close(fileid_t *filep)
1763446Smrj {
1773446Smrj if ((filep->fi_flags & FI_COMPRESSED) == 0)
1783446Smrj return;
1793446Smrj dprintf("cf_close: %s\n", filep->fi_path);
1803446Smrj (void) inflateEnd(filep->fi_dcstream);
1813446Smrj bkmem_free(filep->fi_dcstream, sizeof (z_stream));
1823446Smrj if (free_dcomp_bufs == MAX_DECOMP_BUFS) {
1833446Smrj bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE);
1843446Smrj } else {
1853446Smrj scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf;
1863446Smrj }
1873446Smrj }
1883446Smrj
1893446Smrj void
cf_rewind(fileid_t * filep)1903446Smrj cf_rewind(fileid_t *filep)
1913446Smrj {
1923446Smrj z_stream *zsp;
1933446Smrj
1943446Smrj dprintf("cf_rewind: %s\n", filep->fi_path);
1953446Smrj zsp = filep->fi_dcstream;
1963446Smrj zsp->avail_in = 0;
1973446Smrj zsp->next_in = NULL;
1983446Smrj (void) inflateReset(zsp);
1993446Smrj filep->fi_cfoff = 0;
2003446Smrj }
2013446Smrj
2023446Smrj #define FLG_FHCRC 0x02 /* crc field present */
2033446Smrj #define FLG_FEXTRA 0x04 /* "extra" field present */
2043446Smrj #define FLG_FNAME 0x08 /* file name field present */
2053446Smrj #define FLG_FCOMMENT 0x10 /* comment field present */
2063446Smrj
2073446Smrj /*
2083446Smrj * Read at the current uncompressed offset from the compressed file described
2093446Smrj * by *filep. Will return decompressed data.
2103446Smrj */
2113446Smrj int
cf_read(fileid_t * filep,caddr_t buf,size_t count)2123446Smrj cf_read(fileid_t *filep, caddr_t buf, size_t count)
2133446Smrj {
2143446Smrj z_stream *zsp;
2153446Smrj struct inode *ip;
2163446Smrj int err = Z_OK;
2173886Sahl int infbytes;
2183446Smrj off_t soff;
2193446Smrj caddr_t smemp;
2203446Smrj
2213446Smrj dprintf("cf_read: %s ", filep->fi_path);
2223446Smrj dprintf("%lx bytes\n", count);
2233446Smrj zsp = filep->fi_dcstream;
2243446Smrj ip = filep->fi_inode;
2253446Smrj dprintf(" reading at offset %lx\n", zsp->total_out);
2263446Smrj zsp->next_out = (unsigned char *)buf;
2273446Smrj zsp->avail_out = count;
2283446Smrj while (zsp->avail_out != 0) {
2293446Smrj if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) {
2303446Smrj /*
2313446Smrj * read a block of the file to inflate
2323446Smrj */
2333446Smrj soff = filep->fi_offset;
2343446Smrj smemp = filep->fi_memp;
2353446Smrj filep->fi_memp = NULL;
2363446Smrj filep->fi_offset = filep->fi_cfoff;
2373446Smrj filep->fi_count = 0;
2383446Smrj if ((*filep->fi_getblock)(filep) == -1)
2393446Smrj return (-1);
2403446Smrj filep->fi_offset = soff;
2413446Smrj zsp->next_in = (unsigned char *)filep->fi_memp;
2423446Smrj zsp->avail_in = filep->fi_count;
2433446Smrj filep->fi_memp = smemp;
2443446Smrj filep->fi_cfoff += filep->fi_count;
2453446Smrj }
2463446Smrj infbytes = zsp->avail_out;
2473446Smrj dprintf("attempting inflate of %x bytes to buf at: %lx\n",
2483446Smrj zsp->avail_out, (unsigned long)zsp->next_out);
2493446Smrj err = inflate(zsp, Z_NO_FLUSH);
2503446Smrj infbytes -= zsp->avail_out;
2513446Smrj dprintf("inflated %x bytes, errcode=%d\n", infbytes, err);
2523446Smrj /*
2533446Smrj * break out if we hit end of the compressed file
2543446Smrj * or the end of the compressed byte stream
2553446Smrj */
2563446Smrj if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END)
2573446Smrj break;
2583446Smrj }
2593446Smrj dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out);
2603446Smrj return (count - zsp->avail_out);
2613446Smrj }
2623446Smrj
2633446Smrj /*
2643446Smrj * Seek to the location specified by addr
2653446Smrj */
2663446Smrj void
cf_seek(fileid_t * filep,off_t addr,int whence)2673446Smrj cf_seek(fileid_t *filep, off_t addr, int whence)
2683446Smrj {
2693446Smrj z_stream *zsp;
2703446Smrj int readsz;
2713446Smrj
2723446Smrj dprintf("cf_seek: %s ", filep->fi_path);
2733446Smrj dprintf("to %lx\n", addr);
2743446Smrj zsp = filep->fi_dcstream;
2753446Smrj if (whence == SEEK_CUR)
2763446Smrj addr += zsp->total_out;
2773446Smrj /*
2783446Smrj * To seek backwards, must rewind and seek forwards
2793446Smrj */
2803446Smrj if (addr < zsp->total_out) {
2813446Smrj cf_rewind(filep);
2823446Smrj filep->fi_offset = 0;
2833446Smrj } else {
2843446Smrj addr -= zsp->total_out;
2853446Smrj }
2863446Smrj while (addr > 0) {
2873446Smrj readsz = MIN(addr, SEEKBUFSIZE);
2883446Smrj (void) cf_read(filep, seek_scrbuf, readsz);
2893446Smrj addr -= readsz;
2903446Smrj }
2913446Smrj }
292