Coverage Report

Created: 2026-10-14 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzip/lib/zip_source_zip_new.c
Line
Count
Source
1
/*
2
  zip_source_zip_new.c -- prepare data structures for zip_fopen/zip_source_zip
3
  Copyright (C) 2012-2024 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
35
#include <stdlib.h>
36
37
#include "zipint.h"
38
39
static void _zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de);
40
41
0
ZIP_EXTERN zip_source_t *zip_source_zip_file(zip_t *za, zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_int64_t len, const char *password) {
42
0
    return zip_source_zip_file_create(srcza, srcidx, flags, start, len, password, &za->error);
43
0
}
44
45
46
10.4k
ZIP_EXTERN zip_source_t *zip_source_zip_file_create(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_int64_t len, const char *password, zip_error_t *error) {
47
    /* TODO: We need to make sure that the returned source is invalidated when srcza is closed. */
48
10.4k
    zip_source_t *src, *s2;
49
10.4k
    zip_stat_t st;
50
10.4k
    zip_file_attributes_t attributes;
51
10.4k
    zip_dirent_t *de;
52
10.4k
    bool partial_data, needs_crc, encrypted, needs_decrypt, compressed, needs_decompress, changed_data, have_size, have_comp_size;
53
10.4k
    zip_flags_t stat_flags;
54
10.4k
    zip_int64_t data_len;
55
10.4k
    bool take_ownership = false;
56
10.4k
    bool empty_data = false;
57
58
10.4k
    if (srcza == NULL || srcidx >= srcza->nentry || len < -1) {
59
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
60
0
        return NULL;
61
0
    }
62
63
10.4k
    if (flags & ZIP_FL_ENCRYPTED) {
64
0
        flags |= ZIP_FL_COMPRESSED;
65
0
    }
66
67
10.4k
    changed_data = false;
68
10.4k
    if ((flags & ZIP_FL_UNCHANGED) == 0) {
69
10.4k
        zip_entry_t *entry = srcza->entry + srcidx;
70
10.4k
        if (ZIP_ENTRY_DATA_CHANGED(entry)) {
71
0
            if ((flags & ZIP_FL_COMPRESSED) || !zip_source_supports_reopen(entry->source)) {
72
0
                zip_error_set(error, ZIP_ER_CHANGED, 0);
73
0
                return NULL;
74
0
            }
75
76
0
            changed_data = true;
77
0
        }
78
10.4k
        else if (entry->deleted) {
79
0
            zip_error_set(error, ZIP_ER_CHANGED, 0);
80
0
            return NULL;
81
0
        }
82
10.4k
    }
83
84
10.4k
    stat_flags = flags;
85
10.4k
    if (!changed_data) {
86
10.4k
        stat_flags |= ZIP_FL_UNCHANGED;
87
10.4k
    }
88
89
10.4k
    if (zip_stat_index(srcza, srcidx, stat_flags, &st) < 0) {
90
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
91
0
        return NULL;
92
0
    }
93
94
10.4k
    if ((start > 0 || len >= 0) && (flags & ZIP_FL_COMPRESSED)) {
95
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
96
0
        return NULL;
97
0
    }
98
99
    /* Check for overflow of end. */
100
10.4k
    if (len > 0 && start > 0 && start + len < start) {
101
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
102
0
        return NULL;
103
0
    }
104
105
10.4k
    have_size = (st.valid & ZIP_STAT_SIZE) != 0;
106
10.4k
    if (have_size) {
107
        /* Check that start and end are within the size of the file. */
108
10.4k
        if (start > st.size) {
109
0
            zip_error_set(error, ZIP_ER_INVAL, 0);
110
0
            return NULL;
111
0
        }
112
10.4k
        if (len > 0 && start + len > st.size) {
113
0
            zip_error_set(error, ZIP_ER_INVAL, 0);
114
0
            return NULL;
115
0
        }
116
10.4k
    }
117
118
10.4k
    if (len == -1) {
119
10.4k
        if (have_size) {
120
10.4k
            if (st.size - start > ZIP_INT64_MAX) {
121
0
                zip_error_set(error, ZIP_ER_INVAL, 0);
122
0
                return NULL;
123
0
            }
124
10.4k
            data_len = (zip_int64_t)(st.size - start);
125
10.4k
        }
126
0
        else {
127
0
            data_len = -1;
128
0
        }
129
10.4k
    }
130
0
    else {
131
0
        data_len = len;
132
0
    }
133
134
10.4k
    if (have_size) {
135
10.4k
        partial_data = (zip_uint64_t)(data_len) < st.size;
136
10.4k
    }
137
0
    else {
138
0
        partial_data = true;
139
0
    }
140
10.4k
    encrypted = (st.valid & ZIP_STAT_ENCRYPTION_METHOD) && (st.encryption_method != ZIP_EM_NONE);
141
10.4k
    needs_decrypt = ((flags & ZIP_FL_ENCRYPTED) == 0) && encrypted;
142
10.4k
    compressed = (st.valid & ZIP_STAT_COMP_METHOD) && (st.comp_method != ZIP_CM_STORE);
143
10.4k
    needs_decompress = ((flags & ZIP_FL_COMPRESSED) == 0) && compressed;
144
    /* when reading the whole file, check for CRC errors */
145
10.4k
    needs_crc = ((flags & ZIP_FL_COMPRESSED) == 0 || !compressed) && !partial_data && (st.valid & ZIP_STAT_CRC) != 0;
146
147
10.4k
    if (needs_decrypt) {
148
35
        if (password == NULL) {
149
35
            password = srcza->default_password;
150
35
        }
151
35
        if (password == NULL) {
152
35
            zip_error_set(error, ZIP_ER_NOPASSWD, 0);
153
35
            return NULL;
154
35
        }
155
35
    }
156
157
10.3k
    if ((de = _zip_get_dirent(srcza, srcidx, flags, error)) == NULL) {
158
0
        return NULL;
159
0
    }
160
10.3k
    _zip_file_attributes_from_dirent(&attributes, de);
161
162
10.3k
    have_comp_size = (st.valid & ZIP_STAT_COMP_SIZE) != 0;
163
10.3k
    if (needs_decrypt || needs_decompress) {
164
7.88k
        empty_data = (have_comp_size && st.comp_size == 0);
165
7.88k
    }
166
2.49k
    else {
167
2.49k
        empty_data = (have_size && st.size == 0);
168
2.49k
    }
169
10.3k
    if (empty_data) {
170
82
        src = zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error);
171
        /* If we created source buffer above, we want the window source to take ownership of it. */
172
82
        take_ownership = true;
173
        /* if we created a buffer source above, then treat it as if
174
           reading the changed data - that way we don't need add another
175
           special case to the code below that wraps it in the window
176
           source */
177
82
        changed_data = true;
178
82
    }
179
10.2k
    else {
180
10.2k
        src = NULL;
181
10.2k
    }
182
183
10.3k
    if (partial_data && !needs_decrypt && !needs_decompress) {
184
0
        struct zip_stat st2;
185
0
        zip_t *source_archive;
186
0
        zip_uint64_t source_index;
187
188
0
        if (start + (zip_uint64_t)data_len > st.comp_size) {
189
0
            zip_error_set(error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_STORED_SIZE_MISMATCH, srcidx));
190
0
            if (take_ownership) {
191
0
                zip_source_free(src);
192
0
            }
193
0
            return NULL;
194
0
        }
195
196
0
        if (changed_data) {
197
0
            if (src == NULL) {
198
0
                src = srcza->entry[srcidx].source;
199
0
            }
200
0
            source_archive = NULL;
201
0
            source_index = 0;
202
0
        }
203
0
        else {
204
0
            src = srcza->src;
205
0
            source_archive = srcza;
206
0
            source_index = srcidx;
207
0
        }
208
209
0
        st2.comp_method = ZIP_CM_STORE;
210
0
        st2.valid = ZIP_STAT_COMP_METHOD;
211
0
        if (data_len >= 0) {
212
0
            st2.size = (zip_uint64_t)data_len;
213
0
            st2.comp_size = (zip_uint64_t)data_len;
214
0
            st2.valid |= ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE;
215
0
        }
216
0
        if (st.valid & ZIP_STAT_MTIME) {
217
0
            st2.mtime = st.mtime;
218
0
            st2.valid |= ZIP_STAT_MTIME;
219
0
        }
220
221
0
        s2 = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, &attributes, &de->last_mod, source_archive, source_index, take_ownership, error);
222
0
        if (s2 == NULL) {
223
0
            if (take_ownership) {
224
0
                zip_source_free(src);
225
0
            }
226
0
            return NULL;
227
0
        }
228
0
        src = s2;
229
0
    }
230
    /* here we restrict src to file data, so no point in doing it for
231
       source that already represents only the file data */
232
10.3k
    else if (!changed_data) {
233
        /* this branch is executed only for archive sources; we know
234
           that stat data come from the archive too, so it's safe to
235
           assume that st has a comp_size specified */
236
10.2k
        if (take_ownership) {
237
0
            zip_source_free(src);
238
0
        }
239
10.2k
        if (st.comp_size > ZIP_INT64_MAX) {
240
0
            zip_error_set(error, ZIP_ER_INVAL, 0);
241
0
            return NULL;
242
0
        }
243
        /* despite the fact that we want the whole data file, we still
244
           wrap the source into a window source to add st and
245
           attributes and to have a source that positions the read
246
           offset properly before each read for multiple zip_file_t
247
           referring to the same underlying source */
248
10.2k
        if ((src = _zip_source_window_new(srcza->src, 0, (zip_int64_t)st.comp_size, &st, ZIP_STAT_NAME, &attributes, &de->last_mod, srcza, srcidx, take_ownership, error)) == NULL) {
249
525
            return NULL;
250
525
        }
251
10.2k
    }
252
82
    else {
253
        /* this branch gets executed when reading the whole changed
254
           data file or when "reading" from a zero-sized source buffer
255
           that we created above */
256
82
        if (src == NULL) {
257
0
            src = srcza->entry[srcidx].source;
258
0
        }
259
        /* despite the fact that we want the whole data file, we still
260
           wrap the source into a window source to add st and
261
           attributes and to have a source that positions the read
262
           offset properly before each read for multiple zip_file_t
263
           referring to the same underlying source */
264
82
        s2 = _zip_source_window_new(src, 0, data_len, &st, ZIP_STAT_NAME, &attributes, &de->last_mod, NULL, 0, take_ownership, error);
265
82
        if (s2 == NULL) {
266
28
            if (take_ownership) {
267
28
                zip_source_free(src);
268
28
            }
269
28
            return NULL;
270
28
        }
271
54
        src = s2;
272
54
    }
273
274
    /* In all cases, src is a window source and therefore is owned by this function. */
275
276
9.82k
    if (_zip_source_set_source_archive(src, srcza) < 0) {
277
0
        zip_source_free(src);
278
0
        return NULL;
279
0
    }
280
281
    /* creating a layered source calls zip_keep() on the lower layer, so we free it */
282
283
9.82k
    if (needs_decrypt) {
284
0
        zip_encryption_implementation enc_impl;
285
286
0
        if ((enc_impl = _zip_get_encryption_implementation(st.encryption_method, ZIP_CODEC_DECODE)) == NULL) {
287
0
            zip_source_free(src);
288
0
            zip_error_set(error, ZIP_ER_ENCRNOTSUPP, 0);
289
0
            return NULL;
290
0
        }
291
292
0
        s2 = enc_impl(srcza, src, st.encryption_method, 0, password);
293
0
        if (s2 == NULL) {
294
0
            zip_source_free(src);
295
0
            return NULL;
296
0
        }
297
298
0
        src = s2;
299
0
    }
300
9.82k
    if (needs_decompress) {
301
7.53k
        s2 = zip_source_decompress(srcza, src, st.comp_method);
302
7.53k
        if (s2 == NULL) {
303
522
            zip_source_free(src);
304
522
            return NULL;
305
522
        }
306
7.01k
        src = s2;
307
7.01k
    }
308
9.30k
    if (needs_crc) {
309
9.30k
        s2 = zip_source_crc_create(src, 1, error);
310
9.30k
        if (s2 == NULL) {
311
0
            zip_source_free(src);
312
0
            return NULL;
313
0
        }
314
9.30k
        src = s2;
315
9.30k
    }
316
317
9.30k
    if (partial_data && (needs_decrypt || needs_decompress)) {
318
0
        zip_stat_t st2;
319
0
        zip_stat_init(&st2);
320
0
        if (data_len >= 0) {
321
0
            st2.valid = ZIP_STAT_SIZE;
322
0
            st2.size = (zip_uint64_t)data_len;
323
0
        }
324
0
        s2 = _zip_source_window_new(src, start, data_len, &st2, ZIP_STAT_NAME, NULL, NULL, NULL, 0, true, error);
325
0
        if (s2 == NULL) {
326
0
            zip_source_free(src);
327
0
            return NULL;
328
0
        }
329
0
        src = s2;
330
0
    }
331
332
9.30k
    return src;
333
9.30k
}
334
335
10.3k
static void _zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de) {
336
10.3k
    zip_file_attributes_init(attributes);
337
10.3k
    attributes->valid = ZIP_FILE_ATTRIBUTES_ASCII | ZIP_FILE_ATTRIBUTES_HOST_SYSTEM | ZIP_FILE_ATTRIBUTES_EXTERNAL_FILE_ATTRIBUTES | ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS;
338
10.3k
    attributes->ascii = de->int_attrib & 1;
339
10.3k
    attributes->host_system = de->version_madeby >> 8;
340
10.3k
    attributes->external_file_attributes = de->ext_attrib;
341
10.3k
    attributes->general_purpose_bit_flags = de->bitflags;
342
10.3k
    attributes->general_purpose_bit_mask = ZIP_FILE_ATTRIBUTES_GENERAL_PURPOSE_BIT_FLAGS_ALLOWED_MASK;
343
10.3k
}