1 /*
2   zip_source_compress.c -- (de)compression routines
3   Copyright (C) 2017-2019 Dieter Baron and Thomas Klausner
4 
5   This file is part of libzip, a library to manipulate ZIP archives.
6   The authors can be contacted at <libzip@nih.at>
7 
8   Redistribution and use in source and binary forms, with or without
9   modification, are permitted provided that the following conditions
10   are met:
11   1. Redistributions of source code must retain the above copyright
12      notice, this list of conditions and the following disclaimer.
13   2. Redistributions in binary form must reproduce the above copyright
14      notice, this list of conditions and the following disclaimer in
15      the documentation and/or other materials provided with the
16      distribution.
17   3. The names of the authors may not be used to endorse or promote
18      products derived from this software without specific prior
19      written permission.
20 
21   THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
22   OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
25   DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
27   GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
29   IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
31   IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "zipint.h"
38 
39 struct context {
40     zip_error_t error;
41 
42     bool end_of_input;
43     bool end_of_stream;
44     bool can_store;
45     bool is_stored; /* only valid if end_of_stream is true */
46     bool compress;
47     zip_int32_t method;
48 
49     zip_uint64_t size;
50     zip_int64_t first_read;
51     zip_uint8_t buffer[BUFSIZE];
52 
53     zip_compression_algorithm_t *algorithm;
54     void *ud;
55 };
56 
57 
58 struct implementation {
59     zip_uint16_t method;
60     zip_compression_algorithm_t *compress;
61     zip_compression_algorithm_t *decompress;
62 };
63 
64 static struct implementation implementations[] = {
65     {ZIP_CM_DEFLATE, &zip_algorithm_deflate_compress, &zip_algorithm_deflate_decompress},
66 #if defined(HAVE_LIBBZ2)
67     {ZIP_CM_BZIP2, &zip_algorithm_bzip2_compress, &zip_algorithm_bzip2_decompress},
68 #endif
69 #if defined(HAVE_LIBLZMA)
70     /*  Disabled - because 7z isn't able to unpack ZIP+LZMA ZIP+LZMA2
71 	archives made this way - and vice versa.
72 
73 	{ZIP_CM_LZMA, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
74 	{ZIP_CM_LZMA2, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
75     */
76     {ZIP_CM_XZ, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
77 #endif
78 
79 };
80 
81 static size_t implementations_size = sizeof(implementations) / sizeof(implementations[0]);
82 
83 static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, int compression_flags);
84 static zip_int64_t compress_callback(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
85 static void context_free(struct context *ctx);
86 static struct context *context_new(zip_int32_t method, bool compress, int compression_flags, zip_compression_algorithm_t *algorithm);
87 static zip_int64_t compress_read(zip_source_t *, struct context *, void *, zip_uint64_t);
88 
89 static zip_compression_algorithm_t *
get_algorithm(zip_int32_t method,bool compress)90 get_algorithm(zip_int32_t method, bool compress) {
91     size_t i;
92     zip_uint16_t real_method = ZIP_CM_ACTUAL(method);
93 
94     for (i = 0; i < implementations_size; i++) {
95 	if (implementations[i].method == real_method) {
96 	    if (compress) {
97 		return implementations[i].compress;
98 	    }
99 	    else {
100 		return implementations[i].decompress;
101 	    }
102 	}
103     }
104 
105     return NULL;
106 }
107 
108 ZIP_EXTERN int
zip_compression_method_supported(zip_int32_t method,int compress)109 zip_compression_method_supported(zip_int32_t method, int compress) {
110     if (method == ZIP_CM_STORE) {
111 	return 1;
112     }
113     return get_algorithm(method, compress) != NULL;
114 }
115 
116 zip_source_t *
zip_source_compress(zip_t * za,zip_source_t * src,zip_int32_t method,int compression_flags)117 zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, int compression_flags) {
118     return compression_source_new(za, src, method, true, compression_flags);
119 }
120 
121 zip_source_t *
zip_source_decompress(zip_t * za,zip_source_t * src,zip_int32_t method)122 zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) {
123     return compression_source_new(za, src, method, false, 0);
124 }
125 
126 
127 static zip_source_t *
compression_source_new(zip_t * za,zip_source_t * src,zip_int32_t method,bool compress,int compression_flags)128 compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, int compression_flags) {
129     struct context *ctx;
130     zip_source_t *s2;
131     zip_compression_algorithm_t *algorithm = NULL;
132 
133     if (src == NULL) {
134 	zip_error_set(&za->error, ZIP_ER_INVAL, 0);
135 	return NULL;
136     }
137 
138     if ((algorithm = get_algorithm(method, compress)) == NULL) {
139 	zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
140 	return NULL;
141     }
142 
143     if ((ctx = context_new(method, compress, compression_flags, algorithm)) == NULL) {
144 	zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
145 	return NULL;
146     }
147 
148     if ((s2 = zip_source_layered(za, src, compress_callback, ctx)) == NULL) {
149 	context_free(ctx);
150 	return NULL;
151     }
152 
153     return s2;
154 }
155 
156 
157 static struct context *
context_new(zip_int32_t method,bool compress,int compression_flags,zip_compression_algorithm_t * algorithm)158 context_new(zip_int32_t method, bool compress, int compression_flags, zip_compression_algorithm_t *algorithm) {
159     struct context *ctx;
160 
161     if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) {
162 	return NULL;
163     }
164     zip_error_init(&ctx->error);
165     ctx->can_store = compress ? ZIP_CM_IS_DEFAULT(method) : false;
166     ctx->algorithm = algorithm;
167     ctx->method = method;
168     ctx->compress = compress;
169     ctx->end_of_input = false;
170     ctx->end_of_stream = false;
171     ctx->is_stored = false;
172 
173     if ((ctx->ud = ctx->algorithm->allocate(ZIP_CM_ACTUAL(method), compression_flags, &ctx->error)) == NULL) {
174 	zip_error_fini(&ctx->error);
175 	free(ctx);
176 	return NULL;
177     }
178 
179     return ctx;
180 }
181 
182 
183 static void
context_free(struct context * ctx)184 context_free(struct context *ctx) {
185     if (ctx == NULL) {
186 	return;
187     }
188 
189     ctx->algorithm->deallocate(ctx->ud);
190     zip_error_fini(&ctx->error);
191 
192     free(ctx);
193 }
194 
195 
196 static zip_int64_t
compress_read(zip_source_t * src,struct context * ctx,void * data,zip_uint64_t len)197 compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t len) {
198     zip_compression_status_t ret;
199     bool end;
200     zip_int64_t n;
201     zip_uint64_t out_offset;
202     zip_uint64_t out_len;
203 
204     if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) {
205 	return -1;
206     }
207 
208     if (len == 0 || ctx->end_of_stream) {
209 	return 0;
210     }
211 
212     out_offset = 0;
213 
214     end = false;
215     while (!end && out_offset < len) {
216 	out_len = len - out_offset;
217 	ret = ctx->algorithm->process(ctx->ud, (zip_uint8_t *)data + out_offset, &out_len);
218 
219 	if (ret != ZIP_COMPRESSION_ERROR) {
220 	    out_offset += out_len;
221 	}
222 
223 	switch (ret) {
224 	case ZIP_COMPRESSION_END:
225 	    ctx->end_of_stream = true;
226 
227 	    if (!ctx->end_of_input) {
228 		/* TODO: garbage after stream, or compression ended before all data read */
229 	    }
230 
231 	    if (ctx->first_read < 0) {
232 		/* we got end of processed stream before reading any input data */
233 		zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
234 		end = true;
235 		break;
236 	    }
237 	    if (ctx->can_store && (zip_uint64_t)ctx->first_read <= out_offset) {
238 		ctx->is_stored = true;
239 		ctx->size = (zip_uint64_t)ctx->first_read;
240 		memcpy(data, ctx->buffer, ctx->size);
241 		return (zip_int64_t)ctx->size;
242 	    }
243 	    end = true;
244 	    break;
245 
246 	case ZIP_COMPRESSION_OK:
247 	    break;
248 
249 	case ZIP_COMPRESSION_NEED_DATA:
250 	    if (ctx->end_of_input) {
251 		/* TODO: error: stream not ended, but no more input */
252 		end = true;
253 		break;
254 	    }
255 
256 	    if ((n = zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
257 		_zip_error_set_from_source(&ctx->error, src);
258 		end = true;
259 		break;
260 	    }
261 	    else if (n == 0) {
262 		ctx->end_of_input = true;
263 		ctx->algorithm->end_of_input(ctx->ud);
264 		if (ctx->first_read < 0) {
265 		    ctx->first_read = 0;
266 		}
267 	    }
268 	    else {
269 		if (ctx->first_read >= 0) {
270 		    /* we overwrote a previously filled ctx->buffer */
271 		    ctx->can_store = false;
272 		}
273 		else {
274 		    ctx->first_read = n;
275 		}
276 
277 		ctx->algorithm->input(ctx->ud, ctx->buffer, (zip_uint64_t)n);
278 	    }
279 	    break;
280 
281 	case ZIP_COMPRESSION_ERROR:
282 	    /* error set by algorithm */
283 	    if (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) {
284 		zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
285 	    }
286 	    end = true;
287 	    break;
288 	}
289     }
290 
291     if (out_offset > 0) {
292 	ctx->can_store = false;
293 	ctx->size += out_offset;
294 	return (zip_int64_t)out_offset;
295     }
296 
297     return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1;
298 }
299 
300 
301 static zip_int64_t
compress_callback(zip_source_t * src,void * ud,void * data,zip_uint64_t len,zip_source_cmd_t cmd)302 compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
303     struct context *ctx;
304 
305     ctx = (struct context *)ud;
306 
307     switch (cmd) {
308     case ZIP_SOURCE_OPEN:
309 	ctx->size = 0;
310 	ctx->end_of_input = false;
311 	ctx->end_of_stream = false;
312 	ctx->is_stored = false;
313 	ctx->first_read = -1;
314 
315 	if (!ctx->algorithm->start(ctx->ud)) {
316 	    return -1;
317 	}
318 
319 	return 0;
320 
321     case ZIP_SOURCE_READ:
322 	return compress_read(src, ctx, data, len);
323 
324     case ZIP_SOURCE_CLOSE:
325 	if (!ctx->algorithm->end(ctx->ud)) {
326 	    return -1;
327 	}
328 	return 0;
329 
330     case ZIP_SOURCE_STAT: {
331 	zip_stat_t *st;
332 
333 	st = (zip_stat_t *)data;
334 
335 	if (ctx->compress) {
336 	    if (ctx->end_of_stream) {
337 		st->comp_method = ctx->is_stored ? ZIP_CM_STORE : ZIP_CM_ACTUAL(ctx->method);
338 		st->comp_size = ctx->size;
339 		st->valid |= ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD;
340 	    }
341 	    else {
342 		st->valid &= ~(ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD);
343 	    }
344 	}
345 	else {
346 	    st->comp_method = ZIP_CM_STORE;
347 	    st->valid |= ZIP_STAT_COMP_METHOD;
348 	    if (ctx->end_of_stream) {
349 		st->size = ctx->size;
350 		st->valid |= ZIP_STAT_SIZE;
351 	    }
352 	    else {
353 		st->valid &= ~ZIP_STAT_SIZE;
354 	    }
355 	}
356     }
357 	return 0;
358 
359     case ZIP_SOURCE_ERROR:
360 	return zip_error_to_data(&ctx->error, data, len);
361 
362     case ZIP_SOURCE_FREE:
363 	context_free(ctx);
364 	return 0;
365 
366     case ZIP_SOURCE_GET_FILE_ATTRIBUTES: {
367 	zip_file_attributes_t *attributes = (zip_file_attributes_t *)data;
368 
369 	if (len < sizeof(*attributes)) {
370 	    zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
371 	    return -1;
372 	}
373 
374 	attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
375 	attributes->version_needed = ctx->algorithm->version_needed;
376 	attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
377 	attributes->general_purpose_bit_flags = (ctx->is_stored ? 0 : ctx->algorithm->general_purpose_bit_flags(ctx->ud));
378 
379 	return sizeof(*attributes);
380     }
381 
382     case ZIP_SOURCE_SUPPORTS:
383 	return ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1);
384 
385     default:
386 	zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
387 	return -1;
388     }
389 }
390