1 /*
2   zip_source_filep.c -- create data source from FILE *
3   Copyright (C) 1999-2019 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 <fcntl.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 #include "zipint.h"
42 
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46 
47 #ifdef HAVE_CLONEFILE
48 #include <sys/attr.h>
49 #include <sys/clonefile.h>
50 #define CAN_CLONE
51 #endif
52 #ifdef HAVE_FICLONERANGE
53 #include <linux/fs.h>
54 #include <sys/ioctl.h>
55 #define CAN_CLONE
56 #endif
57 
58 struct read_file {
59     zip_error_t error; /* last error information */
60     zip_int64_t supports;
61 
62     /* reading */
63     char *fname;            /* name of file to read from */
64     FILE *f;                /* file to read from */
65     struct zip_stat st;     /* stat information passed in */
66     zip_error_t stat_error; /* error returned for stat */
67     zip_uint64_t start;     /* start offset of data to read */
68     zip_uint64_t end;       /* end offset of data to read relative to start, 0 for up to EOF */
69     zip_uint64_t current;   /* current offset relative to start (0 is beginning of part we read) */
70 
71     /* writing */
72     char *tmpname;
73     FILE *fout;
74 };
75 
76 static zip_int64_t read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd);
77 static int create_temp_output(struct read_file *ctx);
78 #ifdef CAN_CLONE
79 static zip_int64_t create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset);
80 #endif
81 static FILE *_zip_fopen(const char *name, bool writeable);
82 static int _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error);
83 static int _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error);
84 
85 
86 ZIP_EXTERN zip_source_t *
zip_source_filep(zip_t * za,FILE * file,zip_uint64_t start,zip_int64_t len)87 zip_source_filep(zip_t *za, FILE *file, zip_uint64_t start, zip_int64_t len) {
88     if (za == NULL)
89 	return NULL;
90 
91     return zip_source_filep_create(file, start, len, &za->error);
92 }
93 
94 
95 ZIP_EXTERN zip_source_t *
zip_source_filep_create(FILE * file,zip_uint64_t start,zip_int64_t length,zip_error_t * error)96 zip_source_filep_create(FILE *file, zip_uint64_t start, zip_int64_t length, zip_error_t *error) {
97     if (file == NULL || length < -1) {
98 	zip_error_set(error, ZIP_ER_INVAL, 0);
99 	return NULL;
100     }
101 
102     return _zip_source_file_or_p(NULL, file, start, length, NULL, error);
103 }
104 
105 
106 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)107 _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) {
108     struct read_file *ctx;
109     zip_source_t *zs;
110     struct stat sb;
111     bool stat_valid;
112 
113     if (file == NULL && fname == NULL) {
114 	zip_error_set(error, ZIP_ER_INVAL, 0);
115 	return NULL;
116     }
117 
118     if (len < 0) {
119 	len = 0;
120     }
121 
122     if (start > ZIP_INT64_MAX || start + (zip_uint64_t)len < start) {
123 	zip_error_set(error, ZIP_ER_INVAL, 0);
124 	return NULL;
125     }
126 
127     if ((ctx = (struct read_file *)malloc(sizeof(struct read_file))) == NULL) {
128 	zip_error_set(error, ZIP_ER_MEMORY, 0);
129 	return NULL;
130     }
131 
132     ctx->fname = NULL;
133     if (fname) {
134 	if ((ctx->fname = strdup(fname)) == NULL) {
135 	    zip_error_set(error, ZIP_ER_MEMORY, 0);
136 	    free(ctx);
137 	    return NULL;
138 	}
139     }
140     ctx->f = file;
141     ctx->start = start;
142     ctx->end = (zip_uint64_t)len;
143     if (st) {
144 	memcpy(&ctx->st, st, sizeof(ctx->st));
145 	ctx->st.name = NULL;
146 	ctx->st.valid &= ~ZIP_STAT_NAME;
147     }
148     else {
149 	zip_stat_init(&ctx->st);
150     }
151 
152     if (ctx->end > 0) {
153 	ctx->st.size = ctx->end;
154 	ctx->st.valid |= ZIP_STAT_SIZE;
155     }
156 
157     zip_error_init(&ctx->stat_error);
158 
159     ctx->tmpname = NULL;
160     ctx->fout = NULL;
161 
162     zip_error_init(&ctx->error);
163 
164     ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
165 
166     if (ctx->fname) {
167 	stat_valid = stat(ctx->fname, &sb) >= 0;
168 
169 	if (!stat_valid) {
170 	    if (ctx->start == 0 && ctx->end == 0) {
171 		ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
172 	    }
173 	}
174     }
175     else {
176 	stat_valid = fstat(fileno(ctx->f), &sb) >= 0;
177     }
178 
179     if (!stat_valid) {
180 	zip_error_set(&ctx->stat_error, ZIP_ER_READ, errno);
181     }
182     else {
183 	if ((ctx->st.valid & ZIP_STAT_MTIME) == 0) {
184 	    ctx->st.mtime = sb.st_mtime;
185 	    ctx->st.valid |= ZIP_STAT_MTIME;
186 	}
187 	if (S_ISREG(sb.st_mode)) {
188 	    ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
189 
190 	    if (ctx->start + ctx->end > (zip_uint64_t)sb.st_size) {
191 		zip_error_set(error, ZIP_ER_INVAL, 0);
192 		free(ctx->fname);
193 		free(ctx);
194 		return NULL;
195 	    }
196 
197 	    if (ctx->end == 0) {
198 		ctx->st.size = (zip_uint64_t)sb.st_size - ctx->start;
199 		ctx->st.valid |= ZIP_STAT_SIZE;
200 
201 		if (ctx->fname && start == 0) {
202 		    ctx->supports = ZIP_SOURCE_SUPPORTS_WRITABLE;
203 		}
204 	    }
205 	}
206     }
207 
208     ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY);
209 #ifdef CAN_CLONE
210     if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) {
211 	ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING);
212     }
213 #endif
214 
215     if ((zs = zip_source_function_create(read_file, ctx, error)) == NULL) {
216 	free(ctx->fname);
217 	free(ctx);
218 	return NULL;
219     }
220 
221     return zs;
222 }
223 
224 
225 static int
create_temp_output(struct read_file * ctx)226 create_temp_output(struct read_file *ctx) {
227     char *temp;
228     int tfd;
229     int mode;
230     FILE *tfp;
231     struct stat st;
232 
233     if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) {
234 	zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
235 	return -1;
236     }
237 
238     if (stat(ctx->fname, &st) == 0) {
239 	mode = st.st_mode;
240     }
241     else {
242 	mode = -1;
243     }
244 
245     sprintf(temp, "%s.XXXXXX", ctx->fname);
246 
247     if ((tfd = _zip_mkstempm(temp, mode)) == -1) {
248 	zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
249 	free(temp);
250 	return -1;
251     }
252 
253     if ((tfp = fdopen(tfd, "r+b")) == NULL) {
254 	zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
255 	close(tfd);
256 	(void)remove(temp);
257 	free(temp);
258 	return -1;
259     }
260 
261     ctx->fout = tfp;
262     ctx->tmpname = temp;
263 
264     return 0;
265 }
266 
267 #ifdef CAN_CLONE
create_temp_output_cloning(struct read_file * ctx,zip_uint64_t offset)268 zip_int64_t static create_temp_output_cloning(struct read_file *ctx, zip_uint64_t offset) {
269     char *temp;
270     FILE *tfp;
271 
272     if (offset > ZIP_OFF_MAX) {
273 	zip_error_set(&ctx->error, ZIP_ER_SEEK, E2BIG);
274 	return -1;
275     }
276 
277     if ((temp = (char *)malloc(strlen(ctx->fname) + 8)) == NULL) {
278 	zip_error_set(&ctx->error, ZIP_ER_MEMORY, 0);
279 	return -1;
280     }
281     sprintf(temp, "%s.XXXXXX", ctx->fname);
282 
283 #ifdef HAVE_CLONEFILE
284 #ifndef __clang_analyzer__
285     /* we can't use mkstemp, since clonefile insists on creating the file */
286     if (mktemp(temp) == NULL) {
287 	zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
288 	free(temp);
289 	return -1;
290     }
291 #endif
292 
293     if (clonefile(ctx->fname, temp, 0) < 0) {
294 	zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
295 	free(temp);
296 	return -1;
297     }
298     if ((tfp = _zip_fopen(temp, true)) == NULL) {
299 	zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
300 	(void)remove(temp);
301 	free(temp);
302 	return -1;
303     }
304 #else
305     {
306 	int fd;
307 	struct file_clone_range range;
308 	struct stat st;
309 
310 	if (fstat(fileno(ctx->f), &st) < 0) {
311 	    zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
312 	    return -1;
313 	}
314 
315 	if ((fd = mkstemp(temp)) < 0) {
316 	    zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
317 	    free(temp);
318 	    return -1;
319 	}
320 
321 	range.src_fd = fileno(ctx->f);
322 	range.src_offset = 0;
323 	range.src_length = ((offset + st.st_blksize - 1) / st.st_blksize) * st.st_blksize;
324 	if (range.src_length > st.st_size) {
325 	    range.src_length = 0;
326 	}
327 	range.dest_offset = 0;
328 	if (ioctl(fd, FICLONERANGE, &range) < 0) {
329 	    zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
330 	    (void)close(fd);
331 	    (void)remove(temp);
332 	    free(temp);
333 	    return -1;
334 	}
335 
336 	if ((tfp = fdopen(fd, "r+b")) == NULL) {
337 	    zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
338 	    (void)close(fd);
339 	    (void)remove(temp);
340 	    free(temp);
341 	    return -1;
342 	}
343     }
344 #endif
345 
346     if (ftruncate(fileno(tfp), (off_t)offset) < 0) {
347 	(void)fclose(tfp);
348 	(void)remove(temp);
349 	free(temp);
350 	return -1;
351     }
352     if (fseeko(tfp, (off_t)offset, SEEK_SET) < 0) {
353 	(void)fclose(tfp);
354 	(void)remove(temp);
355 	free(temp);
356 	zip_error_set(&ctx->error, ZIP_ER_TMPOPEN, errno);
357     }
358 
359     ctx->fout = tfp;
360     ctx->tmpname = temp;
361 
362     return 0;
363 }
364 #endif
365 
366 
367 static zip_int64_t
read_file(void * state,void * data,zip_uint64_t len,zip_source_cmd_t cmd)368 read_file(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
369     struct read_file *ctx;
370     char *buf;
371     zip_uint64_t n;
372     size_t i;
373 
374     ctx = (struct read_file *)state;
375     buf = (char *)data;
376 
377     switch (cmd) {
378     case ZIP_SOURCE_ACCEPT_EMPTY:
379 	return 0;
380 
381     case ZIP_SOURCE_BEGIN_WRITE:
382 #ifdef _WIN32
383 	return -1;
384 #else
385 	if (ctx->fname == NULL) {
386 	    zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
387 	    return -1;
388 	}
389 	return create_temp_output(ctx);
390 #endif
391 
392 #ifdef CAN_CLONE
393     case ZIP_SOURCE_BEGIN_WRITE_CLONING:
394 	if (ctx->fname == NULL) {
395 	    zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
396 	    return -1;
397 	}
398 	return create_temp_output_cloning(ctx, len);
399 #endif
400 
401     case ZIP_SOURCE_CLOSE:
402 	if (ctx->fname) {
403 	    fclose(ctx->f);
404 	    ctx->f = NULL;
405 	}
406 	return 0;
407 
408     case ZIP_SOURCE_COMMIT_WRITE: {
409 	if (fclose(ctx->fout) < 0) {
410 	    ctx->fout = NULL;
411 	    zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
412 	}
413 	ctx->fout = NULL;
414 	if (rename(ctx->tmpname, ctx->fname) < 0) {
415 	    zip_error_set(&ctx->error, ZIP_ER_RENAME, errno);
416 	    return -1;
417 	}
418 	free(ctx->tmpname);
419 	ctx->tmpname = NULL;
420 	return 0;
421     }
422 
423     case ZIP_SOURCE_ERROR:
424 	return zip_error_to_data(&ctx->error, data, len);
425 
426     case ZIP_SOURCE_FREE:
427 	free(ctx->fname);
428 	free(ctx->tmpname);
429 	if (ctx->f)
430 	    fclose(ctx->f);
431 	free(ctx);
432 	return 0;
433 
434     case ZIP_SOURCE_OPEN:
435 	if (ctx->fname) {
436 	    if ((ctx->f = _zip_fopen(ctx->fname, false)) == NULL) {
437 		zip_error_set(&ctx->error, ZIP_ER_OPEN, errno);
438 		return -1;
439 	    }
440 	}
441 
442 	if (ctx->start > 0) {
443 	    if (_zip_fseek_u(ctx->f, ctx->start, SEEK_SET, &ctx->error) < 0) {
444 		/* TODO: skip by reading */
445 		return -1;
446 	    }
447 	}
448 	ctx->current = 0;
449 	return 0;
450 
451     case ZIP_SOURCE_READ:
452 	if (ctx->end > 0) {
453 	    n = ctx->end - ctx->current;
454 	    if (n > len) {
455 		n = len;
456 	    }
457 	}
458 	else {
459 	    n = len;
460 	}
461 
462 	if (n > SIZE_MAX)
463 	    n = SIZE_MAX;
464 
465 	if ((i = fread(buf, 1, (size_t)n, ctx->f)) == 0) {
466 	    if (ferror(ctx->f)) {
467 		zip_error_set(&ctx->error, ZIP_ER_READ, errno);
468 		return -1;
469 	    }
470 	}
471 	ctx->current += i;
472 
473 	return (zip_int64_t)i;
474 
475     case ZIP_SOURCE_REMOVE:
476 	if (remove(ctx->fname) < 0) {
477 	    zip_error_set(&ctx->error, ZIP_ER_REMOVE, errno);
478 	    return -1;
479 	}
480 	return 0;
481 
482     case ZIP_SOURCE_ROLLBACK_WRITE:
483 	if (ctx->fout) {
484 	    fclose(ctx->fout);
485 	    ctx->fout = NULL;
486 	}
487 	(void)remove(ctx->tmpname);
488 	free(ctx->tmpname);
489 	ctx->tmpname = NULL;
490 	return 0;
491 
492     case ZIP_SOURCE_SEEK: {
493 	zip_int64_t new_current;
494 	int need_seek;
495 	zip_source_args_seek_t *args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
496 
497 	if (args == NULL)
498 	    return -1;
499 
500 	need_seek = 1;
501 
502 	switch (args->whence) {
503 	case SEEK_SET:
504 	    new_current = args->offset;
505 	    break;
506 
507 	case SEEK_END:
508 	    if (ctx->end == 0) {
509 		if (_zip_fseek(ctx->f, args->offset, SEEK_END, &ctx->error) < 0) {
510 		    return -1;
511 		}
512 		if ((new_current = ftello(ctx->f)) < 0) {
513 		    zip_error_set(&ctx->error, ZIP_ER_SEEK, errno);
514 		    return -1;
515 		}
516 		new_current -= (zip_int64_t)ctx->start;
517 		need_seek = 0;
518 	    }
519 	    else {
520 		new_current = (zip_int64_t)ctx->end + args->offset;
521 	    }
522 	    break;
523 
524 	case SEEK_CUR:
525 	    new_current = (zip_int64_t)ctx->current + args->offset;
526 	    break;
527 
528 	default:
529 	    zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
530 	    return -1;
531 	}
532 
533 	if (new_current < 0 || (ctx->end != 0 && (zip_uint64_t)new_current > ctx->end) || (zip_uint64_t)new_current + ctx->start < ctx->start) {
534 	    zip_error_set(&ctx->error, ZIP_ER_INVAL, 0);
535 	    return -1;
536 	}
537 
538 	ctx->current = (zip_uint64_t)new_current;
539 
540 	if (need_seek) {
541 	    if (_zip_fseek_u(ctx->f, ctx->current + ctx->start, SEEK_SET, &ctx->error) < 0) {
542 		return -1;
543 	    }
544 	}
545 	return 0;
546     }
547 
548     case ZIP_SOURCE_SEEK_WRITE: {
549 	zip_source_args_seek_t *args;
550 
551 	args = ZIP_SOURCE_GET_ARGS(zip_source_args_seek_t, data, len, &ctx->error);
552 	if (args == NULL) {
553 	    return -1;
554 	}
555 
556 	if (_zip_fseek(ctx->fout, args->offset, args->whence, &ctx->error) < 0) {
557 	    return -1;
558 	}
559 	return 0;
560     }
561 
562     case ZIP_SOURCE_STAT: {
563 	if (len < sizeof(ctx->st))
564 	    return -1;
565 
566 	if (zip_error_code_zip(&ctx->stat_error) != 0) {
567 	    zip_error_set(&ctx->error, zip_error_code_zip(&ctx->stat_error), zip_error_code_system(&ctx->stat_error));
568 	    return -1;
569 	}
570 
571 	memcpy(data, &ctx->st, sizeof(ctx->st));
572 	return sizeof(ctx->st);
573     }
574 
575     case ZIP_SOURCE_SUPPORTS:
576 	return ctx->supports;
577 
578     case ZIP_SOURCE_TELL:
579 	return (zip_int64_t)ctx->current;
580 
581     case ZIP_SOURCE_TELL_WRITE: {
582 	off_t ret = ftello(ctx->fout);
583 
584 	if (ret < 0) {
585 	    zip_error_set(&ctx->error, ZIP_ER_TELL, errno);
586 	    return -1;
587 	}
588 	return ret;
589     }
590 
591     case ZIP_SOURCE_WRITE: {
592 	size_t ret;
593 
594 	clearerr(ctx->fout);
595 	ret = fwrite(data, 1, len, ctx->fout);
596 	if (ret != len || ferror(ctx->fout)) {
597 	    zip_error_set(&ctx->error, ZIP_ER_WRITE, errno);
598 	    return -1;
599 	}
600 
601 	return (zip_int64_t)ret;
602     }
603 
604     default:
605 	zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
606 	return -1;
607     }
608 }
609 
610 
611 static int
_zip_fseek_u(FILE * f,zip_uint64_t offset,int whence,zip_error_t * error)612 _zip_fseek_u(FILE *f, zip_uint64_t offset, int whence, zip_error_t *error) {
613     if (offset > ZIP_INT64_MAX) {
614 	zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
615 	return -1;
616     }
617     return _zip_fseek(f, (zip_int64_t)offset, whence, error);
618 }
619 
620 
621 static int
_zip_fseek(FILE * f,zip_int64_t offset,int whence,zip_error_t * error)622 _zip_fseek(FILE *f, zip_int64_t offset, int whence, zip_error_t *error) {
623     if (offset > ZIP_FSEEK_MAX || offset < ZIP_FSEEK_MIN) {
624 	zip_error_set(error, ZIP_ER_SEEK, EOVERFLOW);
625 	return -1;
626     }
627     if (fseeko(f, (off_t)offset, whence) < 0) {
628 	zip_error_set(error, ZIP_ER_SEEK, errno);
629 	return -1;
630     }
631     return 0;
632 }
633 
634 
635 /*
636  * fopen replacement that sets the close-on-exec flag
637  * some implementations support an fopen 'e' flag for that,
638  * but e.g. macOS doesn't.
639  */
640 static FILE *
_zip_fopen(const char * name,bool writeable)641 _zip_fopen(const char *name, bool writeable)
642 {
643     int fd;
644     int flags;
645     FILE *fp;
646 
647     flags = O_CLOEXEC;
648     if (writeable) {
649 	flags |= O_RDWR;
650     }
651     else {
652 	flags |= O_RDONLY;
653     }
654 
655     /* mode argument needed on Windows */
656     if ((fd = open(name, flags, 0666)) < 0) {
657 	return NULL;
658     }
659     if ((fp = fdopen(fd, writeable ? "r+b" : "rb")) == NULL) {
660 	return NULL;
661     }
662     return fp;
663 }
664