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>
This commit is contained in:
parent
ba5a6436f8
commit
51d5242a5f
|
@ -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)
|
static UINT STREAMS_delete_row(struct tagMSIVIEW *view, UINT row)
|
||||||
{
|
{
|
||||||
FIXME("(%p %d): stub!\n", view, row);
|
MSIDATABASE *db = ((MSISTREAMSVIEW *)view)->db;
|
||||||
return ERROR_SUCCESS;
|
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)
|
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);
|
r = streams_modify_update(view, rec);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MSIMODIFY_DELETE:
|
||||||
|
r = STREAMS_delete_row(view, row - 1);
|
||||||
|
break;
|
||||||
|
|
||||||
case MSIMODIFY_VALIDATE_NEW:
|
case MSIMODIFY_VALIDATE_NEW:
|
||||||
case MSIMODIFY_INSERT_TEMPORARY:
|
case MSIMODIFY_INSERT_TEMPORARY:
|
||||||
case MSIMODIFY_REFRESH:
|
case MSIMODIFY_REFRESH:
|
||||||
case MSIMODIFY_REPLACE:
|
case MSIMODIFY_REPLACE:
|
||||||
case MSIMODIFY_MERGE:
|
case MSIMODIFY_MERGE:
|
||||||
case MSIMODIFY_DELETE:
|
|
||||||
case MSIMODIFY_VALIDATE:
|
case MSIMODIFY_VALIDATE:
|
||||||
case MSIMODIFY_VALIDATE_FIELD:
|
case MSIMODIFY_VALIDATE_FIELD:
|
||||||
case MSIMODIFY_VALIDATE_DELETE:
|
case MSIMODIFY_VALIDATE_DELETE:
|
||||||
|
|
|
@ -1779,6 +1779,84 @@ static void test_streamtable(void)
|
||||||
MsiCloseHandle( view );
|
MsiCloseHandle( view );
|
||||||
MsiCloseHandle( hdb );
|
MsiCloseHandle( hdb );
|
||||||
DeleteFileA(msifile);
|
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)
|
static void test_binary(void)
|
||||||
|
|
Loading…
Reference in New Issue