From: Theodoros Chatzigiannakis <tchatzigiannakis@gmail.com> --- dlls/msi/tests/db.c | 273 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) diff --git a/dlls/msi/tests/db.c b/dlls/msi/tests/db.c index 56fff94c5df..8f1413fe75d 100644 --- a/dlls/msi/tests/db.c +++ b/dlls/msi/tests/db.c @@ -9365,6 +9365,277 @@ static void test_viewfetch_wraparound(void) DeleteFileA(msifile); } +static void write_raw_stream(IStorage *stg, const WCHAR *name, const void *data, DWORD size) +{ + IStream *stm; + DWORD count; + HRESULT hr; + + hr = IStorage_CreateStream(stg, name, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + hr = IStream_Write(stm, data, size, &count); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + IStream_Release(stm); +} + +static void test_table_stream_padding(void) +{ + static const WCHAR moo_encoded[] = {0x4840, 0x3e16, 0x4818, 0}; /* encoded "MOO" */ + MSIHANDLE hdb, hview, hrec; + IStorage *stg; + IStream *stm; + WCHAR nameW[MAX_PATH]; + HRESULT hr; + BYTE moo_data[64]; + BYTE padded[64]; + DWORD moo_size; + const char *query; + UINT r; + + DeleteFileA(msifile); + + + r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `MOO` (`A` LONG PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`) VALUES (10)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`) VALUES (20)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(hdb); + + + MultiByteToWideChar(CP_ACP, 0, msifile, -1, nameW, MAX_PATH); + hr = StgOpenStorage(nameW, NULL, STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + NULL, 0, &stg); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + + hr = IStorage_OpenStream(stg, moo_encoded, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + hr = IStream_Read(stm, moo_data, sizeof(moo_data), &moo_size); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + ok(moo_size == 8, "Expected 8, got %lu\n", moo_size); + IStream_Release(stm); + + + memcpy(padded, moo_data, moo_size); + padded[moo_size] = 0; + padded[moo_size + 1] = 0; + + + hr = IStorage_DestroyElement(stg, moo_encoded); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + + hr = IStorage_CreateStream(stg, moo_encoded, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + hr = IStream_Write(stm, padded, moo_size + 2, &moo_size); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + IStream_Release(stm); + + IStorage_Release(stg); + + + r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT `A` FROM `MOO`"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + check_record(hrec, 1, "10"); + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + check_record(hrec, 1, "20"); + MsiCloseHandle(hrec); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_NO_MORE_ITEMS, "Expected ERROR_NO_MORE_ITEMS, got %d\n", r); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + +/* Convert column-major table data from 2-byte to 3-byte string references. Returns the new size. */ +static DWORD convert_to_3byte_strref(const BYTE *old_data, DWORD old_size, + BYTE *new_data, const int *col_types, UINT col_count) +{ + UINT old_row_size = col_count * 2; + UINT row_count = old_size / old_row_size; + DWORD new_size = 0; + UINT old_ofs = 0; + UINT j, i; + + for (j = 0; j < col_count; j++) + { + for (i = 0; i < row_count; i++) + { + new_data[new_size++] = old_data[old_ofs++]; + new_data[new_size++] = old_data[old_ofs++]; + if (!col_types[j]) + new_data[new_size++] = 0; + } + } + return new_size; +} + +static void test_table_mismatched_strref(void) +{ + static const CLSID CLSID_MsiDatabase = + { 0xc1084, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} }; + static const WCHAR stringpool_enc[] = {0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0}; + static const WCHAR tables_enc[] = {0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0}; + static const WCHAR columns_enc[] = {0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0}; + static const int tables_col_types[] = {0}; + static const int columns_col_types[] = {0, 1, 0, 1}; + + MSIHANDLE hdb, hview, hrec; + IStorage *src, *dst; + IEnumSTATSTG *enumstg; + STATSTG stat; + IStream *stm; + WCHAR nameW[MAX_PATH], tmpW[MAX_PATH]; + HRESULT hr; + BYTE buf[4096], converted[4096]; + DWORD size, new_size; + const char *query; + UINT r; + + DeleteFileA(msifile); + + /* Create a valid MSI database with 2-byte string refs */ + r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_CREATE, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "CREATE TABLE `MOO` (`A` SHORT NOT NULL, `B` CHAR(72) PRIMARY KEY `A`)"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (1, 'one')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (2, 'two')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (3, 'three')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (4, 'four')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "INSERT INTO `MOO` (`A`, `B`) VALUES (5, 'five')"; + r = run_query(hdb, 0, query); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiDatabaseCommit(hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(hdb); + + /* Rebuild the MSI with modifications: + * - Set 0x8000 flag in _StringPool (declares 3-byte string refs) + * - Convert _Tables and _Columns to 3-byte string refs + * - Leave MOO stream unchanged (2-byte string refs) to simulate mismatch */ + MultiByteToWideChar(CP_ACP, 0, msifile, -1, nameW, MAX_PATH); + hr = StgOpenStorage(nameW, NULL, STGM_READ | STGM_SHARE_DENY_WRITE, NULL, 0, &src); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + + MultiByteToWideChar(CP_ACP, 0, "msitest2.msi", -1, tmpW, MAX_PATH); + hr = StgCreateDocfile(tmpW, STGM_CREATE | STGM_READWRITE | STGM_DIRECT | STGM_SHARE_EXCLUSIVE, + 0, &dst); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + + hr = IStorage_SetClass(dst, &CLSID_MsiDatabase); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + + hr = IStorage_EnumElements(src, 0, NULL, 0, &enumstg); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + + while (IEnumSTATSTG_Next(enumstg, 1, &stat, NULL) == S_OK) + { + if (stat.type == STGTY_STREAM) + { + hr = IStorage_OpenStream(src, stat.pwcsName, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + hr = IStream_Read(stm, buf, sizeof(buf), &size); + ok(hr == S_OK, "Expected S_OK, got %#lx\n", hr); + IStream_Release(stm); + + if (!lstrcmpW(stat.pwcsName, stringpool_enc)) + { + buf[3] |= 0x80; + write_raw_stream(dst, stat.pwcsName, buf, size); + } + else if (!lstrcmpW(stat.pwcsName, tables_enc)) + { + new_size = convert_to_3byte_strref(buf, size, converted, tables_col_types, 1); + write_raw_stream(dst, stat.pwcsName, converted, new_size); + } + else if (!lstrcmpW(stat.pwcsName, columns_enc)) + { + new_size = convert_to_3byte_strref(buf, size, converted, columns_col_types, 4); + write_raw_stream(dst, stat.pwcsName, converted, new_size); + } + else + { + write_raw_stream(dst, stat.pwcsName, buf, size); + } + } + CoTaskMemFree(stat.pwcsName); + } + + IEnumSTATSTG_Release(enumstg); + IStorage_Release(src); + IStorage_Release(dst); + + r = DeleteFileA(msifile); + ok(r, "DeleteFileA failed: %lu\n", GetLastError()); + r = MoveFileA("msitest2.msi", msifile); + ok(r, "MoveFileA failed: %lu\n", GetLastError()); + + /* Open the modified MSI and verify the table is queryable despite mismatched strref */ + r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_READONLY, &hdb); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + query = "SELECT `A`, `B` FROM `MOO`"; + r = MsiDatabaseOpenViewA(hdb, query, &hview); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewExecute(hview, 0); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + + r = MsiViewFetch(hview, &hrec); + ok(r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiCloseHandle(hrec); + + MsiViewClose(hview); + MsiCloseHandle(hview); + MsiCloseHandle(hdb); + DeleteFileA(msifile); +} + START_TEST(db) { test_msidatabase(); @@ -9425,4 +9696,6 @@ START_TEST(db) test_viewmodify_insert(); test_view_get_error(); test_viewfetch_wraparound(); + test_table_stream_padding(); + test_table_mismatched_strref(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10114