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