Imported Upstream version 1.2.0
[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
119     if (file == NULL && fname == NULL) {
120         zip_error_set(error, ZIP_ER_INVAL, 0);
121         return NULL;
122     }
123
124     if (len < 0) {
125         len = 0;
126     }
127
128     if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
129         zip_error_set(error, ZIP_ER_INVAL, 0);
130         return NULL;
131     }
132
133     if ((ctx=(struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
134         zip_error_set(error, ZIP_ER_MEMORY, 0);
135         return NULL;
136     }
137
138     ctx->fname = NULL;
139     if (fname) {
140         if ((ctx->fname=strdup(fname)) == NULL) {
141             zip_error_set(error, ZIP_ER_MEMORY, 0);
142             free(ctx);
143             return NULL;
144         }
145     }
146     ctx->f = file;
147     ctx->start = start;
148     ctx->end = (zip_uint64_t)len;
149     if (st) {
150         memcpy(&ctx->st, st, sizeof(ctx->st));
151         ctx->st.name = NULL;
152         ctx->st.valid &= ~ZIP_STAT_NAME;
153     }
154     else {
155         zip_stat_init(&ctx->st);
156     }
157
158     if (ctx->end > 0) {
159         ctx->st.size = ctx->end;
160         ctx->st.valid |= ZIP_STAT_SIZE;
161     }
162
163     zip_error_init(&ctx->stat_error);
164
165     ctx->tmpname = NULL;
166     ctx->fout = NULL;
167
168     zip_error_init(&ctx->error);
169
170     ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
171
172     if (ctx->fname) {
173         struct stat sb;
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;
178             }
179         }
180         else {
181             if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
182                 ctx->st.mtime = sb.st_mtime;
183                 ctx->st.valid |= ZIP_STAT_MTIME;
184             }
185             if (S_ISREG(sb.st_mode)) {
186                 ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
187
188                 if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
189                     zip_error_set(error, ZIP_ER_INVAL, 0);
190                     free(ctx->fname);
191                     free(ctx);
192                     return NULL;
193                 }
194
195                 if (ctx->end == 0) {
196                     ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
197                     ctx->st.valid |= ZIP_STAT_SIZE;
198
199                     if (start == 0) {
200                         ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
201                     }
202                 }
203             }
204         }
205     }
206     else if (fseeko(ctx->f, 0, SEEK_CUR) == 0) {
207         ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
208     }
209
210     if ((zs=zip_source_function_create(read_file, ctx, error)) == NULL) {
211         free(ctx->fname);
212         free(ctx);
213         return NULL;
214     }
215
216     return zs;
217 }
218
219
220 static int
221 create_temp_output(struct read_file *ctx)
222 {
223     char *temp;
224     int tfd;
225     mode_t mask;
226     FILE *tfp;
227
228     if ((temp=(char *)malloc(strlen(ctx->fname)+8)) == NULL) {
229         zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
230         return -1;
231     }
232     sprintf(temp, "%s.XXXXXX", ctx->fname);
233
234     mask = umask(_SAFE_MASK);
235     if ((tfd=mkstemp(temp)) == -1) {
236         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
237         umask(mask);
238         free(temp);
239         return -1;
240     }
241     umask(mask);
242
243     if ((tfp=fdopen(tfd, "r+b")) == NULL) {
244         zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
245         close(tfd);
246         (void)remove(temp);
247         free(temp);
248         return -1;
249     }
250
251 #ifdef _WIN32
252     /*
253      According to Pierre Joye, Windows in some environments per
254      default creates text files, so force binary mode.
255      */
256     _setmode(_fileno(tfp), _O_BINARY );
257 #endif
258
259     ctx->fout = tfp;
260     ctx->tmpname = temp;
261
262     return 0;
263 }
264
265
266 static zip_int64_t
267 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd)
268 {
269     struct read_file *ctx;
270     char *buf;
271     zip_uint64_t n;
272     size_t i;
273
274     ctx = (struct read_file *)state;
275     buf = (char *)data;
276
277     switch (cmd) {
278         case ZIP_SOURCE_BEGIN_WRITE:
279             if (ctx->fname == NULL) {
280                 zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
281                 return -1;
282             }
283             return create_temp_output(ctx);
284
285         case ZIP_SOURCE_COMMIT_WRITE: {
286             mode_t mask;
287
288             if (fclose(ctx->fout) < 0) {
289                 ctx->fout = NULL;
290                 zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
291             }
292             ctx->fout = NULL;
293             if (rename(ctx->tmpname, ctx->fname) < 0) {
294                 zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
295                 return -1;
296             }
297             mask = umask(022);
298             umask(mask);
299             /* not much we can do if chmod fails except make the whole commit fail */
300             (void)chmod(ctx->fname, 0666&~mask);
301             free(ctx->tmpname);
302             ctx->tmpname = NULL;
303             return 0;
304         }
305
306         case ZIP_SOURCE_CLOSE:
307             if (ctx->fname) {
308                 fclose(ctx->f);
309                 ctx->f = NULL;
310             }
311             return 0;
312
313         case ZIP_SOURCE_ERROR:
314             return zip_error_to_data(&ctx->error, data, len);
315
316         case ZIP_SOURCE_FREE:
317             free(ctx->fname);
318             free(ctx->tmpname);
319             if (ctx->f)
320                 fclose(ctx->f);
321             free(ctx);
322             return 0;
323
324         case ZIP_SOURCE_OPEN:
325             if (ctx->fname) {
326                 if ((ctx->f=fopen(ctx->fname, "rb")) == NULL) {
327                     zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
328                     return -1;
329                 }
330             }
331
332             if (ctx->start > 0) {
333                 if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
334                     /* TODO: skip by reading */
335                     return -1;
336                 }
337             }
338             ctx->current = 0;
339             return 0;
340
341         case ZIP_SOURCE_READ:
342             if (ctx->end > 0) {
343                 n = ctx->end - ctx->current;
344                 if (n > len) {
345                     n = len;
346                 }
347             }
348             else {
349                 n = len;
350             }
351
352             if (n > SIZE_MAX)
353                 n = SIZE_MAX;
354
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);
358                     return -1;
359                 }
360             }
361             ctx->current += i;
362
363             return (zip_int64_t)i;
364
365         case ZIP_SOURCE_REMOVE:
366             if (remove(ctx->fname) < 0) {
367                 zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
368                 return -1;
369             }
370             return 0;
371
372         case ZIP_SOURCE_ROLLBACK_WRITE:
373             if (ctx->fout) {
374                 fclose(ctx->fout);
375                 ctx->fout = NULL;
376             }
377             (void)remove(ctx->tmpname);
378             free(ctx->tmpname);
379             ctx->tmpname = NULL;
380             return 0;
381
382         case ZIP_SOURCE_SEEK: {
383             zip_int64_t new_current;
384             int need_seek;
385             zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
386
387             if (args == NULL)
388                 return -1;
389
390             need_seek = 1;
391
392             switch (args->whence) {
393                 case SEEK_SET:
394                     new_current = args->offset;
395                     break;
396
397                 case SEEK_END:
398                     if (ctx->end == 0) {
399                         if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
400                             return -1;
401                         }
402                         if ((new_current = ftello(ctx->f)) < 0) {
403                             zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
404                             return -1;
405                         }
406                         new_current -= (zip_int64_t)ctx->start;
407                         need_seek = 0;
408                     }
409                     else {
410                         new_current = (zip_int64_t)ctx->end + args->offset;
411                     }
412                     break;
413
414                 case SEEK_CUR:
415                     new_current = (zip_int64_t)ctx->current + args->offset;
416                     break;
417
418                 default:
419                     zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
420                     return -1;
421             }
422
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);
426                 return -1;
427             }
428
429             ctx->current = (zip_uint64_t)new_current;
430
431             if (need_seek) {
432                 if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
433                     return -1;
434                 }
435             }
436             return 0;
437         }
438
439         case ZIP_SOURCE_SEEK_WRITE: {
440             zip_source_args_seek_t *args;
441
442             args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
443             if (args == NULL) {
444                 return -1;
445             }
446
447             if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
448                 return -1;
449             }
450             return 0;
451         }
452
453         case ZIP_SOURCE_STAT: {
454             if (len < sizeof(ctx->st))
455                 return -1;
456
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));
459                 return -1;
460             }
461
462             memcpy(data, &ctx->st, sizeof(ctx->st));
463             return sizeof(ctx->st);
464         }
465
466         case ZIP_SOURCE_SUPPORTS:
467             return ctx->supports;
468
469         case ZIP_SOURCE_TELL:
470             return (zip_int64_t)ctx->current;
471
472         case ZIP_SOURCE_TELL_WRITE:
473         {
474             off_t ret = ftello(ctx->fout);
475
476             if (ret < 0) {
477                 zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
478                 return -1;
479             }
480             return ret;
481         }
482
483         case ZIP_SOURCE_WRITE:
484         {
485             size_t ret;
486
487             clearerr(ctx->fout);
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);
491                 return -1;
492             }
493
494             return (zip_int64_t)ret;
495         }
496
497         default:
498             zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
499             return -1;
500     }
501 }
502
503
504 static int
505 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error)
506 {
507     if (offset > ZIP_INT64_MAX) {
508         zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
509         return -1;
510     }
511     return _zip_fseek(f, (zip_int64_t)offset, whence, error);
512 }
513
514
515 static int
516 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error)
517 {
518     if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
519         zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
520         return -1;
521     }
522     if (fseeko(f, (off_t)offset, whence) < 0) {
523         zip_error_set(error, ZIP_ER_SEEK, errno);
524         return -1;
525     }
526     return 0;
527 }