2 zip_source_filep.c -- create data source from FILE *
3 Copyright (C) 1999-2016 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.
46 /* WIN32 needs <fcntl.h> for _O_BINARY */
50 /* Windows sys/types.h does not provide these */
52 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
54 #if defined(S_IXUSR) && defined(S_IRWXG) && defined(S_IRWXO)
55 #define _SAFE_MASK (S_IXUSR | S_IRWXG | S_IRWXO)
56 #elif defined(_S_IWRITE)
57 #define _SAFE_MASK (_S_IWRITE)
59 #error do not know safe values for umask, please report this
63 /* MSVC doesn't have mode_t */
68 zip_error_t error; /* last error information */
72 char *fname; /* name of file to read from */
73 FILE *f; /* file to read from */
74 struct zip_stat st; /* stat information passed in */
75 zip_error_t stat_error; /* error returned for stat */
76 zip_uint64_t start; /* start offset of data to read */
77 zip_uint64_t end; /* end offset of data to read relative to start, 0 for up to EOF */
78 zip_uint64_t current; /* current offset relative to start (0 is beginning of part we read) */
85 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
86 static int create_temp_output(struct read_file *ctx);
87 static int _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error);
88 static int _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error);
91 ZIP_EXTERN zip_source_t *
92 zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len)
97 return zip_source_filep_create(file, start, len, &za->error);
101 ZIP_EXTERN zip_source_t *
102 zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error)
104 if (file == NULL || length < -1) {
105 zip_error_set(error, ZIP_ER_INVAL, 0);
109 return _zip_source_file_or_p(NULL, file, start, length, NULL, error);
114 _zip_source_file_or_p(const char *fname, FILE *file, zip_uint64_t start, zip_int64_t len, const zip_stat_t *st, zip_error_t *error)
116 struct read_file *ctx;
121 if (file == NULL && fname == NULL) {
122 zip_error_set(error, ZIP_ER_INVAL, 0);
130 if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
131 zip_error_set(error, ZIP_ER_INVAL, 0);
135 if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
136 zip_error_set(error, ZIP_ER_MEMORY, 0);
142 if ((ctx->fname=strdup(fname)) == NULL) {
143 zip_error_set(error, ZIP_ER_MEMORY, 0);
150 ctx->end = (zip_uint64_t)len;
152 memcpy(&ctx->st, st, sizeof(ctx->st));
154 ctx->st.valid &= ~ZIP_STAT_NAME;
157 zip_stat_init(&ctx->st);
161 ctx->st.size = ctx->end;
162 ctx->st.valid |= ZIP_STAT_SIZE;
165 zip_error_init(&ctx->stat_error);
170 zip_error_init(&ctx->error);
172 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
175 stat_valid = stat(ctx->fname, &sb) >= 0;
178 if (ctx->start == 0 && ctx->end == 0) {
179 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
184 stat_valid = fstat(fileno(ctx->f), &sb) >= 0;
188 zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno);
191 if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
192 ctx->st.mtime = sb.st_mtime;
193 ctx->st.valid |= ZIP_STAT_MTIME;
195 if (S_ISREG(sb.st_mode)) {
196 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
198 if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
199 zip_error_set(error, ZIP_ER_INVAL, 0);
206 ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
207 ctx->st.valid |= ZIP_STAT_SIZE;
209 if (ctx->fname && start == 0) {
210 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
216 if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
227 create_temp_output(struct read_file *ctx)
234 if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
235 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
238 sprintf(temp, "%s.XXXXXX", ctx->fname);
240 mask = umask(_SAFE_MASK);
241 if ((tfd=mkstemp(temp)) == -1) {
242 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
249 if ((tfp=fdopen(tfd, "r+b")) == NULL) {
250 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
259 According to Pierre Joye, Windows in some environments per
260 default creates text files, so force binary mode.
262 _setmode(_fileno(tfp), _O_BINARY );
273 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
275 struct read_file *ctx;
280 ctx = (struct read_file *)state;
284 case ZIP_SOURCE_BEGIN_WRITE:
285 if (ctx->fname == NULL) {
286 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
289 return create_temp_output(ctx);
291 case ZIP_SOURCE_COMMIT_WRITE: {
294 if (fclose(ctx->fout) < 0) {
296 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
299 if (rename(ctx->tmpname, ctx->fname) < 0) {
300 zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
305 /* not much we can do if chmod fails except make the whole commit fail */
306 (void)chmod(ctx->fname, 0666&~mask);
312 case ZIP_SOURCE_CLOSE:
319 case ZIP_SOURCE_ERROR:
320 return zip_error_to_data(&ctx->error, data, len);
322 case ZIP_SOURCE_FREE:
330 case ZIP_SOURCE_OPEN:
332 if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) {
333 zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
338 if (ctx->start > 0) {
339 if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
340 /* TODO: skip by reading */
347 case ZIP_SOURCE_READ:
349 n = ctx->end - ctx->current;
361 if ((i=fread(buf, 1, (size_t)n, ctx->f)) == 0) {
362 if (ferror(ctx->f)) {
363 zip_error_set(&ctx->error, ZIP_ER_READ, errno);
369 return (zip_int64_t)i;
371 case ZIP_SOURCE_REMOVE:
372 if (remove(ctx->fname) < 0) {
373 zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
378 case ZIP_SOURCE_ROLLBACK_WRITE:
383 (void)remove(ctx->tmpname);
388 case ZIP_SOURCE_SEEK: {
389 zip_int64_t new_current;
391 zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
398 switch (args->whence) {
400 new_current = args->offset;
405 if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
408 if ((new_current = ftello(ctx->f)) < 0) {
409 zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
412 new_current -= (zip_int64_t)ctx->start;
416 new_current = (zip_int64_t)ctx->end + args->offset;
421 new_current = (zip_int64_t)ctx->current + args->offset;
425 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
429 if (new_current < 0 || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)
430 || (zip_uint64_t)new_current + ctx->start < ctx->start) {
431 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
435 ctx->current = (zip_uint64_t)new_current;
438 if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
445 case ZIP_SOURCE_SEEK_WRITE: {
446 zip_source_args_seek_t *args;
448 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
453 if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
459 case ZIP_SOURCE_STAT: {
460 if (len < sizeof(ctx->st))
463 if (zip_error_code_zip(&ctx->stat_error) != 0) {
464 zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
468 memcpy(data, &ctx->st, sizeof(ctx->st));
469 return sizeof(ctx->st);
472 case ZIP_SOURCE_SUPPORTS:
473 return ctx->supports;
475 case ZIP_SOURCE_TELL:
476 return (zip_int64_t)ctx->current;
478 case ZIP_SOURCE_TELL_WRITE:
480 off_t ret = ftello(ctx->fout);
483 zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
489 case ZIP_SOURCE_WRITE:
494 ret = fwrite(data, 1, len, ctx->fout);
495 if (ret != len || ferror(ctx->fout)) {
496 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
500 return (zip_int64_t)ret;
504 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
511 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error)
513 if (offset > ZIP_INT64_MAX) {
514 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
517 return _zip_fseek(f, (zip_int64_t)offset, whence, error);
522 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error)
524 if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
525 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
528 if (fseeko(f, (off_t)offset, whence) < 0) {
529 zip_error_set(error, ZIP_ER_SEEK, errno);