Imported Upstream version 1.8.0
[platform/upstream/libzip.git] / lib / zip_source_window.c
1 /*
2   zip_source_window.c -- return part of lower source
3   Copyright (C) 2012-2021 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
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "zipint.h"
39
40 struct window {
41     zip_uint64_t start; /* where in file we start reading */
42     zip_uint64_t end;   /* where in file we stop reading */
43     bool end_valid;     /* whether end is set, otherwise read until EOF */
44
45     /* if not NULL, read file data for this file */
46     zip_t *source_archive;
47     zip_uint64_t source_index;
48
49     zip_uint64_t offset; /* offset in src for next read */
50
51     zip_stat_t stat;
52     zip_file_attributes_t attributes;
53     zip_error_t error;
54     zip_int64_t supports;
55     bool needs_seek;
56 };
57
58 static zip_int64_t window_read(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
59
60
61 ZIP_EXTERN zip_source_t *
62 zip_source_window_create(zip_source_t *src, zip_uint64_t start, zip_int64_t len, zip_error_t *error) {
63     return _zip_source_window_new(src, start, len, NULL, 0, NULL, 0, error);
64 }
65
66
67 zip_source_t *
68 _zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length, zip_stat_t *st, zip_file_attributes_t *attributes, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error) {
69     struct window *ctx;
70
71     if (src == NULL || length < -1 || (source_archive == NULL && source_index != 0)) {
72         zip_error_set(error, ZIP_ER_INVAL, 0);
73         return NULL;
74     }
75     
76     if (length >= 0) {
77         if (start + (zip_uint64_t)length < start) {
78             zip_error_set(error, ZIP_ER_INVAL, 0);
79             return NULL;
80         }
81     }
82
83     if ((ctx = (struct window *)malloc(sizeof(*ctx))) == NULL) {
84         zip_error_set(error, ZIP_ER_MEMORY, 0);
85         return NULL;
86     }
87
88     ctx->start = start;
89     if (length == -1) {
90         ctx->end_valid = false;
91     }
92     else {
93         ctx->end = start + (zip_uint64_t)length;
94         ctx->end_valid = true;
95     }
96     zip_stat_init(&ctx->stat);
97     if (attributes != NULL) {
98         memcpy(&ctx->attributes, attributes, sizeof(ctx->attributes));
99     }
100     else {
101         zip_file_attributes_init(&ctx->attributes);
102     }
103     ctx->source_archive = source_archive;
104     ctx->source_index = source_index;
105     zip_error_init(&ctx->error);
106     ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1));
107     ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false;
108
109     if (st) {
110         if (_zip_stat_merge(&ctx->stat, st, error) < 0) {
111             free(ctx);
112             return NULL;
113         }
114     }
115     
116     return zip_source_layered_create(src, window_read, ctx, error);
117 }
118
119
120 int
121 _zip_source_set_source_archive(zip_source_t *src, zip_t *za) {
122     src->source_archive = za;
123     return _zip_register_source(za, src);
124 }
125
126
127 /* called by zip_discard to avoid operating on file from closed archive */
128 void
129 _zip_source_invalidate(zip_source_t *src) {
130     src->source_closed = 1;
131
132     if (zip_error_code_zip(&src->error) == ZIP_ER_OK) {
133         zip_error_set(&src->error, ZIP_ER_ZIPCLOSED, 0);
134     }
135 }
136
137
138 static zip_int64_t
139 window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
140     struct window *ctx;
141     zip_int64_t ret;
142     zip_uint64_t n, i;
143
144     ctx = (struct window *)_ctx;
145
146     switch (cmd) {
147     case ZIP_SOURCE_CLOSE:
148         return 0;
149
150     case ZIP_SOURCE_ERROR:
151         return zip_error_to_data(&ctx->error, data, len);
152
153     case ZIP_SOURCE_FREE:
154         free(ctx);
155         return 0;
156
157     case ZIP_SOURCE_OPEN:
158         if (ctx->source_archive) {
159             zip_uint64_t offset;
160
161             if ((offset = _zip_file_get_offset(ctx->source_archive, ctx->source_index, &ctx->error)) == 0) {
162                 return -1;
163             }
164             if (ctx->end + offset < ctx->end) {
165                 /* zip archive data claims end of data past zip64 limits */
166                 zip_error_set(&ctx->error, ZIP_ER_INCONS, MAKE_DETAIL_WITH_INDEX(ZIP_ER_DETAIL_CDIR_ENTRY_INVALID, ctx->source_index));
167                 return -1;
168             }
169             ctx->start += offset;
170             ctx->end += offset;
171             ctx->source_archive = NULL;
172         }
173
174         if (!ctx->needs_seek) {
175             DEFINE_BYTE_ARRAY(b, BUFSIZE);
176
177             if (!byte_array_init(b, BUFSIZE)) {
178                 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
179                 return -1;
180             }
181
182             for (n = 0; n < ctx->start; n += (zip_uint64_t)ret) {
183                 i = (ctx->start - n > BUFSIZE ? BUFSIZE : ctx->start - n);
184                 if ((ret = zip_source_read(src, b, i)) < 0) {
185                     _zip_error_set_from_source(&ctx->error, src);
186                     byte_array_fini(b);
187                     return -1;
188                 }
189                 if (ret == 0) {
190                     zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
191                     byte_array_fini(b);
192                     return -1;
193                 }
194             }
195
196             byte_array_fini(b);
197         }
198
199         ctx->offset = ctx->start;
200         return 0;
201
202     case ZIP_SOURCE_READ:
203         if (ctx->end_valid && len > ctx->end - ctx->offset) {
204             len = ctx->end - ctx->offset;
205         }
206
207         if (len == 0) {
208             return 0;
209         }
210
211         if (ctx->needs_seek) {
212             if (zip_source_seek(src, (zip_int64_t)ctx->offset, SEEK_SET) < 0) {
213                 _zip_error_set_from_source(&ctx->error, src);
214                 return -1;
215             }
216         }
217
218         if ((ret = zip_source_read(src, data, len)) < 0) {
219             zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
220             return -1;
221         }
222
223         ctx->offset += (zip_uint64_t)ret;
224
225         if (ret == 0) {
226             if (ctx->end_valid && ctx->offset < ctx->end) {
227                 zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
228                 return -1;
229             }
230         }
231         return ret;
232
233     case ZIP_SOURCE_SEEK: {
234         zip_int64_t new_offset;
235         
236         if (!ctx->end_valid) {
237             zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
238             
239             if (args == NULL) {
240                 return -1;
241             }
242             if (args->whence == SEEK_END) {
243                 if (zip_source_seek(src, args->offset, args->whence) < 0) {
244                     _zip_error_set_from_source(&ctx->error, src);
245                     return -1;
246                 }
247                 new_offset = zip_source_tell(src);
248                 if (new_offset < 0) {
249                     _zip_error_set_from_source(&ctx->error, src);
250                     return -1;
251                 }
252                 if ((zip_uint64_t)new_offset < ctx->start) {
253                     zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
254                     (void)zip_source_seek(src, (zip_int64_t)ctx->offset, SEEK_SET);
255                     return -1;
256                 }
257                 ctx->offset = (zip_uint64_t)new_offset;
258                 return 0;
259             }
260         }
261
262         new_offset = zip_source_seek_compute_offset(ctx->offset - ctx->start, ctx->end - ctx->start, data, len, &ctx->error);
263         
264         if (new_offset < 0) {
265             return -1;
266         }
267         
268         ctx->offset = (zip_uint64_t)new_offset + ctx->start;
269         return 0;
270     }
271
272     case ZIP_SOURCE_STAT: {
273         zip_stat_t *st;
274
275         st = (zip_stat_t *)data;
276
277         if (_zip_stat_merge(st, &ctx->stat, &ctx->error) < 0) {
278             return -1;
279         }
280         return 0;
281     }
282
283     case ZIP_SOURCE_GET_FILE_ATTRIBUTES:
284         if (len < sizeof(ctx->attributes)) {
285             zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
286             return -1;
287         }
288
289         memcpy(data, &ctx->attributes, sizeof(ctx->attributes));
290         return sizeof(ctx->attributes);
291
292     case ZIP_SOURCE_SUPPORTS:
293         return ctx->supports;
294
295     case ZIP_SOURCE_TELL:
296         return (zip_int64_t)(ctx->offset - ctx->start);
297
298     default:
299         zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
300         return -1;
301     }
302 }
303
304
305 void
306 _zip_deregister_source(zip_t *za, zip_source_t *src) {
307     unsigned int i;
308
309     for (i = 0; i < za->nopen_source; i++) {
310         if (za->open_source[i] == src) {
311             za->open_source[i] = za->open_source[za->nopen_source - 1];
312             za->nopen_source--;
313             break;
314         }
315     }
316 }
317
318
319 int
320 _zip_register_source(zip_t *za, zip_source_t *src) {
321     zip_source_t **open_source;
322
323     if (za->nopen_source + 1 >= za->nopen_source_alloc) {
324         unsigned int n;
325         n = za->nopen_source_alloc + 10;
326         open_source = (zip_source_t **)realloc(za->open_source, n * sizeof(zip_source_t *));
327         if (open_source == NULL) {
328             zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
329             return -1;
330         }
331         za->nopen_source_alloc = n;
332         za->open_source = open_source;
333     }
334
335     za->open_source[za->nopen_source++] = src;
336
337     return 0;
338 }