xref: /PHP-7.2/ext/zip/lib/zip_source_buffer.c (revision dac6c639)
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