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. |
27 | HRESULT |
28 | CLRRuntimeHostInternal_GetImageVersionString( |
29 | __out_ecount(*pcchBuffer) |
30 | LPWSTR wszBuffer, |
31 | DWORD * pcchBuffer); |
32 | |
33 | TiggerStorage::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 | |
43 | TiggerStorage::~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 | //***************************************************************************** |
56 | HRESULT |
57 | TiggerStorage::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 | |
96 | ErrExit: |
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 | //***************************************************************************** |
108 | HRESULT |
109 | TiggerStorage::( |
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 | //***************************************************************************** |
133 | HRESULT |
134 | TiggerStorage::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 | |
176 | HRESULT |
177 | TiggerStorage::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 | //***************************************************************************** |
204 | HRESULT |
205 | TiggerStorage::( |
206 | ULONG *, // 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 | //***************************************************************************** |
233 | HRESULT |
234 | TiggerStorage::( |
235 | STORAGESTREAMLST *pList, // List of streams. |
236 | ULONG , // Size of extra data, may be 0. |
237 | BYTE *) // 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 | //***************************************************************************** |
298 | HRESULT |
299 | TiggerStorage::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 | //***************************************************************************** |
374 | HRESULT 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 | //***************************************************************************** |
386 | HRESULT |
387 | TiggerStorage::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 | //***************************************************************************** |
416 | HRESULT TiggerStorage::GetStorageSaveSize( // Return code. |
417 | ULONG *pcbSaveSize, // [in] current size, [out] plus overhead. |
418 | ULONG , // 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 | //***************************************************************************** |
437 | HRESULT TiggerStorage::CalcOffsets( // Return code. |
438 | STORAGESTREAMLST *pStreamList, // List of streams for header. |
439 | ULONG , // 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 | |
470 | HRESULT 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 |
484 | HRESULT 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 | |
528 | HRESULT 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 | |
539 | HRESULT 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 | |
550 | HRESULT STDMETHODCALLTYPE |
551 | TiggerStorage::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 | |
563 | HRESULT STDMETHODCALLTYPE |
564 | TiggerStorage::CopyTo( |
565 | DWORD cIidExclude, |
566 | const IID * rgIidExclude, |
567 | __in |
568 | SNB snbExclude, |
569 | IStorage * pStgDest) |
570 | { |
571 | return E_NOTIMPL; |
572 | } |
573 | |
574 | |
575 | HRESULT 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 | |
585 | HRESULT STDMETHODCALLTYPE TiggerStorage::Commit( |
586 | DWORD grfCommitFlags) |
587 | { |
588 | return (E_NOTIMPL); |
589 | } |
590 | |
591 | |
592 | HRESULT STDMETHODCALLTYPE TiggerStorage::Revert() |
593 | { |
594 | return (E_NOTIMPL); |
595 | } |
596 | |
597 | |
598 | HRESULT STDMETHODCALLTYPE TiggerStorage::EnumElements( |
599 | DWORD reserved1, |
600 | void *reserved2, |
601 | DWORD reserved3, |
602 | IEnumSTATSTG **ppenum) |
603 | { |
604 | return (E_NOTIMPL); |
605 | } |
606 | |
607 | |
608 | HRESULT STDMETHODCALLTYPE TiggerStorage::DestroyElement( |
609 | const OLECHAR *pwcsName) |
610 | { |
611 | return (E_NOTIMPL); |
612 | } |
613 | |
614 | |
615 | HRESULT STDMETHODCALLTYPE TiggerStorage::RenameElement( |
616 | const OLECHAR *pwcsOldName, |
617 | const OLECHAR *pwcsNewName) |
618 | { |
619 | return (E_NOTIMPL); |
620 | } |
621 | |
622 | |
623 | HRESULT 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 | |
633 | HRESULT STDMETHODCALLTYPE TiggerStorage::SetClass( |
634 | REFCLSID clsid) |
635 | { |
636 | return (E_NOTIMPL); |
637 | } |
638 | |
639 | |
640 | HRESULT STDMETHODCALLTYPE TiggerStorage::SetStateBits( |
641 | DWORD grfStateBits, |
642 | DWORD grfMask) |
643 | { |
644 | return (E_NOTIMPL); |
645 | } |
646 | |
647 | |
648 | HRESULT STDMETHODCALLTYPE TiggerStorage::Stat( |
649 | STATSTG *pstatstg, |
650 | DWORD grfStatFlag) |
651 | { |
652 | return (E_NOTIMPL); |
653 | } |
654 | |
655 | |
656 | |
657 | HRESULT 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 | //***************************************************************************** |
690 | HRESULT |
691 | TiggerStorage::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 | |
750 | HRESULT |
751 | TiggerStorage::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 | //***************************************************************************** |
807 | HRESULT |
808 | TiggerStorage::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 | //***************************************************************************** |
851 | HRESULT |
852 | TiggerStorage::() |
853 | { |
854 | PSTORAGESTREAM pAppend, pStream; // For copy of array. |
855 | void *ptr; // Working pointer. |
856 | ULONG iOffset; // Offset of header data. |
857 | ULONG ; // 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 | //***************************************************************************** |
982 | HRESULT TiggerStorage::() |
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) |
992 | ULONG 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 | |