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.
986 lines
26 KiB
986 lines
26 KiB
#include "nt.h"
|
|
#include "ntdef.h"
|
|
#include "ntrtl.h"
|
|
#include "nturtl.h"
|
|
#include "stdio.h"
|
|
#include "sxs-rtl.h"
|
|
#undef INVALID_HANDLE_VALUE
|
|
#include "windows.h"
|
|
#include "environment.h"
|
|
#include "manifestcooked.h"
|
|
#include "assemblygac.h"
|
|
#include "bcl_common.h"
|
|
#include "bcl_w32unicodeinlinestringbuffer.h"
|
|
#include "search.h"
|
|
|
|
//
|
|
// Crypto stack
|
|
//
|
|
#include "hashers.h"
|
|
#include "digesters.h"
|
|
|
|
|
|
|
|
CEnv::StatusCode
|
|
FsCopyFileWithHashGeneration(
|
|
const CEnv::CConstantUnicodeStringPair &Source,
|
|
const CEnv::CConstantUnicodeStringPair &Target,
|
|
CHashObject &HashObjectTarget,
|
|
CDigestMethod &DigestMethod
|
|
)
|
|
{
|
|
CEnv::StatusCode Result;
|
|
CEnv::CByteRegion TempAllocation(NULL, 0);
|
|
HANDLE SourceHandle = INVALID_HANDLE_VALUE;
|
|
HANDLE TargetHandle = INVALID_HANDLE_VALUE;
|
|
SIZE_T cbDidRead, cbOffset;
|
|
|
|
//
|
|
// Force initialization of the hasher and digester.
|
|
//
|
|
|
|
HashObjectTarget.Initialize();
|
|
DigestMethod.Initialize(HashObjectTarget);
|
|
|
|
//
|
|
// Snag some memory to perform the copies
|
|
//
|
|
Result = CEnv::AllocateHeap(2048, TempAllocation, NULL);
|
|
if (CEnv::DidFail(Result))
|
|
goto Exit;
|
|
|
|
//
|
|
// Obtain handles for both files
|
|
//
|
|
Result = CEnv::GetFileHandle(&SourceHandle, Source, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING);
|
|
if (CEnv::DidFail(Result))
|
|
goto Exit;
|
|
|
|
Result = CEnv::GetFileHandle(&TargetHandle, Target, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS);
|
|
if (CEnv::DidFail(Result))
|
|
goto Exit;
|
|
|
|
//
|
|
// Now spin through the file reading blocks, digesting them.
|
|
//
|
|
cbOffset = 0;
|
|
do
|
|
{
|
|
Result = CEnv::ReadFile(SourceHandle, TempAllocation, cbDidRead);
|
|
if (CEnv::DidFail(Result))
|
|
goto Exit;
|
|
|
|
if (cbDidRead == 0)
|
|
break;
|
|
|
|
Result = DigestMethod.DigestExtent(HashObjectTarget, cbOffset, TempAllocation);
|
|
if (CEnv::DidFail(Result))
|
|
goto Exit;
|
|
|
|
cbOffset += cbDidRead;
|
|
|
|
Result = CEnv::WriteFile(TargetHandle, TempAllocation, cbDidRead);
|
|
if (CEnv::DidFail(Result))
|
|
goto Exit;
|
|
}
|
|
while (true);
|
|
|
|
//
|
|
// Ok, we're done.
|
|
//
|
|
|
|
Exit:
|
|
if (TempAllocation.GetPointer())
|
|
CEnv::FreeHeap(TempAllocation.GetPointer(), NULL);
|
|
|
|
if (SourceHandle != INVALID_HANDLE_VALUE)
|
|
CEnv::CloseHandle(SourceHandle);
|
|
|
|
if (TargetHandle != INVALID_HANDLE_VALUE)
|
|
CEnv::CloseHandle(TargetHandle);
|
|
|
|
return Result;
|
|
}
|
|
|
|
COSAssemblyCache*
|
|
CDotNetSxsAssemblyCache::CreateSelf(
|
|
ULONG ulFlags,
|
|
const GUID *pCacheIdent
|
|
)
|
|
{
|
|
CDotNetSxsAssemblyCache *pAllocation = NULL;
|
|
CEnv::StatusCode status;
|
|
|
|
if (!pCacheIdent || (*pCacheIdent != CacheIdentifier))
|
|
return NULL;
|
|
|
|
status = CEnv::AllocateHeap(sizeof(CDotNetSxsAssemblyCache), (PVOID*)&pAllocation, NULL);
|
|
if (CEnv::DidFail(status))
|
|
return NULL;
|
|
|
|
pAllocation->CDotNetSxsAssemblyCache::CDotNetSxsAssemblyCache(ulFlags);
|
|
return pAllocation;
|
|
}
|
|
|
|
CDotNetSxsAssemblyCache::CDotNetSxsAssemblyCache(
|
|
ULONG ulFlags
|
|
)
|
|
{
|
|
}
|
|
|
|
CDotNetSxsAssemblyCache::~CDotNetSxsAssemblyCache()
|
|
{
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CDotNetSxsAssemblyCache::IdentityToTargetPath(
|
|
const CAssemblyIdentity& Ident,
|
|
CEnv::CStringBuffer &PathSegment
|
|
)
|
|
{
|
|
typedef CEnv::CConstantUnicodeStringPair CStringPair;
|
|
|
|
enum {
|
|
Identity_ProcArch = 0,
|
|
Identity_Name,
|
|
Identity_PublicKeyToken,
|
|
Identity_Version,
|
|
Identity_Language,
|
|
};
|
|
|
|
static const struct {
|
|
CStringPair Namespace;
|
|
CStringPair Name;
|
|
CStringPair DefaultValue;
|
|
} PathComponents[] = {
|
|
{ CStringPair(), CStringPair(L"processorArchitecture", NUMBER_OF(L"processorArchitecture") - 1), CStringPair(L"data", 4) },
|
|
{ CStringPair(), CStringPair(L"name", NUMBER_OF(L"name") - 1), CStringPair() },
|
|
{ CStringPair(), CStringPair(L"publicKeyToken", NUMBER_OF(L"publicKeyToken") - 1), CStringPair(L"no-public-key-token", NUMBER_OF(L"no-public-key-token") - 1) },
|
|
{ CStringPair(), CStringPair(L"version", NUMBER_OF(L"version") - 1), CStringPair(L"0.0.0.0", 7) },
|
|
{ CStringPair(), CStringPair(L"language", NUMBER_OF(L"language") - 1), CStringPair(L"x-ww", 4) },
|
|
};
|
|
|
|
CEnv::StatusCode Result = CEnv::SuccessCode;
|
|
CStringPair FoundComponents[NUMBER_OF(PathComponents) * 2];
|
|
SIZE_T i;
|
|
|
|
|
|
PathSegment.Clear();
|
|
|
|
//
|
|
// Look for these identity components in order
|
|
//
|
|
for (i = 0; i < NUMBER_OF(PathComponents); i++)
|
|
{
|
|
Result = Ident.FindAttribute(PathComponents[i].Namespace, PathComponents[i].Name, FoundComponents[2*i]);
|
|
|
|
if (Result == CEnv::NotFound)
|
|
{
|
|
FoundComponents[2*i] = PathComponents[i].DefaultValue;
|
|
}
|
|
else if (CEnv::DidFail(Result))
|
|
{
|
|
goto Exit;
|
|
}
|
|
|
|
FoundComponents[2*i+1].SetPointerAndCount(L"_", 1);
|
|
}
|
|
|
|
//
|
|
// Meddle with the 'name' string to ensure that it falls under the filesystem limits
|
|
//
|
|
// TODO: Fix name string
|
|
//
|
|
|
|
if (!PathSegment.Assign(NUMBER_OF(FoundComponents), FoundComponents))
|
|
{
|
|
//
|
|
// I think we need to fix the string APIs such that on win32 they return lasterror
|
|
// rather than returning BOOL all the time - it'd make transitioning into the RTL
|
|
// much easier...
|
|
//
|
|
Result = CEnv::OutOfMemory;
|
|
PathSegment.Clear();
|
|
goto Exit;
|
|
}
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
|
|
CEnv::StatusCode
|
|
CDotNetSxsAssemblyCache::Initialize()
|
|
{
|
|
this->m_WindowsDirectory = CEnv::StringFrom(USER_SHARED_DATA->NtSystemRoot);
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CDotNetSxsAssemblyCache::EnsurePathsAvailable()
|
|
{
|
|
CEnv::CConstantUnicodeStringPair PathBuilder[3] = {
|
|
m_WindowsDirectory,
|
|
s_BaseDirectory,
|
|
s_ManifestsPath
|
|
};
|
|
CEnv::StatusCode Result = CEnv::SuccessCode;
|
|
|
|
//
|
|
// Ensure that the WinSxS store is available:
|
|
// {windir}\{winsxs2}
|
|
// {windir}\{winsxs2}\{manifests}
|
|
//
|
|
// This call has the side-effect of building all three levels.
|
|
//
|
|
if (CEnv::DidFail(Result = CEnv::CreateDirectory(3, PathBuilder)))
|
|
goto Exit;
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
/*
|
|
|
|
Installation has these phases:
|
|
|
|
- Create mandatory directories in the store
|
|
- Store path
|
|
- Manifests
|
|
- Create the target assembly identity
|
|
- If the target assembly is a policy
|
|
- Write the .policy file into the right place
|
|
- Notify the store metadata manager about the new policy
|
|
- Done
|
|
- If the target assembly is not already installed
|
|
- Create '%storeroot%\%asmpath%-temp' to store files
|
|
- Stream files into install target path, validating hashes
|
|
- Rename path to remove -temp marker
|
|
- Place the .manifest
|
|
- Notify the store metadata manager about the new manifest
|
|
- Done
|
|
- If the target assembly is already installed
|
|
- Notify the store metadata manager about another reference
|
|
- Done
|
|
|
|
Unless:
|
|
- If the "refresh" flag is set, then the bits are copied in again
|
|
*/
|
|
|
|
CEnv::StatusCode
|
|
CDotNetSxsAssemblyCache::InstallAssembly(
|
|
ULONG Flags,
|
|
PMANIFEST_COOKED_DATA ManifestData,
|
|
const CEnv::CConstantUnicodeStringPair &FilePath
|
|
)
|
|
{
|
|
CEnv::StatusCode Result = CEnv::SuccessCode;
|
|
CNtEnvironment::StatusCode NtResult;
|
|
CEnv::CStringBuffer FirstFile, SecondFile;
|
|
CAssemblyIdentity ThisIdentity;
|
|
|
|
//
|
|
// First, let's ensure that the required directories are present
|
|
//
|
|
if (CEnv::DidFail(Result = EnsurePathsAvailable()))
|
|
goto Exit;
|
|
|
|
//
|
|
// Now let's stage the files in question into an installtemp location, validating as we
|
|
// copy. We will never store the actual manifest, we will only store the solidified blob
|
|
// that we got after parsing and cooking it. But first, let's turn that set of identity
|
|
// values into an actual identity...
|
|
//
|
|
// Result = ThisIdentity.C
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
|
|
CEnv::StatusCode
|
|
CDotNetSxsAssemblyCache::UninstallAssembly(
|
|
ULONG Flags,
|
|
PMANIFEST_COOKED_DATA ManifestData,
|
|
UninstallResult & Result
|
|
)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
const GUID CDotNetSxsAssemblyCache::CacheIdentifier = { /* 37e3c37d-667f-4aee-8dab-a0d117acfa68 */
|
|
0x37e3c37d,
|
|
0x667f,
|
|
0x4aee,
|
|
{0x8d, 0xab, 0xa0, 0xd1, 0x17, 0xac, 0xfa, 0x68}
|
|
};
|
|
|
|
const CEnv::CConstantUnicodeStringPair CDotNetSxsAssemblyCache::s_PoliciesPath(L"Policies", 8);
|
|
const CEnv::CConstantUnicodeStringPair CDotNetSxsAssemblyCache::s_ManifestsPath(L"Manifests", 9);
|
|
const CEnv::CConstantUnicodeStringPair CDotNetSxsAssemblyCache::s_InstallTemp(L"InstallTemp", 11);
|
|
const CEnv::CConstantUnicodeStringPair CDotNetSxsAssemblyCache::s_BaseDirectory(L"WinSxS2", 7);
|
|
|
|
|
|
|
|
//
|
|
// Assembly identity stuff - should get split into another file
|
|
//
|
|
CAssemblyIdentity::CAssemblyIdentity()
|
|
: m_cIdentityValues(0), m_IdentityValues(NULL), m_fFrozen(false),
|
|
m_fSorted(true), m_fHashDirtyV1(true), m_fHashDirtyV2(true),
|
|
m_ulHashV1(0), m_cAvailableIdentitySlots(0)
|
|
{
|
|
ZeroMemory(m_IdentityShaHash, sizeof(m_IdentityShaHash));
|
|
}
|
|
|
|
CAssemblyIdentity::~CAssemblyIdentity()
|
|
{
|
|
DeleteAllValues();
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::DeleteAllValues()
|
|
{
|
|
//
|
|
// Let's keep the table around, just because that's handy
|
|
//
|
|
if (m_IdentityValues != NULL)
|
|
{
|
|
for (SIZE_T c = 0; c < m_cIdentityValues; c++)
|
|
{
|
|
CEnv::FreeHeap(m_IdentityValues[c], NULL);
|
|
m_IdentityValues[c] = NULL;
|
|
}
|
|
|
|
m_cIdentityValues = 0;
|
|
}
|
|
|
|
this->m_fHashDirtyV1 = this->m_fHashDirtyV2 = true;
|
|
this->m_ulHashV1 = 0;
|
|
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::SetAttribute(
|
|
const CStringPair &Namespace,
|
|
const CStringPair &Name,
|
|
const CStringPair &Value,
|
|
bool fReplace
|
|
)
|
|
{
|
|
CEnv::StatusCode Result;
|
|
SIZE_T cIndex;
|
|
|
|
//
|
|
// If looking for this value had some other problem aside from not being found,
|
|
// then exit.
|
|
//
|
|
Result = this->InternalFindValue(Namespace, Name, cIndex);
|
|
if (CEnv::DidFail(Result) && (Result != CEnv::NotFound))
|
|
goto Exit;
|
|
|
|
//
|
|
// Easy - create a new attribute to hold this value.
|
|
//
|
|
if (Result == CEnv::NotFound)
|
|
{
|
|
CIdentityValue *NewValue = NULL;
|
|
SIZE_T c;
|
|
|
|
if (CEnv::DidFail(Result = this->InternalAllocateValue(Namespace, Name, Value, NewValue)))
|
|
goto Exit;
|
|
|
|
//
|
|
// After we've allocated one, go insert it into the table. If that failed,
|
|
// the clean up and exit
|
|
//
|
|
if (CEnv::DidFail(Result = this->InternalInsertValue(NewValue)))
|
|
{
|
|
this->InternalDestroyValue(NewValue);
|
|
goto Exit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CIdentityValue *ThisValue = m_IdentityValues[cIndex];
|
|
const SIZE_T cbRequiredSize = ((Namespace.GetCount() + Name.GetCount() + Value.GetCount()) * sizeof(WCHAR)) + sizeof(CIdentityValue);
|
|
|
|
ASSERT(ThisValue != NULL);
|
|
|
|
//
|
|
// Spiffy, there's enough space in the existing value to hold the data from
|
|
// the input value.
|
|
//
|
|
if (cbRequiredSize < ThisValue->cbAllocationSize)
|
|
{
|
|
if (CEnv::DidFail(Result = ThisValue->WriteValues(Namespace, Name, Value)))
|
|
goto Exit;
|
|
}
|
|
//
|
|
// Bah, have to reallocate this entry
|
|
//
|
|
else
|
|
{
|
|
CIdentityValue *NewValue = NULL;
|
|
|
|
if (CEnv::DidFail(Result = this->InternalAllocateValue(Namespace, Name, Value, NewValue)))
|
|
goto Exit;
|
|
|
|
m_IdentityValues[cIndex] = NewValue;
|
|
NewValue = NULL;
|
|
|
|
if (CEnv::DidFail(Result = this->InternalDestroyValue(ThisValue)))
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now clear all the relevant flags
|
|
//
|
|
this->m_fHashDirtyV1 = this->m_fHashDirtyV2 = true;
|
|
this->m_fSorted = false;
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::DeleteAttribute(
|
|
const CStringPair &Namespace,
|
|
const CStringPair &Name
|
|
)
|
|
{
|
|
SIZE_T cIndex;
|
|
CEnv::StatusCode Result;
|
|
CIdentityValue *Victim = NULL;
|
|
|
|
//
|
|
// InternalFindValue will return NotFound if it didn't find it.
|
|
//
|
|
if (CEnv::DidFail(Result = this->InternalFindValue(Namespace, Name, cIndex)))
|
|
goto Exit;
|
|
|
|
//
|
|
// Remember the one we found, clear its slot, delete the allocation
|
|
//
|
|
Victim = m_IdentityValues[cIndex];
|
|
m_IdentityValues[cIndex] = NULL;
|
|
|
|
//
|
|
// And clear the state flags before we delete it
|
|
//
|
|
this->m_fHashDirtyV1 = this->m_fHashDirtyV2 = true;
|
|
this->m_fSorted = false;
|
|
|
|
if (CEnv::DidFail(Result = this->InternalDestroyValue(Victim)))
|
|
goto Exit;
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::CIdentityValue::WriteValues(
|
|
const CStringPair & InNamespace,
|
|
const CStringPair & InName,
|
|
const CStringPair & InValue
|
|
)
|
|
{
|
|
PWSTR pwszWriteCursor = (PWSTR)(this + 1);
|
|
const SIZE_T cRequired = ((InNamespace.GetCount() + InName.GetCount() + InValue.GetCount()) * sizeof(WCHAR)) * sizeof(*this);
|
|
|
|
if (cRequired < this->cbAllocationSize)
|
|
{
|
|
return CEnv::NotEnoughBuffer;
|
|
}
|
|
|
|
this->HashV1Valid = false;
|
|
this->HashV1 = 0;
|
|
|
|
this->Namespace.SetPointerAndCount(pwszWriteCursor, InNamespace.GetCount());
|
|
memcpy(pwszWriteCursor, InNamespace.GetPointer(), InNamespace.GetCount() * sizeof(WCHAR));
|
|
pwszWriteCursor += InNamespace.GetCount();
|
|
|
|
this->Name.SetPointerAndCount(pwszWriteCursor, InName.GetCount());
|
|
memcpy(pwszWriteCursor, InName.GetPointer(), InName.GetCount() * sizeof(WCHAR));
|
|
pwszWriteCursor += InName.GetCount();
|
|
|
|
this->Value.SetPointerAndCount(pwszWriteCursor, InValue.GetCount());
|
|
memcpy(pwszWriteCursor, InValue.GetPointer(), InValue.GetCount() * sizeof(WCHAR));
|
|
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::CIdentityValue::Compare(
|
|
const CAssemblyIdentity::CIdentityValue& Other,
|
|
int &iResult
|
|
) const
|
|
{
|
|
CEnv::StatusCode Result;
|
|
int iMyResult = 0;
|
|
|
|
iResult = -1;
|
|
|
|
//
|
|
// this == this
|
|
//
|
|
if (this == &Other)
|
|
{
|
|
iResult = 0;
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
//
|
|
// Namespace first, then name.
|
|
//
|
|
if (CEnv::DidFail(Result = CEnv::CompareStrings(this->Namespace, Other.Namespace, iMyResult)))
|
|
goto Exit;
|
|
|
|
//
|
|
// Only bother if the namespaces match
|
|
//
|
|
if (iMyResult == 0)
|
|
{
|
|
if (CEnv::DidFail(Result = CEnv::CompareStringsCaseInsensitive(this->Name, Other.Name, iMyResult)))
|
|
goto Exit;
|
|
}
|
|
|
|
iResult = iMyResult;
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::InternalDestroyValue(
|
|
CIdentityValue *Victim
|
|
)
|
|
{
|
|
//
|
|
// This just calls heapfree on the attribute
|
|
//
|
|
return CEnv::FreeHeap((PVOID)Victim, NULL);
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::InternalAllocateValue(
|
|
const CStringPair &Namespace,
|
|
const CStringPair &Name,
|
|
const CStringPair &Value,
|
|
CAssemblyIdentity::CIdentityValue* &pCreated
|
|
)
|
|
{
|
|
const SIZE_T cbRequired = ((Namespace.GetCount() + Name.GetCount() + Value.GetCount()) * sizeof(WCHAR)) + sizeof(CIdentityValue);
|
|
CIdentityValue *pTempCreated = NULL;
|
|
CEnv::StatusCode Result;
|
|
|
|
pCreated = NULL;
|
|
|
|
if (CEnv::DidFail(Result = CEnv::AllocateHeap(cbRequired, (PVOID*)&pTempCreated, NULL)))
|
|
goto Exit;
|
|
|
|
if (CEnv::DidFail(Result = pTempCreated->WriteValues(Namespace, Name, Value)))
|
|
goto Exit;
|
|
|
|
pCreated = pTempCreated;
|
|
pTempCreated = NULL;
|
|
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
if (pTempCreated)
|
|
{
|
|
CEnv::FreeHeap(pTempCreated, NULL);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
//
|
|
// Non-const version can sort.
|
|
//
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::InternalFindValue(
|
|
const CStringPair & Namespace,
|
|
const CStringPair & Name,
|
|
SIZE_T &cIndex
|
|
)
|
|
{
|
|
CEnv::StatusCode Result;
|
|
cIndex = -1;
|
|
|
|
if (!this->m_fSorted)
|
|
{
|
|
if (CEnv::DidFail(Result = this->SortIdentityAttributes()))
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Use the built-in searcher on the const one.
|
|
//
|
|
Result = (const_cast<const CAssemblyIdentity&>(*this)).InternalFindValue(Namespace, Name, cIndex);
|
|
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::FindAttribute(
|
|
const CStringPair & Namespace,
|
|
const CStringPair & Name,
|
|
CStringPair & Value
|
|
)
|
|
{
|
|
CEnv::StatusCode Result;
|
|
SIZE_T cIndex;
|
|
|
|
Value.SetPointerAndCount(NULL, 0);
|
|
|
|
Result = this->InternalFindValue(Namespace, Name, cIndex);
|
|
|
|
if (!CEnv::DidFail(Result))
|
|
{
|
|
Value = this->m_IdentityValues[cIndex]->Value;
|
|
Result = CEnv::SuccessCode;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::FindAttribute(
|
|
const CStringPair &Namespace,
|
|
const CStringPair &Name,
|
|
CStringPair& Value
|
|
) const
|
|
{
|
|
CEnv::StatusCode Result;
|
|
SIZE_T cIndex;
|
|
|
|
Value.SetPointerAndCount(NULL, 0);
|
|
|
|
Result = this->InternalFindValue(Namespace, Name, cIndex);
|
|
|
|
if (!CEnv::DidFail(Result))
|
|
{
|
|
Value = this->m_IdentityValues[cIndex]->Value;
|
|
Result = CEnv::SuccessCode;
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::InternalFindValue(
|
|
const CStringPair &Namespace,
|
|
const CStringPair &Name,
|
|
SIZE_T &cIndex
|
|
) const
|
|
{
|
|
CEnv::StatusCode Result;
|
|
SIZE_T cLow, cHigh;
|
|
CIdentityValue ComparisonDump;
|
|
|
|
cIndex = -1;
|
|
ComparisonDump.Namespace = Namespace;
|
|
ComparisonDump.Name = Name;
|
|
|
|
if (!this->m_fSorted)
|
|
{
|
|
//
|
|
// Ick, linear search.
|
|
//
|
|
for (cLow = 0; cLow < this->m_cIdentityValues; cLow++)
|
|
{
|
|
int iResult = 0;
|
|
CIdentityValue *pFound = this->m_IdentityValues[cLow];
|
|
|
|
//
|
|
// Possible to have holes in the const linear-search version
|
|
//
|
|
if (!pFound)
|
|
continue;
|
|
|
|
if (CEnv::DidFail(Result = pFound->Compare(ComparisonDump, iResult)))
|
|
goto Exit;
|
|
|
|
if (iResult == 0)
|
|
{
|
|
cIndex = cLow;
|
|
Result = CEnv::SuccessCode;
|
|
goto Exit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We're doing our own bsearch here.
|
|
//
|
|
cLow = 0;
|
|
cHigh = this->m_cIdentityValues;
|
|
|
|
while (cLow < cHigh)
|
|
{
|
|
SIZE_T cMiddle = (cHigh - cLow) / 2;
|
|
int iResult = 0;
|
|
CIdentityValue *pFound = this->m_IdentityValues[cMiddle];
|
|
|
|
//
|
|
// The sorting should have reorganized the attributes so NULL slots
|
|
// were past the number of in-use identity values.
|
|
//
|
|
ASSERT(pFound != NULL);
|
|
|
|
if (CEnv::DidFail(Result = pFound->Compare(ComparisonDump, iResult)))
|
|
goto Exit;
|
|
|
|
if (iResult == 0)
|
|
{
|
|
cIndex = cMiddle;
|
|
Result = CEnv::SuccessCode;
|
|
goto Exit;
|
|
}
|
|
else if (iResult < 0)
|
|
{
|
|
cHigh = cMiddle;
|
|
continue;
|
|
}
|
|
else if (iResult > 0)
|
|
{
|
|
cLow = cMiddle;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
Result = CEnv::NotFound;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
#define ASSEMBLY_IDENTITY_TABLE_EXPANDO_FACTOR (20)
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::Freeze()
|
|
{
|
|
CEnv::StatusCode Result;
|
|
|
|
if (m_fFrozen)
|
|
return CEnv::SuccessCode;
|
|
|
|
if (CEnv::DidFail(Result = SortIdentityAttributes()))
|
|
return Result;
|
|
|
|
if (CEnv::DidFail(Result = RegenerateHash()))
|
|
return Result;
|
|
|
|
m_fFrozen = true;
|
|
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::RegenerateHash()
|
|
{
|
|
return CEnv::NotImplemented;
|
|
}
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::InternalInsertValue(
|
|
CAssemblyIdentity::CIdentityValue * NewValue
|
|
)
|
|
{
|
|
CEnv::StatusCode Result;
|
|
|
|
//
|
|
// If the number of values is the same as the available slots, then we
|
|
// have to expand the internal table.
|
|
//
|
|
if (m_cIdentityValues == m_cAvailableIdentitySlots)
|
|
{
|
|
//
|
|
// Simple logic - increase by 20 slots each time
|
|
//
|
|
CIdentityValue** ppNewTable = NULL;
|
|
CIdentityValue** ppOldTable = m_IdentityValues;
|
|
const SIZE_T cNewSlots = m_cAvailableIdentitySlots + ASSEMBLY_IDENTITY_TABLE_EXPANDO_FACTOR;
|
|
const SIZE_T cbRequired = sizeof(CIdentityValue*) * cNewSlots;
|
|
|
|
if (CEnv::DidFail(Result = CEnv::AllocateHeap(cbRequired, (PVOID*)&ppNewTable, NULL)))
|
|
goto Exit;
|
|
|
|
//
|
|
// Pointers make this easy
|
|
//
|
|
if (ppOldTable)
|
|
{
|
|
memcpy(ppNewTable, ppOldTable, sizeof(CIdentityValue*) * m_cAvailableIdentitySlots);
|
|
}
|
|
|
|
//
|
|
// Clear out the data in these slots
|
|
//
|
|
for (SIZE_T i = m_cAvailableIdentitySlots; i < cNewSlots; i++)
|
|
{
|
|
ppNewTable[i] = NULL;
|
|
}
|
|
|
|
m_IdentityValues = ppNewTable;
|
|
m_cAvailableIdentitySlots = cNewSlots;
|
|
|
|
//
|
|
// Free the old table
|
|
//
|
|
if (ppOldTable)
|
|
{
|
|
CEnv::FreeHeap((PVOID)ppOldTable, NULL);
|
|
}
|
|
}
|
|
|
|
ASSERT(m_IdentityValues != NULL);
|
|
ASSERT(m_cIdentityValues < m_cAvailableIdentitySlots);
|
|
|
|
m_IdentityValues[m_cIdentityValues++] = NewValue;
|
|
Result = CEnv::SuccessCode;
|
|
Exit:
|
|
return Result;
|
|
}
|
|
|
|
int __cdecl
|
|
CAssemblyIdentity::SortingCallback(
|
|
const CAssemblyIdentity::CIdentityValue **left,
|
|
const CAssemblyIdentity::CIdentityValue **right
|
|
)
|
|
{
|
|
const CIdentityValue *pLeft = *left;
|
|
const CIdentityValue *pRight = *right;
|
|
CEnv::StatusCode Result = CEnv::SuccessCode;
|
|
int iResult = 0;
|
|
|
|
//
|
|
// Percolate NULL slots towards the 'end'
|
|
//
|
|
if (!left && !right)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (!left && right)
|
|
{
|
|
return 1;
|
|
}
|
|
else if (left && !right)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
ASSERT(pLeft && pRight);
|
|
|
|
Result = pLeft->Compare(*pRight, iResult);
|
|
ASSERT(!CEnv::DidFail(Result));
|
|
|
|
return iResult;
|
|
}
|
|
|
|
|
|
CEnv::StatusCode
|
|
CAssemblyIdentity::SortIdentityAttributes()
|
|
{
|
|
CEnv::StatusCode Result;
|
|
|
|
if (this->m_cIdentityValues == 0)
|
|
{
|
|
m_fSorted = true;
|
|
}
|
|
|
|
if (m_fSorted)
|
|
{
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
qsort(
|
|
this->m_IdentityValues,
|
|
this->m_cAvailableIdentitySlots,
|
|
sizeof(this->m_IdentityValues[0]),
|
|
(int (__cdecl*)(const void*, const void*))SortingCallback
|
|
);
|
|
|
|
//
|
|
// Sorted now, but we've invalidated the hash of the overall object.
|
|
//
|
|
m_fSorted = true;
|
|
m_fHashDirtyV1 = m_fHashDirtyV2 = true;
|
|
|
|
return CEnv::SuccessCode;
|
|
}
|
|
|
|
/*
|
|
class CAssemblyIdentity
|
|
{
|
|
public:
|
|
typedef CEnv::CConstantUnicodeStringPair CStringPair;
|
|
|
|
CAssemblyIdentity();
|
|
~CAssemblyIdentity();
|
|
|
|
CEnv::StatusCode SetAttribute(const CStringPair &Namespace, const CStringPair &Name, const CStringPair &Value, bool fReplace = true);
|
|
CEnv::StatusCode SetAttribute(const CStringPair &Name, const CStringPair& Value, bool fReplace = true) { return SetAttribute(CStringPair(), Name, Value, fReplace); }
|
|
CEnv::StatusCode DeleteAttribute(const CStringPair &Namespace, const CStringPair &Name);
|
|
|
|
//
|
|
// The const version will do a linear or a bsearch depending on the sorted state. The non-const
|
|
// version will sort the internal attribute list first if necessary before looking up the value
|
|
//
|
|
CEnv::StatusCode FindAttribute(const CStringPair &Namespace, const CStringPair &Name, CStringPair& Value) const;
|
|
CEnv::StatusCode FindAttribute(const CStringPair &Namespace, const CStringPair &Name, CStringPair& Value);
|
|
|
|
unsigned long IdentityHash() const;
|
|
unsigned long IdentityHash();
|
|
unsigned long long IdentityHashV2() const;
|
|
unsigned long long IdentityHashV2();
|
|
|
|
//
|
|
// Maintainence stuff
|
|
//
|
|
CEnv::StatusCode Freeze();
|
|
CEnv::StatusCode DeleteAllValues();
|
|
|
|
static CEnv::StatusCode ConstructFromCookedData(
|
|
CAssemblyIdentity& Target,
|
|
PMANIFEST_COOKED_IDENTITY IdentityData
|
|
);
|
|
|
|
protected:
|
|
|
|
//
|
|
// This is an all-in-one allocation blob
|
|
//
|
|
typedef struct {
|
|
CStringPair Namespace;
|
|
CStringPair Name;
|
|
CStringPair Value;
|
|
} CIdentityValue;
|
|
|
|
SIZE_T m_cIdentityValues;
|
|
CIdentityValue** m_IdentityValues;
|
|
|
|
bool m_fFrozen;
|
|
bool m_fSorted;
|
|
bool m_fHashDirty;
|
|
|
|
CEnv::StatusCode RegenerateHash();
|
|
CEnv::StatusCode SortIdentityAttributes();
|
|
int SortingCallback(const CIdentityValue *left, const CIdentityValue *right);
|
|
};
|
|
*/
|
|
|