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};
|
{'I','n','s','t','a','l','l','O','D','B','C',0};
|
||||||
static const WCHAR szInstallServices[] =
|
static const WCHAR szInstallServices[] =
|
||||||
{'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
|
{'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[] =
|
static const WCHAR szPublishComponents[] =
|
||||||
{'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
|
{'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
|
||||||
static const WCHAR szRegisterComPlus[] =
|
static const WCHAR szRegisterComPlus[] =
|
||||||
|
@ -1847,6 +1845,70 @@ static UINT load_all_files(MSIPACKAGE *package)
|
||||||
return ERROR_SUCCESS;
|
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 )
|
static UINT load_folder( MSIRECORD *row, LPVOID param )
|
||||||
{
|
{
|
||||||
MSIPACKAGE *package = param;
|
MSIPACKAGE *package = param;
|
||||||
|
@ -1955,6 +2017,7 @@ static UINT ACTION_CostInitialize(MSIPACKAGE *package)
|
||||||
load_all_components( package );
|
load_all_components( package );
|
||||||
load_all_features( package );
|
load_all_features( package );
|
||||||
load_all_files( package );
|
load_all_files( package );
|
||||||
|
load_all_patches( package );
|
||||||
|
|
||||||
return ERROR_SUCCESS;
|
return ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -7351,12 +7414,6 @@ static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
|
||||||
return ERROR_SUCCESS;
|
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 UINT ACTION_BindImage( MSIPACKAGE *package )
|
||||||
{
|
{
|
||||||
static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
|
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
|
* InstallFiles
|
||||||
* DuplicateFiles
|
* DuplicateFiles
|
||||||
* MoveFiles
|
* MoveFiles
|
||||||
* PatchFiles (TODO)
|
* PatchFiles
|
||||||
* RemoveDuplicateFiles
|
* RemoveDuplicateFiles
|
||||||
* RemoveFiles
|
* RemoveFiles
|
||||||
*/
|
*/
|
||||||
|
@ -47,6 +47,9 @@
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
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 )
|
static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action )
|
||||||
{
|
{
|
||||||
MSIRECORD *uirow;
|
MSIRECORD *uirow;
|
||||||
|
@ -388,6 +391,155 @@ done:
|
||||||
return rc;
|
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))))
|
#define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
|
|
|
@ -134,6 +134,20 @@ MSIFILE* get_loaded_file( MSIPACKAGE* package, LPCWSTR key )
|
||||||
return NULL;
|
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 )
|
int track_tempfile( MSIPACKAGE *package, LPCWSTR path )
|
||||||
{
|
{
|
||||||
MSITEMPFILE *temp;
|
MSITEMPFILE *temp;
|
||||||
|
|
|
@ -346,6 +346,7 @@ typedef struct tagMSIPACKAGE
|
||||||
struct list components;
|
struct list components;
|
||||||
struct list features;
|
struct list features;
|
||||||
struct list files;
|
struct list files;
|
||||||
|
struct list filepatches;
|
||||||
struct list tempfiles;
|
struct list tempfiles;
|
||||||
struct list folders;
|
struct list folders;
|
||||||
struct list binaries;
|
struct list binaries;
|
||||||
|
@ -531,6 +532,16 @@ typedef struct tagMSITEMPFILE
|
||||||
LPWSTR Path;
|
LPWSTR Path;
|
||||||
} MSITEMPFILE;
|
} MSITEMPFILE;
|
||||||
|
|
||||||
|
typedef struct tagMSIFILEPATCH
|
||||||
|
{
|
||||||
|
struct list entry;
|
||||||
|
MSIFILE *File;
|
||||||
|
INT Sequence;
|
||||||
|
INT PatchSize;
|
||||||
|
INT Attributes;
|
||||||
|
BOOL IsApplied;
|
||||||
|
} MSIFILEPATCH;
|
||||||
|
|
||||||
typedef struct tagMSIAPPID
|
typedef struct tagMSIAPPID
|
||||||
{
|
{
|
||||||
struct list entry;
|
struct list entry;
|
||||||
|
@ -921,6 +932,7 @@ extern UINT ACTION_AppSearch(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_CCPSearch(MSIPACKAGE *package);
|
extern UINT ACTION_CCPSearch(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package);
|
extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_InstallFiles(MSIPACKAGE *package);
|
extern UINT ACTION_InstallFiles(MSIPACKAGE *package);
|
||||||
|
extern UINT ACTION_PatchFiles( MSIPACKAGE *package );
|
||||||
extern UINT ACTION_RemoveFiles(MSIPACKAGE *package);
|
extern UINT ACTION_RemoveFiles(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_MoveFiles(MSIPACKAGE *package);
|
extern UINT ACTION_MoveFiles(MSIPACKAGE *package);
|
||||||
extern UINT ACTION_DuplicateFiles(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 MSICOMPONENT *get_loaded_component( MSIPACKAGE* package, LPCWSTR Component );
|
||||||
extern MSIFEATURE *get_loaded_feature( MSIPACKAGE* package, LPCWSTR Feature );
|
extern MSIFEATURE *get_loaded_feature( MSIPACKAGE* package, LPCWSTR Feature );
|
||||||
extern MSIFILE *get_loaded_file( MSIPACKAGE* package, LPCWSTR file );
|
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 MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir );
|
||||||
extern int track_tempfile(MSIPACKAGE *package, LPCWSTR path);
|
extern int track_tempfile(MSIPACKAGE *package, LPCWSTR path);
|
||||||
extern UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action);
|
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 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 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 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 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 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};
|
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->components );
|
||||||
list_init( &package->features );
|
list_init( &package->features );
|
||||||
list_init( &package->files );
|
list_init( &package->files );
|
||||||
|
list_init( &package->filepatches );
|
||||||
list_init( &package->tempfiles );
|
list_init( &package->tempfiles );
|
||||||
list_init( &package->folders );
|
list_init( &package->folders );
|
||||||
list_init( &package->subscriptions );
|
list_init( &package->subscriptions );
|
||||||
|
|
|
@ -39,6 +39,10 @@ enum msidbFileAttributes {
|
||||||
msidbFileAttributesCompressed = 0x00004000
|
msidbFileAttributesCompressed = 0x00004000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum msidbPatchAttributes {
|
||||||
|
msidbPatchAttributesNonVital = 0x00000001
|
||||||
|
};
|
||||||
|
|
||||||
enum msidbDialogAttributes {
|
enum msidbDialogAttributes {
|
||||||
msidbDialogAttributesVisible = 0x00000001,
|
msidbDialogAttributesVisible = 0x00000001,
|
||||||
msidbDialogAttributesModal = 0x00000002,
|
msidbDialogAttributesModal = 0x00000002,
|
||||||
|
|
Loading…
Reference in New Issue