1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  "Compressed" file system			File: cfe_zlibfs.c
5    *
6    *  This is more of a filesystem "hook" than an actual file system.
7    *  You can stick it on the front of the chain of file systems
8    *  that CFE calls and it will route data read from the
9    *  underlying filesystem through ZLIB before passing it up to the
10    *  user.
11    *
12    *  Author:  Mitch Lichtenberg
13    *
14    *********************************************************************
15    *
16    *  Copyright 2000,2001,2002,2003
17    *  Broadcom Corporation. All rights reserved.
18    *
19    *  This software is furnished under license and may be used and
20    *  copied only in accordance with the following terms and
21    *  conditions.  Subject to these conditions, you may download,
22    *  copy, install, use, modify and distribute modified or unmodified
23    *  copies of this software in source and/or binary form.  No title
24    *  or ownership is transferred hereby.
25    *
26    *  1) Any source code used, modified or distributed must reproduce
27    *     and retain this copyright notice and list of conditions
28    *     as they appear in the source file.
29    *
30    *  2) No right is granted to use any trade name, trademark, or
31    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
32    *     name may not be used to endorse or promote products derived
33    *     from this software without the prior written permission of
34    *     Broadcom Corporation.
35    *
36    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
37    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
38    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
39    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
40    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
41    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
42    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
44    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
45    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
46    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
47    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
48    *     THE POSSIBILITY OF SUCH DAMAGE.
49    ********************************************************************* */
50
51#if CFG_ZLIB
52
53
54#include "cfe.h"
55#include "cfe_fileops.h"
56
57#include "zlib.h"
58
59/*  *********************************************************************
60    *  ZLIBFS context
61    ********************************************************************* */
62
63/*
64 * File system context - describes overall file system info,
65 * such as the handle to the underlying device.
66 */
67
68typedef struct zlibfs_fsctx_s {
69    void *zlibfsctx_subfsctx;
70    const fileio_dispatch_t *zlibfsctx_subops;
71    int zlibfsctx_refcnt;
72} zlibfs_fsctx_t;
73
74/*
75 * File context - describes an open file on the file system.
76 * For raw devices, this is pretty meaningless, but we do
77 * keep track of where we are.
78 */
79
80#define ZLIBFS_BUFSIZE	1024
81typedef struct zlibfs_file_s {
82    zlibfs_fsctx_t *zlibfs_fsctx;
83    int zlibfs_fileoffset;
84    void *zlibfs_subfile;
85    z_stream zlibfs_stream;
86    uint8_t *zlibfs_inbuf;
87    uint8_t *zlibfs_outbuf;
88    int zlibfs_outlen;
89    uint8_t *zlibfs_outptr;
90    int zlibfs_eofseen;
91} zlibfs_file_t;
92
93/*  *********************************************************************
94    *  Prototypes
95    ********************************************************************* */
96
97static int zlibfs_fileop_init(void **fsctx,void *ctx);
98static int zlibfs_fileop_open(void **ref,void *fsctx,char *filename,int mode);
99static int zlibfs_fileop_read(void *ref,hsaddr_t buf,int len);
100static int zlibfs_fileop_write(void *ref,hsaddr_t buf,int len);
101static int zlibfs_fileop_seek(void *ref,int offset,int how);
102static void zlibfs_fileop_close(void *ref);
103static void zlibfs_fileop_uninit(void *fsctx);
104
105voidpf zcalloc(voidpf opaque,unsigned items, unsigned size);
106void zcfree(voidpf opaque,voidpf ptr);
107
108/*  *********************************************************************
109    *  ZLIB fileio dispatch table
110    ********************************************************************* */
111
112
113static uint8_t gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
114
115
116/* gzip flag byte */
117#define ASCII_FLAG   0x01 /* bit 0 set: file probably ascii text */
118#define HEAD_CRC     0x02 /* bit 1 set: header CRC present */
119#define EXTRA_FIELD  0x04 /* bit 2 set: extra field present */
120#define ORIG_NAME    0x08 /* bit 3 set: original file name present */
121#define COMMENT      0x10 /* bit 4 set: file comment present */
122#define RESERVED     0xE0 /* bits 5..7: reserved */
123
124const fileio_dispatch_t zlibfs_fileops = {
125    "z",
126    0,
127    zlibfs_fileop_init,
128    zlibfs_fileop_open,
129    zlibfs_fileop_read,
130    zlibfs_fileop_write,
131    zlibfs_fileop_seek,
132    zlibfs_fileop_close,
133    zlibfs_fileop_uninit
134};
135
136/*
137 * Utility functions needed by the ZLIB routines
138 */
139voidpf zcalloc(voidpf opaque,unsigned items, unsigned size)
140{
141    void *ptr;
142
143    ptr = KMALLOC(items*size,0);
144    if (ptr) lib_memset(ptr,0,items*size);
145    return ptr;
146}
147
148void zcfree(voidpf opaque,voidpf ptr)
149{
150    KFREE(ptr);
151}
152
153
154static int zlibfs_fileop_init(void **newfsctx,void *curfsvoid)
155{
156    zlibfs_fsctx_t *fsctx;
157    fileio_ctx_t *curfsctx = (fileio_ctx_t *) curfsvoid;
158
159    *newfsctx = NULL;
160
161    fsctx = KMALLOC(sizeof(zlibfs_fsctx_t),0);
162    if (!fsctx) {
163	return CFE_ERR_NOMEM;
164	}
165
166    fsctx->zlibfsctx_refcnt = 0;
167    fsctx->zlibfsctx_subops  =  curfsctx->ops;
168    fsctx->zlibfsctx_subfsctx = curfsctx->fsctx;
169
170    *newfsctx = fsctx;
171
172    return 0;
173}
174
175
176static int get_byte(zlibfs_file_t *file,uint8_t *ch)
177{
178    int res;
179
180    res = BDREAD(file->zlibfs_fsctx->zlibfsctx_subops,
181		 file->zlibfs_subfile,
182		 PTR2HSADDR(ch),
183		 1);
184
185    return res;
186}
187
188
189static int check_header(zlibfs_file_t *file)
190{
191    int method; /* method byte */
192    int flags;  /* flags byte */
193    uInt len;
194    uint8_t c;
195    int res;
196
197    /* Check the gzip magic header */
198    for (len = 0; len < 2; len++) {
199	res = get_byte(file,&c);
200	if (c != gz_magic[len]) {
201	    return -1;
202	}
203    }
204
205    get_byte(file,&c); method = (int) c;
206    get_byte(file,&c); flags = (int) c;
207
208    if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
209	return -1;
210	}
211
212    /* Discard time, xflags and OS code: */
213    for (len = 0; len < 6; len++) (void)get_byte(file,&c);
214
215    if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
216	get_byte(file,&c);
217	len = (uInt) c;
218	get_byte(file,&c);
219	len += ((uInt)c)<<8;
220	/* len is garbage if EOF but the loop below will quit anyway */
221	while ((len-- != 0) && (get_byte(file,&c) == 1)) ;
222    }
223    if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
224	while ((get_byte(file,&c) == 1) && (c != 0)) ;
225    }
226    if ((flags & COMMENT) != 0) {   /* skip the .gz file comment */
227	while ((get_byte(file,&c) == 1) && (c != 0)) ;
228    }
229    if ((flags & HEAD_CRC) != 0) {  /* skip the header crc */
230	for (len = 0; len < 2; len++) (void)get_byte(file,&c);
231    }
232
233    return 0;
234}
235
236static int zlibfs_fileop_open(void **ref,void *fsctx_arg,char *filename,int mode)
237{
238    zlibfs_fsctx_t *fsctx;
239    zlibfs_file_t *file;
240    int err;
241
242    if (mode != FILE_MODE_READ) return CFE_ERR_UNSUPPORTED;
243
244    fsctx = (zlibfs_fsctx_t *) fsctx_arg;
245
246    file = KMALLOC(sizeof(zlibfs_file_t),0);
247    if (!file) {
248	return CFE_ERR_NOMEM;
249	}
250
251    file->zlibfs_fileoffset = 0;
252    file->zlibfs_fsctx = fsctx;
253    file->zlibfs_inbuf = NULL;
254    file->zlibfs_outbuf = NULL;
255    file->zlibfs_eofseen = 0;
256
257    err = BDOPEN(fsctx->zlibfsctx_subops,&(file->zlibfs_subfile),
258		 fsctx->zlibfsctx_subfsctx,filename);
259
260    if (err != 0) {
261	goto error2;
262	return err;
263	}
264
265    /* Open the zstream */
266
267    file->zlibfs_inbuf = KMALLOC(ZLIBFS_BUFSIZE,0);
268    file->zlibfs_outbuf = KMALLOC(ZLIBFS_BUFSIZE,0);
269
270    if (!file->zlibfs_inbuf || !file->zlibfs_outbuf) {
271	err = CFE_ERR_NOMEM;
272	goto error;
273	}
274
275    file->zlibfs_stream.next_in = NULL;
276    file->zlibfs_stream.avail_in = 0;
277    file->zlibfs_stream.next_out = file->zlibfs_outbuf;
278    file->zlibfs_stream.avail_out = ZLIBFS_BUFSIZE;
279    file->zlibfs_stream.zalloc = (alloc_func)0;
280    file->zlibfs_stream.zfree = (free_func)0;
281
282    file->zlibfs_outlen = 0;
283    file->zlibfs_outptr = file->zlibfs_outbuf;
284
285    err = inflateInit2(&(file->zlibfs_stream),-15);
286    if (err != Z_OK) {
287	err = CFE_ERR;
288	goto error;
289	}
290
291    check_header(file);
292
293    fsctx->zlibfsctx_refcnt++;
294
295    *ref = file;
296    return 0;
297
298error:
299    BDCLOSE(file->zlibfs_fsctx->zlibfsctx_subops,file->zlibfs_subfile);
300error2:
301    if (file->zlibfs_inbuf) KFREE(file->zlibfs_inbuf);
302    if (file->zlibfs_outbuf) KFREE(file->zlibfs_outbuf);
303    KFREE(file);
304    return err;
305}
306
307static int zlibfs_fileop_read(void *ref,hsaddr_t buf,int len)
308{
309    zlibfs_file_t *file = (zlibfs_file_t *) ref;
310    int res = 0;
311    int err;
312    int amtcopy;
313    int ttlcopy = 0;
314
315    if (len == 0) return 0;
316
317    while (len) {
318
319	/* Figure the amount to copy.  This is the min of what we
320	   have left to do and what is available. */
321	amtcopy = len;
322	if (amtcopy > file->zlibfs_outlen) {
323	    amtcopy = file->zlibfs_outlen;
324	    }
325
326	/* Copy the data. */
327
328	if (buf) {
329	    hs_memcpy_to_hs(buf,file->zlibfs_outptr,amtcopy);
330	    buf += amtcopy;
331	    }
332
333	/* Update the pointers. */
334	file->zlibfs_outptr += amtcopy;
335	file->zlibfs_outlen -= amtcopy;
336	len -= amtcopy;
337	ttlcopy += amtcopy;
338
339	/* If we've eaten all of the output, reset and call inflate
340	   again. */
341
342	if (file->zlibfs_outlen == 0) {
343	    /* If no input data to decompress, get some more if we can. */
344	    if (file->zlibfs_eofseen) break;
345	    if (file->zlibfs_stream.avail_in == 0) {
346		res = BDREAD(file->zlibfs_fsctx->zlibfsctx_subops,
347			     file->zlibfs_subfile,
348			     PTR2HSADDR(file->zlibfs_inbuf),
349			     ZLIBFS_BUFSIZE);
350		/* If at EOF or error, get out. */
351		if (res <= 0) break;
352		file->zlibfs_stream.next_in = file->zlibfs_inbuf;
353		file->zlibfs_stream.avail_in = res;
354		}
355
356	    /* inflate the input data. */
357	    file->zlibfs_stream.next_out = file->zlibfs_outbuf;
358	    file->zlibfs_stream.avail_out = ZLIBFS_BUFSIZE;
359	    file->zlibfs_outptr = file->zlibfs_outbuf;
360	    err = inflate(&(file->zlibfs_stream),Z_SYNC_FLUSH);
361	    if (err == Z_STREAM_END) {
362		/* We can get a partial buffer fill here. */
363	        file->zlibfs_eofseen = 1;
364		}
365	    else if (err != Z_OK) {
366		res = CFE_ERR;
367		break;
368		}
369	    file->zlibfs_outlen = file->zlibfs_stream.next_out -
370		file->zlibfs_outptr;
371	    }
372
373	}
374
375    file->zlibfs_fileoffset += ttlcopy;
376
377    return (res < 0) ? res : ttlcopy;
378}
379
380static int zlibfs_fileop_write(void *ref,hsaddr_t buf,int len)
381{
382    return CFE_ERR_UNSUPPORTED;
383}
384
385static int zlibfs_fileop_seek(void *ref,int offset,int how)
386{
387    zlibfs_file_t *file = (zlibfs_file_t *) ref;
388    int res;
389    int delta;
390
391    switch (how) {
392	case FILE_SEEK_BEGINNING:
393	    delta = offset - file->zlibfs_fileoffset;
394	    break;
395	case FILE_SEEK_CURRENT:
396	    delta = offset;
397	    break;
398	default:
399	    return CFE_ERR_UNSUPPORTED;
400	    break;
401	}
402
403    /* backward seeking not allowed on compressed streams */
404    if (delta < 0) {
405	return CFE_ERR_UNSUPPORTED;
406	}
407
408    res = zlibfs_fileop_read(ref,NULL,delta);
409
410    if (res < 0) return res;
411
412    return file->zlibfs_fileoffset;
413}
414
415
416static void zlibfs_fileop_close(void *ref)
417{
418    zlibfs_file_t *file = (zlibfs_file_t *) ref;
419
420    file->zlibfs_fsctx->zlibfsctx_refcnt--;
421
422    inflateEnd(&(file->zlibfs_stream));
423
424    BDCLOSE(file->zlibfs_fsctx->zlibfsctx_subops,file->zlibfs_subfile);
425
426    KFREE(file);
427}
428
429static void zlibfs_fileop_uninit(void *fsctx_arg)
430{
431    zlibfs_fsctx_t *fsctx = (zlibfs_fsctx_t *) fsctx_arg;
432
433    if (fsctx->zlibfsctx_refcnt) {
434	xprintf("zlibfs_fileop_uninit: warning: refcnt not zero\n");
435	}
436
437    BDUNINIT(fsctx->zlibfsctx_subops,fsctx->zlibfsctx_subfsctx);
438
439    KFREE(fsctx);
440}
441
442
443#endif
444