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