[PATCH 3/3] msi: Add support for patching files.

David Hedberg dhedberg at codeweavers.com
Tue Mar 22 14:45:11 CDT 2011


---
 dlls/msi/action.c  |   73 ++++++++++++++++++++++---
 dlls/msi/files.c   |  154 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 dlls/msi/helpers.c |   14 +++++
 dlls/msi/msipriv.h |   14 +++++
 dlls/msi/package.c |    1 +
 include/msidefs.h  |    6 ++-
 6 files changed, 252 insertions(+), 10 deletions(-)

diff --git a/dlls/msi/action.c b/dlls/msi/action.c
index 145a1a6..8a1aeb2 100644
--- a/dlls/msi/action.c
+++ b/dlls/msi/action.c
@@ -115,8 +115,6 @@ static const WCHAR szInstallODBC[] =
     {'I','n','s','t','a','l','l','O','D','B','C',0};
 static const WCHAR szInstallServices[] = 
     {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
-static const WCHAR szPatchFiles[] =
-    {'P','a','t','c','h','F','i','l','e','s',0};
 static const WCHAR szPublishComponents[] = 
     {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
 static const WCHAR szRegisterComPlus[] =
@@ -1847,6 +1845,70 @@ static UINT load_all_files(MSIPACKAGE *package)
     return ERROR_SUCCESS;
 }
 
+static UINT load_patch(MSIRECORD *row, LPVOID param)
+{
+    MSIPACKAGE *package = param;
+    MSIFILEPATCH *patch;
+    LPWSTR file_key;
+
+    patch = msi_alloc_zero( sizeof (MSIFILEPATCH) );
+    if (!patch)
+        return ERROR_NOT_ENOUGH_MEMORY;
+
+    file_key = msi_dup_record_field( row, 1 );
+    patch->File = get_loaded_file( package, file_key );
+    msi_free(file_key);
+
+    if( !patch->File )
+    {
+        ERR("Failed to find target for patch in File table\n");
+        msi_free(patch);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    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;
+
+    /* FIXME:
+     * Header field - for patch validation.
+     * _StreamRef   - External key into MsiPatchHeaders (instead of the header field)
+     */
+
+    TRACE("Patch Loaded (%s)\n", debugstr_w(patch->File->File));
+
+    list_add_tail( &package->filepatches, &patch->entry );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT load_all_patches(MSIPACKAGE *package)
+{
+    MSIQUERY *view;
+    UINT rc;
+    static const WCHAR Query[] =
+        {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
+         '`','P','a','t','c','h','`',' ','O','R','D','E','R',' ','B','Y',' ',
+         '`','S','e','q','u','e','n','c','e','`',0};
+
+    if (!list_empty(&package->filepatches))
+        return ERROR_SUCCESS;
+
+    rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
+    if (rc != ERROR_SUCCESS)
+        return ERROR_SUCCESS;
+
+    rc = MSI_IterateRecords(view, NULL, load_patch, package);
+    msiobj_release(&view->hdr);
+
+    return ERROR_SUCCESS;
+}
+
 static UINT load_folder( MSIRECORD *row, LPVOID param )
 {
     MSIPACKAGE *package = param;
@@ -1955,6 +2017,7 @@ static UINT ACTION_CostInitialize(MSIPACKAGE *package)
     load_all_components( package );
     load_all_features( package );
     load_all_files( package );
+    load_all_patches( package );
 
     return ERROR_SUCCESS;
 }
@@ -7351,12 +7414,6 @@ static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
     return ERROR_SUCCESS;
 }
 
-static UINT ACTION_PatchFiles( MSIPACKAGE *package )
-{
-    static const WCHAR table[] = { 'P','a','t','c','h',0 };
-    return msi_unimplemented_action_stub( package, "PatchFiles", table );
-}
-
 static UINT ACTION_BindImage( MSIPACKAGE *package )
 {
     static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
diff --git a/dlls/msi/files.c b/dlls/msi/files.c
index 4d8c502..a4fe8ff 100644
--- a/dlls/msi/files.c
+++ b/dlls/msi/files.c
@@ -25,7 +25,7 @@
  * InstallFiles
  * DuplicateFiles
  * MoveFiles
- * PatchFiles (TODO)
+ * PatchFiles
  * RemoveDuplicateFiles
  * RemoveFiles
  */
@@ -47,6 +47,9 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(msi);
 
+static HMODULE hmspatcha;
+static BOOL (WINAPI *ApplyPatchToFileW)(LPCWSTR, LPCWSTR, LPCWSTR, ULONG);
+
 static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action )
 {
     MSIRECORD *uirow;
@@ -388,6 +391,155 @@ done:
     return rc;
 }
 
+static BOOL load_mspatcha(void)
+{
+    hmspatcha = LoadLibraryA("mspatcha.dll");
+    if (!hmspatcha)
+    {
+        ERR("Failed to load mspatcha.dll: %d\n", GetLastError());
+        return FALSE;
+    }
+
+    ApplyPatchToFileW = (void*)GetProcAddress(hmspatcha, "ApplyPatchToFileW");
+    if(!ApplyPatchToFileW)
+    {
+        ERR("GetProcAddress(ApplyPatchToFileW) failed: %d.\n", GetLastError());
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void unload_mspatch(void)
+{
+    FreeLibrary(hmspatcha);
+}
+
+static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
+                          LPWSTR *path, DWORD *attrs, PVOID user)
+{
+    static MSIFILEPATCH *p = NULL;
+    static WCHAR patch_path[MAX_PATH] = {0};
+    static WCHAR temp_folder[MAX_PATH] = {0};
+
+    if (action == MSICABEXTRACT_BEGINEXTRACT)
+    {
+        if (temp_folder[0] == '\0')
+            GetTempPathW(MAX_PATH, temp_folder);
+
+        p = get_loaded_filepatch(package, file);
+        if (!p)
+        {
+            TRACE("unknown file in cabinet (%s)\n", debugstr_w(file));
+            return FALSE;
+        }
+
+        msi_file_update_ui(package, p->File, szPatchFiles);
+
+        GetTempFileNameW(temp_folder, NULL, 0, patch_path);
+
+        *path = strdupW(patch_path);
+        *attrs = p->File->Attributes;
+    }
+    else if (action == MSICABEXTRACT_FILEEXTRACTED)
+    {
+        WCHAR patched_file[MAX_PATH];
+        BOOL br;
+
+        GetTempFileNameW(temp_folder, NULL, 0, patched_file);
+
+        br = ApplyPatchToFileW(patch_path, p->File->TargetPath, patched_file, 0);
+        if (br)
+        {
+            /* FIXME: baseline cache */
+
+            DeleteFileW( p->File->TargetPath );
+            MoveFileW( patched_file, p->File->TargetPath );
+
+            p->IsApplied = TRUE;
+        }
+        else
+            ERR("Failed patch %s: %d.\n", debugstr_w(p->File->TargetPath), GetLastError());
+
+        DeleteFileW(patch_path);
+        p = NULL;
+    }
+
+    return TRUE;
+}
+
+UINT ACTION_PatchFiles( MSIPACKAGE *package )
+{
+    MSIFILEPATCH *patch;
+    MSIMEDIAINFO *mi;
+    UINT rc = ERROR_SUCCESS;
+    BOOL mspatcha_loaded = FALSE;
+
+    TRACE("%p\n", package);
+
+    /* increment progress bar each time action data is sent */
+    ui_progress(package,1,1,0,0);
+
+    mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
+
+    LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
+    {
+        MSIFILE *file = patch->File;
+
+        rc = msi_load_media_info( package, patch->Sequence, mi );
+        if (rc != ERROR_SUCCESS)
+        {
+            ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc);
+            return ERROR_FUNCTION_FAILED;
+        }
+        if (!file->Component->Enabled) continue;
+
+        if (!patch->IsApplied)
+        {
+            MSICABDATA data;
+
+            rc = ready_media( package, patch->Sequence, TRUE, mi );
+            if (rc != ERROR_SUCCESS)
+            {
+                ERR("Failed to ready media for %s\n", debugstr_w(file->File));
+                goto done;
+            }
+
+            if (!mspatcha_loaded && !load_mspatcha())
+            {
+                rc = ERROR_FUNCTION_FAILED;
+                goto done;
+            }
+            mspatcha_loaded = TRUE;
+
+            data.mi = mi;
+            data.package = package;
+            data.cb = patchfiles_cb;
+            data.user = (PVOID)(UINT_PTR)mi->disk_id;
+
+            if (!msi_cabextract(package, mi, &data))
+            {
+                ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
+                rc = ERROR_INSTALL_FAILURE;
+                goto done;
+            }
+        }
+
+        if (!patch->IsApplied && !(patch->Attributes & msidbPatchAttributesNonVital))
+        {
+            ERR("Failed to apply patch to file: %s\n", debugstr_w(file->File));
+            rc = ERROR_INSTALL_FAILURE;
+            goto done;
+        }
+    }
+
+done:
+    msi_free_media_info(mi);
+    if (mspatcha_loaded)
+        unload_mspatch();
+    return rc;
+}
+
 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
 
 typedef struct
diff --git a/dlls/msi/helpers.c b/dlls/msi/helpers.c
index ffbe58b..2f7389d 100644
--- a/dlls/msi/helpers.c
+++ b/dlls/msi/helpers.c
@@ -134,6 +134,20 @@ MSIFILE* get_loaded_file( MSIPACKAGE* package, LPCWSTR key )
     return NULL;
 }
 
+MSIFILEPATCH* get_loaded_filepatch( MSIPACKAGE* package, LPCWSTR key )
+{
+    MSIFILEPATCH *patch;
+
+    /* FIXME: There might be more than one patch */
+
+    LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
+    {
+        if (!strcmpW( key, patch->File->File ))
+            return patch;
+    }
+    return NULL;
+}
+
 int track_tempfile( MSIPACKAGE *package, LPCWSTR path )
 {
     MSITEMPFILE *temp;
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
index 266c25e..301001b 100644
--- a/dlls/msi/msipriv.h
+++ b/dlls/msi/msipriv.h
@@ -346,6 +346,7 @@ typedef struct tagMSIPACKAGE
     struct list components;
     struct list features;
     struct list files;
+    struct list filepatches;
     struct list tempfiles;
     struct list folders;
     struct list binaries;
@@ -531,6 +532,16 @@ typedef struct tagMSITEMPFILE
     LPWSTR Path;
 } MSITEMPFILE;
 
+typedef struct tagMSIFILEPATCH
+{
+    struct list entry;
+    MSIFILE *File;
+    INT Sequence;
+    INT PatchSize;
+    INT Attributes;
+    BOOL IsApplied;
+} MSIFILEPATCH;
+
 typedef struct tagMSIAPPID
 {
     struct list entry;
@@ -921,6 +932,7 @@ extern UINT ACTION_AppSearch(MSIPACKAGE *package);
 extern UINT ACTION_CCPSearch(MSIPACKAGE *package);
 extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package);
 extern UINT ACTION_InstallFiles(MSIPACKAGE *package);
+extern UINT ACTION_PatchFiles( MSIPACKAGE *package );
 extern UINT ACTION_RemoveFiles(MSIPACKAGE *package);
 extern UINT ACTION_MoveFiles(MSIPACKAGE *package);
 extern UINT ACTION_DuplicateFiles(MSIPACKAGE *package);
@@ -952,6 +964,7 @@ extern void msi_reset_folders( MSIPACKAGE *package, BOOL source );
 extern MSICOMPONENT *get_loaded_component( MSIPACKAGE* package, LPCWSTR Component );
 extern MSIFEATURE *get_loaded_feature( MSIPACKAGE* package, LPCWSTR Feature );
 extern MSIFILE *get_loaded_file( MSIPACKAGE* package, LPCWSTR file );
+extern MSIFILEPATCH *get_loaded_filepatch( MSIPACKAGE* package, LPCWSTR key );
 extern MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir );
 extern int track_tempfile(MSIPACKAGE *package, LPCWSTR path);
 extern UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action);
@@ -1049,6 +1062,7 @@ static const WCHAR szRegisterMIMEInfo[] = {'R','e','g','i','s','t','e','r','M','
 static const WCHAR szDuplicateFiles[] = {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
 static const WCHAR szRemoveDuplicateFiles[] = {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
 static const WCHAR szInstallFiles[] = {'I','n','s','t','a','l','l','F','i','l','e','s',0};
+static const WCHAR szPatchFiles[] = {'P','a','t','c','h','F','i','l','e','s',0};
 static const WCHAR szRemoveFiles[] = {'R','e','m','o','v','e','F','i','l','e','s',0};
 static const WCHAR szFindRelatedProducts[] = {'F','i','n','d','R','e','l','a','t','e','d','P','r','o','d','u','c','t','s',0};
 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
diff --git a/dlls/msi/package.c b/dlls/msi/package.c
index c84c28a..00ed9f8 100644
--- a/dlls/msi/package.c
+++ b/dlls/msi/package.c
@@ -1065,6 +1065,7 @@ static MSIPACKAGE *msi_alloc_package( void )
         list_init( &package->components );
         list_init( &package->features );
         list_init( &package->files );
+        list_init( &package->filepatches );
         list_init( &package->tempfiles );
         list_init( &package->folders );
         list_init( &package->subscriptions );
diff --git a/include/msidefs.h b/include/msidefs.h
index a3b487c..640bfa1 100644
--- a/include/msidefs.h
+++ b/include/msidefs.h
@@ -38,7 +38,11 @@ enum msidbFileAttributes {
     msidbFileAttributesNoncompressed = 0x00002000,
     msidbFileAttributesCompressed = 0x00004000
 };
-        
+
+enum msidbPatchAttributes {
+    msidbPatchAttributesNonVital = 0x00000001
+};
+
 enum msidbDialogAttributes {
     msidbDialogAttributesVisible = 0x00000001,
     msidbDialogAttributesModal = 0x00000002,
-- 
1.7.4.1




More information about the wine-patches mailing list