diff libzip/lib/zip_close.c @ 4:2306f4b04790

libzip: import 1.1.2
author David Demelier <markand@malikania.fr>
date Wed, 24 Feb 2016 21:19:28 +0100
parents
children 056ee6b5913e
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libzip/lib/zip_close.c	Wed Feb 24 21:19:28 2016 +0100
@@ -0,0 +1,492 @@
+/*
+  zip_close.c -- close zip archive and update changes
+  Copyright (C) 1999-2015 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  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
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. Redistributions in binary form must reproduce the above copyright
+     notice, this list of conditions and the following disclaimer in
+     the documentation and/or other materials provided with the
+     distribution.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
+  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+  IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "zipint.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include <io.h>
+#include <fcntl.h>
+#endif
+
+
+/* max deflate size increase: size + ceil(size/16k)*5+6 */
+#define MAX_DEFLATE_SIZE_32	4293656963u
+
+static int add_data(zip_t *, zip_source_t *, zip_dirent_t *);
+static int copy_data(zip_t *, zip_uint64_t);
+static int copy_source(zip_t *, zip_source_t *);
+static int write_cdir(zip_t *, const zip_filelist_t *, zip_uint64_t);
+
+
+ZIP_EXTERN int
+zip_close(zip_t *za)
+{
+    zip_uint64_t i, j, survivors;
+    zip_int64_t off;
+    int error;
+    zip_filelist_t *filelist;
+    int changed;
+
+    if (za == NULL)
+	return -1;
+
+    changed = _zip_changed(za, &survivors);
+
+    /* don't create zip files with no entries */
+    if (survivors == 0) {
+	if ((za->open_flags & ZIP_TRUNCATE) || changed) {
+	    if (zip_source_remove(za->src) < 0) {
+		_zip_error_set_from_source(&za->error, za->src);
+		return -1;
+	    }
+	}
+	zip_discard(za);
+	return 0;
+    }	       
+
+    if (!changed) {
+	zip_discard(za);
+	return 0;
+    }
+
+    if (survivors > za->nentry) {
+        zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+        return -1;
+    }
+    
+    if ((filelist=(zip_filelist_t *)malloc(sizeof(filelist[0])*(size_t)survivors)) == NULL)
+	return -1;
+
+    /* create list of files with index into original archive  */
+    for (i=j=0; i<za->nentry; i++) {
+	if (za->entry[i].deleted)
+	    continue;
+
+        if (j >= survivors) {
+            free(filelist);
+            zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+            return -1;
+        }
+        
+	filelist[j].idx = i;
+	j++;
+    }
+    if (j < survivors) {
+        free(filelist);
+        zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+        return -1;
+    }
+
+    if (zip_source_begin_write(za->src) < 0) {
+	_zip_error_set_from_source(&za->error, za->src);
+	free(filelist);
+	return -1;
+    }
+    
+    error = 0;
+    for (j=0; j<survivors; j++) {
+	int new_data;
+	zip_entry_t *entry;
+	zip_dirent_t *de;
+
+	i = filelist[j].idx;
+	entry = za->entry+i;
+
+	new_data = (ZIP_ENTRY_DATA_CHANGED(entry) || ZIP_ENTRY_CHANGED(entry, ZIP_DIRENT_COMP_METHOD));
+
+	/* create new local directory entry */
+	if (entry->changes == NULL) {
+	    if ((entry->changes=_zip_dirent_clone(entry->orig)) == NULL) {
+                zip_error_set(&za->error, ZIP_ER_MEMORY, 0);
+                error = 1;
+                break;
+	    }
+	}
+	de = entry->changes;
+
+	if (_zip_read_local_ef(za, i) < 0) {
+	    error = 1;
+	    break;
+	}
+
+        if ((off = zip_source_tell_write(za->src)) < 0) {
+            error = 1;
+            break;
+        }
+        de->offset = (zip_uint64_t)off;
+
+	if (new_data) {
+	    zip_source_t *zs;
+
+	    zs = NULL;
+	    if (!ZIP_ENTRY_DATA_CHANGED(entry)) {
+		if ((zs=_zip_source_zip_new(za, za, i, ZIP_FL_UNCHANGED, 0, 0, NULL)) == NULL) {
+		    error = 1;
+		    break;
+		}
+	    }
+
+	    /* add_data writes dirent */
+	    if (add_data(za, zs ? zs : entry->source, de) < 0) {
+		error = 1;
+		if (zs)
+		    zip_source_free(zs);
+		break;
+	    }
+	    if (zs)
+		zip_source_free(zs);
+	}
+	else {
+	    zip_uint64_t offset;
+
+	    /* when copying data, all sizes are known -> no data descriptor needed */
+	    de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
+	    if (_zip_dirent_write(za, de, ZIP_FL_LOCAL) < 0) {
+		error = 1;
+		break;
+	    }
+	    if ((offset=_zip_file_get_offset(za, i, &za->error)) == 0) {
+		error = 1;
+		break;
+	    }
+	    if (zip_source_seek(za->src, (zip_int64_t)offset, SEEK_SET) < 0) {
+		_zip_error_set_from_source(&za->error, za->src);
+		error = 1;
+		break;
+	    }
+	    if (copy_data(za, de->comp_size) < 0) {
+		error = 1;
+		break;
+	    }
+	}
+    }
+
+    if (!error) {
+	if (write_cdir(za, filelist, survivors) < 0)
+	    error = 1;
+    }
+
+    free(filelist);
+
+    if (!error) {
+	if (zip_source_commit_write(za->src) != 0) {
+	    _zip_error_set_from_source(&za->error, za->src);
+	    error = 1;
+	}
+    }
+
+    if (error) {
+	zip_source_rollback_write(za->src);
+	return -1;
+    }
+
+    zip_discard(za);
+    
+    return 0;
+}
+
+
+static int
+add_data(zip_t *za, zip_source_t *src, zip_dirent_t *de)
+{
+    zip_int64_t offstart, offdata, offend;
+    struct zip_stat st;
+    zip_source_t *s2;
+    int ret;
+    int is_zip64;
+    zip_flags_t flags;
+    
+    if (zip_source_stat(src, &st) < 0) {
+	_zip_error_set_from_source(&za->error, src);
+	return -1;
+    }
+
+    if ((st.valid & ZIP_STAT_COMP_METHOD) == 0) {
+	st.valid |= ZIP_STAT_COMP_METHOD;
+	st.comp_method = ZIP_CM_STORE;
+    }
+
+    if (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != ZIP_CM_STORE)
+	de->comp_method = st.comp_method;
+    else if (de->comp_method == ZIP_CM_STORE && (st.valid & ZIP_STAT_SIZE)) {
+	st.valid |= ZIP_STAT_COMP_SIZE;
+	st.comp_size = st.size;
+    }
+    else {
+	/* we'll recompress */
+	st.valid &= ~ZIP_STAT_COMP_SIZE;
+    }
+
+
+    flags = ZIP_EF_LOCAL;
+
+    if ((st.valid & ZIP_STAT_SIZE) == 0)
+	flags |= ZIP_FL_FORCE_ZIP64;
+    else {
+	de->uncomp_size = st.size;
+	
+	if ((st.valid & ZIP_STAT_COMP_SIZE) == 0) {
+	    if (( ((de->comp_method == ZIP_CM_DEFLATE || ZIP_CM_IS_DEFAULT(de->comp_method)) && st.size > MAX_DEFLATE_SIZE_32)
+		  || (de->comp_method != ZIP_CM_STORE && de->comp_method != ZIP_CM_DEFLATE && !ZIP_CM_IS_DEFAULT(de->comp_method))))
+		flags |= ZIP_FL_FORCE_ZIP64;
+	}
+	else
+	    de->comp_size = st.comp_size;
+    }
+
+    if ((offstart = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
+
+    /* as long as we don't support non-seekable output, clear data descriptor bit */
+    de->bitflags &= (zip_uint16_t)~ZIP_GPBF_DATA_DESCRIPTOR;
+    if ((is_zip64=_zip_dirent_write(za, de, flags)) < 0)
+	return -1;
+
+
+    if (st.comp_method == ZIP_CM_STORE || (ZIP_CM_IS_DEFAULT(de->comp_method) && st.comp_method != de->comp_method)) {
+	zip_source_t *s_store, *s_crc;
+	zip_compression_implementation comp_impl;
+	
+	if (st.comp_method != ZIP_CM_STORE) {
+	    if ((comp_impl=_zip_get_compression_implementation(st.comp_method)) == NULL) {
+		zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+		return -1;
+	    }
+	    if ((s_store=comp_impl(za, src, st.comp_method, ZIP_CODEC_DECODE)) == NULL) {
+		/* error set by comp_impl */
+		return -1;
+	    }
+	}
+	else {
+	    /* to have the same reference count to src as in the case where it's not stored */
+	    zip_source_keep(src);
+	    s_store = src;
+	}
+
+	s_crc = zip_source_crc(za, s_store, 0);
+	zip_source_free(s_store);
+	if (s_crc == NULL) {
+	    return -1;
+	}
+
+	if (de->comp_method != ZIP_CM_STORE && ((st.valid & ZIP_STAT_SIZE) == 0 || st.size != 0)) {
+	    if ((comp_impl=_zip_get_compression_implementation(de->comp_method)) == NULL) {
+		zip_error_set(&za->error, ZIP_ER_COMPNOTSUPP, 0);
+		zip_source_free(s_crc);
+		return -1;
+	    }
+	    s2 = comp_impl(za, s_crc, de->comp_method, ZIP_CODEC_ENCODE);
+	    zip_source_free(s_crc);
+	    if (s2 == NULL) {
+		return -1;
+	    }
+	}
+	else {
+	    s2 = s_crc;
+	}
+    }
+    else {
+	zip_source_keep(src);
+	s2 = src;
+    }
+
+    if ((offdata = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
+
+    ret = copy_source(za, s2);
+	
+    if (zip_source_stat(s2, &st) < 0)
+	ret = -1;
+
+    zip_source_free(s2);
+
+    if (ret < 0)
+	return -1;
+
+    if ((offend = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
+
+    if (zip_source_seek_write(za->src, offstart, SEEK_SET) < 0) {
+	_zip_error_set_from_source(&za->error, za->src);
+	return -1;
+    }
+
+    if ((st.valid & (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) != (ZIP_STAT_COMP_METHOD|ZIP_STAT_CRC|ZIP_STAT_SIZE)) {
+	zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+	return -1;
+    }
+
+    if ((de->changed & ZIP_DIRENT_LAST_MOD) == 0) {
+        if (st.valid & ZIP_STAT_MTIME)
+            de->last_mod = st.mtime;
+        else
+            time(&de->last_mod);
+    }
+    de->comp_method = st.comp_method;
+    de->crc = st.crc;
+    de->uncomp_size = st.size;
+    de->comp_size = (zip_uint64_t)(offend - offdata);
+
+    if ((ret=_zip_dirent_write(za, de, flags)) < 0)
+	return -1;
+ 
+    if (is_zip64 != ret) {
+	/* Zip64 mismatch between preliminary file header written before data and final file header written afterwards */
+	zip_error_set(&za->error, ZIP_ER_INTERNAL, 0);
+	return -1;
+    }
+
+   
+    if (zip_source_seek_write(za->src, offend, SEEK_SET) < 0) {
+	_zip_error_set_from_source(&za->error, za->src);
+	return -1;
+    }
+
+    return 0;
+}
+
+
+static int
+copy_data(zip_t *za, zip_uint64_t len)
+{
+    zip_uint8_t buf[BUFSIZE];
+    size_t n;
+
+    while (len > 0) {
+	n = len > sizeof(buf) ? sizeof(buf) : len;
+	if (_zip_read(za->src, buf, n, &za->error) < 0) {
+	    return -1;
+	}
+
+	if (_zip_write(za, buf, n) < 0) {
+	    return -1;
+	}
+	
+	len -= n;
+    }
+
+    return 0;
+}
+
+
+static int
+copy_source(zip_t *za, zip_source_t *src)
+{
+    zip_uint8_t buf[BUFSIZE];
+    zip_int64_t n;
+    int ret;
+
+    if (zip_source_open(src) < 0) {
+	_zip_error_set_from_source(&za->error, src);
+	return -1;
+    }
+
+    ret = 0;
+    while ((n=zip_source_read(src, buf, sizeof(buf))) > 0) {
+	if (_zip_write(za, buf, (zip_uint64_t)n) < 0) {
+	    ret = -1;
+	    break;
+	}
+    }
+    
+    if (n < 0) {
+	_zip_error_set_from_source(&za->error, src);
+	ret = -1;
+    }
+
+    zip_source_close(src);
+    
+    return ret;
+}
+
+
+static int
+write_cdir(zip_t *za, const zip_filelist_t *filelist, zip_uint64_t survivors)
+{
+    zip_int64_t cd_start, end, size;
+    
+    if ((cd_start = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
+
+    if ((size=_zip_cdir_write(za, filelist, survivors)) < 0) {
+	return -1;
+    }
+    
+    if ((end = zip_source_tell_write(za->src)) < 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+
+int
+_zip_changed(const zip_t *za, zip_uint64_t *survivorsp)
+{
+    int changed;
+    zip_uint64_t i, survivors;
+
+    changed = 0;
+    survivors = 0;
+
+    if (za->comment_changed || za->ch_flags != za->flags)
+	changed = 1;
+
+    for (i=0; i<za->nentry; i++) {
+	if (za->entry[i].deleted || za->entry[i].source || (za->entry[i].changes && za->entry[i].changes->changed != 0))
+	    changed = 1;
+	if (!za->entry[i].deleted)
+	    survivors++;
+    }
+
+    if (survivorsp)
+	*survivorsp = survivors;
+
+    return changed;
+}