Erich E. Hoover : msi: Add support for deleting streams from an MSI database.

Alexandre Julliard julliard at winehq.org
Fri Mar 15 15:20:00 CDT 2019


Module: wine
Branch: master
Commit: 51d5242a5fa07a76077f2318fbbff968d760389d
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=51d5242a5fa07a76077f2318fbbff968d760389d

Author: Erich E. Hoover <erich.e.hoover at gmail.com>
Date:   Fri Mar 15 10:03:56 2019 +0100

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 at gmail.com>
Signed-off-by: Hans Leidekker <hans at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/msi/streams.c  | 29 +++++++++++++++++---
 dlls/msi/tests/db.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+), 3 deletions(-)

diff --git a/dlls/msi/streams.c b/dlls/msi/streams.c
index cfce816..777eb98 100644
--- a/dlls/msi/streams.c
+++ b/dlls/msi/streams.c
@@ -218,8 +218,28 @@ static UINT STREAMS_insert_row(struct tagMSIVIEW *view, MSIRECORD *rec, UINT row
 
 static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
 {
-    FIXME("(%p %d): stub!\n", view, row);
-    return ERROR_SUCCESS;
+    MSIDATABASE *db = ((MSISTREAMSVIEW *)view)->db;
+    UINT i, num_rows = db->num_streams - 1;
+    const WCHAR *name;
+    WCHAR *encname;
+    HRESULT hr;
+
+    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;
+    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;
+
+    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)
@@ -317,12 +337,15 @@ static UINT STREAMS_modify(struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRE
         r = streams_modify_update(view, rec);
         break;
 
+    case MSIMODIFY_DELETE:
+        r = STREAMS_delete_row(view, row - 1);
+        break;
+
     case MSIMODIFY_VALIDATE_NEW:
     case MSIMODIFY_INSERT_TEMPORARY:
     case MSIMODIFY_REFRESH:
     case MSIMODIFY_REPLACE:
     case MSIMODIFY_MERGE:
-    case MSIMODIFY_DELETE:
     case MSIMODIFY_VALIDATE:
     case MSIMODIFY_VALIDATE_FIELD:
     case MSIMODIFY_VALIDATE_DELETE:
diff --git a/dlls/msi/tests/db.c b/dlls/msi/tests/db.c
index 0382e54..9fbf43e 100644
--- a/dlls/msi/tests/db.c
+++ b/dlls/msi/tests/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)




More information about the wine-cvs mailing list