xref: /titanic_52/usr/src/common/fs/decompress.c (revision 9e6e15042fe8eed186bfa123fa6d79d7728f0ade)
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 
109*9e6e1504Ssmaybe 	/*
110*9e6e1504Ssmaybe 	 * If the file is not long enough to check for a decompression header
111*9e6e1504Ssmaybe 	 * then return not compressed.
112*9e6e1504Ssmaybe 	 */
113*9e6e1504Ssmaybe 	if (filep->fi_inode->i_size < 3)
114*9e6e1504Ssmaybe 		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 	/*
142ae115bc7Smrj 	 * Initialize the decompression stream
143ae115bc7Smrj 	 */
144ae115bc7Smrj 	bzero(zsp, sizeof (*zsp));
145ae115bc7Smrj 	zsp->opaque = filep;
146ae115bc7Smrj 	zsp->zalloc = cf_alloc;
147ae115bc7Smrj 	zsp->zfree = cf_free;
148ae115bc7Smrj 	zsp->avail_in = 0;
149ae115bc7Smrj 	zsp->next_in = NULL;
150ae115bc7Smrj 	zsp->avail_out = 0;
151ae115bc7Smrj 	zsp->next_out = NULL;
152ae115bc7Smrj 	if (inflateInit2(zsp, -MAX_WBITS) != Z_OK)
153ae115bc7Smrj 		return (-1);
154ae115bc7Smrj 	return (0);
155ae115bc7Smrj }
156ae115bc7Smrj 
157ae115bc7Smrj /*
158ae115bc7Smrj  * If the file described by fileid_t struct at *filep is compressed
159ae115bc7Smrj  * free any resources associated with the decompression.  (decompression
160ae115bc7Smrj  * buffer, etc.).
161ae115bc7Smrj  */
162ae115bc7Smrj void
163ae115bc7Smrj cf_close(fileid_t *filep)
164ae115bc7Smrj {
165ae115bc7Smrj 	if ((filep->fi_flags & FI_COMPRESSED) == 0)
166ae115bc7Smrj 		return;
167ae115bc7Smrj 	dprintf("cf_close: %s\n", filep->fi_path);
168ae115bc7Smrj 	(void) inflateEnd(filep->fi_dcstream);
169ae115bc7Smrj 	bkmem_free(filep->fi_dcstream, sizeof (z_stream));
170ae115bc7Smrj 	if (free_dcomp_bufs == MAX_DECOMP_BUFS) {
171ae115bc7Smrj 		bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE);
172ae115bc7Smrj 	} else {
173ae115bc7Smrj 		scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf;
174ae115bc7Smrj 	}
175ae115bc7Smrj }
176ae115bc7Smrj 
177ae115bc7Smrj void
178ae115bc7Smrj cf_rewind(fileid_t *filep)
179ae115bc7Smrj {
180ae115bc7Smrj 	z_stream *zsp;
181ae115bc7Smrj 
182ae115bc7Smrj 	dprintf("cf_rewind: %s\n", filep->fi_path);
183ae115bc7Smrj 	zsp = filep->fi_dcstream;
184ae115bc7Smrj 	zsp->avail_in = 0;
185ae115bc7Smrj 	zsp->next_in = NULL;
186ae115bc7Smrj 	(void) inflateReset(zsp);
187ae115bc7Smrj 	filep->fi_cfoff = 0;
188ae115bc7Smrj }
189ae115bc7Smrj 
190ae115bc7Smrj #define	FLG_FHCRC	0x02	/* crc field present */
191ae115bc7Smrj #define	FLG_FEXTRA	0x04	/* "extra" field present */
192ae115bc7Smrj #define	FLG_FNAME	0x08	/* file name field present */
193ae115bc7Smrj #define	FLG_FCOMMENT	0x10	/* comment field present */
194ae115bc7Smrj 
195ae115bc7Smrj /*
196ae115bc7Smrj  * Calculate the header length of a gzip compressed file
197ae115bc7Smrj  */
198ae115bc7Smrj static int
199ae115bc7Smrj cf_headerlen(unsigned char *hp)
200ae115bc7Smrj {
201ae115bc7Smrj 	unsigned char flag;
202ae115bc7Smrj 	int xlen, hlen = 0;
203ae115bc7Smrj 
204ae115bc7Smrj 	hlen += 3;	/* skip ID bytes and compression method */
205ae115bc7Smrj 	flag = hp[hlen];
206ae115bc7Smrj 	hlen += 7;	/* skip flag, mtime(4 bytes), xfl, and os */
207ae115bc7Smrj 	if (flag & FLG_FEXTRA) {
208ae115bc7Smrj 		xlen = hp[hlen++];
209ae115bc7Smrj 		xlen += hp[hlen++] << 8;
210ae115bc7Smrj 		hlen += xlen;	/* skip extra field */
211ae115bc7Smrj 	}
212ae115bc7Smrj 	if (flag & FLG_FNAME)
213ae115bc7Smrj 		hlen += strlen((char *)&hp[hlen]) + 1;	/* skip file name */
214ae115bc7Smrj 	if (flag & FLG_FCOMMENT)
215ae115bc7Smrj 		hlen += strlen((char *)&hp[hlen]) + 1;	/* skip comment */
216ae115bc7Smrj 	if (flag & FLG_FHCRC)
217ae115bc7Smrj 		hlen += 2;	/* skip crc */
218ae115bc7Smrj 	return (hlen);
219ae115bc7Smrj }
220ae115bc7Smrj 
221ae115bc7Smrj /*
222ae115bc7Smrj  * Read at the current uncompressed offset from the compressed file described
223ae115bc7Smrj  * by *filep.  Will return decompressed data.
224ae115bc7Smrj  */
225ae115bc7Smrj int
226ae115bc7Smrj cf_read(fileid_t *filep, caddr_t buf, size_t count)
227ae115bc7Smrj {
228ae115bc7Smrj 	z_stream *zsp;
229ae115bc7Smrj 	struct inode *ip;
230ae115bc7Smrj 	int err = Z_OK;
231ae115bc7Smrj 	int hlen, infbytes;
232ae115bc7Smrj 	off_t soff;
233ae115bc7Smrj 	caddr_t smemp;
234ae115bc7Smrj 
235ae115bc7Smrj 	dprintf("cf_read: %s ", filep->fi_path);
236ae115bc7Smrj 	dprintf("%lx bytes\n", count);
237ae115bc7Smrj 	zsp = filep->fi_dcstream;
238ae115bc7Smrj 	ip = filep->fi_inode;
239ae115bc7Smrj 	dprintf("   reading at offset %lx\n", zsp->total_out);
240ae115bc7Smrj 	zsp->next_out = (unsigned char *)buf;
241ae115bc7Smrj 	zsp->avail_out = count;
242ae115bc7Smrj 	while (zsp->avail_out != 0) {
243ae115bc7Smrj 		if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) {
244ae115bc7Smrj 			/*
245ae115bc7Smrj 			 * read a block of the file to inflate
246ae115bc7Smrj 			 */
247ae115bc7Smrj 			soff = filep->fi_offset;
248ae115bc7Smrj 			smemp = filep->fi_memp;
249ae115bc7Smrj 			filep->fi_memp = NULL;
250ae115bc7Smrj 			filep->fi_offset = filep->fi_cfoff;
251ae115bc7Smrj 			filep->fi_count = 0;
252ae115bc7Smrj 			if ((*filep->fi_getblock)(filep) == -1)
253ae115bc7Smrj 				return (-1);
254ae115bc7Smrj 			filep->fi_offset = soff;
255ae115bc7Smrj 			zsp->next_in = (unsigned char *)filep->fi_memp;
256ae115bc7Smrj 			zsp->avail_in = filep->fi_count;
257ae115bc7Smrj 			filep->fi_memp = smemp;
258ae115bc7Smrj 			/*
259ae115bc7Smrj 			 * If we are reading the first block of the file, we
260ae115bc7Smrj 			 * need to skip over the header bytes before inflating
261ae115bc7Smrj 			 */
262ae115bc7Smrj 			if (filep->fi_cfoff == 0) {
263ae115bc7Smrj 				hlen = cf_headerlen(zsp->next_in);
264ae115bc7Smrj 				zsp->next_in += hlen;
265ae115bc7Smrj 				zsp->avail_in -= hlen;
266ae115bc7Smrj 			}
267ae115bc7Smrj 			filep->fi_cfoff += filep->fi_count;
268ae115bc7Smrj 		}
269ae115bc7Smrj 		infbytes = zsp->avail_out;
270ae115bc7Smrj 		dprintf("attempting inflate of %x bytes to buf at: %lx\n",
271ae115bc7Smrj 		    zsp->avail_out, (unsigned long)zsp->next_out);
272ae115bc7Smrj 		err = inflate(zsp, Z_NO_FLUSH);
273ae115bc7Smrj 		infbytes -= zsp->avail_out;
274ae115bc7Smrj 		dprintf("inflated %x bytes, errcode=%d\n", infbytes, err);
275ae115bc7Smrj 		/*
276ae115bc7Smrj 		 * break out if we hit end of the compressed file
277ae115bc7Smrj 		 * or the end of the compressed byte stream
278ae115bc7Smrj 		 */
279ae115bc7Smrj 		if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END)
280ae115bc7Smrj 			break;
281ae115bc7Smrj 	}
282ae115bc7Smrj 	dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out);
283ae115bc7Smrj 	return (count - zsp->avail_out);
284ae115bc7Smrj }
285ae115bc7Smrj 
286ae115bc7Smrj /*
287ae115bc7Smrj  * Seek to the location specified by addr
288ae115bc7Smrj  */
289ae115bc7Smrj void
290ae115bc7Smrj cf_seek(fileid_t *filep, off_t addr, int whence)
291ae115bc7Smrj {
292ae115bc7Smrj 	z_stream *zsp;
293ae115bc7Smrj 	int readsz;
294ae115bc7Smrj 
295ae115bc7Smrj 	dprintf("cf_seek: %s ", filep->fi_path);
296ae115bc7Smrj 	dprintf("to %lx\n", addr);
297ae115bc7Smrj 	zsp = filep->fi_dcstream;
298ae115bc7Smrj 	if (whence == SEEK_CUR)
299ae115bc7Smrj 		addr += zsp->total_out;
300ae115bc7Smrj 	/*
301ae115bc7Smrj 	 * To seek backwards, must rewind and seek forwards
302ae115bc7Smrj 	 */
303ae115bc7Smrj 	if (addr < zsp->total_out) {
304ae115bc7Smrj 		cf_rewind(filep);
305ae115bc7Smrj 		filep->fi_offset = 0;
306ae115bc7Smrj 	} else {
307ae115bc7Smrj 		addr -= zsp->total_out;
308ae115bc7Smrj 	}
309ae115bc7Smrj 	while (addr > 0) {
310ae115bc7Smrj 		readsz = MIN(addr, SEEKBUFSIZE);
311ae115bc7Smrj 		(void) cf_read(filep, seek_scrbuf, readsz);
312ae115bc7Smrj 		addr -= readsz;
313ae115bc7Smrj 	}
314ae115bc7Smrj }
315