001239571811a408003c19a32a34b658c5991474
[platform/upstream/libzip.git] / lib / zip_source_file_common.c
1 /*
2   zip_source_file_common.c -- create data source from file
3   Copyright (C) 1999-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 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37
38 #include "zipint.h"
39
40 #include "zip_source_file.h"
41
42 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
43
44 static void
45 zip_source_file_stat_init(zip_source_file_stat_t *st) {
46     st->size = 0;
47     st->mtime = time(NULL);
48     st->exists = false;
49     st->regular_file = false;
50 }
51
52 zip_source_t *
53 zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_source_file_operations_t *ops, void *ops_userdata, zip_error_t *error) {
54     zip_source_file_context_t *ctx;
55     zip_source_t *zs;
56     zip_source_file_stat_t sb;
57
58     if (ops == NULL) {
59         zip_error_set(error, ZIP_ER_INVAL, 0);
60         return NULL;
61     }
62
63     if (ops->close == NULL || ops->read == NULL || ops->seek == NULL || ops->stat == NULL) {
64         zip_error_set(error, ZIP_ER_INTERNAL, 0);
65         return NULL;
66     }
67
68     if (ops->write != NULL && (ops->commit_write == NULL || ops->create_temp_output == NULL || ops->remove == NULL || ops->rollback_write == NULL || ops->tell == NULL)) {
69         zip_error_set(error, ZIP_ER_INTERNAL, 0);
70         return NULL;
71     }
72
73     if (fname != NULL) {
74         if (ops->open == NULL || ops->string_duplicate == NULL) {
75             zip_error_set(error, ZIP_ER_INTERNAL, 0);
76             return NULL;
77         }
78     }
79     else if (file == NULL) {
80         zip_error_set(error, ZIP_ER_INVAL, 0);
81         return NULL;
82     }
83
84     if (len < 0) {
85         len = 0;
86     }
87
88     if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
89         zip_error_set(error, ZIP_ER_INVAL, 0);
90         return NULL;
91     }
92
93     if ((ctx = (zip_source_file_context_t *)malloc(sizeof(zip_source_file_context_t))) == NULL) {
94         zip_error_set(error, ZIP_ER_MEMORY, 0);
95         return NULL;
96     }
97
98     ctx->ops = ops;
99     ctx->ops_userdata = ops_userdata;
100     ctx->fname = NULL;
101     if (fname) {
102         if ((ctx->fname = ops->string_duplicate(ctx, fname)) == NULL) {
103             zip_error_set(error, ZIP_ER_MEMORY, 0);
104             free(ctx);
105             return NULL;
106         }
107     }
108     ctx->f = file;
109     ctx->start = start;
110     ctx->len = (zip_uint64_t)len;
111     if (st) {
112         memcpy(&ctx->st, st, sizeof(ctx->st));
113         ctx->st.name = NULL;
114         ctx->st.valid &= ~ZIP_STAT_NAME;
115     }
116     else {
117         zip_stat_init(&ctx->st);
118     }
119
120     if (ctx->len > 0) {
121         ctx->st.size = ctx->len;
122         ctx->st.valid |= ZIP_STAT_SIZE;
123     }
124
125     zip_error_init(&ctx->stat_error);
126
127     ctx->tmpname = NULL;
128     ctx->fout = NULL;
129
130     zip_error_init(&ctx->error);
131     zip_file_attributes_init(&ctx->attributes);
132
133     ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
134
135     zip_source_file_stat_init(&sb);
136     if (!ops->stat(ctx, &sb)) {
137         _zip_error_copy(error, &ctx->error);
138         free(ctx->fname);
139         free(ctx);
140         return NULL;
141     }
142
143     if (!sb.exists) {
144         if (ctx->fname && ctx->start == 0 && ctx->len == 0 && ops->write != NULL) {
145             ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
146             /* zip_open_from_source checks for this to detect non-existing files */
147             zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT);
148         }
149         else {
150             zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT);
151             free(ctx->fname);
152             free(ctx);
153             return NULL;
154         }
155     }
156     else {
157         if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
158             ctx->st.mtime = sb.mtime;
159             ctx->st.valid |= ZIP_STAT_MTIME;
160         }
161         if (sb.regular_file) {
162             ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
163
164             if (ctx->start + ctx->len > sb.size) {
165                 zip_error_set(error, ZIP_ER_INVAL, 0);
166                 free(ctx->fname);
167                 free(ctx);
168                 return NULL;
169             }
170
171             if (ctx->len == 0) {
172                 ctx->len = sb.size - ctx->start;
173                 ctx->st.size = ctx->len;
174                 ctx->st.valid |= ZIP_STAT_SIZE;
175
176                 /* when using a partial file, don't allow writing */
177                 if (ctx->fname && start == 0 && ops->write != NULL) {
178                     ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
179                 }
180             }
181         }
182
183         ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES);
184     }
185
186     ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY);
187     if (ops->create_temp_output_cloning != NULL) {
188         if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) {
189             ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING);
190         }
191     }
192
193     if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) {
194         free(ctx->fname);
195         free(ctx);
196         return NULL;
197     }
198
199     return zs;
200 }
201
202
203 static zip_int64_t
204 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
205     zip_source_file_context_t *ctx;
206     char *buf;
207
208     ctx = (zip_source_file_context_t *)state;
209     buf = (char *)data;
210
211     switch (cmd) {
212     case ZIP_SOURCE_ACCEPT_EMPTY:
213         return 0;
214
215     case ZIP_SOURCE_BEGIN_WRITE:
216         /* write support should not be set if fname is NULL */
217         if (ctx->fname == NULL) {
218             zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
219             return -1;
220         }
221         return ctx->ops->create_temp_output(ctx);
222
223     case ZIP_SOURCE_BEGIN_WRITE_CLONING:
224         /* write support should not be set if fname is NULL */
225         if (ctx->fname == NULL) {
226             zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
227             return -1;
228         }
229         return ctx->ops->create_temp_output_cloning(ctx, len);
230
231     case ZIP_SOURCE_CLOSE:
232         if (ctx->fname) {
233             ctx->ops->close(ctx);
234             ctx->f = NULL;
235         }
236         return 0;
237
238     case ZIP_SOURCE_COMMIT_WRITE: {
239         zip_int64_t ret = ctx->ops->commit_write(ctx);
240         ctx->fout = NULL;
241         if (ret == 0) {
242             free(ctx->tmpname);
243             ctx->tmpname = NULL;
244         }
245         return ret;
246     }
247
248     case ZIP_SOURCE_ERROR:
249         return zip_error_to_data(&ctx->error, data, len);
250
251     case ZIP_SOURCE_FREE:
252         free(ctx->fname);
253         free(ctx->tmpname);
254         if (ctx->f) {
255             ctx->ops->close(ctx);
256         }
257         free(ctx);
258         return 0;
259
260     case ZIP_SOURCE_GET_FILE_ATTRIBUTES:
261         if (len < sizeof(ctx->attributes)) {
262             zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
263             return -1;
264         }
265         memcpy(data, &ctx->attributes, sizeof(ctx->attributes));
266         return sizeof(ctx->attributes);
267
268     case ZIP_SOURCE_OPEN:
269         if (ctx->fname) {
270             if (ctx->ops->open(ctx) == false) {
271                 return -1;
272             }
273         }
274
275         if (ctx->start > 0) { // TODO: rewind on re-open
276             if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)ctx->start, SEEK_SET) == false) {
277                 /* TODO: skip by reading */
278                 return -1;
279             }
280         }
281         ctx->offset = 0;
282         return 0;
283
284     case ZIP_SOURCE_READ: {
285         zip_int64_t i;
286         zip_uint64_t n;
287
288         if (ctx->len > 0) {
289             n = ZIP_MIN(ctx->len - ctx->offset, len);
290         }
291         else {
292             n = len;
293         }
294
295         if ((i = ctx->ops->read(ctx, buf, n)) < 0) {
296             zip_error_set(&ctx->error, ZIP_ER_READ, errno);
297             return -1;
298         }
299         ctx->offset += (zip_uint64_t)i;
300
301         return i;
302     }
303
304     case ZIP_SOURCE_REMOVE:
305         return ctx->ops->remove(ctx);
306
307     case ZIP_SOURCE_ROLLBACK_WRITE:
308         ctx->ops->rollback_write(ctx);
309         ctx->fout = NULL;
310         free(ctx->tmpname);
311         ctx->tmpname = NULL;
312         return 0;
313
314     case ZIP_SOURCE_SEEK: {
315         zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset, ctx->len, data, len, &ctx->error);
316
317         if (new_offset < 0) {
318             return -1;
319         }
320
321         /* The actual offset inside the file must be representable as zip_int64_t. */
322         if (new_offset > ZIP_INT64_MAX - (zip_int64_t)ctx->start) {
323             zip_error_set(&ctx->error, ZIP_ER_SEEK, EOVERFLOW);
324             return -1;
325         }
326
327         ctx->offset = (zip_uint64_t)new_offset;
328
329         if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)(ctx->offset + ctx->start), SEEK_SET) == false) {
330             return -1;
331         }
332         return 0;
333     }
334
335     case ZIP_SOURCE_SEEK_WRITE: {
336         zip_source_args_seek_t *args;
337
338         args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
339         if (args == NULL) {
340             return -1;
341         }
342
343         if (ctx->ops->seek(ctx, ctx->fout, args->offset, args->whence) == false) {
344             return -1;
345         }
346         return 0;
347     }
348
349     case ZIP_SOURCE_STAT: {
350         if (len < sizeof(ctx->st))
351             return -1;
352
353         if (zip_error_code_zip(&ctx->stat_error) != 0) {
354             zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
355             return -1;
356         }
357
358         memcpy(data, &ctx->st, sizeof(ctx->st));
359         return sizeof(ctx->st);
360     }
361
362     case ZIP_SOURCE_SUPPORTS:
363         return ctx->supports;
364
365     case ZIP_SOURCE_TELL:
366         return (zip_int64_t)ctx->offset;
367
368     case ZIP_SOURCE_TELL_WRITE:
369         return ctx->ops->tell(ctx, ctx->fout);
370
371     case ZIP_SOURCE_WRITE:
372         return ctx->ops->write(ctx, data, len);
373
374     default:
375         zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
376         return -1;
377     }
378 }