1 /* zlib.c: routines for zlib (de)compression of data
2 Copyright (c) 2002 Darren Salt, Philip Kendall
3
4 $Id: zlib.c 3698 2008-06-30 15:12:02Z pak21 $
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 Author contact information:
21
22 Darren: E-mail: linux@youmustbejoking.demon.co.uk
23
24 Philip: E-mail: philip-fuse@shadowmagic.org.uk
25
26 */
27
28 #include <config.h>
29
30 #ifdef HAVE_ZLIB_H
31
32 #include <errno.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/types.h>
36
37 #ifdef HAVE_UNISTD_H
38 #include <unistd.h>
39 #endif /* #ifdef HAVE_UNISTD_H */
40
41 #include <zlib.h>
42
43 #include "internals.h"
44
45 static libspectrum_error
46 skip_gzip_header( const libspectrum_byte **gzptr, size_t *gzlength );
47 static libspectrum_error
48 skip_null_terminated_string( const libspectrum_byte **ptr, size_t *length,
49 const char *name );
50 static libspectrum_error
51 zlib_inflate( const libspectrum_byte *gzptr, size_t gzlength,
52 libspectrum_byte **outptr, size_t *outlength, int gzip_hack );
53
54 libspectrum_error
libspectrum_zlib_inflate(const libspectrum_byte * gzptr,size_t gzlength,libspectrum_byte ** outptr,size_t * outlength)55 libspectrum_zlib_inflate( const libspectrum_byte *gzptr, size_t gzlength,
56 libspectrum_byte **outptr, size_t *outlength )
57 /* Inflates a block of data.
58 * Input: gzptr -> source (deflated) data
59 * *gzlength == source data length
60 * Output: *outptr -> inflated data (malloced in this fn)
61 * *outlength == length of the inflated data
62 * Returns: error flag (libspectrum_error)
63 */
64 {
65 return zlib_inflate( gzptr, gzlength, outptr, outlength, 0 );
66 }
67
68 libspectrum_error
libspectrum_gzip_inflate(const libspectrum_byte * gzptr,size_t gzlength,libspectrum_byte ** outptr,size_t * outlength)69 libspectrum_gzip_inflate( const libspectrum_byte *gzptr, size_t gzlength,
70 libspectrum_byte **outptr, size_t *outlength )
71 {
72 int error;
73
74 error = skip_gzip_header( &gzptr, &gzlength ); if( error ) return error;
75
76 return zlib_inflate( gzptr, gzlength, outptr, outlength, 1 );
77 }
78
79 static libspectrum_error
zlib_inflate(const libspectrum_byte * gzptr,size_t gzlength,libspectrum_byte ** outptr,size_t * outlength,int gzip_hack)80 zlib_inflate( const libspectrum_byte *gzptr, size_t gzlength,
81 libspectrum_byte **outptr, size_t *outlength, int gzip_hack )
82 {
83 z_stream stream;
84 int error;
85
86 /* Use default memory management */
87 stream.zalloc = Z_NULL; stream.zfree = Z_NULL; stream.opaque = Z_NULL;
88
89 /* Cast needed to avoid warning about losing const */
90 stream.next_in = (libspectrum_byte*)gzptr; stream.avail_in = gzlength;
91
92 if( gzip_hack ) {
93
94 /*
95 * HACK ALERT (comment from zlib 1.1.14:gzio.c:143)
96 *
97 * windowBits is passed < 0 to tell that there is no zlib header.
98 * Note that in this case inflate *requires* an extra "dummy" byte
99 * after the compressed stream in order to complete decompression
100 * and return Z_STREAM_END. Here the gzip CRC32 ensures that 4 bytes
101 * are present after the compressed stream.
102 *
103 */
104 error = inflateInit2( &stream, -15 );
105
106 } else {
107
108 error = inflateInit( &stream );
109
110 }
111
112 switch( error ) {
113
114 case Z_OK: break;
115
116 case Z_MEM_ERROR:
117 libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY,
118 "out of memory at %s:%d", __FILE__, __LINE__ );
119 inflateEnd( &stream );
120 return LIBSPECTRUM_ERROR_MEMORY;
121
122 default:
123 libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
124 "error from inflateInit2: %s", stream.msg );
125 inflateEnd( &stream );
126 return LIBSPECTRUM_ERROR_MEMORY;
127
128 }
129
130 if( *outlength ) {
131
132 *outptr = libspectrum_malloc( *outlength );
133 stream.next_out = *outptr; stream.avail_out = *outlength;
134 error = inflate( &stream, Z_FINISH );
135
136 } else {
137
138 *outptr = stream.next_out = NULL;
139 *outlength = stream.avail_out = 0;
140
141 do {
142
143 libspectrum_byte *ptr;
144
145 *outlength += 16384; stream.avail_out += 16384;
146 ptr = libspectrum_realloc( *outptr, *outlength );
147 stream.next_out = ptr + ( stream.next_out - *outptr );
148 *outptr = ptr;
149
150 error = inflate( &stream, 0 );
151
152 } while( error == Z_OK );
153
154 }
155
156 *outlength = stream.next_out - *outptr;
157 *outptr = libspectrum_realloc( *outptr, *outlength );
158
159 switch( error ) {
160
161 case Z_STREAM_END: break;
162
163 case Z_NEED_DICT:
164 libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
165 "gzip inflation needs dictionary" );
166 libspectrum_free( *outptr );
167 inflateEnd( &stream );
168 return LIBSPECTRUM_ERROR_UNKNOWN;
169
170 case Z_DATA_ERROR:
171 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT, "corrupt gzip data" );
172 libspectrum_free( *outptr );
173 inflateEnd( &stream );
174 return LIBSPECTRUM_ERROR_CORRUPT;
175
176 case Z_MEM_ERROR:
177 libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY,
178 "out of memory at %s:%d", __FILE__, __LINE__ );
179 libspectrum_free( *outptr );
180 inflateEnd( &stream );
181 return LIBSPECTRUM_ERROR_MEMORY;
182
183 case Z_BUF_ERROR:
184 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
185 "not enough space in gzip output buffer" );
186 libspectrum_free( *outptr );
187 inflateEnd( &stream );
188 return LIBSPECTRUM_ERROR_CORRUPT;
189
190 default:
191 libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
192 "gzip error from inflate: %s",
193 stream.msg );
194 libspectrum_free( *outptr );
195 inflateEnd( &stream );
196 return LIBSPECTRUM_ERROR_LOGIC;
197
198 }
199
200 error = inflateEnd( &stream );
201 if( error != Z_OK ) {
202 libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
203 "gzip error from inflateEnd: %s", stream.msg );
204 libspectrum_free( *outptr );
205 inflateEnd( &stream );
206 return LIBSPECTRUM_ERROR_LOGIC;
207 }
208
209 return LIBSPECTRUM_ERROR_NONE;
210 }
211
212 static libspectrum_error
skip_gzip_header(const libspectrum_byte ** gzptr,size_t * gzlength)213 skip_gzip_header( const libspectrum_byte **gzptr, size_t *gzlength )
214 {
215 libspectrum_byte flags;
216 libspectrum_error error;
217
218 if( *gzlength < 10 ) {
219 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
220 "not enough data for gzip header" );
221 return LIBSPECTRUM_ERROR_CORRUPT;
222 }
223
224 if( (*gzptr)[0] != 0x1f || (*gzptr)[1] != 0x8b ) {
225 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
226 "gzip header missing" );
227 return LIBSPECTRUM_ERROR_CORRUPT;
228 }
229
230 if( (*gzptr)[2] != 8 ) {
231 libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
232 "unknown gzip compression method %d",
233 (*gzptr)[2] );
234 return LIBSPECTRUM_ERROR_UNKNOWN;
235 }
236
237 flags = (*gzptr)[3];
238
239 (*gzptr) += 10; (*gzlength) -= 10;
240
241 if( flags & 0x04 ) { /* extra header present */
242
243 size_t length;
244
245 if( *gzlength < 2 ) {
246 libspectrum_print_error(
247 LIBSPECTRUM_ERROR_CORRUPT,
248 "not enough data for gzip extra header length"
249 );
250 return LIBSPECTRUM_ERROR_CORRUPT;
251 }
252
253 length = (*gzptr)[0] + (*gzptr)[1] * 0x100;
254 (*gzptr) += 2; (*gzlength) -= 2;
255
256 if( *gzlength < length ) {
257 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
258 "not enough data for gzip extra header" );
259 return LIBSPECTRUM_ERROR_CORRUPT;
260 }
261
262 }
263
264 if( flags & 0x08 ) { /* original file name present */
265 error = skip_null_terminated_string( gzptr, gzlength, "original name" );
266 if( error ) return error;
267 }
268
269 if( flags & 0x10 ) { /* comment present */
270 error = skip_null_terminated_string( gzptr, gzlength, "comment" );
271 if( error ) return error;
272 }
273
274 if( flags & 0x02 ) { /* header CRC present */
275
276 if( *gzlength < 2 ) {
277 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
278 "not enough data for gzip header CRC" );
279 return LIBSPECTRUM_ERROR_CORRUPT;
280 }
281
282 /* Could check the header CRC if we really wanted to */
283 (*gzptr) += 2; (*gzptr) -= 2;
284 }
285
286 return LIBSPECTRUM_ERROR_NONE;
287 }
288
289 static libspectrum_error
skip_null_terminated_string(const libspectrum_byte ** ptr,size_t * length,const char * name)290 skip_null_terminated_string( const libspectrum_byte **ptr, size_t *length,
291 const char *name )
292 {
293 while( **ptr && *length ) { (*ptr)++; (*length)--; }
294
295 if( !( *length ) ) {
296 libspectrum_print_error( LIBSPECTRUM_ERROR_CORRUPT,
297 "not enough data for gzip %s", name );
298 return LIBSPECTRUM_ERROR_CORRUPT;
299 }
300
301 /* Skip the null as well */
302 (*ptr)++; (*length)--;
303
304 return LIBSPECTRUM_ERROR_NONE;
305 }
306
307 libspectrum_error
libspectrum_zlib_compress(const libspectrum_byte * data,size_t length,libspectrum_byte ** gzptr,size_t * gzlength)308 libspectrum_zlib_compress( const libspectrum_byte *data, size_t length,
309 libspectrum_byte **gzptr, size_t *gzlength )
310 /* Deflates a block of data.
311 * Input: data -> source data
312 * length == source data length
313 * Output: *gzptr -> deflated data (malloced in this fn),
314 * *gzlength == length of the deflated data
315 * Returns: error flag (libspectrum_error)
316 */
317 {
318 uLongf gzl = (uLongf)( length * 1.001 ) + 12;
319 int gzret;
320
321 *gzptr = libspectrum_malloc( gzl );
322 gzret = compress2( *gzptr, &gzl, data, length, Z_BEST_COMPRESSION );
323
324 switch (gzret) {
325
326 case Z_OK: /* initialised OK */
327 *gzlength = gzl;
328 return LIBSPECTRUM_ERROR_NONE;
329
330 case Z_MEM_ERROR: /* out of memory */
331 libspectrum_free( *gzptr ); *gzptr = 0;
332 libspectrum_print_error( LIBSPECTRUM_ERROR_MEMORY,
333 "libspectrum_zlib_compress: out of memory" );
334 return LIBSPECTRUM_ERROR_MEMORY;
335
336 case Z_VERSION_ERROR: /* unrecognised version */
337 libspectrum_free( *gzptr ); *gzptr = 0;
338 libspectrum_print_error( LIBSPECTRUM_ERROR_UNKNOWN,
339 "libspectrum_zlib_compress: unknown version" );
340 return LIBSPECTRUM_ERROR_UNKNOWN;
341
342 case Z_BUF_ERROR: /* Not enough space in output buffer.
343 Shouldn't happen */
344 libspectrum_free( *gzptr ); *gzptr = 0;
345 libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
346 "libspectrum_zlib_compress: out of space?" );
347 return LIBSPECTRUM_ERROR_LOGIC;
348
349 default: /* some other error */
350 libspectrum_free( *gzptr ); *gzptr = 0;
351 libspectrum_print_error( LIBSPECTRUM_ERROR_LOGIC,
352 "libspectrum_zlib_compress: unexpected error?" );
353 return LIBSPECTRUM_ERROR_LOGIC;
354 }
355 }
356
357 #endif /* #ifdef HAVE_ZLIB_H */
358