|
|
#include "stdinc.h"
#include "xmlparser.hxx"
#include "FusionEventLog.h"
#include "hashfile.h"
#include "CAssemblyRecoveryInfo.h"
#include "recover.h"
#include "sxsprotect.h"
#include "fusionheap.h"
#include "fusionparser.h"
#include "protectionui.h"
#include "parsing.h"
#include "msi.h"
#define WINSXS_INSTALLATION_INFO_REGKEY ( ASSEMBLY_REGISTRY_ROOT L"\\Installations")
class CSetErrorMode { public: CSetErrorMode(UINT uMode) { m_uiPreviousMode = ::SetErrorMode(uMode); } ~CSetErrorMode() { ::SetErrorMode(m_uiPreviousMode); }
private: UINT m_uiPreviousMode;
CSetErrorMode(); CSetErrorMode(const CSetErrorMode &r); void operator =(const CSetErrorMode &r); };
BOOL SxspOpenAssemblyInstallationKey( DWORD dwFlags, DWORD dwAccess, CRegKey &rhkAssemblyInstallation ) { FN_PROLOG_WIN32
rhkAssemblyInstallation = CRegKey::GetInvalidValue();
PARAMETER_CHECK(dwFlags == 0);
IFREGFAILED_ORIGINATE_AND_EXIT( ::RegCreateKeyExW( HKEY_LOCAL_MACHINE, WINSXS_INSTALLATION_INFO_REGKEY, 0, NULL, 0, dwAccess | FUSIONP_KEY_WOW64_64KEY, NULL, &rhkAssemblyInstallation , NULL));
FN_EPILOG }
BOOL SxspSaveAssemblyRegistryData( DWORD Flags, IN PCASSEMBLY_IDENTITY pcAssemblyIdentity ) { FN_PROLOG_WIN32
CFusionRegKey hkAllInstallInfo; CFusionRegKey hkAssemblyKey; CSmallStringBuffer buffRegKeyName; CStringBuffer buffTargetFile; BOOL fTempFlag; CTokenPrivilegeAdjuster Adjuster;
PARAMETER_CHECK(Flags == 0);
IFW32FALSE_EXIT(Adjuster.Initialize()); IFW32FALSE_EXIT(Adjuster.AddPrivilege(L"SeBackupPrivilege")); IFW32FALSE_EXIT(Adjuster.EnablePrivileges()); IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0, KEY_ALL_ACCESS, hkAllInstallInfo)); IFW32FALSE_EXIT(SxspGenerateAssemblyNameInRegistry(pcAssemblyIdentity, buffRegKeyName));
//
// Generate the path of this target file. This could move into SxspGenerateSxsPath,
// but that's really gross looking and error prone... the last thing I want to do
// is break it..
//
IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffTargetFile)); IFW32FALSE_EXIT(buffTargetFile.Win32AppendPathElement( REGISTRY_BACKUP_ROOT_DIRECTORY_NAME, REGISTRY_BACKUP_ROOT_DIRECTORY_NAME_CCH ));
//
// Ensure the target path is there, don't fail if the path exists
//
IFW32FALSE_EXIT_UNLESS2( CreateDirectoryW(buffTargetFile, NULL), { ERROR_ALREADY_EXISTS }, fTempFlag ); IFW32FALSE_EXIT(buffTargetFile.Win32AppendPathElement(buffRegKeyName));
//
// Now open the target subkey
//
IFW32FALSE_EXIT(hkAllInstallInfo.OpenSubKey( hkAssemblyKey, buffRegKeyName, KEY_ALL_ACCESS, REG_OPTION_BACKUP_RESTORE ) );
//
// Ensure the target path is there
//
//
// And blort it out
//
DeleteFileW(buffTargetFile); IFW32FALSE_EXIT(hkAssemblyKey.Save(buffTargetFile));
IFW32FALSE_EXIT(Adjuster.DisablePrivileges());
FN_EPILOG }
//
// BUGBUG: The BBT folk need the 'codebase' key to be at the top level.
// Why we're shipping metadata that's only required for an internal
// build tool is beyond my meager understanding.
// - jonwis 07/11/2002
//
#define SXS_BBT_REG_HACK (TRUE)
BOOL SxspAddAssemblyInstallationInfo( DWORD dwFlags, IN CAssemblyRecoveryInfo& AssemblyInfo, IN const CCodebaseInformation& rcCodebase ) /*++
Called by SxsInstallAssemblyW to add the codebase and prompt information to the registry for future use with SxspGetAssemblyInstallationInfoW.
--*/ { FN_PROLOG_WIN32
CFusionRegKey hkAllInstallationInfo; CFusionRegKey hkSingleAssemblyInfo; CStringBuffer buffRegKeyName; const CSecurityMetaData &rcsmdAssemblySecurityData = AssemblyInfo.GetSecurityInformation(); const CBaseStringBuffer &rcbuffAssemblyIdentity = rcsmdAssemblySecurityData.GetTextualIdentity(); DWORD dwDisposition;
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_INSTALLATION, "SXS: %s - starting\n", __FUNCTION__);
PARAMETER_CHECK((dwFlags & ~(SXSP_ADD_ASSEMBLY_INSTALLATION_INFO_FLAG_REFRESH)) == 0);
//
// Create or open the top-level key - take our name and append the
// key to it.
//
IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0, KEY_CREATE_SUB_KEY, hkAllInstallationInfo));
//
// Convert back to an identity so we can figure out where to install this data to
//
IFW32FALSE_EXIT(::SxspGenerateAssemblyNameInRegistry(rcbuffAssemblyIdentity, buffRegKeyName));
IFW32FALSE_EXIT( hkAllInstallationInfo.OpenOrCreateSubKey( hkSingleAssemblyInfo, buffRegKeyName, KEY_WRITE | KEY_READ | FUSIONP_KEY_WOW64_64KEY, 0, &dwDisposition));
ULONG WriteRegFlags; WriteRegFlags = 0; if (dwFlags & SXSP_ADD_ASSEMBLY_INSTALLATION_INFO_FLAG_REFRESH) { WriteRegFlags |= SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_TO_REGISTRY_KEY_FLAG_REFRESH; #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INSTALLATION, "SXS.DLL: %s - propping recovery flag to WritePrimaryAssemblyInfoToRegistryKey\n", __FUNCTION__); #endif
}
IFW32FALSE_EXIT(AssemblyInfo.PrepareForWriting()); IFW32FALSE_EXIT(AssemblyInfo.WritePrimaryAssemblyInfoToRegistryKey(WriteRegFlags, hkSingleAssemblyInfo)); IFW32FALSE_EXIT(AssemblyInfo.WriteSecondaryAssemblyInfoIntoRegistryKey( hkSingleAssemblyInfo ) );
//
// If we got this far, then we've got all the right moves.
//
//
// Are we still being broken for BBT? If so, then write the codebase generated for this
// installation back into the "Codebase" value of the single-assembly-info key. This
// ensures last-installer-wins semantic.
//
#if SXS_BBT_REG_HACK
if ((dwFlags & SXSP_ADD_ASSEMBLY_INSTALLATION_INFO_FLAG_REFRESH) == 0) { IFW32FALSE_EXIT(hkSingleAssemblyInfo.SetValue( CSMD_TOPLEVEL_CODEBASE, rcCodebase.GetCodebase())); } else { #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INSTALLATION, "SXS.DLL: %s - refresh/wfp/sfc not writing top level codebase\n", __FUNCTION__); #endif
} #endif
FN_EPILOG }
BOOL SxspLookForCDROMLocalPathForURL( IN const CBaseStringBuffer &rbuffURL, OUT CBaseStringBuffer &rbuffLocalPath ) { FN_PROLOG_WIN32
BOOL fFoundMedia = FALSE; CSmallStringBuffer sbIdentData1, sbIdentData2; CSmallStringBuffer buffDriveStrings; CSmallStringBuffer buffTemp; CStringBufferAccessor acc; SIZE_T HeadLength = 0; PCWSTR wcsCursor = NULL; ULONG ulSerialNumber = 0; WCHAR rgchVolumeName[MAX_PATH]; SIZE_T i; PCWSTR pszSource = rbuffURL; SIZE_T cchTemp;
enum CDRomSearchType { CDRST_Tagfile, CDRST_SerialNumber, CDRST_VolumeName } SearchType;
rbuffLocalPath.Clear();
#define ENTRY(_x, _st) { _x, NUMBER_OF(_x) - 1, _st },
const static struct { PCWSTR pszPrefix; SIZE_T cchPrefix; CDRomSearchType SearchType; } s_rgMap[] = { ENTRY(L"tagfile", CDRST_Tagfile) ENTRY(L"serialnumber", CDRST_SerialNumber) ENTRY(L"volumename", CDRST_VolumeName) };
#undef ENTRY
SearchType = CDRST_Tagfile; // arbitrary initialization to make compiler happy about init only
// occurring in the for loop
for (i=0; i<NUMBER_OF(s_rgMap); i++) { if (::_wcsnicmp(s_rgMap[i].pszPrefix, rbuffURL, s_rgMap[i].cchPrefix) == 0) { HeadLength = s_rgMap[i].cchPrefix; SearchType = s_rgMap[i].SearchType; break; } }
// If it wasn't in the map, it's a bogus cdrom: url so we just skip it.
if (i == NUMBER_OF(s_rgMap)) { #if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - no prefix found, skipping CDROM Drive %ls\n", __FUNCTION__, static_cast<PCWSTR>(rbuffURL)); #endif
FN_SUCCESSFUL_EXIT(); }
//
// Get the type of identifier here, and then move the cursor past them and
// the slashes in the url.
//
pszSource += HeadLength; pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
//
// Spin past slashes, assign chunklets
//
IFW32FALSE_EXIT(sbIdentData1.Win32Assign(pszSource, wcscspn(pszSource, CUnicodeCharTraits::PathSeparators()))); pszSource += sbIdentData1.Cch(); pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators());
//
// If this is a tagfile, also get another blobbet of data off the string
//
if (SearchType == CDRST_Tagfile) { IFW32FALSE_EXIT(sbIdentData2.Win32Assign(pszSource, wcscspn(pszSource, CUnicodeCharTraits::PathSeparators()))); pszSource += sbIdentData2.Cch(); pszSource += wcsspn(pszSource, CUnicodeCharTraits::PathSeparators()); } else if (SearchType == CDRST_SerialNumber) { IFW32FALSE_EXIT(CFusionParser::ParseULONG( ulSerialNumber, sbIdentData1, sbIdentData1.Cch(), 16)); }
// Find the CDROM drives...
IFW32ZERO_ORIGINATE_AND_EXIT(cchTemp = ::GetLogicalDriveStringsW(0, NULL)); IFW32FALSE_EXIT(buffDriveStrings.Win32ResizeBuffer(cchTemp + 1, eDoNotPreserveBufferContents));
acc.Attach(&buffDriveStrings);
IFW32ZERO_ORIGINATE_AND_EXIT( ::GetLogicalDriveStringsW( acc.GetBufferCchAsDWORD(), acc));
acc.Detach();
wcsCursor = buffDriveStrings;
//
// Look at all the found drive letters
//
while ((wcsCursor != NULL) && (wcsCursor[0] != L'\0') && !fFoundMedia) { DWORD dwSerialNumber; const UINT uiDriveType = ::GetDriveTypeW(wcsCursor);
if (uiDriveType != DRIVE_CDROM) { wcsCursor += (::wcslen(wcsCursor) + 1); continue; }
CSetErrorMode sem(SEM_FAILCRITICALERRORS); bool fNotReady;
IFW32FALSE_ORIGINATE_AND_EXIT_UNLESS2( ::GetVolumeInformationW( wcsCursor, rgchVolumeName, NUMBER_OF(rgchVolumeName), &dwSerialNumber, NULL, NULL, NULL, 0), LIST_2(ERROR_NOT_READY, ERROR_CRC), fNotReady);
if (fNotReady) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - CDROM Drive %ls has no media present or had read errors; skipping\n", __FUNCTION__, wcsCursor);
// skip past this drive
wcsCursor += (::wcslen(wcsCursor) + 1); continue; }
switch (SearchType) { case CDRST_Tagfile: { CFusionFile FileHandle; CStringBufferAccessor acc; DWORD dwTextLength; bool fNoFile; CHAR rgchBuffer[32];
IFW32FALSE_EXIT_UNLESS2( FileHandle.Win32CreateFile( sbIdentData1, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING), LIST_3(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_NOT_READY), fNoFile);
if (fNoFile) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - CDROM Drive %ls could not open tag file \"%ls\"; skipping\n", __FUNCTION__, wcsCursor, static_cast<PCWSTR>(sbIdentData1));
// skip past this drive
wcsCursor += (::wcslen(wcsCursor) + 1); continue; }
buffTemp.Clear();
for (;;) { IFW32FALSE_ORIGINATE_AND_EXIT( ::ReadFile( FileHandle, rgchBuffer, sizeof(rgchBuffer), &dwTextLength, NULL));
IFW32FALSE_EXIT(buffTemp.Win32Append(rgchBuffer, dwTextLength));
if ((dwTextLength != sizeof(rgchBuffer)) || (buffTemp.Cch() > sbIdentData2.Cch())) break; }
fFoundMedia = (::FusionpCompareStrings(buffTemp, sbIdentData2, true) == 0);
break; } case CDRST_SerialNumber: fFoundMedia = (dwSerialNumber == ulSerialNumber); break;
case CDRST_VolumeName: fFoundMedia = (::FusionpCompareStrings(rgchVolumeName, ::wcslen(rgchVolumeName), sbIdentData1, true) == 0); break;
default: INTERNAL_ERROR_CHECK(false); break; }
if (!fFoundMedia) wcsCursor += ::wcslen(wcsCursor) + 1; }
if (fFoundMedia) { IFW32FALSE_EXIT(buffTemp.Win32Assign(wcsCursor, ::wcslen(wcsCursor))); IFW32FALSE_EXIT(buffTemp.Win32AppendPathElement(pszSource, ::wcslen(pszSource))); IFW32FALSE_EXIT(rbuffLocalPath.Win32Assign(buffTemp)); }
FN_EPILOG }
BOOL SxspResolveWinSourceMediaURL( const CBaseStringBuffer &rbuffCodebaseInfo, CBaseStringBuffer &rbuffLocalPath ) { FN_PROLOG_WIN32
CSmallStringBuffer buffWindowsInstallSource; CSmallStringBuffer buffLocalPathTemp; #if DBG
CSmallStringBuffer buffLocalPathCodebasePrefix; #endif
DWORD dwWin32Error; DWORD dwFileAttributes;
const static PCWSTR AssemblySourceStrings[] = { WINSXS_INSTALL_SVCPACK_REGKEY, WINSXS_INSTALL_SOURCEPATH_REGKEY };
SIZE_T iWhichSource = 0; bool fFoundCodebase = false; CFusionRegKey hkSetupInfo; DWORD dwWasFromCDRom = 0;
rbuffLocalPath.Clear();
IFREGFAILED_ORIGINATE_AND_EXIT( ::RegOpenKeyExW( HKEY_LOCAL_MACHINE, WINSXS_INSTALL_SOURCE_BASEDIR, 0, KEY_READ | FUSIONP_KEY_WOW64_64KEY, &hkSetupInfo));
if (!::FusionpRegQueryDwordValueEx( 0, hkSetupInfo, WINSXS_INSTALL_SOURCE_IS_CDROM, &dwWasFromCDRom)) { dwWasFromCDRom = 0; }
for (iWhichSource = 0; (!fFoundCodebase) && iWhichSource < NUMBER_OF(AssemblySourceStrings); iWhichSource++) { IFW32FALSE_EXIT( ::FusionpRegQuerySzValueEx( FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING, hkSetupInfo, AssemblySourceStrings[iWhichSource], buffWindowsInstallSource));
//
// This really _really_ should not be empty. If it is, then someone
// went and fiddled with the registry on us.
//
if (buffWindowsInstallSource.Cch() == 0) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - skipping use of source string \"%ls\" in registry because either missing or null value\n", __FUNCTION__, AssemblySourceStrings[iWhichSource]);
continue; }
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - WFP probing windows source location \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(buffWindowsInstallSource));
//
// If this was from a CD, then spin through the list of CD's in the system
// and see if we can match the codebase against the root dir of the CD
//
if (dwWasFromCDRom) { CSmallStringBuffer buffDriveStrings; CStringBufferAccessor acc; PCWSTR pszCursor; SIZE_T cchTemp;
IFW32ZERO_EXIT(cchTemp = ::GetLogicalDriveStringsW(0, NULL)); IFW32FALSE_EXIT(buffDriveStrings.Win32ResizeBuffer(cchTemp + 1, eDoNotPreserveBufferContents));
acc.Attach(&buffDriveStrings);
IFW32ZERO_EXIT( ::GetLogicalDriveStringsW( acc.GetBufferCchAsDWORD(), acc.GetBufferPtr()));
acc.Detach();
pszCursor = buffDriveStrings;
while (pszCursor[0] != L'\0') { if (::GetDriveTypeW(pszCursor) == DRIVE_CDROM) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - Scanning CDROM drive \"%ls\" for windows source media\n", __FUNCTION__, pszCursor);
IFW32FALSE_EXIT(buffLocalPathTemp.Win32Assign(pszCursor, ::wcslen(pszCursor))); IFW32FALSE_EXIT(buffLocalPathTemp.Win32AppendPathElement(rbuffCodebaseInfo));
IFW32FALSE_EXIT( ::SxspGetFileAttributesW( buffLocalPathTemp, dwFileAttributes, dwWin32Error, 4, ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_NOT_READY, ERROR_ACCESS_DENIED));
if (dwWin32Error == ERROR_SUCCESS) { #if DBG
buffLocalPathCodebasePrefix.Win32Assign(pszCursor, ::wcslen(pszCursor)); #endif
fFoundCodebase = true; break; }
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - Could not find key file \"%ls\"; moving on to next drive\n", __FUNCTION__, static_cast<PCWSTR>(buffLocalPathTemp)); }
pszCursor += ::wcslen(pszCursor) + 1; }
if (fFoundCodebase) break;
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - Could not find any CDROMs with key file \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(rbuffCodebaseInfo));
buffLocalPathTemp.Clear(); } else { //
// This wasn't a CD-rom installation, so prepend the install source path to
// the string that was passed in.
//
IFW32FALSE_EXIT(buffLocalPathTemp.Win32Assign(buffWindowsInstallSource)); IFW32FALSE_EXIT(buffLocalPathTemp.Win32AppendPathElement(rbuffCodebaseInfo));
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - trying to access windows source file \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(buffLocalPathTemp));
IFW32FALSE_EXIT( ::SxspGetFileAttributesW( buffLocalPathTemp, dwFileAttributes, dwWin32Error, 4, ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_NOT_READY, ERROR_ACCESS_DENIED));
if (dwWin32Error == ERROR_SUCCESS) { #if DBG
buffLocalPathCodebasePrefix.Win32Assign(buffWindowsInstallSource); #endif
fFoundCodebase = true; break; }
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - Unable to find key file \"%ls\"; win32 status = %lu\n", __FUNCTION__, static_cast<PCWSTR>(buffLocalPathTemp), dwWin32Error);
buffLocalPathTemp.Clear(); } if (fFoundCodebase) break; }
IFW32FALSE_EXIT(rbuffLocalPath.Win32Assign(buffLocalPathTemp));
#if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - buffLocalPathCodebasePrefix \"%ls\" and returning rbuffLocalPath \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(buffLocalPathCodebasePrefix), static_cast<PCWSTR>(rbuffLocalPath) ); #endif
FN_EPILOG }
#define SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI (0x00000001)
BOOL SxspRepeatUntilLocalPathAvailable( IN ULONG Flags, IN const CAssemblyRecoveryInfo &rRecoveryInfo, IN const CCodebaseInformation *pCodeBaseIn, IN SxsWFPResolveCodebase CodebaseType, IN const CBaseStringBuffer &rbuffCodebaseInfo, OUT CBaseStringBuffer &rbuffLocalPath, OUT BOOL &fRetryPressed ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
BOOL fCodebaseOk = FALSE; CSmallStringBuffer buffFinalLocalPath; DWORD dwAttributes;
PARAMETER_CHECK(pCodeBaseIn != NULL);
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - got codebase \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(pCodeBaseIn->GetCodebase()));
rbuffLocalPath.Clear(); fRetryPressed = FALSE;
PARAMETER_CHECK( (CodebaseType == CODEBASE_RESOLVED_URLHEAD_FILE) || (CodebaseType == CODEBASE_RESOLVED_URLHEAD_WINSOURCE) || (CodebaseType == CODEBASE_RESOLVED_URLHEAD_CDROM));
PARAMETER_CHECK((Flags & ~(SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI)) == 0);
#if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s() CodebaseType : %s (0x%lx)\n", __FUNCTION__, (CodebaseType == CODEBASE_RESOLVED_URLHEAD_FILE) ? "file" : (CodebaseType == CODEBASE_RESOLVED_URLHEAD_WINSOURCE) ? "winsource" : (CodebaseType == CODEBASE_RESOLVED_URLHEAD_CDROM) ? "cdrom" : "", static_cast<ULONG>(CodebaseType) ); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s() rbuffCodebaseInfo : %ls\n", __FUNCTION__, static_cast<PCWSTR>(rbuffCodebaseInfo) ); #endif
for (;;) { bool fNotFound = true;
// First, let's see if we have to do any trickery.
switch (CodebaseType) { case CODEBASE_RESOLVED_URLHEAD_CDROM: IFW32FALSE_EXIT( ::SxspLookForCDROMLocalPathForURL( rbuffCodebaseInfo, buffFinalLocalPath));
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - cdrom: URL resolved to \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(buffFinalLocalPath));
break;
case CODEBASE_RESOLVED_URLHEAD_WINSOURCE: IFW32FALSE_EXIT( ::SxspResolveWinSourceMediaURL( rbuffCodebaseInfo, buffFinalLocalPath));
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - windows source URL resolved to \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(buffFinalLocalPath));
break;
case CODEBASE_RESOLVED_URLHEAD_FILE: IFW32FALSE_EXIT(buffFinalLocalPath.Win32Assign(rbuffCodebaseInfo));
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - file: URL resolved to \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(buffFinalLocalPath));
break; }
if (buffFinalLocalPath.Cch() != 0) { DWORD dwWin32Error = NO_ERROR;
IFW32FALSE_EXIT( ::SxspGetFileAttributesW( buffFinalLocalPath, dwAttributes, dwWin32Error, 5, ERROR_PATH_NOT_FOUND, ERROR_FILE_NOT_FOUND, ERROR_BAD_NET_NAME, ERROR_BAD_NETPATH, ERROR_ACCESS_DENIED));
if (dwWin32Error == ERROR_SUCCESS) break; }
if ((Flags & SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI) == 0) { buffFinalLocalPath.Clear(); break; }
//
// Nope, didn't find it (or the codebase specified is gone. Ask the user
// to insert media or whatnot so we can find it again.
//
if (fNotFound) { CSXSMediaPromptDialog PromptBox; CSXSMediaPromptDialog::DialogResults result;
IFW32FALSE_EXIT(PromptBox.Initialize(pCodeBaseIn));
IFW32FALSE_EXIT(PromptBox.ShowSelf(result));
if (result == CSXSMediaPromptDialog::DialogCancelled) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - user cancelled media prompt dialog\n", __FUNCTION__);
buffFinalLocalPath.Clear(); break; }
// Otherwise, try again!
fRetryPressed = TRUE; break; } }
IFW32FALSE_EXIT(rbuffLocalPath.Win32Assign(buffFinalLocalPath));
#if DBG
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - returning rbuffLocalPath \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(rbuffLocalPath) ); #endif
FN_EPILOG }
BOOL SxspAskDarwinDoReinstall( IN PCWSTR buffLocalPath) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess);
UINT (WINAPI * pfnMsiProvideAssemblyW)( LPCWSTR wzAssemblyName, LPCWSTR szAppContext, DWORD dwInstallMode, DWORD dwUnused, LPWSTR lpPathBuf, DWORD *pcchPathBuf) = NULL;
INSTALLUILEVEL (WINAPI * pfnMsiSetInternalUI)( INSTALLUILEVEL dwUILevel, // UI level
HWND *phWnd) // handle of owner window
= NULL;
INSTALLUILEVEL OldInstallUILevel; CDynamicLinkLibrary hMSIDll;
//
// We should hoist the load/unload out of the loop.
//
IFW32FALSE_ORIGINATE_AND_EXIT(hMSIDll.Win32LoadLibrary(L"msi.dll")); IFW32NULL_ORIGINATE_AND_EXIT(hMSIDll.Win32GetProcAddress("MsiProvideAssemblyW", &pfnMsiProvideAssemblyW)); IFW32NULL_ORIGINATE_AND_EXIT(hMSIDll.Win32GetProcAddress("MsiSetInternalUI", &pfnMsiSetInternalUI));
// No real failure from this API...
OldInstallUILevel = (*pfnMsiSetInternalUI)(INSTALLUILEVEL_NONE, NULL); IFREGFAILED_ORIGINATE_AND_EXIT((*pfnMsiProvideAssemblyW)(buffLocalPath, NULL, REINSTALLMODE_FILEREPLACE, MSIASSEMBLYINFO_WIN32ASSEMBLY, NULL, NULL)); // and restore it
(*pfnMsiSetInternalUI)(OldInstallUILevel, NULL);
fSuccess = TRUE; Exit: return fSuccess; }
BOOL SxspRecoverAssembly( IN const CAssemblyRecoveryInfo &AsmRecoveryInfo, IN CRecoveryCopyQueue *pRecoveryQueue, OUT SxsRecoveryResult &rStatus ) { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); CSmallStringBuffer sbPerTypeCodebaseString; SxsWFPResolveCodebase CodebaseType; SXS_INSTALLW Install = { sizeof(SXS_INSTALLW) }; Install.dwFlags |= SXS_INSTALL_FLAG_REPLACE_EXISTING; bool fNotFound = false; CCodebaseInformationList::ConstIterator CodebaseIterator; const CCodebaseInformationList& CodebaseList = AsmRecoveryInfo.GetCodeBaseList(); ULONG RetryNumber = 0; BOOL fRetryPressed = FALSE; ULONG RetryPressedCount = 0;
rStatus = Recover_Unknown;
//
// As long as they hit retry, keep putting up the ui, cycling through the paths.
//
for (RetryNumber = 0 ; (rStatus != Recover_OK) && RetryNumber != 3 ; RetryNumber += (fRetryPressed ? 0 : 1)) { for (CodebaseIterator = CodebaseList.Begin() ; (rStatus != Recover_OK) && CodebaseIterator != CodebaseList.End() ; ++CodebaseIterator) { fRetryPressed = FALSE;
//
// eg:
// xcopy /fiver \\winbuilds\release\main\usa\latest.idw\x86fre\pro\i386 x:\blah\blah\i386
//
// buffLocalPath x:\blah\blah\i386\asms\1000\msft\windows\gdiplus\gdiplus.man
// buffLocalPathCodebasePrefix x:\blah\blah
// buffCodebaseMetaPrefix x-ms-windows://
// buffCodebaseTail \i386\asms\1000\msft\windows\gdiplus\gdiplus.man.
//
// Install.lpCodeBaseUrl x:\blah\blah
// Install.lpManifestPath x:\blah\blah\i386\asms\1000\msft\windows\gdiplus\gdiplus.man
//
CSmallStringBuffer buffLocalPath; CSmallStringBuffer buffCodebaseTail;
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - beginning recovery of assembly directory \"%ls\"\n", __FUNCTION__, static_cast<PCWSTR>(AsmRecoveryInfo.GetAssemblyDirectoryName()));
//
// Go try and get the codebase resolved
//
rStatus = Recover_Unknown;
IFW32FALSE_EXIT( ::SxspDetermineCodebaseType( // this should be cached in m_CodebaseInfo.
CodebaseIterator->GetCodebase(), CodebaseType, &buffCodebaseTail)); if (CodebaseType == CODEBASE_RESOLVED_URLHEAD_UNKNOWN) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - Couldn't figure out what to do with codebase \"%ls\"; skipping\n", __FUNCTION__, static_cast<PCWSTR>(CodebaseIterator->GetCodebase()));
rStatus = Recover_SourceMissing; continue; }
if (!::SxspRepeatUntilLocalPathAvailable( (RetryNumber == 2 && (CodebaseIterator == (CodebaseList.Begin() + (RetryPressedCount % CodebaseList.GetSize())))) ? SXSP_REPEAT_UNTIL_LOCAL_PATH_AVAILABLE_FLAG_UI : 0, AsmRecoveryInfo, &*CodebaseIterator, CodebaseType, buffCodebaseTail, buffLocalPath, fRetryPressed)) { continue; } if (fRetryPressed) RetryPressedCount += 1;
if (buffLocalPath.Cch() == 0 ) { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - unable to resolve codebase \"%ls\" to a local path\n", __FUNCTION__, static_cast<PCWSTR>(CodebaseIterator->GetCodebase()));
rStatus = Recover_ManifestMissing; continue; }
Install.lpManifestPath = buffLocalPath; Install.dwFlags |= SXS_INSTALL_FLAG_REFRESH;
IFW32FALSE_EXIT_UNLESS2( ::SxsInstallW(&Install), LIST_2(ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND), fNotFound);
if (fNotFound) { rStatus = Recover_ManifestMissing; // may also be a file in the assembly missing
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - installation from %ls failed with win32 last error = %ld\n", __FUNCTION__, static_cast<PCWSTR>(buffLocalPath), ::FusionpGetLastWin32Error()); continue; } else { rStatus = Recover_OK; break; } }
//
// Last chance - try MSI reinstallation
//
if ( rStatus != Recover_OK ) { BOOL fMsiKnowsAssembly = FALSE; const CBaseStringBuffer &rcbuffIdentity = AsmRecoveryInfo.GetSecurityInformation().GetTextualIdentity(); IFW32FALSE_EXIT(SxspDoesMSIStillNeedAssembly( rcbuffIdentity, fMsiKnowsAssembly));
if ( fMsiKnowsAssembly && ::SxspAskDarwinDoReinstall(rcbuffIdentity)) { rStatus = Recover_OK; break; } } } fSuccess = TRUE; Exit: CSxsPreserveLastError ple;
//
// Here we have to check something. If the assembly wasn't able to be reinstalled,
// then we do the following:
//
// 1. Rename away old assembly directory to .old or similar
// 2. Log a message to the event log
//
DWORD dwMessageToPrint = 0;
if (rStatus != Recover_OK) { dwMessageToPrint = MSG_SXS_SFC_ASSEMBLY_RESTORE_FAILED; } else { dwMessageToPrint = MSG_SXS_SFC_ASSEMBLY_RESTORE_SUCCESS; }
::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s: Recovery of assembly \"%ls\" resulted in fSuccess=%d rStatus=%d\n", __FUNCTION__, static_cast<PCWSTR>(AsmRecoveryInfo.GetAssemblyDirectoryName()), fSuccess, rStatus);
::FusionpLogError( dwMessageToPrint, CUnicodeString(AsmRecoveryInfo.GetAssemblyDirectoryName()));
ple.Restore();
return fSuccess; }
|