-
Couldn't load subscription status.
- Fork 300
Allow changed reads #286
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow changed reads #286
Changes from all commits
4cc039a
389a5f9
f5acde1
699194a
0b876e4
9011a65
168a960
2b0fc64
a68477e
b13daae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should it still be |
||
| return NULL; | ||
| } | ||
|
|
@@ -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); | ||
krnowak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| 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) { | ||
|
|
@@ -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 { | ||
krnowak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| /* 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); | ||
|
|
@@ -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; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
|
@@ -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 | ||
|
|
||
| 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 |
| 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
zip_set_default_passwordalready turns an empty password intoNULL, so the check here is redundant. Just passsrcza->default_passwordto_zip_source_zip_new.