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