Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1307 lines
41 KiB

#include <fusenetincludes.h>
#include <assemblycache.h>
#include <sxsapi.h>
#include "fusion.h"
#include "macros.h"
//BUGBUG - this is not localizeable ? could cause avalon a problem
// use shell apis instead.
#define WZ_CACHE_LOCALROOTDIR L"Local Settings\\My Programs\\"
#define WZ_TEMP_DIR L"__temp__\\"
#define WZ_MANIFEST_STAGING_DIR L"__temp__\\__manifests__\\"
#define WZ_SHARED_DIR L"__shared__\\"
#define WZ_WILDCARDSTRING L"*"
typedef HRESULT(*PFNGETCORSYSTEMDIRECTORY)(LPWSTR, DWORD, LPDWORD);
typedef HRESULT (__stdcall *PFNCREATEASSEMBLYCACHE) (IAssemblyCache **ppAsmCache, DWORD dwReserved);
#define WZ_MSCOREE_DLL_NAME L"mscoree.dll"
#define GETCORSYSTEMDIRECTORY_FN_NAME "GetCORSystemDirectory"
#define CREATEASSEMBLYCACHE_FN_NAME "CreateAssemblyCache"
#define WZ_FUSION_DLL_NAME L"Fusion.dll"
IAssemblyCache* CAssemblyCache::g_pFusionAssemblyCache = NULL;
// ---------------------------------------------------------------------------
// CreateAssemblyCacheImport
// ---------------------------------------------------------------------------
HRESULT CreateAssemblyCacheImport(
LPASSEMBLY_CACHE_IMPORT *ppAssemblyCacheImport,
LPASSEMBLY_IDENTITY pAssemblyIdentity,
DWORD dwFlags)
{
return CAssemblyCache::Retrieve(ppAssemblyCacheImport, pAssemblyIdentity, dwFlags);
}
// ---------------------------------------------------------------------------
// CreateAssemblyCacheEmit
// ---------------------------------------------------------------------------
HRESULT CreateAssemblyCacheEmit(
LPASSEMBLY_CACHE_EMIT *ppAssemblyCacheEmit,
LPASSEMBLY_CACHE_EMIT pAssemblyCacheEmit,
DWORD dwFlags)
{
return CAssemblyCache::Create(ppAssemblyCacheEmit, pAssemblyCacheEmit, dwFlags);
}
// ---------------------------------------------------------------------------
// Retrieve
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Retrieve(
LPASSEMBLY_CACHE_IMPORT *ppAssemblyCacheImport,
LPASSEMBLY_IDENTITY pAssemblyIdentity,
DWORD dwFlags)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
LPWSTR pwzSearchDisplayName = NULL;
BOOL bNewAsmId = FALSE;
LPWSTR pwzBuf = NULL;
DWORD dwCC = 0;
CAssemblyCache *pAssemblyCache = NULL;
CString sManifestFilename;
CString sDisplayName;
IF_FALSE_EXIT(dwFlags == CACHEIMP_CREATE_RETRIEVE_MAX
|| dwFlags == CACHEIMP_CREATE_RETRIEVE
|| dwFlags == CACHEIMP_CREATE_RESOLVE_REF
|| dwFlags == CACHEIMP_CREATE_RESOLVE_REF_EX, E_INVALIDARG);
IF_NULL_EXIT(pAssemblyIdentity, E_INVALIDARG);
IF_ALLOC_FAILED_EXIT(pAssemblyCache = new(CAssemblyCache));
IF_FAILED_EXIT(pAssemblyCache->Init(NULL, ASSEMBLY_CACHE_TYPE_APP | ASSEMBLY_CACHE_TYPE_IMPORT));
// get the identity name
IF_FALSE_EXIT(pAssemblyIdentity->GetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_NAME,
&pwzBuf, &dwCC) == S_OK, E_INVALIDARG);
// filename of the manifest must be the same as the assembly name
// BUGBUG??: this implies manifest filename (and asm name) be remained unchange because
// the assembly name from the new AsmId is used for looking up in the older cached version...
IF_FAILED_EXIT(sManifestFilename.TakeOwnership(pwzBuf, dwCC));
IF_FAILED_EXIT(sManifestFilename.Append(L".manifest"));
if (dwFlags == CACHEIMP_CREATE_RETRIEVE_MAX)
{
LPASSEMBLY_IDENTITY pNewAsmId = NULL;
IF_FAILED_EXIT(CloneAssemblyIdentity(pAssemblyIdentity, &pNewAsmId));
pAssemblyIdentity = pNewAsmId;
bNewAsmId = TRUE;
// force Version to be a wildcard
IF_FAILED_EXIT(pAssemblyIdentity->SetAttribute(SXS_ASSEMBLY_IDENTITY_STD_ATTRIBUTE_NAME_VERSION,
WZ_WILDCARDSTRING, lstrlen(WZ_WILDCARDSTRING)+1));
}
if (dwFlags == CACHEIMP_CREATE_RETRIEVE_MAX
|| dwFlags == CACHEIMP_CREATE_RESOLVE_REF
|| dwFlags == CACHEIMP_CREATE_RESOLVE_REF_EX)
{
// issues: what if other then Version is already wildcarded? does version comparison make sense here?
IF_FAILED_EXIT(pAssemblyIdentity->GetDisplayName(ASMID_DISPLAYNAME_WILDCARDED,
&pwzSearchDisplayName, &dwCC));
if ( (hr = SearchForHighestVersionInCache(&pwzBuf, pwzSearchDisplayName, CAssemblyCache::VISIBLE, pAssemblyCache) == S_OK))
{
IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzBuf));
// BUGBUG - make GetDisplayName call getassemblyid/getdisplayname instead
IF_FAILED_EXIT((pAssemblyCache->_sDisplayName).Assign(sDisplayName));
}
else
{
IF_FAILED_EXIT(hr);
// can't resolve
hr = S_FALSE;
if (dwFlags != CACHEIMP_CREATE_RESOLVE_REF_EX)
goto exit;
}
}
if (dwFlags == CACHEIMP_CREATE_RETRIEVE
|| (hr == S_FALSE && dwFlags == CACHEIMP_CREATE_RESOLVE_REF_EX))
{
// make the name anyway if resolving a ref that does not have any completed cache counterpart
// BUGBUG: this may no longer be necessary if shortcut code/UI changes - it's expecting a path
// plus this is inefficient as it searchs the disk at above, even if ref is fully qualified
IF_FAILED_EXIT(pAssemblyIdentity->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING, &pwzBuf, &dwCC));
IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzBuf, dwCC));
// BUGBUG - make GetDisplayName call getassemblyid/getdisplayname instead
IF_FAILED_EXIT((pAssemblyCache->_sDisplayName).Assign(sDisplayName));
}
// Note: this will prepare for delay initializing _pManifestImport
IF_FAILED_EXIT((pAssemblyCache->_sManifestFileDir).Assign(pAssemblyCache->_sRootDir));
// build paths
IF_FAILED_EXIT((pAssemblyCache->_sManifestFileDir).Append(sDisplayName));
if (dwFlags == CACHEIMP_CREATE_RETRIEVE)
{
BOOL bExists = FALSE;
// simple check if dir is in cache or not
IF_FAILED_EXIT(CheckFileExistence((pAssemblyCache->_sManifestFileDir)._pwz, &bExists));
if (!bExists)
{
// cache dir not exists
hr = S_FALSE;
goto exit;
}
}
IF_FAILED_EXIT((pAssemblyCache->_sManifestFileDir).Append(L"\\"));
IF_FAILED_EXIT((pAssemblyCache->_sManifestFilePath).Assign(pAssemblyCache->_sManifestFileDir));
IF_FAILED_EXIT((pAssemblyCache->_sManifestFilePath).Append(sManifestFilename));
*ppAssemblyCacheImport = static_cast<IAssemblyCacheImport*> (pAssemblyCache);
(*ppAssemblyCacheImport)->AddRef();
exit:
SAFEDELETEARRAY(pwzSearchDisplayName);
if (bNewAsmId)
SAFERELEASE(pAssemblyIdentity);
SAFERELEASE(pAssemblyCache);
return hr;
}
// ---------------------------------------------------------------------------
// Create
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Create(
LPASSEMBLY_CACHE_EMIT *ppAssemblyCacheEmit,
LPASSEMBLY_CACHE_EMIT pAssemblyCacheEmit,
DWORD dwFlags)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
CAssemblyCache *pAssemblyCache = NULL;
IF_ALLOC_FAILED_EXIT(pAssemblyCache = new(CAssemblyCache) );
IF_FAILED_EXIT(hr = pAssemblyCache->Init(static_cast<CAssemblyCache*> (pAssemblyCacheEmit),
ASSEMBLY_CACHE_TYPE_APP | ASSEMBLY_CACHE_TYPE_EMIT));
*ppAssemblyCacheEmit = static_cast<IAssemblyCacheEmit*> (pAssemblyCache);
(*ppAssemblyCacheEmit)->AddRef();
exit:
SAFERELEASE(pAssemblyCache);
return hr;
}
// ---------------------------------------------------------------------------
// FindVersionInDisplayName
// ---------------------------------------------------------------------------
LPCWSTR CAssemblyCache::FindVersionInDisplayName(LPCWSTR pwzDisplayName)
{
int cNumUnderscoreFromEndToVersionString = 2;
int count = 0;
int ccLen = lstrlen(pwzDisplayName);
LPWSTR pwz = (LPWSTR) (pwzDisplayName+ccLen-1);
LPWSTR pwzRetVal = NULL;
// return a pointer to the start of Version string inside a displayName
while (*pwz != NULL && pwz > pwzDisplayName)
{
if (*pwz == L'_')
count++;
if (count == cNumUnderscoreFromEndToVersionString)
break;
pwz--;
}
if (count == cNumUnderscoreFromEndToVersionString)
pwzRetVal = ++pwz;
return pwzRetVal;
}
// ---------------------------------------------------------------------------
// CompareVersion
// ---------------------------------------------------------------------------
int CAssemblyCache::CompareVersion(LPCWSTR pwzVersion1, LPCWSTR pwzVersion2)
{
// BUGBUG: this should compare version by its major minor build revision!
// possible break if V1=10.0.0.0 and V2=2.0.0.0?
// plus pwzVersion1 is something like "1.0.0.0_en"
return wcscmp(pwzVersion1, pwzVersion2); // This is not used....
}
// ---------------------------------------------------------------------------
// SearchForHighestVersionInCache
// Look for a copy in cache that has the highest version and the specified status
// pwzSearchDisplayName should really be created from a partial ref
//
// return: S_OK - found a version from the ref
// S_FALSE - not found any version from the ref, or
// ref not partial and that version is not there/not in that status
// E_*
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::SearchForHighestVersionInCache(LPWSTR *ppwzResultDisplayName, LPWSTR pwzSearchDisplayName, CAssemblyCache::CacheStatus eCacheStatus, CAssemblyCache* pCache)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
HANDLE hFind = INVALID_HANDLE_VALUE;
WIN32_FIND_DATA fdAppDir;
DWORD dwLastError = 0;
BOOL fFound = FALSE;
CString sDisplayName;
CString sSearchPath;
*ppwzResultDisplayName = NULL;
sDisplayName.Assign(pwzSearchDisplayName);
IF_FAILED_EXIT(sSearchPath.Assign(pCache->_sRootDir));
IF_FAILED_EXIT(sSearchPath.Append(sDisplayName));
hFind = FindFirstFileEx(sSearchPath._pwz, FindExInfoStandard, &fdAppDir, FindExSearchLimitToDirectories, NULL, 0);
if (hFind == INVALID_HANDLE_VALUE)
{
hr = S_FALSE;
goto exit;
}
do
{
// ???? check file attribute to see if it's a directory? needed only if the file system does not support the filter...
// ???? check version string format?
if (CAssemblyCache::IsStatus(fdAppDir.cFileName, eCacheStatus))
{
ULONGLONG ullMax;
ULONGLONG ullCur;
LPCWSTR pwzVerStr = FindVersionInDisplayName(sDisplayName._pwz);
IF_FAILED_EXIT(ConvertVersionStrToULL(pwzVerStr, &ullMax));
pwzVerStr = FindVersionInDisplayName(fdAppDir.cFileName);
if(!pwzVerStr || FAILED(hr = ConvertVersionStrToULL(pwzVerStr, &ullCur)) )
{
// ignore badly formed dirs; maybe we should delete them
continue;
}
if (ullCur > ullMax)
{
IF_FAILED_EXIT(sDisplayName.Assign(fdAppDir.cFileName));
fFound = TRUE;
} else if (ullCur == ullMax)
fFound = TRUE;
// else keep the newest
}
} while(FindNextFile(hFind, &fdAppDir));
if( (dwLastError = GetLastError()) != ERROR_NO_MORE_FILES)
{
IF_WIN32_FAILED_EXIT(dwLastError);
}
if (fFound)
{
sDisplayName.ReleaseOwnership(ppwzResultDisplayName);
hr = S_OK;
}
else
hr = S_FALSE;
exit:
if (hFind != INVALID_HANDLE_VALUE)
{
if (!FindClose(hFind) && SUCCEEDED(hr)) // don't overwrite if we already have useful hr.
{
ASSERT(0);
hr = FusionpHresultFromLastError();
}
}
return hr;
}
// ---------------------------------------------------------------------------
// CreateFusionAssemblyCacheEx
// ---------------------------------------------------------------------------
HRESULT CreateFusionAssemblyCacheEx (IAssemblyCache **ppFusionAsmCache)
{
HRESULT hr = S_OK;
hr = CAssemblyCache::CreateFusionAssemblyCache(ppFusionAsmCache);
return hr;
}
// ---------------------------------------------------------------------------
// ctor
// ---------------------------------------------------------------------------
CAssemblyCache::CAssemblyCache()
: _dwSig('hcac'), _cRef(1), _hr(S_OK), _dwFlags(0), _pManifestImport(NULL), _pAssemblyId(NULL)
{}
// ---------------------------------------------------------------------------
// dtor
// ---------------------------------------------------------------------------
CAssemblyCache::~CAssemblyCache()
{
SAFERELEASE(_pManifestImport);
SAFERELEASE(_pAssemblyId);
/*
if( _hr != S_OK)
RemoveDirectoryAndChildren(_sManifestFileDir._pwz);
*/
}
// ---------------------------------------------------------------------------
// Init
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Init(CAssemblyCache *pAssemblyCache, DWORD dwFlags)
{
_dwFlags = dwFlags;
if (!pAssemblyCache)
{
if (_dwFlags & ASSEMBLY_CACHE_TYPE_APP)
{
if (_dwFlags & ASSEMBLY_CACHE_TYPE_IMPORT)
IF_FAILED_EXIT( GetCacheRootDir(_sRootDir, Base));
else if (_dwFlags & ASSEMBLY_CACHE_TYPE_EMIT)
IF_FAILED_EXIT( GetCacheRootDir(_sRootDir, Temp));
}
else if (_dwFlags & ASSEMBLY_CACHE_TYPE_SHARED)
{
IF_FAILED_EXIT( GetCacheRootDir(_sRootDir, Shared));
}
}
else
IF_FAILED_EXIT( _sRootDir.Assign(pAssemblyCache->_sManifestFileDir));
exit :
return _hr;
}
// ---------------------------------------------------------------------------
// GetManifestFilePath
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetManifestFilePath(LPOLESTR *ppwzManifestFilePath,
LPDWORD pccManifestFilePath)
{
CString sPathOut;
IF_FAILED_EXIT(sPathOut.Assign(_sManifestFilePath));
*pccManifestFilePath = sPathOut.CharCount();
IF_FAILED_EXIT(sPathOut.ReleaseOwnership(ppwzManifestFilePath));
exit:
if(FAILED(_hr))
{
*ppwzManifestFilePath = NULL;
*pccManifestFilePath = 0;
}
return _hr;
}
// ---------------------------------------------------------------------------
// GetManifestFileDir
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetManifestFileDir(LPOLESTR *ppwzManifestFileDir,
LPDWORD pccManifestFileDir)
{
CString sDirOut;
IF_FAILED_EXIT(sDirOut.Assign(_sManifestFileDir));
*pccManifestFileDir = sDirOut.CharCount();
IF_FAILED_EXIT(sDirOut.ReleaseOwnership(ppwzManifestFileDir));
exit:
if(FAILED(_hr))
{
*ppwzManifestFileDir = NULL;
*pccManifestFileDir = 0;
}
return _hr;
}
// ---------------------------------------------------------------------------
// GetManifestImport
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetManifestImport(LPASSEMBLY_MANIFEST_IMPORT *ppManifestImport)
{
IF_NULL_EXIT(_pManifestImport, E_INVALIDARG);
*ppManifestImport = _pManifestImport;
(*ppManifestImport)->AddRef();
_hr = S_OK;
exit:
return _hr;
}
// ---------------------------------------------------------------------------
// GetAssemblyIdentity
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetAssemblyIdentity(LPASSEMBLY_IDENTITY *ppAssemblyId)
{
if (_pAssemblyId)
{
*ppAssemblyId = _pAssemblyId;
(*ppAssemblyId)->AddRef();
_hr = S_OK;
}
else
{
IF_NULL_EXIT(_pManifestImport, E_INVALIDARG);
IF_FAILED_EXIT(_pManifestImport->GetAssemblyIdentity(&_pAssemblyId));
*ppAssemblyId = _pAssemblyId;
(*ppAssemblyId)->AddRef();
_hr = S_OK;
}
exit:
return _hr;
}
// ---------------------------------------------------------------------------
// GetDisplayName
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetDisplayName(LPOLESTR *ppwzDisplayName, LPDWORD pccDiaplyName)
{
CString sDisplayNameOut;
IF_FAILED_EXIT(sDisplayNameOut.Assign(_sDisplayName));
*pccDiaplyName= sDisplayNameOut.CharCount();
IF_FAILED_EXIT(sDisplayNameOut.ReleaseOwnership(ppwzDisplayName));
exit:
if(FAILED(_hr))
{
*pccDiaplyName= 0;
*ppwzDisplayName = NULL;
}
return _hr;
}
// ---------------------------------------------------------------------------
// FindExistMatching
// return:
// S_OK
// S_FALSE -not exist or not match
// E_*
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::FindExistMatching(IManifestInfo *pAssemblyFileInfo, LPOLESTR *ppwzPath)
{
LPWSTR pwzBuf = NULL;
DWORD cbBuf = 0, dwFlag;
CString sFileName;
CString sTargetPath;
IManifestInfo *pFoundFileInfo = NULL;
BOOL bExists=FALSE;
IF_NULL_EXIT(pAssemblyFileInfo, E_INVALIDARG);
IF_NULL_EXIT(ppwzPath, E_INVALIDARG);
*ppwzPath = NULL;
if (_pManifestImport == NULL)
{
if (_sManifestFilePath._cc == 0)
{
// no manifest path
_hr = CO_E_NOTINITIALIZED;
goto exit;
}
// lazy init
IF_FAILED_EXIT(CreateAssemblyManifestImport(&_pManifestImport,
_sManifestFilePath._pwz, NULL, 0));
}
// file name parsed from manifest.
IF_FAILED_EXIT(pAssemblyFileInfo->Get(MAN_INFO_ASM_FILE_NAME,
(LPVOID *)&pwzBuf, &cbBuf, &dwFlag));
IF_FAILED_EXIT(sFileName.TakeOwnership(pwzBuf));
IF_FAILED_EXIT(sTargetPath.Assign(_sManifestFileDir));
IF_FAILED_EXIT(sTargetPath.Append(sFileName._pwz));
// optimization: check if the target exists
IF_FAILED_EXIT(CheckFileExistence(sTargetPath._pwz, &bExists));
if (!bExists)
{
// file doesn't exist - no point looking into the manifest file
_hr = S_FALSE;
goto exit;
}
// find the specified file entry in the manifest
// BUGBUG: check for missing attribute case
if (FAILED(_hr = _pManifestImport->QueryFile(sFileName._pwz, &pFoundFileInfo))
|| _hr == S_FALSE)
goto exit;
// check if the entries match
if (pAssemblyFileInfo->IsEqual(pFoundFileInfo) == S_OK)
{
// BUGBUG:? should now check if the actual file has the matching hash etc.
*ppwzPath = sTargetPath._pwz;
IF_FAILED_EXIT(sTargetPath.ReleaseOwnership(ppwzPath));
}
else
_hr = S_FALSE;
exit:
SAFERELEASE(pFoundFileInfo);
return _hr;
}
// ---------------------------------------------------------------------------
// CopyFile
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::CopyFile(LPOLESTR pwzSourcePath, LPOLESTR pwzRelativeFileName, DWORD dwFlags)
{
LPWSTR pwzBuf = NULL;
DWORD ccBuf = 0, cbBuf =0, dwFlag = 0, n = 0;
WCHAR wzRandom[8+1] = {0};
CString sDisplayName;
LPASSEMBLY_MANIFEST_IMPORT pManifestImport = NULL;
LPASSEMBLY_IDENTITY pIdentity = NULL;
IManifestInfo *pAssemblyFile= NULL;
if (dwFlags & MANIFEST)
{
// Get display name.
IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManifestImport, pwzSourcePath, NULL, 0));
IF_FAILED_EXIT(pManifestImport->GetAssemblyIdentity(&pIdentity));
IF_FAILED_EXIT(pIdentity->GetDisplayName(ASMID_DISPLAYNAME_NOMANGLING,
&pwzBuf, &ccBuf));
IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwzBuf, ccBuf));
IF_FAILED_EXIT(_sDisplayName.Assign(sDisplayName));
SAFERELEASE(pManifestImport);
// Create manifest file path.
IF_FAILED_EXIT(_sManifestFilePath.Assign(_sRootDir));
// Component manifests cached
// relative to application dir.
if (!(dwFlags & COMPONENT))
{
IF_FAILED_EXIT(CreateRandomDir(_sManifestFilePath._pwz, wzRandom, 8));
IF_FAILED_EXIT(_sManifestFilePath.Append(wzRandom));
IF_FAILED_EXIT(_sManifestFilePath.Append(L"\\"));
}
IF_FAILED_EXIT(_sManifestFilePath.Append(pwzRelativeFileName));
_sManifestFilePath.PathNormalize();
// Manifest file dir.
IF_FAILED_EXIT(_sManifestFileDir.Assign(_sManifestFilePath));
IF_FAILED_EXIT(_sManifestFileDir.RemoveLastElement());
IF_FAILED_EXIT(_sManifestFileDir.Append(L"\\"));
// Construct target paths
IF_FAILED_EXIT(CreateDirectoryHierarchy(NULL, _sManifestFilePath._pwz));
// Copy the manifest from staging area into cache.
IF_WIN32_FALSE_EXIT(::CopyFile(pwzSourcePath, _sManifestFilePath._pwz, FALSE));
// Create the manifest import interface on cached manifest.
IF_FAILED_EXIT(CreateAssemblyManifestImport(&_pManifestImport, _sManifestFilePath._pwz, NULL, 0));
// Enumerate files from manifest and pre-generate nested
// directories required for background file copy.
while (_pManifestImport->GetNextFile(n++, &pAssemblyFile) == S_OK)
{
CString sPath;
IF_FAILED_EXIT(pAssemblyFile->Get(MAN_INFO_ASM_FILE_NAME,
(LPVOID *)&pwzBuf, &cbBuf, &dwFlag));
IF_FAILED_EXIT(sPath.TakeOwnership(pwzBuf));
sPath.PathNormalize();
IF_FAILED_EXIT(CreateDirectoryHierarchy(_sManifestFileDir._pwz, sPath._pwz));
// RELEASE pAssebmlyFile everytime through the while loop
SAFERELEASE(pAssemblyFile);
}
}
else
{
CString sTargetPath;
// Construct target path
IF_FAILED_EXIT(sTargetPath.Assign(_sManifestFileDir));
IF_FAILED_EXIT(sTargetPath.Append(pwzRelativeFileName));
IF_FAILED_EXIT(CreateDirectoryHierarchy(NULL, sTargetPath._pwz));
// Copy non-manifest files into cache. Presumably from previous cached location to the new
IF_WIN32_FALSE_EXIT(::CopyFile(pwzSourcePath, sTargetPath._pwz, FALSE));
}
exit:
SAFERELEASE(pIdentity);
SAFERELEASE(pAssemblyFile);
SAFERELEASE(pManifestImport);
return _hr;
}
// ---------------------------------------------------------------------------
// Commit
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::Commit(DWORD dwFlags)
{
CString sTargetDir;
IF_NULL_EXIT( _sDisplayName._pwz, E_INVALIDARG);
// No-op for shared assemblies; no directory move.
if (_dwFlags & ASSEMBLY_CACHE_TYPE_SHARED)
{
_hr = S_OK;
goto exit;
}
// Need to rename directory
IF_FAILED_EXIT(GetCacheRootDir(sTargetDir, Base));
IF_FAILED_EXIT(sTargetDir.Append(_sDisplayName));
// Move the file from staging dir. The application is now complete.
if(!MoveFileEx(_sManifestFileDir._pwz, sTargetDir._pwz, MOVEFILE_COPY_ALLOWED))
{
_hr = FusionpHresultFromLastError();
// BUGBUG : move this to destructor.
RemoveDirectoryAndChildren(_sManifestFileDir._pwz);
if(_hr == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
{
_hr = S_FALSE;
goto exit;
}
IF_FAILED_EXIT(_hr);
}
//BUGBUG - if any files are held open, eg due to leaked/unreleased interfaces
// then movefile will fail. Solution is to ensure that IAssemblyManifestImport does
// not hold file, and to attempt copy if failure occurs. In case a collision occurs,
// delete redundant app copy in staging dir.
exit:
return _hr;
}
#define APP_STATUS_KEY TEXT("1.0.0.0\\Cache\\")
#define WZ_STATUS_CONFIRMED L"Confirmed"
#define WZ_STATUS_VISIBLE L"Visible"
#define WZ_STATUS_CRITICAL L"Critical"
HRESULT CAssemblyCache::GetStatusStrings( CacheStatus eStatus,
LPWSTR *ppValueString,
LPCWSTR pwzDisplayName,
CString& sRelStatusKey)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
switch(eStatus)
{
case VISIBLE:
*ppValueString = WZ_STATUS_VISIBLE;
break;
case CONFIRMED:
*ppValueString = WZ_STATUS_CONFIRMED;
break;
case CRITICAL:
*ppValueString = WZ_STATUS_CRITICAL;
break;
default:
hr = E_INVALIDARG;
goto exit;
}
IF_FAILED_EXIT(sRelStatusKey.Assign(APP_STATUS_KEY));
IF_FAILED_EXIT(sRelStatusKey.Append(pwzDisplayName));
exit:
return hr;
}
// ---------------------------------------------------------------------------
// IsStatus
// return FALSE if value FALSE or absent, TRUE if value TRUE
// ---------------------------------------------------------------------------
BOOL CAssemblyCache::IsStatus(LPWSTR pwzDisplayName, CacheStatus eStatus)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
CString sStatus;
DWORD dwValue = -1;
LPWSTR pwzQueryString = NULL;
// Default values spelled out.
BOOL bStatus = FALSE;
CRegImport *pRegImport = NULL;
if((eStatus == VISIBLE) || (eStatus == CONFIRMED) )
{
bStatus = TRUE;
}
IF_FAILED_EXIT(hr = GetStatusStrings( eStatus, &pwzQueryString, pwzDisplayName, sStatus));
IF_FAILED_EXIT(hr = CRegImport::Create(&pRegImport, sStatus._pwz));
if(hr == S_FALSE)
goto exit;
IF_FAILED_EXIT(pRegImport->ReadDword(pwzQueryString, &dwValue));
// Found a value in registry. Return value.
bStatus = (BOOL) dwValue;
hr = S_OK;
exit:
SAFEDELETE(pRegImport);
return bStatus;
}
// ---------------------------------------------------------------------------
// SetStatus
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::SetStatus(LPWSTR pwzDisplayName, CacheStatus eStatus, BOOL fStatus)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
CString sStatus;
DWORD dwValue = (DWORD) (fStatus);
LPWSTR pwzValueNameString = NULL;
CRegEmit *pRegEmit = NULL;
// BUGBUG: should this be in-sync with what server does to register update?
IF_FAILED_EXIT(GetStatusStrings( eStatus, &pwzValueNameString, pwzDisplayName, sStatus));
IF_FAILED_EXIT(CRegEmit::Create(&pRegEmit, sStatus._pwz));
// Write
IF_FAILED_EXIT(pRegEmit->WriteDword(pwzValueNameString, dwValue));
hr = S_OK;
exit:
SAFEDELETE(pRegEmit);
return hr;
}
// ---------------------------------------------------------------------------
// GetCacheRootDir
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GetCacheRootDir(CString &sCacheDir, CacheFlags eFlags)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
CString sPath;
LPWSTR pwzPath = NULL;
DWORD ccSize=0;
IF_FALSE_EXIT((ccSize = GetEnvironmentVariable(L"UserProfile", NULL, 0)) != 0, E_FAIL);
IF_ALLOC_FAILED_EXIT(pwzPath = new WCHAR[ccSize+1]);
IF_FALSE_EXIT(GetEnvironmentVariable(L"UserProfile", pwzPath, ccSize) != 0, E_FAIL);
IF_FAILED_EXIT(sCacheDir.Assign(pwzPath));
// BUGBUG: don't use PathCombine
IF_FAILED_EXIT((DoPathCombine(sCacheDir, WZ_CACHE_LOCALROOTDIR)));
switch(eFlags)
{
case Base:
break;
case Manifests:
// BUGBUG: don't use PathCombine
IF_FAILED_EXIT(DoPathCombine(sCacheDir, WZ_MANIFEST_STAGING_DIR));
break;
case Temp:
// BUGBUG: don't use PathCombine
IF_FAILED_EXIT(DoPathCombine(sCacheDir, WZ_TEMP_DIR));
break;
case Shared:
// BUGBUG: don't use PathCombine
IF_FAILED_EXIT(DoPathCombine(sCacheDir, WZ_SHARED_DIR));
break;
default:
break;
}
exit:
SAFEDELETEARRAY(pwzPath);
return hr;
}
// ---------------------------------------------------------------------------
// IsCached
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::IsCached(IAssemblyIdentity *pAppId)
{
HRESULT hr = S_FALSE;
MAKE_ERROR_MACROS_STATIC(hr);
LPWSTR pwz = NULL;
DWORD cc = 0, dwAttrib = 0;
CString sDisplayName;
CString sCacheDir;
BOOL bExists=FALSE;
// Get the assembly display name.
IF_FAILED_EXIT(pAppId->GetDisplayName(0, &pwz, &cc));
IF_FAILED_EXIT(sDisplayName.TakeOwnership(pwz));
// Check if top-level dir is present.
IF_FAILED_EXIT(GetCacheRootDir(sCacheDir, Base));
IF_FAILED_EXIT(sCacheDir.Append(sDisplayName));
IF_FAILED_EXIT(CheckFileExistence(sCacheDir._pwz, &bExists));
(bExists) ? (hr = S_OK) : (hr = S_FALSE);
exit :
return hr;
}
// ---------------------------------------------------------------------------
// IsKnownAssembly
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::IsKnownAssembly(IAssemblyIdentity *pId, DWORD dwFlags)
{
return ::IsKnownAssembly(pId, dwFlags);
}
// ---------------------------------------------------------------------------
// IsaMissingSystemAssembly
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::IsaMissingSystemAssembly(IAssemblyIdentity *pId, DWORD dwFlags)
{
HRESULT hr = S_FALSE;
CString sCurrentAssemblyPath;
// check if this is a system assembly.
if ((hr = CAssemblyCache::IsKnownAssembly(pId, KNOWN_SYSTEM_ASSEMBLY)) != S_OK)
goto exit;
// see if it exists in GAC
if ((hr = CAssemblyCache::GlobalCacheLookup(pId, sCurrentAssemblyPath)) == S_OK)
goto exit;
if(hr == S_FALSE)
hr = S_OK; // this is a system assembly which has not yet been installed to GAC.
exit:
return hr;
}
// ---------------------------------------------------------------------------
// CreateFusionAssemblyCache
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::CreateFusionAssemblyCache(IAssemblyCache **ppFusionAsmCache)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
HMODULE hEEShim = NULL;
HMODULE hFusion = NULL;
DWORD ccPath = MAX_PATH;
CString sFusionPath;
LPWSTR pwzPath=NULL;
if (g_pFusionAssemblyCache)
{
*ppFusionAsmCache = g_pFusionAssemblyCache;
(*ppFusionAsmCache)->AddRef();
goto exit;
}
PFNGETCORSYSTEMDIRECTORY pfnGetCorSystemDirectory = NULL;
PFNCREATEASSEMBLYCACHE pfnCreateAssemblyCache = NULL;
// Find out where the current version of URT is installed
hEEShim = LoadLibrary(WZ_MSCOREE_DLL_NAME);
if(!hEEShim)
{
hr = FusionpHresultFromLastError();
goto exit;
}
pfnGetCorSystemDirectory = (PFNGETCORSYSTEMDIRECTORY)
GetProcAddress(hEEShim, GETCORSYSTEMDIRECTORY_FN_NAME);
if((!pfnGetCorSystemDirectory))
{
hr = FusionpHresultFromLastError();
goto exit;
}
// Get cor path.
hr = pfnGetCorSystemDirectory(NULL, 0, &ccPath);
IF_FALSE_EXIT(hr == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER), FAILED(hr) ? hr : E_FAIL);
IF_ALLOC_FAILED_EXIT(pwzPath = new WCHAR[ccPath+1]);
IF_FAILED_EXIT(pfnGetCorSystemDirectory(pwzPath, ccPath, &ccPath));
IF_FAILED_EXIT(sFusionPath.Assign(pwzPath));
// Form path to fusion
IF_FAILED_EXIT(sFusionPath.Append(WZ_FUSION_DLL_NAME));
// Fusion.dll has a static dependency on msvcr70.dll.
// If msvcr70.dll is not in the path (a rare case), a simple LoadLibrary() fails (ERROR_MOD_NOT_FOUND).
// LoadLibraryEx() with LOAD_WITH_ALTERED_SEARCH_PATH fixes this.
hFusion = LoadLibraryEx(sFusionPath._pwz, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if(!hFusion)
{
hr = FusionpHresultFromLastError();
goto exit;
}
// Get method ptr.
pfnCreateAssemblyCache = (PFNCREATEASSEMBLYCACHE)
GetProcAddress(hFusion, CREATEASSEMBLYCACHE_FN_NAME);
if((!pfnCreateAssemblyCache))
{
hr = FusionpHresultFromLastError();
goto exit;
}
// Create the fusion cache interface.
IF_FAILED_EXIT(pfnCreateAssemblyCache(ppFusionAsmCache, 0));
//BUGBUG - we never unload fusion, which is ok for now
// but should when switchover to cache api objects.
g_pFusionAssemblyCache = *ppFusionAsmCache;
g_pFusionAssemblyCache->AddRef();
hr = S_OK;
exit:
SAFEDELETEARRAY(pwzPath);
return hr;
}
// ---------------------------------------------------------------------------
// GlobalCacheLookup
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GlobalCacheLookup(IAssemblyIdentity *pId, CString& sCurrentAssemblyPath)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
LPWSTR pwz = NULL;
DWORD cc = 0;
CString sCLRDisplayName;
IAssemblyCache *pFusionCache = NULL;
ASSEMBLY_INFO asminfo = {0};
WCHAR pwzPath[MAX_PATH];
CString sPath;
IF_FAILED_EXIT(sPath.ResizeBuffer(MAX_PATH+1));
// Get the URT display name for lookup.
IF_FAILED_EXIT(pId->GetCLRDisplayName(0, &pwz, &cc));
IF_FAILED_EXIT(sCLRDisplayName.TakeOwnership(pwz));
// Set size on asminfo struct.
asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
asminfo.pszCurrentAssemblyPathBuf = sPath._pwz;
asminfo.cchBuf = MAX_PATH;
// Create the fusion cache object for lookup.
IF_FAILED_EXIT(CreateFusionAssemblyCache(&pFusionCache));
// Get cache info for assembly. Needs to free [out] pathbuf
if(FAILED(hr = pFusionCache->QueryAssemblyInfo(0, sCLRDisplayName._pwz, &asminfo)))
{
hr = S_FALSE; // all failures are being interpreted as ERROR_FILE_NOT_FOUND
goto exit;
}
// Return ok if install flag present.
if (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED)
{
IF_FAILED_EXIT(sCurrentAssemblyPath.Assign(asminfo.pszCurrentAssemblyPathBuf));
hr = S_OK;
}
else
hr = S_FALSE;
exit:
SAFERELEASE(pFusionCache);
return hr;
}
// ---------------------------------------------------------------------------
// GlobalCacheInstall
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::GlobalCacheInstall(IAssemblyCacheImport *pCacheImport,
CString& sCurrentAssemblyPath, CString& sInstallRefString)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
LPWSTR pwz = NULL;
DWORD cc = 0;
// Fusion.dll's assembly cache, not to be confusing.
IAssemblyCache *pFusionCache = NULL;
// note: InstallAssembly takes in LPCFUSION_INSTALL_REFERENCE
// so fix this to have one fiRef instead (of one per loop)
// - make static also ? - adriaanc
FUSION_INSTALL_REFERENCE fiRef = {0};
// Create Fusion cache object for install.
IF_FAILED_EXIT(CreateFusionAssemblyCache(&pFusionCache));
// Setup the necessary reference struct.
fiRef.cbSize = sizeof(FUSION_INSTALL_REFERENCE);
fiRef.dwFlags = 0;
fiRef.guidScheme = FUSION_REFCOUNT_OPAQUE_STRING_GUID;
fiRef.szIdentifier = sInstallRefString._pwz;
fiRef.szNonCannonicalData = NULL;
if (pCacheImport != NULL)
{
CString sManifestFilePath;
// 1. Install the downloaded assembly
// Get the source manifest path.
IF_FAILED_EXIT(pCacheImport->GetManifestFilePath(&pwz, &cc));
IF_FAILED_EXIT(sManifestFilePath.TakeOwnership(pwz));
// Do the install.
// ISSUE - always refresh - check fusion doc on refresh
IF_FAILED_EXIT(pFusionCache->InstallAssembly(IASSEMBLYCACHE_INSTALL_FLAG_REFRESH, sManifestFilePath._pwz, &fiRef));
}
else if ((sCurrentAssemblyPath)._cc != 0)
{
// bugbug - as the list is set up during pre-download, the assemblies to be add-ref-ed
// could have been removed by this time. Need to recover from this.
// ignore error from Fusion and continue for now
// 2. Up the ref count of the existing assembly by doing install.
IF_FAILED_EXIT(pFusionCache->InstallAssembly(0, sCurrentAssemblyPath._pwz, &fiRef));
}
exit :
SAFERELEASE(pFusionCache);
return hr;
}
// ---------------------------------------------------------------------------
// DeleteAssemblyAndModules
// ---------------------------------------------------------------------------
HRESULT CAssemblyCache::DeleteAssemblyAndModules(LPWSTR pszManifestFilePath)
{
HRESULT hr = S_OK;
MAKE_ERROR_MACROS_STATIC(hr);
IAssemblyManifestImport *pManImport=NULL;
DWORD nIndex=0;
DWORD dwFlag;
DWORD cbBuf;
LPWSTR pwzBuf=NULL;
IManifestInfo *pFileInfo = NULL;
CString sAssemblyPath;
IF_FAILED_EXIT(CreateAssemblyManifestImport(&pManImport, pszManifestFilePath, NULL, 0));
IF_FAILED_EXIT(sAssemblyPath.Assign(pszManifestFilePath));
while ((hr = pManImport->GetNextFile(nIndex++, &pFileInfo)) == S_OK)
{
IF_FAILED_EXIT(pFileInfo->Get(MAN_INFO_ASM_FILE_NAME, (LPVOID *)&pwzBuf, &cbBuf, &dwFlag));
IF_FAILED_EXIT(sAssemblyPath.RemoveLastElement());
IF_FAILED_EXIT(sAssemblyPath.Append(L"\\"));
IF_FAILED_EXIT(sAssemblyPath.Append(pwzBuf));
IF_WIN32_FALSE_EXIT(::DeleteFile(sAssemblyPath._pwz));
SAFEDELETEARRAY(pwzBuf);
SAFERELEASE(pFileInfo);
}
if(hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS))
hr = S_OK;
IF_FAILED_EXIT(hr);
SAFERELEASE(pManImport); // release manImport before deleting manifestFile
IF_WIN32_FALSE_EXIT(::DeleteFile(pszManifestFilePath));
IF_FAILED_EXIT(sAssemblyPath.RemoveLastElement());
if(!::RemoveDirectory(sAssemblyPath._pwz))
{
hr = FusionpHresultFromLastError();
if(hr == HRESULT_FROM_WIN32(ERROR_DIR_NOT_EMPTY))
hr = S_OK; // looks like there are more files in this dir.
goto exit;
}
hr = S_OK;
exit:
SAFERELEASE(pManImport);
SAFEDELETEARRAY(pwzBuf);
SAFERELEASE(pFileInfo);
return hr;
}
// IUnknown methods
// ---------------------------------------------------------------------------
// CAssemblyCache::QI
// ---------------------------------------------------------------------------
STDMETHODIMP
CAssemblyCache::QueryInterface(REFIID riid, void** ppvObj)
{
if ( IsEqualIID(riid, IID_IUnknown)
|| IsEqualIID(riid, IID_IAssemblyCacheImport)
)
{
*ppvObj = static_cast<IAssemblyCacheImport*> (this);
AddRef();
return S_OK;
}
else if (IsEqualIID(riid, IID_IAssemblyCacheEmit))
{
*ppvObj = static_cast<IAssemblyCacheEmit*> (this);
AddRef();
return S_OK;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
}
// ---------------------------------------------------------------------------
// CAssemblyCache::AddRef
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CAssemblyCache::AddRef()
{
return InterlockedIncrement ((LONG*) &_cRef);
}
// ---------------------------------------------------------------------------
// CAssemblyCache::Release
// ---------------------------------------------------------------------------
STDMETHODIMP_(ULONG)
CAssemblyCache::Release()
{
ULONG lRet = InterlockedDecrement ((LONG*) &_cRef);
if (!lRet)
delete this;
return lRet;
}