[5/7] ntdll: Implement RtlDecompressFragment.
Sebastian Lackner
sebastian at fds-team.de
Wed Jul 8 20:07:03 CDT 2015
Based on a patch by Michael Müller.
For https://bugs.winehq.org/show_bug.cgi?id=37449
---
dlls/ntdll/ntdll.spec | 2
dlls/ntdll/rtl.c | 214 ++++++++++++++++++++++++++++++++++--
dlls/ntdll/tests/rtl.c | 3
dlls/ntoskrnl.exe/ntoskrnl.exe.spec | 2
4 files changed, 210 insertions(+), 11 deletions(-)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index 75dc647..ca3561d 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -513,7 +513,7 @@
@ stdcall RtlDecodePointer(ptr)
# @ stub RtlDecodeSystemPointer
@ stdcall RtlDecompressBuffer(long ptr long ptr long ptr)
-@ stub RtlDecompressFragment
+@ stdcall RtlDecompressFragment(long ptr long ptr long long ptr ptr)
@ stub RtlDefaultNpAcl
@ stub RtlDelete
@ stdcall RtlDeleteAce(ptr long)
diff --git a/dlls/ntdll/rtl.c b/dlls/ntdll/rtl.c
index d9e448a..9a196d2 100644
--- a/dlls/ntdll/rtl.c
+++ b/dlls/ntdll/rtl.c
@@ -1309,17 +1309,219 @@ NTSTATUS WINAPI RtlCompressBuffer(USHORT format, PUCHAR uncompressed, ULONG unco
}
}
+/* decompress a single LZNT1 chunk */
+static PUCHAR lznt1_decompress_chunk(UCHAR *dst, ULONG dst_size, UCHAR *src, ULONG src_size)
+{
+ UCHAR *src_cur = src, *src_end = src + src_size;
+ UCHAR *dst_cur = dst, *dst_end = dst + dst_size;
+ ULONG displacement_bits, length_bits;
+ ULONG code_displacement, code_length;
+ WORD flags, code;
+
+ while (src_cur < src_end && dst_cur < dst_end)
+ {
+ flags = 0x8000 | *src_cur++;
+ while ((flags & 0xFF00) && src_cur < src_end)
+ {
+ if (flags & 1)
+ {
+ /* backwards reference */
+ if (src_cur + sizeof(WORD) > src_end)
+ return NULL;
+
+ code = *(WORD *)src_cur;
+ src_cur += sizeof(WORD);
+
+ /* find length / displacement bits */
+ for (displacement_bits = 12; displacement_bits > 4; displacement_bits--)
+ if ((1 << (displacement_bits - 1)) < dst_cur - dst) break;
+
+ length_bits = 16 - displacement_bits;
+ code_length = (code & ((1 << length_bits) - 1)) + 3;
+ code_displacement = (code >> length_bits) + 1;
+
+ if (dst_cur < dst + code_displacement)
+ return NULL;
+
+ /* copy bytes of chunk - we can't use memcpy()
+ * since source and dest can be overlapping */
+ while (code_length--)
+ {
+ if (dst_cur >= dst_end) return dst_cur;
+ *dst_cur = *(dst_cur - code_displacement);
+ dst_cur++;
+ }
+ }
+ else
+ {
+ /* uncompressed data */
+ if (dst_cur >= dst_end) return dst_cur;
+ *dst_cur++ = *src_cur++;
+ }
+ flags >>= 1;
+ }
+ }
+
+ return dst_cur;
+}
+
+/* decompress data encoded with LZNT1 */
+static NTSTATUS lznt1_decompress(UCHAR *dst, ULONG dst_size, UCHAR *src, ULONG src_size,
+ ULONG offset, ULONG *final_size, UCHAR *workspace)
+{
+ UCHAR *src_cur = src, *src_end = src + src_size;
+ UCHAR *dst_cur = dst, *dst_end = dst + dst_size;
+ ULONG chunk_size, block_size;
+ WORD chunk_header;
+ UCHAR *ptr;
+
+ if (src_cur + sizeof(WCHAR) > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ /* skip over chunks which have a distance >= 0x1000 to the destination offset */
+ while (offset >= 0x1000 && src_cur + sizeof(WCHAR) <= src_end)
+ {
+ chunk_header = *(WORD *)src_cur;
+ src_cur += sizeof(WCHAR);
+ if (!chunk_header) goto out;
+ chunk_size = (chunk_header & 0xFFF) + 1;
+
+ if (src_cur + chunk_size > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ src_cur += chunk_size;
+ offset -= 0x1000;
+ }
+
+ /* check if a chunk is included partially */
+ if (offset && src_cur + sizeof(WCHAR) <= src_end)
+ {
+ chunk_header = *(WORD *)src_cur;
+ src_cur += sizeof(WCHAR);
+ if (!chunk_header) goto out;
+ chunk_size = (chunk_header & 0xFFF) + 1;
+
+ if (src_cur + chunk_size > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ if (dst_cur >= dst_end)
+ goto out;
+
+ if (chunk_header & 0x8000)
+ {
+ /* compressed chunk */
+ if (!workspace) return STATUS_ACCESS_VIOLATION;
+ ptr = lznt1_decompress_chunk(workspace, 0x1000, src_cur, chunk_size);
+ if (!ptr) return STATUS_BAD_COMPRESSION_BUFFER;
+ if (ptr - workspace > offset)
+ {
+ block_size = min((ptr - workspace) - offset, dst_end - dst_cur);
+ memcpy(dst_cur, workspace + offset, block_size);
+ dst_cur += block_size;
+ }
+ }
+ else
+ {
+ /* uncompressed chunk */
+ if (chunk_size > offset)
+ {
+ block_size = min(chunk_size - offset, dst_end - dst_cur);
+ memcpy(dst_cur, src_cur + offset, block_size);
+ dst_cur += block_size;
+ }
+ }
+
+ src_cur += chunk_size;
+ }
+
+ /* remaining blocks */
+ while (src_cur + sizeof(WCHAR) <= src_end)
+ {
+ chunk_header = *(WORD *)src_cur;
+ src_cur += sizeof(WCHAR);
+ if (!chunk_header) goto out;
+ chunk_size = (chunk_header & 0xFFF) + 1;
+
+ if (src_cur + chunk_size > src_end)
+ return STATUS_BAD_COMPRESSION_BUFFER;
+
+ /* fill space with padding */
+ block_size = ((dst_cur - dst) + offset) & 0xFFF;
+ if (block_size)
+ {
+ block_size = 0x1000 - block_size;
+ if (dst_cur + block_size >= dst_end)
+ goto out;
+ memset(dst_cur, 0, block_size);
+ dst_cur += block_size;
+ }
+
+ if (dst_cur >= dst_end)
+ goto out;
+
+ if (chunk_header & 0x8000)
+ {
+ /* compressed chunk */
+ dst_cur = lznt1_decompress_chunk(dst_cur, dst_end - dst_cur, src_cur, chunk_size);
+ if (!dst_cur) return STATUS_BAD_COMPRESSION_BUFFER;
+ }
+ else
+ {
+ /* uncompressed chunk */
+ block_size = min(chunk_size, dst_end - dst_cur);
+ memcpy(dst_cur, src_cur, block_size);
+ dst_cur += block_size;
+ }
+
+ src_cur += chunk_size;
+ }
+
+out:
+ if (final_size)
+ *final_size = dst_cur - dst;
+
+ return STATUS_SUCCESS;
+
+}
+
+/******************************************************************************
+ * RtlDecompressFragment [NTDLL.@]
+ */
+NTSTATUS RtlDecompressFragment(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
+ PUCHAR compressed, ULONG compressed_size, ULONG offset,
+ PULONG final_size, PVOID workspace)
+{
+ TRACE("0x%04x, %p, %u, %p, %u, %u, %p, %p\n", format, uncompressed,
+ uncompressed_size, compressed, compressed_size, offset, final_size, workspace);
+
+ switch (format & ~COMPRESSION_ENGINE_MAXIMUM)
+ {
+ case COMPRESSION_FORMAT_LZNT1:
+ return lznt1_decompress(uncompressed, uncompressed_size, compressed,
+ compressed_size, offset, final_size, workspace);
+
+ case COMPRESSION_FORMAT_NONE:
+ case COMPRESSION_FORMAT_DEFAULT:
+ return STATUS_INVALID_PARAMETER;
+
+ default:
+ FIXME("format %u not implemented\n", format);
+ return STATUS_UNSUPPORTED_COMPRESSION;
+ }
+}
+
+
/******************************************************************************
* RtlDecompressBuffer [NTDLL.@]
*/
-NTSTATUS WINAPI RtlDecompressBuffer(USHORT CompressionFormat, PUCHAR UncompressedBuffer,
- ULONG UncompressedBufferSize, PUCHAR CompressedBuffer,
- ULONG CompressedBufferSize, PULONG FinalUncompressedSize)
+NTSTATUS WINAPI RtlDecompressBuffer(USHORT format, PUCHAR uncompressed, ULONG uncompressed_size,
+ PUCHAR compressed, ULONG compressed_size, PULONG final_size)
{
- FIXME("0x%04x, %p, %u, %p, %u, %p :stub\n", CompressionFormat, UncompressedBuffer, UncompressedBufferSize,
- CompressedBuffer, CompressedBufferSize, FinalUncompressedSize);
+ TRACE("0x%04x, %p, %u, %p, %u, %p\n", format, uncompressed,
+ uncompressed_size, compressed, compressed_size, final_size);
- return STATUS_NOT_IMPLEMENTED;
+ return RtlDecompressFragment(format, uncompressed, uncompressed_size,
+ compressed, compressed_size, 0, final_size, NULL);
}
/***********************************************************************
diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c
index c6a7023..99fd413 100644
--- a/dlls/ntdll/tests/rtl.c
+++ b/dlls/ntdll/tests/rtl.c
@@ -1663,11 +1663,8 @@ static void test_RtlCompressBuffer(void)
memset(buf2, 0x11, sizeof(buf2));
status = pRtlDecompressBuffer(COMPRESSION_FORMAT_LZNT1, buf2, sizeof(buf2),
buf1, buf_size, &final_size);
- todo_wine
ok(status == STATUS_SUCCESS, "got wrong status 0x%08x\n", status);
- todo_wine
ok(final_size == sizeof(test_buffer), "got wrong final_size %u\n", final_size);
- todo_wine
ok(!memcmp(buf2, test_buffer, sizeof(test_buffer)), "got wrong decoded data\n");
ok(buf2[sizeof(test_buffer)] == 0x11, "too many bytes written\n");
diff --git a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
index 11d1c62..0bb7d04 100644
--- a/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
+++ b/dlls/ntoskrnl.exe/ntoskrnl.exe.spec
@@ -977,7 +977,7 @@
@ stub RtlCustomCPToUnicodeN
@ stdcall RtlDecompressBuffer(long ptr long ptr long ptr) ntdll.RtlDecompressBuffer
@ stub RtlDecompressChunks
-@ stub RtlDecompressFragment
+@ stdcall RtlDecompressFragment(long ptr long ptr long long ptr ptr) ntdll.RtlDecompressFragment
@ stub RtlDelete
@ stdcall RtlDeleteAce(ptr long) ntdll.RtlDeleteAce
@ stdcall RtlDeleteAtomFromAtomTable(ptr long) ntdll.RtlDeleteAtomFromAtomTable
--
2.4.5
More information about the wine-patches
mailing list