diff options
Diffstat (limited to 'src/md/enc/liteweightstgdbrw.cpp')
-rw-r--r-- | src/md/enc/liteweightstgdbrw.cpp | 1278 |
1 files changed, 1278 insertions, 0 deletions
diff --git a/src/md/enc/liteweightstgdbrw.cpp b/src/md/enc/liteweightstgdbrw.cpp new file mode 100644 index 0000000000..12779f59c0 --- /dev/null +++ b/src/md/enc/liteweightstgdbrw.cpp @@ -0,0 +1,1278 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +//***************************************************************************** + +// +// LiteWeightStgdb.cpp +// +// This contains definition of class CLiteWeightStgDB. This is light weight +// read-only implementation for accessing compressed meta data format. +// +//***************************************************************************** +#include "stdafx.h" // Precompiled header. + +#include "metamodelrw.h" +#include "liteweightstgdb.h" + +// include stgdatabase.h for GUID_POOL_STREAM definition +// #include "stgdatabase.h" + +// include StgTiggerStorage for TiggerStorage definition +#include "stgtiggerstorage.h" +#include "stgio.h" +#include "pedecoder.h" + +#include <log.h> + + +#ifndef TYPELIB_SIG +#define TYPELIB_SIG_MSFT 0x5446534D // MSFT +#define TYPELIB_SIG_SLTG 0x47544C53 // SLTG +#endif + +//***************************************************************************** +// Checks the given storage object to see if it is an NT PE image. +//***************************************************************************** +int _IsNTPEImage( // true if file is NT PE image. + StgIO *pStgIO) // Storage object. +{ + LONG lfanew=0; // Offset in DOS header to NT header. + ULONG lSignature=0; // For NT header signature. + HRESULT hr; + + // Read DOS header to find the NT header offset. + if (FAILED(hr = pStgIO->Seek(60, FILE_BEGIN)) || + FAILED(hr = pStgIO->Read(&lfanew, sizeof(LONG), 0))) + { + return (false); + } + + // Seek to the NT header and read the signature. + if (FAILED(hr = pStgIO->Seek(VAL32(lfanew), FILE_BEGIN)) || + FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) || + FAILED(hr = pStgIO->Seek(0, FILE_BEGIN))) + { + return (false); + } + + // If the signature is a match, then we have a PE format. + if (lSignature == VAL32(IMAGE_NT_SIGNATURE)) + return (true); + else + return (false); +} + +BOOL _GetFileTypeForPathExt(StgIO * pStgIO, FILETYPE * piType) +{ + // Avoid confusion. + *piType = pStgIO->GetFileType(); + + // All file types except .obj have a signature built in. You should + // not get to this code for those file types unless that file is corrupt, + // or someone has changed a format without updating this code. + _ASSERTE((*piType == FILETYPE_UNKNOWN) || (*piType == FILETYPE_NTOBJ) || (*piType == FILETYPE_TLB)); + + // If we found a type, then you're ok. + return (*piType != FILETYPE_UNKNOWN); +} + +HRESULT _GetFileTypeForPath(StgIO *pStgIO, FILETYPE *piType) +{ + ULONG lSignature=0; + HRESULT hr; + + // Assume native file. + *piType = FILETYPE_CLB; + + // Need to read signature to see what type it is. + if (!(pStgIO->GetFlags() & DBPROP_TMODEF_CREATE)) + { + if (FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) || + FAILED(hr = pStgIO->Seek(0, FILE_BEGIN))) + { + return (hr); + } + lSignature = VAL32(lSignature); + if (lSignature == STORAGE_MAGIC_SIG) + *piType = FILETYPE_CLB; + else if ((WORD) lSignature ==IMAGE_DOS_SIGNATURE && _IsNTPEImage(pStgIO)) + *piType = FILETYPE_NTPE; + else if (lSignature == TYPELIB_SIG_MSFT || lSignature == TYPELIB_SIG_SLTG) + *piType = FILETYPE_TLB; + else if (!_GetFileTypeForPathExt(pStgIO, piType)) + return CLDB_E_FILE_CORRUPT; + } + return S_OK; +} + +//***************************************************************************** +// Prepare to go away. +//***************************************************************************** +CLiteWeightStgdbRW::~CLiteWeightStgdbRW() +{ + // Free up this stacks reference on the I/O object. + if (m_pStgIO != NULL) + { + m_pStgIO->Release(); + m_pStgIO = NULL; + } + + if (m_pStreamList != NULL) + { + delete m_pStreamList; + } + + if (m_wszFileName != NULL) + { + delete [] m_wszFileName; + } +} + +//***************************************************************************** +// Open an in-memory metadata section for read +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::InitOnMem( + ULONG cbData, // count of bytes in pData + LPCVOID pData, // points to meta data section in memory + int bReadOnly) // If true, read-only. +{ + StgIO *pStgIO = NULL; // For file i/o. + HRESULT hr = NOERROR; + + if ((pStgIO = new (nothrow) StgIO) == 0) + IfFailGo( E_OUTOFMEMORY); + + // Open the storage based on the pbData and cbData + IfFailGo( pStgIO->Open( + NULL, // filename + STGIO_READ, + pData, + cbData, + NULL, // IStream* + NULL) // LPSecurityAttributes + ); + + IfFailGo( InitFileForRead(pStgIO, bReadOnly) ); + +ErrExit: + if (SUCCEEDED(hr)) + { + m_pStgIO = pStgIO; + } + else + { + if (pStgIO) + pStgIO->Release(); + } + return hr; +} // CLiteWeightStgdbRW::InitOnMem + + +//***************************************************************************** +// 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 + +//***************************************************************************** +// Open a metadata section for read +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::OpenForRead( + LPCWSTR szDatabase, // Name of database. + void *pbData, // Data to open on top of, 0 default. + ULONG cbData, // How big is the data. + DWORD dwFlags) // Flags for the open. +{ + LPCWSTR pNoFile=W(""); // Constant for empty file name. + StgIO *pStgIO = NULL; // For file i/o. + HRESULT hr; + + m_pImage = NULL; + m_dwImageSize = 0; + m_eFileType = FILETYPE_UNKNOWN; + // szDatabase, and pbData are mutually exclusive. Only one may be + // non-NULL. Having both NULL means empty stream creation. + // + _ASSERTE(!(szDatabase && (pbData))); + _ASSERTE(!(pbData && (szDatabase))); + + // Open on memory needs there to be something to work with. + if (pbData && cbData == 0) + IfFailGo(CLDB_E_NO_DATA); + + // Make sure we have a path to work with. + if (!szDatabase) + szDatabase = pNoFile; + + // Sanity check the name lentgh. + if (!IsValidFileNameLength(szDatabase)) + { + IfFailGo(E_INVALIDARG); + } + + // If we have storage to work with, init it and get type. + if (*szDatabase || pbData) + { + // Allocate a storage instance to use for i/o. + if ((pStgIO = new (nothrow) StgIO) == 0) + IfFailGo( E_OUTOFMEMORY ); + + DBPROPMODE dmOpenFlags = DBPROP_TMODEF_READ; + + // If we're taking ownership of this memory..... + if (IsOfTakeOwnership(dwFlags)) + { +#ifdef FEATURE_METADATA_STANDALONE_WINRT_RO + // Shared memory uses ole32.dll - we cannot depend on it in the standalone WinRT Read-Only DLL + IfFailGo(E_INVALIDARG); +#else + dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_SHAREDMEM); +#endif //!FEATURE_METADATA_STANDALONE_WINRT_RO + } +#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES + if (IsOfTrustedImage(dwFlags)) + dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_TRYLOADLIBRARY); +#endif + + // Open the storage so we can read the signature if there is already data. + IfFailGo( pStgIO->Open(szDatabase, + dmOpenFlags, + pbData, + cbData, + 0, // IStream* + NULL) ); + + // Determine the type of file we are working with. + IfFailGo( _GetFileTypeForPath(pStgIO, &m_eFileType) ); + } + + // Check for default type. + if (m_eFileType == FILETYPE_CLB) + { + // If user wanted us to make a local copy of the data, do that now. + if (IsOfCopyMemory(dwFlags)) + IfFailGo(pStgIO->LoadFileToMemory()); + + // Try the native .clb file. + IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) ); + } + // PE/COFF executable/object format. This requires us to find the .clb + // inside the binary before doing the Init. + else if (m_eFileType == FILETYPE_NTPE || m_eFileType == FILETYPE_NTOBJ) + { + //<TODO>@FUTURE: Ideally the FindImageMetaData function + //@FUTURE: would take the pStgIO and map only the part of the file where + //@FUTURE: our data lives, leaving the rest alone. This would be smaller + //@FUTURE: working set for us.</TODO> + void *ptr; + ULONG cbSize; + + // Map the entire binary for the FindImageMetaData function. + IfFailGo( pStgIO->MapFileToMem(ptr, &cbSize) ); + + // Find the .clb inside of the content. + if (m_eFileType == FILETYPE_NTPE) + { + m_pImage = ptr; + m_dwImageSize = cbSize; + hr = FindImageMetaData(ptr, + cbSize, + pStgIO->GetMemoryMappedType() == MTYPE_IMAGE, + &ptr, + &cbSize); + } + else + { + _ASSERTE(pStgIO->GetMemoryMappedType() != MTYPE_IMAGE); + hr = FindObjMetaData(ptr, cbSize, &ptr, &cbSize); + } + // Was the metadata found inside the PE file? + if (FAILED(hr)) + { + if (hr == E_OUTOFMEMORY) + IfFailGo(E_OUTOFMEMORY); + + // No clb in the PE, assume it is a type library. + m_eFileType = FILETYPE_TLB; + + // Let the caller deal with a TypeLib. + IfFailGo(hr); + } + else + { + // Metadata was found inside the file. + // Now reset the base of the stg object so that all memory accesses + // are relative to the .clb content. + // + IfFailGo( pStgIO->SetBaseRange(ptr, cbSize) ); + + // If user wanted us to make a local copy of the data, do that now. + if (IsOfCopyMemory(dwFlags)) + { + // Cache the PEKind, Machine. + GetPEKind(pStgIO->GetMemoryMappedType(), NULL, NULL); + // Copy the file into memory; releases the file. + IfFailGo(pStgIO->LoadFileToMemory()); + // No longer have the image. + m_pImage = NULL; + m_dwImageSize = 0; + } + + // Defer to the normal lookup. + IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) ); + } + } + else if (m_eFileType == FILETYPE_TLB) + { + // Let the caller deal with a TypeLib. + IfFailGo(CLDB_E_NO_DATA); + } + // This spells trouble, we need to handle all types we might find. + else + { + _ASSERTE(!"Unknown file type."); + IfFailGo( E_FAIL ); + } + + // Save off everything. + IfFailGo(SetFileName(szDatabase)); + + // If this was a file... + if (pbData == NULL) + { + WIN32_FILE_ATTRIBUTE_DATA faData; + if (!WszGetFileAttributesEx(szDatabase, GetFileExInfoStandard, &faData)) + IfFailGo(E_FAIL); + m_dwDatabaseLFS = faData.nFileSizeLow; + m_dwDatabaseLFT = faData.ftLastWriteTime.dwLowDateTime; + } + +ErrExit: + if (SUCCEEDED(hr)) + { + m_pStgIO = pStgIO; + } + else + { + if (pStgIO != NULL) + pStgIO->Release(); + } + return hr; +} + +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE +// Open a metadata section for read/write +__checkReturn +HRESULT CLiteWeightStgdbRW::OpenForRead( + IMDCustomDataSource *pDataSource, // data to open on top of + DWORD dwFlags) // Flags for the open. +{ + LPCWSTR pNoFile = W(""); // Constant for empty file name. + StgIO *pStgIO = NULL; // For file i/o. + HRESULT hr; + + m_pImage = NULL; + m_dwImageSize = 0; + m_eFileType = FILETYPE_UNKNOWN; + + IfFailGo(m_MiniMd.InitOnCustomDataSource(pDataSource)); + IfFailGo(m_MiniMd.PostInit(0)); + + // Save off everything. + IfFailGo(SetFileName(pNoFile)); + +ErrExit: + return hr; +} +#endif + +// Read/Write versions. +//***************************************************************************** +// Init the Stgdb and its subcomponents. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::InitNew() +{ + InitializeLogging(); + LOG((LF_METADATA, LL_INFO10, "Metadata logging enabled\n")); + + //<TODO>@FUTURE: should probably init the pools here instead of in the MiniMd.</TODO> + return m_MiniMd.InitNew(); +} + +//***************************************************************************** +// Determine what the size of the saved data will be. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetSaveSize(// S_OK or error. + CorSaveSize fSave, // Quick or accurate? + UINT32 *pcbSaveSize, // Put the size here. + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) // Profile data for working set optimization +{ + HRESULT hr = S_OK; // A result. + UINT32 cbTotal = 0; // The total size. + UINT32 cbSize = 0; // Size of a component. + + m_cbSaveSize = 0; + + // Allocate stream list if not already done. + if (m_pStreamList == NULL) + { + IfNullGo(m_pStreamList = new (nothrow) STORAGESTREAMLST); + } + else + { + m_pStreamList->Clear(); + } + + // Make sure the user string pool is not empty. An empty user string pool causes + // problems with edit and continue + + if (m_MiniMd.m_UserStringHeap.GetUnalignedSize() <= 1) + { + if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode) && + !m_MiniMd.IsMinimalDelta()) + { + BYTE rgData[] = {' ', 0, 0}; + UINT32 nIndex_Ignore; + IfFailGo(m_MiniMd.PutUserString( + MetaData::DataBlob(rgData, sizeof(rgData)), + &nIndex_Ignore)); + } + } + + // If we're saving a delta metadata, figure out how much space it will take to + // save the minimal metadata stream (used only to identify that we have a delta + // metadata... nothing should be in that stream. + if ((m_MiniMd.m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateDelta) + { + IfFailGo(AddStreamToList(0, MINIMAL_MD_STREAM)); + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(MINIMAL_MD_STREAM, 0, &cbSize)); + cbTotal += cbSize; + } + + if (reorderingOptions & ReArrangeStringPool) + { + if (pProfileData != NULL) + { + UINT32 cbHotSize = 0; // Size of pool data. + UINT32 cbStream; // Size of just the stream. + DWORD bCompressed; // Will the stream be compressed data? + + // Ask the metadata to size its hot data. + IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData)); + cbStream = cbHotSize; + m_bSaveCompressed = bCompressed; + + if (cbHotSize != 0) + { + // Add this item to the save list. + IfFailGo(AddStreamToList(cbHotSize, HOT_MODEL_STREAM)); + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(HOT_MODEL_STREAM, cbHotSize, &cbHotSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + HOT_MODEL_STREAM, cbStream, cbHotSize)); + + cbTotal += cbHotSize; + } + } + + // get string pool save size + IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize)); + cbTotal += cbSize; + } + + // Query the MiniMd for its size. + IfFailGo(GetTablesSaveSize(fSave, &cbSize, reorderingOptions, pProfileData)); + cbTotal += cbSize; + + // Get the pools' sizes. + if( !(reorderingOptions & ReArrangeStringPool) ) + { + IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize)); + cbTotal += cbSize; + } + IfFailGo(GetPoolSaveSize(US_BLOB_POOL_STREAM, MDPoolUSBlobs, &cbSize)); + cbTotal += cbSize; + IfFailGo(GetPoolSaveSize(GUID_POOL_STREAM, MDPoolGuids, &cbSize)); + cbTotal += cbSize; + IfFailGo(GetPoolSaveSize(BLOB_POOL_STREAM, MDPoolBlobs, &cbSize)); + cbTotal += cbSize; + + // Finally, ask the storage system to add fixed overhead it needs for the + // file format. The overhead of each stream has already be calculated as + // part of GetStreamSaveSize. What's left is the signature and header + // fixed size overhead. + IfFailGo(TiggerStorage::GetStorageSaveSize((ULONG *)&cbTotal, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize total is %d.\n", cbTotal)); + + // The list of streams that will be saved are now in the stream save list. + // Next step is to walk that list and fill out the correct offsets. This is + // done here so that the data can be streamed without fixing up the header. + TiggerStorage::CalcOffsets(m_pStreamList, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion); + + if (pcbSaveSize != NULL) + { + *pcbSaveSize = cbTotal; + } + + // Don't cache the value for the EnC case + if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode)) + m_cbSaveSize = cbTotal; + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::GetSaveSize + +//***************************************************************************** +// Get the save size of one of the pools. Also adds the pool's stream to +// the list of streams to be saved. +//***************************************************************************** +__checkReturn +HRESULT +CLiteWeightStgdbRW::GetPoolSaveSize( + LPCWSTR szHeap, // Name of the heap stream. + int iPool, // The pool of which to get size. + UINT32 *pcbSaveSize) // Add pool data to this value. +{ + UINT32 cbSize = 0; // Size of pool data. + UINT32 cbStream; // Size of just the stream. + HRESULT hr; + + *pcbSaveSize = 0; + + // If there is no data, then don't bother. + if (m_MiniMd.IsPoolEmpty(iPool)) + return (S_OK); + + // Ask the pool to size its data. + IfFailGo(m_MiniMd.GetPoolSaveSize(iPool, &cbSize)); + cbStream = cbSize; + + // Add this item to the save list. + IfFailGo(AddStreamToList(cbSize, szHeap)); + + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(szHeap, cbSize, &cbSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + szHeap, cbStream, cbSize)); + + // Give the size of the pool to the caller's total. + *pcbSaveSize = cbSize; + +ErrExit: + return hr; +} + +//***************************************************************************** +// Get the save size of the metadata tables. Also adds the tables stream to +// the list of streams to be saved. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetTablesSaveSize( + CorSaveSize fSave, + UINT32 *pcbSaveSize, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) // Add pool data to this value. +{ + UINT32 cbSize = 0; // Size of pool data. + UINT32 cbHotSize = 0; // Size of pool data. + UINT32 cbStream; // Size of just the stream. + DWORD bCompressed; // Will the stream be compressed data? + LPCWSTR szName; // What will the name of the pool be? + HRESULT hr; + + *pcbSaveSize = 0; + + if( !(reorderingOptions & ReArrangeStringPool) ) + { + if (pProfileData != NULL) + { + // Ask the metadata to size its hot data. + IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData)); + cbStream = cbHotSize; + m_bSaveCompressed = bCompressed; + + if (cbHotSize != 0) + { + szName = HOT_MODEL_STREAM; + + // Add this item to the save list. + IfFailGo(AddStreamToList(cbHotSize, szName)); + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbHotSize, &cbHotSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + szName, cbStream, cbHotSize)); + } + } + } + // Ask the metadata to size its data. + IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbSize, &bCompressed)); + cbStream = cbSize; + m_bSaveCompressed = bCompressed; + szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM; + + // Add this item to the save list. + IfFailGo(AddStreamToList(cbSize, szName)); + + // Ask the storage system to add stream fixed overhead. + IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbSize, &cbSize)); + + // Log the size info. + LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n", + szName, cbStream, cbSize)); + + // Give the size of the pool to the caller's total. + *pcbSaveSize = cbHotSize + cbSize; + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::GetTablesSaveSize + +//***************************************************************************** +// Add a stream, and its size, to the list of streams to be saved. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::AddStreamToList( + UINT32 cbSize, + LPCWSTR szName) +{ + HRESULT hr = S_OK; + PSTORAGESTREAM pItem; // New item to allocate & fill. + + // Add a new item to the end of the list. + IfNullGo(pItem = m_pStreamList->Append()); + + // Fill out the data. + pItem->SetOffset(0); + pItem->SetSize((ULONG)cbSize); + pItem->SetName(szName); + +ErrExit: + return hr; +} + +//***************************************************************************** +// Save the data to a stream. A TiggerStorage sub-allocates streams within +// the stream. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::SaveToStream( + IStream *pIStream, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr = S_OK; // A result. + StgIO *pStgIO = 0; + TiggerStorage *pStorage = 0; + + // Allocate a storage subsystem and backing store. + IfNullGo(pStgIO = new (nothrow) StgIO); + IfNullGo(pStorage = new (nothrow) TiggerStorage); + + // Open around this stream for write. + IfFailGo(pStgIO->Open(W(""), + DBPROP_TMODEF_DFTWRITEMASK, + 0, 0, // pbData, cbData + pIStream, + 0)); // LPSecurityAttributes + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion)); + + // Save worker will do tables, pools. + IfFailGo(SaveToStorage(pStorage, reorderingOptions, pProfileData)); + +ErrExit: + if (pStgIO != NULL) + pStgIO->Release(); + if (pStorage != NULL) + delete pStorage; + return hr; +} // CLiteWeightStgdbRW::SaveToStream + +//***************************************************************************** +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::SaveToStorage( + TiggerStorage *pStorage, + MetaDataReorderingOptions reorderingOptions, + CorProfileData *pProfileData) +{ + HRESULT hr; // A result. + LPCWSTR szName; // Name of the tables stream. + IStream *pIStreamTbl = 0; + UINT32 cb; + UINT32 cbSaveSize = m_cbSaveSize; + + // Must call GetSaveSize to cache the streams up front. + // Don't trust cached values in the delta case... if there was a previous call to get + // a non-delta size, it will be incorrect. + if ((m_cbSaveSize == 0) || IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode)) + { + IfFailGo(GetSaveSize(cssAccurate, &cbSaveSize)); + } + + // Save the header of the data file. + IfFailGo(pStorage->WriteHeader(m_pStreamList, 0, NULL)); + + // If this is a minimal delta, write a stream marker + if (IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode)) + { + IfFailGo(pStorage->CreateStream(MINIMAL_MD_STREAM, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStreamTbl)); + pIStreamTbl->Release(); + pIStreamTbl = 0; + } + + if (pProfileData != NULL) + { + DWORD bCompressed; + UINT32 cbHotSize; + // Will the stream be compressed data? + + // Only create this additional stream if it will be non-empty + IfFailGo(m_MiniMd.GetSaveSize(cssAccurate, &cbHotSize, &bCompressed, reorderingOptions, pProfileData)); + + if (cbHotSize > 0) + { + // Create a stream and save the hot tables. + szName = HOT_MODEL_STREAM; + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStreamTbl)); + IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, reorderingOptions, pProfileData)); + pIStreamTbl->Release(); + pIStreamTbl = 0; + } + } + + if (reorderingOptions & ReArrangeStringPool) + { + // Save the string pool before the tables when we do not have the string pool cache + IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings)); + } + + // Create a stream and save the tables. + szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM; + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStreamTbl)); + IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, NoReordering, NULL)); + pIStreamTbl->Release(); + pIStreamTbl = 0; + + // Save the pools. + if (!(reorderingOptions & ReArrangeStringPool)) + { + // string pool must be saved after the tables when we have the string pool cache + IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings)); + } + IfFailGo(SavePool(US_BLOB_POOL_STREAM, pStorage, MDPoolUSBlobs)); + IfFailGo(SavePool(GUID_POOL_STREAM, pStorage, MDPoolGuids)); + IfFailGo(SavePool(BLOB_POOL_STREAM, pStorage, MDPoolBlobs)); + + // Write the header to disk. + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + + IfFailGo(pStorage->WriteFinished(m_pStreamList, (ULONG *)&cb, IsENCDelta(ov.m_UpdateMode))); + + _ASSERTE(cbSaveSize == cb); + + // Let the Storage release some memory. + pStorage->ResetBackingStore(); + + IfFailGo(m_MiniMd.SaveDone()); + +ErrExit: + if (pIStreamTbl != NULL) + pIStreamTbl->Release(); + delete m_pStreamList; + m_pStreamList = 0; + m_cbSaveSize = 0; + return hr; +} // CLiteWeightStgdbRW::SaveToStorage + +//***************************************************************************** +// Save a pool of data out to a stream. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::SavePool( // Return code. + LPCWSTR szName, // Name of stream on disk. + TiggerStorage *pStorage, // The storage to put data in. + int iPool) // The pool to save. +{ + IStream *pIStream=0; // For writing. + HRESULT hr; + + // If there is no data, then don't bother. + if (m_MiniMd.IsPoolEmpty(iPool)) + return (S_OK); + + // Create the new stream to hold this table and save it. + IfFailGo(pStorage->CreateStream(szName, + STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, + 0, 0, &pIStream)); + IfFailGo(m_MiniMd.SavePoolToStream(iPool, pIStream)); + +ErrExit: + if (pIStream) + pIStream->Release(); + return hr; +} // CLiteWeightStgdbRW::SavePool + + +//***************************************************************************** +// Save the metadata to a file. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::Save( + LPCWSTR szDatabase, // Name of file to which to save. + DWORD dwSaveFlags) // Flags for the save. +{ + TiggerStorage * pStorage = NULL; // IStorage object. + StgIO * pStgIO = NULL; // Backing storage. + HRESULT hr = S_OK; + + if (m_wszFileName == NULL) + { + if (szDatabase == NULL) + { + // Make sure that a NULL is not passed in the first time around. + _ASSERTE(!"Not allowed to pass a NULL for filename on the first call to Save."); + return E_INVALIDARG; + } + else + { + // Save the file name. + IfFailGo(SetFileName(szDatabase)); + } + } + else if ((szDatabase != NULL) && (SString::_wcsicmp(szDatabase, m_wszFileName) != 0)) + { + // Save the file name. + IfFailGo(SetFileName(szDatabase)); + } + + // Sanity check the name. + if (!IsValidFileNameLength(m_wszFileName)) + { + IfFailGo(E_INVALIDARG); + } + + m_eFileType = FILETYPE_CLB; + + // Allocate a new storage object. + IfNullGo(pStgIO = new (nothrow) StgIO); + + // Create the output file. + IfFailGo(pStgIO->Open(m_wszFileName, + DBPROP_TMODEF_DFTWRITEMASK, + 0,0, // pbData, cbData + 0, // IStream* + 0)); // LPSecurityAttributes + + // Allocate an IStorage object to use. + IfNullGo(pStorage = new (nothrow) TiggerStorage); + + // Init the storage object on the i/o system. + OptionValue ov; + IfFailGo(m_MiniMd.GetOption(&ov)); + IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion)); + + // Save the data. + IfFailGo(SaveToStorage(pStorage)); + +ErrExit: + if (pStgIO != NULL) + pStgIO->Release(); + if (pStorage != NULL) + delete pStorage; + return hr; +} // CLiteWeightStgdbRW::Save + +//***************************************************************************** +// Pull the PEKind and Machine out of PE headers -- if we have PE headers. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetPEKind( // S_OK or error. + MAPPINGTYPE mtMapping, // The type of mapping the image has + DWORD *pdwPEKind, // [OUT] The kind of PE (0 - not a PE) + DWORD *pdwMachine) // [OUT] Machine as defined in NT header +{ + HRESULT hr = NOERROR; + DWORD dwPEKind=0; // Working copy of pe kind. + DWORD dwMachine=0; // Working copy of machine. + +#ifndef DACCESS_COMPILE + // Do we already have cached information? + if (m_dwPEKind != (DWORD)(-1)) + { + dwPEKind = m_dwPEKind; + dwMachine = m_dwMachine; + } + else if (m_pImage) + { + PEDecoder pe; + + // We need to use different PEDecoder initialization based on the type of data we give it. + // We use the one with a 'bool' as the second argument when dealing with a mapped file, + // and we use the one that takes a COUNT_T as the second argument when dealing with a + // flat file. + + if (mtMapping == MTYPE_IMAGE) + { + if (FAILED(pe.Init(m_pImage, false)) || + !pe.CheckNTHeaders()) + { + IfFailRet(COR_E_BADIMAGEFORMAT); + } + } + else + { + pe.Init(m_pImage, (COUNT_T)(m_dwImageSize)); + } + + if (pe.HasContents() && pe.HasNTHeaders()) + { + pe.GetPEKindAndMachine(&dwPEKind, &dwMachine); + + + // Cache entries. + m_dwPEKind = dwPEKind; + m_dwMachine = dwMachine; + } + else // if (pe.HasContents()... + { + hr = COR_E_BADIMAGEFORMAT; + } + } + else + { + hr = S_FALSE; + } +#endif + if (pdwPEKind) + *pdwPEKind = dwPEKind; + if (pdwMachine) + *pdwMachine = dwMachine; + + return hr; +} // CLiteWeightStgdbRW::GetPEKind + +//***************************************************************************** +// Low level access to the data. Intended for metainfo, and such. +//***************************************************************************** +__checkReturn +HRESULT CLiteWeightStgdbRW::GetRawData( + const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB'). + ULONG *pcbMd) // [OUT] put size of the stream here. +{ +#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE + if (m_pStgIO == NULL) + return COR_E_NOTSUPPORTED; +#endif + + *ppvMd = (const void*) m_pStgIO->m_pData; + *pcbMd = m_pStgIO->m_cbData; + return S_OK; +} // CLiteWeightStgdbRW::GetRawData + +//***************************************************************************** +// 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 + +//======================================================================================= +// +// Set file name of this database (makes copy of the file name). +// +// Return value: S_OK or E_OUTOFMEMORY +// +__checkReturn +HRESULT +CLiteWeightStgdbRW::SetFileName( + const WCHAR * wszFileName) +{ + HRESULT hr = S_OK; + + if (m_wszFileName != NULL) + { + delete [] m_wszFileName; + m_wszFileName = NULL; + } + + if ((wszFileName == NULL) || (*wszFileName == 0)) + { // The new file name is empty + _ASSERTE(m_wszFileName == NULL); + + // No need to allocate anything, NULL means empty name + hr = S_OK; + goto ErrExit; + } + + // Size of the file name incl. null terminator + size_t cchFileName; + cchFileName = wcslen(wszFileName) + 1; + + // Allocate and copy the file name + m_wszFileName = new (nothrow) WCHAR[cchFileName]; + IfNullGo(m_wszFileName); + wcscpy_s(m_wszFileName, cchFileName, wszFileName); + +ErrExit: + return hr; +} // CLiteWeightStgdbRW::SetFileName + +//======================================================================================= +// +// Returns TRUE if wszFileName has valid path length (MAX_PATH or 32767 if prefixed with \\?\). +// +//static +BOOL +CLiteWeightStgdbRW::IsValidFileNameLength( + const WCHAR * wszFileName) +{ +#ifdef FEATURE_CORECLR + return TRUE; +#else + static const WCHAR const_wszLongPathPrefix[] = W("\\\\?\\"); + + if (wszFileName == NULL) + { + return TRUE; + } + size_t cchFileName = wcslen(wszFileName); + if (cchFileName < _MAX_PATH) + { + return TRUE; + } + if (SString::_wcsnicmp(wszFileName, const_wszLongPathPrefix, _countof(const_wszLongPathPrefix) - 1) != 0) + { // Path does not have long path prefix \\?\ (as required by CreateFile API) + return FALSE; + } + if (cchFileName < 32767) + { // Limit for the long path length as defined in CreateFile API + return TRUE; + } + return FALSE; +#endif +} // CLiteWeightStgdbRW::IsValidFileNameLength |