[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