Coverage Report

Created: 2025-12-03 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzip/lib/zip_source_compress.c
Line
Count
Source
1
/*
2
  zip_source_compress.c -- (de)compression routines
3
  Copyright (C) 2017-2023 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 <info@libzip.org>
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 "zipint.h"
35
36
#include <stdlib.h>
37
#include <string.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
    bool check_consistency;
48
    zip_int32_t method;
49
50
    zip_uint64_t size;
51
    zip_int64_t first_read;
52
    zip_uint8_t buffer[BUFSIZE];
53
54
    zip_compression_algorithm_t *algorithm;
55
    void *ud;
56
};
57
58
59
struct implementation {
60
    zip_uint16_t method;
61
    zip_compression_algorithm_t *compress;
62
    zip_compression_algorithm_t *decompress;
63
};
64
65
static struct implementation implementations[] = {
66
    {ZIP_CM_DEFLATE, &zip_algorithm_deflate_compress, &zip_algorithm_deflate_decompress},
67
#if defined(HAVE_LIBBZ2)
68
    {ZIP_CM_BZIP2, &zip_algorithm_bzip2_compress, &zip_algorithm_bzip2_decompress},
69
#endif
70
#if defined(HAVE_LIBLZMA)
71
    {ZIP_CM_LZMA, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
72
    /*  Disabled - because 7z isn't able to unpack ZIP+LZMA2
73
        archives made this way - and vice versa.
74
75
        {ZIP_CM_LZMA2, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
76
    */
77
    {ZIP_CM_XZ, &zip_algorithm_xz_compress, &zip_algorithm_xz_decompress},
78
#endif
79
#if defined(HAVE_LIBZSTD)
80
    {ZIP_CM_ZSTD, &zip_algorithm_zstd_compress, &zip_algorithm_zstd_decompress},
81
#endif
82
83
};
84
85
static size_t implementations_size = sizeof(implementations) / sizeof(implementations[0]);
86
87
static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags);
88
static zip_int64_t compress_callback(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
89
static void context_free(struct context *ctx);
90
static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm, bool check_consistency);
91
static zip_int64_t compress_read(zip_source_t *, struct context *, void *, zip_uint64_t);
92
93
43.4k
zip_compression_algorithm_t *_zip_get_compression_algorithm(zip_int32_t method, bool compress) {
94
43.4k
    size_t i;
95
43.4k
    zip_uint16_t real_method = ZIP_CM_ACTUAL(method);
96
97
108k
    for (i = 0; i < implementations_size; i++) {
98
102k
        if (implementations[i].method == real_method) {
99
38.0k
            if (compress) {
100
1.89k
                return implementations[i].compress;
101
1.89k
            }
102
36.1k
            else {
103
36.1k
                return implementations[i].decompress;
104
36.1k
            }
105
38.0k
        }
106
102k
    }
107
108
5.35k
    return NULL;
109
43.4k
}
110
111
0
ZIP_EXTERN int zip_compression_method_supported(zip_int32_t method, int compress) {
112
0
    if (method == ZIP_CM_STORE) {
113
0
        return 1;
114
0
    }
115
0
    return _zip_get_compression_algorithm(method, compress) != NULL;
116
0
}
117
118
947
zip_source_t *zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, zip_uint32_t compression_flags) {
119
947
    return compression_source_new(za, src, method, true, compression_flags);
120
947
}
121
122
zip_source_t *
123
41.5k
zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) {
124
41.5k
    return compression_source_new(za, src, method, false, 0);
125
41.5k
}
126
127
128
42.4k
static zip_source_t *compression_source_new(zip_t *za, zip_source_t *src, zip_int32_t method, bool compress, zip_uint32_t compression_flags) {
129
42.4k
    struct context *ctx;
130
42.4k
    zip_source_t *s2;
131
42.4k
    zip_compression_algorithm_t *algorithm = NULL;
132
133
42.4k
    if (src == NULL) {
134
0
        zip_error_set(&za->error, ZIP_ER_INVAL, 0);
135
0
        return NULL;
136
0
    }
137
138
42.4k
    if ((algorithm = _zip_get_compression_algorithm(method, compress)) == NULL) {
139
5.35k
        zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
140
5.35k
        return NULL;
141
5.35k
    }
142
143
37.1k
    if ((ctx = context_new(method, compress, compression_flags, algorithm, za->open_flags & ZIP_CHECKCONS)) == NULL) {
144
0
        zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
145
0
        return NULL;
146
0
    }
147
148
37.1k
    if ((s2 = zip_source_layered(za, src, compress_callback, ctx)) == NULL) {
149
0
        context_free(ctx);
150
0
        return NULL;
151
0
    }
152
153
37.1k
    return s2;
154
37.1k
}
155
156
157
37.1k
static struct context *context_new(zip_int32_t method, bool compress, zip_uint32_t compression_flags, zip_compression_algorithm_t *algorithm, bool check_consistency) {
158
37.1k
    struct context *ctx;
159
160
37.1k
    if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) {
161
0
        return NULL;
162
0
    }
163
37.1k
    zip_error_init(&ctx->error);
164
37.1k
    ctx->can_store = compress ? method == ZIP_CM_DEFAULT : false;
165
37.1k
    ctx->algorithm = algorithm;
166
37.1k
    ctx->method = method;
167
37.1k
    ctx->compress = compress;
168
37.1k
    ctx->end_of_input = false;
169
37.1k
    ctx->end_of_stream = false;
170
37.1k
    ctx->is_stored = false;
171
37.1k
    ctx->check_consistency = check_consistency;
172
173
37.1k
    if ((ctx->ud = ctx->algorithm->allocate(ZIP_CM_ACTUAL(method), compression_flags, &ctx->error)) == NULL) {
174
0
        zip_error_fini(&ctx->error);
175
0
        free(ctx);
176
0
        return NULL;
177
0
    }
178
179
37.1k
    return ctx;
180
37.1k
}
181
182
183
static void
184
37.1k
context_free(struct context *ctx) {
185
37.1k
    if (ctx == NULL) {
186
0
        return;
187
0
    }
188
189
37.1k
    ctx->algorithm->deallocate(ctx->ud);
190
37.1k
    zip_error_fini(&ctx->error);
191
192
37.1k
    free(ctx);
193
37.1k
}
194
195
196
static zip_int64_t
197
65.0k
compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t len) {
198
65.0k
    zip_compression_status_t ret;
199
65.0k
    bool end;
200
65.0k
    zip_int64_t n;
201
65.0k
    zip_uint64_t out_offset;
202
65.0k
    zip_uint64_t out_len;
203
204
65.0k
    if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) {
205
1.05k
        return -1;
206
1.05k
    }
207
208
63.9k
    if (len == 0 || ctx->end_of_stream) {
209
3.18k
        return 0;
210
3.18k
    }
211
212
60.8k
    out_offset = 0;
213
214
60.8k
    end = false;
215
219k
    while (!end && out_offset < len) {
216
159k
        out_len = len - out_offset;
217
159k
        ret = ctx->algorithm->process(ctx->ud, (zip_uint8_t *)data + out_offset, &out_len);
218
219
159k
        if (ret != ZIP_COMPRESSION_ERROR) {
220
146k
            out_offset += out_len;
221
146k
        }
222
223
159k
        switch (ret) {
224
5.21k
        case ZIP_COMPRESSION_END:
225
5.21k
            ctx->end_of_stream = true;
226
227
5.21k
            if (!ctx->end_of_input) {
228
4.26k
                n = zip_source_read(src, ctx->buffer, 1);
229
4.26k
                if (n < 0) {
230
966
                    zip_error_set_from_source(&ctx->error, src);
231
966
                    end = true;
232
966
                    break;
233
966
                }
234
3.29k
                else if (n == 0) {
235
2.78k
                    ctx->end_of_input = true;
236
2.78k
                    n = ctx->algorithm->end_of_input(ctx->ud) ? 1 : 0;
237
2.78k
                }
238
239
3.29k
                if (n > 0 && ctx->check_consistency) {
240
                    /* garbage after stream, or compression ended before all data read */
241
0
                    zip_error_set(&ctx->error, ZIP_ER_INCONS, ZIP_ER_DETAIL_COMPRESSED_DATA_TRAILING_GARBAGE);
242
0
                    end = true;
243
0
                    break;
244
0
                }
245
3.29k
            }
246
247
4.24k
            if (ctx->first_read < 0) {
248
                /* we got end of processed stream before reading any input data */
249
0
                zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
250
0
                end = true;
251
0
                break;
252
0
            }
253
4.24k
            if (ctx->can_store && (zip_uint64_t)ctx->first_read <= out_offset) {
254
269
                ctx->is_stored = true;
255
269
                ctx->size = (zip_uint64_t)ctx->first_read;
256
269
                (void)memcpy_s(data, ctx->size, ctx->buffer, ctx->size);
257
269
                return (zip_int64_t)ctx->size;
258
269
            }
259
3.97k
            end = true;
260
3.97k
            break;
261
262
63.3k
        case ZIP_COMPRESSION_OK:
263
63.3k
            break;
264
265
77.9k
        case ZIP_COMPRESSION_NEED_DATA:
266
77.9k
            if (ctx->end_of_input) {
267
                /* TODO: error: stream not ended, but no more input */
268
7.62k
                end = true;
269
7.62k
                break;
270
7.62k
            }
271
272
70.3k
            if ((n = zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
273
6.67k
                zip_error_set_from_source(&ctx->error, src);
274
6.67k
                end = true;
275
6.67k
                break;
276
6.67k
            }
277
63.7k
            else if (n == 0) {
278
6.73k
                ctx->end_of_input = true;
279
6.73k
                ctx->algorithm->end_of_input(ctx->ud);
280
6.73k
                if (ctx->first_read < 0) {
281
2.41k
                    ctx->first_read = 0;
282
2.41k
                }
283
6.73k
            }
284
56.9k
            else {
285
56.9k
                if (ctx->first_read >= 0) {
286
                    /* we overwrote a previously filled ctx->buffer */
287
32.6k
                    ctx->can_store = false;
288
32.6k
                }
289
24.3k
                else {
290
24.3k
                    ctx->first_read = n;
291
24.3k
                }
292
293
56.9k
                ctx->algorithm->input(ctx->ud, ctx->buffer, (zip_uint64_t)n);
294
56.9k
            }
295
63.7k
            break;
296
297
63.7k
        case ZIP_COMPRESSION_ERROR:
298
            /* error set by algorithm */
299
12.5k
            if (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) {
300
0
                zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
301
0
            }
302
12.5k
            end = true;
303
12.5k
            break;
304
159k
        }
305
159k
    }
306
307
60.5k
    if (out_offset > 0) {
308
34.5k
        ctx->can_store = false;
309
34.5k
        ctx->size += out_offset;
310
34.5k
        return (zip_int64_t)out_offset;
311
34.5k
    }
312
313
26.0k
    return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1;
314
60.5k
}
315
316
317
static zip_int64_t
318
232k
compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
319
232k
    struct context *ctx;
320
321
232k
    ctx = (struct context *)ud;
322
323
232k
    switch (cmd) {
324
30.2k
    case ZIP_SOURCE_OPEN: {
325
30.2k
        zip_stat_t st;
326
30.2k
        zip_file_attributes_t attributes;
327
        
328
30.2k
        ctx->size = 0;
329
30.2k
        ctx->end_of_input = false;
330
30.2k
        ctx->end_of_stream = false;
331
30.2k
        ctx->is_stored = false;
332
30.2k
        ctx->first_read = -1;
333
        
334
30.2k
        if (zip_source_stat(src, &st) < 0 || zip_source_get_file_attributes(src, &attributes) < 0) {
335
0
            zip_error_set_from_source(&ctx->error, src);
336
0
            return -1;
337
0
        }
338
339
30.2k
        if (!ctx->algorithm->start(ctx->ud, &st, &attributes)) {
340
0
            return -1;
341
0
        }
342
343
30.2k
        return 0;
344
30.2k
    }
345
346
65.0k
    case ZIP_SOURCE_READ:
347
65.0k
        return compress_read(src, ctx, data, len);
348
349
30.2k
    case ZIP_SOURCE_CLOSE:
350
30.2k
        if (!ctx->algorithm->end(ctx->ud)) {
351
0
            return -1;
352
0
        }
353
30.2k
        return 0;
354
355
9.44k
    case ZIP_SOURCE_STAT: {
356
9.44k
        zip_stat_t *st;
357
358
9.44k
        st = (zip_stat_t *)data;
359
360
9.44k
        if (ctx->compress) {
361
1.41k
            if (ctx->end_of_stream) {
362
947
                st->comp_method = ctx->is_stored ? ZIP_CM_STORE : ZIP_CM_ACTUAL(ctx->method);
363
947
                st->comp_size = ctx->size;
364
947
                st->valid |= ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD;
365
947
            }
366
471
            else {
367
471
                st->valid &= ~(ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD);
368
471
            }
369
1.41k
        }
370
8.02k
        else {
371
8.02k
            st->comp_method = ZIP_CM_STORE;
372
8.02k
            st->valid |= ZIP_STAT_COMP_METHOD;
373
8.02k
            st->valid &= ~ZIP_STAT_COMP_SIZE;
374
8.02k
            if (ctx->end_of_stream) {
375
3.29k
                st->size = ctx->size;
376
3.29k
                st->valid |= ZIP_STAT_SIZE;
377
3.29k
            }
378
8.02k
        }
379
9.44k
    }
380
9.44k
        return 0;
381
382
21.2k
    case ZIP_SOURCE_ERROR:
383
21.2k
        return zip_error_to_data(&ctx->error, data, len);
384
385
37.1k
    case ZIP_SOURCE_FREE:
386
37.1k
        context_free(ctx);
387
37.1k
        return 0;
388
389
1.89k
    case ZIP_SOURCE_GET_FILE_ATTRIBUTES: {
390
1.89k
        zip_file_attributes_t *attributes = (zip_file_attributes_t *)data;
391
392
1.89k
        if (len < sizeof(*attributes)) {
393
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
394
0
            return -1;
395
0
        }
396
397
1.89k
        attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
398
1.89k
        attributes->version_needed = ctx->algorithm->version_needed;
399
1.89k
        attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
400
1.89k
        attributes->general_purpose_bit_flags = (ctx->is_stored ? 0 : ctx->algorithm->general_purpose_bit_flags(ctx->ud));
401
402
1.89k
        return sizeof(*attributes);
403
1.89k
    }
404
405
37.1k
    case ZIP_SOURCE_SUPPORTS:
406
37.1k
        return ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS_REOPEN, -1);
407
408
0
    default:
409
0
        return zip_source_pass_to_lower_layer(src, data, len, cmd);
410
232k
    }
411
232k
}