Merge pull request #119 from randy408/fuzz

Add fuzz target to build
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e64684b..4db2ac9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -201,12 +201,9 @@
 
 TEST_BIG_ENDIAN(WORDS_BIGENDIAN)
 
-FIND_PACKAGE(ZLIB REQUIRED)
+FIND_PACKAGE(ZLIB 1.1.2 REQUIRED)
 INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIR})
 SET(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR})
-IF(ZLIB_VERSION_STRING VERSION_LESS "1.1.2")
-  MESSAGE(FATAL_ERROR "-- ZLIB version too old, please install at least v1.1.2")
-ENDIF(ZLIB_VERSION_STRING VERSION_LESS "1.1.2")
 
 IF(ENABLE_BZIP2)
   FIND_PACKAGE(BZip2)
diff --git a/NEWS.md b/NEWS.md
index 42ef46b..b4f6f7f 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -3,6 +3,7 @@
 
 * Avoid using umask() since it's not thread-safe.
 * Set close-on-exec flag when opening files.
+* Do not accept empty files as valid zip archives any longer.
 
 1.5.2 [2019-03-12]
 ==================
diff --git a/THANKS b/THANKS
index ee7e57e..f92b7a2 100644
--- a/THANKS
+++ b/THANKS
@@ -18,6 +18,7 @@
 Brian 'geeknik' Carpenter <geeknik@protonmail.ch>
 Carl Mastrangelo <notcarl@google.com>
 Cédric Tabin
+celan69
 Chris Nehren <cnehren+libzip@pobox.com>
 Coverity <info@coverity.com>
 Dane Springmeyer <dane.springmeyer@gmail.com>
@@ -59,8 +60,10 @@
 Michal Vyskocil <mvyskocil@suse.cz>
 Mikhail Gusarov <dottedmag@dottedmag.net>.
 Miklos Vajna
+Morris Hafner
 Oliver Kaiser <under.northern.sky@googlemail.com>
 Oliver Kuckertz <oliver.kuckertz@mologie.de>
+OSS-Fuzz Team
 Pascal Terjan <pterjan@gmail.com>
 Patrick Spendrin <ps_ml@gmx.de>
 Paul Harris <harris.pc@gmail.com>
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index 1f7ee12..0fedd17 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -119,6 +119,7 @@
   zip_set_file_comment.c
   zip_set_file_compression.c
   zip_set_name.c
+  zip_source_accept_empty.c
   zip_source_begin_write.c
   zip_source_begin_write_cloning.c
   zip_source_buffer.c
@@ -220,7 +221,7 @@
 ENDIF()
 
 
-TARGET_LINK_LIBRARIES(zip ${ZLIB_LIBRARY} ${OPTIONAL_LIBRARY})
+TARGET_LINK_LIBRARIES(zip ${ZLIB_LIBRARIES} ${OPTIONAL_LIBRARY})
 INSTALL(TARGETS zip
   RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
   ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/lib/compat.h b/lib/compat.h
index 41b7ddb..91e72bb 100644
--- a/lib/compat.h
+++ b/lib/compat.h
@@ -77,6 +77,11 @@
 #define EOVERFLOW EFBIG
 #endif
 
+/* not supported on at least Windows */
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+
 #ifdef _WIN32
 #if defined(HAVE__CHMOD)
 #define chmod _chmod
@@ -191,4 +196,8 @@
 #define S_ISDIR(mode) (((mode)&S_IFMT) == S_IFDIR)
 #endif
 
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode)&S_IFMT) == S_IFREG)
+#endif
+
 #endif /* compat.h */
diff --git a/lib/zip.h b/lib/zip.h
index 42c3e14..5688b5d 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -229,7 +229,8 @@
     ZIP_SOURCE_SUPPORTS,              /* check whether source supports command */
     ZIP_SOURCE_REMOVE,                /* remove file */
     ZIP_SOURCE_GET_COMPRESSION_FLAGS, /* get compression flags, internal only */
-    ZIP_SOURCE_BEGIN_WRITE_CLONING    /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */
+    ZIP_SOURCE_BEGIN_WRITE_CLONING,   /* like ZIP_SOURCE_BEGIN_WRITE, but keep part of original file */
+    ZIP_SOURCE_ACCEPT_EMPTY           /* whether empty files are valid archives */
 };
 typedef enum zip_source_cmd zip_source_cmd_t;
 
diff --git a/lib/zip_close.c b/lib/zip_close.c
index 2657226..6980a57 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -73,8 +73,10 @@
     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;
+		if (!((zip_error_code_zip(zip_source_error(za->src)) == ZIP_ER_REMOVE) && (zip_error_code_system(zip_source_error(za->src)) == ENOENT))) {
+		    _zip_error_set_from_source(&za->error, za->src);
+		    return -1;
+		}
 	    }
 	}
 	zip_discard(za);
diff --git a/lib/zip_crypto_gnutls.h b/lib/zip_crypto_gnutls.h
index a574466..7e15808 100644
--- a/lib/zip_crypto_gnutls.h
+++ b/lib/zip_crypto_gnutls.h
@@ -39,8 +39,8 @@
 #include <nettle/aes.h>
 #include <nettle/pbkdf2.h>
 
-#include <gnutls/crypto.h>
 #include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
 
 typedef struct {
     union {
diff --git a/lib/zip_open.c b/lib/zip_open.c
index 593bfde..dec6a98 100644
--- a/lib/zip_open.c
+++ b/lib/zip_open.c
@@ -43,8 +43,7 @@
 typedef enum {
     EXISTS_ERROR = -1,
     EXISTS_NOT = 0,
-    EXISTS_EMPTY,
-    EXISTS_NONEMPTY,
+    EXISTS_OK
 } exists_t;
 static zip_t *_zip_allocate_new(zip_source_t *src, unsigned int flags, zip_error_t *error);
 static zip_int64_t _zip_checkcons(zip_t *za, zip_cdir_t *cdir, zip_error_t *error);
@@ -174,19 +173,16 @@
     }
     len = st.size;
 
-    /* treat empty files as empty archives */
-    if (len == 0) {
-	if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
-	    return NULL;
-	}
-
-	return za;
-    }
 
     if ((za = _zip_allocate_new(src, flags, error)) == NULL) {
 	return NULL;
     }
 
+    /* treat empty files as empty archives */
+    if (len == 0 && zip_source_accept_empty(src)) {
+	return za;
+    }
+
     if ((cdir = _zip_find_central_dir(za, len)) == NULL) {
 	_zip_error_copy(error, &za->error);
 	/* keep src so discard does not get rid of it */
@@ -540,7 +536,7 @@
 	return EXISTS_ERROR;
     }
 
-    return (st.valid & ZIP_STAT_SIZE) && st.size == 0 ? EXISTS_EMPTY : EXISTS_NONEMPTY;
+    return EXISTS_OK;
 }
 
 
@@ -725,16 +721,19 @@
     eocd_disk = _zip_buffer_get_16(buffer);
     eocd_offset = _zip_buffer_get_64(buffer);
 
-    if (eocd_offset > ZIP_INT64_MAX || eocd_offset + EOCD64LEN < eocd_offset) {
+    /* valid seek value for start of EOCD */
+    if (eocd_offset > ZIP_INT64_MAX) {
 	zip_error_set(error, ZIP_ER_SEEK, EFBIG);
 	return NULL;
     }
 
+    /* does EOCD fit before EOCD locator? */
     if (eocd_offset + EOCD64LEN > eocdloc_offset + buf_offset) {
 	zip_error_set(error, ZIP_ER_INCONS, 0);
 	return NULL;
     }
 
+    /* make sure current position of buffer is beginning of EOCD */
     if (eocd_offset >= buf_offset && eocd_offset + EOCD64LEN <= buf_offset + _zip_buffer_size(buffer)) {
 	_zip_buffer_set_offset(buffer, eocd_offset - buf_offset);
 	free_buffer = false;
@@ -758,8 +757,10 @@
 	return NULL;
     }
 
+    /* size of EOCD */
     size = _zip_buffer_get_64(buffer);
 
+    /* is there a hole between EOCD and EOCD locator, or do they overlap? */
     if ((flags & ZIP_CHECKCONS) && size + eocd_offset + 12 != buf_offset + eocdloc_offset) {
 	zip_error_set(error, ZIP_ER_INCONS, 0);
 	if (free_buffer) {
@@ -811,6 +812,7 @@
     size = _zip_buffer_get_64(buffer);
     offset = _zip_buffer_get_64(buffer);
 
+    /* did we read past the end of the buffer? */
     if (!_zip_buffer_ok(buffer)) {
 	zip_error_set(error, ZIP_ER_INTERNAL, 0);
 	if (free_buffer) {
@@ -837,6 +839,11 @@
 	return NULL;
     }
 
+    if (nentry > size / CDENTRYSIZE) {
+	zip_error_set(error, ZIP_ER_INCONS, 0);
+	return NULL;
+    }
+
     if ((cd = _zip_cdir_new(nentry, error)) == NULL)
 	return NULL;
 
diff --git a/lib/zip_source_accept_empty.c b/lib/zip_source_accept_empty.c
new file mode 100644
index 0000000..e6d5151
--- /dev/null
+++ b/lib/zip_source_accept_empty.c
@@ -0,0 +1,52 @@
+/*
+  zip_source_accept_empty.c -- if empty source is a valid archive
+  Copyright (C) 2019 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"
+
+
+bool
+zip_source_accept_empty(zip_source_t *src) {
+    int ret;
+
+    if ((zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY)) == 0) {
+	if (ZIP_SOURCE_IS_LAYERED(src)) {
+	    return zip_source_accept_empty(src->src);
+	}
+	return true;
+    }
+
+    ret = (int)_zip_source_call(src, NULL, 0, ZIP_SOURCE_ACCEPT_EMPTY);
+
+    return ret != 0;
+}
diff --git a/lib/zip_source_filep.c b/lib/zip_source_filep.c
index b2fc4b7..30e5991 100644
--- a/lib/zip_source_filep.c
+++ b/lib/zip_source_filep.c
@@ -205,6 +205,7 @@
 	}
     }
 
+    ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_ACCEPT_EMPTY);
 #ifdef CAN_CLONE
     if (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE)) {
 	ctx->supports |= ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_BEGIN_WRITE_CLONING);
@@ -374,12 +375,19 @@
     buf = (char *)data;
 
     switch (cmd) {
+    case ZIP_SOURCE_ACCEPT_EMPTY:
+	return 0;
+
     case ZIP_SOURCE_BEGIN_WRITE:
+#ifdef _WIN32
+	return -1;
+#else
 	if (ctx->fname == NULL) {
 	    zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
 	    return -1;
 	}
 	return create_temp_output(ctx);
+#endif
 
 #ifdef CAN_CLONE
     case ZIP_SOURCE_BEGIN_WRITE_CLONING:
@@ -390,6 +398,13 @@
 	return create_temp_output_cloning(ctx, len);
 #endif
 
+    case ZIP_SOURCE_CLOSE:
+	if (ctx->fname) {
+	    fclose(ctx->f);
+	    ctx->f = NULL;
+	}
+	return 0;
+
     case ZIP_SOURCE_COMMIT_WRITE: {
 	if (fclose(ctx->fout) < 0) {
 	    ctx->fout = NULL;
@@ -405,13 +420,6 @@
 	return 0;
     }
 
-    case ZIP_SOURCE_CLOSE:
-	if (ctx->fname) {
-	    fclose(ctx->f);
-	    ctx->f = NULL;
-	}
-	return 0;
-
     case ZIP_SOURCE_ERROR:
 	return zip_error_to_data(&ctx->error, data, len);
 
@@ -644,7 +652,8 @@
 	flags |= O_RDONLY;
     }
 
-    if ((fd = open(name, flags)) < 0) {
+    /* mode argument needed on Windows */
+    if ((fd = open(name, flags, 0666)) < 0) {
 	return NULL;
     }
     if ((fp = fdopen(fd, writeable ? "r+b" : "rb")) == NULL) {
diff --git a/lib/zip_source_win32handle.c b/lib/zip_source_win32handle.c
index 3a2f52e..96406cd 100644
--- a/lib/zip_source_win32handle.c
+++ b/lib/zip_source_win32handle.c
@@ -127,6 +127,8 @@
 	ctx->supports = ZIP_SOURCE_SUPPORTS_SEEKABLE;
     }
 
+    ctx->supports |= ZIP_SOURCE_ACCEPT_EMPTY;
+
     if ((zs = zip_source_function_create(_win32_read_file, ctx, error)) == NULL) {
 	free(ctx->fname);
 	free(ctx);
@@ -148,6 +150,9 @@
     buf = (char *)data;
 
     switch (cmd) {
+    case ZIP_SOURCE_ACCEPT_EMPTY:
+	return 0;
+
     case ZIP_SOURCE_BEGIN_WRITE:
 	if (ctx->fname == NULL) {
 	    zip_error_set(&ctx->error, ZIP_ER_OPNOTSUPP, 0);
diff --git a/lib/zipint.h b/lib/zipint.h
index 76e8709..e52b60e 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -547,6 +547,7 @@
 
 void _zip_set_open_error(int *zep, const zip_error_t *err, int ze);
 
+bool zip_source_accept_empty(zip_source_t *src);
 zip_int64_t _zip_source_call(zip_source_t *src, void *data, zip_uint64_t length, zip_source_cmd_t command);
 bool _zip_source_eof(zip_source_t *);
 zip_source_t *_zip_source_file_or_p(const char *, FILE *, zip_uint64_t, zip_int64_t, const zip_stat_t *, zip_error_t *error);
diff --git a/man/zip_close.mdoc b/man/zip_close.mdoc
index d9327cb..b5bfd21 100644
--- a/man/zip_close.mdoc
+++ b/man/zip_close.mdoc
@@ -1,5 +1,5 @@
 .\" zip_close.mdoc -- close zip archive
-.\" Copyright (C) 2003-2018 Dieter Baron and Thomas Klausner
+.\" Copyright (C) 2003-2019 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>
@@ -29,7 +29,7 @@
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd December 18, 2017
+.Dd September 17, 2019
 .Dt ZIP_CLOSE 3
 .Os
 .Sh NAME
@@ -54,9 +54,7 @@
 If successful,
 .Ar archive
 is freed.
-If writing fails,
-.Fn zip_close
-fails;
+Otherwise
 .Ar archive
 is left unchanged and must still be freed.
 .Pp
diff --git a/man/zip_source_function.mdoc b/man/zip_source_function.mdoc
index a433b37..9a9cc4b 100644
--- a/man/zip_source_function.mdoc
+++ b/man/zip_source_function.mdoc
@@ -1,5 +1,5 @@
 .\" zip_source_function.mdoc -- create data source from function
-.\" Copyright (C) 2004-2018 Dieter Baron and Thomas Klausner
+.\" Copyright (C) 2004-2019 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>
@@ -29,7 +29,7 @@
 .\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 .\" IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd December 18, 2017
+.Dd September 17, 2019
 .Dt ZIP_SOURCE_FUNCTION 3
 .Os
 .Sh NAME
@@ -110,6 +110,10 @@
 and
 .Dv ZIP_SOURCE_REMOVE .
 .El
+.Ss Dv ZIP_SOURCE_ACCEPT_EMPTY
+Return 1 if an empty source should be accepted as a valid zip archive.
+This is the default if this command is not supported by a source.
+File system backed sources should return 0.
 .Ss Dv ZIP_SOURCE_BEGIN_WRITE
 Prepare the source for writing.
 Use this to create any temporary file(s).
diff --git a/regress/fseek.c b/regress/fseek.c
index 19f3c40..cba1033 100644
--- a/regress/fseek.c
+++ b/regress/fseek.c
@@ -1,6 +1,6 @@
 /*
   fseek.c -- test tool for seeking in zip archives
-  Copyright (C) 2016-2018 Dieter Baron and Thomas Klausner
+  Copyright (C) 2016-2019 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>
@@ -77,6 +77,7 @@
 
     if (zip_fseek(zf, offset, SEEK_SET) < 0) {
 	fprintf(stderr, "%s: zip_fseek failed: %s\n", progname, zip_error_strerror(zip_file_get_error(zf)));
+	zip_fclose(zf);
 	zip_close(z);
 	return 1;
     }
@@ -86,10 +87,16 @@
     }
     if (n < 0) {
 	fprintf(stderr, "%s: zip_fread failed: %s\n", progname, zip_error_strerror(zip_file_get_error(zf)));
+	zip_fclose(zf);
 	zip_close(z);
 	return 1;
     }
 
+    if (zip_fclose(zf) == -1) {
+	fprintf(stderr, "%s: can't close zip archive entry %" PRIu64 " in '%s': %s\n", progname, index, archive, zip_strerror(z));
+	return 1;
+    }
+
     if (zip_close(z) == -1) {
 	fprintf(stderr, "%s: can't close zip archive '%s': %s\n", progname, archive, zip_strerror(z));
 	return 1;
diff --git a/regress/open_empty_2.test b/regress/open_empty_2.test
index 07a5358..bb35bc6 100644
--- a/regress/open_empty_2.test
+++ b/regress/open_empty_2.test
@@ -2,5 +2,6 @@
 program tryopen
 file testfile.txt testfile.txt testfile.txt
 args testfile.txt
-return 0
-stdout opening 'testfile.txt' succeeded, 0 entries
+return 1
+stdout opening 'testfile.txt' returned error 19
+stderr 1 errors
diff --git a/regress/open_file_count.test b/regress/open_file_count.test
index 56f0447..5078978 100644
--- a/regress/open_file_count.test
+++ b/regress/open_file_count.test
@@ -8,5 +8,5 @@
 return 1
 stdout opening 'incons-file-count-high.zzip' returned error 21
 stdout opening 'incons-file-count-low.zzip' returned error 21
-stdout opening 'incons-file-count-overflow.zzip' returned error 14
+stdout opening 'incons-file-count-overflow.zzip' returned error 21
 stderr 3 errors
diff --git a/regress/open_incons.test b/regress/open_incons.test
index 0843816..cda6afb 100644
--- a/regress/open_incons.test
+++ b/regress/open_incons.test
@@ -60,7 +60,7 @@
 stdout opening 'incons-eocd-magic-bad.zzip' returned error 19
 stdout opening 'incons-file-count-high.zzip' returned error 21
 stdout opening 'incons-file-count-low.zzip' returned error 21
-stdout opening 'incons-file-count-overflow.zzip' returned error 14
+stdout opening 'incons-file-count-overflow.zzip' returned error 21
 stdout opening 'incons-local-compression-method.zzip' returned error 21
 stdout opening 'incons-local-compsize-larger.zzip' returned error 21
 stdout opening 'incons-local-compsize-smaller.zzip' returned error 21
diff --git a/src/zipcmp.c b/src/zipcmp.c
index c452684..4e14e60 100644
--- a/src/zipcmp.c
+++ b/src/zipcmp.c
@@ -1,6 +1,6 @@
 /*
   zipcmp.c -- compare zip files
-  Copyright (C) 2003-2018 Dieter Baron and Thomas Klausner
+  Copyright (C) 2003-2019 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>
@@ -233,16 +233,25 @@
     if (paranoid) {
 	if (comment_compare(a[0].comment, a[0].comment_length, a[1].comment, a[1].comment_length) != 0) {
 	    if (verbose) {
-		printf("--- archive comment (%ld)\n", a[0].comment_length);
-		printf("+++ archive comment (%ld)\n", a[1].comment_length);
+		printf("--- archive comment (%zd)\n", a[0].comment_length);
+		printf("+++ archive comment (%zd)\n", a[1].comment_length);
 	    }
 	    res = 1;
 	}
     }
 
-    for (i = 0; i < 2; i++)
-	if (a[i].za)
+    for (i = 0; i < 2; i++) {
+	int j;
+
+	if (a[i].za) {
 	    zip_close(a[i].za);
+	}
+	for (j = 0; j < a[i].nentry; j++) {
+	    free(a[i].entry[j].name);
+	    free(a[i].entry[j].extra_fields);
+	}
+	free(a[i].entry);
+    }
 
     switch (res) {
     case 0:
diff --git a/src/ziptool.c b/src/ziptool.c
index e33b9b5..e074a3d 100644
--- a/src/ziptool.c
+++ b/src/ziptool.c
@@ -503,7 +503,7 @@
     idx = strtoull(argv[0], NULL, 10);
     mtime = (time_t)strtoull(argv[1], NULL, 10);
     if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
-	fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%ld': %s\n", idx, mtime, zip_strerror(za));
+	fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%lld': %s\n", idx, (long long)mtime, zip_strerror(za));
 	return -1;
     }
     return 0;
@@ -523,7 +523,7 @@
     }
     for (idx = 0; idx < (zip_uint64_t)num_entries; idx++) {
 	if (zip_file_set_mtime(za, idx, mtime, 0) < 0) {
-	    fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%ld': %s\n", idx, mtime, zip_strerror(za));
+	    fprintf(stderr, "can't set file mtime at index '%" PRIu64 "' to '%lld': %s\n", idx, (long long)mtime, zip_strerror(za));
 	    return -1;
 	}
     }