diff options
-rw-r--r-- | libmsi/database.c | 281 | ||||
-rw-r--r-- | libmsi/msipriv.h | 9 | ||||
-rw-r--r-- | libmsi/msiquery.c | 11 | ||||
-rw-r--r-- | libmsi/string.c | 18 | ||||
-rw-r--r-- | libmsi/table.c | 18 |
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", |