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
6//
7// LiteWeightStgdb.cpp
8//
9// This contains definition of class CLiteWeightStgDB. This is light weight
10// read-only implementation for accessing compressed meta data format.
11//
12//*****************************************************************************
13#include "stdafx.h" // Precompiled header.
14
15#include "metamodelrw.h"
16#include "liteweightstgdb.h"
17
18// include stgdatabase.h for GUID_POOL_STREAM definition
19// #include "stgdatabase.h"
20
21// include StgTiggerStorage for TiggerStorage definition
22#include "stgtiggerstorage.h"
23#include "stgio.h"
24#include "pedecoder.h"
25
26#include <log.h>
27
28
29#ifndef TYPELIB_SIG
30#define TYPELIB_SIG_MSFT 0x5446534D // MSFT
31#define TYPELIB_SIG_SLTG 0x47544C53 // SLTG
32#endif
33
34//*****************************************************************************
35// Checks the given storage object to see if it is an NT PE image.
36//*****************************************************************************
37int _IsNTPEImage( // true if file is NT PE image.
38 StgIO *pStgIO) // Storage object.
39{
40 LONG lfanew=0; // Offset in DOS header to NT header.
41 ULONG lSignature=0; // For NT header signature.
42 HRESULT hr;
43
44 // Read DOS header to find the NT header offset.
45 if (FAILED(hr = pStgIO->Seek(60, FILE_BEGIN)) ||
46 FAILED(hr = pStgIO->Read(&lfanew, sizeof(LONG), 0)))
47 {
48 return (false);
49 }
50
51 // Seek to the NT header and read the signature.
52 if (FAILED(hr = pStgIO->Seek(VAL32(lfanew), FILE_BEGIN)) ||
53 FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) ||
54 FAILED(hr = pStgIO->Seek(0, FILE_BEGIN)))
55 {
56 return (false);
57 }
58
59 // If the signature is a match, then we have a PE format.
60 if (lSignature == VAL32(IMAGE_NT_SIGNATURE))
61 return (true);
62 else
63 return (false);
64}
65
66BOOL _GetFileTypeForPathExt(StgIO * pStgIO, FILETYPE * piType)
67{
68 // Avoid confusion.
69 *piType = pStgIO->GetFileType();
70
71 // All file types except .obj have a signature built in. You should
72 // not get to this code for those file types unless that file is corrupt,
73 // or someone has changed a format without updating this code.
74 _ASSERTE((*piType == FILETYPE_UNKNOWN) || (*piType == FILETYPE_NTOBJ) || (*piType == FILETYPE_TLB));
75
76 // If we found a type, then you're ok.
77 return (*piType != FILETYPE_UNKNOWN);
78}
79
80HRESULT _GetFileTypeForPath(StgIO *pStgIO, FILETYPE *piType)
81{
82 ULONG lSignature=0;
83 HRESULT hr;
84
85 // Assume native file.
86 *piType = FILETYPE_CLB;
87
88 // Need to read signature to see what type it is.
89 if (!(pStgIO->GetFlags() & DBPROP_TMODEF_CREATE))
90 {
91 if (FAILED(hr = pStgIO->Read(&lSignature, sizeof(ULONG), 0)) ||
92 FAILED(hr = pStgIO->Seek(0, FILE_BEGIN)))
93 {
94 return (hr);
95 }
96 lSignature = VAL32(lSignature);
97 if (lSignature == STORAGE_MAGIC_SIG)
98 *piType = FILETYPE_CLB;
99 else if ((WORD) lSignature ==IMAGE_DOS_SIGNATURE && _IsNTPEImage(pStgIO))
100 *piType = FILETYPE_NTPE;
101 else if (lSignature == TYPELIB_SIG_MSFT || lSignature == TYPELIB_SIG_SLTG)
102 *piType = FILETYPE_TLB;
103 else if (!_GetFileTypeForPathExt(pStgIO, piType))
104 return CLDB_E_FILE_CORRUPT;
105 }
106 return S_OK;
107}
108
109//*****************************************************************************
110// Prepare to go away.
111//*****************************************************************************
112CLiteWeightStgdbRW::~CLiteWeightStgdbRW()
113{
114 // Free up this stacks reference on the I/O object.
115 if (m_pStgIO != NULL)
116 {
117 m_pStgIO->Release();
118 m_pStgIO = NULL;
119 }
120
121 if (m_pStreamList != NULL)
122 {
123 delete m_pStreamList;
124 }
125
126 if (m_wszFileName != NULL)
127 {
128 delete [] m_wszFileName;
129 }
130}
131
132//*****************************************************************************
133// Open an in-memory metadata section for read
134//*****************************************************************************
135__checkReturn
136HRESULT CLiteWeightStgdbRW::InitOnMem(
137 ULONG cbData, // count of bytes in pData
138 LPCVOID pData, // points to meta data section in memory
139 int bReadOnly) // If true, read-only.
140{
141 StgIO *pStgIO = NULL; // For file i/o.
142 HRESULT hr = NOERROR;
143
144 if ((pStgIO = new (nothrow) StgIO) == 0)
145 IfFailGo( E_OUTOFMEMORY);
146
147 // Open the storage based on the pbData and cbData
148 IfFailGo( pStgIO->Open(
149 NULL, // filename
150 STGIO_READ,
151 pData,
152 cbData,
153 NULL, // IStream*
154 NULL) // LPSecurityAttributes
155 );
156
157 IfFailGo( InitFileForRead(pStgIO, bReadOnly) );
158
159ErrExit:
160 if (SUCCEEDED(hr))
161 {
162 m_pStgIO = pStgIO;
163 }
164 else
165 {
166 if (pStgIO)
167 pStgIO->Release();
168 }
169 return hr;
170} // CLiteWeightStgdbRW::InitOnMem
171
172
173//*****************************************************************************
174// Given an StgIO, opens compressed streams and do proper initialization.
175// This is a helper for other Init functions.
176//*****************************************************************************
177__checkReturn
178HRESULT
179CLiteWeightStgdbRW::InitFileForRead(
180 StgIO * pStgIO, // For file i/o.
181 int bReadOnly) // If read-only open.
182{
183 TiggerStorage * pStorage = NULL;
184 void * pvData;
185 ULONG cbData;
186 HRESULT hr = NOERROR;
187
188 // Allocate a new storage object which has IStorage on it.
189 pStorage = new (nothrow) TiggerStorage();
190 IfNullGo(pStorage);
191
192 // Init the storage object on the backing storage.
193 OptionValue ov;
194 IfFailGo(m_MiniMd.GetOption(&ov));
195 IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion));
196
197 // Save pointers to header structure for version string.
198 _ASSERTE((m_pvMd == NULL) && (m_cbMd == 0));
199 IfFailGo(pStorage->GetHeaderPointer(&m_pvMd, &m_cbMd));
200
201 // Check to see if this is a minimal metadata
202 if (SUCCEEDED(pStorage->OpenStream(MINIMAL_MD_STREAM, &cbData, &pvData)))
203 {
204 m_MiniMd.m_fMinimalDelta = TRUE;
205 }
206
207 // Load the string pool.
208 if (SUCCEEDED(hr = pStorage->OpenStream(STRING_POOL_STREAM, &cbData, &pvData)))
209 {
210 // String pool has to end with a null-terminator, therefore we don't have to check string pool
211 // content on access.
212 // Shrink size of the pool to the last null-terminator found.
213 while (cbData != 0)
214 {
215 if (((LPBYTE)pvData)[cbData - 1] == 0)
216 { // We have found last null terminator
217 break;
218 }
219 // Shrink size of the pool
220 cbData--;
221 Debug_ReportError("String heap/pool does not end with null-terminator ... shrinking the heap.");
222 }
223 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, pvData, cbData, bReadOnly));
224 }
225 else
226 {
227 if (hr != STG_E_FILENOTFOUND)
228 {
229 IfFailGo(hr);
230 }
231 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolStrings, NULL, 0, bReadOnly));
232 }
233
234 // Load the user string blob pool.
235 if (SUCCEEDED(hr = pStorage->OpenStream(US_BLOB_POOL_STREAM, &cbData, &pvData)))
236 {
237 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, pvData, cbData, bReadOnly));
238 }
239 else
240 {
241 if (hr != STG_E_FILENOTFOUND)
242 {
243 IfFailGo(hr);
244 }
245 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolUSBlobs, NULL, 0, bReadOnly));
246 }
247
248 // Load the guid pool.
249 if (SUCCEEDED(hr = pStorage->OpenStream(GUID_POOL_STREAM, &cbData, &pvData)))
250 {
251 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, pvData, cbData, bReadOnly));
252 }
253 else
254 {
255 if (hr != STG_E_FILENOTFOUND)
256 {
257 IfFailGo(hr);
258 }
259 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolGuids, NULL, 0, bReadOnly));
260 }
261
262 // Load the blob pool.
263 if (SUCCEEDED(hr = pStorage->OpenStream(BLOB_POOL_STREAM, &cbData, &pvData)))
264 {
265 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, pvData, cbData, bReadOnly));
266 }
267 else
268 {
269 if (hr != STG_E_FILENOTFOUND)
270 {
271 IfFailGo(hr);
272 }
273 IfFailGo(m_MiniMd.InitPoolOnMem(MDPoolBlobs, NULL, 0, bReadOnly));
274 }
275
276 // Open the metadata.
277 hr = pStorage->OpenStream(COMPRESSED_MODEL_STREAM, &cbData, &pvData);
278 if (hr == STG_E_FILENOTFOUND)
279 {
280 IfFailGo(pStorage->OpenStream(ENC_MODEL_STREAM, &cbData, &pvData));
281 }
282 IfFailGo(m_MiniMd.InitOnMem(pvData, cbData, bReadOnly));
283 IfFailGo(m_MiniMd.PostInit(0));
284
285ErrExit:
286 if (pStorage != NULL)
287 {
288 delete pStorage;
289 }
290 return hr;
291} // CLiteWeightStgdbRW::InitFileForRead
292
293//*****************************************************************************
294// Open a metadata section for read
295//*****************************************************************************
296__checkReturn
297HRESULT CLiteWeightStgdbRW::OpenForRead(
298 LPCWSTR szDatabase, // Name of database.
299 void *pbData, // Data to open on top of, 0 default.
300 ULONG cbData, // How big is the data.
301 DWORD dwFlags) // Flags for the open.
302{
303 LPCWSTR pNoFile=W(""); // Constant for empty file name.
304 StgIO *pStgIO = NULL; // For file i/o.
305 HRESULT hr;
306
307 m_pImage = NULL;
308 m_dwImageSize = 0;
309 m_eFileType = FILETYPE_UNKNOWN;
310 // szDatabase, and pbData are mutually exclusive. Only one may be
311 // non-NULL. Having both NULL means empty stream creation.
312 //
313 _ASSERTE(!(szDatabase && (pbData)));
314 _ASSERTE(!(pbData && (szDatabase)));
315
316 // Open on memory needs there to be something to work with.
317 if (pbData && cbData == 0)
318 IfFailGo(CLDB_E_NO_DATA);
319
320 // Make sure we have a path to work with.
321 if (!szDatabase)
322 szDatabase = pNoFile;
323
324 // Sanity check the name lentgh.
325 if (!IsValidFileNameLength(szDatabase))
326 {
327 IfFailGo(E_INVALIDARG);
328 }
329
330 // If we have storage to work with, init it and get type.
331 if (*szDatabase || pbData)
332 {
333 // Allocate a storage instance to use for i/o.
334 if ((pStgIO = new (nothrow) StgIO) == 0)
335 IfFailGo( E_OUTOFMEMORY );
336
337 DBPROPMODE dmOpenFlags = DBPROP_TMODEF_READ;
338
339 // If we're taking ownership of this memory.....
340 if (IsOfTakeOwnership(dwFlags))
341 {
342 dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_SHAREDMEM);
343 }
344#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES
345 if (IsOfTrustedImage(dwFlags))
346 dmOpenFlags = (DBPROPMODE)(dmOpenFlags | DBPROP_TMODEF_TRYLOADLIBRARY);
347#endif
348
349 // Open the storage so we can read the signature if there is already data.
350 IfFailGo( pStgIO->Open(szDatabase,
351 dmOpenFlags,
352 pbData,
353 cbData,
354 0, // IStream*
355 NULL) );
356
357 // Determine the type of file we are working with.
358 IfFailGo( _GetFileTypeForPath(pStgIO, &m_eFileType) );
359 }
360
361 // Check for default type.
362 if (m_eFileType == FILETYPE_CLB)
363 {
364 // If user wanted us to make a local copy of the data, do that now.
365 if (IsOfCopyMemory(dwFlags))
366 IfFailGo(pStgIO->LoadFileToMemory());
367
368 // Try the native .clb file.
369 IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) );
370 }
371 // PE/COFF executable/object format. This requires us to find the .clb
372 // inside the binary before doing the Init.
373 else if (m_eFileType == FILETYPE_NTPE || m_eFileType == FILETYPE_NTOBJ)
374 {
375 //<TODO>@FUTURE: Ideally the FindImageMetaData function
376 //@FUTURE: would take the pStgIO and map only the part of the file where
377 //@FUTURE: our data lives, leaving the rest alone. This would be smaller
378 //@FUTURE: working set for us.</TODO>
379 void *ptr;
380 ULONG cbSize;
381
382 // Map the entire binary for the FindImageMetaData function.
383 IfFailGo( pStgIO->MapFileToMem(ptr, &cbSize) );
384
385 // Find the .clb inside of the content.
386 if (m_eFileType == FILETYPE_NTPE)
387 {
388 m_pImage = ptr;
389 m_dwImageSize = cbSize;
390 hr = FindImageMetaData(ptr,
391 cbSize,
392 pStgIO->GetMemoryMappedType() == MTYPE_IMAGE,
393 &ptr,
394 &cbSize);
395 }
396 else
397 {
398 _ASSERTE(pStgIO->GetMemoryMappedType() != MTYPE_IMAGE);
399 hr = FindObjMetaData(ptr, cbSize, &ptr, &cbSize);
400 }
401 // Was the metadata found inside the PE file?
402 if (FAILED(hr))
403 {
404 if (hr == E_OUTOFMEMORY)
405 IfFailGo(E_OUTOFMEMORY);
406
407 // No clb in the PE, assume it is a type library.
408 m_eFileType = FILETYPE_TLB;
409
410 // Let the caller deal with a TypeLib.
411 IfFailGo(hr);
412 }
413 else
414 {
415 // Metadata was found inside the file.
416 // Now reset the base of the stg object so that all memory accesses
417 // are relative to the .clb content.
418 //
419 IfFailGo( pStgIO->SetBaseRange(ptr, cbSize) );
420
421 // If user wanted us to make a local copy of the data, do that now.
422 if (IsOfCopyMemory(dwFlags))
423 {
424 // Cache the PEKind, Machine.
425 GetPEKind(pStgIO->GetMemoryMappedType(), NULL, NULL);
426 // Copy the file into memory; releases the file.
427 IfFailGo(pStgIO->LoadFileToMemory());
428 // No longer have the image.
429 m_pImage = NULL;
430 m_dwImageSize = 0;
431 }
432
433 // Defer to the normal lookup.
434 IfFailGo( InitFileForRead(pStgIO, IsOfRead(dwFlags)) );
435 }
436 }
437 else if (m_eFileType == FILETYPE_TLB)
438 {
439 // Let the caller deal with a TypeLib.
440 IfFailGo(CLDB_E_NO_DATA);
441 }
442 // This spells trouble, we need to handle all types we might find.
443 else
444 {
445 _ASSERTE(!"Unknown file type.");
446 IfFailGo( E_FAIL );
447 }
448
449 // Save off everything.
450 IfFailGo(SetFileName(szDatabase));
451
452 // If this was a file...
453 if (pbData == NULL)
454 {
455 WIN32_FILE_ATTRIBUTE_DATA faData;
456 if (!WszGetFileAttributesEx(szDatabase, GetFileExInfoStandard, &faData))
457 IfFailGo(E_FAIL);
458 m_dwDatabaseLFS = faData.nFileSizeLow;
459 m_dwDatabaseLFT = faData.ftLastWriteTime.dwLowDateTime;
460 }
461
462ErrExit:
463 if (SUCCEEDED(hr))
464 {
465 m_pStgIO = pStgIO;
466 }
467 else
468 {
469 if (pStgIO != NULL)
470 pStgIO->Release();
471 }
472 return hr;
473}
474
475#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
476// Open a metadata section for read/write
477__checkReturn
478HRESULT CLiteWeightStgdbRW::OpenForRead(
479 IMDCustomDataSource *pDataSource, // data to open on top of
480 DWORD dwFlags) // Flags for the open.
481{
482 LPCWSTR pNoFile = W(""); // Constant for empty file name.
483 StgIO *pStgIO = NULL; // For file i/o.
484 HRESULT hr;
485
486 m_pImage = NULL;
487 m_dwImageSize = 0;
488 m_eFileType = FILETYPE_UNKNOWN;
489
490 IfFailGo(m_MiniMd.InitOnCustomDataSource(pDataSource));
491 IfFailGo(m_MiniMd.PostInit(0));
492
493 // Save off everything.
494 IfFailGo(SetFileName(pNoFile));
495
496ErrExit:
497 return hr;
498}
499#endif
500
501// Read/Write versions.
502//*****************************************************************************
503// Init the Stgdb and its subcomponents.
504//*****************************************************************************
505__checkReturn
506HRESULT CLiteWeightStgdbRW::InitNew()
507{
508 InitializeLogging();
509 LOG((LF_METADATA, LL_INFO10, "Metadata logging enabled\n"));
510
511 //<TODO>@FUTURE: should probably init the pools here instead of in the MiniMd.</TODO>
512 return m_MiniMd.InitNew();
513}
514
515//*****************************************************************************
516// Determine what the size of the saved data will be.
517//*****************************************************************************
518__checkReturn
519HRESULT CLiteWeightStgdbRW::GetSaveSize(// S_OK or error.
520 CorSaveSize fSave, // Quick or accurate?
521 UINT32 *pcbSaveSize, // Put the size here.
522 MetaDataReorderingOptions reorderingOptions,
523 CorProfileData *pProfileData) // Profile data for working set optimization
524{
525 HRESULT hr = S_OK; // A result.
526 UINT32 cbTotal = 0; // The total size.
527 UINT32 cbSize = 0; // Size of a component.
528
529 m_cbSaveSize = 0;
530
531 // Allocate stream list if not already done.
532 if (m_pStreamList == NULL)
533 {
534 IfNullGo(m_pStreamList = new (nothrow) STORAGESTREAMLST);
535 }
536 else
537 {
538 m_pStreamList->Clear();
539 }
540
541 // Make sure the user string pool is not empty. An empty user string pool causes
542 // problems with edit and continue
543
544 if (m_MiniMd.m_UserStringHeap.GetUnalignedSize() <= 1)
545 {
546 if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode) &&
547 !m_MiniMd.IsMinimalDelta())
548 {
549 BYTE rgData[] = {' ', 0, 0};
550 UINT32 nIndex_Ignore;
551 IfFailGo(m_MiniMd.PutUserString(
552 MetaData::DataBlob(rgData, sizeof(rgData)),
553 &nIndex_Ignore));
554 }
555 }
556
557 // If we're saving a delta metadata, figure out how much space it will take to
558 // save the minimal metadata stream (used only to identify that we have a delta
559 // metadata... nothing should be in that stream.
560 if ((m_MiniMd.m_OptionValue.m_UpdateMode & MDUpdateMask) == MDUpdateDelta)
561 {
562 IfFailGo(AddStreamToList(0, MINIMAL_MD_STREAM));
563 // Ask the storage system to add stream fixed overhead.
564 IfFailGo(TiggerStorage::GetStreamSaveSize(MINIMAL_MD_STREAM, 0, &cbSize));
565 cbTotal += cbSize;
566 }
567
568 if (reorderingOptions & ReArrangeStringPool)
569 {
570 if (pProfileData != NULL)
571 {
572 UINT32 cbHotSize = 0; // Size of pool data.
573 UINT32 cbStream; // Size of just the stream.
574 DWORD bCompressed; // Will the stream be compressed data?
575
576 // Ask the metadata to size its hot data.
577 IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData));
578 cbStream = cbHotSize;
579 m_bSaveCompressed = bCompressed;
580
581 if (cbHotSize != 0)
582 {
583 // Add this item to the save list.
584 IfFailGo(AddStreamToList(cbHotSize, HOT_MODEL_STREAM));
585
586 // Ask the storage system to add stream fixed overhead.
587 IfFailGo(TiggerStorage::GetStreamSaveSize(HOT_MODEL_STREAM, cbHotSize, &cbHotSize));
588
589 // Log the size info.
590 LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
591 HOT_MODEL_STREAM, cbStream, cbHotSize));
592
593 cbTotal += cbHotSize;
594 }
595 }
596
597 // get string pool save size
598 IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize));
599 cbTotal += cbSize;
600 }
601
602 // Query the MiniMd for its size.
603 IfFailGo(GetTablesSaveSize(fSave, &cbSize, reorderingOptions, pProfileData));
604 cbTotal += cbSize;
605
606 // Get the pools' sizes.
607 if( !(reorderingOptions & ReArrangeStringPool) )
608 {
609 IfFailGo(GetPoolSaveSize(STRING_POOL_STREAM, MDPoolStrings, &cbSize));
610 cbTotal += cbSize;
611 }
612 IfFailGo(GetPoolSaveSize(US_BLOB_POOL_STREAM, MDPoolUSBlobs, &cbSize));
613 cbTotal += cbSize;
614 IfFailGo(GetPoolSaveSize(GUID_POOL_STREAM, MDPoolGuids, &cbSize));
615 cbTotal += cbSize;
616 IfFailGo(GetPoolSaveSize(BLOB_POOL_STREAM, MDPoolBlobs, &cbSize));
617 cbTotal += cbSize;
618
619 // Finally, ask the storage system to add fixed overhead it needs for the
620 // file format. The overhead of each stream has already be calculated as
621 // part of GetStreamSaveSize. What's left is the signature and header
622 // fixed size overhead.
623 IfFailGo(TiggerStorage::GetStorageSaveSize((ULONG *)&cbTotal, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion));
624
625 // Log the size info.
626 LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize total is %d.\n", cbTotal));
627
628 // The list of streams that will be saved are now in the stream save list.
629 // Next step is to walk that list and fill out the correct offsets. This is
630 // done here so that the data can be streamed without fixing up the header.
631 TiggerStorage::CalcOffsets(m_pStreamList, 0, m_MiniMd.m_OptionValue.m_RuntimeVersion);
632
633 if (pcbSaveSize != NULL)
634 {
635 *pcbSaveSize = cbTotal;
636 }
637
638 // Don't cache the value for the EnC case
639 if (!IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode))
640 m_cbSaveSize = cbTotal;
641
642ErrExit:
643 return hr;
644} // CLiteWeightStgdbRW::GetSaveSize
645
646//*****************************************************************************
647// Get the save size of one of the pools. Also adds the pool's stream to
648// the list of streams to be saved.
649//*****************************************************************************
650__checkReturn
651HRESULT
652CLiteWeightStgdbRW::GetPoolSaveSize(
653 LPCWSTR szHeap, // Name of the heap stream.
654 int iPool, // The pool of which to get size.
655 UINT32 *pcbSaveSize) // Add pool data to this value.
656{
657 UINT32 cbSize = 0; // Size of pool data.
658 UINT32 cbStream; // Size of just the stream.
659 HRESULT hr;
660
661 *pcbSaveSize = 0;
662
663 // If there is no data, then don't bother.
664 if (m_MiniMd.IsPoolEmpty(iPool))
665 return (S_OK);
666
667 // Ask the pool to size its data.
668 IfFailGo(m_MiniMd.GetPoolSaveSize(iPool, &cbSize));
669 cbStream = cbSize;
670
671 // Add this item to the save list.
672 IfFailGo(AddStreamToList(cbSize, szHeap));
673
674
675 // Ask the storage system to add stream fixed overhead.
676 IfFailGo(TiggerStorage::GetStreamSaveSize(szHeap, cbSize, &cbSize));
677
678 // Log the size info.
679 LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
680 szHeap, cbStream, cbSize));
681
682 // Give the size of the pool to the caller's total.
683 *pcbSaveSize = cbSize;
684
685ErrExit:
686 return hr;
687}
688
689//*****************************************************************************
690// Get the save size of the metadata tables. Also adds the tables stream to
691// the list of streams to be saved.
692//*****************************************************************************
693__checkReturn
694HRESULT CLiteWeightStgdbRW::GetTablesSaveSize(
695 CorSaveSize fSave,
696 UINT32 *pcbSaveSize,
697 MetaDataReorderingOptions reorderingOptions,
698 CorProfileData *pProfileData) // Add pool data to this value.
699{
700 UINT32 cbSize = 0; // Size of pool data.
701 UINT32 cbHotSize = 0; // Size of pool data.
702 UINT32 cbStream; // Size of just the stream.
703 DWORD bCompressed; // Will the stream be compressed data?
704 LPCWSTR szName; // What will the name of the pool be?
705 HRESULT hr;
706
707 *pcbSaveSize = 0;
708
709 if( !(reorderingOptions & ReArrangeStringPool) )
710 {
711 if (pProfileData != NULL)
712 {
713 // Ask the metadata to size its hot data.
714 IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbHotSize, &bCompressed, reorderingOptions, pProfileData));
715 cbStream = cbHotSize;
716 m_bSaveCompressed = bCompressed;
717
718 if (cbHotSize != 0)
719 {
720 szName = HOT_MODEL_STREAM;
721
722 // Add this item to the save list.
723 IfFailGo(AddStreamToList(cbHotSize, szName));
724
725 // Ask the storage system to add stream fixed overhead.
726 IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbHotSize, &cbHotSize));
727
728 // Log the size info.
729 LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
730 szName, cbStream, cbHotSize));
731 }
732 }
733 }
734 // Ask the metadata to size its data.
735 IfFailGo(m_MiniMd.GetSaveSize(fSave, &cbSize, &bCompressed));
736 cbStream = cbSize;
737 m_bSaveCompressed = bCompressed;
738 szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM;
739
740 // Add this item to the save list.
741 IfFailGo(AddStreamToList(cbSize, szName));
742
743 // Ask the storage system to add stream fixed overhead.
744 IfFailGo(TiggerStorage::GetStreamSaveSize(szName, cbSize, &cbSize));
745
746 // Log the size info.
747 LOG((LF_METADATA, LL_INFO10, "Metadata: GetSaveSize for %ls: %d data, %d total.\n",
748 szName, cbStream, cbSize));
749
750 // Give the size of the pool to the caller's total.
751 *pcbSaveSize = cbHotSize + cbSize;
752
753ErrExit:
754 return hr;
755} // CLiteWeightStgdbRW::GetTablesSaveSize
756
757//*****************************************************************************
758// Add a stream, and its size, to the list of streams to be saved.
759//*****************************************************************************
760__checkReturn
761HRESULT CLiteWeightStgdbRW::AddStreamToList(
762 UINT32 cbSize,
763 LPCWSTR szName)
764{
765 HRESULT hr = S_OK;
766 PSTORAGESTREAM pItem; // New item to allocate & fill.
767
768 // Add a new item to the end of the list.
769 IfNullGo(pItem = m_pStreamList->Append());
770
771 // Fill out the data.
772 pItem->SetOffset(0);
773 pItem->SetSize((ULONG)cbSize);
774 pItem->SetName(szName);
775
776ErrExit:
777 return hr;
778}
779
780//*****************************************************************************
781// Save the data to a stream. A TiggerStorage sub-allocates streams within
782// the stream.
783//*****************************************************************************
784__checkReturn
785HRESULT CLiteWeightStgdbRW::SaveToStream(
786 IStream *pIStream,
787 MetaDataReorderingOptions reorderingOptions,
788 CorProfileData *pProfileData)
789{
790 HRESULT hr = S_OK; // A result.
791 StgIO *pStgIO = 0;
792 TiggerStorage *pStorage = 0;
793
794 // Allocate a storage subsystem and backing store.
795 IfNullGo(pStgIO = new (nothrow) StgIO);
796 IfNullGo(pStorage = new (nothrow) TiggerStorage);
797
798 // Open around this stream for write.
799 IfFailGo(pStgIO->Open(W(""),
800 DBPROP_TMODEF_DFTWRITEMASK,
801 0, 0, // pbData, cbData
802 pIStream,
803 0)); // LPSecurityAttributes
804 OptionValue ov;
805 IfFailGo(m_MiniMd.GetOption(&ov));
806 IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion));
807
808 // Save worker will do tables, pools.
809 IfFailGo(SaveToStorage(pStorage, reorderingOptions, pProfileData));
810
811ErrExit:
812 if (pStgIO != NULL)
813 pStgIO->Release();
814 if (pStorage != NULL)
815 delete pStorage;
816 return hr;
817} // CLiteWeightStgdbRW::SaveToStream
818
819//*****************************************************************************
820//*****************************************************************************
821__checkReturn
822HRESULT CLiteWeightStgdbRW::SaveToStorage(
823 TiggerStorage *pStorage,
824 MetaDataReorderingOptions reorderingOptions,
825 CorProfileData *pProfileData)
826{
827 HRESULT hr; // A result.
828 LPCWSTR szName; // Name of the tables stream.
829 IStream *pIStreamTbl = 0;
830 UINT32 cb;
831 UINT32 cbSaveSize = m_cbSaveSize;
832
833 // Must call GetSaveSize to cache the streams up front.
834 // Don't trust cached values in the delta case... if there was a previous call to get
835 // a non-delta size, it will be incorrect.
836 if ((m_cbSaveSize == 0) || IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode))
837 {
838 IfFailGo(GetSaveSize(cssAccurate, &cbSaveSize));
839 }
840
841 // Save the header of the data file.
842 IfFailGo(pStorage->WriteHeader(m_pStreamList, 0, NULL));
843
844 // If this is a minimal delta, write a stream marker
845 if (IsENCDelta(m_MiniMd.m_OptionValue.m_UpdateMode))
846 {
847 IfFailGo(pStorage->CreateStream(MINIMAL_MD_STREAM,
848 STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
849 0, 0, &pIStreamTbl));
850 pIStreamTbl->Release();
851 pIStreamTbl = 0;
852 }
853
854 if (pProfileData != NULL)
855 {
856 DWORD bCompressed;
857 UINT32 cbHotSize;
858 // Will the stream be compressed data?
859
860 // Only create this additional stream if it will be non-empty
861 IfFailGo(m_MiniMd.GetSaveSize(cssAccurate, &cbHotSize, &bCompressed, reorderingOptions, pProfileData));
862
863 if (cbHotSize > 0)
864 {
865 // Create a stream and save the hot tables.
866 szName = HOT_MODEL_STREAM;
867 IfFailGo(pStorage->CreateStream(szName,
868 STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
869 0, 0, &pIStreamTbl));
870 IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, reorderingOptions, pProfileData));
871 pIStreamTbl->Release();
872 pIStreamTbl = 0;
873 }
874 }
875
876 if (reorderingOptions & ReArrangeStringPool)
877 {
878 // Save the string pool before the tables when we do not have the string pool cache
879 IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings));
880 }
881
882 // Create a stream and save the tables.
883 szName = m_bSaveCompressed ? COMPRESSED_MODEL_STREAM : ENC_MODEL_STREAM;
884 IfFailGo(pStorage->CreateStream(szName,
885 STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
886 0, 0, &pIStreamTbl));
887 IfFailGo(m_MiniMd.SaveTablesToStream(pIStreamTbl, NoReordering, NULL));
888 pIStreamTbl->Release();
889 pIStreamTbl = 0;
890
891 // Save the pools.
892 if (!(reorderingOptions & ReArrangeStringPool))
893 {
894 // string pool must be saved after the tables when we have the string pool cache
895 IfFailGo(SavePool(STRING_POOL_STREAM, pStorage, MDPoolStrings));
896 }
897 IfFailGo(SavePool(US_BLOB_POOL_STREAM, pStorage, MDPoolUSBlobs));
898 IfFailGo(SavePool(GUID_POOL_STREAM, pStorage, MDPoolGuids));
899 IfFailGo(SavePool(BLOB_POOL_STREAM, pStorage, MDPoolBlobs));
900
901 // Write the header to disk.
902 OptionValue ov;
903 IfFailGo(m_MiniMd.GetOption(&ov));
904
905 IfFailGo(pStorage->WriteFinished(m_pStreamList, (ULONG *)&cb, IsENCDelta(ov.m_UpdateMode)));
906
907 _ASSERTE(cbSaveSize == cb);
908
909 // Let the Storage release some memory.
910 pStorage->ResetBackingStore();
911
912 IfFailGo(m_MiniMd.SaveDone());
913
914ErrExit:
915 if (pIStreamTbl != NULL)
916 pIStreamTbl->Release();
917 delete m_pStreamList;
918 m_pStreamList = 0;
919 m_cbSaveSize = 0;
920 return hr;
921} // CLiteWeightStgdbRW::SaveToStorage
922
923//*****************************************************************************
924// Save a pool of data out to a stream.
925//*****************************************************************************
926__checkReturn
927HRESULT CLiteWeightStgdbRW::SavePool( // Return code.
928 LPCWSTR szName, // Name of stream on disk.
929 TiggerStorage *pStorage, // The storage to put data in.
930 int iPool) // The pool to save.
931{
932 IStream *pIStream=0; // For writing.
933 HRESULT hr;
934
935 // If there is no data, then don't bother.
936 if (m_MiniMd.IsPoolEmpty(iPool))
937 return (S_OK);
938
939 // Create the new stream to hold this table and save it.
940 IfFailGo(pStorage->CreateStream(szName,
941 STGM_DIRECT | STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
942 0, 0, &pIStream));
943 IfFailGo(m_MiniMd.SavePoolToStream(iPool, pIStream));
944
945ErrExit:
946 if (pIStream)
947 pIStream->Release();
948 return hr;
949} // CLiteWeightStgdbRW::SavePool
950
951
952//*****************************************************************************
953// Save the metadata to a file.
954//*****************************************************************************
955__checkReturn
956HRESULT CLiteWeightStgdbRW::Save(
957 LPCWSTR szDatabase, // Name of file to which to save.
958 DWORD dwSaveFlags) // Flags for the save.
959{
960 TiggerStorage * pStorage = NULL; // IStorage object.
961 StgIO * pStgIO = NULL; // Backing storage.
962 HRESULT hr = S_OK;
963
964 if (m_wszFileName == NULL)
965 {
966 if (szDatabase == NULL)
967 {
968 // Make sure that a NULL is not passed in the first time around.
969 _ASSERTE(!"Not allowed to pass a NULL for filename on the first call to Save.");
970 return E_INVALIDARG;
971 }
972 else
973 {
974 // Save the file name.
975 IfFailGo(SetFileName(szDatabase));
976 }
977 }
978 else if ((szDatabase != NULL) && (SString::_wcsicmp(szDatabase, m_wszFileName) != 0))
979 {
980 // Save the file name.
981 IfFailGo(SetFileName(szDatabase));
982 }
983
984 // Sanity check the name.
985 if (!IsValidFileNameLength(m_wszFileName))
986 {
987 IfFailGo(E_INVALIDARG);
988 }
989
990 m_eFileType = FILETYPE_CLB;
991
992 // Allocate a new storage object.
993 IfNullGo(pStgIO = new (nothrow) StgIO);
994
995 // Create the output file.
996 IfFailGo(pStgIO->Open(m_wszFileName,
997 DBPROP_TMODEF_DFTWRITEMASK,
998 0,0, // pbData, cbData
999 0, // IStream*
1000 0)); // LPSecurityAttributes
1001
1002 // Allocate an IStorage object to use.
1003 IfNullGo(pStorage = new (nothrow) TiggerStorage);
1004
1005 // Init the storage object on the i/o system.
1006 OptionValue ov;
1007 IfFailGo(m_MiniMd.GetOption(&ov));
1008 IfFailGo(pStorage->Init(pStgIO, ov.m_RuntimeVersion));
1009
1010 // Save the data.
1011 IfFailGo(SaveToStorage(pStorage));
1012
1013ErrExit:
1014 if (pStgIO != NULL)
1015 pStgIO->Release();
1016 if (pStorage != NULL)
1017 delete pStorage;
1018 return hr;
1019} // CLiteWeightStgdbRW::Save
1020
1021//*****************************************************************************
1022// Pull the PEKind and Machine out of PE headers -- if we have PE headers.
1023//*****************************************************************************
1024__checkReturn
1025HRESULT CLiteWeightStgdbRW::GetPEKind( // S_OK or error.
1026 MAPPINGTYPE mtMapping, // The type of mapping the image has
1027 DWORD *pdwPEKind, // [OUT] The kind of PE (0 - not a PE)
1028 DWORD *pdwMachine) // [OUT] Machine as defined in NT header
1029{
1030 HRESULT hr = NOERROR;
1031 DWORD dwPEKind=0; // Working copy of pe kind.
1032 DWORD dwMachine=0; // Working copy of machine.
1033
1034#ifndef DACCESS_COMPILE
1035 // Do we already have cached information?
1036 if (m_dwPEKind != (DWORD)(-1))
1037 {
1038 dwPEKind = m_dwPEKind;
1039 dwMachine = m_dwMachine;
1040 }
1041 else if (m_pImage)
1042 {
1043 PEDecoder pe;
1044
1045 // We need to use different PEDecoder initialization based on the type of data we give it.
1046 // We use the one with a 'bool' as the second argument when dealing with a mapped file,
1047 // and we use the one that takes a COUNT_T as the second argument when dealing with a
1048 // flat file.
1049
1050 if (mtMapping == MTYPE_IMAGE)
1051 {
1052 if (FAILED(pe.Init(m_pImage, false)) ||
1053 !pe.CheckNTHeaders())
1054 {
1055 IfFailRet(COR_E_BADIMAGEFORMAT);
1056 }
1057 }
1058 else
1059 {
1060 pe.Init(m_pImage, (COUNT_T)(m_dwImageSize));
1061 }
1062
1063 if (pe.HasContents() && pe.HasNTHeaders())
1064 {
1065 pe.GetPEKindAndMachine(&dwPEKind, &dwMachine);
1066
1067
1068 // Cache entries.
1069 m_dwPEKind = dwPEKind;
1070 m_dwMachine = dwMachine;
1071 }
1072 else // if (pe.HasContents()...
1073 {
1074 hr = COR_E_BADIMAGEFORMAT;
1075 }
1076 }
1077 else
1078 {
1079 hr = S_FALSE;
1080 }
1081#endif
1082 if (pdwPEKind)
1083 *pdwPEKind = dwPEKind;
1084 if (pdwMachine)
1085 *pdwMachine = dwMachine;
1086
1087 return hr;
1088} // CLiteWeightStgdbRW::GetPEKind
1089
1090//*****************************************************************************
1091// Low level access to the data. Intended for metainfo, and such.
1092//*****************************************************************************
1093__checkReturn
1094HRESULT CLiteWeightStgdbRW::GetRawData(
1095 const void **ppvMd, // [OUT] put pointer to MD section here (aka, 'BSJB').
1096 ULONG *pcbMd) // [OUT] put size of the stream here.
1097{
1098#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
1099 if (m_pStgIO == NULL)
1100 return COR_E_NOTSUPPORTED;
1101#endif
1102
1103 *ppvMd = (const void*) m_pStgIO->m_pData;
1104 *pcbMd = m_pStgIO->m_cbData;
1105 return S_OK;
1106} // CLiteWeightStgdbRW::GetRawData
1107
1108//*****************************************************************************
1109// Get info about the MD stream.
1110// Low level access to stream data. Intended for metainfo, and such.
1111//*****************************************************************************
1112__checkReturn
1113STDMETHODIMP
1114CLiteWeightStgdbRW::GetRawStreamInfo(
1115 ULONG ix, // [IN] Stream ordinal desired.
1116 const char **ppchName, // [OUT] put pointer to stream name here.
1117 const void **ppv, // [OUT] put pointer to MD stream here.
1118 ULONG *pcb) // [OUT] put size of the stream here.
1119{
1120 HRESULT hr = NOERROR;
1121 STORAGEHEADER sHdr; // Header for the storage.
1122 PSTORAGESTREAM pStream; // Pointer to each stream.
1123 ULONG i; // Loop control.
1124 void *pData;
1125 ULONG cbData;
1126
1127#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
1128 if (m_pStgIO == NULL)
1129 IfFailGo(COR_E_NOTSUPPORTED);
1130#endif
1131
1132 pData = m_pStgIO->m_pData;
1133 cbData = m_pStgIO->m_cbData;
1134
1135 // Validate the signature of the format, or it isn't ours.
1136 IfFailGo(MDFormat::VerifySignature((PSTORAGESIGNATURE) pData, cbData));
1137
1138 // Get back the first stream.
1139 pStream = MDFormat::GetFirstStream(&sHdr, pData);
1140 if (pStream == NULL)
1141 {
1142 Debug_ReportError("Invalid MetaData storage signature - cannot get the first stream header.");
1143 IfFailGo(CLDB_E_FILE_CORRUPT);
1144 }
1145
1146 // Check that the requested stream exists.
1147 if (ix >= sHdr.GetiStreams())
1148 return S_FALSE;
1149
1150 // Skip to the desired stream.
1151 for (i = 0; i < ix; i++)
1152 {
1153 PSTORAGESTREAM pNext = pStream->NextStream();
1154
1155 // Check that stream header is within the buffer.
1156 if (((LPBYTE)pStream >= ((LPBYTE)pData + cbData)) ||
1157 ((LPBYTE)pNext > ((LPBYTE)pData + cbData)))
1158 {
1159 Debug_ReportError("Stream header is not within MetaData block.");
1160 hr = CLDB_E_FILE_CORRUPT;
1161 goto ErrExit;
1162 }
1163
1164 // Check that the stream data starts and fits within the buffer.
1165 // need two checks on size because of wraparound.
1166 if ((pStream->GetOffset() > cbData) ||
1167 (pStream->GetSize() > cbData) ||
1168 ((pStream->GetSize() + pStream->GetOffset()) > cbData))
1169 {
1170 Debug_ReportError("Stream data are not within MetaData block.");
1171 hr = CLDB_E_FILE_CORRUPT;
1172 goto ErrExit;
1173 }
1174
1175 // Pick off the next stream if there is one.
1176 pStream = pNext;
1177 }
1178
1179 if (pStream != NULL)
1180 {
1181 *ppv = (const void *)((const BYTE *)pData + pStream->GetOffset());
1182 *pcb = pStream->GetSize();
1183 *ppchName = pStream->GetName();
1184 }
1185 else
1186 {
1187 *ppv = NULL;
1188 *pcb = 0;
1189 *ppchName = NULL;
1190
1191 // Invalid input to the method
1192 hr = CLDB_E_FILE_CORRUPT;
1193 }
1194
1195ErrExit:
1196 return hr;
1197} // CLiteWeightStgdbRW::GetRawStreamInfo
1198
1199//=======================================================================================
1200//
1201// Set file name of this database (makes copy of the file name).
1202//
1203// Return value: S_OK or E_OUTOFMEMORY
1204//
1205__checkReturn
1206HRESULT
1207CLiteWeightStgdbRW::SetFileName(
1208 const WCHAR * wszFileName)
1209{
1210 HRESULT hr = S_OK;
1211
1212 if (m_wszFileName != NULL)
1213 {
1214 delete [] m_wszFileName;
1215 m_wszFileName = NULL;
1216 }
1217
1218 if ((wszFileName == NULL) || (*wszFileName == 0))
1219 { // The new file name is empty
1220 _ASSERTE(m_wszFileName == NULL);
1221
1222 // No need to allocate anything, NULL means empty name
1223 hr = S_OK;
1224 goto ErrExit;
1225 }
1226
1227 // Size of the file name incl. null terminator
1228 size_t cchFileName;
1229 cchFileName = wcslen(wszFileName) + 1;
1230
1231 // Allocate and copy the file name
1232 m_wszFileName = new (nothrow) WCHAR[cchFileName];
1233 IfNullGo(m_wszFileName);
1234 wcscpy_s(m_wszFileName, cchFileName, wszFileName);
1235
1236ErrExit:
1237 return hr;
1238} // CLiteWeightStgdbRW::SetFileName
1239
1240//=======================================================================================
1241//
1242// Returns TRUE if wszFileName has valid path length (MAX_PATH or 32767 if prefixed with \\?\).
1243//
1244//static
1245BOOL
1246CLiteWeightStgdbRW::IsValidFileNameLength(
1247 const WCHAR * wszFileName)
1248{
1249 return TRUE;
1250} // CLiteWeightStgdbRW::IsValidFileNameLength
1251