diff --git a/dlls/msi/action.c b/dlls/msi/action.c index 8267aa9505c..7bed12fb921 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -806,18 +806,137 @@ done: return ERROR_SUCCESS; } +static const WCHAR patch_media_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ', + 'W','H','E','R','E',' ','`','S','o','u','r','c','e','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ', + 'A','N','D',' ','`','C','a','b','i','n','e','t','`',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0}; + +struct patch_media +{ + struct list entry; + UINT disk_id; + UINT last_sequence; + WCHAR *prompt; + WCHAR *cabinet; + WCHAR *volume; + WCHAR *source; +}; + +static UINT msi_add_patch_media( MSIPACKAGE *package, IStorage *patch ) +{ + static const WCHAR delete_query[] = { + 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ', + 'W','H','E','R','E',' ','`','D','i','s','k','I','d','`','=','?',0}; + static const WCHAR insert_query[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','M','e','d','i','a','`',' ', + '(','`','D','i','s','k','I','d','`',',','`','L','a','s','t','S','e','q','u','e','n','c','e','`',',', + '`','D','i','s','k','P','r','o','m','p','t','`',',','`','C','a','b','i','n','e','t','`',',', + '`','V','o','l','u','m','e','L','a','b','e','l','`',',','`','S','o','u','r','c','e','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0}; + MSIQUERY *view; + MSIRECORD *rec = NULL; + UINT r, disk_id; + struct list media_list; + struct patch_media *media, *next; + + r = MSI_DatabaseOpenViewW( package->db, patch_media_query, &view ); + if (r != ERROR_SUCCESS) return r; + + r = MSI_ViewExecute( view, 0 ); + if (r != ERROR_SUCCESS) + { + msiobj_release( &view->hdr ); + TRACE("query failed %u\n", r); + return r; + } + + list_init( &media_list ); + while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + disk_id = MSI_RecordGetInteger( rec, 1 ); + TRACE("disk_id %u\n", disk_id); + if (disk_id >= MSI_INITIAL_MEDIA_TRANSFORM_DISKID) + { + msiobj_release( &rec->hdr ); + continue; + } + if (!(media = msi_alloc( sizeof( *media )))) goto done; + media->disk_id = disk_id; + media->last_sequence = MSI_RecordGetInteger( rec, 2 ); + media->prompt = msi_dup_record_field( rec, 3 ); + media->cabinet = msi_dup_record_field( rec, 4 ); + media->volume = msi_dup_record_field( rec, 5 ); + media->source = msi_dup_record_field( rec, 6 ); + + list_add_tail( &media_list, &media->entry ); + msiobj_release( &rec->hdr ); + } + LIST_FOR_EACH_ENTRY( media, &media_list, struct patch_media, entry ) + { + MSIQUERY *delete_view, *insert_view; + + r = MSI_DatabaseOpenViewW( package->db, delete_query, &delete_view ); + if (r != ERROR_SUCCESS) goto done; + + rec = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger( rec, 1, media->disk_id ); + + r = MSI_ViewExecute( delete_view, rec ); + msiobj_release( &delete_view->hdr ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) goto done; + + r = MSI_DatabaseOpenViewW( package->db, insert_query, &insert_view ); + if (r != ERROR_SUCCESS) goto done; + + disk_id = package->db->media_transform_disk_id; + TRACE("disk id %u\n", disk_id); + TRACE("last sequence %u\n", media->last_sequence); + TRACE("prompt %s\n", debugstr_w(media->prompt)); + TRACE("cabinet %s\n", debugstr_w(media->cabinet)); + TRACE("volume %s\n", debugstr_w(media->volume)); + TRACE("source %s\n", debugstr_w(media->source)); + + rec = MSI_CreateRecord( 6 ); + MSI_RecordSetInteger( rec, 1, disk_id ); + MSI_RecordSetInteger( rec, 2, media->last_sequence ); + MSI_RecordSetStringW( rec, 3, media->prompt ); + MSI_RecordSetStringW( rec, 4, media->cabinet ); + MSI_RecordSetStringW( rec, 5, media->volume ); + MSI_RecordSetStringW( rec, 6, media->source ); + + r = MSI_ViewExecute( insert_view, rec ); + msiobj_release( &insert_view->hdr ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) goto done; + + r = msi_add_cabinet_stream( package, disk_id, patch, media->cabinet ); + if (r != ERROR_SUCCESS) WARN("failed to add cabinet stream %u\n", r); + package->db->media_transform_disk_id++; + } + +done: + msiobj_release( &view->hdr ); + LIST_FOR_EACH_ENTRY_SAFE( media, next, &media_list, struct patch_media, entry ) + { + list_remove( &media->entry ); + msi_free( media->prompt ); + msi_free( media->cabinet ); + msi_free( media->volume ); + msi_free( media->source ); + msi_free( media ); + } + return r; +} + static UINT msi_set_patch_offsets(MSIDATABASE *db) { - static const WCHAR query_media[] = { - 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','M','e','d','i','a',' ', - 'W','H','E','R','E',' ','S','o','u','r','c','e',' ','I','S',' ','N','O','T',' ','N','U','L','L', - ' ','A','N','D',' ','C','a','b','i','n','e','t',' ','I','S',' ','N','O','T',' ','N','U','L','L',' ', - 'O','R','D','E','R',' ','B','Y',' ','D','i','s','k','I','d',0}; - MSIQUERY *view = NULL; + MSIQUERY *view; MSIRECORD *rec = NULL; UINT r; - r = MSI_DatabaseOpenViewW( db, query_media, &view ); + r = MSI_DatabaseOpenViewW( db, patch_media_query, &view ); if (r != ERROR_SUCCESS) return r; @@ -869,7 +988,6 @@ static UINT msi_set_patch_offsets(MSIDATABASE *db) done: msiobj_release( &view->hdr ); - return r; } @@ -884,7 +1002,10 @@ UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINF { r = msi_apply_substorage_transform( package, patch_db, substorage[i] ); if (r == ERROR_SUCCESS) + { + msi_add_patch_media( package, patch_db->storage ); msi_set_patch_offsets( package->db ); + } } msi_free( substorage ); @@ -893,12 +1014,6 @@ UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINF msi_set_media_source_prop( package ); - /* - * There might be a CAB file in the patch package, - * so append it to the list of storages to search for streams. - */ - append_storage_to_db( package->db, patch_db->storage ); - patch->state = MSIPATCHSTATE_APPLIED; list_add_tail( &package->patches, &patch->entry ); return ERROR_SUCCESS; @@ -1848,6 +1963,34 @@ static UINT load_all_files(MSIPACKAGE *package) return ERROR_SUCCESS; } +static UINT load_media( MSIRECORD *row, LPVOID param ) +{ + MSIPACKAGE *package = param; + UINT disk_id = MSI_RecordGetInteger( row, 1 ); + const WCHAR *cabinet = MSI_RecordGetString( row, 4 ); + + /* FIXME: load external cabinets and directory sources too */ + if (!cabinet || cabinet[0] != '#') return ERROR_SUCCESS; + msi_add_cabinet_stream( package, disk_id, package->db->storage, cabinet ); + return ERROR_SUCCESS; +} + +static UINT load_all_media( MSIPACKAGE *package ) +{ + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','D','i','s','k','I','d','`',0}; + MSIQUERY *view; + UINT r; + + r = MSI_DatabaseOpenViewW( package->db, query, &view ); + if (r != ERROR_SUCCESS) return ERROR_SUCCESS; + + MSI_IterateRecords( view, NULL, load_media, package ); + msiobj_release( &view->hdr ); + return ERROR_SUCCESS; +} + static UINT load_patch(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; @@ -2018,6 +2161,7 @@ static UINT ACTION_CostInitialize(MSIPACKAGE *package) load_all_features( package ); load_all_files( package ); load_all_patches( package ); + load_all_media( package ); return ERROR_SUCCESS; } diff --git a/dlls/msi/database.c b/dlls/msi/database.c index fe970695dfc..2cb9ec48527 100644 --- a/dlls/msi/database.c +++ b/dlls/msi/database.c @@ -416,8 +416,8 @@ UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) lstrcpyW( path, save_path ); db->path = strdupW( 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 ); diff --git a/dlls/msi/files.c b/dlls/msi/files.c index a4fe8ff416e..53ed2ab63da 100644 --- a/dlls/msi/files.c +++ b/dlls/msi/files.c @@ -364,7 +364,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package) } msi_free(source); } - else if (file->state != msifs_installed) + else if (file->state != msifs_installed && !(file->Attributes & msidbFileAttributesPatchAdded)) { ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->TargetPath)); rc = ERROR_INSTALL_FAILURE; diff --git a/dlls/msi/media.c b/dlls/msi/media.c index c699ecc07fe..c261d7c7dc9 100644 --- a/dlls/msi/media.c +++ b/dlls/msi/media.c @@ -198,27 +198,40 @@ static LONG CDECL cabinet_seek(INT_PTR hf, LONG dist, int seektype) return SetFilePointer(handle, dist, NULL, seektype); } -struct cab_stream +struct package_disk { - MSIDATABASE *db; - WCHAR *name; + MSIPACKAGE *package; + UINT id; }; -static struct cab_stream cab_stream; +static struct package_disk package_disk; static INT_PTR CDECL cabinet_open_stream( char *pszFile, int oflag, int pmode ) { - UINT r; - IStream *stm; + MSICABINETSTREAM *cab; + IStream *stream; + WCHAR *encoded; + HRESULT hr; - r = db_get_raw_stream( cab_stream.db, cab_stream.name, &stm ); - if (r != ERROR_SUCCESS) + cab = msi_get_cabinet_stream( package_disk.package, package_disk.id ); + if (!cab) { - WARN("Failed to get cabinet stream %u\n", r); + WARN("failed to get cabinet stream\n"); return 0; } - - return (INT_PTR)stm; + if (!cab->stream[0] || !(encoded = encode_streamname( FALSE, cab->stream + 1 ))) + { + WARN("failed to encode stream name\n"); + return 0; + } + hr = IStorage_OpenStream( cab->storage, encoded, NULL, STGM_READ|STGM_SHARE_EXCLUSIVE, 0, &stream ); + msi_free( encoded ); + if (FAILED(hr)) + { + WARN("failed to open stream 0x%08x\n", hr); + return 0; + } + return (INT_PTR)stream; } static UINT CDECL cabinet_read_stream( INT_PTR hf, void *pv, UINT cb ) @@ -378,14 +391,9 @@ static INT_PTR cabinet_next_cabinet_stream( FDINOTIFICATIONTYPE fdint, ERR("Failed to get next cabinet information: %u\n", rc); return -1; } + package_disk.id = mi->disk_id; - msi_free( cab_stream.name ); - cab_stream.name = encode_streamname( FALSE, mi->cabinet + 1 ); - if (!cab_stream.name) - return -1; - - TRACE("next cabinet is %s\n", debugstr_w(mi->cabinet)); - + TRACE("next cabinet is %s disk id %u\n", debugstr_w(mi->cabinet), mi->disk_id); return 0; } @@ -553,7 +561,7 @@ static BOOL extract_cabinet( MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data ERF erf; BOOL ret = FALSE; - TRACE("Extracting %s\n", debugstr_w(mi->cabinet)); + TRACE("extracting %s disk id %u\n", debugstr_w(mi->cabinet), mi->disk_id); hfdi = FDICreate( cabinet_alloc, cabinet_free, cabinet_open, cabinet_read, cabinet_write, cabinet_close, cabinet_seek, 0, &erf ); @@ -593,7 +601,7 @@ static BOOL extract_cabinet_stream( MSIPACKAGE *package, MSIMEDIAINFO *mi, LPVOI ERF erf; BOOL ret = FALSE; - TRACE("Extracting %s\n", debugstr_w(mi->cabinet)); + TRACE("extracting %s disk id %u\n", debugstr_w(mi->cabinet), mi->disk_id); hfdi = FDICreate( cabinet_alloc, cabinet_free, cabinet_open_stream, cabinet_read_stream, cabinet_write, cabinet_close_stream, cabinet_seek_stream, 0, &erf ); @@ -603,22 +611,14 @@ static BOOL extract_cabinet_stream( MSIPACKAGE *package, MSIMEDIAINFO *mi, LPVOI return FALSE; } - cab_stream.db = package->db; - cab_stream.name = encode_streamname( FALSE, mi->cabinet + 1 ); - if (!cab_stream.name) - goto done; + package_disk.package = package; + package_disk.id = mi->disk_id; ret = FDICopy( hfdi, filename, NULL, 0, cabinet_notify_stream, NULL, data ); - if (!ret) - ERR("FDICopy failed\n"); + if (!ret) ERR("FDICopy failed\n"); -done: FDIDestroy( hfdi ); - msi_free( cab_stream.name ); - - if (ret) - mi->is_extracted = TRUE; - + if (ret) mi->is_extracted = TRUE; return ret; } diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index fead8ea4796..c132477bd00 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -76,6 +76,7 @@ struct tagMSIOBJECTHDR }; #define MSI_INITIAL_MEDIA_TRANSFORM_OFFSET 10000 +#define MSI_INITIAL_MEDIA_TRANSFORM_DISKID 10000 typedef struct tagMSIDATABASE { @@ -88,6 +89,7 @@ typedef struct tagMSIDATABASE LPWSTR localfile; LPCWSTR mode; UINT media_transform_offset; + UINT media_transform_disk_id; struct list tables; struct list transforms; struct list streams; diff --git a/dlls/msi/tests/patch.c b/dlls/msi/tests/patch.c index 23aac2dd8ff..4c6db56c86f 100644 --- a/dlls/msi/tests/patch.c +++ b/dlls/msi/tests/patch.c @@ -1095,6 +1095,9 @@ static void test_system_tables( void ) r = find_entry( hdb, "_Tables", "Media" ); ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r ); + r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `VolumeLabel`=\'DISK1\'"); + ok( r == 1, "Got %u\n", r ); + r = find_entry( hdb, "_Tables", "_Property" ); ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r ); @@ -1170,7 +1173,7 @@ static void test_system_tables( void ) ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r ); cr = get_string( hdb, 6, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL"); - todo_wine ok( !strcmp(cr, patchsource), "Expected %s, got %s\n", patchsource, cr ); + todo_wine ok( !strcmp(cr, patchsource), "Expected \"%s\", got \"%s\"\n", patchsource, cr ); r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL"); todo_wine ok( r == 100, "Got %u\n", r ); @@ -1178,6 +1181,12 @@ static void test_system_tables( void ) r = get_integer( hdb, 2, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL"); todo_wine ok( r == 10000, "Got %u\n", r ); + r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `VolumeLabel`=\'DISK1\'"); + ok( r == 1, "Got %u\n", r ); + + cr = get_string( hdb, 4, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL"); + ok( !strcmp(cr, "#CAB_msitest"), "Expected \"#CAB_msitest\", got \"%s\"\n", cr ); + r = get_integer( hdb, 8, "SELECT * FROM `File` WHERE `File` = 'patch.txt'"); ok( r == 10000, "Got %u\n", r );