[6/6] msi: Add offsets to disk ids added by patches.

Hans Leidekker hans at codeweavers.com
Thu Apr 14 07:42:48 CDT 2011


---
 dlls/msi/action.c      |  172 ++++++++++++++++++++++++++++++++++++++++++++----
 dlls/msi/database.c    |    2 +-
 dlls/msi/files.c       |    2 +-
 dlls/msi/media.c       |   64 +++++++++---------
 dlls/msi/msipriv.h     |    2 +
 dlls/msi/tests/patch.c |   11 +++-
 6 files changed, 204 insertions(+), 49 deletions(-)

diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index 8267aa9..7bed12f 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 fe97069..2cb9ec4 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 a4fe8ff..53ed2ab 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 c699ecc..c261d7c 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 fead8ea..c132477 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 23aac2d..4c6db56 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 );
 
-- 
1.7.4.1






More information about the wine-patches mailing list