xref: /onnv-gate/usr/src/common/fs/decompress.c (revision 7563:84ec90ffc3f7)
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