windows-nt/Source/XPSP1/NT/base/win32/fusion/sxs/recover.cpp

1102 lines
33 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
#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;
}