diff options
Diffstat (limited to 'src/md/enc/stgtiggerstorage.cpp')
-rw-r--r-- | src/md/enc/stgtiggerstorage.cpp | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/src/md/enc/stgtiggerstorage.cpp b/src/md/enc/stgtiggerstorage.cpp new file mode 100644 index 0000000000..436b3d72e3 --- /dev/null +++ b/src/md/enc/stgtiggerstorage.cpp @@ -0,0 +1,1025 @@ +// 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. +//***************************************************************************** +// StgTiggerStorage.cpp +// + +// +// TiggerStorage is a stripped down version of compound doc files. Doc files +// have some very useful and complex features to them, unfortunately nothing +// comes for free. Given the incredibly tuned format of existing .tlb files, +// every single byte counts and 10% added by doc files is just too expensive. +// +//***************************************************************************** +#include "stdafx.h" // Standard header. +#include "stgio.h" // I/O subsystem. +#include "stgtiggerstorage.h" // Our interface. +#include "stgtiggerstream.h" // Stream interface. +#include "corerror.h" +#include "posterror.h" +#include "mdfileformat.h" +#include "sstring.h" + +//#CLRRuntimeHostInternal_GetImageVersionString +// External implementation of call to code:ICLRRuntimeHostInternal::GetImageVersionString. +// Implemented in clr.dll and mscordbi.dll. +HRESULT +CLRRuntimeHostInternal_GetImageVersionString( + __out_ecount(*pcchBuffer) + LPWSTR wszBuffer, + DWORD * pcchBuffer); + +TiggerStorage::TiggerStorage() : + m_pStgIO(0), + m_cRef(1), + m_pStreamList(0), + m_pbExtra(0) +{ + memset(&m_StgHdr, 0, sizeof(STORAGEHEADER)); +} + + +TiggerStorage::~TiggerStorage() +{ + if (m_pStgIO) + { + m_pStgIO->Release(); + m_pStgIO = 0; + } +} + + +//***************************************************************************** +// Init this storage object on top of the given storage unit. +//***************************************************************************** +HRESULT +TiggerStorage::Init( + StgIO *pStgIO, // The I/O subsystem. + __in __in_z LPSTR pVersion) // 'Compiled for' CLR version +{ + PSTORAGESIGNATURE pSig; // Signature data for file. + ULONG cbData; // Offset of header data. + void *ptr; // Signature. + HRESULT hr = S_OK; + + // Make sure we always start at the beginning. + // + pStgIO->Seek(0, FILE_BEGIN); + + // Save the storage unit. + m_pStgIO = pStgIO; + m_pStgIO->AddRef(); + + // For cases where the data already exists, verify the signature. + if ((pStgIO->GetFlags() & DBPROP_TMODEF_CREATE) == 0) + { + // Map the contents into memory for easy access. + IfFailGo(pStgIO->MapFileToMem(ptr, &cbData)); + + // Get a pointer to the signature of the file, which is the first part. + IfFailGo(pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr)); + + // Finally, we can check the signature. + pSig = (PSTORAGESIGNATURE)ptr; + IfFailGo(MDFormat::VerifySignature(pSig, cbData)); + + // Read and verify the header. + IfFailGo(ReadHeader()); + } + // For write case, dump the signature into the file up front. + else + { + IfFailGo(WriteSignature(pVersion)); + } + +ErrExit: + if (FAILED(hr) && (m_pStgIO != NULL)) + { + m_pStgIO->Release(); + m_pStgIO = NULL; + } + return hr; +} // TiggerStorage::Init + +//***************************************************************************** +// This function is a workaround to allow access to the "version requested" string. +//***************************************************************************** +HRESULT +TiggerStorage::GetHeaderPointer( + const void **ppv, // Put pointer to header here. + ULONG *pcb) // Put size of pointer here. +{ + void *ptr; // Working pointer. + HRESULT hr; + + // Read the signature + if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr))) + return hr; + + PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE) ptr; + // Header data starts after signature. + *pcb = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength(); + + *ppv = ptr; + + return S_OK; + +} // TiggerStorage::GetHeaderPointer + +//***************************************************************************** +// Get the default "Compiled for" version used to emit the meta-data +//***************************************************************************** +HRESULT +TiggerStorage::GetDefaultVersion( + LPCSTR *ppVersion) +{ + static LPSTR g_pDefaultVersion; + + if (g_pDefaultVersion == NULL) + { +#ifdef FEATURE_METADATA_STANDALONE_WINRT + g_pDefaultVersion = ""; +#else //!FEATURE_METADATA_STANDALONE_WINRT +#ifndef DACCESS_COMPILE + HRESULT hr; + + WCHAR wszVersion[_MAX_PATH]; + DWORD cchVersion = _MAX_PATH; + //#CallTo_CLRRuntimeHostInternal_GetImageVersionString + IfFailRet(CLRRuntimeHostInternal_GetImageVersionString(wszVersion, &cchVersion)); + + CHAR szVersion[_MAX_PATH]; + DWORD dwSize = WszWideCharToMultiByte(CP_UTF8, 0, wszVersion, -1, szVersion, _MAX_PATH, NULL, NULL); + if (dwSize == 0) + { + _ASSERTE_MSG(FALSE, "WideCharToMultiByte conversion failed"); + szVersion[0] = 0; + dwSize = 1; + } + + NewArrayHolder<CHAR> pVersion = new (nothrow) CHAR[dwSize]; + IfNullRet(pVersion); + + memcpy(pVersion, szVersion, dwSize); + + if (InterlockedCompareExchangeT<CHAR *>(&g_pDefaultVersion, pVersion, NULL) == NULL) + { // We won the initialization race + pVersion.SuppressRelease(); + } +#else + DacNotImpl(); +#endif //DACCESS_COMPILE +#endif //!FEATURE_METADATA_STANDALONE_WINRT + } + + *ppVersion = g_pDefaultVersion; + return S_OK; +} // TiggerStorage::GetDefaultVersion + +HRESULT +TiggerStorage::SizeOfStorageSignature(LPCSTR pVersion, ULONG *pcbSignatureSize) +{ + HRESULT hr; + + if (pVersion == NULL) + { + IfFailRet(GetDefaultVersion(&pVersion)); + } + _ASSERTE(pVersion != NULL); + + ULONG versionSize = (ULONG)strlen(pVersion)+1; + ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4); + + *pcbSignatureSize = sizeof(STORAGESIGNATURE) + alignedVersionSize; + return S_OK; +} + + +//***************************************************************************** +// Retrieves a the size and a pointer to the extra data that can optionally be +// written in the header of the storage system. This data is not required to +// be in the file, in which case *pcbExtra will come back as 0 and pbData will +// be set to NULL. You must have initialized the storage using Init() before +// calling this function. +// +// Return value: S_OK if found, S_FALSE, or error. +//***************************************************************************** +HRESULT +TiggerStorage::GetExtraData( + ULONG *pcbExtra, // Return size of extra data. + BYTE *&pbData) // Return a pointer to extra data. +{ + // Assuming there is extra data, then return the size and a pointer to it. + if (m_pbExtra != NULL) + { + if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) == 0) + { + Debug_ReportError("Inconsistent information about extra data in MetaData."); + return PostError(CLDB_E_FILE_CORRUPT); + } + *pcbExtra = *(ULONG *)m_pbExtra; + pbData = (BYTE *)((ULONG *) m_pbExtra + 1); + } + else + { + *pcbExtra = 0; + pbData = NULL; + return S_FALSE; + } + return S_OK; +} // TiggerStorage::GetExtraData + + +//***************************************************************************** +// Called when this stream is going away. +//***************************************************************************** +HRESULT +TiggerStorage::WriteHeader( + STORAGESTREAMLST *pList, // List of streams. + ULONG cbExtraData, // Size of extra data, may be 0. + BYTE *pbExtraData) // Pointer to extra data for header. +{ + ULONG iLen; // For variable sized data. + ULONG cbWritten; // Track write quantity. + HRESULT hr; + SAVETRACE(ULONG cbDebugSize); // Track debug size of header. + + SAVETRACE(DbgWriteEx(W("PSS: Header:\n"))); + + // Save the count and set flags. + m_StgHdr.SetiStreams(pList->Count()); + if (cbExtraData != 0) + m_StgHdr.AddFlags(STGHDR_EXTRADATA); + + // Write out the header of the file. + IfFailRet(m_pStgIO->Write(&m_StgHdr, sizeof(STORAGEHEADER), &cbWritten)); + + // Write out extra data if there is any. + if (cbExtraData != 0) + { + _ASSERTE(pbExtraData); + _ASSERTE((cbExtraData % 4) == 0); + + // First write the length value. + IfFailRet(m_pStgIO->Write(&cbExtraData, sizeof(ULONG), &cbWritten)); + + // And then the data. + IfFailRet(m_pStgIO->Write(pbExtraData, cbExtraData, &cbWritten)); + SAVETRACE(DbgWriteEx(W("PSS: extra data size %d\n"), m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset()); + } + + // Save off each data stream. + for (int i = 0; i < pList->Count(); i++) + { + PSTORAGESTREAM pStream = pList->Get(i); + + // How big is the structure (aligned) for this struct. + iLen = (ULONG)(sizeof(STORAGESTREAM) - MAXSTREAMNAME + strlen(pStream->GetName()) + 1); + + // Write the header including the name to disk. Does not include + // full name buffer in struct, just string and null terminator. + IfFailRet(m_pStgIO->Write(pStream, iLen, &cbWritten)); + + // Align the data out to 4 bytes. + if (iLen != ALIGN4BYTE(iLen)) + { + IfFailRet(m_pStgIO->Write(&hr, ALIGN4BYTE(iLen) - iLen, 0)); + } + SAVETRACE(DbgWriteEx(W("PSS: Table %hs header size %d\n"), pStream->rcName, m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset()); + } + SAVETRACE(DbgWriteEx(W("PSS: Total size of header data %d\n"), m_pStgIO->GetCurrentOffset())); + // Make sure the whole thing is 4 byte aligned. + _ASSERTE((m_pStgIO->GetCurrentOffset() % 4) == 0); + return S_OK; +} // TiggerStorage::WriteHeader + + +//***************************************************************************** +// Called when all data has been written. Forces cached data to be flushed +// and stream lists to be validated. +//***************************************************************************** +HRESULT +TiggerStorage::WriteFinished( + STORAGESTREAMLST *pList, // List of streams. + ULONG *pcbSaveSize, // Return size of total data. + BOOL fDeltaSave) // Was this a delta +{ + PSTORAGESTREAM pEntry; // Loop control. + HRESULT hr; + + // If caller wants the total size of the file, we are there right now. + if (pcbSaveSize != NULL) + *pcbSaveSize = m_pStgIO->GetCurrentOffset(); + + // Flush our internal write cache to disk. + IfFailRet(m_pStgIO->FlushCache()); + + // Force user's data onto disk right now so that Commit() can be + // more accurate (although not totally up to the D in ACID). + hr = m_pStgIO->FlushFileBuffers(); + _ASSERTE(SUCCEEDED(hr)); + + // Run through all of the streams and validate them against the expected + // list we wrote out originally. + + // Robustness check: stream counts must match what was written. + _ASSERTE(pList->Count() == m_Streams.Count()); + if (pList->Count() != m_Streams.Count()) + { + _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // If we're saving a true delta, then this sanity check won't help. + // @TODO - Implement a sanity check for the deltas + if (!fDeltaSave) + { + // Sanity check each saved stream data size and offset. + for (int i = 0; i < pList->Count(); i++) + { + pEntry = pList->Get(i); + + _ASSERTE(pEntry->GetOffset() == m_Streams[i].GetOffset()); + _ASSERTE(pEntry->GetSize() == m_Streams[i].GetSize()); + _ASSERTE(strcmp(pEntry->GetName(), m_Streams[i].GetName()) == 0); + + // For robustness, check that everything matches expected value, + // and if it does not, refuse to save the data and force a rollback. + // The alternative is corruption of the data file. + if ((pEntry->GetOffset() != m_Streams[i].GetOffset()) || + (pEntry->GetSize() != m_Streams[i].GetSize()) || + (strcmp(pEntry->GetName(), m_Streams[i].GetName()) != 0)) + { + _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption."); + hr = PostError(CLDB_E_FILE_CORRUPT); + break; + } + + //<REVISIT_TODO>@future: + // if iOffset or iSize mismatches, it means a bug in GetSaveSize + // which we can successfully detect right here. In that case, we + // could use the pStgIO and seek back to the header and correct the + // mistmake. This will break any client who lives on the GetSaveSize + // value which came back originally, but would be more robust than + // simply throwing back an error which will corrupt the file.</REVISIT_TODO> + } + } + return hr; +} // TiggerStorage::WriteFinished + + +//***************************************************************************** +// Called after a successful rewrite of an existing file. The in memory +// backing store is no longer valid because all new data is in memory and +// on disk. This is essentially the same state as created, so free up some +// working set and remember this state. +//***************************************************************************** +HRESULT TiggerStorage::ResetBackingStore() // Return code. +{ + return (m_pStgIO->ResetBackingStore()); +} + + +//***************************************************************************** +// Given the name of a stream that will be persisted into a stream in this +// storage type, figure out how big that stream would be including the user's +// stream data and the header overhead the file format incurs. The name is +// stored in ANSI and the header struct is aligned to 4 bytes. +//***************************************************************************** +HRESULT +TiggerStorage::GetStreamSaveSize( + LPCWSTR szStreamName, // Name of stream. + UINT32 cbDataSize, // Size of data to go into stream. + UINT32 *pcbSaveSize) // Return data size plus stream overhead. +{ + UINT32 cbTotalSize; // Add up each element. + + // Find out how large the name will be. + cbTotalSize = ::WszWideCharToMultiByte(CP_ACP, 0, szStreamName, -1, 0, 0, 0, 0); + _ASSERTE(cbTotalSize != 0); + + // Add the size of the stream header minus the static name array. + cbTotalSize += sizeof(STORAGESTREAM) - MAXSTREAMNAME; + + // Finally align the header value. + cbTotalSize = ALIGN4BYTE(cbTotalSize); + + // Return the size of the user data and the header data. + *pcbSaveSize = cbTotalSize + cbDataSize; + return S_OK; +} // TiggerStorage::GetStreamSaveSize + + +//***************************************************************************** +// Return the fixed size overhead for the storage implementation. This includes +// the signature and fixed header overhead. The overhead in the header for each +// stream is calculated as part of GetStreamSaveSize because these structs are +// variable sized on the name. +//***************************************************************************** +HRESULT TiggerStorage::GetStorageSaveSize( // Return code. + ULONG *pcbSaveSize, // [in] current size, [out] plus overhead. + ULONG cbExtra, // How much extra data to store in header. + LPCSTR pRuntimeVersion) +{ + HRESULT hr; + + ULONG cbSignatureSize; + IfFailRet(SizeOfStorageSignature(pRuntimeVersion, &cbSignatureSize)); + + *pcbSaveSize += cbSignatureSize + sizeof(STORAGEHEADER); + if (cbExtra) + *pcbSaveSize += sizeof(ULONG) + cbExtra; + return (S_OK); +} + + +//***************************************************************************** +// Adjust the offset in each known stream to match where it will wind up after +// a save operation. +//***************************************************************************** +HRESULT TiggerStorage::CalcOffsets( // Return code. + STORAGESTREAMLST *pStreamList, // List of streams for header. + ULONG cbExtra, // Size of variable extra data in header. + LPCSTR pRuntimeVersion) // The version string as it's length is part of the total size. +{ + PSTORAGESTREAM pEntry; // Each entry in the list. + ULONG cbOffset=0; // Running offset for streams. + int i; // Loop control. + + // Prime offset up front. + GetStorageSaveSize(&cbOffset, cbExtra, pRuntimeVersion); + + // Add on the size of each header entry. + for (i=0; i<pStreamList->Count(); i++) + { + VERIFY(pEntry = pStreamList->Get(i)); + cbOffset += sizeof(STORAGESTREAM) - MAXSTREAMNAME; + cbOffset += (ULONG)(strlen(pEntry->GetName()) + 1); + cbOffset = ALIGN4BYTE(cbOffset); + } + + // Go through each stream and reset its expected offset. + for (i=0; i<pStreamList->Count(); i++) + { + VERIFY(pEntry = pStreamList->Get(i)); + pEntry->SetOffset(cbOffset); + cbOffset += pEntry->GetSize(); + } + return (S_OK); +} + + + +HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream( + const OLECHAR *pwcsName, + DWORD grfMode, + DWORD reserved1, + DWORD reserved2, + IStream **ppstm) +{ + char rcStream[MAXSTREAMNAME];// For converted name. + VERIFY(Wsz_wcstombs(rcStream, pwcsName, sizeof(rcStream))); + return (CreateStream(rcStream, grfMode, reserved1, reserved2, ppstm)); +} + + +#ifndef DACCESS_COMPILE +HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream( + LPCSTR szName, + DWORD grfMode, + DWORD reserved1, + DWORD reserved2, + IStream **ppstm) +{ + PSTORAGESTREAM pStream; // For lookup. + HRESULT hr; + + _ASSERTE(szName && *szName); + + // Check for existing stream, which might be an error or more likely + // a rewrite of a file. + if (SUCCEEDED(FindStream(szName, &pStream))) + { + // <REVISIT_TODO>REVIEW: STGM_FAILIFTHERE is 0, the following condition will be always false</REVISIT_TODO> + if (pStream->GetOffset() != 0xffffffff && ((grfMode & STGM_CREATE) == STGM_FAILIFTHERE)) + return (PostError(STG_E_FILEALREADYEXISTS)); + } + // Add a control to track this stream. + else if (!pStream && (pStream = m_Streams.Append()) == 0) + return (PostError(OutOfMemory())); + pStream->SetOffset(0xffffffff); + pStream->SetSize(0); + strcpy_s(pStream->GetName(), 32, szName); + + // Now create a stream object to allow reading and writing. + TiggerStream *pNew = new (nothrow) TiggerStream; + if (!pNew) + return (PostError(OutOfMemory())); + *ppstm = (IStream *) pNew; + + // Init the new object. + if (FAILED(hr = pNew->Init(this, pStream->GetName()))) + { + delete pNew; + return (hr); + } + return (S_OK); +} +#endif //!DACCESS_COMPILE + + +HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream( + const OLECHAR *pwcsName, + void *reserved1, + DWORD grfMode, + DWORD reserved2, + IStream **ppstm) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStorage( + const OLECHAR *pwcsName, + DWORD grfMode, + DWORD dwStgFmt, + DWORD reserved2, + IStorage **ppstg) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE +TiggerStorage::OpenStorage( + const OLECHAR * wcsName, + IStorage * pStgPriority, + DWORD dwMode, + __in + SNB snbExclude, + DWORD reserved, + IStorage ** ppStg) +{ + return E_NOTIMPL; +} + +HRESULT STDMETHODCALLTYPE +TiggerStorage::CopyTo( + DWORD cIidExclude, + const IID * rgIidExclude, + __in + SNB snbExclude, + IStorage * pStgDest) +{ + return E_NOTIMPL; +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::MoveElementTo( + const OLECHAR *pwcsName, + IStorage *pstgDest, + const OLECHAR *pwcsNewName, + DWORD grfFlags) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::Commit( + DWORD grfCommitFlags) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::Revert() +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::EnumElements( + DWORD reserved1, + void *reserved2, + DWORD reserved3, + IEnumSTATSTG **ppenum) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::DestroyElement( + const OLECHAR *pwcsName) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::RenameElement( + const OLECHAR *pwcsOldName, + const OLECHAR *pwcsNewName) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::SetElementTimes( + const OLECHAR *pwcsName, + const FILETIME *pctime, + const FILETIME *patime, + const FILETIME *pmtime) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::SetClass( + REFCLSID clsid) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::SetStateBits( + DWORD grfStateBits, + DWORD grfMask) +{ + return (E_NOTIMPL); +} + + +HRESULT STDMETHODCALLTYPE TiggerStorage::Stat( + STATSTG *pstatstg, + DWORD grfStatFlag) +{ + return (E_NOTIMPL); +} + + + +HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream( + LPCWSTR szStream, + ULONG *pcbData, + void **ppAddress) +{ + PSTORAGESTREAM pStream; // For lookup. + char rcName[MAXSTREAMNAME]; // For conversion. + HRESULT hr; + + // Convert the name for internal use. + VERIFY(::WszWideCharToMultiByte(CP_ACP, 0, szStream, -1, rcName, sizeof(rcName), 0, 0)); + + // Look for the stream which must be found for this to work. Note that + // this error is explicitly not posted as an error object since unfound streams + // are a common occurence and do not warrant a resource file load. + IfFailRet(FindStream(rcName, &pStream)); + + // Get the memory for the stream. + IfFailRet( m_pStgIO->GetPtrForMem(pStream->GetOffset(), pStream->GetSize(), *ppAddress) ); + *pcbData = pStream->GetSize(); + return (S_OK); +} + + + +// +// Protected. +// + + +//***************************************************************************** +// Called by the stream implementation to write data out to disk. +//***************************************************************************** +HRESULT +TiggerStorage::Write( + LPCSTR szName, // Name of stream we're writing. + const void *pData, // Data to write. + ULONG cbData, // Size of data. + ULONG *pcbWritten) // How much did we write. +{ + PSTORAGESTREAM pStream; // Update size data. + ULONG iOffset = 0; // Offset for write. + ULONG cbWritten; // Handle null case. + HRESULT hr; + + // Get the stream descriptor. + if (FAILED(FindStream(szName, &pStream))) + return CLDB_E_FILE_BADWRITE; + + // If we need to know the offset, keep it now. + if (pStream->GetOffset() == 0xffffffff) + { + iOffset = m_pStgIO->GetCurrentOffset(); + + // Align the storage on a 4 byte boundary. + if ((iOffset % 4) != 0) + { + ULONG cb; + ULONG pad = 0; + + if (FAILED(hr = m_pStgIO->Write(&pad, ALIGN4BYTE(iOffset) - iOffset, &cb))) + return hr; + iOffset = m_pStgIO->GetCurrentOffset(); + + _ASSERTE((iOffset % 4) == 0); + } + } + + // Avoid confusion. + if (pcbWritten == NULL) + pcbWritten = &cbWritten; + *pcbWritten = 0; + + // Let OS do the write. + if (SUCCEEDED(hr = m_pStgIO->Write(pData, cbData, pcbWritten))) + { + // On success, record the new data. + if (pStream->GetOffset() == 0xffffffff) + pStream->SetOffset(iOffset); + pStream->SetSize(pStream->GetSize() + *pcbWritten); + return S_OK; + } + else + { + return hr; + } +} // TiggerStorage::Write + + +// +// Private +// + +HRESULT +TiggerStorage::FindStream( + LPCSTR szName, + __out PSTORAGESTREAM *stream) +{ + *stream = NULL; + // In read mode, just walk the list and return one. + if (m_pStreamList != NULL) + { + PSTORAGESTREAM p = m_pStreamList; + + SIZE_T pStartMD = (SIZE_T)(m_pStgIO->m_pData); + SIZE_T pEndMD = NULL; + + if (!ClrSafeInt<SIZE_T>::addition(pStartMD, m_pStgIO->m_cbData, pEndMD)) + { + Debug_ReportError("Invalid MetaData storage headers - size overflow."); + return CLDB_E_FILE_CORRUPT; + } + + for (int i = 0; i < m_StgHdr.GetiStreams(); i++) + { + // Make sure this stream pointer is still inside the metadata + if (((SIZE_T)p < pStartMD) || ((SIZE_T)p > pEndMD)) + { + Debug_ReportError("Invalid MetaData storage header - reached outside headers block."); + return CLDB_E_FILE_CORRUPT; + } + + if (SString::_stricmp(p->GetName(), szName) == 0) + { + *stream = p; + return S_OK; + } + p = p->NextStream(); + } + } + // In write mode, walk the array which is not on disk yet. + else + { + for (int j = 0; j < m_Streams.Count(); j++) + { + if (SString::_stricmp(m_Streams[j].GetName(), szName) == 0) + { + *stream = &m_Streams[j]; + return S_OK; + } + } + } + return STG_E_FILENOTFOUND; +} // TiggerStorage::FindStream + + +//***************************************************************************** +// Write the signature area of the file format to disk. This includes the +// "magic" identifier and the version information. +//***************************************************************************** +HRESULT +TiggerStorage::WriteSignature( + LPCSTR pVersion) +{ + STORAGESIGNATURE sSig; + ULONG cbWritten; + HRESULT hr = S_OK; + + if (pVersion == NULL) + { + IfFailRet(GetDefaultVersion(&pVersion)); + } + _ASSERTE(pVersion != NULL); + + ULONG versionSize = (ULONG)strlen(pVersion) + 1; + ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4); + + // Signature belongs at the start of the file. + _ASSERTE(m_pStgIO->GetCurrentOffset() == 0); + + sSig.SetSignature(STORAGE_MAGIC_SIG); + sSig.SetMajorVer(FILE_VER_MAJOR); + sSig.SetMinorVer(FILE_VER_MINOR); + sSig.SetExtraDataOffset(0); // We have no extra inforation + sSig.SetVersionStringLength(alignedVersionSize); + IfFailRet(m_pStgIO->Write(&sSig, sizeof(STORAGESIGNATURE), &cbWritten)); + IfFailRet(m_pStgIO->Write(pVersion, versionSize, &cbWritten)); + + // Write padding + if (alignedVersionSize - versionSize != 0) + { + BYTE padding[4]; + ZeroMemory(padding, sizeof(padding)); + IfFailRet(m_pStgIO->Write(padding, alignedVersionSize - versionSize, &cbWritten)); + } + + return hr; +} // TiggerStorage::WriteSignature + + +//***************************************************************************** +// Read the header from disk. This reads the header for the most recent version +// of the file format which has the header at the front of the data file. +//***************************************************************************** +HRESULT +TiggerStorage::ReadHeader() +{ + PSTORAGESTREAM pAppend, pStream; // For copy of array. + void *ptr; // Working pointer. + ULONG iOffset; // Offset of header data. + ULONG cbExtra; // Size of extra data. + ULONG cbRead; // For calc of read sizes. + HRESULT hr; + + // Read the signature + if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr))) + { + Debug_ReportError("Cannot read MetaData storage signature header."); + return hr; + } + + PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE)ptr; + + // Header data starts after signature. + iOffset = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength(); + + // Read the storage header which has the stream counts. Throw in the extra + // count which might not exist, but saves us down stream. + if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr))) + { + Debug_ReportError("Cannot read first MetaData storage header."); + return hr; + } + _ASSERTE(m_pStgIO->IsAlignedPtr((ULONG_PTR) ptr, 4)); + + // Read the storage header which has the stream counts. Throw in the extra + // count which might not exist, but saves us down stream. + if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr))) + { + Debug_ReportError("Cannot read second MetaData storage header."); + return hr; + } + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4)) + { + Debug_ReportError("Invalid MetaData storage headers - unaligned size."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // Copy the header into memory and check it. + memcpy(&m_StgHdr, ptr, sizeof(STORAGEHEADER)); + IfFailRet( VerifyHeader() ); + ptr = (void *)((PSTORAGEHEADER)ptr + 1); + iOffset += sizeof(STORAGEHEADER); + + // Save off a pointer to the extra data. + if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) != 0) + { + m_pbExtra = ptr; + cbExtra = sizeof(ULONG) + *(ULONG *)ptr; + + // Force the extra data to get faulted in. + IfFailRet(m_pStgIO->GetPtrForMem(iOffset, cbExtra, ptr)); + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4)) + { + Debug_ReportError("Invalid MetaData storage signature - unaligned extra data."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + else + { + m_pbExtra = 0; + cbExtra = 0; + } + iOffset += cbExtra; + + // Force the worst case scenario of bytes to get faulted in for the + // streams. This makes the rest of this code very simple. + cbRead = sizeof(STORAGESTREAM) * m_StgHdr.GetiStreams(); + if (cbRead != 0) + { + cbRead = min(cbRead, m_pStgIO->GetDataSize() - iOffset); + if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, cbRead, ptr))) + { + Debug_ReportError("Invalid MetaData stogare headers."); + return hr; + } + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4)) + { + Debug_ReportError("Invalid MetaData stogare headers - unaligned start."); + return PostError(CLDB_E_FILE_CORRUPT); + } + + // For read only, just access the header data. + if (m_pStgIO->IsReadOnly()) + { + // Save a pointer to the current list of streams. + m_pStreamList = (PSTORAGESTREAM)ptr; + } + // For writeable, need a copy we can modify. + else + { + pStream = (PSTORAGESTREAM)ptr; + + // Copy each of the stream headers. + for (int i = 0; i < m_StgHdr.GetiStreams(); i++) + { + if ((pAppend = m_Streams.Append()) == NULL) + return PostError(OutOfMemory()); + // Validate that the stream header is not too big. + ULONG sz = pStream->GetStreamSize(); + if (sz > sizeof(STORAGESTREAM)) + { + Debug_ReportError("Invalid MetaData storage stream - data too big."); + return PostError(CLDB_E_FILE_CORRUPT); + } + memcpy (pAppend, pStream, sz); + pStream = pStream->NextStream(); + if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)pStream, 4)) + { + Debug_ReportError("Invalid MetaData storage stream - unaligned data."); + return PostError(CLDB_E_FILE_CORRUPT); + } + } + + // All must be loaded and accounted for. + _ASSERTE(m_StgHdr.GetiStreams() == m_Streams.Count()); + } + } + return S_OK; +} // TiggerStorage::ReadHeader + + +//***************************************************************************** +// Verify the header is something this version of the code can support. +//***************************************************************************** +HRESULT TiggerStorage::VerifyHeader() +{ + //<REVISIT_TODO>@FUTURE: add version check for format.</REVISIT_TODO> + return S_OK; +} + +//***************************************************************************** +// Print the sizes of the various streams. +//***************************************************************************** +#if defined(_DEBUG) +ULONG TiggerStorage::PrintSizeInfo(bool verbose) +{ + ULONG total = 0; + + printf("Storage Header: %d\n", sizeof(STORAGEHEADER)); + if (m_pStreamList != NULL) + { + PSTORAGESTREAM storStream = m_pStreamList; + PSTORAGESTREAM pNext; + for (int i = 0; i < m_StgHdr.GetiStreams(); i++) + { + pNext = storStream->NextStream(); + printf("Stream #%d (%s) Header: %d, Data: %d\n",i,storStream->GetName(), (BYTE*)pNext - (BYTE*)storStream, storStream->GetSize()); + total += storStream->GetSize(); + storStream = pNext; + } + } + else + { + //<REVISIT_TODO>todo: Add support for the case where m_Streams exists and m_pStreamList does not</REVISIT_TODO> + } + + if (m_pbExtra != NULL) + { + printf("Extra bytes: %d\n",*(ULONG*)m_pbExtra); + total += *(ULONG*)m_pbExtra; + } + return total; +} +#endif // _DEBUG |