ec0c8b4767da28a402f3e2500b0acaf2b4813db2
[platform/upstream/libzip.git] / lib / zip_source_window.c
1 /*
2   zip_source_window.c -- return part of lower source
3   Copyright (C) 2012-2014 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
44     /* if not NULL, read file data for this file */
45     zip_t *source_archive;
46     zip_uint64_t source_index;
47
48     zip_uint64_t offset;                /* offset in src for next read */
49
50     zip_stat_t stat;
51     zip_int8_t compression_flags;
52     zip_error_t error;
53     zip_int64_t supports;
54     bool needs_seek;
55 };
56
57 static zip_int64_t window_read(zip_source_t *, void *, void *, zip_uint64_t, zip_source_cmd_t);
58
59
60 zip_source_t *
61 zip_source_window(zip_t *za, zip_source_t *src, zip_uint64_t start, zip_uint64_t len)
62 {
63     return _zip_source_window_new(src, start, len, NULL, 0, NULL, 0, &za->error);
64 }
65
66
67 zip_source_t *
68 _zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_uint64_t length, zip_stat_t *st, zip_int8_t compression_flags, zip_t *source_archive, zip_uint64_t source_index, zip_error_t *error)
69 {
70     struct window *ctx;
71
72     if (src == NULL || start + length < start || (source_archive == NULL && source_index != 0)) {
73         zip_error_set(error, ZIP_ER_INVAL, 0);
74         return NULL;
75     }
76
77     if ((ctx=(struct window *)malloc(sizeof(*ctx))) == NULL) {
78         zip_error_set(error, ZIP_ER_MEMORY, 0);
79         return NULL;
80     }
81
82     ctx->start = start;
83     ctx->end = start + length;
84     zip_stat_init(&ctx->stat);
85     ctx->compression_flags = compression_flags;
86     ctx->source_archive = source_archive;
87     ctx->source_index = source_index;
88     zip_error_init(&ctx->error);
89     ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_COMPRESSION_FLAGS, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1));
90     ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false;
91
92     if (st) {
93         if (_zip_stat_merge(&ctx->stat, st, error) < 0) {
94             free(ctx);
95             return NULL;
96         }
97     }
98
99     return zip_source_layered_create(src, window_read, ctx, error);
100 }
101
102
103 int
104 _zip_source_set_source_archive(zip_source_t *src, zip_t *za)
105 {
106     src->source_archive = za;
107     return _zip_register_source(za, src);
108 }
109
110
111 /* called by zip_discard to avoid operating on file from closed archive */
112 void
113 _zip_source_invalidate(zip_source_t *src)
114 {
115     src->source_closed = 1;
116
117     if (zip_error_code_zip(&src->error) == ZIP_ER_OK) {
118         zip_error_set(&src->error, ZIP_ER_ZIPCLOSED, 0);
119     }
120 }
121
122
123 static zip_int64_t
124 window_read(zip_source_t *src, void *_ctx, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
125 {
126     struct window *ctx;
127     zip_int64_t ret;
128     zip_uint64_t n, i;
129     char b[8192];
130
131     ctx = (struct window *)_ctx;
132
133     switch (cmd) {
134         case ZIP_SOURCE_CLOSE:
135             return 0;
136
137         case ZIP_SOURCE_ERROR:
138             return zip_error_to_data(&ctx->error, data, len);
139
140         case ZIP_SOURCE_FREE:
141             free(ctx);
142             return 0;
143
144         case ZIP_SOURCE_OPEN:
145             if (ctx->source_archive) {
146                 zip_int64_t offset;
147
148                 if ((offset = _zip_file_get_offset(ctx->source_archive, ctx->source_index, &ctx->error)) == 0) {
149                     return -1;
150                 }
151                 if (ctx->end + offset < ctx->end) {
152                     /* zip archive data claims end of data past zip64 limits */
153                     zip_error_set(&ctx->error, ZIP_ER_INCONS, 0);
154                     return -1;
155                 }
156                 ctx->start += offset;
157                 ctx->end += offset;
158                 ctx->source_archive = NULL;
159             }
160
161             if (!ctx->needs_seek) {
162                 for (n=0; n<ctx->start; n+=(zip_uint64_t)ret) {
163                     i = (ctx->start-n > sizeof(b) ? sizeof(b) : ctx->start-n);
164                     if ((ret=zip_source_read(src, b, i)) < 0) {
165                         _zip_error_set_from_source(&ctx->error, src);
166                         return -1;
167                     }
168                     if (ret==0) {
169                         zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
170                         return -1;
171                     }
172                 }
173
174             }
175             ctx->offset = ctx->start;
176             return 0;
177
178         case ZIP_SOURCE_READ:
179             if (len > ctx->end - ctx->offset)
180                 len = ctx->end - ctx->offset;
181
182             if (len == 0)
183                 return 0;
184
185             if (ctx->needs_seek) {
186                 if (zip_source_seek(src, (zip_int64_t)ctx->offset, SEEK_SET) < 0) {
187                     _zip_error_set_from_source(&ctx->error, src);
188                     return -1;
189                 }
190             }
191
192             if ((ret=zip_source_read(src, data, len)) < 0) {
193                 zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
194                 return -1;
195             }
196
197             ctx->offset += (zip_uint64_t)ret;
198
199             if (ret == 0) {
200                 if (ctx->offset < ctx->end) {
201                     zip_error_set(&ctx->error, ZIP_ER_EOF, 0);
202                     return -1;
203                 }
204             }
205             return ret;
206
207         case ZIP_SOURCE_SEEK:
208         {
209             zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset - ctx->start, ctx->end - ctx->start, data, len, &ctx->error);
210
211             if (new_offset < 0) {
212                 return -1;
213             }
214
215             ctx->offset = (zip_uint64_t)new_offset + ctx->start;
216             return 0;
217         }
218
219        case ZIP_SOURCE_STAT:
220         {
221             zip_stat_t *st;
222
223             st = (zip_stat_t *)data;
224
225             if (_zip_stat_merge(st, &ctx->stat, &ctx->error) < 0) {
226                 return -1;
227             }
228             return 0;
229         }
230
231         case ZIP_SOURCE_GET_COMPRESSION_FLAGS:
232             return ctx->compression_flags;
233
234         case ZIP_SOURCE_SUPPORTS:
235             return ctx->supports;
236
237         case ZIP_SOURCE_TELL:
238             return (zip_int64_t)(ctx->offset - ctx->start);
239
240        default:
241             zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
242             return -1;
243     }
244 }
245
246
247 void
248 _zip_deregister_source(zip_t *za, zip_source_t *src)
249 {
250     unsigned int i;
251
252     for (i=0; i<za->nopen_source; i++) {
253         if (za->open_source[i] == src) {
254             za->open_source[i] = za->open_source[za->nopen_source-1];
255             za->nopen_source--;
256             break;
257         }
258     }
259 }
260
261
262 int
263 _zip_register_source(zip_t *za, zip_source_t *src)
264 {
265     zip_source_t **open_source;
266
267     if (za->nopen_source+1 >= za->nopen_source_alloc) {
268         unsigned int n;
269         n = za->nopen_source_alloc + 10;
270         open_source = (zip_source_t **)realloc(za->open_source, n*sizeof(zip_source_t *));
271         if (open_source == NULL) {
272             zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
273             return -1;
274         }
275         za->nopen_source_alloc = n;
276         za->open_source = open_source;
277     }
278
279     za->open_source[za->nopen_source++] = src;
280
281     return 0;
282 }