#include "stdinc.h" #include "fusionheap.h" #include "fusionbuffer.h" #include "fusionparser.h" #include "parsing.h" #include "strongname.h" #include "CAssemblyRecoveryInfo.h" #include "hashfile.h" #include "FusionHandle.h" #include "Util.h" #include "Sxsp.h" #include "recover.h" SMARTTYPE(CAssemblyRecoveryInfo); static BOOL pMapShortNameToAssembly( IN OUT CBaseStringBuffer &rbuffAssemblyName, IN const CRegKey &hkInstallInfoKey, OUT CRegKey &hRequestedAsm, IN REGSAM rsReadRights = KEY_READ ) { FN_PROLOG_WIN32 CSmallStringBuffer buffManifestName; CSmallStringBuffer buffCatalogName; DWORD dwIndex = 0; PCWSTR pszValueName = NULL; hRequestedAsm = CRegKey::GetInvalidValue(); IFW32FALSE_EXIT(buffManifestName.Win32Assign(rbuffAssemblyName)); IFW32FALSE_EXIT(buffManifestName.Win32Append(L".man", 4)); IFW32FALSE_EXIT(buffCatalogName.Win32Assign(rbuffAssemblyName)); IFW32FALSE_EXIT(buffCatalogName.Win32Append(L".cat", 4)); // // Look for this under the CSMD_TOPLEVEL_SHORTNAME first // for (;;) { CStringBuffer buffKeyName; CSmallStringBuffer buffAcquiredShortName; BOOL fTempBoolean; CRegKey hAsm; IFW32FALSE_EXIT(hkInstallInfoKey.EnumKey( dwIndex++, buffKeyName, NULL, &fTempBoolean)); if (fTempBoolean) break; IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey(hAsm, buffKeyName, rsReadRights)); // // Get the value of the key // IFW32FALSE_EXIT( ::FusionpRegQuerySzValueEx( FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING, hAsm, CSMD_TOPLEVEL_SHORTNAME, buffAcquiredShortName)); // // If the key was there to be read: // if (buffAcquiredShortName.Cch() != 0) { if (::FusionpCompareStrings( buffAcquiredShortName, buffAcquiredShortName.Cch(), rbuffAssemblyName, rbuffAssemblyName.Cch(), true) == 0) { IFW32FALSE_EXIT(rbuffAssemblyName.Win32Assign(buffKeyName)); IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey( hRequestedAsm, buffKeyName, rsReadRights ) ); break; } } // // Get the value of the key // IFW32FALSE_EXIT( ::FusionpRegQuerySzValueEx( FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING, hAsm, CSMD_TOPLEVEL_SHORTMANIFEST, buffAcquiredShortName)); // // If the key was there to be read: // if (buffAcquiredShortName.Cch() != 0) { if (::FusionpCompareStrings( buffAcquiredShortName, buffAcquiredShortName.Cch(), buffManifestName, buffManifestName.Cch(), true) == 0) { IFW32FALSE_EXIT(rbuffAssemblyName.Win32Assign(buffKeyName)); IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey(hRequestedAsm, buffKeyName, rsReadRights)); break; } } // // Get the value of the key // IFW32FALSE_EXIT( ::FusionpRegQuerySzValueEx( FUSIONP_REG_QUERY_SZ_VALUE_EX_MISSING_GIVES_NULL_STRING, hAsm, CSMD_TOPLEVEL_SHORTCATALOG, buffAcquiredShortName)); // // If the key was there to be read: // if (buffAcquiredShortName.Cch() != 0) { if (::FusionpCompareStrings( buffAcquiredShortName, buffAcquiredShortName.Cch(), buffCatalogName, buffCatalogName.Cch(), true) == 0) { IFW32FALSE_EXIT(rbuffAssemblyName.Win32Assign(buffKeyName)); IFW32FALSE_EXIT(hkInstallInfoKey.OpenSubKey(hRequestedAsm, buffKeyName, rsReadRights)); break; } } } FN_EPILOG } BOOL CAssemblyRecoveryInfo::ResolveCDRomURL( PCWSTR pszSource, CBaseStringBuffer &rsbDestination ) const { BOOL fSuccess = TRUE, fFoundMedia = FALSE; CStringBuffer sbIdentKind, sbIdentData1, sbIdentData2; CSmallStringBuffer buffDriveStrings; CStringBufferAccessor acc; SIZE_T HeadLength; PCWSTR wcsCursor; ULONG ulSerialNumber = 0; WCHAR rgchVolumeName[MAX_PATH]; CDRomSearchType SearchType; FN_TRACE_WIN32(fSuccess); PARAMETER_CHECK(pszSource != NULL); if (!_wcsnicmp(pszSource, URLHEAD_CDROM_TYPE_TAG, URLHEAD_LENGTH_CDROM_TYPE_TAG)) { HeadLength = URLHEAD_LENGTH_CDROM_TYPE_TAG; SearchType = CDRST_Tagfile; } else if (!_wcsnicmp( pszSource, URLHEAD_CDROM_TYPE_SERIALNUMBER, URLHEAD_LENGTH_CDROM_TYPE_SERIALNUMBER)) { HeadLength = URLHEAD_LENGTH_CDROM_TYPE_SERIALNUMBER; SearchType = CDRST_SerialNumber; } else if (!_wcsnicmp( pszSource, URLHEAD_CDROM_TYPE_VOLUMENAME, URLHEAD_LENGTH_CDROM_TYPE_VOLUMENAME)) { HeadLength = URLHEAD_LENGTH_CDROM_TYPE_VOLUMENAME; SearchType = CDRST_VolumeName; } else { ::FusionpSetLastWin32Error(ERROR_INVALID_PARAMETER); goto Exit; } // // Get the type of identifier here, and then move the cursor past them and // the slashes in the url. // IFW32FALSE_EXIT(sbIdentKind.Win32Assign(pszSource, HeadLength)); 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)); } // // Now let's do ... interesting ... things to the CD-roms. // IFW32FALSE_EXIT(buffDriveStrings.Win32ResizeBuffer(GetLogicalDriveStringsW(0, NULL) + 1, eDoNotPreserveBufferContents)); acc.Attach(&buffDriveStrings); IFW32FALSE_ORIGINATE_AND_EXIT( ::GetLogicalDriveStringsW( static_cast(acc.GetBufferCch()), acc)); acc.Detach(); wcsCursor = buffDriveStrings; // // Look at all the found drive letters // while (wcsCursor && *wcsCursor && !fFoundMedia) { DWORD dwSerialNumber; const DWORD dwDriveType = ::GetDriveTypeW(wcsCursor); if (dwDriveType == DRIVE_CDROM) { // // I argue that a failure in GetVolumeInformationW isn't "bad enough" // to kill the call to this function. Instead, it should just skip // the check of the failed drive letter, as it does here. // if(!::GetVolumeInformationW( wcsCursor, rgchVolumeName, NUMBER_OF(rgchVolumeName), &dwSerialNumber, NULL, NULL, NULL, 0)) { #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s() - Failed getting volume information for drive letter %ls (Win32 Error = %ld), skipping\n", __FUNCTION__, wcsCursor, ::FusionpGetLastWin32Error()); #endif continue; } switch (SearchType) { case CDRST_Tagfile: { CFusionFile FileHandle; CHAR chBuffer[MAX_PATH]; CStringBuffer sbContents; DWORD dwTextLength; if (FileHandle.Win32CreateFile(sbIdentData1, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)) { IFW32FALSE_ORIGINATE_AND_EXIT( ::ReadFile( FileHandle, chBuffer, NUMBER_OF(chBuffer), &dwTextLength, NULL)); IFW32FALSE_EXIT(sbContents.Win32Assign(chBuffer, dwTextLength)); fFoundMedia = !_wcsnicmp(sbContents, sbIdentData2, sbIdentData2.Cch()); } } break; case CDRST_SerialNumber: fFoundMedia = (dwSerialNumber == ulSerialNumber); break; case CDRST_VolumeName: fFoundMedia = (::FusionpStrCmpI(rgchVolumeName, sbIdentData1) == 0); break; default: break; } } if (!fFoundMedia) wcsCursor += ::wcslen(wcsCursor) + 1; } if (fFoundMedia) { IFW32FALSE_EXIT(rsbDestination.Win32Assign(wcsCursor, ::wcslen(wcsCursor))); IFW32FALSE_EXIT(rsbDestination.Win32AppendPathElement(pszSource, ::wcslen(pszSource))); } fSuccess = TRUE; Exit: // // Failure indicated by an empty destination. // if (!fSuccess) { rsbDestination.Clear(); } return fSuccess; } BOOL CAssemblyRecoveryInfo::ResolveWinSourceMediaURL( PCWSTR wcsUrlTrailer, CBaseStringBuffer &rsbDestination ) const { CStringBuffer buffWindowsInstallSource; const static PCWSTR AssemblySourceStrings[] = { WINSXS_INSTALL_SVCPACK_REGKEY, WINSXS_INSTALL_SOURCEPATH_REGKEY }; SIZE_T iWhichSource = 0; BOOL fSuccess = TRUE, fFoundCodebase = FALSE; CFusionRegKey hkSetupInfo; DWORD dwWasFromCDRom = 0; FN_TRACE_WIN32(fSuccess); PARAMETER_CHECK(wcsUrlTrailer != NULL); 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; } while (iWhichSource < NUMBER_OF(AssemblySourceStrings)) { 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. // ASSERT(buffWindowsInstallSource.Cch() != 0); if (buffWindowsInstallSource.Cch() == 0) { iWhichSource++; continue; } // // 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; DWORD dwSize; IFW32FALSE_EXIT( buffDriveStrings.Win32ResizeBuffer( dwSize = (::GetLogicalDriveStringsW(0, NULL) + 1), eDoNotPreserveBufferContents)); acc.Attach(&buffDriveStrings); ::GetLogicalDriveStringsW( static_cast(acc.GetBufferCch()), acc); acc.Detach(); pszCursor = buffDriveStrings; while (*pszCursor) { if (::GetDriveTypeW(pszCursor) == DRIVE_CDROM) { DWORD dwAttributes; DWORD dwWin32Error; IFW32FALSE_EXIT(rsbDestination.Win32Assign(pszCursor, ::wcslen(pszCursor))); IFW32FALSE_EXIT(rsbDestination.Win32AppendPathElement(wcsUrlTrailer, ::wcslen(wcsUrlTrailer))); IFW32FALSE_EXIT( ::SxspGetFileAttributesW( rsbDestination, dwAttributes, dwWin32Error, 3, ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, ERROR_NOT_READY, ERROR_ACCESS_DENIED)); if (dwWin32Error == ERROR_SUCCESS) { fFoundCodebase = TRUE; FN_SUCCESSFUL_EXIT(); } } pszCursor += ::wcslen(pszCursor) + 1; } } // // This wasn't a CD-rom installation, so prepend the install source path to // the string that was passed in. // else { IFW32FALSE_EXIT(rsbDestination.Win32Assign(buffWindowsInstallSource, buffWindowsInstallSource.Cch())); IFW32FALSE_EXIT(rsbDestination.Win32AppendPathElement(wcsUrlTrailer, ::wcslen(wcsUrlTrailer))); if (::GetFileAttributesW(rsbDestination) != -1) { fFoundCodebase = TRUE; fSuccess = TRUE; goto Exit; } } iWhichSource++; } fSuccess = TRUE; Exit: if (!fFoundCodebase) { rsbDestination.Clear(); } return fSuccess; } BOOL CAssemblyRecoveryInfo::AssociateWithAssembly( IN OUT CBaseStringBuffer &rsbSourceAssemblyName, bool &rfNoAssembly ) { // // First check the manifest that the assembly came out of. If it still exists, // then nifty, go and load the info from it. Otherwise, we should look in the // registry for information instead. // FN_PROLOG_WIN32 CFusionRegKey hInstallInfoKey; CFusionRegKey hRequestedAsm; rfNoAssembly = true; // // First attempt - try to load it directly off the root installation key. // if (!m_fLoadedAndReady) { IFW32FALSE_EXIT(this->Initialize()); IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey(0, KEY_READ, hInstallInfoKey)); IFW32FALSE_EXIT( hInstallInfoKey.OpenSubKey( hRequestedAsm, rsbSourceAssemblyName, KEY_READ)); // // This direct entry wasn't found, so let's go see if we can map it back to some // other assembly name using the shortname stuff. // if (hRequestedAsm == CRegKey::GetInvalidValue()) { IFW32FALSE_EXIT( ::pMapShortNameToAssembly( rsbSourceAssemblyName, // if this is a short name of an assembly, this function would set this to be real assembly name if match is found hInstallInfoKey, hRequestedAsm)); // // Still not found? Darn. // if (hRequestedAsm == CRegKey::GetInvalidValue()) FN_SUCCESSFUL_EXIT(); } IFW32FALSE_EXIT(this->m_sbAssemblyDirectoryName.Win32Assign(rsbSourceAssemblyName)); IFW32FALSE_EXIT(this->m_SecurityMetaData.LoadFromRegistryKey(hRequestedAsm)); this->m_fLoadedAndReady = TRUE; } // // Only set the "no assembly" if we were able to associate (before or // above.) // if ( this->m_fLoadedAndReady ) rfNoAssembly = false; FN_EPILOG } BOOL SxspDetermineCodebaseType( IN const CBaseStringBuffer &rcbuffUrlString, OUT SxsWFPResolveCodebase &rcbaseType, OUT CBaseStringBuffer *pbuffRemainder ) { FN_PROLOG_WIN32 PCWSTR pcwszStringTop = rcbuffUrlString; SIZE_T cch = rcbuffUrlString.Cch(); SIZE_T i; CSmallStringBuffer buffTemp; // may be used if we mangle the URL text more rcbaseType = CODEBASE_RESOLVED_URLHEAD_UNKNOWN; if (pbuffRemainder != NULL) pbuffRemainder->Clear(); #define ENTRY(_x) { URLHEAD_ ## _x, NUMBER_OF(URLHEAD_ ## _x) - 1, NUMBER_OF(URLHEAD_ ## _x) - 1, CODEBASE_RESOLVED_URLHEAD_ ## _x }, static const struct { PCWSTR pszPrefix; SIZE_T cchPrefix; SIZE_T cchAdvance; SxsWFPResolveCodebase cbaseType; } s_rgMap[] = { ENTRY(FILE) ENTRY(WINSOURCE) ENTRY(CDROM) ENTRY(HTTP) }; #undef ENTRY for (i=0; i 0) && (pcwszStringTop[0] == L'/')) { cch--; pcwszStringTop++; } if ((cch > 0) && (pcwszStringTop[0] == L'/')) { cch--; pcwszStringTop++; } if ((cch > 0) && (pcwszStringTop[0] == L'/')) { cch--; pcwszStringTop++; } } else if (rcbaseType == CODEBASE_RESOLVED_URLHEAD_HTTP) { // Hey, on Whistler, we have the WebDAV redirector, so // we can turn this URL into a UNC path! bool fGeneratedUNCPath = false; IFW32FALSE_EXIT(buffTemp.Win32Assign(L"\\\\", 2)); if (pcwszStringTop[0] == L'/') { if (pcwszStringTop[1] == L'/') { // http:// so far; the next thing must be a hostname! PCWSTR pszSlash = wcschr(pcwszStringTop + 2, L'/'); if (pszSlash != NULL) { // //foo/bar (http: removed earlier...) // pcwszStringTop == [0] // pszSlash == [5] // cch == 9 IFW32FALSE_EXIT(buffTemp.Win32Append(pcwszStringTop + 2, (pszSlash - pcwszStringTop) - 3)); IFW32FALSE_EXIT(buffTemp.Win32Append(L"\\", 1)); IFW32FALSE_EXIT(buffTemp.Win32Append(pszSlash + 1, cch - (pszSlash - pcwszStringTop) - 1)); fGeneratedUNCPath = true; } } } if (fGeneratedUNCPath) { // poof, it's a file path pcwszStringTop = buffTemp; cch = buffTemp.Cch(); rcbaseType = CODEBASE_RESOLVED_URLHEAD_FILE; } } } if (pbuffRemainder != NULL) { IFW32FALSE_EXIT( pbuffRemainder->Win32Assign(pcwszStringTop, cch)); } #if DBG { CUnicodeString a(rcbuffUrlString, rcbuffUrlString.Cch()); CUnicodeString b(rcbuffUrlString, (cch <= rcbuffUrlString.Cch()) ? (rcbuffUrlString.Cch() - cch) : 0); CUnicodeString c(pcwszStringTop, (cch <= ::wcslen(pcwszStringTop)) ? cch : 0); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - split \"%wZ\" into \"%wZ\" and \"%wZ\"\n", __FUNCTION__, &a, &b, &c); } #endif FN_EPILOG } BOOL CAssemblyRecoveryInfo::CopyValue(const CAssemblyRecoveryInfo& other) { BOOL bSuccess = FALSE; FN_TRACE_WIN32(bSuccess); if (&other != this) { IFW32FALSE_EXIT(m_sbAssemblyDirectoryName.Win32Assign(other.m_sbAssemblyDirectoryName)); IFW32FALSE_EXIT(m_SecurityMetaData.Initialize(other.m_SecurityMetaData)); m_fLoadedAndReady = other.m_fLoadedAndReady; } bSuccess = TRUE; Exit: if ( !bSuccess ) { this->m_fLoadedAndReady = FALSE; } return bSuccess; } BOOL CAssemblyRecoveryInfo::SetAssemblyIdentity( IN PCASSEMBLY_IDENTITY pcidAssembly ) { FN_PROLOG_WIN32 SIZE_T cbEncodedTextSize; SIZE_T cbActualSize; CStringBuffer sbTextualEncoding; IFW32FALSE_EXIT( ::SxsComputeAssemblyIdentityEncodedSize( 0, pcidAssembly, NULL, SXS_ASSEMBLY_IDENTITY_ENCODING_DEFAULTGROUP_TEXTUAL, &cbEncodedTextSize ) ); INTERNAL_ERROR_CHECK( ( cbEncodedTextSize % sizeof(WCHAR) ) == 0 ); IFW32FALSE_EXIT( sbTextualEncoding.Win32ResizeBuffer( ( cbEncodedTextSize / sizeof(WCHAR) ) + 1, eDoNotPreserveBufferContents ) ); { CStringBufferAccessor sba; sba.Attach( &sbTextualEncoding ); IFW32FALSE_EXIT( ::SxsEncodeAssemblyIdentity( 0, pcidAssembly, NULL, SXS_ASSEMBLY_IDENTITY_ENCODING_DEFAULTGROUP_TEXTUAL, sba.GetBufferCb(), sba.GetBufferPtr(), &cbActualSize ) ); INTERNAL_ERROR_CHECK( cbActualSize == cbEncodedTextSize ); sba.GetBufferPtr()[cbActualSize / sizeof(WCHAR)] = L'\0'; } IFW32FALSE_EXIT( this->SetAssemblyIdentity( sbTextualEncoding ) ); FN_EPILOG } BOOL CAssemblyRecoveryInfo::PrepareForWriting() { FN_PROLOG_WIN32 CSxsPointerWithNamedDestructor pIdentity; CSmallStringBuffer buffAsmRoot; CStringBuffer buffTemp1; CStringBuffer buffTemp2; const CBaseStringBuffer& OurTextualIdentity = m_SecurityMetaData.GetTextualIdentity(); BOOL fIsPolicy; DWORD dwWin32Error; ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS.DLL: %s - handling assembly \"%ls\"\n", __FUNCTION__, static_cast(OurTextualIdentity)); IFW32FALSE_EXIT(::SxspGetAssemblyRootDirectory(buffAsmRoot)); IFW32FALSE_EXIT(::SxspCreateAssemblyIdentityFromTextualString(OurTextualIdentity, &pIdentity)); IFW32FALSE_EXIT(::SxspDetermineAssemblyType(pIdentity, fIsPolicy)); // // It's likely that this short name hasn't been generated yet, mostly because the files // may not have been copied around just yet. // if (this->m_SecurityMetaData.GetInstalledDirShortName().Cch() == 0) { IFW32FALSE_EXIT( ::SxspGenerateSxsPath( 0, SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY | ( fIsPolicy ? SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY : 0 ), buffAsmRoot, buffAsmRoot.Cch(), pIdentity, buffTemp2)); IFW32FALSE_EXIT( ::SxspGetShortPathName( buffTemp2, buffTemp1, dwWin32Error, 4, ERROR_PATH_NOT_FOUND, ERROR_FILE_NOT_FOUND, ERROR_BAD_NET_NAME, ERROR_BAD_NETPATH)); if (dwWin32Error == ERROR_SUCCESS) { buffTemp1.RemoveTrailingPathSeparators(); IFW32FALSE_EXIT(buffTemp1.Win32GetLastPathElement(buffTemp2)); IFW32FALSE_EXIT(m_SecurityMetaData.SetInstalledDirShortName(buffTemp2)); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - decided that the short dir name is \"%ls\"\n", __FUNCTION__, static_cast(buffTemp2)); } else { ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - unable to determine short name for \"%ls\"\n", __FUNCTION__, static_cast(buffTemp2)); } } // // Get the public key token string // { PCWSTR wchString; SIZE_T cchString; CFusionByteArray baStrongNameBits; IFW32FALSE_EXIT(::SxspGetAssemblyIdentityAttributeValue( SXSP_GET_ASSEMBLY_IDENTITY_ATTRIBUTE_VALUE_FLAG_NOT_FOUND_RETURNS_NULL, pIdentity, &s_IdentityAttribute_publicKeyToken, &wchString, &cchString)); if (cchString != 0) { IFW32FALSE_EXIT(::SxspHashStringToBytes(wchString, cchString, baStrongNameBits)); IFW32FALSE_EXIT(m_SecurityMetaData.SetSignerPublicKeyTokenBits(baStrongNameBits)); } } // // And now the short name of the manifest and catalog // { CStringBuffer buffManifestPath; IFW32FALSE_EXIT( ::SxspCreateManifestFileNameFromTextualString( 0, SXSP_GENERATE_SXS_PATH_PATHTYPE_MANIFEST | ( fIsPolicy ? SXSP_GENERATE_SXS_PATH_PATHTYPE_POLICY : 0 ), buffAsmRoot, OurTextualIdentity, buffManifestPath)); // Get the manifest short path first IFW32FALSE_EXIT(::SxspGetShortPathName(buffManifestPath, buffTemp1)); IFW32FALSE_EXIT(buffTemp1.Win32GetLastPathElement(buffTemp2)); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - manifest short path name determined to be \"%ls\"\n", __FUNCTION__, static_cast(buffTemp2)); IFW32FALSE_EXIT(m_SecurityMetaData.SetShortManifestPath(buffTemp2)); // Then swap extensions, get the catalog short path IFW32FALSE_EXIT( buffManifestPath.Win32ChangePathExtension( FILE_EXTENSION_CATALOG, FILE_EXTENSION_CATALOG_CCH, eAddIfNoExtension)); IFW32FALSE_EXIT(::SxspGetShortPathName(buffManifestPath, buffTemp1)); IFW32FALSE_EXIT(buffTemp1.Win32GetLastPathElement(buffTemp2)); ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP, "SXS: %s - catalog short path name determined to be \"%ls\"\n", __FUNCTION__, static_cast(buffTemp2)); IFW32FALSE_EXIT(m_SecurityMetaData.SetShortCatalogPath(buffTemp2)); } FN_EPILOG } BOOL CAssemblyRecoveryInfo::WriteSecondaryAssemblyInfoIntoRegistryKey( CRegKey & rhkRegistryNode ) const { FN_PROLOG_WIN32 IFW32FALSE_EXIT(m_SecurityMetaData.WriteSecondaryAssemblyInfoIntoRegistryKey(rhkRegistryNode)); FN_EPILOG } BOOL CAssemblyRecoveryInfo::WritePrimaryAssemblyInfoToRegistryKey( ULONG Flags, CRegKey & rhkRegistryNode ) const { FN_PROLOG_WIN32 PARAMETER_CHECK((Flags & ~(SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_TO_REGISTRY_KEY_FLAG_REFRESH)) == 0); ULONG Flags2 = 0; if (Flags & SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_TO_REGISTRY_KEY_FLAG_REFRESH) { Flags2 |= SXSP_WRITE_PRIMARY_ASSEMBLY_INFO_INTO_REGISTRY_KEY_FLAG_REFRESH; #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_WFP | FUSION_DBG_LEVEL_INSTALLATION, "SXS.DLL: %s - propping recovery flag to WritePrimaryAssemblyInfoIntoRegistryKey\n", __FUNCTION__); #endif } IFW32FALSE_EXIT(m_SecurityMetaData.WritePrimaryAssemblyInfoIntoRegistryKey(Flags2, rhkRegistryNode)); FN_EPILOG } BOOL CAssemblyRecoveryInfo::OpenInstallationSubKey( CFusionRegKey& hkSingleAssemblyInfo, DWORD OpenOrCreate, DWORD Access) { FN_PROLOG_WIN32 CSmallStringBuffer buffRegKeyName; CFusionRegKey hkAllInstallationInfo; CSxsPointerWithNamedDestructor pAssemblyIdentity; IFW32FALSE_EXIT(::SxspOpenAssemblyInstallationKey( 0, OpenOrCreate, hkAllInstallationInfo)); IFW32FALSE_EXIT( SxspCreateAssemblyIdentityFromTextualString( this->m_SecurityMetaData.GetTextualIdentity(), &pAssemblyIdentity ) ); IFW32FALSE_EXIT( ::SxspGenerateSxsPath( SXSP_GENERATE_SXS_PATH_FLAG_OMIT_ROOT, SXSP_GENERATE_SXS_PATH_PATHTYPE_ASSEMBLY, NULL, 0, pAssemblyIdentity, buffRegKeyName ) ); IFW32FALSE_EXIT( hkAllInstallationInfo.OpenSubKey( hkSingleAssemblyInfo, buffRegKeyName, Access, 0)); FN_EPILOG } VOID CAssemblyRecoveryInfo::RestorePreviouslyExistingRegistryData() { FN_PROLOG_VOID if (m_fHadCatalog) { CFusionRegKey hkSingleAssemblyInfo; #if DBG ::FusionpDbgPrintEx( FUSION_DBG_LEVEL_INSTALLATION, "SXS.DLL: %s() - restoring registry data for %ls\n", __FUNCTION__, static_cast(this->m_SecurityMetaData.GetTextualIdentity())); #endif IFW32FALSE_EXIT( this->OpenInstallationSubKey( hkSingleAssemblyInfo, KEY_CREATE_SUB_KEY, KEY_WRITE | KEY_READ | FUSIONP_KEY_WOW64_64KEY)); IFW32FALSE_EXIT( hkSingleAssemblyInfo.SetValue( CSMD_TOPLEVEL_CATALOG, static_cast(1))); } FN_EPILOG } BOOL CAssemblyRecoveryInfo::ClearExistingRegistryData() { // // Do not be so eager to delete registry metadata, so that // an assembly for which refresh failed might succeed if another // file change comes in, or sfc /scannow. // // As well, if a replace-existing install fails, don't destroy // the metadata for previously successfully installed instances // of the same assembly. // const static struct { PCWSTR Data; SIZE_T Length; } DeletableValues[] = { #define ENTRY(x) { x, NUMBER_OF(x) - 1 } ENTRY(CSMD_TOPLEVEL_CATALOG), #if 0 ENTRY(CSMD_TOPLEVEL_SHORTNAME), ENTRY(CSMD_TOPLEVEL_SHORTCATALOG), ENTRY(CSMD_TOPLEVEL_SHORTMANIFEST), ENTRY(CSMD_TOPLEVEL_MANIFESTHASH), ENTRY(CSMD_TOPLEVEL_PUBLIC_KEY_TOKEN), ENTRY(CSMD_TOPLEVEL_IDENTITY), ENTRY(CSMD_TOPLEVEL_CODEBASE) // legacy, delete (actually not legacy anymore) #endif }; #undef ENTRY static const PCWSTR DeletableKeys[] = { NULL, #if 0 CSMD_TOPLEVEL_FILES, #endif }; FN_PROLOG_WIN32 CFusionRegKey hkSingleAssemblyInfo; IFW32FALSE_EXIT(this->OpenInstallationSubKey(hkSingleAssemblyInfo, KEY_CREATE_SUB_KEY, KEY_WRITE | KEY_READ | FUSIONP_KEY_WOW64_64KEY)); // // We need to delete the installation information for a single assembly - everything // this class owns. // if ( hkSingleAssemblyInfo != CFusionRegKey::GetInvalidValue() ) { ULONG ul = 0; // // Clear values // for ( ul = 0; ul < NUMBER_OF(DeletableValues); ul++ ) { DWORD dwWin32Error = NO_ERROR; IFW32FALSE_EXIT( hkSingleAssemblyInfo.DeleteValue( DeletableValues[ul].Data, dwWin32Error, 2, ERROR_PATH_NOT_FOUND, ERROR_FILE_NOT_FOUND)); if (dwWin32Error == NO_ERROR && !m_fHadCatalog && ::FusionpEqualStrings( DeletableValues[ul].Data, DeletableValues[ul].Length, CSMD_TOPLEVEL_CATALOG, NUMBER_OF(CSMD_TOPLEVEL_CATALOG) - 1, FALSE )) { m_fHadCatalog = true; } } // // Delete eligible keys // for ( ul = 0; ul < NUMBER_OF(DeletableKeys); ul++ ) { if (DeletableKeys[ul] != NULL && DeletableKeys[ul][0] != L'\0') { CFusionRegKey hkTempKey; IFW32FALSE_EXIT(hkSingleAssemblyInfo.OpenSubKey(hkTempKey, DeletableKeys[ul], KEY_ALL_ACCESS, 0)); if ( hkTempKey != CFusionRegKey::GetInvalidValue() ) { IFW32FALSE_EXIT(hkTempKey.DestroyKeyTree()); IFW32FALSE_EXIT(hkSingleAssemblyInfo.DeleteKey(DeletableKeys[ul])); } } } } FN_EPILOG } BOOL SxspLooksLikeAssemblyDirectoryName( const CBaseStringBuffer &rsbSupposedAsmDirectoryName, BOOL &rfLooksLikeAssemblyName ) /*++ Most of this was copied directly from SxspParseAssemblyReference, which is no longer valid, because it can't know how to turn the string back into the actual assembly reference just based on the hash value and public key of a string. --*/ { BOOL fSuccess = FALSE; FN_TRACE_WIN32(fSuccess); PCWSTR pszCursor = NULL; PCWSTR wsNextBlock = NULL; SIZE_T cchSegment; ASSEMBLY_VERSION Version; static const WCHAR UNDERSCORE = L'_'; bool fSyntaxValid = false; bool fAttributeValid = false; rfLooksLikeAssemblyName = FALSE; pszCursor = rsbSupposedAsmDirectoryName; // // Processor architecture // if ((wsNextBlock = ::StringFindChar(pszCursor, UNDERSCORE)) == NULL) FN_SUCCESSFUL_EXIT(); if ((cchSegment = (wsNextBlock - pszCursor)) == 0) FN_SUCCESSFUL_EXIT(); IFW32FALSE_EXIT(::FusionpParseProcessorArchitecture(pszCursor, cchSegment, NULL, fAttributeValid)); if (!fAttributeValid) FN_SUCCESSFUL_EXIT(); pszCursor = wsNextBlock + 1; // // Name // if ((wsNextBlock = StringFindChar(pszCursor, UNDERSCORE)) == NULL) FN_SUCCESSFUL_EXIT(); if ((cchSegment = wsNextBlock - pszCursor) == 0) FN_SUCCESSFUL_EXIT(); pszCursor = wsNextBlock + 1; // // Public key string // if ((wsNextBlock = StringFindChar(pszCursor, UNDERSCORE)) == NULL) FN_SUCCESSFUL_EXIT(); if ((cchSegment = wsNextBlock - pszCursor) == 0) FN_SUCCESSFUL_EXIT(); if ((::FusionpCompareStrings( pszCursor, cchSegment, SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE, NUMBER_OF(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_PUBLICKEY_MISSING_VALUE) - 1, true) == 0) || !::SxspIsFullHexString(pszCursor, cchSegment)) FN_SUCCESSFUL_EXIT(); pszCursor = wsNextBlock + 1; // // Version string // if ((wsNextBlock = StringFindChar(pszCursor, UNDERSCORE)) == NULL) FN_SUCCESSFUL_EXIT(); if ((cchSegment = wsNextBlock - pszCursor) == 0) FN_SUCCESSFUL_EXIT(); IFW32FALSE_EXIT(CFusionParser::ParseVersion(Version, pszCursor, cchSegment, fSyntaxValid)); if (!fSyntaxValid) FN_SUCCESSFUL_EXIT(); pszCursor = wsNextBlock + 1; // // Language ID // if ((wsNextBlock = ::StringFindChar(pszCursor, UNDERSCORE)) == NULL) FN_SUCCESSFUL_EXIT(); if ((cchSegment = wsNextBlock - pszCursor) == 0) FN_SUCCESSFUL_EXIT(); // // BUGBUG (jonwis) - It seems that langids are no longer four-character hex // string representations of shorts anymore. All we're checking at the moment // is to see that the string isn't blank. Is this the Right Thing? // pszCursor = wsNextBlock + 1; // // Last block should just be the hash // if (!::SxspIsFullHexString(pszCursor, ::wcslen(pszCursor))) FN_SUCCESSFUL_EXIT(); // We ran the gauntlet; all the segments of the path look good, let's use it. rfLooksLikeAssemblyName = TRUE; fSuccess = TRUE; Exit: return fSuccess; }