Coverage Report

Created: 2025-07-23 06:33

/src/libzip/lib/zip_source_file_common.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
  zip_source_file_common.c -- create data source from file
3
  Copyright (C) 1999-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 <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
38
#include "zipint.h"
39
40
#include "zip_source_file.h"
41
42
static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
43
44
static void
45
5.51k
zip_source_file_stat_init(zip_source_file_stat_t *st) {
46
5.51k
    st->size = 0;
47
5.51k
    st->mtime = time(NULL);
48
5.51k
    st->exists = false;
49
5.51k
    st->regular_file = false;
50
5.51k
}
51
52
zip_source_t *
53
5.51k
zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error) {
54
5.51k
    zip_source_file_context_t *ctx;
55
5.51k
    zip_source_t *zs;
56
5.51k
    zip_source_file_stat_t sb;
57
5.51k
    zip_uint64_t length;
58
59
5.51k
    if (ops == NULL) {
60
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
61
0
        return NULL;
62
0
    }
63
64
5.51k
    if (ops->close == NULL || ops->read == NULL || ops->seek == NULL || ops->stat == NULL) {
65
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
66
0
        return NULL;
67
0
    }
68
69
5.51k
    if (ops->write != NULL && (ops->commit_write == NULL || ops->create_temp_output == NULL || ops->remove == NULL || ops->rollback_write == NULL || ops->tell == NULL)) {
70
0
        zip_error_set(error, ZIP_ER_INTERNAL, 0);
71
0
        return NULL;
72
0
    }
73
74
5.51k
    if (fname != NULL) {
75
5.51k
        if (ops->open == NULL || ops->string_duplicate == NULL) {
76
0
            zip_error_set(error, ZIP_ER_INTERNAL, 0);
77
0
            return NULL;
78
0
        }
79
5.51k
    }
80
0
    else if (file == NULL) {
81
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
82
0
        return NULL;
83
0
    }
84
85
5.51k
    if (len < 0) {
86
5.51k
        if (len == -1) {
87
5.51k
            len = ZIP_LENGTH_TO_END;
88
5.51k
        }
89
        // TODO: return ZIP_ER_INVAL if len != ZIP_LENGTH_UNCHECKED?
90
5.51k
        length = 0;
91
5.51k
    }
92
0
    else {
93
0
        length = (zip_uint64_t)len;
94
0
    }
95
96
5.51k
    if (start > ZIP_INT64_MAX || start + length < start) {
97
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
98
0
        return NULL;
99
0
    }
100
101
5.51k
    if ((ctx = (zip_source_file_context_t *)malloc(sizeof(zip_source_file_context_t))) == NULL) {
102
0
        zip_error_set(error, ZIP_ER_MEMORY, 0);
103
0
        return NULL;
104
0
    }
105
106
5.51k
    ctx->ops = ops;
107
5.51k
    ctx->ops_userdata = ops_userdata;
108
5.51k
    ctx->fname = NULL;
109
5.51k
    if (fname) {
110
5.51k
        if ((ctx->fname = ops->string_duplicate(ctx, fname)) == NULL) {
111
0
            zip_error_set(error, ZIP_ER_MEMORY, 0);
112
0
            free(ctx);
113
0
            return NULL;
114
0
        }
115
5.51k
    }
116
5.51k
    ctx->f = file;
117
5.51k
    ctx->start = start;
118
5.51k
    ctx->len = length;
119
5.51k
    if (st) {
120
0
        (void)memcpy_s(&ctx->st, sizeof(ctx->st), st, sizeof(*st));
121
0
        ctx->st.name = NULL;
122
0
        ctx->st.valid &= ~ZIP_STAT_NAME;
123
0
    }
124
5.51k
    else {
125
5.51k
        zip_stat_init(&ctx->st);
126
5.51k
    }
127
128
5.51k
    if (ctx->len > 0) {
129
0
        ctx->st.size = ctx->len;
130
0
        ctx->st.valid |= ZIP_STAT_SIZE;
131
0
    }
132
133
5.51k
    zip_error_init(&ctx->stat_error);
134
135
5.51k
    ctx->tmpname = NULL;
136
5.51k
    ctx->fout = NULL;
137
138
5.51k
    zip_error_init(&ctx->error);
139
5.51k
    zip_file_attributes_init(&ctx->attributes);
140
141
5.51k
    ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, ZIP_SOURCE_SUPPORTS_REOPEN, -1);
142
143
5.51k
    zip_source_file_stat_init(&sb);
144
5.51k
    if (!ops->stat(ctx, &sb)) {
145
0
        _zip_error_copy(error, &ctx->error);
146
0
        free(ctx->fname);
147
0
        free(ctx);
148
0
        return NULL;
149
0
    }
150
151
5.51k
    if (!sb.exists) {
152
980
        if (ctx->fname && ctx->start == 0 && ctx->len == 0 && ops->write != NULL) {
153
980
            ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
154
            /* zip_open_from_source checks for this to detect non-existing files */
155
980
            zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT);
156
980
        }
157
0
        else {
158
0
            zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT);
159
0
            free(ctx->fname);
160
0
            free(ctx);
161
0
            return NULL;
162
0
        }
163
980
    }
164
4.53k
    else {
165
4.53k
        if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
166
4.53k
            ctx->st.mtime = sb.mtime;
167
4.53k
            ctx->st.valid |= ZIP_STAT_MTIME;
168
4.53k
        }
169
4.53k
        if (sb.regular_file) {
170
4.53k
            ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
171
172
4.53k
            if (ctx->start + ctx->len > sb.size) {
173
0
                zip_error_set(error, ZIP_ER_INVAL, 0);
174
0
                free(ctx->fname);
175
0
                free(ctx);
176
0
                return NULL;
177
0
            }
178
179
4.53k
            if (ctx->len == 0) {
180
4.53k
                if (len != ZIP_LENGTH_UNCHECKED) {
181
4.53k
                    ctx->len = sb.size - ctx->start;
182
4.53k
                    ctx->st.size = ctx->len;
183
4.53k
                    ctx->st.valid |= ZIP_STAT_SIZE;
184
4.53k
                }
185
186
                /* when using a partial file, don't allow writing */
187
4.53k
                if (ctx->fname && start == 0 && ops->write != NULL) {
188
4.53k
                    ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
189
4.53k
                }
190
4.53k
            }
191
4.53k
        }
192
193
4.53k
        ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES);
194
4.53k
    }
195
196
5.51k
    ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY);
197
5.51k
    if (ops->create_temp_output_cloning != NULL) {
198
5.51k
        if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) {
199
5.51k
            ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING);
200
5.51k
        }
201
5.51k
    }
202
203
5.51k
    if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) {
204
0
        free(ctx->fname);
205
0
        free(ctx);
206
0
        return NULL;
207
0
    }
208
209
5.51k
    return zs;
210
5.51k
}
211
212
213
static zip_int64_t
214
267k
read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
215
267k
    zip_source_file_context_t *ctx;
216
267k
    char *buf;
217
218
267k
    ctx = (zip_source_file_context_t *)state;
219
267k
    buf = (char *)data;
220
221
267k
    switch (cmd) {
222
0
    case ZIP_SOURCE_ACCEPT_EMPTY:
223
0
        return 0;
224
225
980
    case ZIP_SOURCE_BEGIN_WRITE:
226
        /* write support should not be set if fname is NULL */
227
980
        if (ctx->fname == NULL) {
228
0
            zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
229
0
            return -1;
230
0
        }
231
980
        return ctx->ops->create_temp_output(ctx);
232
233
0
    case ZIP_SOURCE_BEGIN_WRITE_CLONING:
234
        /* write support should not be set if fname is NULL */
235
0
        if (ctx->fname == NULL) {
236
0
            zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
237
0
            return -1;
238
0
        }
239
0
        return ctx->ops->create_temp_output_cloning(ctx, len);
240
241
4.53k
    case ZIP_SOURCE_CLOSE:
242
4.53k
        if (ctx->fname) {
243
4.53k
            ctx->ops->close(ctx);
244
4.53k
            ctx->f = NULL;
245
4.53k
        }
246
4.53k
        return 0;
247
248
980
    case ZIP_SOURCE_COMMIT_WRITE: {
249
980
        zip_int64_t ret = ctx->ops->commit_write(ctx);
250
980
        ctx->fout = NULL;
251
980
        if (ret == 0) {
252
980
            free(ctx->tmpname);
253
980
            ctx->tmpname = NULL;
254
980
        }
255
980
        return ret;
256
0
    }
257
258
4.53k
    case ZIP_SOURCE_ERROR:
259
4.53k
        return zip_error_to_data(&ctx->error, data, len);
260
261
5.51k
    case ZIP_SOURCE_FREE:
262
5.51k
        free(ctx->fname);
263
5.51k
        free(ctx->tmpname);
264
5.51k
        if (ctx->f) {
265
0
            ctx->ops->close(ctx);
266
0
        }
267
5.51k
        free(ctx);
268
5.51k
        return 0;
269
270
13.2k
    case ZIP_SOURCE_GET_FILE_ATTRIBUTES:
271
13.2k
        if (len < sizeof(ctx->attributes)) {
272
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
273
0
            return -1;
274
0
        }
275
13.2k
        (void)memcpy_s(data, sizeof(ctx->attributes), &ctx->attributes, sizeof(ctx->attributes));
276
13.2k
        return sizeof(ctx->attributes);
277
278
4.53k
    case ZIP_SOURCE_OPEN:
279
4.53k
        if (ctx->fname) {
280
4.53k
            if (ctx->ops->open(ctx) == false) {
281
0
                return -1;
282
0
            }
283
4.53k
        }
284
285
4.53k
        if (ctx->start > 0) { /* TODO: rewind on re-open */
286
0
            if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)ctx->start, SEEK_SET) == false) {
287
                /* TODO: skip by reading */
288
0
                return -1;
289
0
            }
290
0
        }
291
4.53k
        ctx->offset = 0;
292
4.53k
        return 0;
293
294
94.7k
    case ZIP_SOURCE_READ: {
295
94.7k
        zip_int64_t i;
296
94.7k
        zip_uint64_t n;
297
298
94.7k
        if (ctx->len > 0) {
299
94.7k
            n = ZIP_MIN(ctx->len - ctx->offset, len);
300
94.7k
        }
301
0
        else {
302
0
            n = len;
303
0
        }
304
305
94.7k
        if ((i = ctx->ops->read(ctx, buf, n)) < 0) {
306
0
            zip_error_set(&ctx->error, ZIP_ER_READ, errno);
307
0
            return -1;
308
0
        }
309
94.7k
        ctx->offset += (zip_uint64_t)i;
310
311
94.7k
        return i;
312
94.7k
    }
313
314
0
    case ZIP_SOURCE_REMOVE:
315
0
        return ctx->ops->remove(ctx);
316
317
0
    case ZIP_SOURCE_ROLLBACK_WRITE:
318
0
        ctx->ops->rollback_write(ctx);
319
0
        ctx->fout = NULL;
320
0
        free(ctx->tmpname);
321
0
        ctx->tmpname = NULL;
322
0
        return 0;
323
324
62.7k
    case ZIP_SOURCE_SEEK: {
325
62.7k
        zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset, ctx->len, data, len, &ctx->error);
326
327
62.7k
        if (new_offset < 0) {
328
3.55k
            return -1;
329
3.55k
        }
330
331
        /* The actual offset inside the file must be representable as zip_int64_t. */
332
59.1k
        if (new_offset > ZIP_INT64_MAX - (zip_int64_t)ctx->start) {
333
0
            zip_error_set(&ctx->error, ZIP_ER_SEEK, EOVERFLOW);
334
0
            return -1;
335
0
        }
336
337
59.1k
        ctx->offset = (zip_uint64_t)new_offset;
338
339
59.1k
        if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)(ctx->offset + ctx->start), SEEK_SET) == false) {
340
0
            return -1;
341
0
        }
342
59.1k
        return 0;
343
59.1k
    }
344
345
1.96k
    case ZIP_SOURCE_SEEK_WRITE: {
346
1.96k
        zip_source_args_seek_t *args;
347
348
1.96k
        args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
349
1.96k
        if (args == NULL) {
350
0
            return -1;
351
0
        }
352
353
1.96k
        if (ctx->ops->seek(ctx, ctx->fout, args->offset, args->whence) == false) {
354
0
            return -1;
355
0
        }
356
1.96k
        return 0;
357
1.96k
    }
358
359
27.2k
    case ZIP_SOURCE_STAT: {
360
27.2k
        if (len < sizeof(ctx->st))
361
0
            return -1;
362
363
27.2k
        if (zip_error_code_zip(&ctx->stat_error) != 0) {
364
980
            zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
365
980
            return -1;
366
980
        }
367
368
26.2k
        (void)memcpy_s(data, sizeof(ctx->st), &ctx->st, sizeof(ctx->st));
369
26.2k
        return sizeof(ctx->st);
370
27.2k
    }
371
372
5.51k
    case ZIP_SOURCE_SUPPORTS:
373
5.51k
        return ctx->supports;
374
375
4.60k
    case ZIP_SOURCE_TELL:
376
4.60k
        return (zip_int64_t)ctx->offset;
377
378
7.84k
    case ZIP_SOURCE_TELL_WRITE:
379
7.84k
        return ctx->ops->tell(ctx, ctx->fout);
380
381
28.4k
    case ZIP_SOURCE_WRITE:
382
28.4k
        return ctx->ops->write(ctx, data, len);
383
384
0
    default:
385
0
        zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
386
0
        return -1;
387
267k
    }
388
267k
}