1 /*
2 zip_source_buffer.c -- create zip data source from buffer
3 Copyright (c) 1999-2017 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 <libzip@nih.at>
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 <stdlib.h>
35 #include <string.h>
36
37 #include "zipint.h"
38
39 #ifndef WRITE_FRAGMENT_SIZE
40 #define WRITE_FRAGMENT_SIZE 64*1024
41 #endif
42
43 struct buffer {
44 zip_uint64_t fragment_size; /* size of each fragment */
45
46 zip_uint8_t **fragments; /* pointers to fragments */
47 zip_uint64_t nfragments; /* number of allocated fragments */
48 zip_uint64_t fragments_capacity; /* size of fragments (number of pointers) */
49 zip_uint64_t size; /* size of data in bytes */
50 zip_uint64_t offset; /* current offset */
51 int free_data;
52 };
53
54 typedef struct buffer buffer_t;
55
56 struct read_data {
57 zip_error_t error;
58 time_t mtime;
59 buffer_t *in;
60 buffer_t *out;
61 };
62
63 static void buffer_free(buffer_t *buffer);
64 static buffer_t *buffer_new(zip_uint64_t fragment_size);
65 static buffer_t *buffer_new_read(const void *data, zip_uint64_t length, int free_data);
66 static buffer_t *buffer_new_write(zip_uint64_t fragment_size);
67 static zip_int64_t buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length);
68 static int buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error);
69 static zip_int64_t buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *);
70
71 static zip_int64_t read_data(void *, void *, zip_uint64_t, zip_source_cmd_t);
72
73
74 ZIP_EXTERN zip_source_t *
zip_source_buffer(zip_t * za,const void * data,zip_uint64_t len,int freep)75 zip_source_buffer(zip_t *za, const void *data, zip_uint64_t len, int freep)
76 {
77 if (za == NULL)
78 return NULL;
79
80 return zip_source_buffer_create(data, len, freep, &za->error);
81 }
82
83
84 ZIP_EXTERN zip_source_t *
zip_source_buffer_create(const void * data,zip_uint64_t len,int freep,zip_error_t * error)85 zip_source_buffer_create(const void *data, zip_uint64_t len, int freep, zip_error_t *error)
86 {
87 struct read_data *ctx;
88 zip_source_t *zs;
89
90 if (data == NULL && len > 0) {
91 zip_error_set(error, ZIP_ER_INVAL, 0);
92 return NULL;
93 }
94
95 if ((ctx=(struct read_data *)malloc(sizeof(*ctx))) == NULL) {
96 zip_error_set(error, ZIP_ER_MEMORY, 0);
97 return NULL;
98 }
99
100 if ((ctx->in = buffer_new_read(data, len, freep)) == NULL) {
101 zip_error_set(error, ZIP_ER_MEMORY, 0);
102 free(ctx);
103 return NULL;
104 }
105
106 ctx->out = NULL;
107 ctx->mtime = time(NULL);
108 zip_error_init(&ctx->error);
109
110 if ((zs=zip_source_function_create(read_data, ctx, error)) == NULL) {
111 buffer_free(ctx->in);
112 free(ctx);
113 return NULL;
114 }
115
116 return zs;
117 }
118
119
120 static zip_int64_t
read_data(void * state,void * data,zip_uint64_t len,zip_source_cmd_t cmd)121 read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
122 {
123 struct read_data *ctx = (struct read_data *)state;
124
125 switch (cmd) {
126 case ZIP_SOURCE_BEGIN_WRITE:
127 if ((ctx->out = buffer_new_write(WRITE_FRAGMENT_SIZE)) == NULL) {
128 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
129 return -1;
130 }
131 return 0;
132
133 case ZIP_SOURCE_CLOSE:
134 return 0;
135
136 case ZIP_SOURCE_COMMIT_WRITE:
137 buffer_free(ctx->in);
138 ctx->in = ctx->out;
139 ctx->out = NULL;
140 return 0;
141
142 case ZIP_SOURCE_ERROR:
143 return zip_error_to_data(&ctx->error, data, len);
144
145 case ZIP_SOURCE_FREE:
146 buffer_free(ctx->in);
147 buffer_free(ctx->out);
148 free(ctx);
149 return 0;
150
151 case ZIP_SOURCE_OPEN:
152 ctx->in->offset = 0;
153 return 0;
154
155 case ZIP_SOURCE_READ:
156 if (len > ZIP_INT64_MAX) {
157 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
158 return -1;
159 }
160 return buffer_read(ctx->in, data, len);
161
162 case ZIP_SOURCE_REMOVE:
163 {
164 buffer_t *empty = buffer_new_read(NULL, 0, 0);
165 if (empty == 0) {
166 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
167 return -1;
168 }
169
170 buffer_free(ctx->in);
171 ctx->in = empty;
172 return 0;
173 }
174
175 case ZIP_SOURCE_ROLLBACK_WRITE:
176 buffer_free(ctx->out);
177 ctx->out = NULL;
178 return 0;
179
180 case ZIP_SOURCE_SEEK:
181 return buffer_seek(ctx->in, data, len, &ctx->error);
182
183 case ZIP_SOURCE_SEEK_WRITE:
184 return buffer_seek(ctx->out, data, len, &ctx->error);
185
186 case ZIP_SOURCE_STAT:
187 {
188 zip_stat_t *st;
189
190 if (len < sizeof(*st)) {
191 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
192 return -1;
193 }
194
195 st = (zip_stat_t *)data;
196
197 zip_stat_init(st);
198 st->mtime = ctx->mtime;
199 st->size = ctx->in->size;
200 st->comp_size = st->size;
201 st->comp_method = ZIP_CM_STORE;
202 st->encryption_method = ZIP_EM_NONE;
203 st->valid = ZIP_STAT_MTIME|ZIP_STAT_SIZE|ZIP_STAT_COMP_SIZE|ZIP_STAT_COMP_METHOD|ZIP_STAT_ENCRYPTION_METHOD;
204
205 return sizeof(*st);
206 }
207
208 case ZIP_SOURCE_SUPPORTS:
209 return zip_source_make_command_bitmap(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_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1);
210
211 case ZIP_SOURCE_TELL:
212 if (ctx->in->offset > ZIP_INT64_MAX) {
213 zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW);
214 return -1;
215 }
216 return (zip_int64_t)ctx->in->offset;
217
218
219 case ZIP_SOURCE_TELL_WRITE:
220 if (ctx->out->offset > ZIP_INT64_MAX) {
221 zip_error_set(&ctx->error, ZIP_ER_TELL, EOVERFLOW);
222 return -1;
223 }
224 return (zip_int64_t)ctx->out->offset;
225
226 case ZIP_SOURCE_WRITE:
227 if (len > ZIP_INT64_MAX) {
228 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
229 return -1;
230 }
231 return buffer_write(ctx->out, data, len, &ctx->error);
232
233 default:
234 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
235 return -1;
236 }
237 }
238
239
240 static void
buffer_free(buffer_t * buffer)241 buffer_free(buffer_t *buffer)
242 {
243 if (buffer == NULL) {
244 return;
245 }
246
247 if (buffer->free_data) {
248 zip_uint64_t i;
249
250 for (i=0; i < buffer->nfragments; i++) {
251 free(buffer->fragments[i]);
252 }
253 }
254 free(buffer->fragments);
255 free(buffer);
256 }
257
258
259 static buffer_t *
buffer_new(zip_uint64_t fragment_size)260 buffer_new(zip_uint64_t fragment_size)
261 {
262 buffer_t *buffer;
263
264 if ((buffer = malloc(sizeof(*buffer))) == NULL) {
265 return NULL;
266 }
267
268 buffer->fragment_size = fragment_size;
269 buffer->offset = 0;
270 buffer->free_data = 0;
271 buffer->nfragments = 0;
272 buffer->fragments_capacity = 0;
273 buffer->fragments = NULL;
274 buffer->size = 0;
275
276 return buffer;
277 }
278
279
280 static buffer_t *
buffer_new_read(const void * data,zip_uint64_t length,int free_data)281 buffer_new_read(const void *data, zip_uint64_t length, int free_data)
282 {
283 buffer_t *buffer;
284
285 if ((buffer = buffer_new(length)) == NULL) {
286 return NULL;
287 }
288
289 buffer->size = length;
290
291 if (length > 0) {
292 if ((buffer->fragments = malloc(sizeof(*(buffer->fragments)))) == NULL) {
293 buffer_free(buffer);
294 return NULL;
295 }
296 buffer->fragments_capacity = 1;
297
298 buffer->nfragments = 1;
299 buffer->fragments[0] = (zip_uint8_t *)data;
300 buffer->free_data = free_data;
301 }
302
303 return buffer;
304 }
305
306
307 static buffer_t *
buffer_new_write(zip_uint64_t fragment_size)308 buffer_new_write(zip_uint64_t fragment_size)
309 {
310 buffer_t *buffer;
311
312 if ((buffer = buffer_new(fragment_size)) == NULL) {
313 return NULL;
314 }
315
316 if ((buffer->fragments = malloc(sizeof(*(buffer->fragments)))) == NULL) {
317 buffer_free(buffer);
318 return NULL;
319 }
320 buffer->fragments_capacity = 1;
321 buffer->nfragments = 0;
322 buffer->free_data = 1;
323
324 return buffer;
325 }
326
327
328 static zip_int64_t
buffer_read(buffer_t * buffer,zip_uint8_t * data,zip_uint64_t length)329 buffer_read(buffer_t *buffer, zip_uint8_t *data, zip_uint64_t length)
330 {
331 zip_uint64_t n, i, fragment_offset;
332
333 length = ZIP_MIN(length, buffer->size - buffer->offset);
334
335 if (length == 0) {
336 return 0;
337 }
338 if (length > ZIP_INT64_MAX) {
339 return -1;
340 }
341
342 i = buffer->offset / buffer->fragment_size;
343 fragment_offset = buffer->offset % buffer->fragment_size;
344 n = 0;
345 while (n < length) {
346 zip_uint64_t left = ZIP_MIN(length - n, buffer->fragment_size - fragment_offset);
347
348 memcpy(data + n, buffer->fragments[i] + fragment_offset, left);
349
350 n += left;
351 i++;
352 fragment_offset = 0;
353 }
354
355 buffer->offset += n;
356 return (zip_int64_t)n;
357 }
358
359
360 static int
buffer_seek(buffer_t * buffer,void * data,zip_uint64_t len,zip_error_t * error)361 buffer_seek(buffer_t *buffer, void *data, zip_uint64_t len, zip_error_t *error)
362 {
363 zip_int64_t new_offset = zip_source_seek_compute_offset(buffer->offset, buffer->size, data, len, error);
364
365 if (new_offset < 0) {
366 return -1;
367 }
368
369 buffer->offset = (zip_uint64_t)new_offset;
370 return 0;
371 }
372
373
374 static zip_int64_t
buffer_write(buffer_t * buffer,const zip_uint8_t * data,zip_uint64_t length,zip_error_t * error)375 buffer_write(buffer_t *buffer, const zip_uint8_t *data, zip_uint64_t length, zip_error_t *error)
376 {
377 zip_uint64_t n, i, fragment_offset;
378 zip_uint8_t **fragments;
379
380 if (buffer->offset + length + buffer->fragment_size - 1 < length) {
381 zip_error_set(error, ZIP_ER_INVAL, 0);
382 return -1;
383 }
384
385 /* grow buffer if needed */
386 if (buffer->offset + length > buffer->nfragments * buffer->fragment_size) {
387 zip_uint64_t needed_fragments = (buffer->offset + length + buffer->fragment_size - 1) / buffer->fragment_size;
388
389 if (needed_fragments > buffer->fragments_capacity) {
390 zip_uint64_t new_capacity = buffer->fragments_capacity;
391
392 while (new_capacity < needed_fragments) {
393 new_capacity *= 2;
394 }
395
396 fragments = realloc(buffer->fragments, new_capacity * sizeof(*fragments));
397
398 if (fragments == NULL) {
399 zip_error_set(error, ZIP_ER_MEMORY, 0);
400 return -1;
401 }
402
403 buffer->fragments = fragments;
404 buffer->fragments_capacity = new_capacity;
405 }
406
407 while (buffer->nfragments < needed_fragments) {
408 if ((buffer->fragments[buffer->nfragments] = malloc(buffer->fragment_size)) == NULL) {
409 zip_error_set(error, ZIP_ER_MEMORY, 0);
410 return -1;
411 }
412 buffer->nfragments++;
413 }
414 }
415
416 i = buffer->offset / buffer->fragment_size;
417 fragment_offset = buffer->offset % buffer->fragment_size;
418 n = 0;
419 while (n < length) {
420 zip_uint64_t left = ZIP_MIN(length - n, buffer->fragment_size - fragment_offset);
421
422 memcpy(buffer->fragments[i] + fragment_offset, data + n, left);
423
424 n += left;
425 i++;
426 fragment_offset = 0;
427 }
428
429 buffer->offset += n;
430 if (buffer->offset > buffer->size) {
431 buffer->size = buffer->offset;
432 }
433
434 return (zip_int64_t)n;
435 }
436