msi: Add an offset to sequence numbers belonging to files added by a patch.

This commit is contained in:
David Hedberg 2011-03-22 20:45:09 +01:00 committed by Alexandre Julliard
parent 3998820497
commit fed6e5211c
4 changed files with 333 additions and 0 deletions

View File

@ -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)

View File

@ -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 );

View File

@ -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;

View File

@ -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 );