https://git.reactos.org/?p=reactos.git;a=commitdiff;h=3ad01e9aadaa8dd5ca6c2…
commit 3ad01e9aadaa8dd5ca6c266915632e54d39e3a64
Author: winesync <ros-dev(a)reactos.org>
AuthorDate: Sun Mar 13 00:43:00 2022 +0100
Commit: Mark Jansen <mark.jansen(a)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(a)gmail.com>
Signed-off-by: Hans Leidekker <hans(a)codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard(a)winehq.org>
wine commit id 51d5242a5fa07a76077f2318fbbff968d760389d by Erich E. Hoover
<erich.e.hoover(a)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)