From 6771ed40e808bfaadaee49aae9cbc7092686994a Mon Sep 17 00:00:00 2001 From: David Hedberg Date: Tue, 22 Mar 2011 20:45:11 +0100 Subject: [PATCH] msi: Add support for patching files. --- 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 145a1a6725d..2f0b7395489 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 4d8c502a910..a4fe8ff416e 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 ffbe58b40e0..2f7389d3269 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 266c25e41ef..7af99a6ad4f 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 c84c28a7516..00ed9f839d0 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 a3b487c8573..640bfa16ef3 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,