Imported Upstream version 1.3.2
[platform/upstream/libzip.git] / lib / zip_source_filep.c
1 /*
2   zip_source_filep.c -- create data source from FILE *
3   Copyright (C) 1999-2016 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 <sys/stat.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "zipint.h"
40
41 #ifdef HAVE_UNISTD_H
42 #include <unistd.h>
43 #endif
44
45 #ifdef _WIN32
46 /* WIN32 needs <fcntl.h> for _O_BINARY */
47 #include <fcntl.h>
48 #endif
49
50 /* Windows sys/types.h does not provide these */
51 #ifndef S_ISREG
52 #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
53 #endif
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)
58 #else
59 #error do not know safe values for umask, please report this
60 #endif
61
62 #ifdef _MSC_VER
63 /* MSVC doesn't have mode_t */
64 typedef int mode_t;
65 #endif
66
67 struct read_file {
68     zip_error_t error;      /* last error information */
69     zip_int64_t supports;
70
71     /* reading */
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) */
79
80     /* writing */
81     char *tmpname;
82     FILE *fout;
83 };
84
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);
89
90
91 ZIP_EXTERN zip_source_t *
92 zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len)
93 {
94     if (za == NULL)
95         return NULL;
96
97     return zip_source_filep_create(file, start, len, &za->error);
98 }
99
100
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)
103 {
104     if (file == NULL || length < -1) {
105         zip_error_set(error, ZIP_ER_INVAL, 0);
106         return NULL;
107     }
108
109     return _zip_source_file_or_p(NULL, file, start, length, NULL, error);
110 }
111
112
113 zip_source_t *
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)
115 {
116     struct read_file *ctx;
117     zip_source_t *zs;
118     struct stat sb;
119     bool stat_valid;
120
121     if (file == NULL && fname == NULL) {
122         zip_error_set(error, ZIP_ER_INVAL, 0);
123         return NULL;
124     }
125
126     if (len < 0) {
127         len = 0;
128     }
129
130     if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
131         zip_error_set(error, ZIP_ER_INVAL, 0);
132         return NULL;
133     }
134
135     if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
136         zip_error_set(error, ZIP_ER_MEMORY, 0);
137         return NULL;
138     }
139
140     ctx->fname = NULL;
141     if (fname) {
142         if ((ctx->fname=strdup(fname)) == NULL) {
143             zip_error_set(error, ZIP_ER_MEMORY, 0);
144             free(ctx);
145             return NULL;
146         }
147     }
148     ctx->f = file;
149     ctx->start = start;
150     ctx->end = (zip_uint64_t)len;
151     if (st) {
152         memcpy(&ctx->st, st, sizeof(ctx->st));
153         ctx->st.name = NULL;
154         ctx->st.valid &= ~ZIP_STAT_NAME;
155     }
156     else {
157         zip_stat_init(&ctx->st);
158     }
159
160     if (ctx->end > 0) {
161         ctx->st.size = ctx->end;
162         ctx->st.valid |= ZIP_STAT_SIZE;
163     }
164
165     zip_error_init(&ctx->stat_error);
166
167     ctx->tmpname = NULL;
168     ctx->fout = NULL;
169
170     zip_error_init(&ctx->error);
171
172     ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
173
174     if (ctx->fname) {
175         stat_valid = stat(ctx->fname, &sb) >= 0;
176
177         if (!stat_valid) {
178             if (ctx->start == 0 && ctx->end == 0) {
179                 ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
180             }
181         }
182     }
183     else {
184         stat_valid = fstat(fileno(ctx->f), &sb) >= 0;
185     }
186
187     if (!stat_valid) {
188         zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno);
189     }
190     else {
191         if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
192             ctx->st.mtime = sb.st_mtime;
193             ctx->st.valid |= ZIP_STAT_MTIME;
194         }
195         if (S_ISREG(sb.st_mode)) {
196             ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
197
198             if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
199                 zip_error_set(error, ZIP_ER_INVAL, 0);
200                 free(ctx->fname);
201                 free(ctx);
202                 return NULL;
203             }
204
205             if (ctx->end == 0) {
206                 ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
207                 ctx->st.valid |= ZIP_STAT_SIZE;
208                 
209                 if (ctx->fname && start == 0) {
210                     ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
211                 }
212             }
213         }
214     }
215
216     if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
217         free(ctx->fname);
218         free(ctx);
219         return NULL;
220     }
221
222     return zs;
223 }
224
225
226 static int
227 create_temp_output(struct read_file *ctx)
228 {
229     char *temp;
230     int tfd;
231     mode_t mask;
232     FILE *tfp;
233
234     if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
235         zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
236         return -1;
237     }
238     sprintf(temp, "%s.XXXXXX", ctx->fname);
239
240     mask = umask(_SAFE_MASK);
241     if ((tfd=mkstemp(temp)) == -1) {
242         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
243         umask(mask);
244         free(temp);
245         return -1;
246     }
247     umask(mask);
248
249     if ((tfp=fdopen(tfd, "r+b")) == NULL) {
250         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
251         close(tfd);
252         (void)remove(temp);
253         free(temp);
254         return -1;
255     }
256
257 #ifdef _WIN32
258     /*
259      According to Pierre Joye, Windows in some environments per
260      default creates text files, so force binary mode.
261      */
262     _setmode(_fileno(tfp), _O_BINARY );
263 #endif
264
265     ctx->fout = tfp;
266     ctx->tmpname = temp;
267
268     return 0;
269 }
270
271
272 static zip_int64_t
273 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
274 {
275     struct read_file *ctx;
276     char *buf;
277     zip_uint64_t n;
278     size_t i;
279
280     ctx = (struct read_file *)state;
281     buf = (char *)data;
282
283     switch (cmd) {
284         case ZIP_SOURCE_BEGIN_WRITE:
285             if (ctx->fname == NULL) {
286                 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
287                 return -1;
288             }
289             return create_temp_output(ctx);
290
291         case ZIP_SOURCE_COMMIT_WRITE: {
292             mode_t mask;
293
294             if (fclose(ctx->fout) < 0) {
295                 ctx->fout = NULL;
296                 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
297             }
298             ctx->fout = NULL;
299             if (rename(ctx->tmpname, ctx->fname) < 0) {
300                 zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
301                 return -1;
302             }
303             mask = umask(022);
304             umask(mask);
305             /* not much we can do if chmod fails except make the whole commit fail */
306             (void)chmod(ctx->fname, 0666&~mask);
307             free(ctx->tmpname);
308             ctx->tmpname = NULL;
309             return 0;
310         }
311
312         case ZIP_SOURCE_CLOSE:
313             if (ctx->fname) {
314                 fclose(ctx->f);
315                 ctx->f = NULL;
316             }
317             return 0;
318
319         case ZIP_SOURCE_ERROR:
320             return zip_error_to_data(&ctx->error, data, len);
321
322         case ZIP_SOURCE_FREE:
323             free(ctx->fname);
324             free(ctx->tmpname);
325             if (ctx->f)
326                 fclose(ctx->f);
327             free(ctx);
328             return 0;
329
330         case ZIP_SOURCE_OPEN:
331             if (ctx->fname) {
332                 if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) {
333                     zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
334                     return -1;
335                 }
336             }
337
338             if (ctx->start > 0) {
339                 if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
340                     /* TODO: skip by reading */
341                     return -1;
342                 }
343             }
344             ctx->current = 0;
345             return 0;
346
347         case ZIP_SOURCE_READ:
348             if (ctx->end > 0) {
349                 n = ctx->end - ctx->current;
350                 if (n > len) {
351                     n = len;
352                 }
353             }
354             else {
355                 n = len;
356             }
357
358             if (n > SIZE_MAX)
359                 n = SIZE_MAX;
360
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);
364                     return -1;
365                 }
366             }
367             ctx->current += i;
368
369             return (zip_int64_t)i;
370
371         case ZIP_SOURCE_REMOVE:
372             if (remove(ctx->fname) < 0) {
373                 zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
374                 return -1;
375             }
376             return 0;
377
378         case ZIP_SOURCE_ROLLBACK_WRITE:
379             if (ctx->fout) {
380                 fclose(ctx->fout);
381                 ctx->fout = NULL;
382             }
383             (void)remove(ctx->tmpname);
384             free(ctx->tmpname);
385             ctx->tmpname = NULL;
386             return 0;
387
388         case ZIP_SOURCE_SEEK: {
389             zip_int64_t new_current;
390             int need_seek;
391             zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
392
393             if (args == NULL)
394                 return -1;
395
396             need_seek = 1;
397
398             switch (args->whence) {
399                 case SEEK_SET:
400                     new_current = args->offset;
401                     break;
402
403                 case SEEK_END:
404                     if (ctx->end == 0) {
405                         if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
406                             return -1;
407                         }
408                         if ((new_current = ftello(ctx->f)) < 0) {
409                             zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
410                             return -1;
411                         }
412                         new_current -= (zip_int64_t)ctx->start;
413                         need_seek = 0;
414                     }
415                     else {
416                         new_current = (zip_int64_t)ctx->end + args->offset;
417                     }
418                     break;
419
420                 case SEEK_CUR:
421                     new_current = (zip_int64_t)ctx->current + args->offset;
422                     break;
423
424                 default:
425                     zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
426                     return -1;
427             }
428
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);
432                 return -1;
433             }
434
435             ctx->current = (zip_uint64_t)new_current;
436
437             if (need_seek) {
438                 if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
439                     return -1;
440                 }
441             }
442             return 0;
443         }
444
445         case ZIP_SOURCE_SEEK_WRITE: {
446             zip_source_args_seek_t *args;
447
448             args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
449             if (args == NULL) {
450                 return -1;
451             }
452
453             if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
454                 return -1;
455             }
456             return 0;
457         }
458
459         case ZIP_SOURCE_STAT: {
460             if (len < sizeof(ctx->st))
461                 return -1;
462
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));
465                 return -1;
466             }
467
468             memcpy(data, &ctx->st, sizeof(ctx->st));
469             return sizeof(ctx->st);
470         }
471
472         case ZIP_SOURCE_SUPPORTS:
473             return ctx->supports;
474
475         case ZIP_SOURCE_TELL:
476             return (zip_int64_t)ctx->current;
477
478         case ZIP_SOURCE_TELL_WRITE:
479         {
480             off_t ret = ftello(ctx->fout);
481
482             if (ret < 0) {
483                 zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
484                 return -1;
485             }
486             return ret;
487         }
488
489         case ZIP_SOURCE_WRITE:
490         {
491             size_t ret;
492
493             clearerr(ctx->fout);
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);
497                 return -1;
498             }
499
500             return (zip_int64_t)ret;
501         }
502
503         default:
504             zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
505             return -1;
506     }
507 }
508
509
510 static int
511 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error)
512 {
513     if (offset > ZIP_INT64_MAX) {
514         zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
515         return -1;
516     }
517     return _zip_fseek(f, (zip_int64_t)offset, whence, error);
518 }
519
520
521 static int
522 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error)
523 {
524     if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
525         zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
526         return -1;
527     }
528     if (fseeko(f, (off_t)offset, whence) < 0) {
529         zip_error_set(error, ZIP_ER_SEEK, errno);
530         return -1;
531     }
532     return 0;
533 }