// -------------------------------------------------------------------------------------- // // Gets next hot heap (*pHotHeap, of index *pHotHeapIndex) from the heaps directory. // Returns S_OK and fills *pHotHeap and *pHotHeapIndex with the next code:HotHeap information. // Returns S_FALSE, if the last hot heap was already returned. Clears *pHotHeap and *pHotHeapIndex in this // case. // Returns error code if the format is invalid. Clears *pHotHeap and *pHotHeapIndex in this case. // __checkReturn HRESULT HotHeapsDirectoryIterator::GetNext( HotHeap *pHotHeap, HeapIndex *pHotHeapIndex) { HRESULT hr; DataBuffer hotHeapHeaderData; DataBuffer hotHeapData; struct HotHeapsDirectoryEntry *pEntry; if (!m_RemainingHeapsDirectoryData.GetData<struct HotHeapsDirectoryEntry>( &pEntry)) { hr = S_FALSE; goto ErrExit; } if (!HeapIndex::IsValid(pEntry->m_nHeapIndex)) { Debug_ReportError("Invalid hot heaps directory format - invalid heap index."); IfFailGo(METADATA_E_INVALID_FORMAT); } pHotHeapIndex->Set(pEntry->m_nHeapIndex); hotHeapHeaderData = m_HotHeapsData; if (!hotHeapHeaderData.SkipToExactSize(pEntry->m_nHeapHeaderStart_NegativeOffset)) { Debug_ReportError("Invalid hot heaps directory format - heap header offset reaches in front of of hot heaps data."); IfFailGo(METADATA_E_INVALID_FORMAT); } struct HotHeapHeader *pHeader; if (!hotHeapHeaderData.PeekData<struct HotHeapHeader>(&pHeader)) { Debug_ReportError("Invalid hot heaps directory format - heap header reaches behind hot heaps data."); IfFailGo(METADATA_E_INVALID_FORMAT); } hotHeapData = m_HotHeapsData; if (!hotHeapData.TruncateBySize(pEntry->m_nHeapHeaderStart_NegativeOffset)) { Debug_ReportInternalError("There's a bug because previous call to SkipToExactSize succeeded."); IfFailGo(METADATA_E_INVALID_FORMAT); } IfFailGo(pHotHeap->Initialize(pHeader, hotHeapData)); _ASSERTE(hr == S_OK); return hr; ErrExit: pHotHeap->Clear(); pHotHeapIndex->SetInvalid(); return hr; } // HotHeapsDirectoryIterator::GetNext
// -------------------------------------------------------------------------------------- // // Validates hot heap structure (extension of code:Initialize checks). // __checkReturn HRESULT HotHeap::Debug_Validate() { // Additional verification, more strict checks than in code:Initialize S_UINT32 nValueOffsetTableStart = S_UINT32(2) * S_UINT32(m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset); if (nValueOffsetTableStart.IsOverflow() || (nValueOffsetTableStart.Value() != m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset)) { Debug_ReportError("Invalid hot heap header format."); return METADATA_E_INVALID_FORMAT; } if (m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset <= m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset) { Debug_ReportError("Invalid hot heap header format."); return METADATA_E_INVALID_FORMAT; } // Already validated against underflow in code:Initialize BYTE *pIndexTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset; UINT32 *rgIndexTable = reinterpret_cast<UINT32 *>(pIndexTableStart); // Already validated against underflow in code:Initialize BYTE *pValueOffsetTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset; UINT32 *rgValueOffsetTable = reinterpret_cast<UINT32 *>(pValueOffsetTableStart); // Already validated against underflow in code:Initialize BYTE *pValueHeapStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset; DataBuffer valueHeap( pValueHeapStart, m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset); // Already validated for % 4 == 0 in code:Initialize UINT32 cIndexTableCount = m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset / 4; UINT32 nPreviousValue = 0; for (UINT32 nIndex = 0; nIndex < cIndexTableCount; nIndex++) { if (nPreviousValue >= rgIndexTable[nIndex]) { Debug_ReportError("Invalid hot heap header format."); return METADATA_E_INVALID_FORMAT; } UINT32 nValueOffset = rgValueOffsetTable[nIndex]; if (nValueOffset >= valueHeap.GetSize()) { Debug_ReportError("Invalid hot heap header format."); return METADATA_E_INVALID_FORMAT; } // TODO: Verify item (depends if it is string, blob, guid or user string) } return S_OK; } // HotHeap::Debug_Validate
//***************************************************************************** // Skip over the header and find the actual stream data. Secure version of // GetFirstStream method. // The header is supposed to be verified by VerifySignature. // // Returns pointer to the first stream (behind storage header) and the size of // the remaining buffer in *pcbMd (could be 0). // Returns NULL if there is not enough buffer for reading the headers. The *pcbMd // could be changed if NULL returned. // // Caller has to check available buffer size before using the first stream. //***************************************************************************** PSTORAGESTREAM MDFormat::GetFirstStream_Verify( PSTORAGEHEADER pHeader, // Return copy of header struct. const void *pvMd, // Pointer to the full file. ULONG *pcbMd) // [in, out] Size of pvMd buffer (we don't want to read behind it) { const BYTE *pbMd; // Header data starts after signature. pbMd = (const BYTE *)pvMd; // Check read buffer overflow if (*pcbMd < sizeof(STORAGESIGNATURE)) { Debug_ReportError("Invalid MetaData - Storage signature doesn't fit."); return NULL; } pbMd += sizeof(STORAGESIGNATURE); *pcbMd -= sizeof(STORAGESIGNATURE); ULONG cbVersionString = ((STORAGESIGNATURE *)pvMd)->GetVersionStringLength(); // Check read buffer overflow if (*pcbMd < cbVersionString) { Debug_ReportError("Invalid MetaData storage signature - Version string doesn't fit."); return NULL; } pbMd += cbVersionString; *pcbMd -= cbVersionString; // Is there enough space for storage header? if (*pcbMd < sizeof(STORAGEHEADER)) { Debug_ReportError("Invalid MetaData storage header - Storage header doesn't fit."); return NULL; } PSTORAGEHEADER pHdr = (PSTORAGEHEADER) pbMd; *pHeader = *pHdr; pbMd += sizeof(STORAGEHEADER); *pcbMd -= sizeof(STORAGEHEADER); // ECMA specifies that the flags field is "reserved, must be 0". if (pHdr->GetFlags() != 0) { Debug_ReportError("Invalid MetaData storage header - Flags are not 0."); return NULL; } // The pointer is now at the first stream in the list. return (PSTORAGESTREAM)pbMd; } // MDFormat::GetFirstStream
// -------------------------------------------------------------------------------------- // // Gets stored data at index. // Returns S_FALSE if data index is not stored in hot heap. // __checkReturn HRESULT HotHeap::GetData( UINT32 nDataIndex, __in DataBlob *pData) { // Already validated against underflow in code:Initialize BYTE *pIndexTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset; // Already validated against underflow in code:Initialize BYTE *pValueOffsetTableStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset; // Already validated against underflow in code:Initialize BYTE *pValueHeapStart = reinterpret_cast<BYTE *>(m_pHotHeapHeader) - m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset; const UINT32 *pnFoundDataIndex = BinarySearch<UINT32>( reinterpret_cast<UINT32 *>(pIndexTableStart), m_pHotHeapHeader->m_nIndexTableStart_NegativeOffset / sizeof(UINT32), nDataIndex); if (pnFoundDataIndex == NULL) { // Index is not stored in hot data return S_FALSE; } _ASSERTE(((UINT32 *)pIndexTableStart <= pnFoundDataIndex) && (pnFoundDataIndex + 1 <= (UINT32 *)m_pHotHeapHeader)); // Index of found data index in the index table (note: it is not offset, but really index) UINT32 nIndexOfFoundDataIndex = (UINT32)(pnFoundDataIndex - (UINT32 *)pIndexTableStart); // Value offset contains positive offset to the ValueHeap start // Already validated against overflow in code:Initialize UINT32 nValueOffset_PositiveOffset = reinterpret_cast<UINT32 *>(pValueOffsetTableStart)[nIndexOfFoundDataIndex]; if (nValueOffset_PositiveOffset >= m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset) { pData->Clear(); Debug_ReportError("Invalid hot data format - value offset reaches behind the hot heap data."); return METADATA_E_INVALID_FORMAT; } pData->Init( pValueHeapStart + nValueOffset_PositiveOffset, m_pHotHeapHeader->m_nValueHeapStart_NegativeOffset - nValueOffset_PositiveOffset); return S_OK; } // HotHeap::GetData
// -------------------------------------------------------------------------------------- // // Initializes hot heap from its header and data. // Provides limited debug-only validation of the structure. // __checkReturn HRESULT HotHeap::Initialize( struct HotHeapHeader *pHotHeapHeader, DataBuffer hotHeapData) { _ASSERTE(hotHeapData.GetDataPointerBehind() == reinterpret_cast<BYTE *>(pHotHeapHeader)); UINT32 nMaximumNegativeOffset = hotHeapData.GetSize(); if (pHotHeapHeader->m_nIndexTableStart_NegativeOffset > nMaximumNegativeOffset) { m_pHotHeapHeader = NULL; Debug_ReportError("Invalid hot heap header format - invalid index table offset."); return METADATA_E_INVALID_FORMAT; } if ((pHotHeapHeader->m_nIndexTableStart_NegativeOffset % 4) != 0) { m_pHotHeapHeader = NULL; Debug_ReportError("Invalid hot heap header format - index table offset is not aligned."); return METADATA_E_INVALID_FORMAT; } if (pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset > nMaximumNegativeOffset) { m_pHotHeapHeader = NULL; Debug_ReportError("Invalid hot heap header format - invalid value offset table offset."); return METADATA_E_INVALID_FORMAT; } if ((pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset % 4) != 0) { m_pHotHeapHeader = NULL; Debug_ReportError("Invalid hot heap header format - value offset table offset is not aligned."); return METADATA_E_INVALID_FORMAT; } // Index table has to be behind value offset table if (pHotHeapHeader->m_nValueOffsetTableStart_NegativeOffset < pHotHeapHeader->m_nIndexTableStart_NegativeOffset) { m_pHotHeapHeader = NULL; Debug_ReportError("Invalid hot heap header format - value offset table doesn't start before index table."); return METADATA_E_INVALID_FORMAT; } if (pHotHeapHeader->m_nValueHeapStart_NegativeOffset > nMaximumNegativeOffset) { m_pHotHeapHeader = NULL; Debug_ReportError("Invalid hot heap header format - invalid value heap offset."); return METADATA_E_INVALID_FORMAT; } m_pHotHeapHeader = pHotHeapHeader; return S_OK; } // HotHeap::Initialize
//***************************************************************************** // CheckFileFormat // This function will determine if the in-memory image is a readonly, readwrite, // or ICR format. //***************************************************************************** HRESULT CheckFileFormat( LPVOID pData, ULONG cbData, MDFileFormat *pFormat) // [OUT] the file format { HRESULT hr = NOERROR; STORAGEHEADER sHdr; // Header for the storage. PSTORAGESTREAM pStream; // Pointer to each stream. int i; ULONG cbStreamBuffer; _ASSERTE(pFormat != NULL); *pFormat = MDFormat_Invalid; // Validate the signature of the format, or it isn't ours. if (FAILED(hr = MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData))) goto ErrExit; // Remaining buffer size behind the stream header (pStream). cbStreamBuffer = cbData; // Get back the first stream. pStream = MDFormat::GetFirstStream_Verify(&sHdr, pData, &cbStreamBuffer); if (pStream == NULL) { Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header."); IfFailGo(CLDB_E_FILE_CORRUPT); } // Loop through each stream and pick off the ones we need. for (i = 0; i < sHdr.GetiStreams(); i++) { // Do we have enough buffer to read stream header? if (cbStreamBuffer < sizeof(*pStream)) { Debug_ReportError("Stream header is not within MetaData block."); IfFailGo(CLDB_E_FILE_CORRUPT); } // Get next stream. PSTORAGESTREAM pNext = pStream->NextStream_Verify(); // Check that stream header is within the buffer. if (((LPBYTE)pStream >= ((LPBYTE)pData + cbData)) || ((LPBYTE)pNext > ((LPBYTE)pData + cbData))) { Debug_ReportError("Stream header is not within MetaData block."); hr = CLDB_E_FILE_CORRUPT; goto ErrExit; } // Check that the stream data starts and fits within the buffer. // need two checks on size because of wraparound. if ((pStream->GetOffset() > cbData) || (pStream->GetSize() > cbData) || ((pStream->GetSize() + pStream->GetOffset()) < pStream->GetOffset()) || ((pStream->GetSize() + pStream->GetOffset()) > cbData)) { Debug_ReportError("Stream data are not within MetaData block."); hr = CLDB_E_FILE_CORRUPT; goto ErrExit; } // Pick off the location and size of the data. if (strcmp(pStream->GetName(), COMPRESSED_MODEL_STREAM_A) == 0) { // Validate that only one of compressed/uncompressed is present. if (*pFormat != MDFormat_Invalid) { // Already found a good stream. Debug_ReportError("Compressed model stream #~ is second important stream."); hr = CLDB_E_FILE_CORRUPT; goto ErrExit; } // Found the compressed meta data stream. *pFormat = MDFormat_ReadOnly; } else if (strcmp(pStream->GetName(), ENC_MODEL_STREAM_A) == 0) { // Validate that only one of compressed/uncompressed is present. if (*pFormat != MDFormat_Invalid) { // Already found a good stream. Debug_ReportError("ENC model stream #- is second important stream."); hr = CLDB_E_FILE_CORRUPT; goto ErrExit; } // Found the ENC meta data stream. *pFormat = MDFormat_ReadWrite; } else if (strcmp(pStream->GetName(), SCHEMA_STREAM_A) == 0) { // Found the uncompressed format *pFormat = MDFormat_ICR; // keep going. We may find the compressed format later. // If so, we want to use the compressed format. } // Pick off the next stream if there is one. pStream = pNext; cbStreamBuffer = (ULONG)((LPBYTE)pData + cbData - (LPBYTE)pNext); } if (*pFormat == MDFormat_Invalid) { // Didn't find a good stream. Debug_ReportError("Cannot find MetaData stream."); hr = CLDB_E_FILE_CORRUPT; } ErrExit: return hr; } // CheckFileFormat
//***************************************************************************** // Given an StgIO, opens compressed streams and do proper initialization. // This is a helper for other Init functions. //***************************************************************************** __checkReturn HRESULT CLiteWeightStgdbRW::InitFileForRead( StgIO * pStgIO, // For file i/o. int bReadOnly) // If read-only open. { TiggerStorage * pStorage = NULL; void * pvData; ULONG cbData; HRESULT hr = NOERROR; // Allocate a new storage object which has IStorage on it. pStorage = new (nothrow) TiggerStorage(); IfNullGo(pStorage); // Init the storage object on the backing storage. OptionValue ov; IfFailGo(m_MiniMd.GetOption(&ov)); IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion)); // Save pointers to header structure for version string. _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0)); IfFailGo(pStorage->GetHeaderPointer(&m_pvMd, &m_cbMd)); // Check to see if this is a minimal metadata if (SUCCEEDED(pStorage->OpenStream(MINIMAL_MD_STREAM, &cbData, &pvData))) { m_MiniMd.m_fMinimalDelta = TRUE; } // Load the string pool. if (SUCCEEDED(hr = pStorage->OpenStream(STRING_POOL_STREAM, &cbData, &pvData))) { // String pool has to end with a null-terminator, therefore we don't have to check string pool // content on access. // Shrink size of the pool to the last null-terminator found. while (cbData != 0) { if (((LPBYTE)pvData)[cbData - 1] == 0) { // We have found last null terminator break; } // Shrink size of the pool cbData--; Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap."); } IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, pvData, cbData, bReadOnly)); } else { if (hr != STG_E_FILENOTFOUND) { IfFailGo(hr); } IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, NULL, 0, bReadOnly)); } // Load the user string blob pool. if (SUCCEEDED(hr = pStorage->OpenStream(US_BLOB_POOL_STREAM, &cbData, &pvData))) { IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, pvData, cbData, bReadOnly)); } else { if (hr != STG_E_FILENOTFOUND) { IfFailGo(hr); } IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, NULL, 0, bReadOnly)); } // Load the guid pool. if (SUCCEEDED(hr = pStorage->OpenStream(GUID_POOL_STREAM, &cbData, &pvData))) { IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, pvData, cbData, bReadOnly)); } else { if (hr != STG_E_FILENOTFOUND) { IfFailGo(hr); } IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, NULL, 0, bReadOnly)); } // Load the blob pool. if (SUCCEEDED(hr = pStorage->OpenStream(BLOB_POOL_STREAM, &cbData, &pvData))) { IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, pvData, cbData, bReadOnly)); } else { if (hr != STG_E_FILENOTFOUND) { IfFailGo(hr); } IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, NULL, 0, bReadOnly)); } // Open the metadata. hr = pStorage->OpenStream(COMPRESSED_MODEL_STREAM, &cbData, &pvData); if (hr == STG_E_FILENOTFOUND) { IfFailGo(pStorage->OpenStream(ENC_MODEL_STREAM, &cbData, &pvData)); } IfFailGo(m_MiniMd.InitOnMem(pvData, cbData, bReadOnly)); IfFailGo(m_MiniMd.PostInit(0)); ErrExit: if (pStorage != NULL) { delete pStorage; } return hr; } // CLiteWeightStgdbRW::InitFileForRead
//***************************************************************************** // Get info about the MD stream. // Low level access to stream data. Intended for metainfo, and such. //***************************************************************************** __checkReturn STDMETHODIMP CLiteWeightStgdbRW::GetRawStreamInfo( ULONG ix, // [IN] Stream ordinal desired. const char **ppchName, // [OUT] put pointer to stream name here. const void **ppv, // [OUT] put pointer to MD stream here. ULONG *pcb) // [OUT] put size of the stream here. { HRESULT hr = NOERROR; STORAGEHEADER sHdr; // Header for the storage. PSTORAGESTREAM pStream; // Pointer to each stream. ULONG i; // Loop control. void *pData; ULONG cbData; #ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE if (m_pStgIO == NULL) IfFailGo(COR_E_NOTSUPPORTED); #endif pData = m_pStgIO->m_pData; cbData = m_pStgIO->m_cbData; // Validate the signature of the format, or it isn't ours. IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData)); // Get back the first stream. pStream = MDFormat::GetFirstStream(&sHdr, pData); if (pStream == NULL) { Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header."); IfFailGo(CLDB_E_FILE_CORRUPT); } // Check that the requested stream exists. if (ix >= sHdr.GetiStreams()) return S_FALSE; // Skip to the desired stream. for (i = 0; i < ix; i++) { PSTORAGESTREAM pNext = pStream->NextStream(); // Check that stream header is within the buffer. if (((LPBYTE)pStream >= ((LPBYTE)pData + cbData)) || ((LPBYTE)pNext > ((LPBYTE)pData + cbData))) { Debug_ReportError("Stream header is not within MetaData block."); hr = CLDB_E_FILE_CORRUPT; goto ErrExit; } // Check that the stream data starts and fits within the buffer. // need two checks on size because of wraparound. if ((pStream->GetOffset() > cbData) || (pStream->GetSize() > cbData) || ((pStream->GetSize() + pStream->GetOffset()) > cbData)) { Debug_ReportError("Stream data are not within MetaData block."); hr = CLDB_E_FILE_CORRUPT; goto ErrExit; } // Pick off the next stream if there is one. pStream = pNext; } if (pStream != NULL) { *ppv = (const void *)((const BYTE *)pData + pStream->GetOffset()); *pcb = pStream->GetSize(); *ppchName = pStream->GetName(); } else { *ppv = NULL; *pcb = 0; *ppchName = NULL; // Invalid input to the method hr = CLDB_E_FILE_CORRUPT; } ErrExit: return hr; } // CLiteWeightStgdbRW::GetRawStreamInfo
HRESULT MDFormat::VerifySignature( PSTORAGESIGNATURE pSig, // The signature to check. ULONG cbData) { HRESULT hr = S_OK; // If signature didn't match, you shouldn't be here. ULONG dwSignature = pSig->GetSignature(); if (dwSignature == STORAGE_MAGIC_OLD_SIG) { Debug_ReportError("Invalid MetaData storage signature - old magic signature +MOC."); return PostError(CLDB_E_FILE_OLDVER, 1, 0); } if (dwSignature != STORAGE_MAGIC_SIG) { Debug_ReportError("Invalid MetaData storage signature - unrecognized magic signature, should be BSJB."); return PostError(CLDB_E_FILE_CORRUPT); } // Check for overflow ULONG lVersionString = pSig->GetVersionStringLength(); ULONG sum = sizeof(STORAGESIGNATURE) + lVersionString; if ((sum < sizeof(STORAGESIGNATURE)) || (sum < lVersionString)) { Debug_ReportError("Invalid MetaData storage signature - version string too long, integer overflow."); return PostError(CLDB_E_FILE_CORRUPT); } // Check for invalid version string size if ((sizeof(STORAGESIGNATURE) + lVersionString) > cbData) { Debug_ReportError("Invalid MetaData storage signature - version string too long."); return PostError(CLDB_E_FILE_CORRUPT); } // Check that the version string is null terminated. This string // is ANSI, so no double-null checks need to be made. { BYTE *pStart = &pSig->pVersion[0]; BYTE *pEnd = pStart + lVersionString + 1; // Account for terminating NULL BYTE *pCur; for (pCur = pStart; pCur < pEnd; pCur++) { if (*pCur == 0) break; } // If we got to the end without hitting a NULL, we have a bad version string if (pCur == pEnd) { Debug_ReportError("Invalid MetaData storage signature - version string has not null-terminator."); return PostError(CLDB_E_FILE_CORRUPT); } } #if !defined(FEATURE_METADATA_STANDALONE_WINRT) // Only a specific version of the 0.x format is supported by this code // in order to support the NT 5 beta clients which used this format. if (pSig->GetMajorVer() == FILE_VER_MAJOR_v0) { if (pSig->GetMinorVer() < FILE_VER_MINOR_v0) { Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1."); hr = CLDB_E_FILE_OLDVER; } } else #endif // !defined(FEATURE_METADATA_STANDALONE_WINRT) // There is currently no code to migrate an old format of the 1.x. This // would be added only under special circumstances. if ((pSig->GetMajorVer() != FILE_VER_MAJOR) || (pSig->GetMinorVer() != FILE_VER_MINOR)) { Debug_ReportError("Invalid MetaData storage signature - unrecognized version, should be 1.1."); hr = CLDB_E_FILE_OLDVER; } if (FAILED(hr)) hr = PostError(hr, (int)pSig->GetMajorVer(), (int)pSig->GetMinorVer()); return hr; } // MDFormat::VerifySignature