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// StgIO.h
6//
7
8//
9// This module handles disk/memory i/o for a generic set of storage solutions,
10// including:
11// * File system handle (HFILE)
12// * IStream
13// * User supplied memory buffer (non-movable)
14//
15// The Read, Write, Seek, ... functions are all directed to the corresponding
16// method for each type of file, allowing the consumer to use one set of api's.
17//
18// File system data can be paged fully into memory in two scenarios:
19// read: Normal memory mapped file is created to manage paging.
20// write: A custom paging system provides storage for pages as required. This
21// data is invalidated when you call Rewrite on the file.
22//
23// Transactions and backups are handled in the existing file case only. The
24// Rewrite function can make a backup of the current contents, and the Restore
25// function can be used to recover the data into the current scope. The backup
26// file is flushed to disk (which is slower but safer) after the copy. The
27// Restore also flushed the recovered changes to disk. Worst case scenario you
28// get a crash after calling Rewrite but before Restore, in which case you will
29// have a foo.clb.txn file in the same directory as the source file, foo.clb in
30// this example.
31//<REVISIT_TODO>
32// @FUTURE: issues,
33// 1. For reading a .clb in an image, it would be great to memory map
34// only the portion of the file with the .clb in it.
35//</REVISIT_TODO>
36//*****************************************************************************
37#include "stdafx.h" // Standard headers.
38#include "stgio.h" // Our definitions.
39#include "corerror.h"
40#include "posterror.h"
41#include "pedecoder.h"
42#include "pedecoder.inl"
43
44//********** Types. ***********************************************************
45#define SMALL_ALLOC_MAP_SIZE (64 * 1024) // 64 kb is the minimum size of virtual
46 // memory you can allocate, so anything
47 // less is a waste of VM resources.
48
49
50#define MIN_WRITE_CACHE_BYTES (16 * 1024) // 16 kb for a write back cache
51
52
53//********** Locals. **********************************************************
54HRESULT MapFileError(DWORD error);
55static void *AllocateMemory(int iSize);
56static void FreeMemory(void *pbData);
57inline HRESULT MapFileError(DWORD error)
58{
59 return (PostError(HRESULT_FROM_WIN32(error)));
60}
61
62// Static to class.
63int StgIO::m_iPageSize=0; // Size of an OS page.
64int StgIO::m_iCacheSize=0; // Size for the write cache.
65
66
67
68//********** Code. ************************************************************
69StgIO::StgIO(
70 bool bAutoMap) : // Memory map for read on open?
71 m_bAutoMap(bAutoMap)
72{
73 CtorInit();
74
75 // If the system page size has not been queried, do so now.
76 if (m_iPageSize == 0)
77 {
78 SYSTEM_INFO sInfo; // Some O/S information.
79
80 // Query the system page size.
81 GetSystemInfo(&sInfo);
82 m_iPageSize = sInfo.dwPageSize;
83 m_iCacheSize = ((MIN_WRITE_CACHE_BYTES - 1) & ~(m_iPageSize - 1)) + m_iPageSize;
84 }
85}
86
87
88void StgIO::CtorInit()
89{
90 m_bWriteThrough = false;
91 m_bRewrite = false;
92 m_bFreeMem = false;
93 m_pIStream = 0;
94 m_hFile = INVALID_HANDLE_VALUE;
95 m_hModule = NULL;
96 m_hMapping = 0;
97 m_pBaseData = 0;
98 m_pData = 0;
99 m_cbData = 0;
100 m_fFlags = 0;
101 m_iType = STGIO_NODATA;
102 m_cbOffset = 0;
103 m_rgBuff = 0;
104 m_cbBuff = 0;
105 m_rgPageMap = 0;
106 m_FileType = FILETYPE_UNKNOWN;
107 m_cRef = 1;
108 m_mtMappedType = MTYPE_NOMAPPING;
109}
110
111
112
113StgIO::~StgIO()
114{
115 if (m_rgBuff)
116 {
117 FreeMemory(m_rgBuff);
118 m_rgBuff = 0;
119 }
120
121 Close();
122}
123
124
125//*****************************************************************************
126// Open the base file on top of: (a) file, (b) memory buffer, or (c) stream.
127// If create flag is specified, then this will create a new file with the
128// name supplied. No data is read from an opened file. You must call
129// MapFileToMem before doing direct pointer access to the contents.
130//*****************************************************************************
131HRESULT StgIO::Open( // Return code.
132 LPCWSTR szName, // Name of the storage.
133 int fFlags, // How to open the file.
134 const void *pbBuff, // Optional buffer for memory.
135 ULONG cbBuff, // Size of buffer.
136 IStream *pIStream, // Stream for input.
137 LPSECURITY_ATTRIBUTES pAttributes) // Security token.
138{
139 HRESULT hr;
140
141 // If we were given the storage memory to begin with, then use it.
142 if (pbBuff && cbBuff)
143 {
144 _ASSERTE((fFlags & DBPROP_TMODEF_WRITE) == 0);
145
146 // Save the memory address and size only. No handles.
147 m_pData = (void *) pbBuff;
148 m_cbData = cbBuff;
149
150 // All access to data will be by memory provided.
151 if ((fFlags & DBPROP_TMODEF_SHAREDMEM) == DBPROP_TMODEF_SHAREDMEM)
152 {
153 // We're taking ownership of this memory
154 m_pBaseData = m_pData;
155 m_iType = STGIO_SHAREDMEM;
156 }
157 else
158 {
159 m_iType = STGIO_MEM;
160 }
161 goto ErrExit;
162 }
163 // Check for data backed by a stream pointer.
164 else if (pIStream)
165 {
166 // If this is for the non-create case, get the size of existing data.
167 if ((fFlags & DBPROP_TMODEF_CREATE) == 0)
168 {
169 LARGE_INTEGER iMove = { { 0, 0 } };
170 ULARGE_INTEGER iSize;
171
172 // Need the size of the data so we can map it into memory.
173 if (FAILED(hr = pIStream->Seek(iMove, STREAM_SEEK_END, &iSize)))
174 return (hr);
175 m_cbData = iSize.u.LowPart;
176 }
177 // Else there is nothing.
178 else
179 m_cbData = 0;
180
181 // Save an addref'd copy of the stream.
182 m_pIStream = pIStream;
183 m_pIStream->AddRef();
184
185 // All access to data will be by memory provided.
186 m_iType = STGIO_STREAM;
187 goto ErrExit;
188 }
189
190 // If not on memory, we need a file to do a create/open.
191 if (!szName || !*szName)
192 {
193 return (PostError(E_INVALIDARG));
194 }
195 // Check for create of a new file.
196 else if (fFlags & DBPROP_TMODEF_CREATE)
197 {
198 //<REVISIT_TODO>@future: This could chose to open the file in write through
199 // mode, which would provide better Duribility (from ACID props),
200 // but would be much slower.</REVISIT_TODO>
201
202 // Create the new file, overwriting only if caller allows it.
203 if ((m_hFile = WszCreateFile(szName, GENERIC_READ | GENERIC_WRITE, 0, 0,
204 (fFlags & DBPROP_TMODEF_FAILIFTHERE) ? CREATE_NEW : CREATE_ALWAYS,
205 0, 0)) == INVALID_HANDLE_VALUE)
206 {
207 return (MapFileError(GetLastError()));
208 }
209
210 // Data will come from the file.
211 m_iType = STGIO_HFILE;
212 }
213 // For open in read mode, need to open the file on disk. If opening a shared
214 // memory view, it has to be opened already, so no file open.
215 else if ((fFlags & DBPROP_TMODEF_WRITE) == 0)
216 {
217 // We have not opened the file nor loaded it as module
218 _ASSERTE(m_hFile == INVALID_HANDLE_VALUE);
219 _ASSERTE(m_hModule == NULL);
220
221 // Open the file for read. Sharing is determined by caller, it can
222 // allow other readers or be exclusive.
223 DWORD dwFileSharingFlags = FILE_SHARE_DELETE;
224 if (!(fFlags & DBPROP_TMODEF_EXCLUSIVE))
225 {
226 dwFileSharingFlags |= FILE_SHARE_READ;
227
228#if !defined(DACCESS_COMPILE) && !defined(FEATURE_PAL)
229 // PEDecoder is not defined in DAC
230
231 // We prefer to use LoadLibrary if we can because it will share already loaded images (used for execution)
232 // which saves virtual memory. We only do this if our caller has indicated that this PE file is trusted
233 // and thus it is OK to do LoadLibrary (note that we still only load it as a resource, which mitigates
234 // most of the security risk anyway).
235 if ((fFlags & DBPROP_TMODEF_TRYLOADLIBRARY) != 0)
236 {
237 m_hModule = WszLoadLibraryEx(szName, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
238 if (m_hModule != NULL)
239 {
240 m_iType = STGIO_HMODULE;
241
242 m_mtMappedType = MTYPE_IMAGE;
243
244 // LoadLibraryEx returns 2 lowest bits indicating how the module was loaded
245 m_pBaseData = m_pData = (void *)(((INT_PTR)m_hModule) & ~(INT_PTR)0x3);
246
247 PEDecoder peDecoder;
248 if (SUCCEEDED(peDecoder.Init(
249 m_pBaseData,
250 false)) && // relocated
251 peDecoder.CheckNTHeaders())
252 {
253 m_cbData = peDecoder.GetNTHeaders32()->OptionalHeader.SizeOfImage;
254 }
255 else
256 {
257 // PEDecoder failed on loaded library, let's backout all our changes to this object
258 // and fall back to file mapping
259 m_iType = STGIO_NODATA;
260 m_mtMappedType = MTYPE_NOMAPPING;
261 m_pBaseData = m_pData = NULL;
262
263 FreeLibrary(m_hModule);
264 m_hModule = NULL;
265 }
266 }
267 }
268#endif //!DACCESS_COMPILE && !FEATURE_PAL
269 }
270
271 if (m_hModule == NULL)
272 { // We didn't get the loaded module (we either didn't want to or it failed)
273 HandleHolder hFile(WszCreateFile(szName,
274 GENERIC_READ,
275 dwFileSharingFlags,
276 0,
277 OPEN_EXISTING,
278 0,
279 0));
280
281 if (hFile == INVALID_HANDLE_VALUE)
282 return (MapFileError(GetLastError()));
283
284 // Get size of file.
285 m_cbData = ::SetFilePointer(hFile, 0, 0, FILE_END);
286
287 // Can't read anything from an empty file.
288 if (m_cbData == 0)
289 return (PostError(CLDB_E_NO_DATA));
290
291 // Data will come from the file.
292 m_hFile = hFile.Extract();
293
294 m_iType = STGIO_HFILE;
295 }
296 }
297
298ErrExit:
299
300 // If we will ever write, then we need the buffer cache.
301 if (fFlags & DBPROP_TMODEF_WRITE)
302 {
303 // Allocate a cache buffer for writing.
304 if ((m_rgBuff = (BYTE *) AllocateMemory(m_iCacheSize)) == NULL)
305 {
306 Close();
307 return PostError(OutOfMemory());
308 }
309 m_cbBuff = 0;
310 }
311
312 // Save flags for later.
313 m_fFlags = fFlags;
314 if ((szName != NULL) && (*szName != 0))
315 {
316 WCHAR rcExt[_MAX_PATH];
317 SplitPath(szName, NULL, 0, NULL, 0, NULL, 0, rcExt, _MAX_PATH);
318 if (SString::_wcsicmp(rcExt, W(".obj")) == 0)
319 {
320 m_FileType = FILETYPE_NTOBJ;
321 }
322 else if (SString::_wcsicmp(rcExt, W(".tlb")) == 0)
323 {
324 m_FileType = FILETYPE_TLB;
325 }
326 }
327
328 // For auto map case, map the view of the file as part of open.
329 if (m_bAutoMap &&
330 (m_iType == STGIO_HFILE || m_iType == STGIO_STREAM) &&
331 !(fFlags & DBPROP_TMODEF_CREATE))
332 {
333 void * ptr;
334 ULONG cb;
335
336 if (FAILED(hr = MapFileToMem(ptr, &cb, pAttributes)))
337 {
338 Close();
339 return hr;
340 }
341 }
342 return S_OK;
343} // StgIO::Open
344
345
346//*****************************************************************************
347// Shut down the file handles and allocated objects.
348//*****************************************************************************
349void StgIO::Close()
350{
351 switch (m_iType)
352 {
353 // Free any allocated memory.
354 case STGIO_SHAREDMEM:
355 if (m_pBaseData != NULL)
356 {
357 CoTaskMemFree(m_pBaseData);
358 m_pBaseData = NULL;
359 break;
360 }
361
362 case STGIO_MEM:
363 case STGIO_HFILEMEM:
364 if (m_bFreeMem && m_pBaseData)
365 {
366 FreeMemory(m_pBaseData);
367 m_pBaseData = m_pData = 0;
368 }
369 // Intentional fall through to file case, if we kept handle open.
370
371 case STGIO_HFILE:
372 {
373 // Free the file handle.
374 if (m_hFile != INVALID_HANDLE_VALUE)
375 CloseHandle(m_hFile);
376
377 // If we allocated space for in memory paging, then free it.
378 }
379 break;
380
381 case STGIO_HMODULE:
382 {
383 if (m_hModule != NULL)
384 FreeLibrary(m_hModule);
385 m_hModule = NULL;
386 break;
387 }
388
389 // Free the stream pointer.
390 case STGIO_STREAM:
391 {
392 if (m_pIStream != NULL)
393 m_pIStream->Release();
394 }
395 break;
396
397 // Weird to shut down what you didn't open, isn't it? Allow for
398 // error case where dtor shuts down as an afterthought.
399 case STGIO_NODATA:
400 default:
401 return;
402 }
403
404 // Free any page map and base data.
405 FreePageMap();
406
407 // Reset state values so we don't get confused.
408 CtorInit();
409}
410
411//*****************************************************************************
412// Called to read the data into allocated memory and release the backing store.
413// Only available on read-only data.
414//*****************************************************************************
415HRESULT
416StgIO::LoadFileToMemory()
417{
418 HRESULT hr;
419 void *pData; // Allocated buffer for file.
420 ULONG cbData; // Size of the data.
421 ULONG cbRead = 0; // Data actually read.
422
423 // Make sure it is a read-only file.
424 if (m_fFlags & DBPROP_TMODEF_WRITE)
425 return E_INVALIDARG;
426
427 // Try to allocate the buffer.
428 cbData = m_cbData;
429 pData = AllocateMemory(cbData);
430 IfNullGo(pData);
431
432 // Try to read the file into the buffer.
433 IfFailGo(Read(pData, cbData, &cbRead));
434 if (cbData != cbRead)
435 {
436 _ASSERTE_MSG(FALSE, "Read didn't succeed.");
437 IfFailGo(CLDB_E_FILE_CORRUPT);
438 }
439
440 // Done with the old data.
441 Close();
442
443 // Open with new data.
444 hr = Open(NULL /* szName */, STGIO_READ, pData, cbData, NULL /* IStream* */, NULL /* lpSecurityAttributes */);
445 _ASSERTE(SUCCEEDED(hr)); // should not be a failure code path with open on buffer.
446
447 // Mark the new memory so that it will be freed later.
448 m_pBaseData = m_pData;
449 m_bFreeMem = true;
450
451ErrExit:
452 if (FAILED(hr) && pData)
453 FreeMemory(pData);
454
455 return hr;
456} // StgIO::LoadFileToMemory
457
458
459//*****************************************************************************
460// Read data from the storage source. This will handle all types of backing
461// storage from mmf, streams, and file handles. No read ahead or MRU
462// caching is done.
463//*****************************************************************************
464HRESULT StgIO::Read( // Return code.
465 void *pbBuff, // Write buffer here.
466 ULONG cbBuff, // How much to read.
467 ULONG *pcbRead) // How much read.
468{
469 ULONG cbCopy; // For boundary checks.
470 void *pbData; // Data buffer for mem read.
471 HRESULT hr = S_OK;
472
473 // Validate arguments, don't call if you don't need to.
474 _ASSERTE(pbBuff != 0);
475 _ASSERTE(cbBuff > 0);
476
477 // Get the data based on type.
478 switch (m_iType)
479 {
480 // For data on file, there are two possiblities:
481 // (1) We have an in memory backing store we should use, or
482 // (2) We just need to read from the file.
483 case STGIO_HFILE:
484 case STGIO_HMODULE:
485 {
486 _ASSERTE((m_hFile != INVALID_HANDLE_VALUE) || (m_hModule != NULL));
487
488 // Backing store does its own paging.
489 if (IsBackingStore() || IsMemoryMapped())
490 {
491 // Force the data into memory.
492 if (FAILED(hr = GetPtrForMem(GetCurrentOffset(), cbBuff, pbData)))
493 goto ErrExit;
494
495 // Copy it back for the user and save the size.
496 memcpy(pbBuff, pbData, cbBuff);
497 if (pcbRead)
498 *pcbRead = cbBuff;
499 }
500 // If there is no backing store, this is just a read operation.
501 else
502 {
503 _ASSERTE((m_iType == STGIO_HFILE) && (m_hFile != INVALID_HANDLE_VALUE));
504 _ASSERTE(m_hModule == NULL);
505
506 ULONG cbTemp = 0;
507 if (!pcbRead)
508 pcbRead = &cbTemp;
509 hr = ReadFromDisk(pbBuff, cbBuff, pcbRead);
510 m_cbOffset += *pcbRead;
511 }
512 }
513 break;
514
515 // Data in a stream is always just read.
516 case STGIO_STREAM:
517 {
518 _ASSERTE((IStream *) m_pIStream);
519 if (!pcbRead)
520 pcbRead = &cbCopy;
521 *pcbRead = 0;
522 hr = m_pIStream->Read(pbBuff, cbBuff, pcbRead);
523 if (SUCCEEDED(hr))
524 m_cbOffset += *pcbRead;
525 }
526 break;
527
528 // Simply copy the data from our data.
529 case STGIO_MEM:
530 case STGIO_SHAREDMEM:
531 case STGIO_HFILEMEM:
532 {
533 _ASSERTE(m_pData && m_cbData);
534
535 // Check for read past end of buffer and adjust.
536 if (GetCurrentOffset() + cbBuff > m_cbData)
537 cbCopy = m_cbData - GetCurrentOffset();
538 else
539 cbCopy = cbBuff;
540
541 // Copy the data into the callers buffer.
542 memcpy(pbBuff, (void *) ((DWORD_PTR)m_pData + GetCurrentOffset()), cbCopy);
543 if (pcbRead)
544 *pcbRead = cbCopy;
545
546 // Save a logical offset.
547 m_cbOffset += cbCopy;
548 }
549 break;
550
551 case STGIO_NODATA:
552 default:
553 _ASSERTE(0);
554 break;
555 }
556
557ErrExit:
558 return (hr);
559}
560
561
562//*****************************************************************************
563// Write to disk. This function will cache up to a page of data in a buffer
564// and peridocially flush it on overflow and explicit request. This makes it
565// safe to do lots of small writes without too much performance overhead.
566//*****************************************************************************
567HRESULT StgIO::Write( // true/false.
568 const void *pbBuff, // Data to write.
569 ULONG cbWrite, // How much data to write.
570 ULONG *pcbWritten) // How much did get written.
571{
572 ULONG cbWriteIn=cbWrite; // Track amount written.
573 ULONG cbCopy;
574 HRESULT hr = S_OK;
575
576 _ASSERTE(m_rgBuff != 0);
577 _ASSERTE(cbWrite);
578
579 while (cbWrite)
580 {
581 // In the case where the buffer is already huge, write the whole thing
582 // and avoid the cache.
583 if (m_cbBuff == 0 && cbWrite >= (ULONG) m_iPageSize)
584 {
585 if (SUCCEEDED(hr = WriteToDisk(pbBuff, cbWrite, pcbWritten)))
586 m_cbOffset += cbWrite;
587 break;
588 }
589 // Otherwise cache as much as we can and flush.
590 else
591 {
592 // Determine how much data goes into the cache buffer.
593 cbCopy = m_iPageSize - m_cbBuff;
594 cbCopy = min(cbCopy, cbWrite);
595
596 // Copy the data into the cache and adjust counts.
597 memcpy(&m_rgBuff[m_cbBuff], pbBuff, cbCopy);
598 pbBuff = (void *) ((DWORD_PTR)pbBuff + cbCopy);
599 m_cbBuff += cbCopy;
600 m_cbOffset += cbCopy;
601 cbWrite -= cbCopy;
602
603 // If there is enough data, then flush it to disk and reset count.
604 if (m_cbBuff >= (ULONG) m_iPageSize)
605 {
606 if (FAILED(hr = FlushCache()))
607 break;
608 }
609 }
610 }
611
612 // Return value for caller.
613 if (SUCCEEDED(hr) && pcbWritten)
614 *pcbWritten = cbWriteIn;
615 return (hr);
616}
617
618
619//*****************************************************************************
620// Moves the file pointer to the new location. This handles the different
621// types of storage systems.
622//*****************************************************************************
623HRESULT StgIO::Seek( // New offset.
624 int lVal, // How much to move.
625 ULONG fMoveType) // Direction, use Win32 FILE_xxxx.
626{
627 ULONG cbRtn = 0;
628 HRESULT hr = NOERROR;
629
630 _ASSERTE(fMoveType >= FILE_BEGIN && fMoveType <= FILE_END);
631
632 // Action taken depends on type of storage.
633 switch (m_iType)
634 {
635 case STGIO_HFILE:
636 {
637 // Use the file system's move.
638 _ASSERTE(m_hFile != INVALID_HANDLE_VALUE);
639 cbRtn = ::SetFilePointer(m_hFile, lVal, 0, fMoveType);
640
641 // Save the location redundantly.
642 if (cbRtn != 0xffffffff)
643 {
644 // make sure that m_cbOffset will stay within range
645 if (cbRtn > m_cbData || cbRtn < 0)
646 {
647 IfFailGo(STG_E_INVALIDFUNCTION);
648 }
649 m_cbOffset = cbRtn;
650 }
651 }
652 break;
653
654 case STGIO_STREAM:
655 {
656 LARGE_INTEGER iMove;
657 ULARGE_INTEGER iNewLoc;
658
659 // Need a 64-bit int.
660 iMove.QuadPart = lVal;
661
662 // The move types are named differently, but have same value.
663 if (FAILED(hr = m_pIStream->Seek(iMove, fMoveType, &iNewLoc)))
664 return (hr);
665
666 // make sure that m_cbOffset will stay within range
667 if (iNewLoc.u.LowPart > m_cbData || iNewLoc.u.LowPart < 0)
668 IfFailGo(STG_E_INVALIDFUNCTION);
669
670 // Save off only out location.
671 m_cbOffset = iNewLoc.u.LowPart;
672 }
673 break;
674
675 case STGIO_MEM:
676 case STGIO_SHAREDMEM:
677 case STGIO_HFILEMEM:
678 case STGIO_HMODULE:
679 {
680 // We own the offset, so change our value.
681 switch (fMoveType)
682 {
683 case FILE_BEGIN:
684
685 // make sure that m_cbOffset will stay within range
686 if ((ULONG) lVal > m_cbData || lVal < 0)
687 {
688 IfFailGo(STG_E_INVALIDFUNCTION);
689 }
690 m_cbOffset = lVal;
691 break;
692
693 case FILE_CURRENT:
694
695 // make sure that m_cbOffset will stay within range
696 if (m_cbOffset + lVal > m_cbData)
697 {
698 IfFailGo(STG_E_INVALIDFUNCTION);
699 }
700 m_cbOffset = m_cbOffset + lVal;
701 break;
702
703 case FILE_END:
704 _ASSERTE(lVal < (LONG) m_cbData);
705 // make sure that m_cbOffset will stay within range
706 if (m_cbData + lVal > m_cbData)
707 {
708 IfFailGo(STG_E_INVALIDFUNCTION);
709 }
710 m_cbOffset = m_cbData + lVal;
711 break;
712 }
713
714 cbRtn = m_cbOffset;
715 }
716 break;
717
718 // Weird to seek with no data.
719 case STGIO_NODATA:
720 default:
721 _ASSERTE(0);
722 break;
723 }
724
725ErrExit:
726 return hr;
727}
728
729
730//*****************************************************************************
731// Retrieves the current offset for the storage being used. This value is
732// tracked based on Read, Write, and Seek operations.
733//*****************************************************************************
734ULONG StgIO::GetCurrentOffset() // Current offset.
735{
736 return (m_cbOffset);
737}
738
739
740//*****************************************************************************
741// Map the file contents to a memory mapped file and return a pointer to the
742// data. For read/write with a backing store, map the file using an internal
743// paging system.
744//*****************************************************************************
745HRESULT StgIO::MapFileToMem( // Return code.
746 void *&ptr, // Return pointer to file data.
747 ULONG *pcbSize, // Return size of data.
748 LPSECURITY_ATTRIBUTES pAttributes) // Security token.
749{
750 char rcShared[MAXSHMEM]; // ANSI version of shared name.
751 HRESULT hr = S_OK;
752
753 // Don't penalize for multiple calls. Also, allow calls for mem type so
754 // callers don't need to do so much checking.
755 if (IsBackingStore() ||
756 IsMemoryMapped() ||
757 (m_iType == STGIO_MEM) ||
758 (m_iType == STGIO_SHAREDMEM) ||
759 (m_iType == STGIO_HFILEMEM))
760 {
761 ptr = m_pData;
762 if (pcbSize)
763 *pcbSize = m_cbData;
764 return (S_OK);
765 }
766
767 //#CopySmallFiles
768 // Check the size of the data we want to map. If it is small enough, then
769 // simply allocate a chunk of memory from a finer grained heap. This saves
770 // virtual memory space, page table entries, and should reduce overall working set.
771 // Also, open for read/write needs a full backing store.
772 if ((m_cbData <= SMALL_ALLOC_MAP_SIZE) && (SMALL_ALLOC_MAP_SIZE > 0))
773 {
774 DWORD cbRead = m_cbData;
775 _ASSERTE(m_pData == 0);
776
777 // Just malloc a chunk of data to use.
778 m_pBaseData = m_pData = AllocateMemory(m_cbData);
779 if (!m_pData)
780 {
781 hr = OutOfMemory();
782 goto ErrExit;
783 }
784
785 // Read all of the file contents into this piece of memory.
786 IfFailGo( Seek(0, FILE_BEGIN) );
787 if (FAILED(hr = Read(m_pData, cbRead, &cbRead)))
788 {
789 FreeMemory(m_pData);
790 m_pData = 0;
791 goto ErrExit;
792 }
793 _ASSERTE(cbRead == m_cbData);
794
795 // If the file isn't being opened for exclusive mode, then free it.
796 // If it is for exclusive, then we need to keep the handle open so the
797 // file is locked, preventing other readers. Also leave it open if
798 // in read/write mode so we can truncate and rewrite.
799 if (m_hFile == INVALID_HANDLE_VALUE ||
800 ((m_fFlags & DBPROP_TMODEF_EXCLUSIVE) == 0 && (m_fFlags & DBPROP_TMODEF_WRITE) == 0))
801 {
802 // If there was a handle open, then free it.
803 if (m_hFile != INVALID_HANDLE_VALUE)
804 {
805 VERIFY(CloseHandle(m_hFile));
806 m_hFile = INVALID_HANDLE_VALUE;
807 }
808 // Free the stream pointer.
809 else
810 if (m_pIStream != 0)
811 {
812 m_pIStream->Release();
813 m_pIStream = 0;
814 }
815
816 // Switch the type to memory only access.
817 m_iType = STGIO_MEM;
818 }
819 else
820 m_iType = STGIO_HFILEMEM;
821
822 // Free the memory when we shut down.
823 m_bFreeMem = true;
824 }
825 // Finally, a real mapping file must be created.
826 else
827 {
828 // Now we will map, so better have it right.
829 _ASSERTE(m_hFile != INVALID_HANDLE_VALUE || m_iType == STGIO_STREAM);
830 _ASSERTE(m_rgPageMap == 0);
831
832 // For read mode, use a memory mapped file since the size will never
833 // change for the life of the handle.
834 if ((m_fFlags & DBPROP_TMODEF_WRITE) == 0 && m_iType != STGIO_STREAM)
835 {
836 // Create a mapping object for the file.
837 _ASSERTE(m_hMapping == 0);
838
839 DWORD dwProtectionFlags = PAGE_READONLY;
840
841 if ((m_hMapping = WszCreateFileMapping(m_hFile, pAttributes, dwProtectionFlags,
842 0, 0, nullptr)) == 0)
843 {
844 return (MapFileError(GetLastError()));
845 }
846 m_mtMappedType = MTYPE_FLAT;
847 // Check to see if the memory already exists, in which case we have
848 // no guarantees it is the right piece of data.
849 if (GetLastError() == ERROR_ALREADY_EXISTS)
850 {
851 hr = PostError(CLDB_E_SMDUPLICATE, rcShared);
852 goto ErrExit;
853 }
854
855 // Now map the file into memory so we can read from pointer access.
856 // <REVISIT_TODO>Note: Added a check for IsBadReadPtr per the Services team which
857 // indicates that under some conditions this API can give you back
858 // a totally bogus pointer.</REVISIT_TODO>
859 if ((m_pBaseData = m_pData = MapViewOfFile(m_hMapping, FILE_MAP_READ,
860 0, 0, 0)) == 0)
861 {
862 hr = MapFileError(GetLastError());
863 if (SUCCEEDED(hr))
864 {
865 _ASSERTE_MSG(FALSE, "Error code doesn't indicate error.");
866 hr = PostError(CLDB_E_FILE_CORRUPT);
867 }
868
869 // In case we got back a bogus pointer.
870 m_pBaseData = m_pData = NULL;
871 goto ErrExit;
872 }
873 }
874 // In write mode, we need the hybrid combination of being able to back up
875 // the data in memory via cache, but then later rewrite the contents and
876 // throw away our cached copy. Memory mapped files are not good for this
877 // case due to poor write characteristics.
878 else
879 {
880 ULONG iMaxSize; // How much memory required for file.
881
882 // Figure out how many pages we'll require, round up actual data
883 // size to page size.
884 iMaxSize = (((m_cbData - 1) & ~(m_iPageSize - 1)) + m_iPageSize);
885 // Check integer overflow in previous statement
886 if (iMaxSize < m_cbData)
887 {
888 IfFailGo(PostError(COR_E_OVERFLOW));
889 }
890
891 // Allocate a bit vector to track loaded pages.
892 if ((m_rgPageMap = new (nothrow) BYTE[iMaxSize / m_iPageSize]) == 0)
893 return (PostError(OutOfMemory()));
894 memset(m_rgPageMap, 0, sizeof(BYTE) * (iMaxSize / m_iPageSize));
895
896 // Allocate space for the file contents.
897 if ((m_pBaseData = m_pData = ::ClrVirtualAlloc(0, iMaxSize, MEM_RESERVE, PAGE_NOACCESS)) == 0)
898 {
899 hr = PostError(OutOfMemory());
900 goto ErrExit;
901 }
902 }
903 }
904
905 // Reset any changes made by mapping.
906 IfFailGo( Seek(0, FILE_BEGIN) );
907
908ErrExit:
909
910 // Check for errors and clean up.
911 if (FAILED(hr))
912 {
913 if (m_hMapping)
914 CloseHandle(m_hMapping);
915 m_hMapping = 0;
916 m_pBaseData = m_pData = 0;
917 m_cbData = 0;
918 }
919 ptr = m_pData;
920 if (pcbSize)
921 *pcbSize = m_cbData;
922 return (hr);
923}
924
925
926//*****************************************************************************
927// Free the mapping object for shared memory but keep the rest of the internal
928// state intact.
929//*****************************************************************************
930HRESULT StgIO::ReleaseMappingObject() // Return code.
931{
932 // Check type first.
933 if (m_iType != STGIO_SHAREDMEM)
934 {
935 _ASSERTE(FALSE);
936 return S_OK;
937 }
938
939 // Must have an allocated handle.
940 _ASSERTE(m_hMapping != 0);
941
942 // Freeing the mapping object doesn't do any good if you still have the file.
943 _ASSERTE(m_hFile == INVALID_HANDLE_VALUE);
944
945 // Unmap the memory we allocated before freeing the handle. But keep the
946 // memory address intact.
947 if (m_pData)
948 VERIFY(UnmapViewOfFile(m_pData));
949
950 // Free the handle.
951 if (m_hMapping != 0)
952 {
953 VERIFY(CloseHandle(m_hMapping));
954 m_hMapping = 0;
955 }
956 return S_OK;
957}
958
959
960
961//*****************************************************************************
962// Resets the logical base address and size to the value given. This is for
963// cases like finding a section embedded in another format, like the .clb inside
964// of an image. GetPtrForMem, Read, and Seek will then behave as though only
965// data from pbStart to cbSize is valid.
966//*****************************************************************************
967HRESULT StgIO::SetBaseRange( // Return code.
968 void *pbStart, // Start of file data.
969 ULONG cbSize) // How big is the range.
970{
971 if (m_iType == STGIO_SHAREDMEM)
972 {
973 // The base range must be inside of the current range.
974 _ASSERTE((m_pBaseData != NULL) && (m_cbData != 0));
975 _ASSERTE(((LONG_PTR) pbStart >= (LONG_PTR) m_pBaseData));
976 _ASSERTE(((LONG_PTR) pbStart + cbSize <= (LONG_PTR) m_pBaseData + m_cbData));
977 }
978
979 // Save the base range per user request.
980 m_pData = pbStart;
981 m_cbData = cbSize;
982 return S_OK;
983}
984
985
986//*****************************************************************************
987// Caller wants a pointer to a chunk of the file. This function will make sure
988// that the memory for that chunk has been committed and will load from the
989// file if required. This algorithm attempts to load no more data from disk
990// than is necessary. It walks the required pages from lowest to highest,
991// and for each block of unloaded pages, the memory is committed and the data
992// is read from disk. If all pages are unloaded, all of them are loaded at
993// once to speed throughput from disk.
994//*****************************************************************************
995HRESULT StgIO::GetPtrForMem( // Return code.
996 ULONG cbStart, // Where to start getting memory.
997 ULONG cbSize, // How much data.
998 void *&ptr) // Return pointer to memory here.
999{
1000 int iFirst, iLast; // First and last page required.
1001 ULONG iOffset, iSize; // For committing ranges of memory.
1002 int i, j; // Loop control.
1003 HRESULT hr;
1004
1005 // We need either memory (mmf or user supplied) or a backing store to
1006 // return a pointer. Call Read if you don't have these.
1007 if (!IsBackingStore() && m_pData == 0)
1008 return (PostError(BadError(E_UNEXPECTED)));
1009
1010 // Validate the caller isn't asking for a data value out of range.
1011 if (!(ClrSafeInt<ULONG>::addition(cbStart, cbSize, iOffset)
1012 && (iOffset <= m_cbData)))
1013 return (PostError(E_INVALIDARG));
1014
1015 // This code will check for pages that need to be paged from disk in
1016 // order for us to return a pointer to that memory.
1017 if (IsBackingStore())
1018 {
1019 // Backing store is bogus when in rewrite mode.
1020 if (m_bRewrite)
1021 return (PostError(BadError(E_UNEXPECTED)));
1022
1023 // Must have the page map to continue.
1024 _ASSERTE(m_rgPageMap && m_iPageSize && m_pData);
1025
1026 // Figure out the first and last page that are required for commit.
1027 iFirst = cbStart / m_iPageSize;
1028 iLast = (cbStart + cbSize - 1) / m_iPageSize;
1029
1030 // Avoid confusion.
1031 ptr = 0;
1032
1033 // Do a smart load of every page required. Do not reload pages that have
1034 // already been brought in from disk.
1035 //<REVISIT_TODO>@FUTURE: add an optimization so that when all pages have been faulted, we no
1036 // longer to a page by page search.</REVISIT_TODO>
1037 for (i=iFirst; i<=iLast; )
1038 {
1039 // Find the first page that hasn't already been loaded.
1040 while (GetBit(m_rgPageMap, i) && i<=iLast)
1041 ++i;
1042 if (i > iLast)
1043 break;
1044
1045 // Offset for first thing to load.
1046 iOffset = i * m_iPageSize;
1047 iSize = 0;
1048
1049 // See how many in a row have not been loaded.
1050 for (j=i; i<=iLast && !GetBit(m_rgPageMap, i); i++)
1051 {
1052 // Safe: iSize += m_iPageSize;
1053 if (!(ClrSafeInt<ULONG>::addition(iSize, m_iPageSize, iSize)))
1054 {
1055 return PostError(E_INVALIDARG);
1056 }
1057 }
1058
1059 // First commit the memory for this part of the file.
1060 if (::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset),
1061 iSize, MEM_COMMIT, PAGE_READWRITE) == 0)
1062 return (PostError(OutOfMemory()));
1063
1064 // Now load that portion of the file from disk.
1065 if (FAILED(hr = Seek(iOffset, FILE_BEGIN)) ||
1066 FAILED(hr = ReadFromDisk((void *) ((DWORD_PTR) m_pData + iOffset), iSize, 0)))
1067 {
1068 return (hr);
1069 }
1070
1071 // Change the memory to read only to avoid any modifications. Any faults
1072 // that occur indicate a bug whereby the engine is trying to write to
1073 // protected memory.
1074 _ASSERTE(::ClrVirtualAlloc((void *) ((DWORD_PTR) m_pData + iOffset),
1075 iSize, MEM_COMMIT, PAGE_READONLY) != 0);
1076
1077 // Record each new loaded page.
1078 for (; j<i; j++)
1079 SetBit(m_rgPageMap, j, true);
1080 }
1081
1082 // Everything was brought into memory, so now return pointer to caller.
1083 ptr = (void *) ((DWORD_PTR) m_pData + cbStart);
1084 }
1085 // Memory version or memory mapped file work the same way.
1086 else if (IsMemoryMapped() ||
1087 (m_iType == STGIO_MEM) ||
1088 (m_iType == STGIO_SHAREDMEM) ||
1089 (m_iType == STGIO_HFILEMEM))
1090 {
1091 if (!(cbStart <= m_cbData))
1092 return (PostError(E_INVALIDARG));
1093
1094 ptr = (void *) ((DWORD_PTR) m_pData + cbStart);
1095 }
1096 // What's left?! Add some defense.
1097 else
1098 {
1099 _ASSERTE(0);
1100 ptr = 0;
1101 return (PostError(BadError(E_UNEXPECTED)));
1102 }
1103 return (S_OK);
1104}
1105
1106
1107//*****************************************************************************
1108// For cached writes, flush the cache to the data store.
1109//*****************************************************************************
1110HRESULT StgIO::FlushCache()
1111{
1112 ULONG cbWritten;
1113 HRESULT hr;
1114
1115 if (m_cbBuff)
1116 {
1117 if (FAILED(hr = WriteToDisk(m_rgBuff, m_cbBuff, &cbWritten)))
1118 return (hr);
1119 m_cbBuff = 0;
1120 }
1121 return (S_OK);
1122}
1123
1124//*****************************************************************************
1125// Tells the file system to flush any cached data it may have. This is
1126// expensive, but if successful guarantees you won't lose writes short of
1127// a disk failure.
1128//*****************************************************************************
1129HRESULT StgIO::FlushFileBuffers()
1130{
1131 _ASSERTE(!IsReadOnly());
1132
1133 if (m_hFile != INVALID_HANDLE_VALUE)
1134 {
1135 if (::FlushFileBuffers(m_hFile))
1136 return (S_OK);
1137 else
1138 return (MapFileError(GetLastError()));
1139 }
1140 return (S_OK);
1141}
1142
1143
1144//*****************************************************************************
1145// Called after a successful rewrite of an existing file. The in memory
1146// backing store is no longer valid because all new data is in memory and
1147// on disk. This is essentially the same state as created, so free up some
1148// working set and remember this state.
1149//*****************************************************************************
1150HRESULT StgIO::ResetBackingStore() // Return code.
1151{
1152 // Don't be calling this function for read only data.
1153 _ASSERTE(!IsReadOnly());
1154
1155 // Free up any backing store data we no longer need now that everything
1156 // is in memory.
1157 FreePageMap();
1158 return (S_OK);
1159}
1160
1161
1162//
1163// Private.
1164//
1165
1166
1167
1168//*****************************************************************************
1169// This version will force the data in cache out to disk for real. The code
1170// can handle the different types of storage we might be sitting on based on
1171// the open type.
1172//*****************************************************************************
1173HRESULT StgIO::WriteToDisk( // Return code.
1174 const void *pbBuff, // Buffer to write.
1175 ULONG cbWrite, // How much.
1176 ULONG *pcbWritten) // Return how much written.
1177{
1178 ULONG cbWritten; // Buffer for write funcs.
1179 HRESULT hr = S_OK;
1180
1181 // Pretty obvious.
1182 _ASSERTE(!IsReadOnly());
1183
1184 // Always need a buffer to write this data to.
1185 if (!pcbWritten)
1186 pcbWritten = &cbWritten;
1187
1188 // Action taken depends on type of storage.
1189 switch (m_iType)
1190 {
1191 case STGIO_HFILE:
1192 case STGIO_HFILEMEM:
1193 {
1194 // Use the file system's move.
1195 _ASSERTE(m_hFile != INVALID_HANDLE_VALUE);
1196
1197 // Do the write to disk.
1198 if (!::WriteFile(m_hFile, pbBuff, cbWrite, pcbWritten, 0))
1199 hr = MapFileError(GetLastError());
1200 }
1201 break;
1202
1203 // Free the stream pointer.
1204 case STGIO_STREAM:
1205 {
1206 // Delegate write to stream code.
1207 hr = m_pIStream->Write(pbBuff, cbWrite, pcbWritten);
1208 }
1209 break;
1210
1211 // We cannot write to fixed read/only memory or LoadLibrary module.
1212 case STGIO_HMODULE:
1213 case STGIO_MEM:
1214 case STGIO_SHAREDMEM:
1215 _ASSERTE(0);
1216 hr = BadError(E_UNEXPECTED);
1217 break;
1218
1219 // Weird to seek with no data.
1220 case STGIO_NODATA:
1221 default:
1222 _ASSERTE(0);
1223 break;
1224 }
1225 return (hr);
1226}
1227
1228
1229//*****************************************************************************
1230// This version only reads from disk.
1231//*****************************************************************************
1232HRESULT StgIO::ReadFromDisk( // Return code.
1233 void *pbBuff, // Write buffer here.
1234 ULONG cbBuff, // How much to read.
1235 ULONG *pcbRead) // How much read.
1236{
1237 ULONG cbRead;
1238
1239 _ASSERTE(m_iType == STGIO_HFILE || m_iType == STGIO_STREAM);
1240
1241 // Need to have a buffer.
1242 if (!pcbRead)
1243 pcbRead = &cbRead;
1244
1245 // Read only from file to avoid recursive logic.
1246 if (m_iType == STGIO_HFILE || m_iType == STGIO_HFILEMEM)
1247 {
1248 if (::ReadFile(m_hFile, pbBuff, cbBuff, pcbRead, 0))
1249 return (S_OK);
1250 return (MapFileError(GetLastError()));
1251 }
1252 // Read directly from stream.
1253 else
1254 {
1255 return (m_pIStream->Read(pbBuff, cbBuff, pcbRead));
1256 }
1257}
1258
1259
1260//*****************************************************************************
1261// Copy the contents of the file for this storage to the target path.
1262//*****************************************************************************
1263HRESULT StgIO::CopyFileInternal( // Return code.
1264 LPCWSTR szTo, // Target save path for file.
1265 int bFailIfThere, // true to fail if target exists.
1266 int bWriteThrough) // Should copy be written through OS cache.
1267{
1268 DWORD iCurrent; // Save original location.
1269 DWORD cbRead; // Byte count for buffer.
1270 DWORD cbWrite; // Check write of bytes.
1271 const DWORD cbBuff = 4096; // Size of buffer for copy (in bytes).
1272 BYTE *pBuff = (BYTE*)alloca(cbBuff); // Buffer for copy.
1273 HANDLE hFile; // Target file.
1274 HRESULT hr = S_OK;
1275
1276 // Create target file.
1277 if ((hFile = ::WszCreateFile(szTo, GENERIC_WRITE, 0, 0,
1278 (bFailIfThere) ? CREATE_NEW : CREATE_ALWAYS,
1279 (bWriteThrough) ? FILE_FLAG_WRITE_THROUGH : 0,
1280 0)) == INVALID_HANDLE_VALUE)
1281 {
1282 return (MapFileError(GetLastError()));
1283 }
1284
1285 // Save current location and reset it later.
1286 iCurrent = ::SetFilePointer(m_hFile, 0, 0, FILE_CURRENT);
1287 ::SetFilePointer(m_hFile, 0, 0, FILE_BEGIN);
1288
1289 // Copy while there are bytes.
1290 while (::ReadFile(m_hFile, pBuff, cbBuff, &cbRead, 0) && cbRead)
1291 {
1292 if (!::WriteFile(hFile, pBuff, cbRead, &cbWrite, 0) || cbWrite != cbRead)
1293 {
1294 hr = STG_E_WRITEFAULT;
1295 break;
1296 }
1297 }
1298
1299 // Reset file offset.
1300 ::SetFilePointer(m_hFile, iCurrent, 0, FILE_BEGIN);
1301
1302 // Close target.
1303 if (!bWriteThrough)
1304 VERIFY(::FlushFileBuffers(hFile));
1305 ::CloseHandle(hFile);
1306 return (hr);
1307}
1308
1309
1310//*****************************************************************************
1311// Free the data used for backing store from disk in read/write scenario.
1312//*****************************************************************************
1313void StgIO::FreePageMap()
1314{
1315 // If a small file was allocated, then free that memory.
1316 if (m_bFreeMem && m_pBaseData)
1317 FreeMemory(m_pBaseData);
1318 // For mmf, close handles and free resources.
1319 else if (m_hMapping && m_pBaseData)
1320 {
1321 VERIFY(UnmapViewOfFile(m_pBaseData));
1322 VERIFY(CloseHandle(m_hMapping));
1323 }
1324 // For our own system, free memory.
1325 else if (m_rgPageMap && m_pBaseData)
1326 {
1327 delete [] m_rgPageMap;
1328 m_rgPageMap = 0;
1329 VERIFY(::ClrVirtualFree(m_pBaseData, (((m_cbData - 1) & ~(m_iPageSize - 1)) + m_iPageSize), MEM_DECOMMIT));
1330 VERIFY(::ClrVirtualFree(m_pBaseData, 0, MEM_RELEASE));
1331 m_pBaseData = 0;
1332 m_cbData = 0;
1333 }
1334
1335 m_pBaseData = 0;
1336 m_hMapping = 0;
1337 m_cbData = 0;
1338}
1339
1340
1341//*****************************************************************************
1342// Check the given pointer and ensure it is aligned correct. Return true
1343// if it is aligned, false if it is not.
1344//*****************************************************************************
1345int StgIO::IsAlignedPtr(ULONG_PTR Value, int iAlignment)
1346{
1347 HRESULT hr;
1348 void *ptrStart = NULL;
1349
1350 if ((m_iType == STGIO_STREAM) ||
1351 (m_iType == STGIO_SHAREDMEM) ||
1352 (m_iType == STGIO_MEM))
1353 {
1354 return ((Value - (ULONG_PTR) m_pData) % iAlignment == 0);
1355 }
1356 else
1357 {
1358 hr = GetPtrForMem(0, 1, ptrStart);
1359 _ASSERTE(hr == S_OK && "GetPtrForMem failed");
1360 _ASSERTE(Value > (ULONG_PTR) ptrStart);
1361 return (((Value - (ULONG_PTR) ptrStart) % iAlignment) == 0);
1362 }
1363} // int StgIO::IsAlignedPtr()
1364
1365
1366
1367
1368
1369//*****************************************************************************
1370// These helper functions are used to allocate fairly large pieces of memory,
1371// more than should be taken from the runtime heap, but less that would require
1372// virtual memory overhead.
1373//*****************************************************************************
1374// #define _TRACE_MEM_ 1
1375
1376void *AllocateMemory(int iSize)
1377{
1378 void * ptr;
1379 ptr = new (nothrow) BYTE[iSize];
1380
1381#if defined(_DEBUG) && defined(_TRACE_MEM_)
1382 static int i=0;
1383 DbgWriteEx(W("AllocateMemory: (%d) 0x%08x, size %d\n"), ++i, ptr, iSize);
1384#endif
1385 return (ptr);
1386}
1387
1388
1389void FreeMemory(void *pbData)
1390{
1391#if defined(_DEBUG) && defined(_TRACE_MEM_)
1392 static int i=0;
1393 DbgWriteEx(W("FreeMemory: (%d) 0x%08x\n"), ++i, pbData);
1394#endif
1395
1396 _ASSERTE(pbData);
1397 delete [] (BYTE *) pbData;
1398}
1399
1400