Coverage Report

Created: 2026-10-28 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libzip/lib/zip_source_buffer.c
Line
Count
Source
1
/*
2
  zip_source_buffer.c -- create zip data source from buffer
3
  Copyright (C) 1999-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
#ifndef WRITE_FRAGMENT_SIZE
40
0
#define WRITE_FRAGMENT_SIZE (64 * 1024)
41
#endif
42
43
44
struct buffer_fragment {
45
    zip_uint8_t *data;
46
    zip_uint64_t length;
47
    bool free_data; /* whether to free data when freeing fragment */
48
};
49
typedef struct buffer_fragment buffer_fragment_t;
50
51
struct buffer {
52
    buffer_fragment_t *fragments;    /* fragments */
53
    zip_uint64_t *fragment_offsets;  /* offset of each fragment from start of buffer, nfragments+1 entries */
54
    zip_uint64_t nfragments;         /* number of allocated fragments */
55
    zip_uint64_t fragments_capacity; /* size of fragments (number of pointers) */
56
57
    struct buffer *shared_buffer; /* buffer fragments are shared with */
58
59
    zip_uint64_t size;             /* size of buffer */
60
    zip_uint64_t offset;           /* current offset in buffer */
61
    zip_uint64_t current_fragment; /* fragment current offset is in */
62
};
63
64
typedef struct buffer buffer_t;
65
66
struct read_data {
67
    zip_error_t error;
68
    time_t mtime;
69
    zip_file_attributes_t attributes;
70
    buffer_t *in;
71
    buffer_t *out;
72
};
73
74
typedef struct read_data read_data_t;
75
76
static read_data_t *read_data_new(void);
77
static void read_data_free(read_data_t *ctx);
78
79
/* TODO:
80
    buffer_write
81
*/
82
83
0
#define buffer_capacity(buffer) ((buffer)->fragment_offsets[(buffer)->nfragments])
84
#define buffer_size(buffer) ((buffer)->size)
85
86
static zip_int64_t buffer_at_eof(const buffer_t *buffer);
87
static buffer_t *buffer_clone(buffer_t *buffer, zip_uint64_t length, zip_error_t *error);
88
static zip_uint64_t buffer_find_fragment(const buffer_t *buffer, zip_uint64_t offset);
89
static void buffer_free(buffer_t *buffer);
90
static bool buffer_grow_fragments(buffer_t *buffer, zip_uint64_t capacity, zip_error_t *error);
91
static bool buffer_is_fragment_shared(const buffer_t *buffer, zip_uint64_t fragment_index);
92
static bool buffer_make_fragment_writable(buffer_t *buffer, zip_uint64_t fragment_index, zip_error_t *error);
93
static buffer_t *buffer_new(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int free_data, zip_error_t *error);
94
static zip_int64_t buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length);
95
static int buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error);
96
static zip_int64_t buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *);
97
98
static zip_int64_t read_data(void *, void *, zip_uint64_t, zip_source_cmd_t);
99
100
zip_source_t *zip_source_buffer_with_attributes_create(const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes, zip_error_t *error);
101
zip_source_t *zip_source_buffer_fragment_with_attributes_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_file_attributes_t *attributes, zip_error_t *error);
102
103
104
0
ZIP_EXTERN zip_source_t *zip_source_buffer(zip_t *za, const void *data, zip_uint64_t len, int freep) {
105
0
    if (za == NULL) {
106
0
        return NULL;
107
0
    }
108
109
0
    return zip_source_buffer_with_attributes_create(data, len, freep, NULL, &za->error);
110
0
}
111
112
113
0
ZIP_EXTERN zip_source_t *zip_source_buffer_create(const void *data, zip_uint64_t len, int freep, zip_error_t *error) {
114
0
    return zip_source_buffer_with_attributes_create(data, len, freep, NULL, error);
115
0
}
116
117
118
0
zip_source_t *zip_source_buffer_with_attributes_create(const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes, zip_error_t *error) {
119
0
    zip_buffer_fragment_t fragment;
120
121
0
    if (data == NULL) {
122
0
        if (len > 0) {
123
0
            zip_error_set(error, ZIP_ER_INVAL, 0);
124
0
            return NULL;
125
0
        }
126
127
0
        return zip_source_buffer_fragment_with_attributes_create(NULL, 0, freep, attributes, error);
128
0
    }
129
130
0
    fragment.data = (zip_uint8_t *)data;
131
0
    fragment.length = len;
132
133
0
    return zip_source_buffer_fragment_with_attributes_create(&fragment, 1, freep, attributes, error);
134
0
}
135
136
137
0
ZIP_EXTERN zip_source_t *zip_source_buffer_fragment(zip_t *za, const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep) {
138
0
    if (za == NULL) {
139
0
        return NULL;
140
0
    }
141
142
0
    return zip_source_buffer_fragment_with_attributes_create(fragments, nfragments, freep, NULL, &za->error);
143
0
}
144
145
146
0
ZIP_EXTERN zip_source_t *zip_source_buffer_fragment_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_error_t *error) {
147
0
    return zip_source_buffer_fragment_with_attributes_create(fragments, nfragments, freep, NULL, error);
148
0
}
149
150
0
zip_source_t *zip_source_buffer_fragment_with_attributes_create(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int freep, zip_file_attributes_t *attributes, zip_error_t *error) {
151
0
    read_data_t *ctx;
152
0
    zip_source_t *zs;
153
0
    buffer_t *buffer;
154
155
0
    if (fragments == NULL && nfragments > 0) {
156
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
157
0
        return NULL;
158
0
    }
159
160
0
    if ((buffer = buffer_new(fragments, nfragments, freep, error)) == NULL) {
161
0
        return NULL;
162
0
    }
163
164
0
    if ((ctx = read_data_new()) == NULL) {
165
0
        zip_error_set(error, ZIP_ER_MEMORY, 0);
166
0
        buffer_free(buffer);
167
0
        return NULL;
168
0
    }
169
170
0
    ctx->in = buffer;
171
0
    if (attributes) {
172
0
        (void)memcpy_s(&ctx->attributes, sizeof(ctx->attributes), attributes, sizeof(ctx->attributes));
173
0
    }
174
175
0
    if ((zs = zip_source_function_create(read_data, ctx, error)) == NULL) {
176
0
        read_data_free(ctx);
177
0
        return NULL;
178
0
    }
179
180
0
    return zs;
181
0
}
182
183
184
0
zip_source_t *zip_source_buffer_with_attributes(zip_t *za, const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes) {
185
0
    return zip_source_buffer_with_attributes_create(data, len, freep, attributes, &za->error);
186
0
}
187
188
0
static zip_int64_t read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
189
0
    read_data_t *ctx = (read_data_t *)state;
190
191
0
    switch (cmd) {
192
0
    case ZIP_SOURCE_AT_EOF:
193
0
        return buffer_at_eof(ctx->in);
194
195
0
    case ZIP_SOURCE_BEGIN_WRITE:
196
0
        if ((ctx->out = buffer_new(NULL, 0, 0, &ctx->error)) == NULL) {
197
0
            return -1;
198
0
        }
199
0
        ctx->out->offset = 0;
200
0
        ctx->out->current_fragment = 0;
201
0
        return 0;
202
203
0
    case ZIP_SOURCE_BEGIN_WRITE_CLONING:
204
0
        if ((ctx->out = buffer_clone(ctx->in, len, &ctx->error)) == NULL) {
205
0
            return -1;
206
0
        }
207
0
        ctx->out->offset = len;
208
0
        ctx->out->current_fragment = ctx->out->nfragments;
209
0
        return 0;
210
211
0
    case ZIP_SOURCE_CLOSE:
212
0
        return 0;
213
214
0
    case ZIP_SOURCE_COMMIT_WRITE:
215
0
        buffer_free(ctx->in);
216
0
        ctx->in = ctx->out;
217
0
        ctx->out = NULL;
218
0
        return 0;
219
220
0
    case ZIP_SOURCE_ERROR:
221
0
        return zip_error_to_data(&ctx->error, data, len);
222
223
0
    case ZIP_SOURCE_FREE:
224
0
        read_data_free(ctx);
225
0
        return 0;
226
227
0
    case ZIP_SOURCE_GET_FILE_ATTRIBUTES: {
228
0
        if (len < sizeof(ctx->attributes)) {
229
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
230
0
            return -1;
231
0
        }
232
233
0
        (void)memcpy_s(data, sizeof(ctx->attributes), &ctx->attributes, sizeof(ctx->attributes));
234
235
0
        return sizeof(ctx->attributes);
236
0
    }
237
238
0
    case ZIP_SOURCE_OPEN:
239
0
        ctx->in->offset = 0;
240
0
        ctx->in->current_fragment = 0;
241
0
        return 0;
242
243
0
    case ZIP_SOURCE_READ:
244
0
        if (len > ZIP_INT64_MAX) {
245
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
246
0
            return -1;
247
0
        }
248
0
        return buffer_read(ctx->in, data, len);
249
250
0
    case ZIP_SOURCE_REMOVE: {
251
0
        buffer_t *empty = buffer_new(NULL, 0, 0, &ctx->error);
252
0
        if (empty == NULL) {
253
0
            return -1;
254
0
        }
255
256
0
        buffer_free(ctx->in);
257
0
        ctx->in = empty;
258
0
        return 0;
259
0
    }
260
261
0
    case ZIP_SOURCE_ROLLBACK_WRITE:
262
0
        buffer_free(ctx->out);
263
0
        ctx->out = NULL;
264
0
        return 0;
265
266
0
    case ZIP_SOURCE_SEEK:
267
0
        return buffer_seek(ctx->in, data, len, &ctx->error);
268
269
0
    case ZIP_SOURCE_SEEK_WRITE:
270
0
        return buffer_seek(ctx->out, data, len, &ctx->error);
271
272
0
    case ZIP_SOURCE_STAT: {
273
0
        zip_stat_t *st;
274
275
0
        if (len < sizeof(*st)) {
276
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
277
0
            return -1;
278
0
        }
279
280
0
        st = (zip_stat_t *)data;
281
282
0
        zip_stat_init(st);
283
0
        st->mtime = ctx->mtime;
284
0
        st->size = ctx->in->size;
285
0
        st->comp_size = st->size;
286
0
        st->comp_method = ZIP_CM_STORE;
287
0
        st->encryption_method = ZIP_EM_NONE;
288
0
        st->valid = ZIP_STAT_MTIME | ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_ENCRYPTION_METHOD;
289
290
0
        return sizeof(*st);
291
0
    }
292
293
0
    case ZIP_SOURCE_SUPPORTS:
294
0
        return zip_source_make_command_bitmap(ZIP_SOURCE_AT_EOF, ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_BEGIN_WRITE_CLONING, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, ZIP_SOURCE_SUPPORTS_REOPEN, -1);
295
296
0
    case ZIP_SOURCE_TELL:
297
0
        if (ctx->in->offset > ZIP_INT64_MAX) {
298
0
            zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW);
299
0
            return -1;
300
0
        }
301
0
        return (zip_int64_t)ctx->in->offset;
302
303
304
0
    case ZIP_SOURCE_TELL_WRITE:
305
0
        if (ctx->out->offset > ZIP_INT64_MAX) {
306
0
            zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW);
307
0
            return -1;
308
0
        }
309
0
        return (zip_int64_t)ctx->out->offset;
310
311
0
    case ZIP_SOURCE_WRITE:
312
0
        if (len > ZIP_INT64_MAX) {
313
0
            zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
314
0
            return -1;
315
0
        }
316
0
        return buffer_write(ctx->out, data, len, &ctx->error);
317
318
0
    default:
319
0
        zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
320
0
        return -1;
321
0
    }
322
0
}
323
324
325
0
static buffer_t *buffer_clone(buffer_t *buffer, zip_uint64_t offset, zip_error_t *error) {
326
0
    zip_uint64_t fragment, fragment_offset, waste;
327
0
    buffer_t *clone;
328
0
    size_t i;
329
330
0
    if (offset == 0) {
331
0
        return buffer_new(NULL, 0, 1, error);
332
0
    }
333
334
0
    if (offset > buffer->size) {
335
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
336
0
        return NULL;
337
0
    }
338
0
    if (buffer->shared_buffer != NULL) {
339
0
        zip_error_set(error, ZIP_ER_INUSE, 0);
340
0
        return NULL;
341
0
    }
342
343
0
    fragment = buffer_find_fragment(buffer, offset);
344
0
    fragment_offset = offset - buffer->fragment_offsets[fragment];
345
346
0
    if (fragment_offset == 0) {
347
        /* We can't be at beginning of fragment zero if offset > 0. */
348
0
        fragment--;
349
0
        fragment_offset = buffer->fragments[fragment].length;
350
0
    }
351
352
0
    waste = buffer->fragments[fragment].length - fragment_offset;
353
0
    if (waste > offset) {
354
0
        zip_error_set(error, ZIP_ER_OPNOTSUPP, 0);
355
0
        return NULL;
356
0
    }
357
358
0
    if ((clone = buffer_new(NULL, 0, 1, error)) == NULL) {
359
0
        return NULL;
360
0
    }
361
0
    if (!buffer_grow_fragments(clone, fragment + 1, error)) {
362
0
        buffer_free(clone);
363
0
        return NULL;
364
0
    }
365
366
0
    for (i = 0; i <= fragment; i++) {
367
0
        clone->fragments[i] = buffer->fragments[i];
368
0
        clone->fragment_offsets[i] = buffer->fragment_offsets[i];
369
0
    }
370
371
0
#ifndef __clang_analyzer__
372
    /* clone->fragments can't be null, since it was created with at least one fragment */
373
0
    clone->fragments[fragment].length = fragment_offset;
374
0
#endif
375
0
    clone->nfragments = fragment + 1;
376
0
    clone->fragment_offsets[clone->nfragments] = offset;
377
0
    clone->size = offset;
378
379
0
    buffer->shared_buffer = clone;
380
0
    clone->shared_buffer = buffer;
381
382
0
    return clone;
383
0
}
384
385
386
0
static zip_uint64_t buffer_find_fragment(const buffer_t *buffer, zip_uint64_t offset) {
387
0
    zip_uint64_t low, high, mid;
388
389
0
    if (buffer->nfragments == 0) {
390
0
        return 0;
391
0
    }
392
393
0
    low = 0;
394
0
    high = buffer->nfragments - 1;
395
396
0
    while (low < high) {
397
0
        mid = (high - low) / 2 + low;
398
0
        if (buffer->fragment_offsets[mid] > offset) {
399
0
            high = mid - 1;
400
0
        }
401
0
        else if (mid == buffer->nfragments || buffer->fragment_offsets[mid + 1] > offset) {
402
0
            return mid;
403
0
        }
404
0
        else {
405
0
            low = mid + 1;
406
0
        }
407
0
    }
408
409
0
    return low;
410
0
}
411
412
413
0
static void buffer_free(buffer_t *buffer) {
414
0
    zip_uint64_t i;
415
416
0
    if (buffer == NULL) {
417
0
        return;
418
0
    }
419
420
0
    for (i = 0; i < buffer->nfragments; i++) {
421
0
        if (buffer->fragments[i].free_data && !buffer_is_fragment_shared(buffer, i)) {
422
0
            free(buffer->fragments[i].data);
423
0
        }
424
0
    }
425
0
    if (buffer->shared_buffer != NULL) {
426
0
        buffer->shared_buffer->shared_buffer = NULL;
427
0
    }
428
0
    free(buffer->fragments);
429
0
    free(buffer->fragment_offsets);
430
0
    free(buffer);
431
0
}
432
433
434
0
static bool buffer_grow_fragments(buffer_t *buffer, zip_uint64_t capacity, zip_error_t *error) {
435
0
    zip_uint64_t additional_fragments;
436
0
    zip_uint64_t offset_capacity = buffer->fragments_capacity + 1;
437
438
0
    if (capacity <= buffer->fragments_capacity) {
439
0
        return true;
440
0
    }
441
442
0
    additional_fragments = capacity - buffer->fragments_capacity;
443
444
0
    if (!ZIP_REALLOC(buffer->fragments, buffer->fragments_capacity, additional_fragments, error)) {
445
0
        return false;
446
0
    }
447
    /* The size of both buffer->fragments and buffer->fragment_offsets is stored in buffer->fragments_capacity, so use a temporary capacity variable here for reallocating buffer->fragment_offsets. */
448
0
    if (!ZIP_REALLOC(buffer->fragment_offsets, offset_capacity, additional_fragments, error)) {
449
0
        buffer->fragments_capacity -= additional_fragments;
450
0
        return false;
451
0
    }
452
453
0
    return true;
454
0
}
455
456
457
0
static buffer_t *buffer_new(const zip_buffer_fragment_t *fragments, zip_uint64_t nfragments, int free_data, zip_error_t *error) {
458
0
    buffer_t *buffer;
459
0
    bool have_empty_fragment = false;
460
461
0
    if ((buffer = malloc(sizeof(*buffer))) == NULL) {
462
0
        zip_error_set(error, ZIP_ER_MEMORY, 0);
463
0
        return NULL;
464
0
    }
465
466
0
    buffer->offset = 0;
467
0
    buffer->size = 0;
468
0
    buffer->fragments = NULL;
469
0
    buffer->fragment_offsets = NULL;
470
0
    buffer->nfragments = 0;
471
0
    buffer->fragments_capacity = 0;
472
0
    buffer->shared_buffer = NULL;
473
474
0
    if (nfragments == 0) {
475
0
        if ((buffer->fragment_offsets = malloc(sizeof(buffer->fragment_offsets[0]))) == NULL) {
476
0
            free(buffer);
477
0
            zip_error_set(error, ZIP_ER_MEMORY, 0);
478
0
            return NULL;
479
0
        }
480
0
        buffer->fragment_offsets[0] = 0;
481
0
    }
482
0
    else {
483
0
        zip_uint64_t i, j, offset;
484
485
0
        if (!buffer_grow_fragments(buffer, nfragments, NULL)) {
486
0
            zip_error_set(error, ZIP_ER_MEMORY, 0);
487
0
            buffer_free(buffer);
488
0
            return NULL;
489
0
        }
490
491
0
        offset = 0;
492
0
        for (i = 0, j = 0; i < nfragments; i++) {
493
0
            if (fragments[i].length == 0) {
494
0
                if (fragments[i].data != NULL && free_data) {
495
0
                    have_empty_fragment = true;
496
0
                }
497
0
                continue;
498
0
            }
499
0
            if (fragments[i].data == NULL) {
500
0
                zip_error_set(error, ZIP_ER_INVAL, 0);
501
0
                buffer_free(buffer);
502
0
                return NULL;
503
0
            }
504
0
            buffer->fragments[j].data = fragments[i].data;
505
0
            buffer->fragments[j].length = fragments[i].length;
506
0
            buffer->fragments[j].free_data = free_data;
507
0
            buffer->fragment_offsets[j] = offset;
508
0
            if (offset + fragments[i].length < offset) {
509
0
                zip_error_set(error, ZIP_ER_INVAL, 0);
510
0
                buffer_free(buffer);
511
0
                return NULL;
512
0
            }
513
0
            offset += fragments[i].length;
514
0
            j++;
515
0
        }
516
0
        buffer->nfragments = j;
517
0
        buffer->fragment_offsets[buffer->nfragments] = offset;
518
0
        buffer->size = offset;
519
520
0
        if (have_empty_fragment) {
521
0
            for (i = 0; i < nfragments; i++) {
522
0
                if (fragments[i].length == 0 && fragments[i].data != NULL) {
523
0
                    free(fragments[i].data);
524
0
                }
525
0
            }
526
0
        }
527
0
    }
528
529
0
    return buffer;
530
0
}
531
532
0
static zip_int64_t buffer_at_eof(const buffer_t *buffer) {
533
0
    return buffer->offset == buffer->size;
534
0
}
535
536
0
static zip_int64_t buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length) {
537
0
    zip_uint64_t n, i, fragment_offset;
538
539
0
    length = ZIP_MIN(length, buffer->size - buffer->offset);
540
541
0
    if (length == 0) {
542
0
        return 0;
543
0
    }
544
0
    if (length > ZIP_INT64_MAX) {
545
0
        return -1;
546
0
    }
547
548
0
    i = buffer->current_fragment;
549
0
    fragment_offset = buffer->offset - buffer->fragment_offsets[i];
550
0
    n = 0;
551
0
    while (n < length) {
552
0
        zip_uint64_t left = ZIP_MIN(length - n, buffer->fragments[i].length - fragment_offset);
553
#if ZIP_UINT64_MAX > SIZE_MAX
554
        left = ZIP_MIN(left, SIZE_MAX);
555
#endif
556
557
0
        (void)memcpy_s(data + n, (size_t)left, buffer->fragments[i].data + fragment_offset, (size_t)left);
558
559
0
        if (left == buffer->fragments[i].length - fragment_offset) {
560
0
            i++;
561
0
        }
562
0
        n += left;
563
0
        fragment_offset = 0;
564
0
    }
565
566
0
    buffer->offset += n;
567
0
    buffer->current_fragment = i;
568
0
    return (zip_int64_t)n;
569
0
}
570
571
572
0
static int buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error) {
573
0
    zip_int64_t new_offset = zip_source_seek_compute_offset(buffer->offset, buffer->size, data, len, error);
574
575
0
    if (new_offset < 0) {
576
0
        return -1;
577
0
    }
578
579
0
    buffer->offset = (zip_uint64_t)new_offset;
580
0
    buffer->current_fragment = buffer_find_fragment(buffer, buffer->offset);
581
0
    return 0;
582
0
}
583
584
585
0
static zip_int64_t buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *error) {
586
0
    zip_uint64_t copied, i, fragment_offset, capacity;
587
588
0
    if (buffer->offset + length + WRITE_FRAGMENT_SIZE - 1 < length) {
589
0
        zip_error_set(error, ZIP_ER_INVAL, 0);
590
0
        return -1;
591
0
    }
592
593
    /* grow buffer if needed */
594
0
    capacity = buffer_capacity(buffer);
595
0
    if (buffer->offset + length > capacity) {
596
0
        zip_uint64_t needed_fragments = buffer->nfragments + (length - (capacity - buffer->offset) + WRITE_FRAGMENT_SIZE - 1) / WRITE_FRAGMENT_SIZE;
597
598
0
        if (needed_fragments > buffer->fragments_capacity) {
599
0
            zip_uint64_t new_capacity = buffer->fragments_capacity;
600
601
0
            if (new_capacity == 0) {
602
0
                new_capacity = 16;
603
0
            }
604
0
            while (new_capacity < needed_fragments) {
605
0
                new_capacity *= 2;
606
0
            }
607
608
0
            if (!buffer_grow_fragments(buffer, new_capacity, error)) {
609
0
                zip_error_set(error, ZIP_ER_MEMORY, 0);
610
0
                return -1;
611
0
            }
612
0
        }
613
614
0
        while (buffer->nfragments < needed_fragments) {
615
0
            if ((buffer->fragments[buffer->nfragments].data = malloc(WRITE_FRAGMENT_SIZE)) == NULL) {
616
0
                zip_error_set(error, ZIP_ER_MEMORY, 0);
617
0
                return -1;
618
0
            }
619
0
            buffer->fragments[buffer->nfragments].length = WRITE_FRAGMENT_SIZE;
620
0
            buffer->fragments[buffer->nfragments].free_data = true;
621
0
            buffer->nfragments++;
622
0
            capacity += WRITE_FRAGMENT_SIZE;
623
0
            buffer->fragment_offsets[buffer->nfragments] = capacity;
624
0
        }
625
0
    }
626
627
0
    i = buffer->current_fragment;
628
0
    fragment_offset = buffer->offset - buffer->fragment_offsets[i];
629
0
    copied = 0;
630
0
    while (copied < length) {
631
0
        zip_uint64_t n = ZIP_MIN(ZIP_MIN(length - copied, buffer->fragments[i].length - fragment_offset), SIZE_MAX);
632
#if ZIP_UINT64_MAX > SIZE_MAX
633
        n = ZIP_MIN(n, SIZE_MAX);
634
#endif
635
636
0
        if (!buffer_make_fragment_writable(buffer, i, error)) {
637
0
            return -1;
638
0
        }
639
0
        (void)memcpy_s(buffer->fragments[i].data + fragment_offset, (size_t)n, data + copied, (size_t)n);
640
641
0
        if (n == buffer->fragments[i].length - fragment_offset) {
642
0
            i++;
643
0
            fragment_offset = 0;
644
0
        }
645
0
        else {
646
0
            fragment_offset += n;
647
0
        }
648
0
        copied += n;
649
0
    }
650
651
0
    buffer->offset += copied;
652
0
    buffer->current_fragment = i;
653
0
    if (buffer->offset > buffer->size) {
654
0
        buffer->size = buffer->offset;
655
0
    }
656
657
0
    return (zip_int64_t)copied;
658
0
}
659
660
661
0
static bool buffer_is_fragment_shared(const buffer_t *buffer, zip_uint64_t fragment_index) {
662
0
    if (buffer->shared_buffer == NULL) {
663
0
        return false;
664
0
    }
665
666
0
    if (fragment_index >= buffer->shared_buffer->nfragments) {
667
0
        return false;
668
0
    }
669
670
0
    return buffer->fragments[fragment_index].data == buffer->shared_buffer->fragments[fragment_index].data;
671
0
}
672
673
674
0
static bool buffer_make_fragment_writable(buffer_t *buffer, zip_uint64_t fragment_index, zip_error_t *error) {
675
0
    if (!buffer_is_fragment_shared(buffer, fragment_index)) {
676
0
        return true;
677
0
    }
678
679
0
    zip_uint8_t *new_data = malloc(buffer->fragments[fragment_index].length);
680
0
    if (new_data == NULL) {
681
0
        zip_error_set(error, ZIP_ER_MEMORY, 0);
682
0
        return false;
683
0
    }
684
685
0
    (void)memcpy_s(new_data, buffer->fragments[fragment_index].length, buffer->fragments[fragment_index].data, buffer->fragments[fragment_index].length);
686
687
    /* Since the fragment was shared, we don't need to free the old data. */
688
0
    buffer->fragments[fragment_index].data = new_data;
689
0
    buffer->fragments[fragment_index].free_data = true;
690
691
0
    return true;
692
0
}
693
694
0
static read_data_t *read_data_new(void) {
695
0
    read_data_t *ctx = (read_data_t *)malloc(sizeof(read_data_t));
696
0
    if (ctx == NULL) {
697
0
        return NULL;
698
0
    }
699
700
0
    zip_error_init(&ctx->error);
701
0
    ctx->in = NULL;
702
0
    ctx->out = NULL;
703
0
    ctx->mtime = time(NULL);
704
0
    zip_file_attributes_init(&ctx->attributes);
705
0
    return ctx;
706
0
}
707
708
0
static void read_data_free(read_data_t *ctx) {
709
0
    if (ctx == NULL) {
710
0
        return;
711
0
    }
712
713
0
    zip_error_fini(&ctx->error);
714
0
    buffer_free(ctx->in);
715
0
    buffer_free(ctx->out);
716
    /* TODO: attributes */
717
718
0
    free(ctx);
719
0
}