2 zip_source_filep.c -- create data source from FILE *
3 Copyright (C) 1999-2017 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.
47 #include <sys/clonefile.h>
51 /* WIN32 needs <fcntl.h> for _O_BINARY */
55 /* Windows sys/types.h does not provide these */
57 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
59 #if defined(S_IXUSR) && defined(S_IRWXG) && defined(S_IRWXO)
60 #define _SAFE_MASK (S_IXUSR | S_IRWXG | S_IRWXO)
61 #elif defined(_S_IWRITE)
62 #define _SAFE_MASK (_S_IWRITE)
64 #error do not know safe values for umask, please report this
68 /* MSVC doesn't have mode_t */
73 zip_error_t error; /* last error information */
77 char *fname; /* name of file to read from */
78 FILE *f; /* file to read from */
79 struct zip_stat st; /* stat information passed in */
80 zip_error_t stat_error; /* error returned for stat */
81 zip_uint64_t start; /* start offset of data to read */
82 zip_uint64_t end; /* end offset of data to read relative to start, 0 for up to EOF */
83 zip_uint64_t current; /* current offset relative to start (0 is beginning of part we read) */
90 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
91 static int create_temp_output(struct read_file *ctx);
93 static zip_int64_t create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset);
95 static int _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error);
96 static int _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error);
99 ZIP_EXTERN zip_source_t *
100 zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len)
105 return zip_source_filep_create(file, start, len, &za->error);
109 ZIP_EXTERN zip_source_t *
110 zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error)
112 if (file == NULL || length < -1) {
113 zip_error_set(error, ZIP_ER_INVAL, 0);
117 return _zip_source_file_or_p(NULL, file, start, length, NULL, error);
122 _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)
124 struct read_file *ctx;
129 if (file == NULL && fname == NULL) {
130 zip_error_set(error, ZIP_ER_INVAL, 0);
138 if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
139 zip_error_set(error, ZIP_ER_INVAL, 0);
143 if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
144 zip_error_set(error, ZIP_ER_MEMORY, 0);
150 if ((ctx->fname=strdup(fname)) == NULL) {
151 zip_error_set(error, ZIP_ER_MEMORY, 0);
158 ctx->end = (zip_uint64_t)len;
160 memcpy(&ctx->st, st, sizeof(ctx->st));
162 ctx->st.valid &= ~ZIP_STAT_NAME;
165 zip_stat_init(&ctx->st);
169 ctx->st.size = ctx->end;
170 ctx->st.valid |= ZIP_STAT_SIZE;
173 zip_error_init(&ctx->stat_error);
178 zip_error_init(&ctx->error);
180 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
183 stat_valid = stat(ctx->fname, &sb) >= 0;
186 if (ctx->start == 0 && ctx->end == 0) {
187 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
192 stat_valid = fstat(fileno(ctx->f), &sb) >= 0;
196 zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno);
199 if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
200 ctx->st.mtime = sb.st_mtime;
201 ctx->st.valid |= ZIP_STAT_MTIME;
203 if (S_ISREG(sb.st_mode)) {
204 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
206 if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
207 zip_error_set(error, ZIP_ER_INVAL, 0);
214 ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
215 ctx->st.valid |= ZIP_STAT_SIZE;
217 if (ctx->fname && start == 0) {
218 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
224 #ifdef HAVE_CLONEFILE
225 if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) {
226 ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING);
230 if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
241 create_temp_output(struct read_file *ctx)
248 if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
249 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
252 sprintf(temp, "%s.XXXXXX", ctx->fname);
254 mask = umask(_SAFE_MASK);
255 if ((tfd=mkstemp(temp)) == -1) {
256 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
263 if ((tfp=fdopen(tfd, "r+b")) == NULL) {
264 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
273 According to Pierre Joye, Windows in some environments per
274 default creates text files, so force binary mode.
276 _setmode(_fileno(tfp), _O_BINARY );
285 #ifdef HAVE_CLONEFILE
287 static create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset)
292 if (offset > ZIP_OFF_MAX) {
293 zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG);
297 if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
298 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
301 sprintf(temp, "%s.XXXXXX", ctx->fname);
303 if (mktemp(temp) == NULL) {
304 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
309 if (clonefile(ctx->fname, temp, 0) < 0) {
310 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
314 if ((tfp=fopen(temp, "r+b")) == NULL) {
315 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
320 if (ftruncate(fileno(tfp), (off_t)offset) < 0
321 || fseeko(tfp, (off_t)offset, SEEK_SET) < 0) {
322 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
337 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
339 struct read_file *ctx;
344 ctx = (struct read_file *)state;
348 case ZIP_SOURCE_BEGIN_WRITE:
349 if (ctx->fname == NULL) {
350 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
353 return create_temp_output(ctx);
355 #ifdef HAVE_CLONEFILE
356 case ZIP_SOURCE_BEGIN_WRITE_CLONING:
357 if (ctx->fname == NULL) {
358 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
361 return create_temp_output_cloning(ctx, len);
364 case ZIP_SOURCE_COMMIT_WRITE: {
367 if (fclose(ctx->fout) < 0) {
369 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
372 if (rename(ctx->tmpname, ctx->fname) < 0) {
373 zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
378 /* not much we can do if chmod fails except make the whole commit fail */
379 (void)chmod(ctx->fname, 0666&~mask);
385 case ZIP_SOURCE_CLOSE:
392 case ZIP_SOURCE_ERROR:
393 return zip_error_to_data(&ctx->error, data, len);
395 case ZIP_SOURCE_FREE:
403 case ZIP_SOURCE_OPEN:
405 if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) {
406 zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
411 if (ctx->start > 0) {
412 if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
413 /* TODO: skip by reading */
420 case ZIP_SOURCE_READ:
422 n = ctx->end - ctx->current;
434 if ((i=fread(buf, 1, (size_t)n, ctx->f)) == 0) {
435 if (ferror(ctx->f)) {
436 zip_error_set(&ctx->error, ZIP_ER_READ, errno);
442 return (zip_int64_t)i;
444 case ZIP_SOURCE_REMOVE:
445 if (remove(ctx->fname) < 0) {
446 zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
451 case ZIP_SOURCE_ROLLBACK_WRITE:
456 (void)remove(ctx->tmpname);
461 case ZIP_SOURCE_SEEK: {
462 zip_int64_t new_current;
464 zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
471 switch (args->whence) {
473 new_current = args->offset;
478 if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
481 if ((new_current = ftello(ctx->f)) < 0) {
482 zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
485 new_current -= (zip_int64_t)ctx->start;
489 new_current = (zip_int64_t)ctx->end + args->offset;
494 new_current = (zip_int64_t)ctx->current + args->offset;
498 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
502 if (new_current < 0 || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)
503 || (zip_uint64_t)new_current + ctx->start < ctx->start) {
504 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
508 ctx->current = (zip_uint64_t)new_current;
511 if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
518 case ZIP_SOURCE_SEEK_WRITE: {
519 zip_source_args_seek_t *args;
521 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
526 if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
532 case ZIP_SOURCE_STAT: {
533 if (len < sizeof(ctx->st))
536 if (zip_error_code_zip(&ctx->stat_error) != 0) {
537 zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
541 memcpy(data, &ctx->st, sizeof(ctx->st));
542 return sizeof(ctx->st);
545 case ZIP_SOURCE_SUPPORTS:
546 return ctx->supports;
548 case ZIP_SOURCE_TELL:
549 return (zip_int64_t)ctx->current;
551 case ZIP_SOURCE_TELL_WRITE:
553 off_t ret = ftello(ctx->fout);
556 zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
562 case ZIP_SOURCE_WRITE:
567 ret = fwrite(data, 1, len, ctx->fout);
568 if (ret != len || ferror(ctx->fout)) {
569 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
573 return (zip_int64_t)ret;
577 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
584 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error)
586 if (offset > ZIP_INT64_MAX) {
587 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
590 return _zip_fseek(f, (zip_int64_t)offset, whence, error);
595 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error)
597 if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
598 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
601 if (fseeko(f, (off_t)offset, whence) < 0) {
602 zip_error_set(error, ZIP_ER_SEEK, errno);