Recognize and ignore 0 bytes added by Android ZipAlign tool. Note: modifying APKs with libzip will not keep alignments.
diff --git a/TODO b/TODO index cc6034d..3dca4ec 100644 --- a/TODO +++ b/TODO
@@ -46,8 +46,6 @@ * set O_CLOEXEC flag after fopen and mkstemp * configure: use windows backends on windows * add append-only mode writing file to disk incrementally to keep memory usage low -* add zipalign mode or tool that puts uncompressed data on an offset that's a multiple - of four; used on Android to speed up loading of data * zip_file_set_mtime: support InfoZIP time stamps * support streaming output (creating new archive to e.g. stdout)
diff --git a/lib/zip_dirent.c b/lib/zip_dirent.c index 6b268f8..f787e46 100644 --- a/lib/zip_dirent.c +++ b/lib/zip_dirent.c
@@ -423,7 +423,7 @@ } return -1; } - if ((zde->extra_fields=_zip_ef_parse(ef, ef_len, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, error)) == NULL) { + if (!_zip_ef_parse(ef, ef_len, local ? ZIP_EF_LOCAL : ZIP_EF_CENTRAL, &zde->extra_fields, error)) { free(ef); if (!from_buffer) { _zip_buffer_free(buffer);
diff --git a/lib/zip_extra_field.c b/lib/zip_extra_field.c index eb69356..c3d7387 100644 --- a/lib/zip_extra_field.c +++ b/lib/zip_extra_field.c
@@ -205,8 +205,8 @@ } -zip_extra_field_t * -_zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_error_t *error) +bool +_zip_ef_parse(const zip_uint8_t *data, zip_uint16_t len, zip_flags_t flags, zip_extra_field_t **ef_head_p, zip_error_t *error) { zip_buffer_t *buffer; zip_extra_field_t *ef, *ef2, *ef_head; @@ -218,7 +218,7 @@ ef_head = ef = NULL; - while (_zip_buffer_ok(buffer) && !_zip_buffer_eof(buffer)) { + while (_zip_buffer_ok(buffer) && _zip_buffer_left(buffer) >= 4) { zip_uint16_t fid, flen; zip_uint8_t *ef_data; @@ -227,14 +227,17 @@ ef_data = _zip_buffer_get(buffer, flen); if (ef_data == NULL) { - break; + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(buffer); + _zip_ef_free(ef_head); + return false; } if ((ef2=_zip_ef_new(fid, flen, ef_data, flags)) == NULL) { zip_error_set(error, ZIP_ER_MEMORY, 0); _zip_buffer_free(buffer); _zip_ef_free(ef_head); - return NULL; + return false; } if (ef_head) { @@ -246,15 +249,26 @@ } if (!_zip_buffer_eof(buffer)) { - zip_error_set(error, ZIP_ER_INCONS, 0); - _zip_buffer_free(buffer); - _zip_ef_free(ef_head); - return NULL; + /* Android APK files align stored file data with padding in extra fields; ignore. */ + /* see https://android.googlesource.com/platform/build/+/master/tools/zipalign/ZipAlign.cpp */ + size_t glen = _zip_buffer_left(buffer); + zip_uint8_t *garbage; + garbage = _zip_buffer_get(buffer, len); + if (glen >= 4 || garbage == NULL || memcmp(garbage, "\0\0\0", glen) != 0) { + zip_error_set(error, ZIP_ER_INCONS, 0); + _zip_buffer_free(buffer); + _zip_ef_free(ef_head); + return false; + } } _zip_buffer_free(buffer); + + if (ef_head_p) { + *ef_head_p = ef_head; + } - return ef_head; + return true; } @@ -398,14 +412,16 @@ if (ef_raw == NULL) return -1; - if ((ef=_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &za->error)) == NULL) { + if (!_zip_ef_parse(ef_raw, ef_len, ZIP_EF_LOCAL, &ef, &za->error)) { free(ef_raw); return -1; } free(ef_raw); - - ef = _zip_ef_remove_internal(ef); - e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef); + + if (ef) { + ef = _zip_ef_remove_internal(ef); + e->orig->extra_fields = _zip_ef_merge(e->orig->extra_fields, ef); + } } e->orig->local_extra_fields_read = 1;
diff --git a/lib/zipint.h b/lib/zipint.h index f502ce8..86edcb3 100644 --- a/lib/zipint.h +++ b/lib/zipint.h
@@ -503,7 +503,7 @@ const zip_uint8_t *_zip_ef_get_by_id(const zip_extra_field_t *, zip_uint16_t *, zip_uint16_t, zip_uint16_t, zip_flags_t, zip_error_t *); zip_extra_field_t *_zip_ef_merge(zip_extra_field_t *, zip_extra_field_t *); zip_extra_field_t *_zip_ef_new(zip_uint16_t, zip_uint16_t, const zip_uint8_t *, zip_flags_t); -zip_extra_field_t *_zip_ef_parse(const zip_uint8_t *, zip_uint16_t, zip_flags_t, zip_error_t *); +bool _zip_ef_parse(const zip_uint8_t *, zip_uint16_t, zip_flags_t, zip_extra_field_t **, zip_error_t *); zip_extra_field_t *_zip_ef_remove_internal(zip_extra_field_t *); zip_uint16_t _zip_ef_size(const zip_extra_field_t *, zip_flags_t); int _zip_ef_write(zip_t *za, const zip_extra_field_t *ef, zip_flags_t flags);