1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// StgTiggerStorage.cpp
6//
7
8//
9// TiggerStorage is a stripped down version of compound doc files. Doc files
10// have some very useful and complex features to them, unfortunately nothing
11// comes for free. Given the incredibly tuned format of existing .tlb files,
12// every single byte counts and 10% added by doc files is just too expensive.
13//
14//*****************************************************************************
15#include "stdafx.h" // Standard header.
16#include "stgio.h" // I/O subsystem.
17#include "stgtiggerstorage.h" // Our interface.
18#include "stgtiggerstream.h" // Stream interface.
19#include "corerror.h"
20#include "posterror.h"
21#include "mdfileformat.h"
22#include "sstring.h"
23
24//#CLRRuntimeHostInternal_GetImageVersionString
25// External implementation of call to code:ICLRRuntimeHostInternal::GetImageVersionString.
26// Implemented in clr.dll and mscordbi.dll.
27HRESULT
28CLRRuntimeHostInternal_GetImageVersionString(
29 __out_ecount(*pcchBuffer)
30 LPWSTR wszBuffer,
31 DWORD * pcchBuffer);
32
33TiggerStorage::TiggerStorage() :
34 m_pStgIO(0),
35 m_cRef(1),
36 m_pStreamList(0),
37 m_pbExtra(0)
38{
39 memset(&m_StgHdr, 0, sizeof(STORAGEHEADER));
40}
41
42
43TiggerStorage::~TiggerStorage()
44{
45 if (m_pStgIO)
46 {
47 m_pStgIO->Release();
48 m_pStgIO = 0;
49 }
50}
51
52
53//*****************************************************************************
54// Init this storage object on top of the given storage unit.
55//*****************************************************************************
56HRESULT
57TiggerStorage::Init(
58 StgIO *pStgIO, // The I/O subsystem.
59 __in __in_z LPSTR pVersion) // 'Compiled for' CLR version
60{
61 PSTORAGESIGNATURE pSig; // Signature data for file.
62 ULONG cbData; // Offset of header data.
63 void *ptr; // Signature.
64 HRESULT hr = S_OK;
65
66 // Make sure we always start at the beginning.
67 //
68 pStgIO->Seek(0, FILE_BEGIN);
69
70 // Save the storage unit.
71 m_pStgIO = pStgIO;
72 m_pStgIO->AddRef();
73
74 // For cases where the data already exists, verify the signature.
75 if ((pStgIO->GetFlags() & DBPROP_TMODEF_CREATE) == 0)
76 {
77 // Map the contents into memory for easy access.
78 IfFailGo(pStgIO->MapFileToMem(ptr, &cbData));
79
80 // Get a pointer to the signature of the file, which is the first part.
81 IfFailGo(pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr));
82
83 // Finally, we can check the signature.
84 pSig = (PSTORAGESIGNATURE)ptr;
85 IfFailGo(MDFormat::VerifySignature(pSig, cbData));
86
87 // Read and verify the header.
88 IfFailGo(ReadHeader());
89 }
90 // For write case, dump the signature into the file up front.
91 else
92 {
93 IfFailGo(WriteSignature(pVersion));
94 }
95
96ErrExit:
97 if (FAILED(hr) && (m_pStgIO != NULL))
98 {
99 m_pStgIO->Release();
100 m_pStgIO = NULL;
101 }
102 return hr;
103} // TiggerStorage::Init
104
105//*****************************************************************************
106// This function is a workaround to allow access to the "version requested" string.
107//*****************************************************************************
108HRESULT
109TiggerStorage::GetHeaderPointer(
110 const void **ppv, // Put pointer to header here.
111 ULONG *pcb) // Put size of pointer here.
112{
113 void *ptr; // Working pointer.
114 HRESULT hr;
115
116 // Read the signature
117 if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr)))
118 return hr;
119
120 PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE) ptr;
121 // Header data starts after signature.
122 *pcb = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength();
123
124 *ppv = ptr;
125
126 return S_OK;
127
128} // TiggerStorage::GetHeaderPointer
129
130//*****************************************************************************
131// Get the default "Compiled for" version used to emit the meta-data
132//*****************************************************************************
133HRESULT
134TiggerStorage::GetDefaultVersion(
135 LPCSTR *ppVersion)
136{
137 static LPSTR g_pDefaultVersion;
138
139 if (g_pDefaultVersion == NULL)
140 {
141#ifndef DACCESS_COMPILE
142 HRESULT hr;
143
144 WCHAR wszVersion[_MAX_PATH];
145 DWORD cchVersion = _MAX_PATH;
146 //#CallTo_CLRRuntimeHostInternal_GetImageVersionString
147 IfFailRet(CLRRuntimeHostInternal_GetImageVersionString(wszVersion, &cchVersion));
148
149 CHAR szVersion[_MAX_PATH];
150 DWORD dwSize = WszWideCharToMultiByte(CP_UTF8, 0, wszVersion, -1, szVersion, _MAX_PATH, NULL, NULL);
151 if (dwSize == 0)
152 {
153 _ASSERTE_MSG(FALSE, "WideCharToMultiByte conversion failed");
154 szVersion[0] = 0;
155 dwSize = 1;
156 }
157
158 NewArrayHolder<CHAR> pVersion = new (nothrow) CHAR[dwSize];
159 IfNullRet(pVersion);
160
161 memcpy(pVersion, szVersion, dwSize);
162
163 if (InterlockedCompareExchangeT<CHAR *>(&g_pDefaultVersion, pVersion, NULL) == NULL)
164 { // We won the initialization race
165 pVersion.SuppressRelease();
166 }
167#else
168 DacNotImpl();
169#endif //DACCESS_COMPILE
170 }
171
172 *ppVersion = g_pDefaultVersion;
173 return S_OK;
174} // TiggerStorage::GetDefaultVersion
175
176HRESULT
177TiggerStorage::SizeOfStorageSignature(LPCSTR pVersion, ULONG *pcbSignatureSize)
178{
179 HRESULT hr;
180
181 if (pVersion == NULL)
182 {
183 IfFailRet(GetDefaultVersion(&pVersion));
184 }
185 _ASSERTE(pVersion != NULL);
186
187 ULONG versionSize = (ULONG)strlen(pVersion)+1;
188 ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4);
189
190 *pcbSignatureSize = sizeof(STORAGESIGNATURE) + alignedVersionSize;
191 return S_OK;
192}
193
194
195//*****************************************************************************
196// Retrieves a the size and a pointer to the extra data that can optionally be
197// written in the header of the storage system. This data is not required to
198// be in the file, in which case *pcbExtra will come back as 0 and pbData will
199// be set to NULL. You must have initialized the storage using Init() before
200// calling this function.
201//
202// Return value: S_OK if found, S_FALSE, or error.
203//*****************************************************************************
204HRESULT
205TiggerStorage::GetExtraData(
206 ULONG *pcbExtra, // Return size of extra data.
207 BYTE *&pbData) // Return a pointer to extra data.
208{
209 // Assuming there is extra data, then return the size and a pointer to it.
210 if (m_pbExtra != NULL)
211 {
212 if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) == 0)
213 {
214 Debug_ReportError("Inconsistent information about extra data in MetaData.");
215 return PostError(CLDB_E_FILE_CORRUPT);
216 }
217 *pcbExtra = *(ULONG *)m_pbExtra;
218 pbData = (BYTE *)((ULONG *) m_pbExtra + 1);
219 }
220 else
221 {
222 *pcbExtra = 0;
223 pbData = NULL;
224 return S_FALSE;
225 }
226 return S_OK;
227} // TiggerStorage::GetExtraData
228
229
230//*****************************************************************************
231// Called when this stream is going away.
232//*****************************************************************************
233HRESULT
234TiggerStorage::WriteHeader(
235 STORAGESTREAMLST *pList, // List of streams.
236 ULONG cbExtraData, // Size of extra data, may be 0.
237 BYTE *pbExtraData) // Pointer to extra data for header.
238{
239 ULONG iLen; // For variable sized data.
240 ULONG cbWritten; // Track write quantity.
241 HRESULT hr;
242 SAVETRACE(ULONG cbDebugSize); // Track debug size of header.
243
244 SAVETRACE(DbgWriteEx(W("PSS: Header:\n")));
245
246 // Save the count and set flags.
247 m_StgHdr.SetiStreams(pList->Count());
248 if (cbExtraData != 0)
249 m_StgHdr.AddFlags(STGHDR_EXTRADATA);
250
251 // Write out the header of the file.
252 IfFailRet(m_pStgIO->Write(&m_StgHdr, sizeof(STORAGEHEADER), &cbWritten));
253
254 // Write out extra data if there is any.
255 if (cbExtraData != 0)
256 {
257 _ASSERTE(pbExtraData);
258 _ASSERTE((cbExtraData % 4) == 0);
259
260 // First write the length value.
261 IfFailRet(m_pStgIO->Write(&cbExtraData, sizeof(ULONG), &cbWritten));
262
263 // And then the data.
264 IfFailRet(m_pStgIO->Write(pbExtraData, cbExtraData, &cbWritten));
265 SAVETRACE(DbgWriteEx(W("PSS: extra data size %d\n"), m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset());
266 }
267
268 // Save off each data stream.
269 for (int i = 0; i < pList->Count(); i++)
270 {
271 PSTORAGESTREAM pStream = pList->Get(i);
272
273 // How big is the structure (aligned) for this struct.
274 iLen = (ULONG)(sizeof(STORAGESTREAM) - MAXSTREAMNAME + strlen(pStream->GetName()) + 1);
275
276 // Write the header including the name to disk. Does not include
277 // full name buffer in struct, just string and null terminator.
278 IfFailRet(m_pStgIO->Write(pStream, iLen, &cbWritten));
279
280 // Align the data out to 4 bytes.
281 if (iLen != ALIGN4BYTE(iLen))
282 {
283 IfFailRet(m_pStgIO->Write(&hr, ALIGN4BYTE(iLen) - iLen, 0));
284 }
285 SAVETRACE(DbgWriteEx(W("PSS: Table %hs header size %d\n"), pStream->rcName, m_pStgIO->GetCurrentOffset() - cbDebugSize);cbDebugSize=m_pStgIO->GetCurrentOffset());
286 }
287 SAVETRACE(DbgWriteEx(W("PSS: Total size of header data %d\n"), m_pStgIO->GetCurrentOffset()));
288 // Make sure the whole thing is 4 byte aligned.
289 _ASSERTE((m_pStgIO->GetCurrentOffset() % 4) == 0);
290 return S_OK;
291} // TiggerStorage::WriteHeader
292
293
294//*****************************************************************************
295// Called when all data has been written. Forces cached data to be flushed
296// and stream lists to be validated.
297//*****************************************************************************
298HRESULT
299TiggerStorage::WriteFinished(
300 STORAGESTREAMLST *pList, // List of streams.
301 ULONG *pcbSaveSize, // Return size of total data.
302 BOOL fDeltaSave) // Was this a delta
303{
304 PSTORAGESTREAM pEntry; // Loop control.
305 HRESULT hr;
306
307 // If caller wants the total size of the file, we are there right now.
308 if (pcbSaveSize != NULL)
309 *pcbSaveSize = m_pStgIO->GetCurrentOffset();
310
311 // Flush our internal write cache to disk.
312 IfFailRet(m_pStgIO->FlushCache());
313
314 // Force user's data onto disk right now so that Commit() can be
315 // more accurate (although not totally up to the D in ACID).
316 hr = m_pStgIO->FlushFileBuffers();
317 _ASSERTE(SUCCEEDED(hr));
318
319 // Run through all of the streams and validate them against the expected
320 // list we wrote out originally.
321
322 // Robustness check: stream counts must match what was written.
323 _ASSERTE(pList->Count() == m_Streams.Count());
324 if (pList->Count() != m_Streams.Count())
325 {
326 _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption.");
327 return PostError(CLDB_E_FILE_CORRUPT);
328 }
329
330 // If we're saving a true delta, then this sanity check won't help.
331 // @TODO - Implement a sanity check for the deltas
332 if (!fDeltaSave)
333 {
334 // Sanity check each saved stream data size and offset.
335 for (int i = 0; i < pList->Count(); i++)
336 {
337 pEntry = pList->Get(i);
338
339 _ASSERTE(pEntry->GetOffset() == m_Streams[i].GetOffset());
340 _ASSERTE(pEntry->GetSize() == m_Streams[i].GetSize());
341 _ASSERTE(strcmp(pEntry->GetName(), m_Streams[i].GetName()) == 0);
342
343 // For robustness, check that everything matches expected value,
344 // and if it does not, refuse to save the data and force a rollback.
345 // The alternative is corruption of the data file.
346 if ((pEntry->GetOffset() != m_Streams[i].GetOffset()) ||
347 (pEntry->GetSize() != m_Streams[i].GetSize()) ||
348 (strcmp(pEntry->GetName(), m_Streams[i].GetName()) != 0))
349 {
350 _ASSERTE_MSG(FALSE, "Mismatch in streams, save would cause corruption.");
351 hr = PostError(CLDB_E_FILE_CORRUPT);
352 break;
353 }
354
355 //<REVISIT_TODO>@future:
356 // if iOffset or iSize mismatches, it means a bug in GetSaveSize
357 // which we can successfully detect right here. In that case, we
358 // could use the pStgIO and seek back to the header and correct the
359 // mistmake. This will break any client who lives on the GetSaveSize
360 // value which came back originally, but would be more robust than
361 // simply throwing back an error which will corrupt the file.</REVISIT_TODO>
362 }
363 }
364 return hr;
365} // TiggerStorage::WriteFinished
366
367
368//*****************************************************************************
369// Called after a successful rewrite of an existing file. The in memory
370// backing store is no longer valid because all new data is in memory and
371// on disk. This is essentially the same state as created, so free up some
372// working set and remember this state.
373//*****************************************************************************
374HRESULT TiggerStorage::ResetBackingStore() // Return code.
375{
376 return (m_pStgIO->ResetBackingStore());
377}
378
379
380//*****************************************************************************
381// Given the name of a stream that will be persisted into a stream in this
382// storage type, figure out how big that stream would be including the user's
383// stream data and the header overhead the file format incurs. The name is
384// stored in ANSI and the header struct is aligned to 4 bytes.
385//*****************************************************************************
386HRESULT
387TiggerStorage::GetStreamSaveSize(
388 LPCWSTR szStreamName, // Name of stream.
389 UINT32 cbDataSize, // Size of data to go into stream.
390 UINT32 *pcbSaveSize) // Return data size plus stream overhead.
391{
392 UINT32 cbTotalSize; // Add up each element.
393
394 // Find out how large the name will be.
395 cbTotalSize = ::WszWideCharToMultiByte(CP_ACP, 0, szStreamName, -1, 0, 0, 0, 0);
396 _ASSERTE(cbTotalSize != 0);
397
398 // Add the size of the stream header minus the static name array.
399 cbTotalSize += sizeof(STORAGESTREAM) - MAXSTREAMNAME;
400
401 // Finally align the header value.
402 cbTotalSize = ALIGN4BYTE(cbTotalSize);
403
404 // Return the size of the user data and the header data.
405 *pcbSaveSize = cbTotalSize + cbDataSize;
406 return S_OK;
407} // TiggerStorage::GetStreamSaveSize
408
409
410//*****************************************************************************
411// Return the fixed size overhead for the storage implementation. This includes
412// the signature and fixed header overhead. The overhead in the header for each
413// stream is calculated as part of GetStreamSaveSize because these structs are
414// variable sized on the name.
415//*****************************************************************************
416HRESULT TiggerStorage::GetStorageSaveSize( // Return code.
417 ULONG *pcbSaveSize, // [in] current size, [out] plus overhead.
418 ULONG cbExtra, // How much extra data to store in header.
419 LPCSTR pRuntimeVersion)
420{
421 HRESULT hr;
422
423 ULONG cbSignatureSize;
424 IfFailRet(SizeOfStorageSignature(pRuntimeVersion, &cbSignatureSize));
425
426 *pcbSaveSize += cbSignatureSize + sizeof(STORAGEHEADER);
427 if (cbExtra)
428 *pcbSaveSize += sizeof(ULONG) + cbExtra;
429 return (S_OK);
430}
431
432
433//*****************************************************************************
434// Adjust the offset in each known stream to match where it will wind up after
435// a save operation.
436//*****************************************************************************
437HRESULT TiggerStorage::CalcOffsets( // Return code.
438 STORAGESTREAMLST *pStreamList, // List of streams for header.
439 ULONG cbExtra, // Size of variable extra data in header.
440 LPCSTR pRuntimeVersion) // The version string as it's length is part of the total size.
441{
442 PSTORAGESTREAM pEntry; // Each entry in the list.
443 ULONG cbOffset=0; // Running offset for streams.
444 int i; // Loop control.
445
446 // Prime offset up front.
447 GetStorageSaveSize(&cbOffset, cbExtra, pRuntimeVersion);
448
449 // Add on the size of each header entry.
450 for (i=0; i<pStreamList->Count(); i++)
451 {
452 VERIFY(pEntry = pStreamList->Get(i));
453 cbOffset += sizeof(STORAGESTREAM) - MAXSTREAMNAME;
454 cbOffset += (ULONG)(strlen(pEntry->GetName()) + 1);
455 cbOffset = ALIGN4BYTE(cbOffset);
456 }
457
458 // Go through each stream and reset its expected offset.
459 for (i=0; i<pStreamList->Count(); i++)
460 {
461 VERIFY(pEntry = pStreamList->Get(i));
462 pEntry->SetOffset(cbOffset);
463 cbOffset += pEntry->GetSize();
464 }
465 return (S_OK);
466}
467
468
469
470HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream(
471 const OLECHAR *pwcsName,
472 DWORD grfMode,
473 DWORD reserved1,
474 DWORD reserved2,
475 IStream **ppstm)
476{
477 char rcStream[MAXSTREAMNAME];// For converted name.
478 VERIFY(Wsz_wcstombs(rcStream, pwcsName, sizeof(rcStream)));
479 return (CreateStream(rcStream, grfMode, reserved1, reserved2, ppstm));
480}
481
482
483#ifndef DACCESS_COMPILE
484HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStream(
485 LPCSTR szName,
486 DWORD grfMode,
487 DWORD reserved1,
488 DWORD reserved2,
489 IStream **ppstm)
490{
491 PSTORAGESTREAM pStream; // For lookup.
492 HRESULT hr;
493
494 _ASSERTE(szName && *szName);
495
496 // Check for existing stream, which might be an error or more likely
497 // a rewrite of a file.
498 if (SUCCEEDED(FindStream(szName, &pStream)))
499 {
500 // <REVISIT_TODO>REVIEW: STGM_FAILIFTHERE is 0, the following condition will be always false</REVISIT_TODO>
501 if (pStream->GetOffset() != 0xffffffff && ((grfMode & STGM_CREATE) == STGM_FAILIFTHERE))
502 return (PostError(STG_E_FILEALREADYEXISTS));
503 }
504 // Add a control to track this stream.
505 else if (!pStream && (pStream = m_Streams.Append()) == 0)
506 return (PostError(OutOfMemory()));
507 pStream->SetOffset(0xffffffff);
508 pStream->SetSize(0);
509 strcpy_s(pStream->GetName(), 32, szName);
510
511 // Now create a stream object to allow reading and writing.
512 TiggerStream *pNew = new (nothrow) TiggerStream;
513 if (!pNew)
514 return (PostError(OutOfMemory()));
515 *ppstm = (IStream *) pNew;
516
517 // Init the new object.
518 if (FAILED(hr = pNew->Init(this, pStream->GetName())))
519 {
520 delete pNew;
521 return (hr);
522 }
523 return (S_OK);
524}
525#endif //!DACCESS_COMPILE
526
527
528HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream(
529 const OLECHAR *pwcsName,
530 void *reserved1,
531 DWORD grfMode,
532 DWORD reserved2,
533 IStream **ppstm)
534{
535 return (E_NOTIMPL);
536}
537
538
539HRESULT STDMETHODCALLTYPE TiggerStorage::CreateStorage(
540 const OLECHAR *pwcsName,
541 DWORD grfMode,
542 DWORD dwStgFmt,
543 DWORD reserved2,
544 IStorage **ppstg)
545{
546 return (E_NOTIMPL);
547}
548
549
550HRESULT STDMETHODCALLTYPE
551TiggerStorage::OpenStorage(
552 const OLECHAR * wcsName,
553 IStorage * pStgPriority,
554 DWORD dwMode,
555 __in
556 SNB snbExclude,
557 DWORD reserved,
558 IStorage ** ppStg)
559{
560 return E_NOTIMPL;
561}
562
563HRESULT STDMETHODCALLTYPE
564TiggerStorage::CopyTo(
565 DWORD cIidExclude,
566 const IID * rgIidExclude,
567 __in
568 SNB snbExclude,
569 IStorage * pStgDest)
570{
571 return E_NOTIMPL;
572}
573
574
575HRESULT STDMETHODCALLTYPE TiggerStorage::MoveElementTo(
576 const OLECHAR *pwcsName,
577 IStorage *pstgDest,
578 const OLECHAR *pwcsNewName,
579 DWORD grfFlags)
580{
581 return (E_NOTIMPL);
582}
583
584
585HRESULT STDMETHODCALLTYPE TiggerStorage::Commit(
586 DWORD grfCommitFlags)
587{
588 return (E_NOTIMPL);
589}
590
591
592HRESULT STDMETHODCALLTYPE TiggerStorage::Revert()
593{
594 return (E_NOTIMPL);
595}
596
597
598HRESULT STDMETHODCALLTYPE TiggerStorage::EnumElements(
599 DWORD reserved1,
600 void *reserved2,
601 DWORD reserved3,
602 IEnumSTATSTG **ppenum)
603{
604 return (E_NOTIMPL);
605}
606
607
608HRESULT STDMETHODCALLTYPE TiggerStorage::DestroyElement(
609 const OLECHAR *pwcsName)
610{
611 return (E_NOTIMPL);
612}
613
614
615HRESULT STDMETHODCALLTYPE TiggerStorage::RenameElement(
616 const OLECHAR *pwcsOldName,
617 const OLECHAR *pwcsNewName)
618{
619 return (E_NOTIMPL);
620}
621
622
623HRESULT STDMETHODCALLTYPE TiggerStorage::SetElementTimes(
624 const OLECHAR *pwcsName,
625 const FILETIME *pctime,
626 const FILETIME *patime,
627 const FILETIME *pmtime)
628{
629 return (E_NOTIMPL);
630}
631
632
633HRESULT STDMETHODCALLTYPE TiggerStorage::SetClass(
634 REFCLSID clsid)
635{
636 return (E_NOTIMPL);
637}
638
639
640HRESULT STDMETHODCALLTYPE TiggerStorage::SetStateBits(
641 DWORD grfStateBits,
642 DWORD grfMask)
643{
644 return (E_NOTIMPL);
645}
646
647
648HRESULT STDMETHODCALLTYPE TiggerStorage::Stat(
649 STATSTG *pstatstg,
650 DWORD grfStatFlag)
651{
652 return (E_NOTIMPL);
653}
654
655
656
657HRESULT STDMETHODCALLTYPE TiggerStorage::OpenStream(
658 LPCWSTR szStream,
659 ULONG *pcbData,
660 void **ppAddress)
661{
662 PSTORAGESTREAM pStream; // For lookup.
663 char rcName[MAXSTREAMNAME]; // For conversion.
664 HRESULT hr;
665
666 // Convert the name for internal use.
667 VERIFY(::WszWideCharToMultiByte(CP_ACP, 0, szStream, -1, rcName, sizeof(rcName), 0, 0));
668
669 // Look for the stream which must be found for this to work. Note that
670 // this error is explicitly not posted as an error object since unfound streams
671 // are a common occurence and do not warrant a resource file load.
672 IfFailRet(FindStream(rcName, &pStream));
673
674 // Get the memory for the stream.
675 IfFailRet( m_pStgIO->GetPtrForMem(pStream->GetOffset(), pStream->GetSize(), *ppAddress) );
676 *pcbData = pStream->GetSize();
677 return (S_OK);
678}
679
680
681
682//
683// Protected.
684//
685
686
687//*****************************************************************************
688// Called by the stream implementation to write data out to disk.
689//*****************************************************************************
690HRESULT
691TiggerStorage::Write(
692 LPCSTR szName, // Name of stream we're writing.
693 const void *pData, // Data to write.
694 ULONG cbData, // Size of data.
695 ULONG *pcbWritten) // How much did we write.
696{
697 PSTORAGESTREAM pStream; // Update size data.
698 ULONG iOffset = 0; // Offset for write.
699 ULONG cbWritten; // Handle null case.
700 HRESULT hr;
701
702 // Get the stream descriptor.
703 if (FAILED(FindStream(szName, &pStream)))
704 return CLDB_E_FILE_BADWRITE;
705
706 // If we need to know the offset, keep it now.
707 if (pStream->GetOffset() == 0xffffffff)
708 {
709 iOffset = m_pStgIO->GetCurrentOffset();
710
711 // Align the storage on a 4 byte boundary.
712 if ((iOffset % 4) != 0)
713 {
714 ULONG cb;
715 ULONG pad = 0;
716
717 if (FAILED(hr = m_pStgIO->Write(&pad, ALIGN4BYTE(iOffset) - iOffset, &cb)))
718 return hr;
719 iOffset = m_pStgIO->GetCurrentOffset();
720
721 _ASSERTE((iOffset % 4) == 0);
722 }
723 }
724
725 // Avoid confusion.
726 if (pcbWritten == NULL)
727 pcbWritten = &cbWritten;
728 *pcbWritten = 0;
729
730 // Let OS do the write.
731 if (SUCCEEDED(hr = m_pStgIO->Write(pData, cbData, pcbWritten)))
732 {
733 // On success, record the new data.
734 if (pStream->GetOffset() == 0xffffffff)
735 pStream->SetOffset(iOffset);
736 pStream->SetSize(pStream->GetSize() + *pcbWritten);
737 return S_OK;
738 }
739 else
740 {
741 return hr;
742 }
743} // TiggerStorage::Write
744
745
746//
747// Private
748//
749
750HRESULT
751TiggerStorage::FindStream(
752 LPCSTR szName,
753 __out PSTORAGESTREAM *stream)
754{
755 *stream = NULL;
756 // In read mode, just walk the list and return one.
757 if (m_pStreamList != NULL)
758 {
759 PSTORAGESTREAM p = m_pStreamList;
760
761 SIZE_T pStartMD = (SIZE_T)(m_pStgIO->m_pData);
762 SIZE_T pEndMD = NULL;
763
764 if (!ClrSafeInt<SIZE_T>::addition(pStartMD, m_pStgIO->m_cbData, pEndMD))
765 {
766 Debug_ReportError("Invalid MetaData storage headers - size overflow.");
767 return CLDB_E_FILE_CORRUPT;
768 }
769
770 for (int i = 0; i < m_StgHdr.GetiStreams(); i++)
771 {
772 // Make sure this stream pointer is still inside the metadata
773 if (((SIZE_T)p < pStartMD) || ((SIZE_T)p > pEndMD))
774 {
775 Debug_ReportError("Invalid MetaData storage header - reached outside headers block.");
776 return CLDB_E_FILE_CORRUPT;
777 }
778
779 if (SString::_stricmp(p->GetName(), szName) == 0)
780 {
781 *stream = p;
782 return S_OK;
783 }
784 p = p->NextStream();
785 }
786 }
787 // In write mode, walk the array which is not on disk yet.
788 else
789 {
790 for (int j = 0; j < m_Streams.Count(); j++)
791 {
792 if (SString::_stricmp(m_Streams[j].GetName(), szName) == 0)
793 {
794 *stream = &m_Streams[j];
795 return S_OK;
796 }
797 }
798 }
799 return STG_E_FILENOTFOUND;
800} // TiggerStorage::FindStream
801
802
803//*****************************************************************************
804// Write the signature area of the file format to disk. This includes the
805// "magic" identifier and the version information.
806//*****************************************************************************
807HRESULT
808TiggerStorage::WriteSignature(
809 LPCSTR pVersion)
810{
811 STORAGESIGNATURE sSig;
812 ULONG cbWritten;
813 HRESULT hr = S_OK;
814
815 if (pVersion == NULL)
816 {
817 IfFailRet(GetDefaultVersion(&pVersion));
818 }
819 _ASSERTE(pVersion != NULL);
820
821 ULONG versionSize = (ULONG)strlen(pVersion) + 1;
822 ULONG alignedVersionSize = (ULONG)ALIGN_UP(versionSize, 4);
823
824 // Signature belongs at the start of the file.
825 _ASSERTE(m_pStgIO->GetCurrentOffset() == 0);
826
827 sSig.SetSignature(STORAGE_MAGIC_SIG);
828 sSig.SetMajorVer(FILE_VER_MAJOR);
829 sSig.SetMinorVer(FILE_VER_MINOR);
830 sSig.SetExtraDataOffset(0); // We have no extra inforation
831 sSig.SetVersionStringLength(alignedVersionSize);
832 IfFailRet(m_pStgIO->Write(&sSig, sizeof(STORAGESIGNATURE), &cbWritten));
833 IfFailRet(m_pStgIO->Write(pVersion, versionSize, &cbWritten));
834
835 // Write padding
836 if (alignedVersionSize - versionSize != 0)
837 {
838 BYTE padding[4];
839 ZeroMemory(padding, sizeof(padding));
840 IfFailRet(m_pStgIO->Write(padding, alignedVersionSize - versionSize, &cbWritten));
841 }
842
843 return hr;
844} // TiggerStorage::WriteSignature
845
846
847//*****************************************************************************
848// Read the header from disk. This reads the header for the most recent version
849// of the file format which has the header at the front of the data file.
850//*****************************************************************************
851HRESULT
852TiggerStorage::ReadHeader()
853{
854 PSTORAGESTREAM pAppend, pStream; // For copy of array.
855 void *ptr; // Working pointer.
856 ULONG iOffset; // Offset of header data.
857 ULONG cbExtra; // Size of extra data.
858 ULONG cbRead; // For calc of read sizes.
859 HRESULT hr;
860
861 // Read the signature
862 if (FAILED(hr = m_pStgIO->GetPtrForMem(0, sizeof(STORAGESIGNATURE), ptr)))
863 {
864 Debug_ReportError("Cannot read MetaData storage signature header.");
865 return hr;
866 }
867
868 PSTORAGESIGNATURE pStorage = (PSTORAGESIGNATURE)ptr;
869
870 // Header data starts after signature.
871 iOffset = sizeof(STORAGESIGNATURE) + pStorage->GetVersionStringLength();
872
873 // Read the storage header which has the stream counts. Throw in the extra
874 // count which might not exist, but saves us down stream.
875 if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr)))
876 {
877 Debug_ReportError("Cannot read first MetaData storage header.");
878 return hr;
879 }
880 _ASSERTE(m_pStgIO->IsAlignedPtr((ULONG_PTR) ptr, 4));
881
882 // Read the storage header which has the stream counts. Throw in the extra
883 // count which might not exist, but saves us down stream.
884 if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, sizeof(STORAGEHEADER) + sizeof(ULONG), ptr)))
885 {
886 Debug_ReportError("Cannot read second MetaData storage header.");
887 return hr;
888 }
889 if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4))
890 {
891 Debug_ReportError("Invalid MetaData storage headers - unaligned size.");
892 return PostError(CLDB_E_FILE_CORRUPT);
893 }
894
895 // Copy the header into memory and check it.
896 memcpy(&m_StgHdr, ptr, sizeof(STORAGEHEADER));
897 IfFailRet( VerifyHeader() );
898 ptr = (void *)((PSTORAGEHEADER)ptr + 1);
899 iOffset += sizeof(STORAGEHEADER);
900
901 // Save off a pointer to the extra data.
902 if ((m_StgHdr.GetFlags() & STGHDR_EXTRADATA) != 0)
903 {
904 m_pbExtra = ptr;
905 cbExtra = sizeof(ULONG) + *(ULONG *)ptr;
906
907 // Force the extra data to get faulted in.
908 IfFailRet(m_pStgIO->GetPtrForMem(iOffset, cbExtra, ptr));
909 if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4))
910 {
911 Debug_ReportError("Invalid MetaData storage signature - unaligned extra data.");
912 return PostError(CLDB_E_FILE_CORRUPT);
913 }
914 }
915 else
916 {
917 m_pbExtra = 0;
918 cbExtra = 0;
919 }
920 iOffset += cbExtra;
921
922 // Force the worst case scenario of bytes to get faulted in for the
923 // streams. This makes the rest of this code very simple.
924 cbRead = sizeof(STORAGESTREAM) * m_StgHdr.GetiStreams();
925 if (cbRead != 0)
926 {
927 cbRead = min(cbRead, m_pStgIO->GetDataSize() - iOffset);
928 if (FAILED(hr = m_pStgIO->GetPtrForMem(iOffset, cbRead, ptr)))
929 {
930 Debug_ReportError("Invalid MetaData stogare headers.");
931 return hr;
932 }
933 if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)ptr, 4))
934 {
935 Debug_ReportError("Invalid MetaData stogare headers - unaligned start.");
936 return PostError(CLDB_E_FILE_CORRUPT);
937 }
938
939 // For read only, just access the header data.
940 if (m_pStgIO->IsReadOnly())
941 {
942 // Save a pointer to the current list of streams.
943 m_pStreamList = (PSTORAGESTREAM)ptr;
944 }
945 // For writeable, need a copy we can modify.
946 else
947 {
948 pStream = (PSTORAGESTREAM)ptr;
949
950 // Copy each of the stream headers.
951 for (int i = 0; i < m_StgHdr.GetiStreams(); i++)
952 {
953 if ((pAppend = m_Streams.Append()) == NULL)
954 return PostError(OutOfMemory());
955 // Validate that the stream header is not too big.
956 ULONG sz = pStream->GetStreamSize();
957 if (sz > sizeof(STORAGESTREAM))
958 {
959 Debug_ReportError("Invalid MetaData storage stream - data too big.");
960 return PostError(CLDB_E_FILE_CORRUPT);
961 }
962 memcpy (pAppend, pStream, sz);
963 pStream = pStream->NextStream();
964 if (!m_pStgIO->IsAlignedPtr((ULONG_PTR)pStream, 4))
965 {
966 Debug_ReportError("Invalid MetaData storage stream - unaligned data.");
967 return PostError(CLDB_E_FILE_CORRUPT);
968 }
969 }
970
971 // All must be loaded and accounted for.
972 _ASSERTE(m_StgHdr.GetiStreams() == m_Streams.Count());
973 }
974 }
975 return S_OK;
976} // TiggerStorage::ReadHeader
977
978
979//*****************************************************************************
980// Verify the header is something this version of the code can support.
981//*****************************************************************************
982HRESULT TiggerStorage::VerifyHeader()
983{
984 //<REVISIT_TODO>@FUTURE: add version check for format.</REVISIT_TODO>
985 return S_OK;
986}
987
988//*****************************************************************************
989// Print the sizes of the various streams.
990//*****************************************************************************
991#if defined(_DEBUG)
992ULONG TiggerStorage::PrintSizeInfo(bool verbose)
993{
994 ULONG total = 0;
995
996 printf("Storage Header: %d\n", sizeof(STORAGEHEADER));
997 if (m_pStreamList != NULL)
998 {
999 PSTORAGESTREAM storStream = m_pStreamList;
1000 PSTORAGESTREAM pNext;
1001 for (int i = 0; i < m_StgHdr.GetiStreams(); i++)
1002 {
1003 pNext = storStream->NextStream();
1004 printf("Stream #%d (%s) Header: %d, Data: %d\n",i,storStream->GetName(), (BYTE*)pNext - (BYTE*)storStream, storStream->GetSize());
1005 total += storStream->GetSize();
1006 storStream = pNext;
1007 }
1008 }
1009 else
1010 {
1011 //<REVISIT_TODO>todo: Add support for the case where m_Streams exists and m_pStreamList does not</REVISIT_TODO>
1012 }
1013
1014 if (m_pbExtra != NULL)
1015 {
1016 printf("Extra bytes: %d\n",*(ULONG*)m_pbExtra);
1017 total += *(ULONG*)m_pbExtra;
1018 }
1019 return total;
1020}
1021#endif // _DEBUG
1022