From: Vibhav Pant vibhavp@gmail.com
--- dlls/opcservices/compress.c | 151 +++++++++++++++++++++++++++ dlls/opcservices/tests/opcservices.c | 2 +- include/opcbase.idl | 2 + 3 files changed, 154 insertions(+), 1 deletion(-)
diff --git a/dlls/opcservices/compress.c b/dlls/opcservices/compress.c index 3b0f2a9eeb8..2963a0b22d6 100644 --- a/dlls/opcservices/compress.c +++ b/dlls/opcservices/compress.c @@ -581,6 +581,9 @@ HRESULT compress_add_file(struct zip_archive *archive, const WCHAR *path, struct zip_entry { OPC_COMPRESSION_OPTIONS opt; + ULONG64 compressed_size; + ULONG64 uncompressed_size; + DWORD crc32; ULARGE_INTEGER local_file_offset; };
@@ -608,6 +611,140 @@ struct zip_part IOpcPartUri *name; };
+static BOOL compress_validate_part_size(const struct zip_entry *entry, const struct local_file_header *file, + const struct zip64_extra_field *ext) +{ + if (file->flags & USE_DATA_DESCRIPTOR) + return !(file->compressed_size || file->uncompressed_size || file->crc32 || ext->compressed_size || + ext->uncompressed_size); + else + { + ULONG64 compressed_size = file->compressed_size == UINT32_MAX ? ext->compressed_size : file->compressed_size; + ULONG64 uncompressed_size = file->uncompressed_size == UINT32_MAX ? ext->uncompressed_size : file->uncompressed_size; + + return compressed_size == entry->compressed_size && uncompressed_size == entry->uncompressed_size && + file->crc32 == entry->crc32; + } +} + +/* Decompress the ZIP file described by entry in IStream archive into out. */ +static HRESULT decompress_file_to_stream(IStream *archive, const struct zip_entry *entry, IStream *out) +{ + ULONG64 compressed_data_read = 0, data_uncompressed = 0; + ULONG ext_size = 0, read, crc32 = 0, exp_crc32 = 0; + static const ULONG buffer_size = 0x8000; + struct zip64_extra_field ext = {0}; + struct local_file_header file = {0}; + BYTE *input_buf, *output_buf; + z_stream z_str = {0}; + LARGE_INTEGER off; + HRESULT hr; + int ret; + + off.QuadPart = entry->local_file_offset.QuadPart; + if (FAILED(hr = IStream_Seek(archive, off, STREAM_SEEK_SET, NULL))) + return hr; + hr = IStream_Read(archive, &file, sizeof(file), &read); + if (hr != S_OK) + return hr == S_FALSE ? OPC_E_ZIP_DECOMPRESSION_FAILED : hr; + if (file.method && file.method != Z_DEFLATED) + return OPC_E_ZIP_UNSUPPORTEDARCHIVE; + if (file.uncompressed_size == UINT32_MAX) + ext_size = 8; + if (file.compressed_size == UINT32_MAX) + ext_size = 16; + if (ext_size) + ext_size += 4; /* For the signature and size fields. */ + if (file.extra_length < ext_size) + return OPC_E_ZIP_CORRUPTED_ARCHIVE; + + off.QuadPart = file.name_length; + if (FAILED(hr = IStream_Seek(archive, off, STREAM_SEEK_CUR, NULL))) + return hr; + if (ext_size && ((hr = IStream_Read(archive, &ext, ext_size, &read) != S_OK))) + return hr == S_FALSE ? OPC_E_ZIP_DECOMPRESSION_FAILED : hr; + if (!compress_validate_part_size(entry, &file, &ext)) + return OPC_E_ZIP_CORRUPTED_ARCHIVE; + if (!(file.flags & USE_DATA_DESCRIPTOR)) + exp_crc32 = file.crc32; + if (!(input_buf = malloc(buffer_size))) return E_OUTOFMEMORY; + if (!(output_buf = malloc(buffer_size))) + { + free(input_buf); + return E_OUTOFMEMORY; + } + + z_str.zalloc = zalloc; + z_str.zfree = zfree; + z_str.next_in = input_buf; + z_str.next_out = output_buf; + z_str.avail_out = buffer_size; + ret = inflateInit2(&z_str, -MAX_WBITS); + if (ret) + { + free(input_buf); + free(output_buf); + return OPC_E_ZIP_DECOMPRESSION_FAILED; + } + hr = OPC_E_ZIP_DECOMPRESSION_FAILED; + crc32 = RtlComputeCrc32(0, NULL, 0); + while (compressed_data_read < entry->compressed_size && data_uncompressed < entry->uncompressed_size) + { + ULONG to_read = min(entry->compressed_size - compressed_data_read, buffer_size); + + z_str.total_out = 0; + hr = IStream_Read(archive, input_buf, to_read, &read); + if (hr != S_OK) + { + if (hr == S_FALSE) hr = OPC_E_ZIP_DECOMPRESSION_FAILED; + goto fail; + } + z_str.avail_in = read; + ret = inflate(&z_str, Z_SYNC_FLUSH); + if (ret && ret != Z_STREAM_END) goto fail; + if (FAILED(hr = IStream_Write(out, output_buf, z_str.total_out, NULL ))) goto fail; + crc32 = RtlComputeCrc32(crc32, output_buf, z_str.total_out); + compressed_data_read += read; + data_uncompressed += z_str.total_out; + } + if (compressed_data_read != entry->compressed_size || data_uncompressed != entry->uncompressed_size) goto fail; + inflateEnd(&z_str); + free(input_buf); + free(output_buf); + + if (file.flags & USE_DATA_DESCRIPTOR) + { + ULONG64 exp_compressed, exp_uncompressed; + union { + struct zip32_data_descriptor desc32; + struct zip64_data_descriptor desc64; + } desc = {0}; + + hr = IStream_Read(archive, &desc, ext_size ? sizeof(desc.desc64) : sizeof(desc.desc32), &read); + if (hr != S_OK) return hr == S_FALSE ? OPC_E_ZIP_DECOMPRESSION_FAILED : hr; + if (ext_size) + { + exp_compressed = desc.desc64.compressed_size; + exp_uncompressed = desc.desc64.uncompressed_size; + } + else + { + exp_compressed = desc.desc32.compressed_size; + exp_uncompressed = desc.desc32.uncompressed_size; + } + if (exp_compressed != compressed_data_read || exp_uncompressed != data_uncompressed) return OPC_E_ZIP_CORRUPTED_ARCHIVE; + exp_crc32 = desc.desc64.crc32; + } + if (exp_crc32 != crc32) return OPC_E_ZIP_CORRUPTED_ARCHIVE; + return hr; + +fail: + inflateEnd(&z_str); + free(input_buf); + free(output_buf); + return hr; +} + /* The stream should be at the start of the central directory. */ static HRESULT compress_read_entries(IOpcFactory *factory, IStream *stream, ULONG dir_records, IOpcPartSet *partset) { @@ -671,7 +808,11 @@ static HRESULT compress_read_entries(IOpcFactory *factory, IStream *stream, ULON goto done; } entry.opt = dir.method ? deflate_opts[(dir.flags & DEFLATE_LEVEL_MASK) >> 1] : OPC_COMPRESSION_NONE; + entry.uncompressed_size = (dir.uncompressed_size == UINT32_MAX) ? ext.uncompressed_size : dir.uncompressed_size; + entry.compressed_size = (dir.compressed_size == UINT32_MAX) ? ext.compressed_size : dir.compressed_size; + entry.crc32 = dir.crc32; entry.local_file_offset.QuadPart = (dir.local_file_offset == UINT32_MAX) ? ext.offset : dir.local_file_offset; + off.QuadPart = dir.comment_length; if (dir.comment_length && FAILED(hr = IStream_Seek(stream, off, STREAM_SEEK_CUR, NULL))) { @@ -731,11 +872,21 @@ static HRESULT compress_read_entries(IOpcFactory *factory, IStream *stream, ULON
for (i = 0; i < part_count; i++) { + IStream *content; IOpcPart *part;
hr = IOpcPartSet_CreatePart(partset, parts[i].name, L"", parts[i].entry.opt, &part); if (FAILED(hr)) goto done; + if (FAILED(hr = IOpcPart_GetContentStream(part, &content))) + { + IOpcPart_Release(part); + goto done; + } + + hr = decompress_file_to_stream(stream, &parts[i].entry, content); + IStream_Release(content); IOpcPart_Release(part); + if (FAILED(hr)) goto done; }
done: diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c index 31508d860d4..eaaf68f6516 100644 --- a/dlls/opcservices/tests/opcservices.c +++ b/dlls/opcservices/tests/opcservices.c @@ -1356,7 +1356,7 @@ static void test_read_package(void) memset(exp_buf, i, sizeof(exp_buf)); hr = IStream_Read(stream, buf, sizeof(buf), &read); ok(hr == S_OK, "Failed to read from stream, hr %#lx.\n", hr); - todo_wine ok(read == sizeof(buf), "Got read %lu != %Iu.\n", read, sizeof(buf)); + ok(read == sizeof(buf), "Got read %lu != %Iu.\n", read, sizeof(buf)); ok(!memcmp(buf, exp_buf, read), "Got mismatching data.\n"); IStream_Release(stream);
diff --git a/include/opcbase.idl b/include/opcbase.idl index 9ba7b734cfa..ec9bc88b72a 100644 --- a/include/opcbase.idl +++ b/include/opcbase.idl @@ -55,4 +55,6 @@ cpp_quote("#define OPC_E_ENUM_CANNOT_MOVE_PREVIOUS MAKE_HRESULT(SEVERITY_ERROR, cpp_quote("#define OPC_E_ENUM_INVALID_POSITION MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x53)") cpp_quote("#define OPC_E_ZIP_COMPRESSION_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1003)") cpp_quote("#define OPC_E_ZIP_CORRUPTED_ARCHIVE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1002)") +cpp_quote("#define OPC_E_ZIP_DECOMPRESSION_FAILED MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1004)") +cpp_quote("#define OPC_E_ZIP_UNSUPPORTEDARCHIVE MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x1008)") cpp_quote("#define OPC_E_ZIP_DUPLICATE_NAME MAKE_HRESULT(SEVERITY_ERROR, FACILITY_OPC, 0x100b)")