| 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 | //***************************************************************************** |
| 37 | int _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 | |
| 66 | BOOL _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 | |
| 80 | HRESULT _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 | //***************************************************************************** |
| 112 | CLiteWeightStgdbRW::~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 |
| 136 | HRESULT 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 | |
| 159 | ErrExit: |
| 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 |
| 178 | HRESULT |
| 179 | CLiteWeightStgdbRW::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 | |
| 285 | ErrExit: |
| 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 |
| 297 | HRESULT 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 | |
| 462 | ErrExit: |
| 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 |
| 478 | HRESULT 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 | |
| 496 | ErrExit: |
| 497 | return hr; |
| 498 | } |
| 499 | #endif |
| 500 | |
| 501 | // Read/Write versions. |
| 502 | //***************************************************************************** |
| 503 | // Init the Stgdb and its subcomponents. |
| 504 | //***************************************************************************** |
| 505 | __checkReturn |
| 506 | HRESULT 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 |
| 519 | HRESULT 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 | |
| 642 | ErrExit: |
| 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 |
| 651 | HRESULT |
| 652 | CLiteWeightStgdbRW::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 | |
| 685 | ErrExit: |
| 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 |
| 694 | HRESULT 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 | |
| 753 | ErrExit: |
| 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 |
| 761 | HRESULT 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 | |
| 776 | ErrExit: |
| 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 |
| 785 | HRESULT 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 | |
| 811 | ErrExit: |
| 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 |
| 822 | HRESULT 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 | |
| 914 | ErrExit: |
| 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 |
| 927 | HRESULT 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 | |
| 945 | ErrExit: |
| 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 |
| 956 | HRESULT 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 | |
| 1013 | ErrExit: |
| 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 |
| 1025 | HRESULT 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 |
| 1094 | HRESULT 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 |
| 1113 | STDMETHODIMP |
| 1114 | CLiteWeightStgdbRW::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 | |
| 1195 | ErrExit: |
| 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 |
| 1206 | HRESULT |
| 1207 | CLiteWeightStgdbRW::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 | |
| 1236 | ErrExit: |
| 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 |
| 1245 | BOOL |
| 1246 | CLiteWeightStgdbRW::IsValidFileNameLength( |
| 1247 | const WCHAR * wszFileName) |
| 1248 | { |
| 1249 | return TRUE; |
| 1250 | } // CLiteWeightStgdbRW::IsValidFileNameLength |
| 1251 | |