msi: Add an offset to sequence numbers belonging to files added by a patch.
This commit is contained in:
parent
3998820497
commit
fed6e5211c
|
@ -625,6 +625,253 @@ UINT msi_parse_patch_summary( MSISUMMARYINFO *si, MSIPATCHINFO **patch )
|
||||||
return r;
|
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 msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINFO *patch )
|
||||||
{
|
{
|
||||||
UINT i, r = ERROR_SUCCESS;
|
UINT i, r = ERROR_SUCCESS;
|
||||||
|
@ -633,7 +880,11 @@ UINT msi_apply_patch_db( MSIPACKAGE *package, MSIDATABASE *patch_db, MSIPATCHINF
|
||||||
/* apply substorage transforms */
|
/* apply substorage transforms */
|
||||||
substorage = msi_split_string( patch->transforms, ';' );
|
substorage = msi_split_string( patch->transforms, ';' );
|
||||||
for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
|
for (i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++)
|
||||||
|
{
|
||||||
r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
|
r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
|
||||||
|
if (r == ERROR_SUCCESS)
|
||||||
|
msi_set_patch_offsets( package->db );
|
||||||
|
}
|
||||||
|
|
||||||
msi_free( substorage );
|
msi_free( substorage );
|
||||||
if (r != ERROR_SUCCESS)
|
if (r != ERROR_SUCCESS)
|
||||||
|
|
|
@ -417,6 +417,8 @@ UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
|
||||||
|
|
||||||
db->path = strdupW( path );
|
db->path = strdupW( path );
|
||||||
|
|
||||||
|
db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET;
|
||||||
|
|
||||||
if( TRACE_ON( msi ) )
|
if( TRACE_ON( msi ) )
|
||||||
enum_stream_names( stg );
|
enum_stream_names( stg );
|
||||||
|
|
||||||
|
|
|
@ -75,6 +75,8 @@ struct tagMSIOBJECTHDR
|
||||||
msihandledestructor destructor;
|
msihandledestructor destructor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define MSI_INITIAL_MEDIA_TRANSFORM_OFFSET 10000
|
||||||
|
|
||||||
typedef struct tagMSIDATABASE
|
typedef struct tagMSIDATABASE
|
||||||
{
|
{
|
||||||
MSIOBJECTHDR hdr;
|
MSIOBJECTHDR hdr;
|
||||||
|
@ -85,6 +87,7 @@ typedef struct tagMSIDATABASE
|
||||||
LPWSTR deletefile;
|
LPWSTR deletefile;
|
||||||
LPWSTR localfile;
|
LPWSTR localfile;
|
||||||
LPCWSTR mode;
|
LPCWSTR mode;
|
||||||
|
UINT media_transform_offset;
|
||||||
struct list tables;
|
struct list tables;
|
||||||
struct list transforms;
|
struct list transforms;
|
||||||
struct list streams;
|
struct list streams;
|
||||||
|
|
|
@ -957,11 +957,76 @@ static UINT find_entry( MSIHANDLE hdb, const char *table, const char *entry )
|
||||||
return r;
|
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 )
|
static void test_system_tables( void )
|
||||||
{
|
{
|
||||||
UINT r;
|
UINT r;
|
||||||
|
char *cr;
|
||||||
const char *query;
|
const char *query;
|
||||||
MSIHANDLE hproduct, hdb, hview, hrec;
|
MSIHANDLE hproduct, hdb, hview, hrec;
|
||||||
|
static const char patchsource[] = "MSPSRC0F96CDC04CDF4304B2837B9264889EF7";
|
||||||
|
|
||||||
if (!pMsiApplyPatchA)
|
if (!pMsiApplyPatchA)
|
||||||
{
|
{
|
||||||
|
@ -1106,6 +1171,18 @@ static void test_system_tables( void )
|
||||||
r = find_entry( hdb, "_Tables", "PatchPackage" );
|
r = find_entry( hdb, "_Tables", "PatchPackage" );
|
||||||
ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
|
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 );
|
MsiCloseHandle( hrec );
|
||||||
MsiViewClose( hview );
|
MsiViewClose( hview );
|
||||||
MsiCloseHandle( hview );
|
MsiCloseHandle( hview );
|
||||||
|
|
Loading…
Reference in New Issue