Coverage Report

Created: 2026-02-26 06:41

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-2025 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
47.4k
zip_compression_algorithm_t *_zip_get_compression_algorithm(zip_int32_t method, bool compress) {
94
47.4k
    size_t i;
95
47.4k
    zip_uint16_t real_method = ZIP_CM_ACTUAL(method);
96
97
118k
    for (i = 0; i < implementations_size; i++) {
98
113k
        if (implementations[i].method == real_method) {
99
41.5k
            if (compress) {
100
2.06k
                return implementations[i].compress;
101
2.06k
            }
102
39.5k
            else {
103
39.5k
                return implementations[i].decompress;
104
39.5k
            }
105
41.5k
        }
106
113k
    }
107
108
5.84k
    return NULL;
109
47.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
1.03k
zip_source_t *zip_source_compress(zip_t *za, zip_source_t *src, zip_int32_t method, zip_uint32_t compression_flags) {
119
1.03k
    return compression_source_new(za, src, method, true, compression_flags);
120
1.03k
}
121
122
zip_source_t *
123
45.3k
zip_source_decompress(zip_t *za, zip_source_t *src, zip_int32_t method) {
124
45.3k
    return compression_source_new(za, src, method, false, 0);
125
45.3k
}
126
127
128
46.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
46.4k
    struct context *ctx;
130
46.4k
    zip_source_t *s2;
131
46.4k
    zip_compression_algorithm_t *algorithm = NULL;
132
133
46.4k
    if (src == NULL) {
134
0
        zip_error_set(&za->error, ZIP_ER_INVAL, 0);
135
0
        return NULL;
136
0
    }
137
138
46.4k
    if ((algorithm = _zip_get_compression_algorithm(method, compress)) == NULL) {
139
5.84k
        zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
140
5.84k
        return NULL;
141
5.84k
    }
142
143
40.5k
    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
40.5k
    if ((s2 = zip_source_layered(za, src, compress_callback, ctx)) == NULL) {
149
0
        context_free(ctx);
150
0
        return NULL;
151
0
    }
152
153
40.5k
    return s2;
154
40.5k
}
155
156
157
40.5k
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
40.5k
    struct context *ctx;
159
160
40.5k
    if ((ctx = (struct context *)malloc(sizeof(*ctx))) == NULL) {
161
0
        return NULL;
162
0
    }
163
40.5k
    zip_error_init(&ctx->error);
164
40.5k
    ctx->can_store = compress ? method == ZIP_CM_DEFAULT : false;
165
40.5k
    ctx->algorithm = algorithm;
166
40.5k
    ctx->method = method;
167
40.5k
    ctx->compress = compress;
168
40.5k
    ctx->end_of_input = false;
169
40.5k
    ctx->end_of_stream = false;
170
40.5k
    ctx->is_stored = false;
171
40.5k
    ctx->check_consistency = check_consistency;
172
173
40.5k
    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
40.5k
    return ctx;
180
40.5k
}
181
182
183
static void
184
40.5k
context_free(struct context *ctx) {
185
40.5k
    if (ctx == NULL) {
186
0
        return;
187
0
    }
188
189
40.5k
    ctx->algorithm->deallocate(ctx->ud);
190
40.5k
    zip_error_fini(&ctx->error);
191
192
40.5k
    free(ctx);
193
40.5k
}
194
195
196
static zip_int64_t
197
80.6k
compress_read(zip_source_t *src, struct context *ctx, void *data, zip_uint64_t len) {
198
80.6k
    zip_compression_status_t ret;
199
80.6k
    bool end;
200
80.6k
    zip_int64_t n;
201
80.6k
    zip_uint64_t out_offset;
202
80.6k
    zip_uint64_t out_len;
203
204
80.6k
    if (zip_error_code_zip(&ctx->error) != ZIP_ER_OK) {
205
870
        return -1;
206
870
    }
207
208
79.7k
    if (len == 0 || ctx->end_of_stream) {
209
3.76k
        return 0;
210
3.76k
    }
211
212
76.0k
    out_offset = 0;
213
214
76.0k
    end = false;
215
257k
    while (!end && out_offset < len) {
216
181k
        out_len = len - out_offset;
217
181k
        ret = ctx->algorithm->process(ctx->ud, (zip_uint8_t *)data + out_offset, &out_len);
218
219
181k
        if (ret != ZIP_COMPRESSION_ERROR) {
220
167k
            out_offset += out_len;
221
167k
        }
222
223
181k
        switch (ret) {
224
5.77k
        case ZIP_COMPRESSION_END:
225
5.77k
            ctx->end_of_stream = true;
226
227
5.77k
            if (!ctx->end_of_input) {
228
4.74k
                n = zip_source_read(src, ctx->buffer, 1);
229
4.74k
                if (n < 0) {
230
864
                    zip_error_set_from_source(&ctx->error, src);
231
864
                    end = true;
232
864
                    break;
233
864
                }
234
3.87k
                else if (n == 0) {
235
3.10k
                    ctx->end_of_input = true;
236
3.10k
                    n = ctx->algorithm->end_of_input(ctx->ud) ? 1 : 0;
237
3.10k
                }
238
239
3.87k
                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.87k
            }
246
247
4.91k
            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.91k
            if (ctx->can_store && (zip_uint64_t)ctx->first_read <= out_offset) {
254
289
                ctx->is_stored = true;
255
289
                ctx->size = (zip_uint64_t)ctx->first_read;
256
289
                (void)memcpy_s(data, ctx->size, ctx->buffer, ctx->size);
257
289
                return (zip_int64_t)ctx->size;
258
289
            }
259
4.62k
            end = true;
260
4.62k
            break;
261
262
79.2k
        case ZIP_COMPRESSION_OK:
263
79.2k
            break;
264
265
82.6k
        case ZIP_COMPRESSION_NEED_DATA:
266
82.6k
            if (ctx->end_of_input) {
267
                /* TODO: error: stream not ended, but no more input */
268
10.0k
                end = true;
269
10.0k
                break;
270
10.0k
            }
271
272
72.5k
            if ((n = zip_source_read(src, ctx->buffer, sizeof(ctx->buffer))) < 0) {
273
7.20k
                zip_error_set_from_source(&ctx->error, src);
274
7.20k
                end = true;
275
7.20k
                break;
276
7.20k
            }
277
65.3k
            else if (n == 0) {
278
7.51k
                ctx->end_of_input = true;
279
7.51k
                ctx->algorithm->end_of_input(ctx->ud);
280
7.51k
                if (ctx->first_read < 0) {
281
2.38k
                    ctx->first_read = 0;
282
2.38k
                }
283
7.51k
            }
284
57.8k
            else {
285
57.8k
                if (ctx->first_read >= 0) {
286
                    /* we overwrote a previously filled ctx->buffer */
287
30.6k
                    ctx->can_store = false;
288
30.6k
                }
289
27.1k
                else {
290
27.1k
                    ctx->first_read = n;
291
27.1k
                }
292
293
57.8k
                ctx->algorithm->input(ctx->ud, ctx->buffer, (zip_uint64_t)n);
294
57.8k
            }
295
65.3k
            break;
296
297
65.3k
        case ZIP_COMPRESSION_ERROR:
298
            /* error set by algorithm */
299
14.1k
            if (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) {
300
0
                zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
301
0
            }
302
14.1k
            end = true;
303
14.1k
            break;
304
181k
        }
305
181k
    }
306
307
75.7k
    if (out_offset > 0) {
308
46.8k
        ctx->can_store = false;
309
46.8k
        ctx->size += out_offset;
310
46.8k
        return (zip_int64_t)out_offset;
311
46.8k
    }
312
313
28.9k
    return (zip_error_code_zip(&ctx->error) == ZIP_ER_OK) ? 0 : -1;
314
75.7k
}
315
316
317
static zip_int64_t
318
265k
compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
319
265k
    struct context *ctx;
320
321
265k
    ctx = (struct context *)ud;
322
323
265k
    switch (cmd) {
324
33.5k
    case ZIP_SOURCE_OPEN: {
325
33.5k
        zip_stat_t st;
326
33.5k
        zip_file_attributes_t attributes;
327
        
328
33.5k
        ctx->size = 0;
329
33.5k
        ctx->end_of_input = false;
330
33.5k
        ctx->end_of_stream = false;
331
33.5k
        ctx->is_stored = false;
332
33.5k
        ctx->first_read = -1;
333
        
334
33.5k
        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
33.5k
        if (!ctx->algorithm->start(ctx->ud, &st, &attributes)) {
340
0
            return -1;
341
0
        }
342
343
33.5k
        return 0;
344
33.5k
    }
345
346
80.6k
    case ZIP_SOURCE_READ:
347
80.6k
        return compress_read(src, ctx, data, len);
348
349
33.5k
    case ZIP_SOURCE_CLOSE:
350
33.5k
        if (!ctx->algorithm->end(ctx->ud)) {
351
0
            return -1;
352
0
        }
353
33.5k
        return 0;
354
355
10.7k
    case ZIP_SOURCE_STAT: {
356
10.7k
        zip_stat_t *st;
357
358
10.7k
        st = (zip_stat_t *)data;
359
360
10.7k
        if (ctx->compress) {
361
1.52k
            if (ctx->end_of_stream) {
362
1.03k
                st->comp_method = ctx->is_stored ? ZIP_CM_STORE : ZIP_CM_ACTUAL(ctx->method);
363
1.03k
                st->comp_size = ctx->size;
364
1.03k
                st->valid |= ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD;
365
1.03k
            }
366
490
            else {
367
490
                st->valid &= ~(ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD);
368
490
            }
369
1.52k
        }
370
9.24k
        else {
371
9.24k
            st->comp_method = ZIP_CM_STORE;
372
9.24k
            st->valid |= ZIP_STAT_COMP_METHOD;
373
9.24k
            st->valid &= ~ZIP_STAT_COMP_SIZE;
374
9.24k
            if (ctx->end_of_stream) {
375
3.88k
                st->size = ctx->size;
376
3.88k
                st->valid |= ZIP_STAT_SIZE;
377
3.88k
            }
378
9.24k
        }
379
10.7k
    }
380
10.7k
        return 0;
381
382
23.2k
    case ZIP_SOURCE_ERROR:
383
23.2k
        return zip_error_to_data(&ctx->error, data, len);
384
385
40.5k
    case ZIP_SOURCE_FREE:
386
40.5k
        context_free(ctx);
387
40.5k
        return 0;
388
389
2.06k
    case ZIP_SOURCE_GET_FILE_ATTRIBUTES: {
390
2.06k
        zip_file_attributes_t *attributes = (zip_file_attributes_t *)data;
391
392
2.06k
        if (len < sizeof(*attributes)) {
393
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
394
0
            return -1;
395
0
        }
396
397
2.06k
        attributes->valid |= ZIP_FILE_ATTRIBUTES_VERSION_NEEDED | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
398
2.06k
        attributes->version_needed = ctx->algorithm->version_needed;
399
2.06k
        attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
400
2.06k
        attributes->general_purpose_bit_flags = (ctx->is_stored ? 0 : ctx->algorithm->general_purpose_bit_flags(ctx->ud));
401
402
2.06k
        return sizeof(*attributes);
403
2.06k
    }
404
405
40.5k
    case ZIP_SOURCE_SUPPORTS:
406
40.5k
        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
265k
    }
411
265k
}