summaryrefslogtreecommitdiff
path: root/ext/zip/lib/zip_close.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/zip/lib/zip_close.c')
-rw-r--r--ext/zip/lib/zip_close.c260
1 files changed, 189 insertions, 71 deletions
diff --git a/ext/zip/lib/zip_close.c b/ext/zip/lib/zip_close.c
index 11ba8e223..289ca7afb 100644
--- a/ext/zip/lib/zip_close.c
+++ b/ext/zip/lib/zip_close.c
@@ -1,11 +1,9 @@
/*
- $NiH: zip_close.c,v 1.60 2006/05/09 17:21:47 wiz Exp $
-
zip_close.c -- close zip archive and update changes
- Copyright (C) 1999, 2004, 2005 Dieter Baron and Thomas Klausner
+ Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
This file is part of libzip, a library to manipulate ZIP archives.
- The authors can be contacted at <nih@giga.or.at>
+ The authors can be contacted at <libzip@nih.at>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
@@ -32,31 +30,42 @@
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+
+
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
-#ifndef _MSC_VER
-#include <unistd.h>
-#endif
#include <sys/types.h>
#include <sys/stat.h>
-#include "zip.h"
#include "zipint.h"
-static int add_data(struct zip *, int, struct zip_dirent *, FILE *);
+static int add_data(struct zip *, struct zip_source *, struct zip_dirent *,
+ FILE *);
static int add_data_comp(zip_source_callback, void *, struct zip_stat *,
FILE *, struct zip_error *);
-static int add_data_uncomp(zip_source_callback, void *, struct zip_stat *,
- FILE *, struct zip_error *);
+static int add_data_uncomp(struct zip *, zip_source_callback, void *,
+ struct zip_stat *, FILE *);
static void ch_set_error(struct zip_error *, zip_source_callback, void *);
static int copy_data(FILE *, off_t, FILE *, struct zip_error *);
+static int write_cdir(struct zip *, struct zip_cdir *, FILE *);
static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *);
static int _zip_changed(struct zip *, int *);
static char *_zip_create_temp_output(struct zip *, FILE **);
+static int _zip_torrentzip_cmp(const void *, const void *);
-PHPZIPAPI int
+
+
+struct filelist {
+ int idx;
+ const char *name;
+};
+
+
+
+ZIP_EXTERN(int)
zip_close(struct zip *za)
{
int survivors;
@@ -66,7 +75,14 @@ zip_close(struct zip *za)
mode_t mask;
struct zip_cdir *cd;
struct zip_dirent de;
- int rename_error = 0;
+ struct filelist *filelist;
+ int reopen_on_error;
+ int new_torrentzip;
+
+ reopen_on_error = 0;
+
+ if (za == NULL)
+ return -1;
if (!_zip_changed(za, &survivors)) {
_zip_free(za);
@@ -75,7 +91,7 @@ zip_close(struct zip *za)
/* don't create zip files with no entries */
if (survivors == 0) {
- if (za->zn) {
+ if (za->zn && za->zp) {
if (remove(za->zn) != 0) {
_zip_error_set(&za->error, ZIP_ER_REMOVE, errno);
return -1;
@@ -85,52 +101,92 @@ zip_close(struct zip *za)
return 0;
}
- if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL)
+ if ((filelist=(struct filelist *)malloc(sizeof(filelist[0])*survivors))
+ == NULL)
+ return -1;
+
+ if ((cd=_zip_cdir_new(survivors, &za->error)) == NULL) {
+ free(filelist);
return -1;
+ }
for (i=0; i<survivors; i++)
_zip_dirent_init(&cd->entry[i]);
+ /* archive comment is special for torrentzip */
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0)) {
+ cd->comment = _zip_memdup(TORRENT_SIG "XXXXXXXX",
+ TORRENT_SIG_LEN + TORRENT_CRC_LEN,
+ &za->error);
+ if (cd->comment == NULL) {
+ _zip_cdir_free(cd);
+ free(filelist);
+ return -1;
+ }
+ cd->comment_len = TORRENT_SIG_LEN + TORRENT_CRC_LEN;
+ }
+ else if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, ZIP_FL_UNCHANGED) == 0) {
if (_zip_cdir_set_comment(cd, za) == -1) {
_zip_cdir_free(cd);
+ free(filelist);
return -1;
}
+ }
if ((temp=_zip_create_temp_output(za, &out)) == NULL) {
_zip_cdir_free(cd);
return -1;
}
- error = 0;
+
+ /* create list of files with index into original archive */
for (i=j=0; i<za->nentry; i++) {
if (za->entry[i].state == ZIP_ST_DELETED)
continue;
+ filelist[j].idx = i;
+ filelist[j].name = zip_get_name(za, i, 0);
+ j++;
+ }
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
+ qsort(filelist, survivors, sizeof(filelist[0]),
+ _zip_torrentzip_cmp);
+
+ new_torrentzip = (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 1
+ && zip_get_archive_flag(za, ZIP_AFL_TORRENT,
+ ZIP_FL_UNCHANGED) == 0);
+ error = 0;
+ for (j=0; j<survivors; j++) {
+ i = filelist[j].idx;
+
/* create new local directory entry */
- if (ZIP_ENTRY_DATA_CHANGED(za->entry+i)) {
+ if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) {
_zip_dirent_init(&de);
+
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
+ _zip_dirent_torrent_normalize(&de);
+
/* use it as central directory entry */
memcpy(cd->entry+j, &de, sizeof(cd->entry[j]));
/* set/update file name */
if (za->entry[i].ch_filename == NULL) {
- if (za->entry[i].state == ZIP_ST_REPLACED) {
- de.filename = strdup(za->cdir->entry[i].filename);
- de.filename_len = strlen(de.filename);
- cd->entry[j].filename = za->cdir->entry[i].filename;
- cd->entry[j].filename_len = de.filename_len;
- }
- else {
+ if (za->entry[i].state == ZIP_ST_ADDED) {
de.filename = strdup("-");
de.filename_len = 1;
cd->entry[j].filename = "-";
+ }
+ else {
+ de.filename = strdup(za->cdir->entry[i].filename);
+ de.filename_len = strlen(de.filename);
+ cd->entry[j].filename = za->cdir->entry[i].filename;
cd->entry[j].filename_len = de.filename_len;
}
}
}
else {
/* copy existing directory entries */
- if (fseek(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) {
+ if (fseeko(za->zp, za->cdir->entry[i].offset, SEEK_SET) != 0) {
_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
error = 1;
break;
@@ -139,11 +195,11 @@ zip_close(struct zip *za)
error = 1;
break;
}
-
- if (de.bitflags & ZIP_GPBF_USE_DATA_DESCRIPTOR) {
- de.crc = (za->cdir->entry+i)->crc;
- de.comp_size = (za->cdir->entry+i)->comp_size;
- de.uncomp_size = (za->cdir->entry+i)->uncomp_size;
+ if (de.bitflags & ZIP_GPBF_DATA_DESCRIPTOR) {
+ de.crc = za->cdir->entry[i].crc;
+ de.comp_size = za->cdir->entry[i].comp_size;
+ de.uncomp_size = za->cdir->entry[i].uncomp_size;
+ de.bitflags &= ~ZIP_GPBF_DATA_DESCRIPTOR;
}
memcpy(cd->entry+j, za->cdir->entry+i, sizeof(cd->entry[j]));
}
@@ -159,20 +215,31 @@ zip_close(struct zip *za)
cd->entry[j].filename_len = de.filename_len;
}
- if (za->entry[i].ch_comment_len != -1) {
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0
+ && za->entry[i].ch_comment_len != -1) {
/* as the rest of cd entries, its malloc/free is done by za */
cd->entry[j].comment = za->entry[i].ch_comment;
cd->entry[j].comment_len = za->entry[i].ch_comment_len;
}
- cd->entry[j].offset = ftell(out);
+ cd->entry[j].offset = ftello(out);
+
+ if (ZIP_ENTRY_DATA_CHANGED(za->entry+i) || new_torrentzip) {
+ struct zip_source *zs;
- if (ZIP_ENTRY_DATA_CHANGED(za->entry+i)) {
- if (add_data(za, i, &de, out) < 0) {
+ zs = NULL;
+ if (!ZIP_ENTRY_DATA_CHANGED(za->entry+i)) {
+ if ((zs=zip_source_zip(za, za, i, ZIP_FL_RECOMPRESS, 0, -1))
+ == NULL) {
error = 1;
break;
}
+ }
+ if (add_data(za, zs ? zs : za->entry[i].source, &de, out) < 0) {
+ error = 1;
+ break;
+ }
cd->entry[j].last_mod = de.last_mod;
cd->entry[j].comp_method = de.comp_method;
cd->entry[j].comp_size = de.comp_size;
@@ -185,19 +252,18 @@ zip_close(struct zip *za)
break;
}
/* we just read the local dirent, file is at correct position */
- if (copy_data(za->zp, de.comp_size, out, &za->error) < 0) {
+ if (copy_data(za->zp, cd->entry[j].comp_size, out,
+ &za->error) < 0) {
error = 1;
break;
}
}
- j++;
-
_zip_dirent_finalize(&de);
}
if (!error) {
- if (_zip_cdir_write(cd, out, &za->error) < 0)
+ if (write_cdir(za, cd, out) < 0)
error = 1;
}
@@ -223,46 +289,40 @@ zip_close(struct zip *za)
if (za->zp) {
fclose(za->zp);
za->zp = NULL;
+ reopen_on_error = 1;
}
-
-#ifdef PHP_WIN32
- if (!MoveFileEx(temp, za->zn, MOVEFILE_REPLACE_EXISTING)) {
- rename_error = -1;
- }
-#else
- if (rename(temp, za->zn) != 0) {
- rename_error = -1;
- }
-#endif
-
- if (rename_error < 0) {
+ if (_zip_rename(temp, za->zn) != 0) {
_zip_error_set(&za->error, ZIP_ER_RENAME, errno);
remove(temp);
free(temp);
+ if (reopen_on_error) {
+ /* ignore errors, since we're already in an error case */
+ za->zp = fopen(za->zn, "rb");
+ }
return -1;
}
-
mask = umask(0);
umask(mask);
chmod(za->zn, 0666&~mask);
_zip_free(za);
free(temp);
+
return 0;
}
static int
-add_data(struct zip *za, int idx, struct zip_dirent *de, FILE *ft)
+add_data(struct zip *za, struct zip_source *zs, struct zip_dirent *de, FILE *ft)
{
off_t offstart, offend;
zip_source_callback cb;
void *ud;
struct zip_stat st;
- cb = za->entry[idx].source->f;
- ud = za->entry[idx].source->ud;
+ cb = zs->f;
+ ud = zs->ud;
if (cb(ud, &st, sizeof(st), ZIP_SOURCE_STAT) < (ssize_t)sizeof(st)) {
ch_set_error(&za->error, cb, ud);
@@ -274,7 +334,7 @@ add_data(struct zip *za, int idx, struct zip_dirent *de, FILE *ft)
return -1;
}
- offstart = ftell(ft);
+ offstart = ftello(ft);
if (_zip_dirent_write(de, ft, 1, &za->error) < 0)
return -1;
@@ -284,7 +344,7 @@ add_data(struct zip *za, int idx, struct zip_dirent *de, FILE *ft)
return -1;
}
else {
- if (add_data_uncomp(cb, ud, &st, ft, &za->error) < 0)
+ if (add_data_uncomp(za, cb, ud, &st, ft) < 0)
return -1;
}
@@ -293,23 +353,27 @@ add_data(struct zip *za, int idx, struct zip_dirent *de, FILE *ft)
return -1;
}
- offend = ftell(ft);
+ offend = ftello(ft);
- if (fseek(ft, offstart, SEEK_SET) < 0) {
+ if (fseeko(ft, offstart, SEEK_SET) < 0) {
_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
return -1;
}
- de->comp_method = st.comp_method;
+
de->last_mod = st.mtime;
+ de->comp_method = st.comp_method;
de->crc = st.crc;
de->uncomp_size = st.size;
de->comp_size = st.comp_size;
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
+ _zip_dirent_torrent_normalize(de);
+
if (_zip_dirent_write(de, ft, 1, &za->error) < 0)
return -1;
- if (fseek(ft, offend, SEEK_SET) < 0) {
+ if (fseeko(ft, offend, SEEK_SET) < 0) {
_zip_error_set(&za->error, ZIP_ER_SEEK, errno);
return -1;
}
@@ -346,14 +410,15 @@ add_data_comp(zip_source_callback cb, void *ud, struct zip_stat *st,FILE *ft,
static int
-add_data_uncomp(zip_source_callback cb, void *ud, struct zip_stat *st,
- FILE *ft, struct zip_error *error)
+add_data_uncomp(struct zip *za, zip_source_callback cb, void *ud,
+ struct zip_stat *st, FILE *ft)
{
char b1[BUFSIZE], b2[BUFSIZE];
int end, flush, ret;
ssize_t n;
size_t n2;
z_stream zstr;
+ int mem_level;
st->comp_method = ZIP_CM_DEFLATE;
st->comp_size = st->size = 0;
@@ -365,8 +430,13 @@ add_data_uncomp(zip_source_callback cb, void *ud, struct zip_stat *st,
zstr.avail_in = 0;
zstr.avail_out = 0;
- /* -15: undocumented feature of zlib to _not_ write a zlib header */
- deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -15, 9,
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0))
+ mem_level = TORRENT_MEM_LEVEL;
+ else
+ mem_level = MAX_MEM_LEVEL;
+
+ /* -MAX_WBITS: undocumented feature of zlib to _not_ write a zlib header */
+ deflateInit2(&zstr, Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS, mem_level,
Z_DEFAULT_STRATEGY);
zstr.next_out = (Bytef *)b2;
@@ -378,7 +448,7 @@ add_data_uncomp(zip_source_callback cb, void *ud, struct zip_stat *st,
while (!end) {
if (zstr.avail_in == 0 && !flush) {
if ((n=cb(ud, b1, sizeof(b1), ZIP_SOURCE_READ)) < 0) {
- ch_set_error(error, cb, ud);
+ ch_set_error(&za->error, cb, ud);
deflateEnd(&zstr);
return -1;
}
@@ -394,7 +464,7 @@ add_data_uncomp(zip_source_callback cb, void *ud, struct zip_stat *st,
ret = deflate(&zstr, flush);
if (ret != Z_OK && ret != Z_STREAM_END) {
- _zip_error_set(error, ZIP_ER_ZLIB, ret);
+ _zip_error_set(&za->error, ZIP_ER_ZLIB, ret);
return -1;
}
@@ -402,7 +472,7 @@ add_data_uncomp(zip_source_callback cb, void *ud, struct zip_stat *st,
n2 = sizeof(b2) - zstr.avail_out;
if (fwrite(b2, 1, n2, ft) != n2) {
- _zip_error_set(error, ZIP_ER_WRITE, errno);
+ _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
return -1;
}
@@ -473,6 +543,44 @@ copy_data(FILE *fs, off_t len, FILE *ft, struct zip_error *error)
static int
+write_cdir(struct zip *za, struct zip_cdir *cd, FILE *out)
+{
+ off_t offset;
+ uLong crc;
+ char buf[TORRENT_CRC_LEN+1];
+
+ if (_zip_cdir_write(cd, out, &za->error) < 0)
+ return -1;
+
+ if (zip_get_archive_flag(za, ZIP_AFL_TORRENT, 0) == 0)
+ return 0;
+
+
+ /* fix up torrentzip comment */
+
+ offset = ftello(out);
+
+ if (_zip_filerange_crc(out, cd->offset, cd->size, &crc, &za->error) < 0)
+ return -1;
+
+ snprintf(buf, sizeof(buf), "%08lX", (long)crc);
+
+ if (fseeko(out, offset-TORRENT_CRC_LEN, SEEK_SET) < 0) {
+ _zip_error_set(&za->error, ZIP_ER_SEEK, errno);
+ return -1;
+ }
+
+ if (fwrite(buf, TORRENT_CRC_LEN, 1, out) != 1) {
+ _zip_error_set(&za->error, ZIP_ER_WRITE, errno);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+static int
_zip_cdir_set_comment(struct zip_cdir *dest, struct zip *src)
{
if (src->ch_comment_len != -1) {
@@ -503,7 +611,8 @@ _zip_changed(struct zip *za, int *survivorsp)
changed = survivors = 0;
- if (za->ch_comment_len != -1)
+ if (za->ch_comment_len != -1
+ || za->ch_flags != za->flags)
changed = 1;
for (i=0; i<za->nentry; i++) {
@@ -527,14 +636,14 @@ _zip_create_temp_output(struct zip *za, FILE **outp)
char *temp;
int tfd;
FILE *tfp;
- int len = strlen(za->zn) + 8;
+ int len = strlen(za->zn) + 8;
- if ((temp=(char *)malloc(len)) == NULL) {
+ if ((temp=(char *)malloc(strlen(za->zn)+8)) == NULL) {
_zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
return NULL;
}
- snprintf(temp, len, "%s.XXXXXX", za->zn);
+ snprintf(temp, len, "%s.XXXXXX", za->zn);
if ((tfd=mkstemp(temp)) == -1) {
_zip_error_set(&za->error, ZIP_ER_TMPOPEN, errno);
@@ -556,3 +665,12 @@ _zip_create_temp_output(struct zip *za, FILE **outp)
*outp = tfp;
return temp;
}
+
+
+
+static int
+_zip_torrentzip_cmp(const void *a, const void *b)
+{
+ return strcasecmp(((const struct filelist *)a)->name,
+ ((const struct filelist *)b)->name);
+}