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