#include "nt.h" #include "ntdef.h" #include "ntrtl.h" #include "nturtl.h" #include "stdio.h" #include "sxs-rtl.h" #include "fasterxml.h" #include "skiplist.h" #include "namespacemanager.h" #include "xmlstructure.h" #include "stdlib.h" #include "xmlassert.h" #ifdef INVALID_HANDLE_VALUE #undef INVALID_HANDLE_VALUE #endif #include "windows.h" #ifndef NUMBER_OF #define NUMBER_OF(x) (sizeof(x)/sizeof(*x)) #endif NTSTATUS FASTCALL MyAllocator(SIZE_T cb, PVOID* pvOutput, PVOID pvAllocContext) { ASSERT(pvAllocContext == NULL); *pvOutput = HeapAlloc(GetProcessHeap(), 0, cb); return *pvOutput ? STATUS_SUCCESS : STATUS_NO_MEMORY; } NTSTATUS FASTCALL MyFreer(PVOID pv, PVOID pvAllocContext) { ASSERT(pvAllocContext == NULL); return HeapFree(GetProcessHeap(), 0, pv) ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; } EXTERN_C RTL_ALLOCATOR g_DefaultAllocator = {MyAllocator, MyFreer, NULL}; // // The longest an assembly name on-disk can be is: // // wow6432 (7) // _ (1) // {name} (64) // _ (1) // PKT (16) // _ (1) // Version (35) // _ (1) // Language (5) // _ (1) // {Hash} (8) // // Total: ----> 140 (plus NULL) // #define MAX_ASSEMBLY_NAME_LENGTH (140) #define MAX_ASSEMBLY_COMPONENT_LENGTH (64) typedef struct _tagASM_IDENT_COMPONENT { PXML_TOKENIZATION_STATE pState; XML_EXTENT Namespace; XML_EXTENT Attribute; XML_EXTENT Value; } ASM_IDENT_COMPONENT, *PASM_IDENT_COMPONENT; #define MAKE_SPECIAL(q) { L ## q, NUMBER_OF(L##q) - 1 } static XML_SPECIAL_STRING AssemblyIdentity = MAKE_SPECIAL("assemblyIdentity"); static XML_SPECIAL_STRING Assembly = MAKE_SPECIAL("assembly"); static XML_SPECIAL_STRING OurNamespace = MAKE_SPECIAL("urn:schemas-microsoft-com:asm.v1"); static XML_SPECIAL_STRING ss_ident_Name = MAKE_SPECIAL("name"); static XML_SPECIAL_STRING ss_ident_Version = MAKE_SPECIAL("version"); static XML_SPECIAL_STRING ss_ident_PKT = MAKE_SPECIAL("publicKeyToken"); static XML_SPECIAL_STRING ss_ident_Language = MAKE_SPECIAL("language"); static XML_SPECIAL_STRING ss_ident_ProcArch = MAKE_SPECIAL("processorArchitecture"); int __cdecl _CompareIdentityComponents( const void *pvLeft, const void *pvRight ) { PASM_IDENT_COMPONENT pCompLeft = (PASM_IDENT_COMPONENT)pvLeft; PASM_IDENT_COMPONENT pCompRight = (PASM_IDENT_COMPONENT)pvRight; NTSTATUS status = STATUS_SUCCESS; XML_STRING_COMPARE Result; int iResult = 0; ASSERT(pCompLeft->pState == pCompRight->pState); status = pCompLeft->pState->pfnCompareStrings( pCompLeft->pState, &pCompLeft->Namespace, &pCompRight->Namespace, &Result); // // We're sorting... can't really stop here, return "equal" // if (!NT_SUCCESS(status)) { goto Exit; } // // Same namespace, compare attribute names // if (Result == XML_STRING_COMPARE_EQUALS) { int printf(const char*, ...); status = pCompLeft->pState->pfnCompareStrings( pCompLeft->pState, &pCompLeft->Attribute, &pCompRight->Attribute, &Result); if (!NT_SUCCESS(status)) { goto Exit; } // // Nicely enough, the result really has -1, 1, 0 property. // iResult = (int)Result; // // Ick, we should never have two attributes with the same name // in the same namespace on an element. // ASSERT(iResult != 0); } Exit: return iResult; } static NTSTATUS _CompareStrings( PVOID pvContext, PXML_EXTENT pLeft, PXML_EXTENT pRight, BOOLEAN *pfMatches ) { XML_LOGICAL_STATE *pState = (XML_LOGICAL_STATE*)pvContext; NTSTATUS status; XML_STRING_COMPARE Compare; *pfMatches = FALSE; status = pState->ParseState.pfnCompareStrings( &pState->ParseState, pLeft, pRight, &Compare); if (NT_SUCCESS(status)) { *pfMatches = (Compare == XML_STRING_COMPARE_EQUALS); } return status; } #define HASH_MULT_CONSTANT (65599) static NTSTATUS FASTCALL HashXmlExtent( PXML_RAWTOKENIZATION_STATE pState, PXML_EXTENT pExtent, PULONG pulHash ) { PVOID pvOriginal; ULONG ulCharacter; ULONG ulHashResult = 0; NTSTATUS status = STATUS_SUCCESS; SIZE_T cbHashed = 0; *pulHash = 0; ASSERT(pState->cbBytesInLastRawToken == pState->DefaultCharacterSize); ASSERT(NT_SUCCESS(pState->NextCharacterResult)); pvOriginal = pState->pvCursor; pState->pvCursor = pExtent->pvData; for (cbHashed = 0; cbHashed < pExtent->cbData;) { ulCharacter = pState->pfnNextChar(pState); if ((ulCharacter == 0) && !NT_SUCCESS(pState->NextCharacterResult)) { status = pState->NextCharacterResult; goto Exit; } else if (ulCharacter > 0xFFFF) { status = STATUS_INVALID_PARAMETER; goto Exit; } ulHashResult = (ulHashResult * HASH_MULT_CONSTANT) + towupper((WCHAR)ulCharacter); pState->pvCursor = (PBYTE)pState->pvCursor + pState->cbBytesInLastRawToken; cbHashed += pState->cbBytesInLastRawToken; if (pState->cbBytesInLastRawToken != pState->DefaultCharacterSize) { pState->cbBytesInLastRawToken = pState->DefaultCharacterSize; } } *pulHash = ulHashResult; Exit: pState->pvCursor = pvOriginal; return status; } static NTSTATUS FASTCALL HashIdentityElement( PASM_IDENT_COMPONENT pIdent, PULONG pulHash ) { ULONG ulHashValue = 0; ULONG ulTempHashValue = 0; NTSTATUS status = STATUS_SUCCESS; XML_STRING_COMPARE Compare; status = HashXmlExtent(&pIdent->pState->RawTokenState, &pIdent->Namespace, &ulTempHashValue); ulHashValue = (ulHashValue * HASH_MULT_CONSTANT) + ulTempHashValue; if (!NT_SUCCESS(status)) { goto Exit; } status = HashXmlExtent(&pIdent->pState->RawTokenState, &pIdent->Attribute, &ulTempHashValue); ulHashValue = (ulHashValue * HASH_MULT_CONSTANT) + ulTempHashValue; if (!NT_SUCCESS(status)) { goto Exit; } status = HashXmlExtent(&pIdent->pState->RawTokenState, &pIdent->Value, &ulTempHashValue); ulHashValue = (ulHashValue * HASH_MULT_CONSTANT) + ulTempHashValue; if (!NT_SUCCESS(status)) { goto Exit; } *pulHash = ulHashValue; Exit: return status; } static NTSTATUS CalculateIdentityHash( PASM_IDENT_COMPONENT pIdents, SIZE_T cIdents, ULONG *pulHash ) { SIZE_T c = 0; ULONG ulHash = 0; NTSTATUS status = STATUS_SUCCESS; *pulHash = 0; for (c = 0; c < cIdents; c++) { ULONG ulTempHash; status = HashIdentityElement(pIdents + c, &ulTempHash); if (!NT_SUCCESS(status)) { goto Exit; } ulHash = (ulHash * HASH_MULT_CONSTANT) + ulTempHash; } *pulHash = ulHash; Exit: return status; } static int __cdecl _FindIdentityComponents( const XML_SPECIAL_STRING* pKeyString, const PASM_IDENT_COMPONENT pComponent ) { NTSTATUS status; XML_STRING_COMPARE Compare = XML_STRING_COMPARE_GT; // // Our attributes all live in the 'null' namespace // if (pComponent->Namespace.cbData == 0) { status = pComponent->pState->pfnCompareSpecialString( pComponent->pState, &pComponent->Attribute, (XML_SPECIAL_STRING*)pKeyString, &Compare); ASSERT(NT_SUCCESS(status)); // // On failure, make sure we don't match this one - checked builds // will assert above and point out the error // if (!NT_SUCCESS(status)) { Compare = XML_STRING_COMPARE_GT; } } return (int)Compare; } int (__cdecl *pfnIdentCompare)(const void*, const void*) = (int (__cdecl*)(const void*, const void*))_FindIdentityComponents; #define NO_PKT_PRESENT ("no-public-key") #define NO_VERSION_PRESENT ("0.0.0.0") #define WORLD_WIDE_LANGUAGE ("x-ww") #define IsValidChar(q) (((q >= 'a') && (q <= 'z')) || ((q >= 'A') && (q <= 'Z')) || ((q >= '0') && (q <= '9')) || (q == '.') || (q == '-')) static NTSTATUS ExtentToPurifiedString( PASM_IDENT_COMPONENT pIdent, PCSTR pcszDefault, PSTR *pszPureString, PSIZE_T pcchThis ) { NTSTATUS status = STATUS_SUCCESS; WCHAR wchBuffer[MAX_ASSEMBLY_COMPONENT_LENGTH]; PWSTR pwszString = wchBuffer; SIZE_T cchWritten = 0; SIZE_T cchOutput = 0; SIZE_T i; if ((*pcchThis > 0) && (*pszPureString == NULL)) { return STATUS_INVALID_PARAMETER; } if (!pIdent) { // // Input string too small? Allocate and return a new one // if (strlen(pcszDefault) > *pcchThis) { *pszPureString = HeapAlloc(GetProcessHeap(), 0, *pcchThis); } strcpy(*pszPureString, pcszDefault); *pcchThis = strlen(pcszDefault); goto Exit; } // // Start by getting the string out of the extent // cchWritten = NUMBER_OF(wchBuffer); status = RtlXmlCopyStringOut( pIdent->pState, &pIdent->Value, wchBuffer, &cchWritten); // // Oops, allocate a buffer large enough and try again // if (status == STATUS_BUFFER_TOO_SMALL) { pwszString = (PWSTR)HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (++cchWritten)); if (pwszString == NULL) { status = STATUS_NO_MEMORY; goto Exit; } status = RtlXmlCopyStringOut(pIdent->pState, &pIdent->Value, pwszString, &cchWritten); } if (!NT_SUCCESS(status)) { goto Exit; } // // How long is the output string? // for (i = 0; i < cchWritten; i++) { if (IsValidChar(pwszString[i])) { cchOutput++; } } // // Needs more in the output, reallocate // if (cchOutput >= *pcchThis) { *pszPureString = (PSTR)HeapAlloc(GetProcessHeap(), 0, cchOutput + 1); } *pcchThis = cchOutput; cchOutput = 0; // // Now copy characters over // for (i = 0; i < cchWritten; i++) { if (IsValidChar(pwszString[i])) { (*pszPureString)[cchOutput++] = (CHAR)(pwszString[i] & 0xFF); } } (*pszPureString)[cchOutput] = 0; Exit: if ((pwszString != wchBuffer) && pwszString) { HeapFree(GetProcessHeap(), 0, (PVOID)pwszString); pwszString = wchBuffer; } return status; } static NTSTATUS FixUpNamePortion( PSTR pszOutputCursor, PSIZE_T pcchThis ) { ULONG ulSpaceLeft = 64; ULONG i, len; PSTR pszOriginalStart = pszOutputCursor; PSTR pszOriginalEnd = pszOriginalStart + *pcchThis; PSTR pLeftEnd = pszOutputCursor; PSTR pRightStart = pszOriginalEnd; PSTR pszStart, pszEnd, qEnd; CHAR chBuffer[64]; while (pszOriginalStart < pszOriginalEnd) { pszStart = pszOriginalStart; i = 0; while((strchr(".-", pszStart[i]) == 0) && ((pszStart + i) != pRightStart)) i++; pszEnd = pszStart + i; len = i; if (len >= (ulSpaceLeft - 2)) { pLeftEnd += (ulSpaceLeft - 2); break; } ulSpaceLeft -= len; pLeftEnd = pszEnd; qEnd = pszOriginalEnd; i = 0; while (((qEnd + i) != pLeftEnd) && (strchr(".-", qEnd[i]) == 0)) i--; len = 0 - i; if (len >= (ulSpaceLeft - 2)) { pRightStart -= ulSpaceLeft - 2; break; } ulSpaceLeft -= len; pszOriginalStart = pLeftEnd + 1; pszOriginalEnd = pRightStart - 1; } strncpy(chBuffer, pszOutputCursor, pLeftEnd - pszOutputCursor); strcat(chBuffer, ".."); strcat(chBuffer, pRightStart); strcpy(pszOutputCursor, chBuffer); *pcchThis = strlen(chBuffer); return STATUS_SUCCESS; } static NTSTATUS ProcessFile( PVOID pvData, SIZE_T cbData, PSTR pszTarget, SIZE_T *pcchTarget ) { WCHAR wchComponentBuffer[MAX_ASSEMBLY_COMPONENT_LENGTH]; ULONG ulIdentComponents = 0; ULONG i = 0; ULONG ulHash; NTSTATUS status; NS_MANAGER NamespaceManager; XMLDOC_THING DocumentPiece; RTL_GROWING_LIST AttributeList; XML_STRING_COMPARE fMatching; XML_LOGICAL_STATE MasterParseState; PASM_IDENT_COMPONENT pIdentComponents = NULL; status = RtlInitializeGrowingList( &AttributeList, sizeof(XMLDOC_ATTRIBUTE), 20, NULL, 0, &g_DefaultAllocator); if (!NT_SUCCESS(status)) { return status; } status = RtlXmlInitializeNextLogicalThing( &MasterParseState, pvData, cbData, &g_DefaultAllocator); status = RtlNsInitialize( &NamespaceManager, RtlXmlDefaultCompareStrings, &MasterParseState, &g_DefaultAllocator); while (NT_SUCCESS(status)) { status = RtlXmlNextLogicalThing( &MasterParseState, &NamespaceManager, &DocumentPiece, &AttributeList); if (!NT_SUCCESS(status)) { break; } if ((DocumentPiece.ulThingType == XMLDOC_THING_ERROR) || (DocumentPiece.ulThingType == XMLDOC_THING_END_OF_STREAM)) { break; } // // Level 1 or non-elements are simply ignored // if ((DocumentPiece.ulDocumentDepth != 1) || (DocumentPiece.ulThingType != XMLDOC_THING_ELEMENT)) continue; // // Find out the namespace that this thing is in // status = MasterParseState.ParseState.pfnCompareSpecialString( &MasterParseState.ParseState, &DocumentPiece.Element.NsPrefix, &OurNamespace, &fMatching); // // Error, stop // if (!NT_SUCCESS(status)) { goto Exit; } // // Go on then, off with ye. // else if (fMatching != XML_STRING_COMPARE_EQUALS) { continue; } // // Is this assembly identity? // status = MasterParseState.ParseState.pfnCompareSpecialString( &MasterParseState.ParseState, &DocumentPiece.Element.Name, &AssemblyIdentity, &fMatching); if (!NT_SUCCESS(status)) { goto Exit; } else if (fMatching != XML_STRING_COMPARE_EQUALS) { continue; } // // Good, so now we need to look at the attributes // ulIdentComponents = DocumentPiece.Element.ulAttributeCount; pIdentComponents = (PASM_IDENT_COMPONENT)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, ulIdentComponents * sizeof(*pIdentComponents)); // // Copy stuff around // for (i = 0; i < ulIdentComponents; i++) { PXMLDOC_ATTRIBUTE pThisAttribute = NULL; status = RtlIndexIntoGrowingList( &AttributeList, i, (PVOID*)&pThisAttribute, FALSE); if (!NT_SUCCESS(status)) { goto Exit; } pIdentComponents[i].Attribute = pThisAttribute->Name; pIdentComponents[i].Namespace = pThisAttribute->NsPrefix; pIdentComponents[i].Value = pThisAttribute->Value; pIdentComponents[i].pState = &MasterParseState.ParseState; } break; } // // Something bad? Quit. // if (!NT_SUCCESS(status) || !pIdentComponents) { goto Exit; } // // Sort attributes first, then go and create the list of things to be sent to the // identity generator // qsort(pIdentComponents, ulIdentComponents, sizeof(ASM_IDENT_COMPONENT), _CompareIdentityComponents); // // Now for each component of the name part... // if (!NT_SUCCESS(status = CalculateIdentityHash(pIdentComponents, ulIdentComponents, &ulHash))) { return status; } // // Sort and send out the pointer on success // if (NT_SUCCESS(status) && pIdentComponents && ulIdentComponents) { struct { XML_SPECIAL_STRING *pNameBit; PCSTR pszDefault; BOOL fIsName; } NameOperations[] = { { &ss_ident_ProcArch, "data", FALSE }, { &ss_ident_Name, NULL, TRUE }, { &ss_ident_PKT, NO_PKT_PRESENT, FALSE }, { &ss_ident_Version, NO_VERSION_PRESENT, FALSE }, { &ss_ident_Language, WORLD_WIDE_LANGUAGE, FALSE } }; PSTR pszOutputCursor = pszTarget; SIZE_T cchTotal = 0, cchThis, cchRemaining; for (i = 0; i < NUMBER_OF(NameOperations); i++) { PASM_IDENT_COMPONENT pThisOne = NULL; CHAR szStaticPureString[64]; PSTR pszPureString = szStaticPureString; SIZE_T cchPureString = NUMBER_OF(szStaticPureString); // // This segment can have this many characters in it // cchThis = *pcchTarget - cchTotal; // // Find the identity part // pThisOne = bsearch(NameOperations[i].pNameBit, pIdentComponents, ulIdentComponents, sizeof(*pIdentComponents), pfnIdentCompare); // // Append the extent to the output cursor // status = ExtentToPurifiedString( pThisOne, NameOperations[i].pszDefault, &pszPureString, &cchPureString); // // If this is the name part, adjust it downward sizewize before seeing if there's space // in the output buffer // if (NameOperations[i].fIsName && (cchPureString > 64)) { status = FixUpNamePortion(pszPureString, &cchPureString); if (!NT_SUCCESS(status)) { // // Ensure the buffer is freed before returning // if (pszPureString && (pszPureString != szStaticPureString)) { HeapFree(GetProcessHeap(), 0, (PVOID)pszPureString); pszPureString = szStaticPureString; } goto Exit; } } // // No space in the output buffer, or there was no output buffer // if (!pszOutputCursor || (cchPureString > (*pcchTarget - cchTotal))) { pszOutputCursor = NULL; cchThis = cchPureString + 1; } // // Otherwise, copy the pure string onto the output cursor // else { strncpy(pszOutputCursor, pszPureString, cchPureString); pszOutputCursor[cchPureString] = '_'; cchThis = cchPureString + 1; pszOutputCursor += cchThis; } if (pszPureString && (pszPureString != szStaticPureString)) { HeapFree(GetProcessHeap(), 0, (PVOID)pszPureString); } cchTotal += cchThis; } if (pszOutputCursor && ((*pcchTarget - cchTotal) > 8)) { sprintf(pszOutputCursor, "%08lx", ulHash); } cchTotal += 9; if (*pcchTarget < cchTotal) { status = STATUS_BUFFER_TOO_SMALL; } *pcchTarget = cchTotal; } Exit: if (pIdentComponents) { HeapFree(GetProcessHeap(), 0, (PVOID)pIdentComponents); pIdentComponents = NULL; } RtlDestroyGrowingList(&AttributeList); return status; } BOOL SxsIdentDetermineManifestPlacementPathEx( DWORD dwFlags, PVOID pvManifestData, SIZE_T cbLength, PSTR pszPlacementPath, SIZE_T *pcchPlacementPath ) { BOOL fSuccess = FALSE; NTSTATUS status; if (pszPlacementPath) { *pszPlacementPath = UNICODE_NULL; } // // Go do the thing // status = ProcessFile(pvManifestData, cbLength, pszPlacementPath, pcchPlacementPath); switch (status) { case STATUS_NO_MEMORY: SetLastError(ERROR_NOT_ENOUGH_MEMORY); break; case STATUS_BUFFER_TOO_SMALL: SetLastError(ERROR_INSUFFICIENT_BUFFER); break; case STATUS_NOT_FOUND: SetLastError(ERROR_FILE_NOT_FOUND); break; case STATUS_SUCCESS: fSuccess = TRUE; SetLastError(ERROR_SUCCESS); break; default: SetLastError(ERROR_GEN_FAILURE); break; } return fSuccess; } BOOL SxsIdentDetermineManifestPlacementPath( DWORD dwFlags, PCWSTR pcwszManifestPath, PSTR pszPlacementPath, SIZE_T *cchPlacementPath ) { UINT cchUserBufferSize; BOOL fSuccess = FALSE; HANDLE hFile = INVALID_HANDLE_VALUE; PVOID pvFileData = NULL; HANDLE hFileMapping = INVALID_HANDLE_VALUE; DWORD dwFileSize = 0; PASM_IDENT_COMPONENT pAsmIdentSorted = NULL; SIZE_T cAsmIdent = 0, cTemp; // // Some minimal requirements // if ((dwFlags != 0) || !pcwszManifestPath || !cchPlacementPath || ((*cchPlacementPath > 0) && (pszPlacementPath == NULL))) { SetLastError(ERROR_INVALID_PARAMETER); goto Exit; } if (pszPlacementPath) { *pszPlacementPath = UNICODE_NULL; } hFile = CreateFileW( pcwszManifestPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { goto Exit; } dwFileSize = GetFileSize(hFile, NULL); hFileMapping = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL); if ((hFileMapping == NULL) || (hFileMapping == INVALID_HANDLE_VALUE)) { goto Exit; } pvFileData = MapViewOfFile(hFileMapping, FILE_MAP_READ, 0, 0, dwFileSize); if (pvFileData == NULL) { goto Exit; } fSuccess = SxsIdentDetermineManifestPlacementPathEx( dwFlags, pvFileData, dwFileSize, pszPlacementPath, cchPlacementPath); Exit: if (pvFileData != NULL) { UnmapViewOfFile(pvFileData); pvFileData = NULL; } if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; } if (hFileMapping != INVALID_HANDLE_VALUE) { CloseHandle(hFileMapping); hFileMapping = INVALID_HANDLE_VALUE; } return fSuccess; }