msi: Add support for patching files.
This commit is contained in:
parent
18973df9c7
commit
6771ed40e8
|
@ -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 };
|
||||
|
|
154
dlls/msi/files.c
154
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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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 );
|
||||
|
|
|
@ -38,7 +38,11 @@ enum msidbFileAttributes {
|
|||
msidbFileAttributesNoncompressed = 0x00002000,
|
||||
msidbFileAttributesCompressed = 0x00004000
|
||||
};
|
||||
|
||||
|
||||
enum msidbPatchAttributes {
|
||||
msidbPatchAttributesNonVital = 0x00000001
|
||||
};
|
||||
|
||||
enum msidbDialogAttributes {
|
||||
msidbDialogAttributesVisible = 0x00000001,
|
||||
msidbDialogAttributesModal = 0x00000002,
|
||||
|
|
Loading…
Reference in New Issue