diff --git a/dlls/msi/action.c b/dlls/msi/action.c index f806f18cb34..145a1a6725d 100644 --- a/dlls/msi/action.c +++ b/dlls/msi/action.c @@ -625,6 +625,253 @@ UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch ) return r; } +struct msi_patch_offset +{ + struct list entry; + LPWSTR Name; + UINT Sequence; +}; + +struct msi_patch_offset_list +{ + struct list files; + UINT count, min, max; + UINT offset_to_apply; +}; + +static struct msi_patch_offset_list *msi_patch_offset_list_create(void) +{ + struct msi_patch_offset_list *pos = msi_alloc(sizeof(struct msi_patch_offset_list)); + list_init( &pos->files ); + pos->count = pos->max = 0; + pos->min = 999999; + + return pos; +} + +static void msi_patch_offset_list_free(struct msi_patch_offset_list *pos) +{ + struct msi_patch_offset *po, *po2; + + LIST_FOR_EACH_ENTRY_SAFE( po, po2, &pos->files, struct msi_patch_offset, entry ) + { + msi_free( po->Name ); + msi_free( po ); + } + + msi_free( pos ); +} + +static void msi_patch_offset_get_patches(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos) +{ + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + static const WCHAR query_patch[] = { + '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',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0}; + + r = MSI_DatabaseOpenViewW( db, query_patch, &view ); + if (r != ERROR_SUCCESS) + return; + + rec = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger(rec, 1, last_sequence); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + return; + + while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS) + { + UINT sequence = MSI_RecordGetInteger( rec, 2 ); + + /* FIXME: + * We only use the max/min sequence numbers for now. + */ + + pos->min = min(pos->min, sequence); + pos->max = max(pos->max, sequence); + pos->count++; + + msiobj_release( &rec->hdr ); + } + + msiobj_release( &view->hdr ); +} + +static void msi_patch_offset_get_files(MSIDATABASE *db, UINT last_sequence, struct msi_patch_offset_list *pos) +{ + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + static const WCHAR query_files[] = { + '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',' ','<','=',' ','?',' ', + 'O','R','D','E','R',' ','B','Y',' ','S','e','q','u','e','n','c','e',0}; + + r = MSI_DatabaseOpenViewW( db, query_files, &view ); + if (r != ERROR_SUCCESS) + return; + + rec = MSI_CreateRecord( 1 ); + MSI_RecordSetInteger(rec, 1, last_sequence); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + return; + + while (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS) + { + UINT attributes = MSI_RecordGetInteger( rec, 7 ); + if (attributes & msidbFileAttributesPatchAdded) + { + struct msi_patch_offset *po = msi_alloc(sizeof(struct msi_patch_offset)); + + po->Name = msi_dup_record_field( rec, 1 ); + po->Sequence = MSI_RecordGetInteger( rec, 8 ); + + pos->min = min(pos->min, po->Sequence); + pos->max = max(pos->max, po->Sequence); + + list_add_tail( &pos->files, &po->entry ); + pos->count++; + } + msiobj_release( &rec->hdr ); + } + + msiobj_release( &view->hdr ); +} + +static UINT msi_patch_offset_modify_db(MSIDATABASE *db, struct msi_patch_offset_list *pos) +{ + static const WCHAR query_files[] = + {'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 msi_patch_offset *po; + MSIQUERY *view; + MSIRECORD *rec; + UINT r; + + r = MSI_DatabaseOpenViewW( db, query_files, &view ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rec = MSI_CreateRecord( 2 ); + MSI_RecordSetInteger( rec, 1, pos->min ); + MSI_RecordSetInteger( rec, 2, pos->max ); + + r = MSI_ViewExecute( view, rec ); + msiobj_release( &rec->hdr ); + if (r != ERROR_SUCCESS) + goto done; + + LIST_FOR_EACH_ENTRY( po, &pos->files, struct msi_patch_offset, entry ) + { + UINT r_fetch; + while ( (r_fetch = MSI_ViewFetch( view, &rec )) == ERROR_SUCCESS ) + { + LPCWSTR file = MSI_RecordGetString( rec, 1 ); + UINT seq; + + 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; + } + +done: + msiobj_release( &view->hdr ); + + return ERROR_SUCCESS; +} + +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; + MSIRECORD *rec = NULL; + UINT r; + + r = MSI_DatabaseOpenViewW( db, query_media, &view ); + if (r != ERROR_SUCCESS) + return r; + + r = MSI_ViewExecute( view, 0 ); + if (r != ERROR_SUCCESS) + goto done; + + while (MSI_ViewFetch( view, &rec ) == ERROR_SUCCESS) + { + UINT last_sequence = MSI_RecordGetInteger( rec, 2 ); + struct msi_patch_offset_list *pos; + + /* FIXME: Set/Check Source field instead? */ + if (last_sequence >= MSI_INITIAL_MEDIA_TRANSFORM_OFFSET) + { + msiobj_release( &rec->hdr ); + continue; + } + + pos = msi_patch_offset_list_create(); + + msi_patch_offset_get_files( db, last_sequence, pos ); + msi_patch_offset_get_patches( db, last_sequence, pos ); + + if (pos->count) + { + UINT offset = db->media_transform_offset - pos->min; + last_sequence = offset + pos->max; + + /* FIXME: + * This is for the patch table, which is not yet properly transformed. + */ + last_sequence += pos->min; + + pos->offset_to_apply = offset; + msi_patch_offset_modify_db( db, pos ); + + 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); + + db->media_transform_offset = last_sequence + 1; + } + + msi_patch_offset_list_free( pos ); + msiobj_release( &rec->hdr ); + } + +done: + msiobj_release( &view->hdr ); + + return r; +} + UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch ) { UINT i, r = ERROR_SUCCESS; @@ -633,7 +880,11 @@ UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINF /* apply substorage transforms */ substorage = msi_split_string( patch->transforms, ';' ); for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++) + { r = msi_apply_substorage_transform( package, patch_db, substorage[i] ); + if (r == ERROR_SUCCESS) + msi_set_patch_offsets( package->db ); + } msi_free( substorage ); if (r != ERROR_SUCCESS) diff --git a/dlls/msi/database.c b/dlls/msi/database.c index 598d7a8a022..fe970695dfc 100644 --- a/dlls/msi/database.c +++ b/dlls/msi/database.c @@ -417,6 +417,8 @@ UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) db->path = strdupW( path ); + db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; + if( TRACE_ON( msi ) ) enum_stream_names( stg ); diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index 096678c48d6..1a0bee0fdb0 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -75,6 +75,8 @@ struct tagMSIOBJECTHDR msihandledestructor destructor; }; +#define MSI_INITIAL_MEDIA_TRANSFORM_OFFSET 10000 + typedef struct tagMSIDATABASE { MSIOBJECTHDR hdr; @@ -85,6 +87,7 @@ typedef struct tagMSIDATABASE LPWSTR deletefile; LPWSTR localfile; LPCWSTR mode; + UINT media_transform_offset; struct list tables; struct list transforms; struct list streams; diff --git a/dlls/msi/tests/patch.c b/dlls/msi/tests/patch.c index 4cad4ebb6ff..ee712bb38ec 100644 --- a/dlls/msi/tests/patch.c +++ b/dlls/msi/tests/patch.c @@ -957,11 +957,76 @@ static UINT find_entry( MSIHANDLE hdb, const char *table, const char *entry ) return r; } +static INT get_integer( MSIHANDLE hdb, UINT field, const char *query) +{ + UINT r; + INT ret = -1; + MSIHANDLE hview, hrec; + + r = MsiDatabaseOpenView( hdb, query, &hview ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r ); + + r = MsiViewExecute( hview, 0 ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r ); + + r = MsiViewFetch( hview, &hrec ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r ); + if (r == ERROR_SUCCESS) + { + UINT r_tmp; + ret = MsiRecordGetInteger( hrec, field ); + + r_tmp = MsiViewFetch( hview, &hrec ); + ok( r_tmp == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r); + } + + MsiViewClose( hview ); + MsiCloseHandle( hview ); + MsiCloseHandle( hrec ); + + return ret; +} + +static char *get_string( MSIHANDLE hdb, UINT field, const char *query) +{ + UINT r; + static char ret[MAX_PATH]; + MSIHANDLE hview, hrec; + + ret[0] = '\0'; + + r = MsiDatabaseOpenView( hdb, query, &hview ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r ); + + r = MsiViewExecute( hview, 0 ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r ); + + r = MsiViewFetch( hview, &hrec ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r ); + if (r == ERROR_SUCCESS) + { + UINT size = MAX_PATH; + r = MsiRecordGetStringA( hrec, field, ret, &size ); + ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r); + + r = MsiViewFetch( hview, &hrec ); + ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r); + } + + MsiViewClose( hview ); + MsiCloseHandle( hview ); + MsiCloseHandle( hrec ); + + return ret; +} + static void test_system_tables( void ) { UINT r; + char *cr; const char *query; MSIHANDLE hproduct, hdb, hview, hrec; + static const char patchsource[] = "MSPSRC0F96CDC04CDF4304B2837B9264889EF7"; if (!pMsiApplyPatchA) { @@ -1106,6 +1171,18 @@ static void test_system_tables( void ) r = find_entry( hdb, "_Tables", "PatchPackage" ); 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 ); + + r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL"); + todo_wine ok( r == 100, "Got %u\n", r ); + + 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, 8, "SELECT * FROM `File` WHERE `File` = 'patch.txt'"); + ok( r == 10000, "Got %u\n", r ); + MsiCloseHandle( hrec ); MsiViewClose( hview ); MsiCloseHandle( hview );