summaryrefslogtreecommitdiffstats
path: root/libmsi
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2012-12-04 14:18:48 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2012-12-06 20:30:33 +0100
commitb431a82f778aac5117136a7ea528e0d37288401d (patch)
tree6abb52e2b13171316f672d0c311e11571d317258 /libmsi
parentb2015137b1af9aba2ffde189d3d9663e372a9c93 (diff)
downloadmsitools-b431a82f778aac5117136a7ea528e0d37288401d.tar.gz
msitools-b431a82f778aac5117136a7ea528e0d37288401d.tar.xz
msitools-b431a82f778aac5117136a7ea528e0d37288401d.zip
rewrite outfile completely on commit
Diffstat (limited to 'libmsi')
-rw-r--r--libmsi/database.c281
-rw-r--r--libmsi/msipriv.h9
-rw-r--r--libmsi/msiquery.c11
-rw-r--r--libmsi/string.c18
-rw-r--r--libmsi/table.c18
5 files changed, 200 insertions, 137 deletions
diff --git a/libmsi/database.c b/libmsi/database.c
index b3f1887..bacdcbe 100644
--- a/libmsi/database.c
+++ b/libmsi/database.c
@@ -585,144 +585,207 @@ static VOID _libmsi_database_destroy( LibmsiObject *arg )
{
LibmsiDatabase *db = (LibmsiDatabase *) arg;
- msi_free(db->path);
+ _libmsi_database_close( db, false );
free_cached_tables( db );
- free_streams( db );
- free_storages( db );
free_transforms( db );
- if (db->strings) msi_destroy_stringtable( db->strings );
- IStorage_Release( db->infile );
- IStorage_Release( db->outfile );
- if (db->deletefile)
- {
- unlink( db->deletefile );
- msi_free( db->deletefile );
- }
+ msi_free(db->path);
}
-static HRESULT db_initialize( LibmsiDatabase *db )
+LibmsiResult _libmsi_database_close(LibmsiDatabase *db, bool committed)
{
- static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
- HRESULT hr;
+ TRACE("%p %d\n", db, committed);
- /* create the _Tables stream */
- hr = write_stream_data( db, szTables, NULL, 0 );
- if (FAILED( hr ))
+ if ( db->strings )
{
- WARN("failed to create _Tables stream 0x%08x\n", hr);
- return hr;
+ msi_destroy_stringtable( db->strings);
+ db->strings = NULL;
}
- hr = msi_init_string_table( db );
- if (FAILED( hr ))
+ if ( db->infile )
{
- WARN("failed to initialize string table 0x%08x\n", hr);
- return hr;
+ IStorage_Release( db->infile );
+ db->infile = NULL;
}
- hr = IStorage_Commit( db->outfile, 0 );
- if (FAILED( hr ))
+ if ( db->outfile )
{
- WARN("failed to commit changes 0x%08x\n", hr);
- return hr;
+ IStorage_Release( db->outfile );
+ db->outfile = NULL;
}
- return S_OK;
+ free_streams( db );
+ free_storages( db );
+
+ if (db->outpath) {
+ if (!committed) {
+ unlink( db->outpath );
+ msi_free( db->outpath );
+ } else if (db->rename_outpath) {
+ unlink(db->path);
+ rename(db->outpath, db->path);
+ msi_free( db->outpath );
+ } else {
+ msi_free( db->path );
+ db->path = db->outpath;
+ }
+ }
+ db->outpath = NULL;
}
-LibmsiResult libmsi_database_open(const char *szDBPath, const char *szPersist, LibmsiDatabase **pdb)
+LibmsiResult _libmsi_database_open(LibmsiDatabase *db)
{
- IStorage *stg = NULL;
- HRESULT r;
- LibmsiDatabase *db = NULL;
- unsigned ret = LIBMSI_RESULT_FUNCTION_FAILED;
WCHAR *szwDBPath;
- const char *szMode;
+ HRESULT hr;
STATSTG stat;
- bool created = false, patch = false, need_init = false;
- char path[MAX_PATH];
+ IStorage *stg;
+ unsigned ret = LIBMSI_RESULT_OPEN_FAILED;
- TRACE("%s %p\n",debugstr_a(szDBPath),szPersist );
+ TRACE("%p %s\n", db, db->path);
- if( !pdb )
- return LIBMSI_RESULT_INVALID_PARAMETER;
+ szwDBPath = strdupAtoW(db->path);
+ hr = StgOpenStorage( szwDBPath, NULL,
+ STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+ msi_free(szwDBPath);
- if (IS_INTMSIDBOPEN(szPersist - LIBMSI_DB_OPEN_PATCHFILE))
+ if( FAILED( hr ) )
{
- TRACE("Database is a patch\n");
- szPersist -= LIBMSI_DB_OPEN_PATCHFILE;
- patch = true;
+ WARN("open failed hr = %08x for %s\n", hr, debugstr_a(db->path));
+ return LIBMSI_RESULT_OPEN_FAILED;
}
- szMode = szPersist;
- if( !IS_INTMSIDBOPEN(szPersist) )
+ hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
+ if( FAILED( hr ) )
{
- if (!CopyFileA( szDBPath, szPersist, false ))
- return LIBMSI_RESULT_OPEN_FAILED;
-
- szDBPath = szPersist;
- szPersist = LIBMSI_DB_OPEN_TRANSACT;
- created = true;
+ FIXME("Failed to stat storage\n");
+ goto end;
}
- szwDBPath = strdupAtoW(szDBPath);
- if( szPersist == LIBMSI_DB_OPEN_READONLY )
+ if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
+ !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
+ !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
{
- r = StgOpenStorage( szwDBPath, NULL,
- STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+ ERR("storage GUID is not a MSI database GUID %s\n",
+ debugstr_guid(&stat.clsid) );
+ goto end;
}
- else if( szPersist == LIBMSI_DB_OPEN_CREATE )
+
+ if ( db->patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
{
- r = StgCreateDocfile( szwDBPath,
- STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
+ ERR("storage GUID is not the MSI patch GUID %s\n",
+ debugstr_guid(&stat.clsid) );
+ goto end;
+ }
- if ( SUCCEEDED(r) )
- r = IStorage_SetClass( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
+ db->infile = stg;
+ IStorage_AddRef( db->infile );
+
+ cache_infile_structure( db );
- need_init = true;
- created = true;
+ db->strings = msi_load_string_table( db->infile, &db->bytes_per_strref );
+ if( !db->strings )
+ goto end;
+
+ ret = LIBMSI_RESULT_SUCCESS;
+end:
+ if (ret) {
+ if (db->infile)
+ IStorage_Release( db->infile );
+ db->infile = NULL;
}
- else if( szPersist == LIBMSI_DB_OPEN_TRANSACT )
+ IStorage_Release( stg );
+ return ret;
+}
+
+LibmsiResult _libmsi_database_start_transaction(LibmsiDatabase *db, const char *szPersist)
+{
+ unsigned ret = LIBMSI_RESULT_SUCCESS;
+ IStorage *stg = NULL;
+ WCHAR *szwPersist;
+ char *tmpfile = NULL;
+ char path[PATH_MAX];
+ HRESULT hr;
+
+ if( db->mode == LIBMSI_DB_OPEN_READONLY )
+ return LIBMSI_RESULT_SUCCESS;
+
+ if( szPersist == LIBMSI_DB_OPEN_TRANSACT )
{
- r = StgOpenStorage( szwDBPath, NULL,
- STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+ strcpy( path, db->path );
+ strcat( path, ".tmp" );
+ tmpfile = strdup(path);
+ szPersist = tmpfile;
}
- else
+ else if( IS_INTMSIDBOPEN(szPersist) )
{
ERR("unknown flag %p\n",szPersist);
return LIBMSI_RESULT_INVALID_PARAMETER;
}
- msi_free(szwDBPath);
- if( FAILED( r ) || !stg )
- {
- WARN("open failed r = %08x for %s\n", r, debugstr_a(szDBPath));
- return LIBMSI_RESULT_FUNCTION_FAILED;
- }
+ TRACE("%p %s\n", db, szPersist);
- r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
- if( FAILED( r ) )
+ szwPersist = strdupAtoW(szPersist);
+ hr = StgCreateDocfile( szwPersist,
+ STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg );
+
+ msi_free(szwPersist);
+
+ if ( SUCCEEDED(hr) )
+ hr = IStorage_SetClass( stg, db->patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
+
+ if( FAILED( hr ) )
{
- FIXME("Failed to stat storage\n");
+ WARN("open failed hr = %08x for %s\n", hr, debugstr_a(szPersist));
+ ret = LIBMSI_RESULT_FUNCTION_FAILED;
goto end;
}
- if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
- !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
- !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
+ db->outfile = stg;
+ IStorage_AddRef( db->outfile );
+
+ if (!strchr( szPersist, '\\' ))
{
- ERR("storage GUID is not a MSI database GUID %s\n",
- debugstr_guid(&stat.clsid) );
- goto end;
+ getcwd( path, MAX_PATH );
+ strcat( path, "\\" );
+ strcat( path, szPersist );
}
+ else
+ strcpy( path, szPersist );
+
+ db->outpath = strdup( path );
+ db->rename_outpath = (tmpfile != NULL);
+
+end:
+ if (ret) {
+ if (db->outfile)
+ IStorage_Release( db->outfile );
+ db->outfile = NULL;
+ }
+ if (stg)
+ IStorage_Release( stg );
+ msi_free(tmpfile);
+ return ret;
+}
+
+LibmsiResult libmsi_database_open(const char *szDBPath, const char *szPersist, LibmsiDatabase **pdb)
+{
+ LibmsiDatabase *db = NULL;
+ unsigned ret = LIBMSI_RESULT_SUCCESS;
+ const char *szMode;
+ bool patch = false;
+ char path[MAX_PATH];
+
+ TRACE("%s %p\n",debugstr_a(szDBPath),szPersist );
+
+ if( !pdb )
+ return LIBMSI_RESULT_INVALID_PARAMETER;
- if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
+ if (IS_INTMSIDBOPEN(szPersist - LIBMSI_DB_OPEN_PATCHFILE))
{
- ERR("storage GUID is not the MSI patch GUID %s\n",
- debugstr_guid(&stat.clsid) );
- ret = LIBMSI_RESULT_OPEN_FAILED;
- goto end;
+ TRACE("Database is a patch\n");
+ szPersist -= LIBMSI_DB_OPEN_PATCHFILE;
+ patch = true;
}
+ szMode = szPersist;
db = alloc_msiobject( sizeof (LibmsiDatabase), _libmsi_database_destroy );
if( !db )
{
@@ -739,39 +802,35 @@ LibmsiResult libmsi_database_open(const char *szDBPath, const char *szPersist, L
else
strcpy( path, szDBPath );
- db->path = strdup( path );
- db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
- db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID;
-
- if( TRACE_ON( msi ) )
- enum_stream_names( stg );
-
- db->infile = stg;
- IStorage_AddRef( db->infile );
- db->outfile = stg;
- IStorage_AddRef( db->outfile );
-
+ db->patch = patch;
db->mode = szMode;
- if (created)
- db->deletefile = strdup( szDBPath );
list_init( &db->tables );
list_init( &db->transforms );
list_init( &db->streams );
list_init( &db->storages );
- if (need_init) {
- r = db_initialize( db );
-
- if (FAILED( r ))
- {
- ret = LIBMSI_RESULT_FUNCTION_FAILED;
+ if( szPersist != LIBMSI_DB_OPEN_CREATE )
+ {
+ db->path = strdup( path );
+ ret = _libmsi_database_open(db);
+ if (ret)
goto end;
- }
+ } else {
+ szPersist = szDBPath;
+ db->strings = msi_init_string_table( &db->bytes_per_strref );
}
- cache_infile_structure( db );
- db->strings = msi_load_string_table( db->infile, &db->bytes_per_strref );
- if( !db->strings )
+ db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
+ db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID;
+
+ if( TRACE_ON( msi ) )
+ enum_stream_names( db->infile );
+
+ if( szPersist == LIBMSI_DB_OPEN_CREATE )
+ ret = _libmsi_database_start_transaction(db, szDBPath);
+ else
+ ret = _libmsi_database_start_transaction(db, szPersist);
+ if (ret)
goto end;
ret = LIBMSI_RESULT_SUCCESS;
@@ -782,8 +841,6 @@ LibmsiResult libmsi_database_open(const char *szDBPath, const char *szPersist, L
end:
if( db )
msiobj_release( &db->hdr );
- if( stg )
- IStorage_Release( stg );
return ret;
}
diff --git a/libmsi/msipriv.h b/libmsi/msipriv.h
index 1697391..63654f2 100644
--- a/libmsi/msipriv.h
+++ b/libmsi/msipriv.h
@@ -78,7 +78,9 @@ typedef struct LibmsiDatabase
string_table *strings;
unsigned bytes_per_strref;
char *path;
- char *deletefile;
+ char *outpath;
+ bool rename_outpath;
+ bool patch;
const char *mode;
unsigned media_transform_offset;
unsigned media_transform_disk_id;
@@ -307,7 +309,7 @@ extern int _libmsi_add_string( string_table *st, const WCHAR *data, int len, uin
extern unsigned _libmsi_id_from_stringW( const string_table *st, const WCHAR *buffer, unsigned *id );
extern VOID msi_destroy_stringtable( string_table *st );
extern const WCHAR *msi_string_lookup_id( const string_table *st, unsigned id );
-extern HRESULT msi_init_string_table( LibmsiDatabase *db );
+extern string_table *msi_init_string_table( unsigned *bytes_per_strref );
extern string_table *msi_load_string_table( IStorage *stg, unsigned *bytes_per_strref );
extern unsigned msi_save_string_table( const string_table *st, LibmsiDatabase *db, unsigned *bytes_per_strref );
extern unsigned msi_get_string_table_codepage( const string_table *st );
@@ -356,6 +358,9 @@ extern WCHAR *encode_streamname(bool bTable, const WCHAR *in);
extern void decode_streamname(const WCHAR *in, WCHAR *out);
/* database internals */
+extern LibmsiResult _libmsi_database_start_transaction(LibmsiDatabase *db, const char *szPersist);
+extern LibmsiResult _libmsi_database_open(LibmsiDatabase *db);
+extern LibmsiResult _libmsi_database_close(LibmsiDatabase *db, bool committed);
unsigned msi_create_stream( LibmsiDatabase *db, const WCHAR *stname, IStream *stm );
extern unsigned msi_get_raw_stream( LibmsiDatabase *, const WCHAR *, IStream **);
void msi_destroy_stream( LibmsiDatabase *, const WCHAR * );
diff --git a/libmsi/msiquery.c b/libmsi/msiquery.c
index 681cf3e..60b6cd4 100644
--- a/libmsi/msiquery.c
+++ b/libmsi/msiquery.c
@@ -718,17 +718,16 @@ LibmsiResult libmsi_database_commit( LibmsiDatabase *db )
{
WARN("failed to commit changes 0x%08x\n", hr);
r = LIBMSI_RESULT_FUNCTION_FAILED;
+ goto end;
}
+ _libmsi_database_close(db, true);
+ _libmsi_database_open(db);
+ _libmsi_database_start_transaction(db, LIBMSI_DB_OPEN_TRANSACT);
+
end:
msiobj_release( &db->hdr );
- if (r == LIBMSI_RESULT_SUCCESS)
- {
- msi_free( db->deletefile );
- db->deletefile = NULL;
- }
-
return r;
}
diff --git a/libmsi/string.c b/libmsi/string.c
index 8388536..a638197 100644
--- a/libmsi/string.c
+++ b/libmsi/string.c
@@ -457,22 +457,14 @@ static void string_totalsize( const string_table *st, unsigned *datasize, unsign
TRACE("data %u pool %u codepage %x\n", *datasize, *poolsize, st->codepage );
}
-HRESULT msi_init_string_table( LibmsiDatabase *db )
+string_table *msi_init_string_table( unsigned *bytes_per_strref )
{
- uint16_t zero[2] = { 0, 0 };
- unsigned ret;
-
- /* create the StringPool stream... add the zero string to it*/
- ret = write_stream_data(db, szStringPool, zero, sizeof zero);
- if (ret != LIBMSI_RESULT_SUCCESS)
- return E_FAIL;
+ string_table *st;
- /* create the StringData stream... make it zero length */
- ret = write_stream_data(db, szStringData, NULL, 0);
- if (ret != LIBMSI_RESULT_SUCCESS)
- return E_FAIL;
+ *bytes_per_strref = sizeof(uint16_t);
+ st = init_stringtable( 1, CP_ACP );
- return S_OK;
+ return st;
}
string_table *msi_load_string_table( IStorage *stg, unsigned *bytes_per_strref )
diff --git a/libmsi/table.c b/libmsi/table.c
index 15e4eca..064a5cc 100644
--- a/libmsi/table.c
+++ b/libmsi/table.c
@@ -295,6 +295,9 @@ unsigned read_stream_data( IStorage *stg, const WCHAR *stname,
TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+ if ( !stg )
+ return LIBMSI_RESULT_FUNCTION_FAILED;
+
r = IStorage_OpenStream(stg, encname, NULL,
STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
msi_free( encname );
@@ -942,6 +945,13 @@ static unsigned save_table( LibmsiDatabase *db, const LibmsiTable *t, unsigned b
if( t->persistent == LIBMSI_CONDITION_FALSE )
return LIBMSI_RESULT_SUCCESS;
+ /* All tables are copied to the new file when committing, so
+ * we can just skip them if they are empty. However, always
+ * save the Tables stream.
+ */
+ if ( t->row_count == 0 && strcmpW(t->name, szTables) )
+ return LIBMSI_RESULT_SUCCESS;
+
TRACE("Saving %s\n", debugstr_w( t->name ) );
row_size = msi_table_get_row_size( db, t->colinfo, t->col_count, bytes_per_strref );
@@ -2092,6 +2102,9 @@ unsigned _libmsi_database_commit_tables( LibmsiDatabase *db, unsigned bytes_per_
TRACE("%p\n",db);
+ /* Ensure the Tables stream is written. */
+ get_table( db, szTables, &t );
+
LIST_FOR_EACH_ENTRY_SAFE( table, table2, &db->tables, LibmsiTable, entry )
{
r = get_table( db, table->name, &t );
@@ -2101,10 +2114,7 @@ unsigned _libmsi_database_commit_tables( LibmsiDatabase *db, unsigned bytes_per_
debugstr_w(table->name), r);
return r;
}
- /* TODO: delete this if, replace with row_count check in save_table */
- if (table->colinfo) {
- r = save_table( db, table, bytes_per_strref );
- }
+ r = save_table( db, table, bytes_per_strref );
if( r != LIBMSI_RESULT_SUCCESS )
{
WARN("failed to save table %s (r=%08x)\n",