4
|
1 /* |
|
2 zip_source_buffer.c -- create zip data source from buffer |
|
3 Copyright (C) 1999-2016 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 * |
|
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 * |
|
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 |
|
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 |
|
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 * |
|
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 * |
|
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 * |
|
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 |
|
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 |
|
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 |
|
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 } |