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 */
21*342440ecSPrasad Singamsetty
22ae115bc7Smrj /*
23*342440ecSPrasad Singamsetty * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24ae115bc7Smrj * Use is subject to license terms.
25ae115bc7Smrj */
26ae115bc7Smrj
27ae115bc7Smrj /*
28ae115bc7Smrj * Decompression module for stand alone file systems.
29ae115bc7Smrj */
30ae115bc7Smrj
31ae115bc7Smrj #include <sys/param.h>
32ae115bc7Smrj #include <sys/sysmacros.h>
33ae115bc7Smrj #include <sys/vnode.h>
34ae115bc7Smrj #include <sys/bootvfs.h>
35ae115bc7Smrj #include <sys/filep.h>
36ae115bc7Smrj #include <zmod/zlib.h>
37ae115bc7Smrj
38ae115bc7Smrj #ifdef _BOOT
39ae115bc7Smrj #include "../common/util.h"
40ae115bc7Smrj #else
41ae115bc7Smrj #include <sys/sunddi.h>
42ae115bc7Smrj #endif
43ae115bc7Smrj
44ae115bc7Smrj #define MAX_DECOMP_BUFS 8
45ae115bc7Smrj #define GZIP_ID_BYTE_1 0x1f
46ae115bc7Smrj #define GZIP_ID_BYTE_2 0x8b
47ae115bc7Smrj #define GZIP_CM_DEFLATE 0x08
48ae115bc7Smrj #define SEEKBUFSIZE 8192
49ae115bc7Smrj
50*342440ecSPrasad Singamsetty extern void prom_printf(const char *fmt, ...);
51*342440ecSPrasad Singamsetty
52ae115bc7Smrj #ifdef _BOOT
53*342440ecSPrasad Singamsetty #define dprintf if (cf_debug) prom_printf
54ae115bc7Smrj #else
55*342440ecSPrasad Singamsetty #define dprintf if (cf_debug) prom_printf
56ae115bc7Smrj
57ae115bc7Smrj #endif
58ae115bc7Smrj
59ae115bc7Smrj extern int bootrd_debug;
60ae115bc7Smrj extern void *bkmem_alloc(size_t);
61ae115bc7Smrj extern void bkmem_free(void *, size_t);
62ae115bc7Smrj
63ae115bc7Smrj caddr_t scratch_bufs[MAX_DECOMP_BUFS]; /* array of free scratch mem bufs */
64ae115bc7Smrj int decomp_bufcnt; /* total no, of allocated decomp bufs */
65ae115bc7Smrj int free_dcomp_bufs; /* no. of free decomp bufs */
66ae115bc7Smrj char seek_scrbuf[SEEKBUFSIZE]; /* buffer for seeking */
67*342440ecSPrasad Singamsetty int cf_debug = 0; /* non-zero enables debug prints */
68ae115bc7Smrj
69ae115bc7Smrj void *
cf_alloc(void * opaque,unsigned int items,unsigned int size)70ae115bc7Smrj cf_alloc(void *opaque, unsigned int items, unsigned int size)
71ae115bc7Smrj {
72ae115bc7Smrj fileid_t *filep;
73ae115bc7Smrj unsigned int nbytes;
74ae115bc7Smrj caddr_t ptr;
75ae115bc7Smrj
76ae115bc7Smrj filep = (fileid_t *)opaque;
77ae115bc7Smrj nbytes = roundup(items * size, sizeof (long));
78ae115bc7Smrj if (nbytes > (DECOMP_BUFSIZE - filep->fi_dcscrused)) {
79ae115bc7Smrj ptr = bkmem_alloc(nbytes);
80ae115bc7Smrj } else {
81ae115bc7Smrj ptr = &filep->fi_dcscrbuf[filep->fi_dcscrused];
82ae115bc7Smrj filep->fi_dcscrused += nbytes;
83ae115bc7Smrj }
84ae115bc7Smrj bzero(ptr, nbytes);
85ae115bc7Smrj return (ptr);
86ae115bc7Smrj }
87ae115bc7Smrj
88ae115bc7Smrj /*
89ae115bc7Smrj * Decompression scratch memory free routine, does nothing since we free
90ae115bc7Smrj * the entire scratch area all at once on file close.
91ae115bc7Smrj */
92ae115bc7Smrj /* ARGSUSED */
93ae115bc7Smrj void
cf_free(void * opaque,void * addr)94ae115bc7Smrj cf_free(void *opaque, void *addr)
95ae115bc7Smrj {
96ae115bc7Smrj }
97ae115bc7Smrj
98ae115bc7Smrj /*
99ae115bc7Smrj * Read the first block of the file described by filep and determine if
100ae115bc7Smrj * the file is gzip-compressed. If so, the compressed flag will be set
101ae115bc7Smrj * in the fileid_t struct pointed to by filep and it will be initialized
102ae115bc7Smrj * for doing decompression on reads to the file.
103ae115bc7Smrj */
104ae115bc7Smrj int
cf_check_compressed(fileid_t * filep)105ae115bc7Smrj cf_check_compressed(fileid_t *filep)
106ae115bc7Smrj {
107ae115bc7Smrj unsigned char *filebytes;
108ae115bc7Smrj z_stream *zsp;
109ae115bc7Smrj
1109e6e1504Ssmaybe /*
111*342440ecSPrasad Singamsetty * checking for a dcfs compressed file first would involve:
112*342440ecSPrasad Singamsetty *
113*342440ecSPrasad Singamsetty * if (filep->fi_inode->i_cflags & ICOMPRESS)
114*342440ecSPrasad Singamsetty * filep->fi_flags |= FI_COMPRESSED;
115*342440ecSPrasad Singamsetty */
116*342440ecSPrasad Singamsetty
117*342440ecSPrasad Singamsetty /*
118*342440ecSPrasad Singamsetty * If the file is not long enough to check for a
119*342440ecSPrasad Singamsetty * decompression header then return not compressed.
1209e6e1504Ssmaybe */
1219e6e1504Ssmaybe if (filep->fi_inode->i_size < 3)
1229e6e1504Ssmaybe return (0);
123ae115bc7Smrj filep->fi_offset = 0;
124ae115bc7Smrj if ((filep->fi_getblock)(filep) == -1)
125ae115bc7Smrj return (-1);
126ae115bc7Smrj filep->fi_offset = 0;
127ae115bc7Smrj filep->fi_count = 0;
128ae115bc7Smrj filep->fi_cfoff = 0;
129ae115bc7Smrj filebytes = (unsigned char *)filep->fi_memp;
130*342440ecSPrasad Singamsetty if (filebytes[0] != GZIP_ID_BYTE_1 ||
131*342440ecSPrasad Singamsetty filebytes[1] != GZIP_ID_BYTE_2 ||
132ae115bc7Smrj filebytes[2] != GZIP_CM_DEFLATE)
133ae115bc7Smrj return (0); /* not compressed */
134ae115bc7Smrj filep->fi_flags |= FI_COMPRESSED;
135ae115bc7Smrj
136ae115bc7Smrj dprintf("file %s is compressed\n", filep->fi_path);
137ae115bc7Smrj
138ae115bc7Smrj /*
139ae115bc7Smrj * Allocate decompress scratch buffer
140ae115bc7Smrj */
141ae115bc7Smrj if (free_dcomp_bufs) {
142ae115bc7Smrj filep->fi_dcscrbuf = scratch_bufs[--free_dcomp_bufs];
143ae115bc7Smrj } else {
144ae115bc7Smrj filep->fi_dcscrbuf = bkmem_alloc(DECOMP_BUFSIZE);
145ae115bc7Smrj decomp_bufcnt++;
146ae115bc7Smrj }
147ae115bc7Smrj filep->fi_dcscrused = 0;
148ae115bc7Smrj zsp = bkmem_alloc(sizeof (*zsp));
149ae115bc7Smrj filep->fi_dcstream = zsp;
150ae115bc7Smrj /*
151c9431fa1Sahl * Initialize the decompression stream. Adding 16 to the window size
152c9431fa1Sahl * indicates that zlib should expect a gzip header.
153ae115bc7Smrj */
154ae115bc7Smrj bzero(zsp, sizeof (*zsp));
155ae115bc7Smrj zsp->opaque = filep;
156ae115bc7Smrj zsp->zalloc = cf_alloc;
157ae115bc7Smrj zsp->zfree = cf_free;
158ae115bc7Smrj zsp->avail_in = 0;
159ae115bc7Smrj zsp->next_in = NULL;
160ae115bc7Smrj zsp->avail_out = 0;
161ae115bc7Smrj zsp->next_out = NULL;
162*342440ecSPrasad Singamsetty if (inflateInit2(zsp, MAX_WBITS | 0x20) != Z_OK) {
163c9431fa1Sahl dprintf("inflateInit2() failed\n");
164ae115bc7Smrj return (-1);
165c9431fa1Sahl }
166ae115bc7Smrj return (0);
167ae115bc7Smrj }
168ae115bc7Smrj
169ae115bc7Smrj /*
170ae115bc7Smrj * If the file described by fileid_t struct at *filep is compressed
171ae115bc7Smrj * free any resources associated with the decompression. (decompression
172ae115bc7Smrj * buffer, etc.).
173ae115bc7Smrj */
174ae115bc7Smrj void
cf_close(fileid_t * filep)175ae115bc7Smrj cf_close(fileid_t *filep)
176ae115bc7Smrj {
177ae115bc7Smrj if ((filep->fi_flags & FI_COMPRESSED) == 0)
178ae115bc7Smrj return;
179ae115bc7Smrj dprintf("cf_close: %s\n", filep->fi_path);
180ae115bc7Smrj (void) inflateEnd(filep->fi_dcstream);
181ae115bc7Smrj bkmem_free(filep->fi_dcstream, sizeof (z_stream));
182ae115bc7Smrj if (free_dcomp_bufs == MAX_DECOMP_BUFS) {
183ae115bc7Smrj bkmem_free(filep->fi_dcscrbuf, DECOMP_BUFSIZE);
184ae115bc7Smrj } else {
185ae115bc7Smrj scratch_bufs[free_dcomp_bufs++] = filep->fi_dcscrbuf;
186ae115bc7Smrj }
187ae115bc7Smrj }
188ae115bc7Smrj
189ae115bc7Smrj void
cf_rewind(fileid_t * filep)190ae115bc7Smrj cf_rewind(fileid_t *filep)
191ae115bc7Smrj {
192ae115bc7Smrj z_stream *zsp;
193ae115bc7Smrj
194ae115bc7Smrj dprintf("cf_rewind: %s\n", filep->fi_path);
195ae115bc7Smrj zsp = filep->fi_dcstream;
196ae115bc7Smrj zsp->avail_in = 0;
197ae115bc7Smrj zsp->next_in = NULL;
198ae115bc7Smrj (void) inflateReset(zsp);
199ae115bc7Smrj filep->fi_cfoff = 0;
200ae115bc7Smrj }
201ae115bc7Smrj
202ae115bc7Smrj #define FLG_FHCRC 0x02 /* crc field present */
203ae115bc7Smrj #define FLG_FEXTRA 0x04 /* "extra" field present */
204ae115bc7Smrj #define FLG_FNAME 0x08 /* file name field present */
205ae115bc7Smrj #define FLG_FCOMMENT 0x10 /* comment field present */
206ae115bc7Smrj
207ae115bc7Smrj /*
208ae115bc7Smrj * Read at the current uncompressed offset from the compressed file described
209ae115bc7Smrj * by *filep. Will return decompressed data.
210ae115bc7Smrj */
211ae115bc7Smrj int
cf_read(fileid_t * filep,caddr_t buf,size_t count)212ae115bc7Smrj cf_read(fileid_t *filep, caddr_t buf, size_t count)
213ae115bc7Smrj {
214ae115bc7Smrj z_stream *zsp;
215ae115bc7Smrj struct inode *ip;
216ae115bc7Smrj int err = Z_OK;
217c9431fa1Sahl int infbytes;
218ae115bc7Smrj off_t soff;
219ae115bc7Smrj caddr_t smemp;
220ae115bc7Smrj
221ae115bc7Smrj dprintf("cf_read: %s ", filep->fi_path);
222ae115bc7Smrj dprintf("%lx bytes\n", count);
223ae115bc7Smrj zsp = filep->fi_dcstream;
224ae115bc7Smrj ip = filep->fi_inode;
225ae115bc7Smrj dprintf(" reading at offset %lx\n", zsp->total_out);
226ae115bc7Smrj zsp->next_out = (unsigned char *)buf;
227ae115bc7Smrj zsp->avail_out = count;
228ae115bc7Smrj while (zsp->avail_out != 0) {
229ae115bc7Smrj if (zsp->avail_in == 0 && filep->fi_cfoff < ip->i_size) {
230ae115bc7Smrj /*
231ae115bc7Smrj * read a block of the file to inflate
232ae115bc7Smrj */
233ae115bc7Smrj soff = filep->fi_offset;
234ae115bc7Smrj smemp = filep->fi_memp;
235ae115bc7Smrj filep->fi_memp = NULL;
236ae115bc7Smrj filep->fi_offset = filep->fi_cfoff;
237ae115bc7Smrj filep->fi_count = 0;
238ae115bc7Smrj if ((*filep->fi_getblock)(filep) == -1)
239ae115bc7Smrj return (-1);
240ae115bc7Smrj filep->fi_offset = soff;
241ae115bc7Smrj zsp->next_in = (unsigned char *)filep->fi_memp;
242ae115bc7Smrj zsp->avail_in = filep->fi_count;
243ae115bc7Smrj filep->fi_memp = smemp;
244ae115bc7Smrj filep->fi_cfoff += filep->fi_count;
245ae115bc7Smrj }
246ae115bc7Smrj infbytes = zsp->avail_out;
247ae115bc7Smrj dprintf("attempting inflate of %x bytes to buf at: %lx\n",
248ae115bc7Smrj zsp->avail_out, (unsigned long)zsp->next_out);
249ae115bc7Smrj err = inflate(zsp, Z_NO_FLUSH);
250ae115bc7Smrj infbytes -= zsp->avail_out;
251ae115bc7Smrj dprintf("inflated %x bytes, errcode=%d\n", infbytes, err);
252ae115bc7Smrj /*
253ae115bc7Smrj * break out if we hit end of the compressed file
254ae115bc7Smrj * or the end of the compressed byte stream
255ae115bc7Smrj */
256ae115bc7Smrj if (filep->fi_cfoff >= ip->i_size || err == Z_STREAM_END)
257ae115bc7Smrj break;
258ae115bc7Smrj }
259ae115bc7Smrj dprintf("cf_read: returned %lx bytes\n", count - zsp->avail_out);
260ae115bc7Smrj return (count - zsp->avail_out);
261ae115bc7Smrj }
262ae115bc7Smrj
263ae115bc7Smrj /*
264ae115bc7Smrj * Seek to the location specified by addr
265ae115bc7Smrj */
266ae115bc7Smrj void
cf_seek(fileid_t * filep,off_t addr,int whence)267ae115bc7Smrj cf_seek(fileid_t *filep, off_t addr, int whence)
268ae115bc7Smrj {
269ae115bc7Smrj z_stream *zsp;
270ae115bc7Smrj int readsz;
271ae115bc7Smrj
272ae115bc7Smrj dprintf("cf_seek: %s ", filep->fi_path);
273ae115bc7Smrj dprintf("to %lx\n", addr);
274ae115bc7Smrj zsp = filep->fi_dcstream;
275ae115bc7Smrj if (whence == SEEK_CUR)
276ae115bc7Smrj addr += zsp->total_out;
277ae115bc7Smrj /*
278ae115bc7Smrj * To seek backwards, must rewind and seek forwards
279ae115bc7Smrj */
280ae115bc7Smrj if (addr < zsp->total_out) {
281ae115bc7Smrj cf_rewind(filep);
282ae115bc7Smrj filep->fi_offset = 0;
283ae115bc7Smrj } else {
284ae115bc7Smrj addr -= zsp->total_out;
285ae115bc7Smrj }
286ae115bc7Smrj while (addr > 0) {
287ae115bc7Smrj readsz = MIN(addr, SEEKBUFSIZE);
288ae115bc7Smrj (void) cf_read(filep, seek_scrbuf, readsz);
289ae115bc7Smrj addr -= readsz;
290ae115bc7Smrj }
291ae115bc7Smrj }
292