Commit 74239fcd authored by James Hawkins's avatar James Hawkins Committed by Alexandre Julliard

msi: Factor out media handling and use the new interface to efficiently extract assemblies.

parent bf515184
......@@ -28,6 +28,7 @@ C_SRCS = \
insert.c \
install.c \
join.c \
media.c \
msi.c \
msi_main.c \
msiquery.c \
......
......@@ -5665,6 +5665,18 @@ static UINT ACTION_MoveFiles( MSIPACKAGE *package )
return rc;
}
typedef struct tagMSIASSEMBLY
{
struct list entry;
MSICOMPONENT *component;
MSIFEATURE *feature;
MSIFILE *file;
LPWSTR manifest;
LPWSTR application;
DWORD attributes;
BOOL installed;
} MSIASSEMBLY;
static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
DWORD dwReserved);
static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
......@@ -5707,12 +5719,32 @@ static BOOL init_functionpointers(void)
return TRUE;
}
static UINT install_assembly(LPWSTR path)
static UINT install_assembly(MSIASSEMBLY *assembly, LPWSTR path)
{
IAssemblyCache *cache;
HRESULT hr;
UINT r = ERROR_FUNCTION_FAILED;
TRACE("installing assembly: %s\n", debugstr_w(path));
if (assembly->feature)
msi_feature_set_state(assembly->feature, INSTALLSTATE_LOCAL);
if (assembly->manifest)
FIXME("Manifest unhandled\n");
if (assembly->application)
{
FIXME("Assembly should be privately installed\n");
return ERROR_SUCCESS;
}
if (assembly->attributes == msidbAssemblyAttributesWin32)
{
FIXME("Win32 assemblies not handled\n");
return ERROR_SUCCESS;
}
if (!init_functionpointers() || !pCreateAssemblyCache)
return ERROR_FUNCTION_FAILED;
......@@ -5731,106 +5763,207 @@ done:
return r;
}
static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
typedef struct tagASSEMBLY_LIST
{
MSIPACKAGE *package = param;
MSICOMPONENT *comp;
MSIFEATURE *feature;
MSIFILE *file;
WCHAR path[MAX_PATH];
LPCWSTR app;
DWORD attr;
UINT r;
MSIPACKAGE *package;
struct list *assemblies;
} ASSEMBLY_LIST;
comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
if (!comp || !comp->Enabled ||
!(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
static UINT load_assembly(MSIRECORD *rec, LPVOID param)
{
ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
MSIASSEMBLY *assembly;
assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
if (!assembly)
return ERROR_OUTOFMEMORY;
assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
if (!assembly->component || !assembly->component->Enabled ||
!(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
{
TRACE("Component not set for install, not publishing assembly\n");
msi_free(assembly);
return ERROR_SUCCESS;
}
feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
if (feature)
msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
if (MSI_RecordGetString(rec, 3))
FIXME("Manifest unhandled\n");
assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
app = MSI_RecordGetString(rec, 4);
if (app)
if (!assembly->file)
{
FIXME("Assembly should be privately installed\n");
return ERROR_SUCCESS;
ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
return ERROR_FUNCTION_FAILED;
}
attr = MSI_RecordGetInteger(rec, 5);
if (attr == msidbAssemblyAttributesWin32)
{
FIXME("Win32 assemblies not handled\n");
assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
assembly->application = strdupW(MSI_RecordGetString(rec, 4));
assembly->attributes = MSI_RecordGetInteger(rec, 5);
assembly->installed = FALSE;
list_add_head(list->assemblies, &assembly->entry);
return ERROR_SUCCESS;
}
static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
{
MSIQUERY *view;
ASSEMBLY_LIST list;
UINT r;
static const WCHAR query[] =
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
'`','M','s','i','A','s','s','e','m','b','l','y','`',0};
r = MSI_DatabaseOpenViewW(package->db, query, &view);
if (r != ERROR_SUCCESS)
return ERROR_SUCCESS;
}
/* FIXME: extract all files belonging to this component */
file = msi_find_file(package, comp->KeyPath);
if (!file)
list.package = package;
list.assemblies = assemblies;
r = MSI_IterateRecords(view, NULL, load_assembly, &list);
msiobj_release(&view->hdr);
return r;
}
static void free_assemblies(struct list *assemblies)
{
struct list *item, *cursor;
LIST_FOR_EACH_SAFE(item, cursor, assemblies)
{
ERR("File %s not found\n", debugstr_w(comp->KeyPath));
return ERROR_FUNCTION_FAILED;
MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
list_remove(&assembly->entry);
msi_free(assembly->application);
msi_free(assembly->manifest);
msi_free(assembly);
}
}
GetTempPathW(MAX_PATH, path);
static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
{
MSIASSEMBLY *assembly;
if (file->IsCompressed)
LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
{
r = msi_extract_file(package, file, path);
if (r != ERROR_SUCCESS)
if (!lstrcmpW(assembly->file->File, file))
{
ERR("Failed to extract temporary assembly\n");
return r;
*out = assembly;
return TRUE;
}
PathAddBackslashW(path);
lstrcatW(path, file->FileName);
}
else
return FALSE;
}
static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
LPWSTR *path, DWORD *attrs, PVOID user)
{
MSIASSEMBLY *assembly;
WCHAR temppath[MAX_PATH];
struct list *assemblies = (struct list *)user;
UINT r;
if (!find_assembly(assemblies, file, &assembly))
return FALSE;
GetTempPathW(MAX_PATH, temppath);
PathAddBackslashW(temppath);
lstrcatW(temppath, assembly->file->FileName);
if (action == MSICABEXTRACT_BEGINEXTRACT)
{
PathAddBackslashW(path);
lstrcatW(path, file->FileName);
if (assembly->installed)
return FALSE;
if (!CopyFileW(file->SourcePath, path, FALSE))
{
ERR("Failed to copy temporary assembly: %d\n", GetLastError());
return ERROR_FUNCTION_FAILED;
}
*path = strdupW(temppath);
*attrs = assembly->file->Attributes;
}
else if (action == MSICABEXTRACT_FILEEXTRACTED)
{
assembly->installed = TRUE;
r = install_assembly(path);
if (r != ERROR_SUCCESS)
ERR("Failed to install assembly\n");
/* FIXME: write Installer assembly reg values */
r = install_assembly(assembly, temppath);
if (r != ERROR_SUCCESS)
ERR("Failed to install assembly\n");
}
return r;
return TRUE;
}
static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
{
UINT rc;
MSIQUERY *view;
UINT r;
struct list assemblies = LIST_INIT(assemblies);
MSIASSEMBLY *assembly;
MSIMEDIAINFO *mi;
WCHAR path[MAX_PATH];
static const WCHAR ExecSeqQuery[] =
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
'`','M','s','i','A','s','s','e','m','b','l','y','`',0};
r = load_assemblies(package, &assemblies);
if (r != ERROR_SUCCESS)
goto done;
rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
if (rc != ERROR_SUCCESS)
return ERROR_SUCCESS;
if (list_empty(&assemblies))
goto done;
rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
msiobj_release(&view->hdr);
mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
if (!mi)
{
r = ERROR_OUTOFMEMORY;
goto done;
}
return rc;
LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
{
if (assembly->installed && !mi->is_continuous)
continue;
if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
(assembly->file->IsCompressed && !mi->is_extracted))
{
MSICABDATA data;
r = ready_media(package, assembly->file, mi);
if (r != ERROR_SUCCESS)
{
ERR("Failed to ready media\n");
break;
}
data.mi = mi;
data.package = package;
data.cb = installassembly_cb;
data.user = &assemblies;
if (assembly->file->IsCompressed &&
!msi_cabextract(package, mi, &data))
{
ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
r = ERROR_FUNCTION_FAILED;
break;
}
}
if (!assembly->file->IsCompressed)
{
lstrcpyW(path, assembly->file->SourcePath);
r = install_assembly(assembly, path);
if (r != ERROR_SUCCESS)
ERR("Failed to install assembly\n");
}
/* FIXME: write Installer assembly reg values */
}
done:
free_assemblies(&assemblies);
return r;
}
static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
......
......@@ -26,13 +26,9 @@
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "wine/debug.h"
#include "msipriv.h"
#include "winuser.h"
#include "winreg.h"
#include "shlwapi.h"
#include "wine/unicode.h"
#include "msidefs.h"
......@@ -1039,132 +1035,3 @@ void msi_ui_error( DWORD msg_id, DWORD type )
MessageBoxW( NULL, text, title, type );
}
typedef struct
{
MSIPACKAGE *package;
MSIMEDIAINFO *mi;
MSIFILE *file;
LPWSTR destination;
} CabData;
static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
{
TRACE("(%d)\n", fdint);
switch (fdint)
{
case fdintNEXT_CABINET:
{
ERR("continuous cabinets not handled\n");
return 0;
}
case fdintCOPY_FILE:
{
CabData *data = (CabData*) pfdin->pv;
LPWSTR file, path;
DWORD attrs, size;
HANDLE handle;
MSIFILE *f;
file = strdupAtoW(pfdin->psz1);
f = get_loaded_file(data->package, file);
msi_free(file);
if (!f)
{
WARN("unknown file in cabinet (%s)\n",debugstr_a(pfdin->psz1));
return 0;
}
if (lstrcmpW(f->File, data->file->File))
return 0;
size = lstrlenW(data->destination) + lstrlenW(data->file->FileName) + 2;
path = msi_alloc(size * sizeof(WCHAR));
lstrcpyW(path, data->destination);
PathAddBackslashW(path);
lstrcatW(path, data->file->FileName);
TRACE("extracting %s\n", debugstr_w(path));
attrs = f->Attributes & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL;
handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0,
NULL, CREATE_ALWAYS, attrs, NULL);
if (handle == INVALID_HANDLE_VALUE)
{
if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
ERR("failed to create %s (error %d)\n",
debugstr_w(path), GetLastError());
msi_free(path);
return 0;
}
msi_free(path);
return (INT_PTR)handle;
}
case fdintCLOSE_FILE_INFO:
{
FILETIME ft;
FILETIME ftLocal;
HANDLE handle = (HANDLE)pfdin->hf;
if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
return -1;
if (!LocalFileTimeToFileTime(&ft, &ftLocal))
return -1;
if (!SetFileTime(handle, &ftLocal, 0, &ftLocal))
return -1;
CloseHandle(handle);
return 1;
}
default:
return 0;
}
}
UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir)
{
MSIMEDIAINFO *mi;
CabData data;
UINT r;
mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
if (!mi)
return ERROR_OUTOFMEMORY;
r = msi_load_media_info(package, file, mi);
if (r != ERROR_SUCCESS)
goto done;
if (GetFileAttributesW(mi->source) == INVALID_FILE_ATTRIBUTES)
{
r = find_published_source(package, mi);
if (r != ERROR_SUCCESS)
{
ERR("Cabinet not found: %s\n", debugstr_w(mi->source));
return ERROR_INSTALL_FAILURE;
}
}
data.package = package;
data.mi = mi;
data.file = file;
data.destination = destdir;
if (!msi_cabextract(package, mi, cabinet_notify, &data))
{
ERR("Failed to extract cabinet file\n");
r = ERROR_FUNCTION_FAILED;
}
done:
msi_free_media_info(mi);
return r;
}
......@@ -908,10 +908,27 @@ extern UINT msi_create_component_directories( MSIPACKAGE *package );
extern void msi_ui_error( DWORD msg_id, DWORD type );
extern UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
MSIINSTALLCONTEXT context, DWORD options, LPCWSTR value);
/* media */
typedef BOOL (*PMSICABEXTRACTCB)(MSIPACKAGE *, LPCWSTR, DWORD, LPWSTR *, DWORD *, PVOID);
#define MSICABEXTRACT_BEGINEXTRACT 0x01
#define MSICABEXTRACT_FILEEXTRACTED 0x02
typedef struct
{
MSIPACKAGE* package;
MSIMEDIAINFO *mi;
PMSICABEXTRACTCB cb;
LPWSTR curfile;
PVOID user;
} MSICABDATA;
extern UINT ready_media(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi);
extern UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi);
extern void msi_free_media_info(MSIMEDIAINFO *mi);
extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, PFNFDINOTIFY notify, LPVOID data);
extern UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir);
extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, LPVOID data);
extern UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi);
/* control event stuff */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment