msi: Add support for patching files.

This commit is contained in:
David Hedberg 2011-03-22 20:45:11 +01:00 committed by Alexandre Julliard
parent 18973df9c7
commit 6771ed40e8
6 changed files with 252 additions and 10 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -38,7 +38,11 @@ enum msidbFileAttributes {
msidbFileAttributesNoncompressed = 0x00002000,
msidbFileAttributesCompressed = 0x00004000
};
enum msidbPatchAttributes {
msidbPatchAttributesNonVital = 0x00000001
};
enum msidbDialogAttributes {
msidbDialogAttributesVisible = 0x00000001,
msidbDialogAttributesModal = 0x00000002,