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