2006-02-17 00:04:10 +00:00
|
|
|
/*
|
|
|
|
* Implementation of the Microsoft Installer (msi.dll)
|
|
|
|
*
|
|
|
|
* Copyright 2005 Aric Stewart for CodeWeavers
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
2006-08-01 23:12:11 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2006-02-17 00:04:10 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2012-01-21 17:19:12 +00:00
|
|
|
* Actions dealing with files:
|
2006-02-17 00:04:10 +00:00
|
|
|
*
|
|
|
|
* InstallFiles
|
|
|
|
* DuplicateFiles
|
2010-03-06 09:05:09 +00:00
|
|
|
* MoveFiles
|
2012-01-21 17:19:12 +00:00
|
|
|
* PatchFiles
|
2010-03-06 09:05:09 +00:00
|
|
|
* RemoveDuplicateFiles
|
|
|
|
* RemoveFiles
|
2006-02-17 00:04:10 +00:00
|
|
|
*/
|
|
|
|
|
2018-03-04 23:30:58 +00:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
|
|
|
#define COBJMACROS
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2018-03-04 23:30:58 +00:00
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winerror.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
#include "fdi.h"
|
|
|
|
#include "msi.h"
|
|
|
|
#include "msidefs.h"
|
|
|
|
#include "msipriv.h"
|
|
|
|
#include "winuser.h"
|
|
|
|
#include "winreg.h"
|
|
|
|
#include "shlwapi.h"
|
|
|
|
#include "patchapi.h"
|
|
|
|
#include "wine/unicode.h"
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *action )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
MSIRECORD *uirow;
|
|
|
|
|
|
|
|
uirow = MSI_CreateRecord( 9 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, f->FileName );
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
MSI_RecordSetStringW( uirow, 9, f->Component->Directory );
|
2006-02-17 00:04:10 +00:00
|
|
|
MSI_RecordSetInteger( uirow, 6, f->FileSize );
|
2017-10-08 08:14:40 +00:00
|
|
|
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
|
2006-02-17 00:04:10 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_ui_progress( package, 2, f->FileSize, 0, 0 );
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
static BOOL is_registered_patch_media( MSIPACKAGE *package, UINT disk_id )
|
|
|
|
{
|
|
|
|
MSIPATCHINFO *patch;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( patch, &package->patches, MSIPATCHINFO, entry )
|
|
|
|
{
|
|
|
|
if (patch->disk_id == disk_id && patch->registered) return TRUE;
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL is_obsoleted_by_patch( MSIPACKAGE *package, MSIFILE *file )
|
|
|
|
{
|
|
|
|
if (!list_empty( &package->patches ) && file->disk_id < MSI_INITIAL_MEDIA_TRANSFORM_DISKID)
|
|
|
|
{
|
|
|
|
if (!msi_get_property_int( package->db, szInstalled, 0 )) return FALSE;
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (is_registered_patch_media( package, file->disk_id )) return TRUE;
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
static msi_file_state calculate_install_state( MSIPACKAGE *package, MSIFILE *file )
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
|
|
|
MSICOMPONENT *comp = file->Component;
|
|
|
|
VS_FIXEDFILEINFO *file_version;
|
|
|
|
WCHAR *font_version;
|
|
|
|
msi_file_state state;
|
2015-07-19 23:04:25 +00:00
|
|
|
DWORD size;
|
2011-03-20 08:47:41 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL || (comp->assembly && comp->assembly->installed))
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("skipping %s (not scheduled for install)\n", debugstr_w(file->File));
|
2011-03-20 08:47:41 +00:00
|
|
|
return msifs_skipped;
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
if (is_obsoleted_by_patch( package, file ))
|
|
|
|
{
|
|
|
|
TRACE("skipping %s (obsoleted by patch)\n", debugstr_w(file->File));
|
|
|
|
return msifs_skipped;
|
|
|
|
}
|
|
|
|
if ((msi_is_global_assembly( comp ) && !comp->assembly->installed) ||
|
2011-03-20 08:47:41 +00:00
|
|
|
GetFileAttributesW( file->TargetPath ) == INVALID_FILE_ATTRIBUTES)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("installing %s (missing)\n", debugstr_w(file->File));
|
2011-03-20 08:47:41 +00:00
|
|
|
return msifs_missing;
|
|
|
|
}
|
|
|
|
if (file->Version)
|
|
|
|
{
|
|
|
|
if ((file_version = msi_get_disk_file_version( file->TargetPath )))
|
|
|
|
{
|
|
|
|
if (msi_compare_file_versions( file_version, file->Version ) < 0)
|
2015-07-19 23:04:25 +00:00
|
|
|
{
|
|
|
|
TRACE("overwriting %s (new version %s old version %u.%u.%u.%u)\n",
|
|
|
|
debugstr_w(file->File), debugstr_w(file->Version),
|
|
|
|
HIWORD(file_version->dwFileVersionMS), LOWORD(file_version->dwFileVersionMS),
|
|
|
|
HIWORD(file_version->dwFileVersionLS), LOWORD(file_version->dwFileVersionLS));
|
2011-03-20 08:47:41 +00:00
|
|
|
state = msifs_overwrite;
|
2015-07-19 23:04:25 +00:00
|
|
|
}
|
2011-03-20 08:47:41 +00:00
|
|
|
else
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("keeping %s (new version %s old version %u.%u.%u.%u)\n",
|
|
|
|
debugstr_w(file->File), debugstr_w(file->Version),
|
|
|
|
HIWORD(file_version->dwFileVersionMS), LOWORD(file_version->dwFileVersionMS),
|
|
|
|
HIWORD(file_version->dwFileVersionLS), LOWORD(file_version->dwFileVersionLS));
|
2011-03-20 08:47:41 +00:00
|
|
|
state = msifs_present;
|
|
|
|
}
|
|
|
|
msi_free( file_version );
|
|
|
|
return state;
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
else if ((font_version = msi_font_version_from_file( file->TargetPath )))
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
|
|
|
if (msi_compare_font_versions( font_version, file->Version ) < 0)
|
2015-07-19 23:04:25 +00:00
|
|
|
{
|
|
|
|
TRACE("overwriting %s (new version %s old version %s)\n",
|
|
|
|
debugstr_w(file->File), debugstr_w(file->Version), debugstr_w(font_version));
|
2011-03-20 08:47:41 +00:00
|
|
|
state = msifs_overwrite;
|
2015-07-19 23:04:25 +00:00
|
|
|
}
|
2011-03-20 08:47:41 +00:00
|
|
|
else
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("keeping %s (new version %s old version %s)\n",
|
|
|
|
debugstr_w(file->File), debugstr_w(file->Version), debugstr_w(font_version));
|
2011-03-20 08:47:41 +00:00
|
|
|
state = msifs_present;
|
|
|
|
}
|
|
|
|
msi_free( font_version );
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
if ((size = msi_get_disk_file_size( file->TargetPath )) != file->FileSize)
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("overwriting %s (old size %u new size %u)\n", debugstr_w(file->File), size, file->FileSize);
|
2011-03-20 08:47:41 +00:00
|
|
|
return msifs_overwrite;
|
|
|
|
}
|
|
|
|
if (file->hash.dwFileHashInfoSize)
|
|
|
|
{
|
|
|
|
if (msi_file_hash_matches( file ))
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("keeping %s (hash match)\n", debugstr_w(file->File));
|
2011-03-20 08:47:41 +00:00
|
|
|
return msifs_hashmatch;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("overwriting %s (hash mismatch)\n", debugstr_w(file->File));
|
2011-03-20 08:47:41 +00:00
|
|
|
return msifs_overwrite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* assume present */
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("keeping %s\n", debugstr_w(file->File));
|
2011-03-20 08:47:41 +00:00
|
|
|
return msifs_present;
|
|
|
|
}
|
|
|
|
|
2006-11-28 11:21:39 +00:00
|
|
|
static void schedule_install_files(MSIPACKAGE *package)
|
|
|
|
{
|
|
|
|
MSIFILE *file;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
|
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
MSICOMPONENT *comp = file->Component;
|
2006-11-28 11:21:39 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
file->state = calculate_install_state( package, file );
|
2011-03-20 08:47:41 +00:00
|
|
|
if (file->state == msifs_overwrite && (comp->Attributes & msidbComponentAttributesNeverOverwrite))
|
|
|
|
{
|
|
|
|
TRACE("not overwriting %s\n", debugstr_w(file->TargetPath));
|
2006-11-28 11:21:39 +00:00
|
|
|
file->state = msifs_skipped;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static UINT copy_file(MSIFILE *file, LPWSTR source)
|
2006-11-28 11:21:39 +00:00
|
|
|
{
|
|
|
|
BOOL ret;
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
ret = CopyFileW(source, file->TargetPath, FALSE);
|
|
|
|
if (!ret)
|
|
|
|
return GetLastError();
|
2006-11-28 11:21:39 +00:00
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
return ERROR_SUCCESS;
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
|
|
|
|
2009-10-17 21:16:57 +00:00
|
|
|
static UINT copy_install_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR source)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
|
|
|
UINT gle;
|
|
|
|
|
2010-05-29 08:55:43 +00:00
|
|
|
TRACE("Copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
|
2008-01-16 10:11:22 +00:00
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
gle = copy_file(file, source);
|
2008-01-16 10:11:22 +00:00
|
|
|
if (gle == ERROR_SUCCESS)
|
|
|
|
return gle;
|
|
|
|
|
2006-11-28 11:21:39 +00:00
|
|
|
if (gle == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite)
|
|
|
|
{
|
|
|
|
TRACE("overwriting existing file\n");
|
2009-10-17 21:16:57 +00:00
|
|
|
return ERROR_SUCCESS;
|
2006-11-28 11:21:39 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
else if (gle == ERROR_ACCESS_DENIED)
|
|
|
|
{
|
|
|
|
SetFileAttributesW(file->TargetPath, FILE_ATTRIBUTE_NORMAL);
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
gle = copy_file(file, source);
|
2008-01-16 10:11:22 +00:00
|
|
|
TRACE("Overwriting existing file: %d\n", gle);
|
|
|
|
}
|
2009-12-22 09:28:03 +00:00
|
|
|
if (gle == ERROR_SHARING_VIOLATION || gle == ERROR_USER_MAPPED_FILE)
|
2009-10-17 21:16:57 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
WCHAR *tmpfileW, *pathW, *p;
|
2009-10-17 21:16:57 +00:00
|
|
|
DWORD len;
|
|
|
|
|
|
|
|
TRACE("file in use, scheduling rename operation\n");
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (!(pathW = strdupW( file->TargetPath ))) return ERROR_OUTOFMEMORY;
|
2009-10-17 21:16:57 +00:00
|
|
|
if ((p = strrchrW(pathW, '\\'))) *p = 0;
|
2012-01-21 17:19:12 +00:00
|
|
|
len = strlenW( pathW ) + 16;
|
|
|
|
if (!(tmpfileW = msi_alloc(len * sizeof(WCHAR))))
|
|
|
|
{
|
|
|
|
msi_free( pathW );
|
|
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
}
|
|
|
|
if (!GetTempFileNameW( pathW, szMsi, 0, tmpfileW )) tmpfileW[0] = 0;
|
|
|
|
msi_free( pathW );
|
2009-10-17 21:16:57 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (CopyFileW(source, tmpfileW, FALSE) &&
|
2009-10-17 21:16:57 +00:00
|
|
|
MoveFileExW(file->TargetPath, NULL, MOVEFILE_DELAY_UNTIL_REBOOT) &&
|
2012-01-21 17:19:12 +00:00
|
|
|
MoveFileExW(tmpfileW, file->TargetPath, MOVEFILE_DELAY_UNTIL_REBOOT))
|
2009-10-17 21:16:57 +00:00
|
|
|
{
|
2012-05-14 21:41:31 +00:00
|
|
|
package->need_reboot_at_end = 1;
|
2009-10-17 21:16:57 +00:00
|
|
|
gle = ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gle = GetLastError();
|
|
|
|
WARN("failed to schedule rename operation: %d)\n", gle);
|
2012-01-21 17:19:12 +00:00
|
|
|
DeleteFileW( tmpfileW );
|
2009-10-17 21:16:57 +00:00
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_free(tmpfileW);
|
2009-10-17 21:16:57 +00:00
|
|
|
}
|
2006-11-28 11:21:39 +00:00
|
|
|
|
|
|
|
return gle;
|
|
|
|
}
|
|
|
|
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
static UINT msi_create_directory( MSIPACKAGE *package, const WCHAR *dir )
|
|
|
|
{
|
|
|
|
MSIFOLDER *folder;
|
2012-01-21 17:19:12 +00:00
|
|
|
const WCHAR *install_path;
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
install_path = msi_get_target_folder( package, dir );
|
|
|
|
if (!install_path) return ERROR_FUNCTION_FAILED;
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
folder = msi_get_loaded_folder( package, dir );
|
|
|
|
if (folder->State == FOLDER_STATE_UNINITIALIZED)
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_create_full_path( install_path );
|
|
|
|
folder->State = FOLDER_STATE_CREATED;
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2013-09-22 16:30:18 +00:00
|
|
|
static MSIFILE *find_file( MSIPACKAGE *package, UINT disk_id, const WCHAR *filename )
|
2012-05-14 21:41:31 +00:00
|
|
|
{
|
|
|
|
MSIFILE *file;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
|
|
|
{
|
2013-09-22 16:30:18 +00:00
|
|
|
if (file->disk_id == disk_id &&
|
|
|
|
file->state != msifs_installed &&
|
|
|
|
!strcmpiW( filename, file->File )) return file;
|
2012-05-14 21:41:31 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
static BOOL installfiles_cb(MSIPACKAGE *package, LPCWSTR filename, DWORD action,
|
2008-12-27 15:10:14 +00:00
|
|
|
LPWSTR *path, DWORD *attrs, PVOID user)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
MSIFILE *file = *(MSIFILE **)user;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
if (action == MSICABEXTRACT_BEGINEXTRACT)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!(file = find_file( package, file->disk_id, filename )))
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("unknown file in cabinet (%s)\n", debugstr_w(filename));
|
2008-12-27 15:10:14 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
if (file->state != msifs_missing && file->state != msifs_overwrite)
|
2008-12-27 15:10:14 +00:00
|
|
|
return FALSE;
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!msi_is_global_assembly( file->Component ))
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
msi_create_directory( package, file->Component->Directory );
|
2011-03-20 08:47:41 +00:00
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
*path = strdupW( file->TargetPath );
|
|
|
|
*attrs = file->Attributes;
|
|
|
|
*(MSIFILE **)user = file;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
else if (action == MSICABEXTRACT_FILEEXTRACTED)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!msi_is_global_assembly( file->Component )) file->state = msifs_installed;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
WCHAR *msi_resolve_file_source( MSIPACKAGE *package, MSIFILE *file )
|
|
|
|
{
|
|
|
|
WCHAR *p, *path;
|
|
|
|
|
|
|
|
TRACE("Working to resolve source of file %s\n", debugstr_w(file->File));
|
|
|
|
|
|
|
|
if (file->IsCompressed) return NULL;
|
|
|
|
|
|
|
|
p = msi_resolve_source_folder( package, file->Component->Directory, NULL );
|
|
|
|
path = msi_build_directory_name( 2, p, file->ShortName );
|
|
|
|
|
|
|
|
if (file->LongName && GetFileAttributesW( path ) == INVALID_FILE_ATTRIBUTES)
|
|
|
|
{
|
|
|
|
msi_free( path );
|
|
|
|
path = msi_build_directory_name( 2, p, file->LongName );
|
|
|
|
}
|
|
|
|
msi_free( p );
|
|
|
|
TRACE("file %s source resolves to %s\n", debugstr_w(file->File), debugstr_w(path));
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
/*
|
|
|
|
* ACTION_InstallFiles()
|
2008-01-16 10:11:22 +00:00
|
|
|
*
|
2006-02-17 00:04:10 +00:00
|
|
|
* For efficiency, this is done in two passes:
|
|
|
|
* 1) Correct all the TargetPaths and determine what files are to be installed.
|
|
|
|
* 2) Extract Cabinets and copy files.
|
|
|
|
*/
|
|
|
|
UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
|
|
|
{
|
2008-05-17 19:46:01 +00:00
|
|
|
MSIMEDIAINFO *mi;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT rc = ERROR_SUCCESS;
|
|
|
|
MSIFILE *file;
|
|
|
|
|
2006-11-28 11:21:39 +00:00
|
|
|
schedule_install_files(package);
|
2008-05-17 19:46:01 +00:00
|
|
|
mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
|
|
|
{
|
2018-03-04 23:30:58 +00:00
|
|
|
BOOL is_global_assembly = msi_is_global_assembly( file->Component );
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_file_update_ui( package, file, szInstallFiles );
|
|
|
|
|
|
|
|
rc = msi_load_media_info( package, file->Sequence, mi );
|
2011-03-20 08:47:41 +00:00
|
|
|
if (rc != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
ERR("Unable to load media info for %s (%u)\n", debugstr_w(file->File), rc);
|
2012-12-09 19:43:59 +00:00
|
|
|
rc = ERROR_FUNCTION_FAILED;
|
|
|
|
goto done;
|
2011-03-20 08:47:41 +00:00
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (file->state != msifs_hashmatch &&
|
|
|
|
file->state != msifs_skipped &&
|
|
|
|
(file->state != msifs_present || !msi_get_property_int( package->db, szInstalled, 0 )) &&
|
|
|
|
(rc = ready_media( package, file->IsCompressed, mi )))
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
|
|
|
ERR("Failed to ready media for %s\n", debugstr_w(file->File));
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
if (file->state != msifs_missing && !mi->is_continuous && file->state != msifs_overwrite)
|
2006-02-17 00:04:10 +00:00
|
|
|
continue;
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
if (file->Sequence > mi->last_sequence || mi->is_continuous ||
|
|
|
|
(file->IsCompressed && !mi->is_extracted))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-12-27 15:10:14 +00:00
|
|
|
MSICABDATA data;
|
2015-07-19 23:04:25 +00:00
|
|
|
MSIFILE *cursor = file;
|
2008-05-17 19:46:01 +00:00
|
|
|
|
|
|
|
data.mi = mi;
|
|
|
|
data.package = package;
|
2008-12-27 15:10:14 +00:00
|
|
|
data.cb = installfiles_cb;
|
2015-07-19 23:04:25 +00:00
|
|
|
data.user = &cursor;
|
2008-05-17 19:46:01 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if (file->IsCompressed && !msi_cabextract(package, mi, &data))
|
2006-11-28 11:21:39 +00:00
|
|
|
{
|
|
|
|
ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
|
2010-05-29 08:55:43 +00:00
|
|
|
rc = ERROR_INSTALL_FAILURE;
|
2011-03-20 08:47:41 +00:00
|
|
|
goto done;
|
2006-11-28 11:21:39 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2006-11-28 11:21:39 +00:00
|
|
|
if (!file->IsCompressed)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
WCHAR *source = msi_resolve_file_source(package, file);
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2011-03-20 08:47:41 +00:00
|
|
|
TRACE("copying %s to %s\n", debugstr_w(source), debugstr_w(file->TargetPath));
|
2008-07-08 20:52:29 +00:00
|
|
|
|
2018-03-04 23:30:58 +00:00
|
|
|
if (!is_global_assembly)
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
|
|
|
msi_create_directory(package, file->Component->Directory);
|
|
|
|
}
|
2009-10-17 21:16:57 +00:00
|
|
|
rc = copy_install_file(package, file, source);
|
2006-11-28 11:21:39 +00:00
|
|
|
if (rc != ERROR_SUCCESS)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
ERR("Failed to copy %s to %s (%u)\n", debugstr_w(source), debugstr_w(file->TargetPath), rc);
|
2006-11-28 11:21:39 +00:00
|
|
|
rc = ERROR_INSTALL_FAILURE;
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free(source);
|
2011-03-20 08:47:41 +00:00
|
|
|
goto done;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2018-03-04 23:30:58 +00:00
|
|
|
if (!is_global_assembly) file->state = msifs_installed;
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free(source);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2018-03-04 23:30:58 +00:00
|
|
|
else if (!is_global_assembly && file->state != msifs_installed &&
|
|
|
|
!(file->Attributes & msidbFileAttributesPatchAdded))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2013-09-22 16:30:18 +00:00
|
|
|
ERR("compressed file wasn't installed (%s)\n", debugstr_w(file->File));
|
2006-11-28 11:21:39 +00:00
|
|
|
rc = ERROR_INSTALL_FAILURE;
|
2011-03-20 08:47:41 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
MSICOMPONENT *comp = file->Component;
|
|
|
|
|
|
|
|
if (!msi_is_global_assembly( comp ) || comp->assembly->installed ||
|
|
|
|
(file->state != msifs_missing && file->state != msifs_overwrite)) continue;
|
|
|
|
|
|
|
|
rc = msi_install_assembly( package, comp );
|
|
|
|
if (rc != ERROR_SUCCESS)
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
ERR("Failed to install assembly\n");
|
|
|
|
rc = ERROR_INSTALL_FAILURE;
|
|
|
|
break;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
file->state = msifs_installed;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free_media_info(mi);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
static MSIFILEPATCH *find_filepatch( MSIPACKAGE *package, UINT disk_id, const WCHAR *key )
|
2013-04-03 21:58:03 +00:00
|
|
|
{
|
|
|
|
MSIFILEPATCH *patch;
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!patch->extracted && patch->disk_id == disk_id && !strcmpW( key, patch->File->File ))
|
|
|
|
return patch;
|
2013-04-03 21:58:03 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
static BOOL patchfiles_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
|
|
|
|
LPWSTR *path, DWORD *attrs, PVOID user)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
MSIFILEPATCH *patch = *(MSIFILEPATCH **)user;
|
2012-01-21 17:19:12 +00:00
|
|
|
|
|
|
|
if (action == MSICABEXTRACT_BEGINEXTRACT)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
MSICOMPONENT *comp;
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if (is_registered_patch_media( package, patch->disk_id ) ||
|
|
|
|
!(patch = find_filepatch( package, patch->disk_id, file ))) return FALSE;
|
2013-04-03 21:58:03 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
comp = patch->File->Component;
|
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL)
|
|
|
|
{
|
|
|
|
TRACE("file %s component %s not installed or disabled\n",
|
|
|
|
debugstr_w(patch->File->File), debugstr_w(comp->Component));
|
|
|
|
return FALSE;
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
patch->path = msi_create_temp_file( package->db );
|
|
|
|
*path = strdupW( patch->path );
|
|
|
|
*attrs = patch->File->Attributes;
|
|
|
|
*(MSIFILEPATCH **)user = patch;
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
|
|
|
else if (action == MSICABEXTRACT_FILEEXTRACTED)
|
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
patch->extracted = TRUE;
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
static UINT patch_file( MSIPACKAGE *package, MSIFILEPATCH *patch )
|
|
|
|
{
|
|
|
|
UINT r = ERROR_SUCCESS;
|
|
|
|
WCHAR *tmpfile = msi_create_temp_file( package->db );
|
|
|
|
|
|
|
|
if (!tmpfile) return ERROR_INSTALL_FAILURE;
|
|
|
|
if (ApplyPatchToFileW( patch->path, patch->File->TargetPath, tmpfile, 0 ))
|
|
|
|
{
|
|
|
|
DeleteFileW( patch->File->TargetPath );
|
|
|
|
MoveFileW( tmpfile, patch->File->TargetPath );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WARN("failed to patch %s: %08x\n", debugstr_w(patch->File->TargetPath), GetLastError());
|
|
|
|
r = ERROR_INSTALL_FAILURE;
|
|
|
|
}
|
|
|
|
DeleteFileW( patch->path );
|
|
|
|
DeleteFileW( tmpfile );
|
|
|
|
msi_free( tmpfile );
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT patch_assembly( MSIPACKAGE *package, MSIASSEMBLY *assembly, MSIFILEPATCH *patch )
|
|
|
|
{
|
|
|
|
UINT r = ERROR_FUNCTION_FAILED;
|
|
|
|
IAssemblyName *name;
|
|
|
|
IAssemblyEnum *iter;
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!(iter = msi_create_assembly_enum( package, assembly->display_name )))
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
while ((IAssemblyEnum_GetNextAssembly( iter, NULL, &name, 0 ) == S_OK))
|
|
|
|
{
|
|
|
|
WCHAR *displayname, *path;
|
|
|
|
UINT len = 0;
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
hr = IAssemblyName_GetDisplayName( name, NULL, &len, 0 );
|
|
|
|
if (hr != E_NOT_SUFFICIENT_BUFFER || !(displayname = msi_alloc( len * sizeof(WCHAR) )))
|
|
|
|
break;
|
|
|
|
|
|
|
|
hr = IAssemblyName_GetDisplayName( name, displayname, &len, 0 );
|
|
|
|
if (FAILED( hr ))
|
|
|
|
{
|
|
|
|
msi_free( displayname );
|
|
|
|
break;
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if ((path = msi_get_assembly_path( package, displayname )))
|
|
|
|
{
|
|
|
|
if (!CopyFileW( path, patch->File->TargetPath, FALSE ))
|
|
|
|
{
|
|
|
|
ERR("Failed to copy file %s -> %s (%u)\n", debugstr_w(path),
|
|
|
|
debugstr_w(patch->File->TargetPath), GetLastError() );
|
|
|
|
msi_free( path );
|
|
|
|
msi_free( displayname );
|
|
|
|
IAssemblyName_Release( name );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
r = patch_file( package, patch );
|
|
|
|
msi_free( path );
|
|
|
|
}
|
|
|
|
|
|
|
|
msi_free( displayname );
|
|
|
|
IAssemblyName_Release( name );
|
|
|
|
if (r == ERROR_SUCCESS) break;
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
IAssemblyEnum_Release( iter );
|
|
|
|
return r;
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UINT ACTION_PatchFiles( MSIPACKAGE *package )
|
|
|
|
{
|
|
|
|
MSIFILEPATCH *patch;
|
|
|
|
MSIMEDIAINFO *mi;
|
|
|
|
UINT rc = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
TRACE("%p\n", package);
|
|
|
|
|
|
|
|
mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("extracting files\n");
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
|
|
|
|
{
|
|
|
|
MSIFILE *file = patch->File;
|
|
|
|
MSICOMPONENT *comp = file->Component;
|
|
|
|
|
|
|
|
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);
|
2012-12-09 19:43:59 +00:00
|
|
|
rc = ERROR_FUNCTION_FAILED;
|
|
|
|
goto done;
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (!comp->Enabled || comp->Action != INSTALLSTATE_LOCAL) continue;
|
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!patch->extracted)
|
2012-01-21 17:19:12 +00:00
|
|
|
{
|
|
|
|
MSICABDATA data;
|
2015-07-19 23:04:25 +00:00
|
|
|
MSIFILEPATCH *cursor = patch;
|
2012-01-21 17:19:12 +00:00
|
|
|
|
|
|
|
rc = ready_media( package, TRUE, mi );
|
|
|
|
if (rc != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
ERR("Failed to ready media for %s\n", debugstr_w(file->File));
|
|
|
|
goto done;
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
data.mi = mi;
|
2012-01-21 17:19:12 +00:00
|
|
|
data.package = package;
|
2015-07-19 23:04:25 +00:00
|
|
|
data.cb = patchfiles_cb;
|
|
|
|
data.user = &cursor;
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
if (!msi_cabextract( package, mi, &data ))
|
2012-01-21 17:19:12 +00:00
|
|
|
{
|
|
|
|
ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
|
|
|
|
rc = ERROR_INSTALL_FAILURE;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
2015-07-19 23:04:25 +00:00
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
|
2015-07-19 23:04:25 +00:00
|
|
|
TRACE("applying patches\n");
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( patch, &package->filepatches, MSIFILEPATCH, entry )
|
|
|
|
{
|
|
|
|
MSICOMPONENT *comp = patch->File->Component;
|
|
|
|
|
|
|
|
if (!patch->path) continue;
|
|
|
|
|
|
|
|
if (msi_is_global_assembly( comp ))
|
|
|
|
rc = patch_assembly( package, comp->assembly, patch );
|
|
|
|
else
|
|
|
|
rc = patch_file( package, patch );
|
|
|
|
|
|
|
|
if (rc && !(patch->Attributes & msidbPatchAttributesNonVital))
|
2012-01-21 17:19:12 +00:00
|
|
|
{
|
2015-07-19 23:04:25 +00:00
|
|
|
ERR("Failed to apply patch to file: %s\n", debugstr_w(patch->File->File));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msi_is_global_assembly( comp ))
|
|
|
|
{
|
|
|
|
if ((rc = msi_install_assembly( package, comp )))
|
|
|
|
{
|
|
|
|
ERR("Failed to install patched assembly\n");
|
|
|
|
break;
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2011-03-20 08:47:41 +00:00
|
|
|
done:
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free_media_info(mi);
|
2006-02-17 00:04:10 +00:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
#define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
struct list entry;
|
|
|
|
LPWSTR sourcename;
|
|
|
|
LPWSTR destname;
|
|
|
|
LPWSTR source;
|
|
|
|
LPWSTR dest;
|
|
|
|
} FILE_LIST;
|
|
|
|
|
|
|
|
static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
|
|
|
|
{
|
|
|
|
BOOL ret;
|
|
|
|
|
|
|
|
if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
|
|
|
|
GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
|
|
|
|
{
|
|
|
|
WARN("Source or dest is directory, not moving\n");
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options == msidbMoveFileOptionsMove)
|
|
|
|
{
|
|
|
|
TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
|
|
|
|
ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
WARN("MoveFile failed: %d\n", GetLastError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
|
|
|
|
ret = CopyFileW(source, dest, FALSE);
|
|
|
|
if (!ret)
|
|
|
|
{
|
|
|
|
WARN("CopyFile failed: %d\n", GetLastError());
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
|
|
|
|
{
|
|
|
|
LPWSTR path, ptr;
|
|
|
|
DWORD dirlen, pathlen;
|
|
|
|
|
|
|
|
ptr = strrchrW(wildcard, '\\');
|
|
|
|
dirlen = ptr - wildcard + 1;
|
|
|
|
|
|
|
|
pathlen = dirlen + lstrlenW(filename) + 1;
|
|
|
|
path = msi_alloc(pathlen * sizeof(WCHAR));
|
|
|
|
|
|
|
|
lstrcpynW(path, wildcard, dirlen + 1);
|
|
|
|
lstrcatW(path, filename);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_file_entry(FILE_LIST *file)
|
|
|
|
{
|
|
|
|
msi_free(file->source);
|
|
|
|
msi_free(file->dest);
|
|
|
|
msi_free(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_list(FILE_LIST *list)
|
|
|
|
{
|
|
|
|
while (!list_empty(&list->entry))
|
|
|
|
{
|
|
|
|
FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
|
|
|
|
|
|
|
|
list_remove(&file->entry);
|
|
|
|
free_file_entry(file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
|
|
|
|
{
|
|
|
|
FILE_LIST *new, *file;
|
|
|
|
LPWSTR ptr, filename;
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
new = msi_alloc_zero(sizeof(FILE_LIST));
|
|
|
|
if (!new)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
new->source = strdupW(source);
|
|
|
|
ptr = strrchrW(dest, '\\') + 1;
|
|
|
|
filename = strrchrW(new->source, '\\') + 1;
|
|
|
|
|
|
|
|
new->sourcename = filename;
|
|
|
|
|
|
|
|
if (*ptr)
|
|
|
|
new->destname = ptr;
|
|
|
|
else
|
|
|
|
new->destname = new->sourcename;
|
|
|
|
|
|
|
|
size = (ptr - dest) + lstrlenW(filename) + 1;
|
|
|
|
new->dest = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!new->dest)
|
|
|
|
{
|
|
|
|
free_file_entry(new);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
lstrcpynW(new->dest, dest, ptr - dest + 1);
|
|
|
|
lstrcatW(new->dest, filename);
|
|
|
|
|
|
|
|
if (list_empty(&files->entry))
|
|
|
|
{
|
|
|
|
list_add_head(&files->entry, &new->entry);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
|
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
if (strcmpW( source, file->source ) < 0)
|
2010-03-06 09:05:09 +00:00
|
|
|
{
|
|
|
|
list_add_before(&file->entry, &new->entry);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add_after(&file->entry, &new->entry);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
|
|
|
|
{
|
|
|
|
WIN32_FIND_DATAW wfd;
|
|
|
|
HANDLE hfile;
|
|
|
|
LPWSTR path;
|
|
|
|
BOOL res;
|
|
|
|
FILE_LIST files, *file;
|
|
|
|
DWORD size;
|
|
|
|
|
|
|
|
hfile = FindFirstFileW(source, &wfd);
|
|
|
|
if (hfile == INVALID_HANDLE_VALUE) return FALSE;
|
|
|
|
|
|
|
|
list_init(&files.entry);
|
|
|
|
|
|
|
|
for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
|
|
|
|
{
|
|
|
|
if (is_dot_dir(wfd.cFileName)) continue;
|
|
|
|
|
|
|
|
path = wildcard_to_file(source, wfd.cFileName);
|
|
|
|
if (!path)
|
|
|
|
{
|
|
|
|
res = FALSE;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
add_wildcard(&files, path, dest);
|
|
|
|
msi_free(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* no files match the wildcard */
|
|
|
|
if (list_empty(&files.entry))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* only the first wildcard match gets renamed to dest */
|
|
|
|
file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
|
|
|
|
size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
|
|
|
|
file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
|
|
|
|
if (!file->dest)
|
|
|
|
{
|
|
|
|
res = FALSE;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* file->dest may be shorter after the reallocation, so add a NULL
|
|
|
|
* terminator. This is needed for the call to strrchrW, as there will no
|
|
|
|
* longer be a NULL terminator within the bounds of the allocation in this case.
|
|
|
|
*/
|
|
|
|
file->dest[size - 1] = '\0';
|
|
|
|
lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
|
|
|
|
|
|
|
|
while (!list_empty(&files.entry))
|
|
|
|
{
|
|
|
|
file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
|
|
|
|
|
|
|
|
msi_move_file(file->source, file->dest, options);
|
|
|
|
|
|
|
|
list_remove(&file->entry);
|
|
|
|
free_file_entry(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
res = TRUE;
|
|
|
|
|
|
|
|
done:
|
|
|
|
free_list(&files);
|
|
|
|
FindClose(hfile);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
void msi_reduce_to_long_filename( WCHAR *filename )
|
|
|
|
{
|
|
|
|
WCHAR *p = strchrW( filename, '|' );
|
|
|
|
if (p) memmove( filename, p + 1, (strlenW( p + 1 ) + 1) * sizeof(WCHAR) );
|
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2009-01-18 12:52:01 +00:00
|
|
|
MSIPACKAGE *package = param;
|
2010-03-06 09:05:09 +00:00
|
|
|
MSIRECORD *uirow;
|
2006-02-17 00:04:10 +00:00
|
|
|
MSICOMPONENT *comp;
|
2010-03-06 09:05:09 +00:00
|
|
|
LPCWSTR sourcename, component;
|
|
|
|
LPWSTR sourcedir, destname = NULL, destdir = NULL, source = NULL, dest = NULL;
|
|
|
|
int options;
|
|
|
|
DWORD size;
|
2012-12-09 19:43:59 +00:00
|
|
|
BOOL wildcards;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
component = MSI_RecordGetString(rec, 2);
|
2012-01-21 17:19:12 +00:00
|
|
|
comp = msi_get_loaded_component(package, component);
|
2010-03-01 12:01:30 +00:00
|
|
|
if (!comp)
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (comp->Action != INSTALLSTATE_LOCAL)
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
TRACE("component not scheduled for installation %s\n", debugstr_w(component));
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
sourcename = MSI_RecordGetString(rec, 3);
|
|
|
|
options = MSI_RecordGetInteger(rec, 7);
|
|
|
|
|
2010-05-29 08:55:43 +00:00
|
|
|
sourcedir = msi_dup_property(package->db, MSI_RecordGetString(rec, 5));
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!sourcedir)
|
|
|
|
goto done;
|
|
|
|
|
2010-05-29 08:55:43 +00:00
|
|
|
destdir = msi_dup_property(package->db, MSI_RecordGetString(rec, 6));
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!destdir)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (!sourcename)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
source = strdupW(sourcedir);
|
|
|
|
if (!source)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
|
|
|
|
source = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!source)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
lstrcpyW(source, sourcedir);
|
|
|
|
if (source[lstrlenW(source) - 1] != '\\')
|
|
|
|
lstrcatW(source, szBackSlash);
|
|
|
|
lstrcatW(source, sourcename);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
wildcards = strchrW(source, '*') || strchrW(source, '?');
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
if (MSI_RecordIsNull(rec, 4))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!wildcards)
|
|
|
|
{
|
2014-09-23 18:32:48 +00:00
|
|
|
WCHAR *p;
|
|
|
|
if (sourcename)
|
|
|
|
destname = strdupW(sourcename);
|
|
|
|
else if ((p = strrchrW(sourcedir, '\\')))
|
|
|
|
destname = strdupW(p + 1);
|
|
|
|
else
|
|
|
|
destname = strdupW(sourcedir);
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!destname)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
destname = strdupW(MSI_RecordGetString(rec, 4));
|
2012-01-21 17:19:12 +00:00
|
|
|
if (destname) msi_reduce_to_long_filename(destname);
|
2010-03-06 09:05:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size = 0;
|
|
|
|
if (destname)
|
|
|
|
size = lstrlenW(destname);
|
|
|
|
|
|
|
|
size += lstrlenW(destdir) + 2;
|
|
|
|
dest = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!dest)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
lstrcpyW(dest, destdir);
|
|
|
|
if (dest[lstrlenW(dest) - 1] != '\\')
|
|
|
|
lstrcatW(dest, szBackSlash);
|
|
|
|
|
|
|
|
if (destname)
|
|
|
|
lstrcatW(dest, destname);
|
|
|
|
|
|
|
|
if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
|
|
|
|
{
|
2012-12-09 19:43:59 +00:00
|
|
|
if (!msi_create_full_path(destdir))
|
2010-03-06 09:05:09 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
WARN("failed to create directory %u\n", GetLastError());
|
2010-03-06 09:05:09 +00:00
|
|
|
goto done;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!wildcards)
|
|
|
|
msi_move_file(source, dest, options);
|
2006-02-17 00:04:10 +00:00
|
|
|
else
|
2010-03-06 09:05:09 +00:00
|
|
|
move_files_wildcard(source, dest, options);
|
|
|
|
|
|
|
|
done:
|
|
|
|
uirow = MSI_CreateRecord( 9 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(rec, 1) );
|
|
|
|
MSI_RecordSetInteger( uirow, 6, 1 ); /* FIXME */
|
|
|
|
MSI_RecordSetStringW( uirow, 9, destdir );
|
2017-10-08 08:14:40 +00:00
|
|
|
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
|
2010-03-06 09:05:09 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
|
|
|
|
|
|
|
msi_free(sourcedir);
|
|
|
|
msi_free(destdir);
|
|
|
|
msi_free(destname);
|
|
|
|
msi_free(source);
|
|
|
|
msi_free(dest);
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT ACTION_MoveFiles( MSIPACKAGE *package )
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
|
|
'`','M','o','v','e','F','i','l','e','`',0};
|
2010-03-06 09:05:09 +00:00
|
|
|
MSIQUERY *view;
|
2012-01-21 17:19:12 +00:00
|
|
|
UINT rc;
|
2010-03-06 09:05:09 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
rc = MSI_DatabaseOpenViewW(package->db, query, &view);
|
2010-03-06 09:05:09 +00:00
|
|
|
if (rc != ERROR_SUCCESS)
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
|
|
|
rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
static WCHAR *get_duplicate_filename( MSIPACKAGE *package, MSIRECORD *row, const WCHAR *file_key, const WCHAR *src )
|
|
|
|
{
|
|
|
|
DWORD len;
|
|
|
|
WCHAR *dst_name, *dst_path, *dst;
|
|
|
|
|
|
|
|
if (MSI_RecordIsNull( row, 4 ))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
len = strlenW( src ) + 1;
|
|
|
|
if (!(dst_name = msi_alloc( len * sizeof(WCHAR)))) return NULL;
|
|
|
|
strcpyW( dst_name, strrchrW( src, '\\' ) + 1 );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MSI_RecordGetStringW( row, 4, NULL, &len );
|
|
|
|
if (!(dst_name = msi_alloc( ++len * sizeof(WCHAR) ))) return NULL;
|
|
|
|
MSI_RecordGetStringW( row, 4, dst_name, &len );
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_reduce_to_long_filename( dst_name );
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
if (MSI_RecordIsNull( row, 5 ))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
WCHAR *p;
|
|
|
|
dst_path = strdupW( src );
|
|
|
|
p = strrchrW( dst_path, '\\' );
|
|
|
|
if (p) *p = 0;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
const WCHAR *dst_key = MSI_RecordGetString( row, 5 );
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
dst_path = strdupW( msi_get_target_folder( package, dst_key ) );
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!dst_path)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
/* try a property */
|
2010-05-29 08:55:43 +00:00
|
|
|
dst_path = msi_dup_property( package->db, dst_key );
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!dst_path)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
FIXME("Unable to get destination folder, try AppSearch properties\n");
|
2010-03-06 09:05:09 +00:00
|
|
|
msi_free( dst_name );
|
|
|
|
return NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
dst = msi_build_directory_name( 2, dst_path, dst_name );
|
|
|
|
msi_create_full_path( dst_path );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
msi_free( dst_name );
|
|
|
|
msi_free( dst_path );
|
|
|
|
return dst;
|
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param)
|
|
|
|
{
|
|
|
|
MSIPACKAGE *package = param;
|
|
|
|
LPWSTR dest;
|
|
|
|
LPCWSTR file_key, component;
|
|
|
|
MSICOMPONENT *comp;
|
|
|
|
MSIRECORD *uirow;
|
|
|
|
MSIFILE *file;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
component = MSI_RecordGetString(row,2);
|
2012-01-21 17:19:12 +00:00
|
|
|
comp = msi_get_loaded_component(package, component);
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!comp)
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (comp->Action != INSTALLSTATE_LOCAL)
|
2010-03-06 09:05:09 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
TRACE("component not scheduled for installation %s\n", debugstr_w(component));
|
2010-03-06 09:05:09 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
file_key = MSI_RecordGetString(row,3);
|
|
|
|
if (!file_key)
|
|
|
|
{
|
|
|
|
ERR("Unable to get file key\n");
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
file = msi_get_loaded_file( package, file_key );
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!file)
|
|
|
|
{
|
|
|
|
ERR("Original file unknown %s\n", debugstr_w(file_key));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
dest = get_duplicate_filename( package, row, file_key, file->TargetPath );
|
|
|
|
if (!dest)
|
|
|
|
{
|
|
|
|
WARN("Unable to get duplicate filename\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("Duplicating file %s to %s\n", debugstr_w(file->TargetPath), debugstr_w(dest));
|
|
|
|
|
|
|
|
if (!CopyFileW( file->TargetPath, dest, TRUE ))
|
|
|
|
{
|
|
|
|
WARN("Failed to copy file %s -> %s (%u)\n",
|
|
|
|
debugstr_w(file->TargetPath), debugstr_w(dest), GetLastError());
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
FIXME("We should track these duplicate files as well\n");
|
|
|
|
|
|
|
|
uirow = MSI_CreateRecord( 9 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) );
|
|
|
|
MSI_RecordSetInteger( uirow, 6, file->FileSize );
|
|
|
|
MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) );
|
2017-10-08 08:14:40 +00:00
|
|
|
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
|
2010-03-06 09:05:09 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
|
|
|
|
|
|
|
msi_free(dest);
|
2006-02-17 00:04:10 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT ACTION_DuplicateFiles(MSIPACKAGE *package)
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
|
|
'`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
|
|
|
|
MSIQUERY *view;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT rc;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
rc = MSI_DatabaseOpenViewW(package->db, query, &view);
|
2006-02-17 00:04:10 +00:00
|
|
|
if (rc != ERROR_SUCCESS)
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
|
|
|
rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
static UINT ITERATE_RemoveDuplicateFiles( MSIRECORD *row, LPVOID param )
|
|
|
|
{
|
|
|
|
MSIPACKAGE *package = param;
|
|
|
|
LPWSTR dest;
|
|
|
|
LPCWSTR file_key, component;
|
|
|
|
MSICOMPONENT *comp;
|
|
|
|
MSIRECORD *uirow;
|
|
|
|
MSIFILE *file;
|
|
|
|
|
|
|
|
component = MSI_RecordGetString( row, 2 );
|
2012-01-21 17:19:12 +00:00
|
|
|
comp = msi_get_loaded_component( package, component );
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!comp)
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (comp->Action != INSTALLSTATE_ABSENT)
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
TRACE("component not scheduled for removal %s\n", debugstr_w(component));
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2010-03-06 09:05:09 +00:00
|
|
|
file_key = MSI_RecordGetString( row, 3 );
|
|
|
|
if (!file_key)
|
|
|
|
{
|
|
|
|
ERR("Unable to get file key\n");
|
|
|
|
return ERROR_FUNCTION_FAILED;
|
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
file = msi_get_loaded_file( package, file_key );
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!file)
|
|
|
|
{
|
|
|
|
ERR("Original file unknown %s\n", debugstr_w(file_key));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest = get_duplicate_filename( package, row, file_key, file->TargetPath );
|
|
|
|
if (!dest)
|
|
|
|
{
|
|
|
|
WARN("Unable to get duplicate filename\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
TRACE("Removing duplicate %s of %s\n", debugstr_w(dest), debugstr_w(file->TargetPath));
|
|
|
|
|
|
|
|
if (!DeleteFileW( dest ))
|
|
|
|
{
|
|
|
|
WARN("Failed to delete duplicate file %s (%u)\n", debugstr_w(dest), GetLastError());
|
|
|
|
}
|
|
|
|
|
|
|
|
uirow = MSI_CreateRecord( 9 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString( row, 1 ) );
|
|
|
|
MSI_RecordSetStringW( uirow, 9, MSI_RecordGetString( row, 5 ) );
|
2017-10-08 08:14:40 +00:00
|
|
|
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
|
2010-03-06 09:05:09 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
|
|
|
|
|
|
|
msi_free(dest);
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
|
|
'`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0};
|
2010-03-06 09:05:09 +00:00
|
|
|
MSIQUERY *view;
|
2012-01-21 17:19:12 +00:00
|
|
|
UINT rc;
|
2010-03-06 09:05:09 +00:00
|
|
|
|
|
|
|
rc = MSI_DatabaseOpenViewW( package->db, query, &view );
|
|
|
|
if (rc != ERROR_SUCCESS)
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
|
|
|
rc = MSI_IterateRecords( view, NULL, ITERATE_RemoveDuplicateFiles, package );
|
|
|
|
msiobj_release( &view->hdr );
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static BOOL verify_comp_for_removal(MSICOMPONENT *comp, UINT install_mode)
|
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
/* special case */
|
2012-01-21 17:19:12 +00:00
|
|
|
if (comp->Action != INSTALLSTATE_SOURCE &&
|
2011-03-20 08:47:41 +00:00
|
|
|
comp->Attributes & msidbComponentAttributesSourceOnly &&
|
|
|
|
(install_mode == msidbRemoveFileInstallModeOnRemove ||
|
|
|
|
install_mode == msidbRemoveFileInstallModeOnBoth)) return TRUE;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
switch (comp->Action)
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
case INSTALLSTATE_LOCAL:
|
|
|
|
case INSTALLSTATE_SOURCE:
|
|
|
|
if (install_mode == msidbRemoveFileInstallModeOnInstall ||
|
|
|
|
install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE;
|
|
|
|
break;
|
|
|
|
case INSTALLSTATE_ABSENT:
|
|
|
|
if (install_mode == msidbRemoveFileInstallModeOnRemove ||
|
|
|
|
install_mode == msidbRemoveFileInstallModeOnBoth) return TRUE;
|
|
|
|
break;
|
|
|
|
default: break;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT ITERATE_RemoveFiles(MSIRECORD *row, LPVOID param)
|
|
|
|
{
|
2009-01-18 12:52:01 +00:00
|
|
|
MSIPACKAGE *package = param;
|
2008-12-27 15:10:14 +00:00
|
|
|
MSICOMPONENT *comp;
|
2010-03-06 09:05:09 +00:00
|
|
|
MSIRECORD *uirow;
|
2011-03-20 08:47:41 +00:00
|
|
|
LPCWSTR component, dirprop;
|
2008-12-27 15:10:14 +00:00
|
|
|
UINT install_mode;
|
2011-03-20 08:47:41 +00:00
|
|
|
LPWSTR dir = NULL, path = NULL, filename = NULL;
|
2008-12-27 15:10:14 +00:00
|
|
|
DWORD size;
|
2010-05-29 08:55:43 +00:00
|
|
|
UINT ret = ERROR_SUCCESS;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
component = MSI_RecordGetString(row, 2);
|
|
|
|
dirprop = MSI_RecordGetString(row, 4);
|
|
|
|
install_mode = MSI_RecordGetInteger(row, 5);
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp = msi_get_loaded_component(package, component);
|
|
|
|
if (!comp)
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
2008-12-27 15:10:14 +00:00
|
|
|
if (!verify_comp_for_removal(comp, install_mode))
|
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
TRACE("Skipping removal due to install mode\n");
|
2008-12-27 15:10:14 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
if (comp->assembly && !comp->assembly->application)
|
|
|
|
{
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2011-03-20 08:47:41 +00:00
|
|
|
if (comp->Attributes & msidbComponentAttributesPermanent)
|
|
|
|
{
|
|
|
|
TRACE("permanent component, not removing file\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2010-05-29 08:55:43 +00:00
|
|
|
dir = msi_dup_property(package->db, dirprop);
|
2008-12-27 15:10:14 +00:00
|
|
|
if (!dir)
|
2012-01-21 17:19:12 +00:00
|
|
|
{
|
|
|
|
WARN("directory property has no value\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2011-03-20 08:47:41 +00:00
|
|
|
size = 0;
|
|
|
|
if ((filename = strdupW( MSI_RecordGetString(row, 3) )))
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_reduce_to_long_filename( filename );
|
2011-03-20 08:47:41 +00:00
|
|
|
size = lstrlenW( filename );
|
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
size += lstrlenW(dir) + 2;
|
|
|
|
path = msi_alloc(size * sizeof(WCHAR));
|
|
|
|
if (!path)
|
|
|
|
{
|
2010-05-29 08:55:43 +00:00
|
|
|
ret = ERROR_OUTOFMEMORY;
|
2008-12-27 15:10:14 +00:00
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filename)
|
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
lstrcpyW(path, dir);
|
|
|
|
PathAddBackslashW(path);
|
2008-12-27 15:10:14 +00:00
|
|
|
lstrcatW(path, filename);
|
|
|
|
|
|
|
|
TRACE("Deleting misc file: %s\n", debugstr_w(path));
|
|
|
|
DeleteFileW(path);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-03-06 09:05:09 +00:00
|
|
|
TRACE("Removing misc directory: %s\n", debugstr_w(dir));
|
|
|
|
RemoveDirectoryW(dir);
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
2010-03-06 09:05:09 +00:00
|
|
|
uirow = MSI_CreateRecord( 9 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, MSI_RecordGetString(row, 1) );
|
|
|
|
MSI_RecordSetStringW( uirow, 9, dir );
|
2017-10-08 08:14:40 +00:00
|
|
|
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
|
2010-03-06 09:05:09 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
|
|
|
|
2011-03-20 08:47:41 +00:00
|
|
|
msi_free(filename);
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free(path);
|
|
|
|
msi_free(dir);
|
2010-05-29 08:55:43 +00:00
|
|
|
return ret;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
static void remove_folder( MSIFOLDER *folder )
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
FolderList *fl;
|
2011-03-20 08:47:41 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
LIST_FOR_EACH_ENTRY( fl, &folder->children, FolderList, entry )
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
remove_folder( fl->folder );
|
|
|
|
}
|
|
|
|
if (!folder->persistent && folder->State != FOLDER_STATE_REMOVED)
|
|
|
|
{
|
|
|
|
if (RemoveDirectoryW( folder->ResolvedTarget )) folder->State = FOLDER_STATE_REMOVED;
|
2011-03-20 08:47:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT ACTION_RemoveFiles( MSIPACKAGE *package )
|
|
|
|
{
|
2008-12-27 15:10:14 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
|
|
'`','R','e','m','o','v','e','F','i','l','e','`',0};
|
2012-01-21 17:19:12 +00:00
|
|
|
MSIQUERY *view;
|
|
|
|
MSICOMPONENT *comp;
|
|
|
|
MSIFILE *file;
|
|
|
|
UINT r;
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
r = MSI_DatabaseOpenViewW(package->db, query, &view);
|
|
|
|
if (r == ERROR_SUCCESS)
|
|
|
|
{
|
2012-01-21 17:19:12 +00:00
|
|
|
r = MSI_IterateRecords(view, NULL, ITERATE_RemoveFiles, package);
|
2008-12-27 15:10:14 +00:00
|
|
|
msiobj_release(&view->hdr);
|
2012-01-21 17:19:12 +00:00
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return r;
|
2008-12-27 15:10:14 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
|
|
|
{
|
2006-08-01 23:12:11 +00:00
|
|
|
MSIRECORD *uirow;
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
VS_FIXEDFILEINFO *ver;
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp = file->Component;
|
|
|
|
msi_file_update_ui( package, file, szRemoveFiles );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (comp->Action != INSTALLSTATE_ABSENT || comp->Installed == INSTALLSTATE_SOURCE)
|
2006-02-17 00:04:10 +00:00
|
|
|
continue;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (comp->assembly && !comp->assembly->application)
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
continue;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (comp->Attributes & msidbComponentAttributesPermanent)
|
2011-03-20 08:47:41 +00:00
|
|
|
{
|
|
|
|
TRACE("permanent component, not removing file\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
if (file->Version)
|
|
|
|
{
|
|
|
|
ver = msi_get_disk_file_version( file->TargetPath );
|
|
|
|
if (ver && msi_compare_file_versions( ver, file->Version ) > 0)
|
|
|
|
{
|
|
|
|
TRACE("newer version detected, not removing file\n");
|
|
|
|
msi_free( ver );
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
msi_free( ver );
|
|
|
|
}
|
2006-08-30 19:24:26 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (file->state == msifs_installed)
|
|
|
|
WARN("removing installed file %s\n", debugstr_w(file->TargetPath));
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
TRACE("removing %s\n", debugstr_w(file->File) );
|
2011-03-20 08:47:41 +00:00
|
|
|
|
|
|
|
SetFileAttributesW( file->TargetPath, FILE_ATTRIBUTE_NORMAL );
|
2010-03-06 09:05:09 +00:00
|
|
|
if (!DeleteFileW( file->TargetPath ))
|
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
WARN("failed to delete %s (%u)\n", debugstr_w(file->TargetPath), GetLastError());
|
2010-03-06 09:05:09 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
file->state = msifs_missing;
|
2006-08-01 23:12:11 +00:00
|
|
|
|
|
|
|
uirow = MSI_CreateRecord( 9 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, file->FileName );
|
2012-01-21 17:19:12 +00:00
|
|
|
MSI_RecordSetStringW( uirow, 9, comp->Directory );
|
2017-10-08 08:14:40 +00:00
|
|
|
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
|
2006-08-01 23:12:11 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2012-05-14 21:41:31 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
|
|
|
|
{
|
|
|
|
comp->Action = msi_get_component_action( package, comp );
|
|
|
|
if (comp->Action != INSTALLSTATE_ABSENT) continue;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (comp->Attributes & msidbComponentAttributesPermanent)
|
|
|
|
{
|
|
|
|
TRACE("permanent component, not removing directory\n");
|
|
|
|
continue;
|
|
|
|
}
|
2012-05-14 21:41:31 +00:00
|
|
|
if (comp->assembly && !comp->assembly->application)
|
|
|
|
msi_uninstall_assembly( package, comp );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
MSIFOLDER *folder = msi_get_loaded_folder( package, comp->Directory );
|
|
|
|
remove_folder( folder );
|
|
|
|
}
|
2012-01-21 17:19:12 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|