Do not accept empty files as zip archives any longer.
Closes github issue #105.
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/lib/CMakeLists.txt b/lib/CMakeLists.txt
index c7214f9..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
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_open.c b/lib/zip_open.c
index 593bfde..8680700 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;
}
diff --git a/lib/zip_source_accept_empty.c b/lib/zip_source_accept_empty.c
new file mode 100644
index 0000000..07f56f6
--- /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 = _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 51b34f2..4ad3fc2 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,6 +375,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);
@@ -390,6 +394,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 +416,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);
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_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/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