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