Mercurial > embed
comparison libzip/lib/zip_source_buffer.c @ 4:2306f4b04790
libzip: import 1.1.2
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 24 Feb 2016 21:19:28 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
3:543b0653ea27 | 4:2306f4b04790 |
---|---|
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 } |