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