From: Hans Leidekker Subject: [3/3] msi: Add offsets to file patch sequence numbers. Message-Id: <1425475842.22916.5.camel@codeweavers.com> Date: Wed, 04 Mar 2015 14:30:42 +0100 --- dlls/msi/action.c | 14 +--- dlls/msi/msipriv.h | 2 + dlls/msi/patch.c | 220 ++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 166 insertions(+), 70 deletions(-) diff --git a/dlls/msi/action.c b/dlls/msi/action.c index ed09be7..472effc 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -1340,17 +1340,15 @@ static UINT load_patch(MSIRECORD *row, LPVOID param) { MSIPACKAGE *package = param; MSIFILEPATCH *patch; - LPWSTR file_key; + const WCHAR *file_key; patch = msi_alloc_zero( sizeof (MSIFILEPATCH) ); if (!patch) return ERROR_NOT_ENOUGH_MEMORY; - file_key = msi_dup_record_field( row, 1 ); + file_key = MSI_RecordGetString( row, 1 ); patch->File = msi_get_loaded_file( package, file_key ); - msi_free(file_key); - - if( !patch->File ) + if (!patch->File) { ERR("Failed to find target for patch in File table\n"); msi_free(patch); @@ -1358,10 +1356,6 @@ static UINT load_patch(MSIRECORD *row, LPVOID param) } patch->Sequence = MSI_RecordGetInteger( row, 2 ); - - /* FIXME: The database should be properly transformed */ - patch->Sequence += MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; - patch->PatchSize = MSI_RecordGetInteger( row, 3 ); patch->Attributes = MSI_RecordGetInteger( row, 4 ); patch->IsApplied = FALSE; @@ -1371,7 +1365,7 @@ static UINT load_patch(MSIRECORD *row, LPVOID param) * _StreamRef - External key into MsiPatchHeaders (instead of the header field) */ - TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File)); + TRACE("Patch loaded (file %s sequence %u)\n", debugstr_w(patch->File->File), patch->Sequence); list_add_tail( &package->filepatches, &patch->entry ); diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index db2a2af..a69ddb0 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -192,6 +192,8 @@ typedef struct tagMSIPATCHINFO LPWSTR localfile; MSIPATCHSTATE state; BOOL delete_on_close; + UINT disk_id; + UINT last_sequence; } MSIPATCHINFO; typedef struct tagMSIBINARY diff --git a/dlls/msi/patch.c b/dlls/msi/patch.c index a738de1..56bc5b4 100644 --- a/dlls/msi/patch.c +++ b/dlls/msi/patch.c @@ -411,6 +411,7 @@ struct patch_offset struct patch_offset_list { struct list files; + struct list patches; UINT count, min, max; UINT offset_to_apply; }; @@ -419,6 +420,7 @@ static struct patch_offset_list *patch_offset_list_create( void ) { struct patch_offset_list *pos = msi_alloc( sizeof(struct patch_offset_list) ); list_init( &pos->files ); + list_init( &pos->patches ); pos->count = pos->max = 0; pos->min = 999999; return pos; @@ -433,10 +435,15 @@ static void patch_offset_list_free( struct patch_offset_list *pos ) msi_free( po->name ); msi_free( po ); } + LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->patches, struct patch_offset, entry ) + { + msi_free( po->name ); + msi_free( po ); + } msi_free( pos ); } -static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos ) +static void patch_offset_get_filepatches( MSIDATABASE *db, UINT last_sequence, struct patch_offset_list *pos ) { static const WCHAR query[] = { 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','P','a','t','c','h',' ', @@ -460,12 +467,15 @@ static void patch_offset_get_patches( MSIDATABASE *db, UINT last_sequence, struc while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) { - UINT sequence = MSI_RecordGetInteger( rec, 2 ); + struct patch_offset *po = msi_alloc( sizeof(struct patch_offset) ); - /* FIXME: we only use the max/min sequence numbers for now */ - pos->min = min( pos->min, sequence ); - pos->max = max( pos->max, sequence ); + po->name = msi_dup_record_field( rec, 1 ); + po->sequence = MSI_RecordGetInteger( rec, 2 ); + pos->min = min( pos->min, po->sequence ); + pos->max = max( pos->max, po->sequence ); + list_add_tail( &pos->patches, &po->entry ); pos->count++; + msiobj_release( &rec->hdr ); } msiobj_release( &view->hdr ); @@ -512,58 +522,137 @@ static void patch_offset_get_files( MSIDATABASE *db, UINT last_sequence, struct msiobj_release( &view->hdr ); } -static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos ) +static UINT patch_update_file_sequence( MSIDATABASE *db, const struct patch_offset_list *pos, + MSIQUERY *view, MSIRECORD *rec ) { - static const WCHAR query[] = { - 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','F','i','l','e',' ', - 'W','H','E','R','E',' ','S','e','q','u','e','n','c','e',' ','>','=',' ','?',' ', - 'A','N','D',' ','S','e','q','u','e','n','c','e',' ','<','=',' ','?',' ', - 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0}; struct patch_offset *po; + const WCHAR *file = MSI_RecordGetString( rec, 1 ); + UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 8 ); + + LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry ) + { + if (!strcmpiW( file, po->name )) + { + MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply ); + r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); + if (r != ERROR_SUCCESS) + ERR("Failed to update offset for file %s (%u)\n", debugstr_w(file), r); + break; + } + } + return r; +} + +static UINT patch_update_filepatch_sequence( MSIDATABASE *db, const struct patch_offset_list *pos, + MSIQUERY *view, MSIRECORD *rec ) +{ + static const WCHAR delete_query[] = { + 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','P','a','t','c','h','`',' ', + 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','?',' ', + 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','=',' ','?',0}; + static const WCHAR insert_query[] = { + 'I','N','S','E','R','T',' ','I','N','T','O',' ','`','P','a','t','c','h','`',' ', + '(','`','F','i','l','e','_','`',',','`','S','e','q','u','e','n','c','e','`',',', + '`','P','a','t','c','h','S','i','z','e','`',',','`','A','t','t','r','i','b','u','t','e','s','`',',', + '`','H','e','a','d','e','r','`',',','`','S','t','r','e','a','m','R','e','f','_','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',',','?',',','?',',','?',',','?',')',0}; + struct patch_offset *po; + const WCHAR *file = MSI_RecordGetString( rec, 1 ); + UINT r = ERROR_SUCCESS, seq = MSI_RecordGetInteger( rec, 2 ); + + LIST_FOR_EACH_ENTRY( po, &pos->patches, struct patch_offset, entry ) + { + if (seq == po->sequence && !strcmpiW( file, po->name )) + { + MSIQUERY *delete_view, *insert_view; + MSIRECORD *rec2; + + r = MSI_DatabaseOpenViewW( db, delete_query, &delete_view ); + if (r != ERROR_SUCCESS) return r; + + rec2 = MSI_CreateRecord( 2 ); + MSI_RecordSetStringW( rec2, 1, po->name ); + MSI_RecordSetInteger( rec2, 2, po->sequence ); + r = MSI_ViewExecute( delete_view, rec2 ); + msiobj_release( &delete_view->hdr ); + msiobj_release( &rec2->hdr ); + if (r != ERROR_SUCCESS) return r; + + r = MSI_DatabaseOpenViewW( db, insert_query, &insert_view ); + if (r != ERROR_SUCCESS) return r; + + MSI_RecordSetInteger( rec, 2, po->sequence + pos->offset_to_apply ); + + r = MSI_ViewExecute( insert_view, rec ); + msiobj_release( &insert_view->hdr ); + if (r != ERROR_SUCCESS) + ERR("Failed to update offset for filepatch %s (%u)\n", debugstr_w(file), r); + break; + } + } + return r; +} + +static UINT patch_offset_modify_db( MSIDATABASE *db, struct patch_offset_list *pos ) +{ + static const WCHAR file_query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ','`','F','i','l','e','`',' ', + 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ', + 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; + static const WCHAR patch_query[] = { + 'S','E','L','E','C','T',' ','*','F','R','O','M',' ','`','P','a','t','c','h','`',' ', + 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',' ','>','=',' ','?',' ', + 'A','N','D',' ','`','S','e','q','u','e','n','c','e','`',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','S','e','q','u','e','n','c','e','`',0}; MSIRECORD *rec; MSIQUERY *view; - UINT r; + UINT r, min = pos->min, max = pos->max, r_fetch; - r = MSI_DatabaseOpenViewW( db, query, &view ); + r = MSI_DatabaseOpenViewW( db, file_query, &view ); if (r != ERROR_SUCCESS) return ERROR_SUCCESS; rec = MSI_CreateRecord( 2 ); - MSI_RecordSetInteger( rec, 1, pos->min ); - MSI_RecordSetInteger( rec, 2, pos->max ); + MSI_RecordSetInteger( rec, 1, min ); + MSI_RecordSetInteger( rec, 2, max ); r = MSI_ViewExecute( view, rec ); msiobj_release( &rec->hdr ); if (r != ERROR_SUCCESS) goto done; - LIST_FOR_EACH_ENTRY( po, &pos->files, struct patch_offset, entry ) + while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS) { - UINT r_fetch; - while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS) - { - const WCHAR *file = MSI_RecordGetString( rec, 1 ); - UINT seq; + r = patch_update_file_sequence( db, pos, view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) goto done; + } + msiobj_release( &view->hdr ); - if (!strcmpiW( file, po->name )) - { - /* update record */ - seq = MSI_RecordGetInteger( rec, 8 ); - MSI_RecordSetInteger( rec, 8, seq + pos->offset_to_apply ); - r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); - if (r != ERROR_SUCCESS) - ERR("Failed to update offset for file %s\n", debugstr_w(file)); - msiobj_release( &rec->hdr ); - break; - } - msiobj_release( &rec->hdr ); - } - if (r_fetch != ERROR_SUCCESS) break; + r = MSI_DatabaseOpenViewW( db, patch_query, &view ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rec = MSI_CreateRecord( 2 ); + MSI_RecordSetInteger( rec, 1, min ); + MSI_RecordSetInteger( rec, 2, max ); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + goto done; + + while ((r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS) + { + r = patch_update_filepatch_sequence( db, pos, view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) goto done; } done: msiobj_release( &view->hdr ); - return ERROR_SUCCESS; + return r; } static const WCHAR patch_media_query[] = { @@ -583,7 +672,7 @@ struct patch_media WCHAR *source; }; -static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch ) +static UINT patch_add_media( MSIPACKAGE *package, IStorage *storage, MSIPATCHINFO *patch ) { static const WCHAR delete_query[] = { 'D','E','L','E','T','E',' ','F','R','O','M',' ','`','M','e','d','i','a','`',' ', @@ -651,7 +740,7 @@ static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch ) disk_id = package->db->media_transform_disk_id; TRACE("disk id %u\n", disk_id); - TRACE("last sequence %u\n", media->last_sequence); + TRACE("last sequence %u\n", patch->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)); @@ -659,7 +748,7 @@ static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch ) rec = MSI_CreateRecord( 6 ); MSI_RecordSetInteger( rec, 1, disk_id ); - MSI_RecordSetInteger( rec, 2, media->last_sequence ); + MSI_RecordSetInteger( rec, 2, patch->last_sequence ); MSI_RecordSetStringW( rec, 3, media->prompt ); MSI_RecordSetStringW( rec, 4, media->cabinet ); MSI_RecordSetStringW( rec, 5, media->volume ); @@ -670,9 +759,13 @@ static UINT add_patch_media( MSIPACKAGE *package, IStorage *patch ) 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++; + r = msi_add_cabinet_stream( package, disk_id, storage, media->cabinet ); + if (r != ERROR_SUCCESS) ERR("failed to add cabinet stream %u\n", r); + else + { + patch->disk_id = disk_id; + package->db->media_transform_disk_id++; + } } done: @@ -689,7 +782,7 @@ done: return r; } -static UINT set_patch_offsets( MSIDATABASE *db ) +static UINT patch_set_offsets( MSIDATABASE *db, MSIPATCHINFO *patch ) { MSIQUERY *view; MSIRECORD *rec; @@ -705,7 +798,7 @@ static UINT set_patch_offsets( MSIDATABASE *db ) while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) { - UINT last_sequence = MSI_RecordGetInteger( rec, 2 ); + UINT offset, last_sequence = MSI_RecordGetInteger( rec, 2 ); struct patch_offset_list *pos; /* FIXME: set/check Source field instead? */ @@ -716,23 +809,27 @@ static UINT set_patch_offsets( MSIDATABASE *db ) } pos = patch_offset_list_create(); patch_offset_get_files( db, last_sequence, pos ); - patch_offset_get_patches( db, last_sequence, pos ); - { - UINT offset = db->media_transform_offset - pos->min; - last_sequence = offset + pos->max; + patch_offset_get_filepatches( db, last_sequence, pos ); - /* FIXME: this is for the patch table, which is not yet properly transformed */ - last_sequence += pos->min; - pos->offset_to_apply = offset; - if (pos->count) - patch_offset_modify_db( db, pos ); + offset = db->media_transform_offset - pos->min; + last_sequence = offset + pos->max; - MSI_RecordSetInteger( rec, 2, last_sequence ); - r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); + last_sequence += pos->min; + pos->offset_to_apply = offset; + if (pos->count) + { + r = patch_offset_modify_db( db, pos ); if (r != ERROR_SUCCESS) - ERR("Failed to update Media table entry, expect breakage (%u)\n", r); - db->media_transform_offset = last_sequence + 1; + ERR("Failed to set offsets, expect breakage (%u)\n", r); } + MSI_RecordSetInteger( rec, 2, last_sequence ); + r = MSI_ViewModify( view, MSIMODIFY_UPDATE, rec ); + if (r != ERROR_SUCCESS) + ERR("Failed to update Media table entry, expect breakage (%u)\n", r); + + patch->last_sequence = last_sequence; + db->media_transform_offset = last_sequence + 1; + patch_offset_list_free( pos ); msiobj_release( &rec->hdr ); } @@ -754,15 +851,18 @@ static UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIP r = apply_substorage_transform( package, patch_db, substorage[i] ); if (r == ERROR_SUCCESS) { - add_patch_media( package, patch_db->storage ); - set_patch_offsets( package->db ); + r = patch_set_offsets( package->db, patch ); + if (r == ERROR_SUCCESS) + r = patch_add_media( package, patch_db->storage, patch ); } } msi_free( substorage ); if (r != ERROR_SUCCESS) return r; - patch_set_media_source_prop( package ); + r = patch_set_media_source_prop( package ); + if (r != ERROR_SUCCESS) + return r; patch->state = MSIPATCHSTATE_APPLIED; list_add_tail( &package->patches, &patch->entry );