Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/zip.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ enum zip_source_cmd {
ZIP_SOURCE_RESERVED_1, /* previously used internally */
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 */
ZIP_SOURCE_GET_FILE_ATTRIBUTES /* get additional file attributes */
ZIP_SOURCE_GET_FILE_ATTRIBUTES, /* get additional file attributes */
ZIP_SOURCE_SUPPORTS_REOPEN /* allow reading from changed entry */
};
typedef enum zip_source_cmd zip_source_cmd_t;

Expand Down
2 changes: 1 addition & 1 deletion lib/zip_source_buffer.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,7 @@ read_data(void *state, void *data, zip_uint64_t len, zip_source_cmd_t cmd) {
}

case ZIP_SOURCE_SUPPORTS:
return zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_BEGIN_WRITE_CLONING, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, -1);
return zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SEEK, ZIP_SOURCE_TELL, ZIP_SOURCE_BEGIN_WRITE, ZIP_SOURCE_BEGIN_WRITE_CLONING, ZIP_SOURCE_COMMIT_WRITE, ZIP_SOURCE_REMOVE, ZIP_SOURCE_ROLLBACK_WRITE, ZIP_SOURCE_SEEK_WRITE, ZIP_SOURCE_TELL_WRITE, ZIP_SOURCE_WRITE, ZIP_SOURCE_SUPPORTS_REOPEN, -1);

case ZIP_SOURCE_TELL:
if (ctx->in->offset > ZIP_INT64_MAX) {
Expand Down
2 changes: 1 addition & 1 deletion lib/zip_source_compress.c
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ compress_callback(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip
}

case ZIP_SOURCE_SUPPORTS:
return ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, -1);
return ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS_REOPEN, -1);

default:
zip_error_set(&ctx->error, ZIP_ER_INTERNAL, 0);
Expand Down
2 changes: 1 addition & 1 deletion lib/zip_source_file_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ zip_source_file_common_new(const char *fname, void *file, zip_uint64_t start, zi
zip_error_init(&ctx->error);
zip_file_attributes_init(&ctx->attributes);

ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1);
ctx->supports = ZIP_SOURCE_SUPPORTS_READABLE | zip_source_make_command_bitmap(ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, ZIP_SOURCE_SUPPORTS_REOPEN, -1);

zip_source_file_stat_init(&sb);
if (!ops->stat(ctx, &sb)) {
Expand Down
3 changes: 3 additions & 0 deletions lib/zip_source_layered.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ zip_source_layered_create(zip_source_t *src, zip_source_layered_callback cb, voi
if (zs->supports < 0) {
zs->supports = ZIP_SOURCE_SUPPORTS_READABLE;
}
if (!zip_source_supports_reopen(src)) {
zs->supports &= ~ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SUPPORTS_REOPEN);
}

return zs;
}
2 changes: 1 addition & 1 deletion lib/zip_source_pkware_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pkware_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zip_so
}

case ZIP_SOURCE_SUPPORTS:
return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SUPPORTS_REOPEN, -1);

case ZIP_SOURCE_ERROR:
return zip_error_to_data(&ctx->error, data, len);
Expand Down
4 changes: 4 additions & 0 deletions lib/zip_source_supports.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ zip_source_supports(zip_source_t *src) {
return src->supports;
}

bool
zip_source_supports_reopen(zip_source_t *src) {
return (zip_source_supports(src) & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SUPPORTS_REOPEN)) != 0;
}

ZIP_EXTERN zip_int64_t
zip_source_make_command_bitmap(zip_source_cmd_t cmd0, ...) {
Expand Down
2 changes: 1 addition & 1 deletion lib/zip_source_window.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ _zip_source_window_new(zip_source_t *src, zip_uint64_t start, zip_int64_t length
ctx->source_archive = source_archive;
ctx->source_index = source_index;
zip_error_init(&ctx->error);
ctx->supports = (zip_source_supports(src) & ZIP_SOURCE_SUPPORTS_SEEKABLE) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1));
ctx->supports = (zip_source_supports(src) & (ZIP_SOURCE_SUPPORTS_SEEKABLE | ZIP_SOURCE_SUPPORTS_REOPEN)) | (zip_source_make_command_bitmap(ZIP_SOURCE_GET_FILE_ATTRIBUTES, ZIP_SOURCE_SUPPORTS, ZIP_SOURCE_TELL, -1));
ctx->needs_seek = (ctx->supports & ZIP_SOURCE_MAKE_COMMAND_BITMASK(ZIP_SOURCE_SEEK)) ? true : false;

if (st) {
Expand Down
2 changes: 1 addition & 1 deletion lib/zip_source_winzip_aes_decode.c
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ winzip_aes_decrypt(zip_source_t *src, void *ud, void *data, zip_uint64_t len, zi
}

case ZIP_SOURCE_SUPPORTS:
return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, -1);
return zip_source_make_command_bitmap(ZIP_SOURCE_OPEN, ZIP_SOURCE_READ, ZIP_SOURCE_CLOSE, ZIP_SOURCE_STAT, ZIP_SOURCE_ERROR, ZIP_SOURCE_FREE, ZIP_SOURCE_SUPPORTS_REOPEN, -1);

case ZIP_SOURCE_ERROR:
return zip_error_to_data(&ctx->error, data, len);
Expand Down
12 changes: 10 additions & 2 deletions lib/zip_source_zip.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include "zipint.h"

ZIP_EXTERN zip_source_t *zip_source_zip_create(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_int64_t len, zip_error_t *error) {
const char *password;

if (len < -1) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
Expand All @@ -49,8 +51,14 @@ ZIP_EXTERN zip_source_t *zip_source_zip_create(zip_t *srcza, zip_uint64_t srcidx
flags |= ZIP_FL_COMPRESSED;
else
flags &= ~ZIP_FL_COMPRESSED;

return _zip_source_zip_new(srcza, srcidx, flags, start, (zip_uint64_t)len, NULL, error);

password = srcza->default_password;

if (password != NULL && password[0] == '\0') {
password = NULL;
}

return _zip_source_zip_new(srcza, srcidx, flags, start, (zip_uint64_t)len, password, error);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zip_set_default_password already turns an empty password into NULL, so the check here is redundant. Just pass srcza->default_password to _zip_source_zip_new.

}


Expand Down
158 changes: 138 additions & 20 deletions lib/zip_source_zip_new.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,23 +39,43 @@
static void _zip_file_attributes_from_dirent(zip_file_attributes_t *attributes, zip_dirent_t *de);

zip_source_t *_zip_source_zip_new(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t flags, zip_uint64_t start, zip_uint64_t len, const char *password, zip_error_t *error) {
/* TODO: We need to make sure that the returned source is invalidated when srcza is closed. */
zip_source_t *src, *s2;
zip_stat_t st;
zip_file_attributes_t attributes;
zip_dirent_t *de;
bool partial_data, needs_crc, needs_decrypt, needs_decompress;
bool partial_data, needs_crc, encrypted, needs_decrypt, compressed, needs_decompress, changed_data, have_size, have_comp_size;
zip_flags_t stat_flags;
zip_int64_t data_len;

if (srcza == NULL || srcidx >= srcza->nentry || len > ZIP_INT64_MAX) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}

if ((flags & ZIP_FL_UNCHANGED) == 0 && (ZIP_ENTRY_DATA_CHANGED(srcza->entry + srcidx) || srcza->entry[srcidx].deleted)) {
zip_error_set(error, ZIP_ER_CHANGED, 0);
return NULL;
changed_data = false;
if ((flags & ZIP_FL_UNCHANGED) == 0) {
zip_entry_t *entry = srcza->entry + srcidx;
if (ZIP_ENTRY_DATA_CHANGED(entry)) {
if (!zip_source_supports_reopen(entry->source)) {
zip_error_set(error, ZIP_ER_CHANGED, 0);
return NULL;
}

changed_data = true;
}
else if (entry->deleted) {
zip_error_set(error, ZIP_ER_CHANGED, 0);
return NULL;
}
}

if (zip_stat_index(srcza, srcidx, flags | ZIP_FL_UNCHANGED, &st) < 0) {
stat_flags = flags;
if (!changed_data) {
stat_flags |= ZIP_FL_UNCHANGED;
}

if (zip_stat_index(srcza, srcidx, stat_flags, &st) < 0) {
zip_error_set(error, ZIP_ER_INTERNAL, 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to set the error before returning failure. Please reinstate this line.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should it still be ZIP_ER_INTERNAL or should it rather copy the error stored in srcza into error, as zip_stat_index now could be calling third party code?

return NULL;
}
Expand All @@ -69,21 +89,45 @@ zip_source_t *_zip_source_zip_new(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t
return NULL;
}

have_size = (st.valid & ZIP_STAT_SIZE) != 0;
/* overflow or past end of file */
if ((start > 0 || len > 0) && (start + len < start || start + len > st.size)) {
if ((start > 0 || len > 0) && ((start + len < start) || (have_size && (start + len > st.size)))) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}

if (len == 0) {
len = st.size - start;
if (have_size) {
if (st.size - start > ZIP_INT64_MAX) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
data_len = (zip_int64_t)(st.size - start);
}
else {
data_len = -1;
}
}
else {
if (len > ZIP_INT64_MAX) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
data_len = (zip_int64_t)(len);
}

partial_data = len < st.size;
needs_decrypt = ((flags & ZIP_FL_ENCRYPTED) == 0) && (st.encryption_method != ZIP_EM_NONE);
needs_decompress = ((flags & ZIP_FL_COMPRESSED) == 0) && (st.comp_method != ZIP_CM_STORE);
if (have_size) {
partial_data = (zip_uint64_t)(data_len) < st.size;
}
else {
partial_data = true;
}
encrypted = (st.valid & ZIP_STAT_ENCRYPTION_METHOD) && (st.encryption_method != ZIP_EM_NONE);
needs_decrypt = ((flags & ZIP_FL_ENCRYPTED) == 0) && encrypted;
compressed = (st.valid & ZIP_STAT_COMP_METHOD) && (st.comp_method != ZIP_CM_STORE);
needs_decompress = ((flags & ZIP_FL_COMPRESSED) == 0) && compressed;
/* when reading the whole file, check for CRC errors */
needs_crc = ((flags & ZIP_FL_COMPRESSED) == 0 || st.comp_method == ZIP_CM_STORE) && !partial_data;
needs_crc = ((flags & ZIP_FL_COMPRESSED) == 0 || !compressed) && !partial_data && (st.valid & ZIP_STAT_CRC) != 0;

if (needs_decrypt) {
if (password == NULL) {
Expand All @@ -100,32 +144,106 @@ zip_source_t *_zip_source_zip_new(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t
}
_zip_file_attributes_from_dirent(&attributes, de);

if (st.comp_size == 0) {
return zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error);
have_comp_size = (st.valid & ZIP_STAT_COMP_SIZE) != 0;
if (compressed) {
if (have_comp_size && st.comp_size == 0) {
src = zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error);
}
else {
src = NULL;
}
}
else if (have_size && st.size == 0) {
src = zip_source_buffer_with_attributes_create(NULL, 0, 0, &attributes, error);
}
else {
src = NULL;
}

/* if we created source buffer above, store it in s2, so we can
free it after it's wrapped in window source */
s2 = src;
/* if we created a buffer source above, then treat it as if
reading the changed data - that way we don't need add another
special case to the code below that wraps it in the window
source */
changed_data = changed_data || (src != NULL);

if (partial_data && !needs_decrypt && !needs_decompress) {
struct zip_stat st2;
zip_t *source_archive;
zip_uint64_t source_index;

if (changed_data) {
if (src == NULL) {
src = srcza->entry[srcidx].source;
}
source_archive = NULL;
source_index = 0;
}
else {
src = srcza->src;
source_archive = srcza;
source_index = srcidx;
}

st2.size = len;
st2.comp_size = len;
st2.comp_method = ZIP_CM_STORE;
st2.mtime = st.mtime;
st2.valid = ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE | ZIP_STAT_COMP_METHOD | ZIP_STAT_MTIME;
st2.valid = ZIP_STAT_COMP_METHOD;
if (data_len >= 0) {
st2.size = (zip_uint64_t)data_len;
st2.comp_size = (zip_uint64_t)data_len;
st2.valid |= ZIP_STAT_SIZE | ZIP_STAT_COMP_SIZE;
}
if (st.valid & ZIP_STAT_MTIME) {
st2.mtime = st.mtime;
st2.valid |= ZIP_STAT_MTIME;
}

if ((src = _zip_source_window_new(srcza->src, start, (zip_int64_t)len, &st2, &attributes, srcza, srcidx, error)) == NULL) {
if ((src = _zip_source_window_new(src, start, data_len, &st2, &attributes, source_archive, source_index, error)) == NULL) {
return NULL;
}
}
else {
/* here we restrict src to file data, so no point in doing it for
source that already represents only the file data */
else if (!changed_data) {
/* this branch is executed only for archive sources; we know
that stat data come from the archive too, so it's safe to
assume that st has a comp_size specified */
if (st.comp_size > ZIP_INT64_MAX) {
zip_error_set(error, ZIP_ER_INVAL, 0);
return NULL;
}
/* despite the fact that we want the whole data file, we still
wrap the source into a window source to add st and
attributes and to have a source that positions the read
offset properly before each read for multiple zip_file_t
referring to the same underlying source */
if ((src = _zip_source_window_new(srcza->src, 0, (zip_int64_t)st.comp_size, &st, &attributes, srcza, srcidx, error)) == NULL) {
return NULL;
}
}
else {
/* this branch gets executed when reading the whole changed
data file or when "reading" from a zero-sized source buffer
that we created above */
if (src == NULL) {
src = srcza->entry[srcidx].source;
}
/* despite the fact that we want the whole data file, we still
wrap the source into a window source to add st and
attributes and to have a source that positions the read
offset properly before each read for multiple zip_file_t
referring to the same underlying source */
if ((src = _zip_source_window_new(src, 0, data_len, &st, &attributes, NULL, 0, error)) == NULL) {
return NULL;
}
}
/* drop a reference of a buffer source if we created one above -
window source that wraps it has increased the ref count on its
own */
if (s2 != NULL) {
zip_source_free(s2);
}

if (_zip_source_set_source_archive(src, srcza) < 0) {
zip_source_free(src);
Expand Down Expand Up @@ -167,7 +285,7 @@ zip_source_t *_zip_source_zip_new(zip_t *srcza, zip_uint64_t srcidx, zip_flags_t
}

if (partial_data && (needs_decrypt || needs_decompress)) {
s2 = zip_source_window_create(src, start, (zip_int64_t)len, error);
s2 = zip_source_window_create(src, start, data_len, error);
zip_source_free(src);
if (s2 == NULL) {
return NULL;
Expand Down
1 change: 1 addition & 0 deletions lib/zipint.h
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ zip_source_t *zip_source_pkware_decode(zip_t *, zip_source_t *, zip_uint16_t, in
zip_source_t *zip_source_pkware_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *);
int zip_source_remove(zip_source_t *);
zip_int64_t zip_source_supports(zip_source_t *src);
bool zip_source_supports_reopen(zip_source_t *src);
zip_source_t *zip_source_winzip_aes_decode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *);
zip_source_t *zip_source_winzip_aes_encode(zip_t *, zip_source_t *, zip_uint16_t, int, const char *);
zip_source_t *zip_source_buffer_with_attributes(zip_t *za, const void *data, zip_uint64_t len, int freep, zip_file_attributes_t *attributes);
Expand Down
10 changes: 9 additions & 1 deletion man/zip_source_function.mdoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ The functions
.Fn zip_source_function
and
.Fn zip_source_function_create
creates a zip source from the user-provided function
create a zip source from the user-provided function
.Ar fn ,
which must be of the following type:
.Pp
Expand Down Expand Up @@ -109,6 +109,12 @@ Must additionally support
.Dv ZIP_SOURCE_TELL_WRITE ,
and
.Dv ZIP_SOURCE_REMOVE .
.Pp
On top of the above, supporting a pseudo-command
.Dv ZIP_SOURCE_SUPPORTS_REOPEN
will allow calling
.Fn zip_fopen
without the ZIP_FL_UNCHANGED flag to read the modified data.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the zip_source level, it means that you can read the data multiple times. If you want to document what it means for zip_fopen, document it there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I don't understand this. You could read the data multiple times without this PR - it was possible because window source takes care of repositioning the underlying source by seeking it to the desired offset before doing a read. The catch was that it was only possible for unmodified data, and since unmodified data is backed by a source that implements seeking, reading data multiple times always worked.

.El
.Ss Dv ZIP_SOURCE_ACCEPT_EMPTY
Return 1 if an empty source should be accepted as a valid zip archive.
Expand Down Expand Up @@ -300,6 +306,8 @@ Return the current write offset in the source, like
.Ss Dv ZIP_SOURCE_WRITE
Write data to the source.
Return number of bytes written.
.Ss Dv ZIP_SOURCE_SUPPORTS_REOPEN
Never called.
.Ss Return Values
Commands should return \-1 on error.
.Dv ZIP_SOURCE_ERROR
Expand Down
5 changes: 5 additions & 0 deletions regress/fopen_multiple.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# read contents from multiply opened unchanged file
return 0
args test_open_multiple.zip fopen stuff fopen stuff fread 0 2 fread 1 4 fread 0 3 fread 1 3 fread 0 3 fread 1 1 unchange_all
file test_open_multiple.zip test_open_multiple.zip test_open_multiple.zip
stdout ababcdcdeefgfghh
5 changes: 5 additions & 0 deletions regress/fopen_multiple_reopen.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# read contents from multiply reopened changed file
return 0
args test_open_multiple.zip replace_file_contents 0 12345678 fopen stuff fopen stuff fread 0 2 fread 1 4 fread 0 3 fread 1 3 fread 0 3 fread 1 1 unchange_all
file test_open_multiple.zip test_open_multiple.zip test_open_multiple.zip
stdout 1212343455676788
Loading