https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3ad01e9aadaa8dd5ca6c26...
commit 3ad01e9aadaa8dd5ca6c266915632e54d39e3a64 Author: winesync ros-dev@reactos.org AuthorDate: Sun Mar 13 00:43:00 2022 +0100 Commit: Mark Jansen mark.jansen@reactos.org CommitDate: Sun Mar 20 19:28:14 2022 +0100
[WINESYNC] msi: Add support for deleting streams from an MSI database.
msidb allows developers to remove "streams" (cabinet files) from a database with the "-k" mode flag. To support that feature we need MSIMODIFY_DELETE support in the underlying MSI implementation.
Signed-off-by: Erich E. Hoover erich.e.hoover@gmail.com Signed-off-by: Hans Leidekker hans@codeweavers.com Signed-off-by: Alexandre Julliard julliard@winehq.org
wine commit id 51d5242a5fa07a76077f2318fbbff968d760389d by Erich E. Hoover erich.e.hoover@gmail.com --- dll/win32/msi/streams.c | 17 ++++---- modules/rostests/winetests/msi/db.c | 78 +++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 9 deletions(-)
diff --git a/dll/win32/msi/streams.c b/dll/win32/msi/streams.c index dae1d971c4d..777eb98cffa 100644 --- a/dll/win32/msi/streams.c +++ b/dll/win32/msi/streams.c @@ -224,23 +224,22 @@ static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row) WCHAR *encname; HRESULT hr;
- TRACE("(%p %d)!\n", view, row); + TRACE("(%p %d)\n", view, row); + + if (!db->num_streams || row > num_rows) + return ERROR_FUNCTION_FAILED;
name = msi_string_lookup( db->strings, db->streams[row].str_index, NULL ); if (!(encname = encode_streamname( FALSE, name ))) return ERROR_OUTOFMEMORY; - hr = IStorage_DestroyElement( db->storage, encname ); - msi_free( encname ); - if (FAILED( hr )) - return ERROR_FUNCTION_FAILED; - hr = IStream_Release( db->streams[row].stream ); - if (FAILED( hr )) - return ERROR_FUNCTION_FAILED; + IStream_Release( db->streams[row].stream );
for (i = row; i < num_rows; i++) db->streams[i] = db->streams[i + 1]; db->num_streams = num_rows;
- return ERROR_SUCCESS; + hr = IStorage_DestroyElement( db->storage, encname ); + msi_free( encname ); + return FAILED( hr ) ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS; }
static UINT STREAMS_execute(struct tagMSIVIEW *view, MSIRECORD *record) diff --git a/modules/rostests/winetests/msi/db.c b/modules/rostests/winetests/msi/db.c index 0382e5454f6..9fbf43eaee9 100644 --- a/modules/rostests/winetests/msi/db.c +++ b/modules/rostests/winetests/msi/db.c @@ -1779,6 +1779,84 @@ static void test_streamtable(void) MsiCloseHandle( view ); MsiCloseHandle( hdb ); DeleteFileA(msifile); + + /* insert a file into the _Streams table */ + r = MsiOpenDatabaseW(msifileW, MSIDBOPEN_CREATEDIRECT, &hdb); + ok(r == ERROR_SUCCESS, "Failed to create database\n"); + ok( hdb, "failed to create db\n"); + create_file( "test.txt" ); + rec = MsiCreateRecord( 2 ); + MsiRecordSetStringA( rec, 1, "data" ); + r = MsiRecordSetStreamA( rec, 2, "test.txt" ); + ok( r == ERROR_SUCCESS, "Failed to add stream data to the record: %d\n", r); + DeleteFileA("test.txt"); + r = MsiDatabaseOpenViewA( hdb, + "INSERT INTO `_Streams` ( `Name`, `Data` ) VALUES ( ?, ? )", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + r = MsiViewExecute( view, rec ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + MsiCloseHandle( rec ); + MsiViewClose( view ); + MsiCloseHandle( view ); + r = MsiDatabaseCommit( hdb ); + ok( r == ERROR_SUCCESS , "Failed to commit database\n" ); + + /* open a handle to the "data" stream */ + r = MsiDatabaseOpenViewA( hdb, + "SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = 'data'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiViewClose( view ); + MsiCloseHandle( view ); + /* read the stream while it still exists (normal case) */ + size = MAX_PATH; + r = MsiRecordGetStringA( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok( !lstrcmpA(file, "data"), "Expected 'data', got %s\n", file); + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + ok( !lstrcmpA(buf, "test.txt\n"), "Expected 'test.txt\n', got '%s' (%d)\n", buf, size); + MsiCloseHandle( rec ); + + /* open a handle to the "data" stream (and keep it open during removal) */ + r = MsiDatabaseOpenViewA( hdb, + "SELECT `Name`, `Data` FROM `_Streams` WHERE `Name` = 'data'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + r = MsiViewFetch( view, &rec ); + ok( r == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", r); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* remove the stream */ + r = MsiDatabaseOpenViewA( hdb, + "DELETE FROM `_Streams` WHERE `Name` = 'data'", &view ); + ok( r == ERROR_SUCCESS, "Failed to open database view: %d\n", r); + r = MsiViewExecute( view, 0 ); + ok( r == ERROR_SUCCESS, "Failed to execute view: %d\n", r); + MsiViewClose( view ); + MsiCloseHandle( view ); + + /* attempt to read the stream that no longer exists (abnormal case) */ + size = MAX_PATH; + r = MsiRecordGetStringA( rec, 1, file, &size ); + ok( r == ERROR_SUCCESS, "Failed to get string: %d\n", r); + ok( !lstrcmpA(file, "data"), "Expected 'data', got %s\n", file); + size = MAX_PATH; + memset(buf, 0, MAX_PATH); + r = MsiRecordReadStream( rec, 2, buf, &size ); + ok( r == ERROR_SUCCESS, "Failed to get stream: %d\n", r); + todo_wine ok( size == 0, "Expected empty buffer, got %d bytes\n", size); + MsiCloseHandle( rec ); + + MsiCloseHandle( hdb ); + DeleteFileA(msifile); }
static void test_binary(void)