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