xref: /PHP-5.6/ext/zip/lib/zip_close.c (revision 0579e827)
1 /*
2   zip_close.c -- close zip archive and update changes
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 
35 
36 #include "zipint.h"
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #ifdef HAVE_STRINGS_H
42 #include <strings.h>
43 #endif
44 #include <errno.h>
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #ifdef _WIN32
51 #include <io.h>
52 #include <fcntl.h>
53 #endif
54 
55 
56 
57 /* max deflate size increase: size + ceil(size/16k)*5+6 */
58 #define MAX_DEFLATE_SIZE_32	4293656963u
59 
60 static int add_data(struct zip *, struct zip_source *, struct zip_dirent *, FILE *);
61 static int copy_data(FILE *, zip_uint64_t, FILE *, struct zip_error *);
62 static int copy_source(struct zip *, struct zip_source *, FILE *);
63 static int write_cdir(struct zip *, const struct zip_filelist *, zip_uint64_t, FILE *);
64 static char *_zip_create_temp_output(struct zip *, FILE **);
65 static int _zip_torrentzip_cmp(const void *, const void *);
66 
67 
68 
69 ZIP_EXTERN int
zip_close(struct zip * za)70 zip_close(struct zip *za)
71 {
72     zip_uint64_t i, j, survivors;
73     int error;
74     char *temp;
75     FILE *out;
76 #ifndef _WIN32
77     mode_t mask;
78 #endif
79     struct zip_filelist *filelist;
80     int reopen_on_error;
81     int new_torrentzip;
82     int changed;
83 
84     reopen_on_error = 0;
85 
86     if (za == NULL)
87 	return -1;
88 
89     changed = _zip_changed(za, &survivors);
90 
91     /* don't create zip files with no entries */
92     if (survivors == 0) {
93 	if (za->zn && ((za->open_flags & ZIP_TRUNCATE) || (changed && za->zp))) {
94 	    if (remove(za->zn) != 0) {
95 		_zip_error_set(&za->error, ZIP_ER_REMOVE, errno);
96 		return -1;
97 	    }
98 	}
99 	zip_discard(za);
100 	return 0;
101     }
102 
103     if (!changed) {
104 	zip_discard(za);
105 	return 0;
106     }
107 
108     if (survivors > za->nentry) {
109         _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
110         return -1;
111     }
112 
113     if ((filelist=(struct zip_filelist *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL)
114 	return -1;
115 
116     /* archive comment is special for torrentzip */
117     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) {
118 	/* TODO: use internal function when zip_set_archive_comment clears TORRENT flag */
119 	if (zip_set_archive_comment(za, TORRENT_SIG "XXXXXXXX", TORRENT_SIG_LEN + TORRENT_CRC_LEN) < 0) {
120 	    free(filelist);
121 	    return -1;
122 	}
123     }
124     /* TODO: if no longer torrentzip and archive comment not changed by user, delete it */
125 
126 
127     /* create list of files with index into original archive  */
128     for (i=j=0; i<za->nentry; i++) {
129 	if (za->entry[i].deleted)
130 	    continue;
131 
132         if (j >= survivors) {
133             free(filelist);
134             _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
135             return -1;
136         }
137 
138 	filelist[j].idx = i;
139 	filelist[j].name = zip_get_name(za, i, 0);
140 	j++;
141     }
142     if (j < survivors) {
143         free(filelist);
144         _zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
145         return -1;
146     }
147 
148 
149     if ((temp=_zip_create_temp_output(za, &out)) == NULL) {
150 	free(filelist);
151 	return -1;
152     }
153 
154 
155     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
156 	qsort(filelist, (size_t)survivors, sizeof(filelist[0]),
157 	      _zip_torrentzip_cmp);
158 
159     new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1
160 		      && zip_get_archive_flag(za, ZIP_AFL_TORRENT,
161 					      ZIP_FL_UNCHANGED) == 0);
162     error = 0;
163     for (j=0; j<survivors; j++) {
164 	int new_data;
165 	struct zip_entry *entry;
166 	struct zip_dirent *de;
167 
168 	i = filelist[j].idx;
169 	entry = za->entry+i;
170 
171 	new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || new_torrentzip || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD));
172 
173 	/* create new local directory entry */
174 	if (entry->changes == NULL) {
175 	    if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) {
176                 _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
177                 error = 1;
178                 break;
179 	    }
180 	}
181 	de = entry->changes;
182 
183 	if (_zip_read_local_ef(za, i) < 0) {
184 	    error = 1;
185 	    break;
186 	}
187 
188 	if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
189 	    _zip_dirent_torrent_normalize(entry->changes);
190 
191 
192 	de->offset = (zip_uint64_t)ftello(out); /* TODO: check for errors */
193 
194 	if (new_data) {
195 	    struct zip_source *zs;
196 
197 	    zs = NULL;
198 	    if (!ZIP_ENTRY_DATA_CHANGED(entry)) {
199 		if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) {
200 		    error = 1;
201 		    break;
202 		}
203 	    }
204 
205 	    /* add_data writes dirent */
206 	    if (add_data(za, zs ? zs : entry->source, de, out) < 0) {
207 		error = 1;
208 		if (zs)
209 		    zip_source_free(zs);
210 		break;
211 	    }
212 	    if (zs)
213 		zip_source_free(zs);
214 	}
215 	else {
216 	    zip_uint64_t offset;
217 
218 	    /* when copying data, all sizes are known -> no data descriptor needed */
219 	    de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
220 	    if (_zip_dirent_write(de, out, ZIP_FL_LOCAL, &za->error) < 0) {
221 		error = 1;
222 		break;
223 	    }
224 	    if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) {
225 		error = 1;
226 		break;
227 	    }
228 	    if ((fseeko(za->zp, (off_t)offset, SEEK_SET) < 0)) {
229 		_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
230 		error = 1;
231 		break;
232 	    }
233 	    if (copy_data(za->zp, de->comp_size, out, &za->error) < 0) {
234 		error = 1;
235 		break;
236 	    }
237 	}
238     }
239 
240     if (!error) {
241 	if (write_cdir(za, filelist, survivors, out) < 0)
242 	    error = 1;
243     }
244 
245     free(filelist);
246 
247     if (error) {
248 	fclose(out);
249 	(void)remove(temp);
250 	free(temp);
251 	return -1;
252     }
253 
254     if (fclose(out) != 0) {
255 	_zip_error_set(&za->error, ZIP_ER_CLOSE, errno);
256 	(void)remove(temp);
257 	free(temp);
258 	return -1;
259     }
260 
261     if (za->zp) {
262 	fclose(za->zp);
263 	za->zp = NULL;
264 	reopen_on_error = 1;
265     }
266     if (_zip_rename(temp, za->zn) != 0) {
267 	_zip_error_set(&za->error, ZIP_ER_RENAME, errno);
268 	(void)remove(temp);
269 	free(temp);
270 	if (reopen_on_error) {
271 	    /* ignore errors, since we're already in an error case */
272 	    za->zp = fopen(za->zn, "rb");
273 	}
274 	return -1;
275     }
276 #ifndef _WIN32
277     mask = umask(0);
278     umask(mask);
279     chmod(za->zn, 0666&~mask);
280 #endif
281 
282     zip_discard(za);
283     free(temp);
284 
285     return 0;
286 }
287 
288 
289 
290 static int
add_data(struct zip * za,struct zip_source * src,struct zip_dirent * de,FILE * ft)291 add_data(struct zip *za, struct zip_source *src, struct zip_dirent *de, FILE *ft)
292 {
293     off_t offstart, offdata, offend;
294     struct zip_stat st;
295     struct zip_source *s2;
296     int ret;
297     int is_zip64;
298     zip_flags_t flags;
299 
300     if (zip_source_stat(src, &st) < 0) {
301 	_zip_error_set_from_source(&za->error, src);
302 	return -1;
303     }
304 
305     if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) {
306 	st.valid |= ZIP_STAT_COMP_METHOD;
307 	st.comp_method = ZIP_CM_STORE;
308     }
309 
310     if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE)
311 	de->comp_method = st.comp_method;
312     else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) {
313 	st.valid |= ZIP_STAT_COMP_SIZE;
314 	st.comp_size = st.size;
315     }
316     else {
317 	/* we'll recompress */
318 	st.valid &= ~ZIP_STAT_COMP_SIZE;
319     }
320 
321 
322     flags = ZIP_EF_LOCAL;
323 
324     if ((st.valid & ZIP_STAT_SIZE) == 0)
325 	flags |= ZIP_FL_FORCE_ZIP64;
326     else {
327 	de->uncomp_size = st.size;
328 
329 	if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) {
330 	    if (( ((de->comp_method == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(de->comp_method)) && st.size > MAX_DEFLATE_SIZE_32)
331 		 || (de->comp_method != ZIP_CM_STORE && de->comp_method != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(de->comp_method))))
332 		flags |= ZIP_FL_FORCE_ZIP64;
333 	}
334 	else
335 	    de->comp_size = st.comp_size;
336     }
337 
338 
339     offstart = ftello(ft);
340 
341     /* as long as we don't support non-seekable output, clear data descriptor bit */
342     de->bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
343     if ((is_zip64=_zip_dirent_write(de, ft, flags, &za->error)) < 0)
344 	return -1;
345 
346 
347     if (st.comp_method == ZIP_CM_STORE || (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != de->comp_method)) {
348 	struct zip_source *s_store, *s_crc;
349 	zip_compression_implementation comp_impl;
350 
351 	if (st.comp_method != ZIP_CM_STORE) {
352 	    if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) {
353 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
354 		return -1;
355 	    }
356 	    if ((s_store=comp_impl(za, src, st.comp_method, ZIP_CODEC_DECODE)) == NULL) {
357 		/* error set by comp_impl */
358 		return -1;
359 	    }
360 	}
361 	else
362 	    s_store = src;
363 
364 	if ((s_crc=zip_source_crc(za, s_store, 0)) == NULL) {
365 	    if (s_store != src)
366 		zip_source_pop(s_store);
367 	    return -1;
368 	}
369 
370 	/* TODO: deflate 0-byte files for torrentzip? */
371 	if (de->comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) {
372 	    if ((comp_impl=_zip_get_compression_implementation(de->comp_method)) == NULL) {
373 		_zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
374 		zip_source_pop(s_crc);
375 		if (s_store != src)
376 		    zip_source_pop(s_store);
377 		return -1;
378 	    }
379 	    if ((s2=comp_impl(za, s_crc, de->comp_method, ZIP_CODEC_ENCODE)) == NULL) {
380 		zip_source_pop(s_crc);
381 		if (s_store != src)
382 		    zip_source_pop(s_store);
383 		return -1;
384 	    }
385 	}
386 	else
387 	    s2 = s_crc;
388     }
389     else
390 	s2 = src;
391 
392     offdata = ftello(ft);
393 
394     ret = copy_source(za, s2, ft);
395 
396     if (zip_source_stat(s2, &st) < 0)
397 	ret = -1;
398 
399     while (s2 != src) {
400 	if ((s2=zip_source_pop(s2)) == NULL) {
401 	    /* TODO: set erorr */
402 	    ret = -1;
403 	    break;
404 	}
405     }
406 
407     if (ret < 0)
408 	return -1;
409 
410     offend = ftello(ft);
411 
412     if (fseeko(ft, offstart, SEEK_SET) < 0) {
413 	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
414 	return -1;
415     }
416 
417     if ((st.valid & (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) {
418 	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
419 	return -1;
420     }
421 
422     if (st.valid & ZIP_STAT_MTIME)
423 	de->last_mod = st.mtime;
424     else
425 	time(&de->last_mod);
426     de->comp_method = st.comp_method;
427     de->crc = st.crc;
428     de->uncomp_size = st.size;
429     de->comp_size = (zip_uint64_t)(offend - offdata);
430 
431     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
432 	_zip_dirent_torrent_normalize(de);
433 
434     if ((ret=_zip_dirent_write(de, ft, flags, &za->error)) < 0)
435 	return -1;
436 
437     if (is_zip64 != ret) {
438 	/* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */
439 	_zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
440 	return -1;
441     }
442 
443 
444     if (fseeko(ft, offend, SEEK_SET) < 0) {
445 	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
446 	return -1;
447     }
448 
449     return 0;
450 }
451 
452 
453 
454 static int
copy_data(FILE * fs,zip_uint64_t len,FILE * ft,struct zip_error * error)455 copy_data(FILE *fs, zip_uint64_t len, FILE *ft, struct zip_error *error)
456 {
457     char buf[BUFSIZE];
458     size_t n, nn;
459 
460     if (len == 0)
461 	return 0;
462 
463     while (len > 0) {
464 	nn = len > sizeof(buf) ? sizeof(buf) : len > SIZE_MAX ? SIZE_MAX : (size_t)len;
465 	if ((n=fread(buf, 1, nn, fs)) == 0) {
466             if (ferror(fs)) {
467                 _zip_error_set(error, ZIP_ER_READ, errno);
468                 return -1;
469             }
470             else {
471                 _zip_error_set(error, ZIP_ER_EOF, 0);
472                 return -1;
473             }
474         }
475 
476 	if (fwrite(buf, 1, n, ft) != (size_t)n) {
477 	    _zip_error_set(error, ZIP_ER_WRITE, errno);
478 	    return -1;
479 	}
480 
481 	len -= n;
482     }
483 
484     return 0;
485 }
486 
487 
488 
489 static int
copy_source(struct zip * za,struct zip_source * src,FILE * ft)490 copy_source(struct zip *za, struct zip_source *src, FILE *ft)
491 {
492     char buf[BUFSIZE];
493     zip_int64_t n;
494     int ret;
495 
496     if (zip_source_open(src) < 0) {
497 	_zip_error_set_from_source(&za->error, src);
498 	return -1;
499     }
500 
501     ret = 0;
502     while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) {
503 	if (fwrite(buf, 1, (size_t)n, ft) != (size_t)n) {
504 	    _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
505 	    ret = -1;
506 	    break;
507 	}
508     }
509 
510     if (n < 0) {
511 	if (ret == 0)
512 	    _zip_error_set_from_source(&za->error, src);
513 	ret = -1;
514     }
515 
516     zip_source_close(src);
517 
518     return ret;
519 }
520 
521 
522 
523 static int
write_cdir(struct zip * za,const struct zip_filelist * filelist,zip_uint64_t survivors,FILE * out)524 write_cdir(struct zip *za, const struct zip_filelist *filelist, zip_uint64_t survivors, FILE *out)
525 {
526     off_t cd_start, end;
527     zip_int64_t size;
528     uLong crc;
529     char buf[TORRENT_CRC_LEN+1];
530 
531     cd_start = ftello(out);
532 
533     if ((size=_zip_cdir_write(za, filelist, survivors, out)) < 0)
534 	return -1;
535 
536     end = ftello(out);
537 
538     if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0)
539 	return 0;
540 
541 
542     /* fix up torrentzip comment */
543 
544     if (_zip_filerange_crc(out, cd_start, size, &crc, &za->error) < 0)
545 	return -1;
546 
547     snprintf(buf, sizeof(buf), "%08lX", (long)crc);
548 
549     if (fseeko(out, end-TORRENT_CRC_LEN, SEEK_SET) < 0) {
550 	_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
551 	return -1;
552     }
553 
554     if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) {
555 	_zip_error_set(&za->error, ZIP_ER_WRITE, errno);
556 	return -1;
557     }
558 
559     return 0;
560 }
561 
562 
563 
564 int
_zip_changed(const struct zip * za,zip_uint64_t * survivorsp)565 _zip_changed(const struct zip *za, zip_uint64_t *survivorsp)
566 {
567     int changed;
568     zip_uint64_t i, survivors;
569 
570     changed = 0;
571     survivors = 0;
572 
573     if (za->comment_changed || za->ch_flags != za->flags)
574 	changed = 1;
575 
576     for (i=0; i<za->nentry; i++) {
577 	if (za->entry[i].deleted || za->entry[i].source || (za->entry[i].changes && za->entry[i].changes->changed != 0))
578 	    changed = 1;
579 	if (!za->entry[i].deleted)
580 	    survivors++;
581     }
582 
583     if (survivorsp)
584 	*survivorsp = survivors;
585 
586     return changed;
587 }
588 
589 
590 
591 static char *
_zip_create_temp_output(struct zip * za,FILE ** outp)592 _zip_create_temp_output(struct zip *za, FILE **outp)
593 {
594     char *temp;
595     int tfd;
596     FILE *tfp;
597 
598     if (za->tempdir) {
599         if ((temp=(char *)malloc(strlen(za->tempdir)+13)) == NULL) {
600             _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
601             return NULL;
602         }
603         sprintf(temp, "%s/.zip.XXXXXX", za->tempdir);
604     }
605     else {
606         if ((temp=(char *)malloc(strlen(za->zn)+8)) == NULL) {
607             _zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
608             return NULL;
609         }
610         sprintf(temp, "%s.XXXXXX", za->zn);
611     }
612 
613     if ((tfd=mkstemp(temp)) == -1) {
614 	_zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
615 	free(temp);
616 	return NULL;
617     }
618 
619     if ((tfp=fdopen(tfd, "r+b")) == NULL) {
620 	_zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
621 	close(tfd);
622 	(void)remove(temp);
623 	free(temp);
624 	return NULL;
625     }
626 
627 #ifdef _WIN32
628     /*
629       According to Pierre Joye, Windows in some environments per
630       default creates text files, so force binary mode.
631     */
632     _setmode(_fileno(tfp), _O_BINARY );
633 #endif
634 
635     *outp = tfp;
636     return temp;
637 }
638 
639 
640 
641 static int
_zip_torrentzip_cmp(const void * a,const void * b)642 _zip_torrentzip_cmp(const void *a, const void *b)
643 {
644     const char *aname = ((const struct zip_filelist *)a)->name;
645     const char *bname = ((const struct zip_filelist *)b)->name;
646 
647     if (aname == NULL)
648 	return (bname != NULL) * -1;
649     else if (bname == NULL)
650 	return 1;
651 
652     return strcasecmp(aname, bname);
653 }
654