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;
119 if (file == NULL && fname == NULL) {
120 zip_error_set(error, ZIP_ER_INVAL, 0);
128 if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
129 zip_error_set(error, ZIP_ER_INVAL, 0);
133 if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
134 zip_error_set(error, ZIP_ER_MEMORY, 0);
140 if ((ctx->fname=strdup(fname)) == NULL) {
141 zip_error_set(error, ZIP_ER_MEMORY, 0);
148 ctx->end = (zip_uint64_t)len;
150 memcpy(&ctx->st, st, sizeof(ctx->st));
152 ctx->st.valid &= ~ZIP_STAT_NAME;
155 zip_stat_init(&ctx->st);
159 ctx->st.size = ctx->end;
160 ctx->st.valid |= ZIP_STAT_SIZE;
163 zip_error_init(&ctx->stat_error);
168 zip_error_init(&ctx->error);
170 ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
174 if (stat(ctx->fname, &sb) < 0) {
175 zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno);
176 if (ctx->start == 0 && ctx->end == 0) {
177 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
181 if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
182 ctx->st.mtime = sb.st_mtime;
183 ctx->st.valid |= ZIP_STAT_MTIME;
185 if (S_ISREG(sb.st_mode)) {
186 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
188 if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
189 zip_error_set(error, ZIP_ER_INVAL, 0);
196 ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
197 ctx->st.valid |= ZIP_STAT_SIZE;
200 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
206 else if (fseeko(ctx->f, 0, SEEK_CUR) == 0) {
207 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
210 if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
221 create_temp_output(struct read_file *ctx)
228 if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
229 zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
232 sprintf(temp, "%s.XXXXXX", ctx->fname);
234 mask = umask(_SAFE_MASK);
235 if ((tfd=mkstemp(temp)) == -1) {
236 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
243 if ((tfp=fdopen(tfd, "r+b")) == NULL) {
244 zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
253 According to Pierre Joye, Windows in some environments per
254 default creates text files, so force binary mode.
256 _setmode(_fileno(tfp), _O_BINARY );
267 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
269 struct read_file *ctx;
274 ctx = (struct read_file *)state;
278 case ZIP_SOURCE_BEGIN_WRITE:
279 if (ctx->fname == NULL) {
280 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
283 return create_temp_output(ctx);
285 case ZIP_SOURCE_COMMIT_WRITE: {
288 if (fclose(ctx->fout) < 0) {
290 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
293 if (rename(ctx->tmpname, ctx->fname) < 0) {
294 zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
299 /* not much we can do if chmod fails except make the whole commit fail */
300 (void)chmod(ctx->fname, 0666&~mask);
306 case ZIP_SOURCE_CLOSE:
313 case ZIP_SOURCE_ERROR:
314 return zip_error_to_data(&ctx->error, data, len);
316 case ZIP_SOURCE_FREE:
324 case ZIP_SOURCE_OPEN:
326 if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) {
327 zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
332 if (ctx->start > 0) {
333 if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
334 /* TODO: skip by reading */
341 case ZIP_SOURCE_READ:
343 n = ctx->end - ctx->current;
355 if ((i=fread(buf, 1, (size_t)n, ctx->f)) == 0) {
356 if (ferror(ctx->f)) {
357 zip_error_set(&ctx->error, ZIP_ER_READ, errno);
363 return (zip_int64_t)i;
365 case ZIP_SOURCE_REMOVE:
366 if (remove(ctx->fname) < 0) {
367 zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
372 case ZIP_SOURCE_ROLLBACK_WRITE:
377 (void)remove(ctx->tmpname);
382 case ZIP_SOURCE_SEEK: {
383 zip_int64_t new_current;
385 zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
392 switch (args->whence) {
394 new_current = args->offset;
399 if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
402 if ((new_current = ftello(ctx->f)) < 0) {
403 zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
406 new_current -= (zip_int64_t)ctx->start;
410 new_current = (zip_int64_t)ctx->end + args->offset;
415 new_current = (zip_int64_t)ctx->current + args->offset;
419 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
423 if (new_current < 0 || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end)
424 || (zip_uint64_t)new_current + ctx->start < ctx->start) {
425 zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
429 ctx->current = (zip_uint64_t)new_current;
432 if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
439 case ZIP_SOURCE_SEEK_WRITE: {
440 zip_source_args_seek_t *args;
442 args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
447 if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
453 case ZIP_SOURCE_STAT: {
454 if (len < sizeof(ctx->st))
457 if (zip_error_code_zip(&ctx->stat_error) != 0) {
458 zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
462 memcpy(data, &ctx->st, sizeof(ctx->st));
463 return sizeof(ctx->st);
466 case ZIP_SOURCE_SUPPORTS:
467 return ctx->supports;
469 case ZIP_SOURCE_TELL:
470 return (zip_int64_t)ctx->current;
472 case ZIP_SOURCE_TELL_WRITE:
474 off_t ret = ftello(ctx->fout);
477 zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
483 case ZIP_SOURCE_WRITE:
488 ret = fwrite(data, 1, len, ctx->fout);
489 if (ret != len || ferror(ctx->fout)) {
490 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
494 return (zip_int64_t)ret;
498 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
505 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error)
507 if (offset > ZIP_INT64_MAX) {
508 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
511 return _zip_fseek(f, (zip_int64_t)offset, whence, error);
516 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error)
518 if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
519 zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
522 if (fseeko(f, (off_t)offset, whence) < 0) {
523 zip_error_set(error, ZIP_ER_SEEK, errno);