2 zip_source_file_common.c -- create data source from file
3 Copyright (C) 1999-2021 Dieter Baron and Thomas Klausner
5 This file is part of libzip, a library to manipulate ZIP archives.
6 The authors can be contacted at <libzip@nih.at>
8 Redistribution and use in source and binary forms, with or without
9 modification, are permitted provided that the following conditions
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
17 3. The names of the authors may not be used to endorse or promote
18 products derived from this software without specific prior
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.
40 #include "zip_source_file.h"
42 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
45 zip_source_file_stat_init(zip_source_file_stat_t *st) {
47 st->mtime = time(NULL);
49 st->regular_file = false;
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;
56 zip_source_file_stat_t sb;
59 zip_error_set(error, ZIP_ER_INVAL, 0);
63 if (ops->close == NULL || ops->read == NULL || ops->seek == NULL || ops->stat == NULL) {
64 zip_error_set(error, ZIP_ER_INTERNAL, 0);
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);
74 if (ops->open == NULL || ops->string_duplicate == NULL) {
75 zip_error_set(error, ZIP_ER_INTERNAL, 0);
79 else if (file == NULL) {
80 zip_error_set(error, ZIP_ER_INVAL, 0);
88 if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
89 zip_error_set(error, ZIP_ER_INVAL, 0);
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);
99 ctx->ops_userdata = ops_userdata;
102 if ((ctx->fname = ops->string_duplicate(ctx, fname)) == NULL) {
103 zip_error_set(error, ZIP_ER_MEMORY, 0);
110 ctx->len = (zip_uint64_t)len;
112 memcpy(&ctx->st, st, sizeof(ctx->st));
114 ctx->st.valid &= ~ZIP_STAT_NAME;
117 zip_stat_init(&ctx->st);
121 ctx->st.size = ctx->len;
122 ctx->st.valid |= ZIP_STAT_SIZE;
125 zip_error_init(&ctx->stat_error);
130 zip_error_init(&ctx->error);
131 zip_file_attributes_init(&ctx->attributes);
133 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
135 zip_source_file_stat_init(&sb);
136 if (!ops->stat(ctx, &sb)) {
137 _zip_error_copy(error, &ctx->error);
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);
150 zip_error_set(&ctx->stat_error, ZIP_ER_READ, ENOENT);
157 if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
158 ctx->st.mtime = sb.mtime;
159 ctx->st.valid |= ZIP_STAT_MTIME;
161 if (sb.regular_file) {
162 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
164 if (ctx->start + ctx->len > sb.size) {
165 zip_error_set(error, ZIP_ER_INVAL, 0);
172 ctx->len = sb.size - ctx->start;
173 ctx->st.size = ctx->len;
174 ctx->st.valid |= ZIP_STAT_SIZE;
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;
183 ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_GET_FILE_ATTRIBUTES);
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);
193 if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) {
204 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
205 zip_source_file_context_t *ctx;
208 ctx = (zip_source_file_context_t *)state;
212 case ZIP_SOURCE_ACCEPT_EMPTY:
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);
221 return ctx->ops->create_temp_output(ctx);
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);
229 return ctx->ops->create_temp_output_cloning(ctx, len);
231 case ZIP_SOURCE_CLOSE:
233 ctx->ops->close(ctx);
238 case ZIP_SOURCE_COMMIT_WRITE: {
239 zip_int64_t ret = ctx->ops->commit_write(ctx);
248 case ZIP_SOURCE_ERROR:
249 return zip_error_to_data(&ctx->error, data, len);
251 case ZIP_SOURCE_FREE:
255 ctx->ops->close(ctx);
260 case ZIP_SOURCE_GET_FILE_ATTRIBUTES:
261 if (len < sizeof(ctx->attributes)) {
262 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
265 memcpy(data, &ctx->attributes, sizeof(ctx->attributes));
266 return sizeof(ctx->attributes);
268 case ZIP_SOURCE_OPEN:
270 if (ctx->ops->open(ctx) == false) {
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 */
284 case ZIP_SOURCE_READ: {
289 n = ZIP_MIN(ctx->len - ctx->offset, len);
295 if ((i = ctx->ops->read(ctx, buf, n)) < 0) {
296 zip_error_set(&ctx->error, ZIP_ER_READ, errno);
299 ctx->offset += (zip_uint64_t)i;
304 case ZIP_SOURCE_REMOVE:
305 return ctx->ops->remove(ctx);
307 case ZIP_SOURCE_ROLLBACK_WRITE:
308 ctx->ops->rollback_write(ctx);
314 case ZIP_SOURCE_SEEK: {
315 zip_int64_t new_offset = zip_source_seek_compute_offset(ctx->offset, ctx->len, data, len, &ctx->error);
317 if (new_offset < 0) {
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);
327 ctx->offset = (zip_uint64_t)new_offset;
329 if (ctx->ops->seek(ctx, ctx->f, (zip_int64_t)(ctx->offset + ctx->start), SEEK_SET) == false) {
335 case ZIP_SOURCE_SEEK_WRITE: {
336 zip_source_args_seek_t *args;
338 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
343 if (ctx->ops->seek(ctx, ctx->fout, args->offset, args->whence) == false) {
349 case ZIP_SOURCE_STAT: {
350 if (len < sizeof(ctx->st))
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));
358 memcpy(data, &ctx->st, sizeof(ctx->st));
359 return sizeof(ctx->st);
362 case ZIP_SOURCE_SUPPORTS:
363 return ctx->supports;
365 case ZIP_SOURCE_TELL:
366 return (zip_int64_t)ctx->offset;
368 case ZIP_SOURCE_TELL_WRITE:
369 return ctx->ops->tell(ctx, ctx->fout);
371 case ZIP_SOURCE_WRITE:
372 return ctx->ops->write(ctx, data, len);
375 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);