Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3318 lines
96 KiB

//***************************************************************************
//
// (c) 2001 by Microsoft Corp. All Rights Reserved.
//
// PAGEMGR.CPP
//
// Declarations for CPageFile, CPageSource for WMI repository for
// Windows XP. This is a fully transacted high-speed page manager.
//
// 21-Feb-01 raymcc first draft of interfaces
// 28-Feb-01 raymcc first complete working model
// 18-Apr-01 raymcc Final fixes for rollback; page reuse
//
//***************************************************************************
#include "precomp.h"
#include <stdio.h>
#include <helper.h>
#include <pagemgr.h>
#include <sync.h>
#include <wbemcomn.h>
#include <creposit.h>
#include <cwbemtime.h>
#include <evtlog.h>
#include <winmgmtr.h>
#include <autoptr.h>
//
//
/////////////////////////////////////////////////////
extern SECURITY_ATTRIBUTES g_SA;
#ifdef DBG
void ASSERT_DEBUG_BREAK(bool x) {if (!x) __try { DebugBreak(); } __except(EXCEPTION_EXECUTE_HANDLER){};}
#else
void ASSERT_DEBUG_BREAK(bool x){};
#endif
#define GETTIME( a )
//
//
/////////////////////////////////////////////////////
#define MAP_LEADING_SIGNATURE 0xABCD
#define MAP_TRAILING_SIGNATURE 0xDCBA
#define CURRENT_TRANSACTION 0x80000000
#define PREVIOUS_TRANSACTION 0x40000000
#define ALL_REPLACED_FLAGS (CURRENT_TRANSACTION | PREVIOUS_TRANSACTION)
#define MERE_PAGE_ID(x) (x & 0x3FFFFFFF)
//Max pages = MAX_DWORD/8K. This is because we work in 8K pages, and the maximum size of a file is a DWORD because of
//the SetFilePosition we use, and don't use the TOP DWORD!
#define MAX_NUM_PAGES 0x7FFFF
void StripHiBits(std::vector <DWORD, wbem_allocator<DWORD> > &Array);
void MoveCurrentToPrevious(std::vector <DWORD, wbem_allocator<DWORD> > &Array);
/*
Map & cache orientation
(a) There are two types of page ids, logical and physical. The logical
ID is what is used by the external user, such as the B-tree or
object heap. The physical ID is the 0-origin page number into the
file itself. For each logical id, there is a corresponding physical
id. The decoupling is to allow transacted writes without multiple
physical writes during commit/rollback, but to 'simulate' transactions
by interchanging physical-to-logical ID mapping instead. The
physical offset into the file is the ID * the page size.
(b) Logical ID is implied, and is the offset into the PhysicalID array
(c) Cache operation is by physical id only
(d) The physical IDs of the pages contain other transaction information.
The MS 2 bits are transaction-related and only the lower 30 bits is the
physical page id, and so must be masked out when computing offsets.
The bits are manipulated during commit/rollback, etc.
(d) 0xFFFFFFFE is a reserved page, meaning a page that was allocated
by NewPage, but has not yet been written for the first time using PutPage.
This is merely validation technique to ensure pages are written only
after they were requested.
(e) Cache is agnostic to the transaction methodology. It is
simply a physical page cache and has no knowledge of anything
else. It is promote-to-front on all accesses. For optimization
purposes, there is no real movement if the promotion would
move the page from a near-to-front to absolute-front location.
This is the 'PromoteToFrontThreshold' in the Init function.
Note that the cache ordering is by access and not sorted
in any other way. Lookups require a linear scan.
It is possible that during writes new physical pages
are added to the cache which are 'new extent' pages. These
are harmless.
Map PhysId Cache
[0] 5 /-> 2 -> bytes
[1] 6 / 3 -> bytes
[2] -1 / 4 -> bytes
[3] 3 / 5 -> bytes
[4] 2 <-/
[5] 4
(f) Transaction & checkpoint algorithms
First, the general process is this:
1. Begin transaction:
(a) Generation A mapping and Generation B mapping member
variables are identical. Each contains a page map
2. Operations within the transaction occur on Generation B
mapping. Cache spills are allowed to disk, as they are harmless.
3. At rollback, Generation B mapping is copied from Generation A.
4. At commit, Generation A is copied from Generation B.
5. At checkpoint, Generation A/B are identical and written to disk
Cache spills & usage are irrelevant to intermediate transactions.
There are special cases in terms of how pages are reused. First,
as pages are freed within a transaction, we cannot just blindly add them
to the free list, since they might be part of the previous checkpoint
page set. This would allow them to be accidentally reused and then
a checkpoint rollback could never succeed (the original page
having been destroyed).
As pages committed during the previous checkpoint are updated, they
are written to new physical pages under the same logical id. The old
physical pages are added to the "Replaced Pages" array. This allows
them to be identified as new free list pages once the checkpoint
occurs. So, during the checkpoint, replaced pages are merged
into the current free list. Until that time, they are 'off limits',
since we need them for a checkpoint rollback in an emergency.
Within a transaction, as pages are acquired, they are acquired
from the physical free list, or if there is no free list, new
pages are requested. It can happen that during the next transaction (still
within the checkpoint), those pages need updating, whether for rewrite
or delete. Now, because we may have to roll back, we cannot simply
add those replaced pages directly to the free list (allowing them
to be reused by some other operation). Instead, they
have to be part of a 'deferred free list'. Once the current
transaction is committed, they can safely be part of the
regular free list.
The algorithm is this:
(a) The physical Page ID is the lower 30 bits of the entry. The two high
bits have a special meaning.
(b) Writes result either in an update or a new allocation [whether
from an extent or a reuse of the free list]. Any such page is marked
with the CURRENT_TRANSACTION bit (the ms bit) which is merged into
the phyiscal id. If this page is encountered again, we know it
was allocated during the current transaction.
(c) For UPDATES
1. If the page ID is equal to 0xFFFFFFFE, then we need to
allocate a new page, which is marked with CURRENT_TRANSACTION.
2. If the physical page ID written has both high bits clear,
it is a page being updated which was inherited from the previous
checkpoint page set. We allocate and write to a new physical
page, marking this new page with CURRENT_TRANSACTION.
We add the old physical page ID directly to the m_xReplacedPages
array. It is off-limits until the next checkpoint, at which
point it is merged into the free list.
3. If the physical page id already has CURRENT_TRANSACTION, we
simply update the page in place.
4. If the page has the PREVIOUS_TRANSACTION bit set, we allocate
a new page so a rollback will protect it, and add this page
to the DeferredFreeList array. During a commit, these
pages are merged into the FreeList. During a rollback,
we simply throw this array away.
(d) For DELETE
1. If the page has both hi bits clear, we add it to the ReplacedPages
array.
2. If the page has the CURRENT_TRANSACTION bit set, we add it to the free list.
This page was never part of any previous operation and can be reused
right away.
3. If the page has the PREVIOUS_TRANSACTION bit set, it is added to the
DeferredFreeList.
(e) For COMMIT
1. All pages with the CURRENT_TRANSACTION bit set are changed to clear that
bit and set the PREVIOUS_TRANSACTION bit instead.
2. All pages with the PREVIOUS_TRANSACTION bit are left intact.
3. All pages in the DeferredFreeList are added to the FreeList.
(f) For ROLLBACK
1. All pages with the CURRENT_TRANSACTION bit are moved back into the
free list (the bits are cleared).
2. All pages with the PREVIOUS_TRANSACTION bit are left intact.
(g) For Checkpoint
1. All DeferredFreeList entries are added to FreeList.
2. All ReplacedPages are added to FreeList.
3. All ms bits are cleared for physical IDs.
Proof:
T1 = page set touched for transaction 1, T2=page set touched for transaction 2, etc.
After T1-START, all new pages are marked CURRENT. If all are updated n times,
no new pages are required, since rollback brings us back to zero pages
anyway.
At T1-COMMIT, all CURRENT pages are marked PREVIOUS. This is the T1a
page set.
After T2-BEGIN, all new pages are marked CURRENT. Again, updating
all T2 pages infinitely never extends the file beyond the size
of T1a+T2 page sets. Further deleting and reusing T2 pages
never affects T1a pages, proving that deleting a CURRENT page
merits direct addition to the free list.
Updating n pages from T1 requires n new pages. As we update
all the T1 pages, we need a total file size of T2+T1*2. As
we encounter T1a pages marked as PREVIOUS, we allocate CURRENT
pages for T1b and then reuse those indefinitely. Whether we update
all T1a or delete all T1a, we must still protect the original T1a
page set in case of rollback. Therefore all touched pages of T1a
are posted the deferred free list as they become replaced by T1b
equivalents.
At rollback, we simply throw away the deferred free list,
and T1a is intact, since those physical pages were never touched.
At commit, all T1a pages are now in T1b, and al T1a
pages are now reusable, and of course were in fact added
to the deferred free list, which is merged with the
general free list at commit time. T1b and T2 pages are now
protected and marked PREVIOUS.
On the transitive nature of PREVIOUS:
Assume zero T1 pages are touched at T2-BEGIN, and there
are only T2 updates, commit and then T3 is begun. At
this point, both T1/T2 sets are marked as PREVIOUS
and not distinguished. T3 new allocations follow the
same rules. Obviously if all T1 pages are deleted, we
still cannot afford to reuse them, since a rollback
would need to see T1 pages intact. Therefore at
each commit, there is an effective identity shift where
T1 = T2 UNION T1
T3 becomes T2
And we are in the 2-transaction problem space again.
Unmarking pages completely makes them equal to the
previous checkpoint page set. They can be completely
replaced with new physical pages, but never reused until
the next checkpoint, which wastes space.
*/
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// CPageCache
//***************************************************************************
//
// CPageCache::CPageCache
//
//***************************************************************************
// rev2
CPageCache::CPageCache(const wchar_t *wszStoreName)
: m_wszStoreName(wszStoreName)
{
m_hFile = INVALID_HANDLE_VALUE;
m_dwPageSize = 0;
m_dwCacheSize = 0;
m_dwCachePromoteThreshold = 0;
m_dwCacheSpillRatio = 0;
m_dwLastFlushTime = GetCurrentTime();
m_dwWritesSinceFlush = 0;
m_dwLastCacheAccess = 0;
m_dwReadHits = 0;
m_dwReadMisses = 0;
m_dwWriteHits = 0;
m_dwWriteMisses = 0;
}
//***************************************************************************
//
// CPageCache::Init
//
// Initializes the cache. Called once during startup. If the file
// can't be opened, the cache becomes invalid right at the start.
//
//***************************************************************************
// rev2
DWORD CPageCache::Init(
LPCWSTR pszFilename, // File
DWORD dwPageSize, // In bytes
DWORD dwCacheSize, // Pages in cache
DWORD dwCachePromoteThreshold, // When to ignore promote-to-front
DWORD dwCacheSpillRatio // How many additional pages to write on cache write-through
)
{
m_dwPageSize = dwPageSize;
m_dwCacheSize = dwCacheSize;
m_dwCachePromoteThreshold = dwCachePromoteThreshold;
m_dwCacheSpillRatio = dwCacheSpillRatio;
m_hFile = CreateFileW(pszFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, &g_SA,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (m_hFile == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::~CPageCache
//
// Empties cache during destruct; called once at shutdown.
//
//***************************************************************************
// rev2
CPageCache::~CPageCache()
{
DeInit();
}
DWORD CPageCache::DeInit()
{
if (m_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFile);
m_hFile = INVALID_HANDLE_VALUE;
}
Empty();
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::Write()
//
// Writes a page to the cache and promotes it to the front. If the
// page is already present, it is simply marked as dirty and promoted
// to the front (if it is outside the promote-to-front threshold).
// If cache is full and this causes overflow, it is handled by the
// Cache_Spill() at the end. Physical writes occur in Spill() only if
// there is cache overflow.
//
// Errors: Return codes only, sets permanent error status of object
// once error has occurred.
//
// On failure, the caller must invoked Cache::Reinit() before using
// it any further. On error DISK_FULL, there is not much of a point
// in a Reinit() even though it is safe.
//
//***************************************************************************
// rev2
DWORD CPageCache::Write(
DWORD dwPhysId,
LPBYTE pPageMem
)
{
m_dwWritesSinceFlush++;
m_dwLastCacheAccess = GetCurrentTime();
// Search current cache.
// =====================
DWORD dwSize = m_aCache.size();
for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
{
SCachePage *pTest = m_aCache[dwIx];
if (pTest->m_dwPhysId == dwPhysId)
{
delete [] pTest->m_pPage;
pTest->m_pPage = pPageMem;
pTest->m_bDirty = TRUE;
// Promote to front?
// =================
if (dwIx > m_dwCachePromoteThreshold)
{
try
{
m_aCache.erase(m_aCache.begin()+dwIx);
m_aCache.insert(m_aCache.begin(), pTest);
}
catch (CX_MemoryException &)
{
pTest->m_pPage = 0;
return ERROR_OUTOFMEMORY;
}
}
m_dwWriteHits++;
return NO_ERROR;
}
}
// If here, no cache hit, so we create a new entry.
// ================================================
wmilib::auto_ptr<SCachePage> pCP( new SCachePage);
if (NULL == pCP.get())
return ERROR_OUTOFMEMORY;
pCP->m_dwPhysId = dwPhysId;
pCP->m_pPage = 0;
pCP->m_bDirty = TRUE;
try
{
m_aCache.insert(m_aCache.begin(), pCP.get());
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
pCP->m_pPage = pPageMem;
DWORD dwRes = Spill();
if (ERROR_SUCCESS != dwRes)
{
pCP->m_pPage = 0;
m_aCache.erase(m_aCache.begin());
return dwRes;
}
m_dwWriteMisses++;
pCP.release();
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::Read
//
// Reads the requested page from the cache. If the page isn't found
// it is loaded from the disk file. The cache size cannot change, but
// the referenced page is promoted to the front if it is outside of
// the no-promote-threshold.
//
// A pointer directly into the cache mem is returned in <pMem>, so
// the contents should be copied as soon as possible.
//
// Errors: If the read fails due to ERROR_OUTOFMEMORY, then the
// cache is permanently in an error state until Cache->Reinit()
// is called.
//
//***************************************************************************
// rev2
DWORD CPageCache::Read(
IN DWORD dwPhysId,
OUT LPBYTE *pMem // Read-only!
)
{
if (pMem == 0)
return ERROR_INVALID_PARAMETER;
m_dwLastCacheAccess = GetCurrentTime();
// Search current cache.
// =====================
DWORD dwSize = m_aCache.size();
for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
{
SCachePage *pTest = m_aCache[dwIx];
if (pTest->m_dwPhysId == dwPhysId)
{
// Promote to front?
if (dwIx > m_dwCachePromoteThreshold)
{
try
{
m_aCache.erase(m_aCache.begin()+dwIx);
m_aCache.insert(m_aCache.begin(), pTest);
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
}
*pMem = pTest->m_pPage;
m_dwReadHits++;
return NO_ERROR;
}
}
// If here, not found, so we have to read in from disk
// and do a spill test.
// ====================================================
wmilib::auto_ptr<SCachePage> pCP(new SCachePage);
if (NULL == pCP.get())
return ERROR_OUTOFMEMORY;
pCP->m_dwPhysId = dwPhysId;
pCP->m_bDirty = FALSE;
pCP->m_pPage = 0;
m_dwReadMisses++;
DWORD dwRes = ReadPhysPage(dwPhysId, &pCP->m_pPage);
if (ERROR_SUCCESS != dwRes)
return dwRes;
try
{
m_aCache.insert(m_aCache.begin(), pCP.get());
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
dwRes = Spill();
if (ERROR_SUCCESS != dwRes)
{
m_aCache.erase(m_aCache.begin());
return dwRes;
}
*pMem = pCP->m_pPage;
pCP.release(); // cache took ownership
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::Spill
//
// Checks for cache overflow and implements the spill-to-disk
// algorithm.
//
// Precondition: The cache is either within bounds or 1 page too large.
//
// If the physical id of the pages written exceeds the physical extent
// of the file, WritePhysPage will properly extend the file to handle it.
//
// Note that if no write occurs during the spill, additional pages are
// not spilled or written.
//
//***************************************************************************
// rev2
DWORD CPageCache::Spill()
{
BOOL bWritten = FALSE;
DWORD dwRes = 0;
DWORD dwSize = m_aCache.size();
// See if the cache has exceeded its limit.
// ========================================
if (dwSize <= m_dwCacheSize)
return NO_ERROR;
// If here, the cache is too large by 1 element (precondition).
// We remove the last page after checking to see if it is
// dirty and needs writing.
// ============================================================
SCachePage *pDoomed = *(m_aCache.end()-1);
if (pDoomed->m_bDirty)
{
dwRes = WritePhysPage(pDoomed->m_dwPhysId, pDoomed->m_pPage);
if (dwRes != NO_ERROR)
{
return dwRes;
}
bWritten = TRUE;
}
delete pDoomed;
try
{
m_aCache.erase(m_aCache.end()-1);
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
if (!bWritten)
return NO_ERROR;
// If here, we had a write.
// Next, work through the cache from the end and write out
// a few more pages, based on the spill ratio. We don't
// remove these from the cache, we simply write them and
// clear the dirty bit.
// ========================================================
DWORD dwWriteCount = 0;
try
{
std::vector <SCachePage *>::reverse_iterator rit;
rit = m_aCache.rbegin();
while (rit != m_aCache.rend() && dwWriteCount < m_dwCacheSpillRatio)
{
SCachePage *pTest = *rit;
if (pTest->m_bDirty)
{
dwRes = WritePhysPage(pTest->m_dwPhysId, pTest->m_pPage);
if (dwRes)
return dwRes;
pTest->m_bDirty = FALSE;
dwWriteCount++;
}
rit++;
}
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::WritePhysPage
//
// Writes a physical page.
//
//***************************************************************************
// rev2
DWORD CPageCache::WritePhysPage(
IN DWORD dwPageId,
IN LPBYTE pPageMem
)
{
GETTIME(Counter::OpTypeWrite);
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
DWORD dwWritten;
LARGE_INTEGER Li;
Li.QuadPart = m_dwPageSize * dwPageId;
Status = NtWriteFile(m_hFile,
NULL,
NULL,
NULL,
&IoStatusBlock,
(PVOID)pPageMem,
m_dwPageSize,
&Li,
NULL);
if ( Status == STATUS_PENDING)
{
// Operation must complete before return & IoStatusBlock destroyed
Status = NtWaitForSingleObject( m_hFile, FALSE, NULL );
if ( NT_SUCCESS(Status))
{
Status = IoStatusBlock.Status;
}
}
if ( !NT_SUCCESS(Status))
{
return RtlNtStatusToDosError( Status );
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::Empty
//
// Does no checking to see if a flush should have occurred.
//
//***************************************************************************
// rev2
DWORD CPageCache::Empty()
{
DWORD dwSize = m_aCache.size();
for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
{
SCachePage *pTest = m_aCache[dwIx];
delete pTest;
}
m_aCache.clear();
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::Flush
//
//***************************************************************************
// rev2
DWORD CPageCache::Flush()
{
// Short-circuit. If no writes have occurred, just reset
// and return.
// =======================================================
if (m_dwWritesSinceFlush == 0)
{
m_dwLastFlushTime = GetCurrentTime();
m_dwWritesSinceFlush = 0;
return NO_ERROR;
}
// Logical cache flush.
// ====================
DWORD dwRes = 0;
DWORD dwSize = m_aCache.size();
for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
{
SCachePage *pTest = m_aCache[dwIx];
if (pTest->m_bDirty)
{
dwRes = WritePhysPage(pTest->m_dwPhysId, pTest->m_pPage);
if (dwRes)
return dwRes;
pTest->m_bDirty = FALSE;
}
}
// Do the disk flush.
// ==================
{
GETTIME(Counter::OpTypeFlush);
if (!FlushFileBuffers(m_hFile))
return GetLastError();
}
m_dwLastFlushTime = GetCurrentTime();
m_dwWritesSinceFlush = 0;
return NO_ERROR;
}
//***************************************************************************
//
// CPageCache::ReadPhysPage
//
// Reads a physical page from the file.
//
//***************************************************************************
// rev2
DWORD CPageCache::ReadPhysPage(
IN DWORD dwPage,
OUT LPBYTE *pPageMem
)
{
DWORD dwRes;
*pPageMem = 0;
if (pPageMem == 0)
return ERROR_INVALID_PARAMETER;
// Allocate some memory
// ====================
LPBYTE pMem = new BYTE[m_dwPageSize];
if (!pMem)
{
return ERROR_OUTOFMEMORY;
}
// Where is this page hiding?
// ==========================
LARGE_INTEGER pos;
pos.QuadPart = dwPage * m_dwPageSize;
dwRes = SetFilePointerEx(m_hFile, pos, NULL, FILE_BEGIN);
if (dwRes == 0)
{
delete [] pMem;
return GetLastError();
}
// Try to read it.
// ===============
DWORD dwRead = 0;
BOOL bRes = ReadFile(m_hFile, pMem, m_dwPageSize, &dwRead, 0);
if (!bRes || dwRead != m_dwPageSize)
{
delete [] pMem;
// If we can't read it, we probably did a seek past eof,
// meaning the requested page was invalid.
// =====================================================
return ERROR_INVALID_PARAMETER;
}
*pPageMem = pMem;
return NO_ERROR;
}
DWORD CPageCache::GetFileSize(LARGE_INTEGER *pFileSize)
{
if (!GetFileSizeEx(m_hFile, pFileSize))
return GetLastError();
return ERROR_SUCCESS;
}
//***************************************************************************
//
// CPageCache::Dump
//
// Dumps cache info to the specified stream.
//
//***************************************************************************
// rev2
void CPageCache::Dump(FILE *f)
{
DWORD dwSize = m_aCache.size();
fprintf(f, "---Begin Cache Dump---\n");
fprintf(f, "Time since last flush = %d\n", GetCurrentTime() - m_dwLastFlushTime);
fprintf(f, "Writes since last flush = %d\n", m_dwWritesSinceFlush);
fprintf(f, "Read hits = %d\n", m_dwReadHits);
fprintf(f, "Read misses = %d\n", m_dwReadMisses);
fprintf(f, "Write hits = %d\n", m_dwWriteHits);
fprintf(f, "Write misses = %d\n", m_dwWriteMisses);
fprintf(f, "Size = %d\n", dwSize);
for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
{
SCachePage *pTest = m_aCache[dwIx];
fprintf(f, "Cache[%d] ID=%d dirty=%d pMem=0x%p <%s>\n",
dwIx, pTest->m_dwPhysId, pTest->m_bDirty, pTest->m_pPage, pTest->m_pPage);
}
fprintf(f, "---End Cache Dump---\n");
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
//
// CPageFile
//***************************************************************************
//
// CPageFile::AddRef
//
//***************************************************************************
// rev2
ULONG CPageFile::AddRef()
{
return (ULONG) InterlockedIncrement(&m_lRef);
}
//***************************************************************************
//
// CPageFile::ResyncMaps
//
// Reninitializes B maps from A maps.
//
//***************************************************************************
// rev2
DWORD CPageFile::ResyncMaps()
{
try
{
m_aPageMapB.reserve(max(m_aPageMapB.size(), m_aPageMapA.size()));
m_aPhysFreeListB.reserve(max(m_aPhysFreeListB.size(), m_aPhysFreeListA.size()));
m_aLogicalFreeListB.reserve(max(m_aLogicalFreeListB.size(), m_aLogicalFreeListA.size()));
m_aReplacedPagesB.reserve(max(m_aReplacedPagesB.size(), m_aReplacedPagesA.size()));
m_aPageMapB = m_aPageMapA;
m_aPhysFreeListB = m_aPhysFreeListA;
m_aLogicalFreeListB = m_aLogicalFreeListA;
m_aReplacedPagesB = m_aReplacedPagesA;
m_dwPhysPagesB = m_dwPhysPagesA;
m_aDeferredFreeList.clear();
}
catch (CX_MemoryException &)
{
_ASSERT(0, L"WinMgmt: ResyncMaps failed because of out of memory - precondition not satisfied!\n")
return ERROR_OUTOFMEMORY;
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::ReadMap
//
// Reads the map file into memory. Format:
//
// DWORD dwLeadingSignature
// DWORD dwTransactionGeneration
// DWORD dwNumMappedPages
// DWORD PhysicalPages[]
// DWORD dwNumFreePages
// DWORD FreePages[]
// DWORD dwTrailingSignature
//
// The only time the MAP file will not be present is on creation of
// a new map file.
//
// This function is retry-compatible.
//
//***************************************************************************
// rev2
DWORD CPageFile::ReadMap(HANDLE hFile)
{
BOOL bRes;
// If here, read it.
// =================
DWORD dwSignature = 0;
DWORD dwRead = 0;
bRes = ReadFile(hFile, &dwSignature, sizeof(DWORD), &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD) || dwSignature != MAP_LEADING_SIGNATURE)
{
return ERROR_INTERNAL_DB_CORRUPTION;
}
// Read transaction version.
// =========================
bRes = ReadFile(hFile, &m_dwTransVersion, sizeof(DWORD), &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD))
{
return ERROR_INTERNAL_DB_CORRUPTION;
}
// Read in physical page count.
// ============================
bRes = ReadFile(hFile, &m_dwPhysPagesA, sizeof(DWORD), &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD))
{
return ERROR_INTERNAL_DB_CORRUPTION;
}
// Read in the page map length and page map.
// =========================================
DWORD dwNumPages = 0;
bRes = ReadFile(hFile, &dwNumPages, sizeof(DWORD), &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD))
{
return ERROR_INTERNAL_DB_CORRUPTION;
}
try
{
m_aPageMapA.resize(dwNumPages);
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
bRes = ReadFile(hFile, &m_aPageMapA[0], sizeof(DWORD)*dwNumPages, &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD)*dwNumPages)
{
m_aPageMapA.empty();
return ERROR_INTERNAL_DB_CORRUPTION;
}
// Now, read in the physical free list.
// ====================================
DWORD dwFreeListSize = 0;
bRes = ReadFile(hFile, &dwFreeListSize, sizeof(DWORD), &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD))
{
m_aPageMapA.empty();
return ERROR_INTERNAL_DB_CORRUPTION;
}
try
{
m_aPhysFreeListA.resize(dwFreeListSize);
}
catch (CX_MemoryException &)
{
m_aPageMapA.empty();
return ERROR_OUTOFMEMORY;
}
bRes = ReadFile(hFile, &m_aPhysFreeListA[0], sizeof(DWORD)*dwFreeListSize, &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD)*dwFreeListSize)
{
m_aPageMapA.empty();
m_aPhysFreeListA.empty();
return ERROR_INTERNAL_DB_CORRUPTION;
}
// Read trailing signature.
// ========================
bRes = ReadFile(hFile, &dwSignature, sizeof(DWORD), &dwRead, 0);
if (!bRes || dwRead != sizeof(DWORD) || dwSignature != MAP_TRAILING_SIGNATURE)
{
m_aPageMapA.empty();
m_aPhysFreeListA.empty();
return ERROR_INTERNAL_DB_CORRUPTION;
}
//Validate the MAP file length against the size of the data file.
DWORD dwRes = ValidateMapFile();
// Initialize the logical free list from the page map.
// ===================================================
if (dwRes == ERROR_SUCCESS)
dwRes = InitFreeList();
if (dwRes == ERROR_SUCCESS)
dwRes = ResyncMaps();
if (dwRes != ERROR_SUCCESS)
{
//Clear everything up!
m_dwPhysPagesA = 0;
m_aPageMapA.empty();
m_aPhysFreeListA.empty();
m_aLogicalFreeListA.empty();
m_dwPhysPagesB = 0;
m_aPageMapB.empty();
m_aPhysFreeListB.empty();
m_aLogicalFreeListB.empty();
}
return dwRes;
}
//***************************************************************************
//
// CPageFile::ValidateMapFile
//
// Find the highest physical page ID and try and read it! If it fails then something is not
// right!
//***************************************************************************
DWORD CPageFile::ValidateMapFile()
{
DWORD dwRet = ERROR_SUCCESS;
DWORD dwPhysicalPageId = 0;
DWORD dwLogicalPageId = 0;
for (DWORD i = 0; i != m_aPageMapA.size(); i++)
{
if ((m_aPageMapA[i] != WMIREP_INVALID_PAGE) && (dwPhysicalPageId < m_aPageMapA[i]))
{
dwPhysicalPageId = m_aPageMapA[i];
dwLogicalPageId = i;
}
}
if (dwPhysicalPageId != 0)
{
LPBYTE pPageMem = 0;
dwRet = m_Cache.ReadPhysPage(dwPhysicalPageId, &pPageMem);
delete [] pPageMem;
if ((dwRet != ERROR_SUCCESS) && (dwRet != ERROR_OUTOFMEMORY))
{
ERRORTRACE((LOG_REPDRV, "Repository corruption detected. %S data file was not large enough to retrieve page <0x%X>. <0x%X>!\n", m_wszStoreName, dwPhysicalPageId, dwRet));
#ifdef DBG
OutputDebugStringW(L"WinMgmt: Repository corruption detected: ");
OutputDebugStringW(m_wszStoreName);
OutputDebugStringW(L"\n");
#endif
#ifdef WMI_PRIVATE_DBG
DebugBreak();
#endif
dwRet = ERROR_INTERNAL_DB_CORRUPTION;
}
else if (dwRet == ERROR_SUCCESS)
{
#ifdef WMI_PRIVATE_DBG
wchar_t wszDebug[120];
StringCchPrintfW(wszDebug, 120, L"ReadMap(%s), Highest physical Page ID <0x%X> for logical page ID <0x%X>\n", m_wszStoreName, dwPhysicalPageId, dwLogicalPageId);
ERRORTRACE((LOG_REPDRV, "%S", wszDebug));
OutputDebugStringW(wszDebug);
#endif
}
}
return dwRet;
}
//***************************************************************************
//
// CPageFile::WriteMap
//
// Writes the generation A mapping (assuming that we write immediately
// during a checkpoint when A and B generations are the same and that
// the replacement lists have been appended to the free lists, etc., etc.
// This write occurs to a temp file. The renaming occurs externally.
//
// This function is retry compatible.
//
//***************************************************************************
// rev2
DWORD CPageFile::WriteMap(HANDLE hFile)
{
BOOL bRes;
DWORD dwTotal = sizeof(DWORD)*(1 + 1 + 1 + 1 + m_aPageMapA.size() + 1 + m_aPhysFreeListA.size() +1);
ASSERT_DEBUG_BREAK((m_aPageMapA.size() + m_aPhysFreeListA.size())!=0);
BYTE * pMem = new BYTE[dwTotal];
if (NULL == pMem)
return ERROR_OUTOFMEMORY;
CVectorDeleteMe<BYTE> vdm(pMem);
DWORD * pCurrent = (DWORD *)pMem;
DWORD dwSignature = MAP_LEADING_SIGNATURE;
*pCurrent = dwSignature;
pCurrent++;
*pCurrent = m_dwTransVersion;
pCurrent++;
*pCurrent = m_dwPhysPagesA;
pCurrent++;
DWORD dwNumPages = m_aPageMapA.size();
*pCurrent = dwNumPages;
pCurrent++;
memcpy(pCurrent,&m_aPageMapA[0],sizeof(DWORD)*dwNumPages);
pCurrent+=dwNumPages;
DWORD dwFreeListSize = m_aPhysFreeListA.size();
*pCurrent = dwFreeListSize;
pCurrent++;
memcpy(pCurrent,&m_aPhysFreeListA[0], sizeof(DWORD)*dwFreeListSize);
pCurrent+=dwFreeListSize;
dwSignature = MAP_TRAILING_SIGNATURE;
*pCurrent = dwSignature;
DWORD dwWritten = 0;
{
GETTIME(Counter::OpTypeWrite);
bRes = WriteFile(hFile, pMem,dwTotal, &dwWritten, 0);
}
if (!bRes || dwWritten != dwTotal)
{
return GetLastError();
}
#ifdef WMI_PRIVATE_DBG
DumpFileInformation(hFile);
#endif
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::Trans_Commit
//
// Rolls the transaction forward (in-memory). A checkpoint may
// occur afterwards (decided outside this function)
//
// The r/w cache contents are not affected except that they may contain
// garbage pages no longer relevant.
//
//***************************************************************************
// rev2
DWORD CPageFile::Trans_Commit()
{
if (!m_bInTransaction)
return ERROR_INVALID_OPERATION;
try
{
m_aPageMapA.reserve(m_aPageMapB.size());
m_aLogicalFreeListA.reserve(m_aLogicalFreeListB.size());
m_aReplacedPagesA.reserve(m_aReplacedPagesB.size());
m_aPhysFreeListA.reserve(m_aPhysFreeListB.size()+m_aDeferredFreeList.size());
m_aPhysFreeListB.reserve(m_aPhysFreeListB.size()+m_aDeferredFreeList.size());
MoveCurrentToPrevious(m_aPageMapB);
while (m_aDeferredFreeList.size())
{
m_aPhysFreeListB.push_back(m_aDeferredFreeList.back());
m_aDeferredFreeList.pop_back();
}
m_aPageMapA = m_aPageMapB;
m_aPhysFreeListA = m_aPhysFreeListB;
m_aLogicalFreeListA = m_aLogicalFreeListB;
m_aReplacedPagesA = m_aReplacedPagesB;
m_dwPhysPagesA = m_dwPhysPagesB;
}
catch (CX_MemoryException &)
{
_ASSERT(0, L"WinMgmt: Commit failed because of out of memory - precondition not satisfied!\n")
return ERROR_OUTOFMEMORY;
}
m_bInTransaction = FALSE;
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::Trans_Rollback
//
// Rolls back a transaction within the current checkpoint window.
// If the cache is hosed, try to recover it.
//
//***************************************************************************
// rev2
DWORD CPageFile::Trans_Rollback()
{
if (!m_bInTransaction)
return ERROR_INVALID_OPERATION;
m_bInTransaction = FALSE;
return ResyncMaps();
}
//***************************************************************************
//
// CPageFile::Trans_Checkpoint
//
//***************************************************************************
// rev2
DWORD CPageFile::Trans_Checkpoint(HANDLE hFile )
{
DWORD dwRes;
std::vector <DWORD, wbem_allocator<DWORD> > & ref = m_aPageMapA;
if (m_bInTransaction)
return ERROR_INVALID_OPERATION;
// Flush cache. If cache is not in a valid state, it
// will return the error/state immediately.
// ===================================================
dwRes = m_Cache.Flush();
if (dwRes)
return dwRes;
//Make sure memory is pre-allocated before changing anything
try
{
m_aPhysFreeListA.reserve(m_aPhysFreeListA.size()+m_aReplacedPagesA.size());
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
// Strip the hi bits from the page maps.
// =====================================
StripHiBits(ref);
// The replaced pages become added to the free list.
// =================================================
int revertCount = m_aReplacedPagesA.size();
try // Operation one
{
while (m_aReplacedPagesA.size())
{
m_aPhysFreeListA.push_back(m_aReplacedPagesA.back());
m_aReplacedPagesA.pop_back();
}
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
// Ensure maps are in sync.
// ========================
dwRes = ResyncMaps();
if (dwRes)
{
// Revert operation one
while(revertCount>0)
{
m_aReplacedPagesA.push_back(m_aPhysFreeListA.back());
// we allready have space from the operation one
m_aPhysFreeListA.pop_back();
revertCount--;
};
return dwRes;
}
// Write out temp map file. The atomic rename/rollforward
// is handled by CPageSource.
// =======================================================
dwRes = WriteMap(hFile);
m_dwLastCheckpoint = GetCurrentTime();
return dwRes; // May reflect WriteMap failure
}
//***************************************************************************
//
// CPageFile::InitFreeList
//
// Initializes the free list by a one-time analysis of the map.
//
//***************************************************************************
// rev2
DWORD CPageFile::InitFreeList()
{
DWORD dwRes = NO_ERROR;
try
{
for (DWORD i = 0; i < m_aPageMapA.size(); i++)
{
DWORD dwMapId = m_aPageMapA[i];
if (dwMapId == WMIREP_INVALID_PAGE)
m_aLogicalFreeListA.push_back(i);
}
}
catch (CX_MemoryException &)
{
dwRes = ERROR_OUTOFMEMORY;
}
return dwRes;
}
//***************************************************************************
//
// CPageFile::Trans_Begin
//
//***************************************************************************
// rev2
DWORD CPageFile::Trans_Begin()
{
if (m_bInTransaction)
{
_ASSERT(0, L"WinMgmt: Trans_Begin: Nested transactions are not allowed!\n");
return ERROR_INVALID_OPERATION;
}
DWORD dwRes = ResyncMaps();
if (dwRes)
{
return dwRes;
}
m_bInTransaction = TRUE;
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::Release
//
// No checks for a checkpoint.
//
//***************************************************************************
// rev2
ULONG CPageFile::Release()
{
LONG lRes = InterlockedDecrement(&m_lRef);
if (lRes != 0)
return (ULONG) lRes;
// delete this; //NOTE: We are now an embedded object!
return 0;
}
//***************************************************************************
//
// ShellSort
//
// Generic DWORD sort using Donald Shell algorithm.
//
//***************************************************************************
// rev2
static void ShellSort(std::vector <DWORD, wbem_allocator<DWORD> > &Array)
{
for (int nInterval = 1; nInterval < Array.size() / 9; nInterval = nInterval * 3 + 1);
while (nInterval)
{
for (int iCursor = nInterval; iCursor < Array.size(); iCursor++)
{
int iBackscan = iCursor;
while (iBackscan - nInterval >= 0 && Array[iBackscan] < Array[iBackscan-nInterval])
{
DWORD dwTemp = Array[iBackscan-nInterval];
Array[iBackscan-nInterval] = Array[iBackscan];
Array[iBackscan] = dwTemp;
iBackscan -= nInterval;
}
}
nInterval /= 3;
}
}
//***************************************************************************
//
// StripHiBits
//
// Removes the hi bit from the physical disk ids so that they are no
// longer marked as 'replaced' in a transaction.
//
//***************************************************************************
// rev2
void StripHiBits(std::vector <DWORD, wbem_allocator<DWORD> > &Array)
{
for (int i = 0; i < Array.size(); i++)
{
DWORD dwVal = Array[i];
if (dwVal != WMIREP_INVALID_PAGE)
Array[i] = MERE_PAGE_ID(dwVal);
}
}
//***************************************************************************
//
// MoveCurrentToPrevious
//
// Removes the CURRENT_TRANSACTION bit from the array and makes
// it the PREVIOUS_TRANSACTION.
//
//***************************************************************************
// rev2
void MoveCurrentToPrevious(std::vector <DWORD, wbem_allocator<DWORD> > &Array)
{
for (int i = 0; i < Array.size(); i++)
{
DWORD dwVal = Array[i];
if (dwVal != WMIREP_INVALID_PAGE && (dwVal & CURRENT_TRANSACTION))
Array[i] = MERE_PAGE_ID(dwVal) | PREVIOUS_TRANSACTION;
}
}
//***************************************************************************
//
// CPageFile::FreePage
//
// Frees a page within the current transaction. The logical id
// is not removed from the map; its entry is simply assigned to
// 'InvalidPage' (0xFFFFFFFF) and the entry is added to the
// logical free list.
//
// If the associated physical page has been written within
// the transaction, it is simply added to the free list. If the page
// was never written to within this transaction, it is added to
// the replaced list.
//
//***************************************************************************
// rev2
DWORD CPageFile::FreePage(
DWORD dwFlags,
DWORD dwId
)
{
DWORD dwPhysId;
if (!m_bInTransaction)
{
return ERROR_INVALID_OPERATION;
}
// Make sure the page is 'freeable'.
// =================================
if (dwId >= m_aPageMapB.size())
return ERROR_INVALID_PARAMETER;
// Remove from page map.
// =====================
try
{
dwPhysId = m_aPageMapB[dwId];
if (dwPhysId == WMIREP_INVALID_PAGE)
return ERROR_INVALID_OPERATION; // Freeing a 'freed' page?
//Reserve space for all structures!
m_aLogicalFreeListB.reserve(m_aLogicalFreeListB.size() + 1);
m_aLogicalFreeListA.reserve(max(m_aLogicalFreeListA.size(), m_aLogicalFreeListB.size()+1));
if (dwPhysId & CURRENT_TRANSACTION)
{
m_aPhysFreeListB.reserve(m_aPhysFreeListB.size()+1+m_aDeferredFreeList.size());
m_aPhysFreeListA.reserve(max(m_aPhysFreeListA.size(), m_aPhysFreeListB.size()+1+m_aDeferredFreeList.size()));
}
else if (dwPhysId & PREVIOUS_TRANSACTION)
{
m_aDeferredFreeList.reserve(m_aDeferredFreeList.size()+1);
}
else // previous checkpoint
{
m_aReplacedPagesB.reserve(m_aReplacedPagesB.size()+1);
m_aReplacedPagesA.reserve(max(m_aReplacedPagesA.size(), m_aReplacedPagesB.size()+1));
}
//Carry out the operation now we have all the memory we need
m_aPageMapB[dwId] = WMIREP_INVALID_PAGE;
m_aLogicalFreeListB.push_back( MERE_PAGE_ID(dwId));
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
if (dwPhysId == WMIREP_RESERVED_PAGE)
{
// The logical page was freed without being ever actually committed to
// a physical page. Legal, but weird. The caller had a change of heart.
return NO_ERROR;
}
// Examine physical page to determine its ancestry. There are
// three cases.
// 1. If the page was created within the current transaction,
// we simply add it back to the free list.
// 2. If the page was created in a previous transaction, we add
// it to the deferred free list.
// 3. If the page was created in the previous checkpoint, we add
// it to the replaced pages list.
// ==============================================================
try
{
if (dwPhysId & CURRENT_TRANSACTION)
m_aPhysFreeListB.push_back(MERE_PAGE_ID(dwPhysId));
else if (dwPhysId & PREVIOUS_TRANSACTION)
m_aDeferredFreeList.push_back(MERE_PAGE_ID(dwPhysId));
else // previous checkpoint
m_aReplacedPagesB.push_back(MERE_PAGE_ID(dwPhysId));
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::GetPage
//
// Gets a page. Doesn't have to be within a transaction. However, the
// "B" generation map is always used so that getting within a transaction
// will reference the correct page.
//
//***************************************************************************
// rev2
DWORD CPageFile::GetPage(
DWORD dwId,
DWORD dwFlags,
LPVOID pPage
)
{
DWORD dwRes;
if (pPage == 0)
return ERROR_INVALID_PARAMETER;
CInCritSec _(&m_cs);
// Determine physical id from logical id.
// ======================================
if (dwId >= m_aPageMapB.size())
return ERROR_FILE_NOT_FOUND;
DWORD dwPhysId = m_aPageMapB[dwId];
if (dwPhysId == WMIREP_INVALID_PAGE || dwPhysId == WMIREP_RESERVED_PAGE)
return ERROR_INVALID_OPERATION;
LPBYTE pTemp = 0;
dwRes = m_Cache.Read(MERE_PAGE_ID(dwPhysId), &pTemp);
if (dwRes == 0)
memcpy(pPage, pTemp, m_dwPageSize);
return dwRes;
}
//***************************************************************************
//
// CPageFile::PutPage
//
// Writes a page. Must be within a transaction. If the page has been
// written for the first time within the transaction, a new replacement
// page is allocated and the original page is added to the 'replaced'
// pages list. If the page has already been updated within this transaction,
// it is simply updated again. We know this because the physical page
// id has the ms bit set (MAP_REPLACED_PAGE_FLAG).
//
//***************************************************************************
// rev2
DWORD CPageFile::PutPage(
DWORD dwId,
DWORD dwFlags,
LPVOID pPage
)
{
DWORD dwRes = 0, dwNewPhysId = WMIREP_INVALID_PAGE;
if (pPage == 0)
return ERROR_INVALID_PARAMETER;
if (!m_bInTransaction)
return ERROR_INVALID_OPERATION;
// Allocate some memory to hold the page, since we are reading
// the caller's copy and not acquiring it.
// ============================================================
LPBYTE pPageCopy = new BYTE[m_dwPageSize];
if (pPageCopy == 0)
return ERROR_OUTOFMEMORY;
memcpy(pPageCopy, pPage, m_dwPageSize);
std::auto_ptr <BYTE> _autodelete(pPageCopy);
// Look up the page.
// =================
if (dwId >= m_aPageMapB.size())
return ERROR_INVALID_PARAMETER;
DWORD dwPhysId = m_aPageMapB[dwId];
if (dwPhysId == WMIREP_INVALID_PAGE) // Unexpected
return ERROR_GEN_FAILURE;
// See if the page has already been written within this transaction.
// =================================================================
if ((CURRENT_TRANSACTION & dwPhysId)!= 0 && dwPhysId != WMIREP_RESERVED_PAGE)
{
// Just update it again.
// =====================
dwRes = m_Cache.Write(MERE_PAGE_ID(dwPhysId), LPBYTE(pPageCopy));
if (dwRes == 0)
_autodelete.release(); // memory acquired by cache
return dwRes;
}
//Before we change anything else, lets pre-allocate any memory we may need!
if (dwPhysId != WMIREP_RESERVED_PAGE)
{
try
{
if (dwPhysId & PREVIOUS_TRANSACTION)
m_aDeferredFreeList.reserve(m_aDeferredFreeList.size()+1);
else
{
m_aReplacedPagesB.reserve(m_aReplacedPagesB.size()+1);
m_aReplacedPagesA.reserve(max(m_aReplacedPagesA.size(), m_aReplacedPagesB.size()+1));
}
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
}
// If here, we are going to have to get a new page for writing, regardless
// of any special casing. So, we'll do that part first and then decide
// what to do with the old physical page.
// ========================================================================
dwRes = AllocPhysPage(&dwNewPhysId);
if (dwRes)
{
return dwRes;
}
m_aPageMapB[dwId] = dwNewPhysId | CURRENT_TRANSACTION;
dwRes = m_Cache.Write(MERE_PAGE_ID(dwNewPhysId), LPBYTE(pPageCopy));
if (dwRes)
return dwRes;
_autodelete.release(); // Memory safely acquired by cache
// If the old page ID was WMIREP_RESERVED_PAGE, we actually were
// creating a page and there was no old page to update.
// =====================================================================
if (dwPhysId != WMIREP_RESERVED_PAGE)
{
// If here, the old page was either part of the previous checkpoint
// or the previous set of transactions (the case of rewriting in the
// current transaction was handled up above).
try
{
if (dwPhysId & PREVIOUS_TRANSACTION)
m_aDeferredFreeList.push_back(MERE_PAGE_ID(dwPhysId));
else
m_aReplacedPagesB.push_back(MERE_PAGE_ID(dwPhysId));
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
}
return dwRes;
}
//***************************************************************************
//
// CPageFile::ReclaimLogicalPages
//
// Reclaims <dwCount> contiguous logical pages from the free list, if possible.
// This is done by simply sorting the free list, and then seeing if
// any contiguous entries have successive ids.
//
// Returns NO_ERROR on success, ERROR_NOT_FOUND if no sequence could be
// found, or other errors which are considered critical.
//
// Callers verified for proper use of return code.
//
//***************************************************************************
// rev2
DWORD CPageFile::ReclaimLogicalPages(
DWORD dwCount,
DWORD *pdwId
)
{
std::vector <DWORD, wbem_allocator<DWORD> > &v = m_aLogicalFreeListB;
DWORD dwSize = v.size();
if (dwSize < dwCount)
return ERROR_NOT_FOUND;
// Special case for one page.
// ==========================
if (dwCount == 1)
{
try
{
*pdwId = v.back();
v.pop_back();
m_aPageMapB[*pdwId] = WMIREP_RESERVED_PAGE;
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
return NO_ERROR;
}
// If here, a multi-page sequence was requested.
// =============================================
ShellSort(v);
DWORD dwContiguous = 1;
DWORD dwStart = 0;
for (DWORD dwIx = 0; dwIx+1 < dwSize; dwIx++)
{
if (v[dwIx]+1 == v[dwIx+1])
{
dwContiguous++;
}
else
{
dwContiguous = 1;
dwStart = dwIx + 1;
}
// Have we achieved our goal?
if (dwContiguous == dwCount)
{
*pdwId = v[dwStart];
// Remove reclaimed pages from free list.
// ======================================
DWORD dwCount2 = dwCount;
try
{
v.erase(v.begin()+dwStart, v.begin()+dwStart+dwCount);
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
// Change entries in page map to 'reserved'
// ========================================
dwCount2 = dwCount;
for (DWORD i = *pdwId; dwCount2--; i++)
{
m_aPageMapB[i] = WMIREP_RESERVED_PAGE;
}
return NO_ERROR;
}
}
return ERROR_NOT_FOUND;
}
//***************************************************************************
//
// CPageFile::AllocPhysPage
//
// Finds a free page, first by attempting to reuse the free list,
// and only if it is zero-length by allocating a new extent to the file.
//
// The page is marked as CURRENT_TRANSACTION before being returned.
//
//***************************************************************************
// rev2
DWORD CPageFile::AllocPhysPage(DWORD *pdwId)
{
// Check the physical free list.
// =============================
if (m_aPhysFreeListB.size() == 0)
{
// No free pages. Allocate a new one.
// ===================================
if (m_dwPhysPagesB == MAX_NUM_PAGES)
{
*pdwId = WMIREP_INVALID_PAGE;
return ERROR_DISK_FULL;
}
*pdwId = m_dwPhysPagesB++;
return NO_ERROR;
}
// Get the free page id from the block nearest the start of the file.
// ==================================================================
DWORD dwCurId = (DWORD)-1;
DWORD dwCurValue = (DWORD) -1;
for (DWORD dwIndex = 0; dwIndex != m_aPhysFreeListB.size(); dwIndex++)
{
if (m_aPhysFreeListB[dwIndex] < dwCurValue)
{
dwCurValue = m_aPhysFreeListB[dwIndex];
dwCurId = dwIndex;
}
}
*pdwId = dwCurValue;
// Remove the entry from the free list.
// ====================================
m_aPhysFreeListB.erase(m_aPhysFreeListB.begin()+dwCurId);
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::NewPage
//
// Allocates one or more contiguous logical page ids for writing.
//
// This function makes no reference or use of physical pages.
//
// First examines the free list. If there aren't any, then a new range
// of ids is assigned. These pages must be freed, even if they aren't
// written, once this call is completed.
//
//***************************************************************************
// rev2
DWORD CPageFile::NewPage(
DWORD dwFlags,
DWORD dwRequestedCount,
DWORD *pdwFirstId
)
{
DWORD dwRes;
if (!m_bInTransaction)
return ERROR_INVALID_OPERATION;
// See if the logical free list can satisfy the request.
// =====================================================
dwRes = ReclaimLogicalPages(dwRequestedCount, pdwFirstId);
if (dwRes == NO_ERROR)
return NO_ERROR;
if (dwRes != ERROR_NOT_FOUND)
{
return dwRes;
}
// If here, we have to allocate new pages altogether.
// We do this by adding them to the map as 'reserved'
// pages.
// ===================================================
//reserve the space up front
try
{
m_aPageMapB.reserve(m_aPageMapB.size() + dwRequestedCount);
m_aPageMapA.reserve(max(m_aPageMapA.size(), m_aPageMapB.size()+dwRequestedCount));
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
DWORD dwStart = m_aPageMapB.size();
for (DWORD dwIx = 0; dwIx < dwRequestedCount; dwIx++)
{
try
{
m_aPageMapB.push_back(WMIREP_RESERVED_PAGE);
}
catch(CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
}
*pdwFirstId = dwStart;
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::CPageFile
//
//***************************************************************************
// rev2
CPageFile::CPageFile(const wchar_t *wszStoreName)
: m_wszStoreName(wszStoreName), m_Cache(wszStoreName)
{
m_lRef = 1;
m_dwPageSize = 0;
m_dwCacheSpillRatio = 0;
m_bInTransaction = 0;
m_dwLastCheckpoint = GetCurrentTime();
m_dwTransVersion = 0;
m_dwPhysPagesA = 0;
m_dwPhysPagesB = 0;
m_bCsInit = false;
}
//***************************************************************************
//
// CPageFile::~CPageFile
//
//***************************************************************************
// rev2
CPageFile::~CPageFile()
{
if (m_bCsInit)
DeleteCriticalSection(&m_cs);
}
//***************************************************************************
//
// FileExists
//
//***************************************************************************
// rev2
BOOL CPageSource::FileExists(LPCWSTR pszFile, NTSTATUS& Status )
{
if (!NT_SUCCESS(Status)) return FALSE;
UNICODE_STRING PathName;
FILE_BASIC_INFORMATION BasicInfo;
OBJECT_ATTRIBUTES Obja;
CFileName PreFixPath;
if (PreFixPath == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES ;
return FALSE;
}
StringCchCopyW(PreFixPath,PreFixPath.Length(), L"\\??\\");
StringCchCopyW(PreFixPath+4,PreFixPath.Length()-4,pszFile);
Status = RtlInitUnicodeStringEx(&PathName,PreFixPath);
if (!NT_SUCCESS(Status) )
return FALSE;
InitializeObjectAttributes(
&Obja,
&PathName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
GETTIME(Counter::OpTypeExist);
Status = NtQueryAttributesFile(&Obja,&BasicInfo);
if (NT_SUCCESS(Status) )
return TRUE;
else if (STATUS_OBJECT_NAME_NOT_FOUND == Status)
Status = STATUS_SUCCESS;
else
{
#ifdef DBG
DbgPrintfA(0,"NtQueryAttributesFile(%S) status %d\n",pszFile,Status);
#endif
}
return FALSE;
}
struct MapProlog {
DWORD dwSig;
DWORD TransID;
DWORD PhysSize;
DWORD PageSize;
};
//***************************************************************************
//
// CPageFile::Map_Startup
//
// Since this can only happen *after* a successful checkpoint or
// a reboot, we won't set any internal status, as it doesn't affect
// the page set. It only affects rollforward/rollback at the checkpoint
// level.
//
//***************************************************************************
// rev2
DWORD CPageFile::RollForwardV1Maps(WString& sDirectory)
{
WString sSemaphore;
WString sBTreeMap;
WString sBTreeMapNew;
WString sObjMap;
WString sObjMapNew;
try
{
sSemaphore = sDirectory;
sSemaphore += L"\\ROLL_FORWARD";
sBTreeMap = sDirectory;
sBTreeMap += L"\\" ;
sBTreeMap += WMIREP_BTREE_MAP;
sBTreeMapNew = sDirectory;
sBTreeMapNew += L"\\";
sBTreeMapNew += WMIREP_BTREE_MAP_NEW;
sObjMap = sDirectory;
sObjMap += L"\\" ;
sObjMap += WMIREP_OBJECT_MAP;
sObjMapNew = sDirectory;
sObjMapNew += L"\\";
sObjMapNew += WMIREP_OBJECT_MAP_NEW;
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
// To decide what to do, we need to know which files exist and which don't.
// ========================================================================
NTSTATUS status = STATUS_SUCCESS;
BOOL bSemaphore = CPageSource::FileExists(sSemaphore,status); //(WMIREP_ROLL_FORWARD);
BOOL bExists_BTreeMap = CPageSource::FileExists(sBTreeMap,status); //(WMIREP_BTREE_MAP);
BOOL bExists_BTreeMapNew = CPageSource::FileExists(sBTreeMapNew,status); //(WMIREP_BTREE_MAP_NEW);
BOOL bExists_ObjMap = CPageSource::FileExists(sObjMap,status); //(WMIREP_OBJECT_MAP);
BOOL bExists_ObjMapNew = CPageSource::FileExists(sObjMapNew,status); //(WMIREP_OBJECT_MAP_NEW);
if (!NT_SUCCESS(status))
RtlNtStatusToDosError( status );
if (bSemaphore)
{
//Deal with foll forward of tree map file...
if (bExists_BTreeMapNew)
{
if (bExists_BTreeMap)
if (!DeleteFileW((const wchar_t *)sBTreeMap))
return GetLastError();
if (!MoveFileW((const wchar_t *)sBTreeMapNew,(const wchar_t *)sBTreeMap))
return GetLastError();
}
//Deal with foll forward of object map file...
if (bExists_ObjMapNew)
{
if (bExists_ObjMap)
if (!DeleteFileW((const wchar_t *)sObjMap))
return GetLastError();
if (!MoveFileW((const wchar_t *)sObjMapNew,(const wchar_t *)sObjMap))
return GetLastError();
}
if (!DeleteFileW((const wchar_t *)sSemaphore))
return GetLastError();
return NO_ERROR;
}
//delete any .MAP.NEW files. they might still be there if there was no semaphore
if (bExists_BTreeMapNew)
if (!DeleteFileW((const wchar_t *)sBTreeMapNew))
return GetLastError();
if (bExists_ObjMapNew)
if (!DeleteFileW((const wchar_t *)sObjMapNew))
return GetLastError();
if ( bExists_BTreeMap &&
bExists_ObjMap)
{
// this is the good case
return NO_ERROR;
}
else if (!bExists_ObjMap &&
!bExists_BTreeMap )
{
return NO_ERROR;
}
else
{
//We have MAP files and not all the data files! We need to tidy up!
if (bExists_BTreeMap)
if (!DeleteFileW((const wchar_t *)sBTreeMap))
return GetLastError();
if (bExists_ObjMap)
if (!DeleteFileW((const wchar_t *)sObjMap))
return GetLastError();
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageFile::Init
//
// If failure occurs, we assume another call will follow.
//
//***************************************************************************
// rev2
DWORD CPageFile::Init(
WString & sMainFile,
DWORD dwPageSize,
DWORD dwCacheSize,
DWORD dwCacheSpillRatio
)
{
if ( dwPageSize == 0 )
return ERROR_INVALID_PARAMETER;
if (!m_bCsInit)
{
m_bCsInit = InitializeCriticalSectionAndSpinCount(&m_cs,0)?true:false;
if (!m_bCsInit)
return ERROR_OUTOFMEMORY;
}
m_dwPageSize = dwPageSize;
m_dwCacheSpillRatio = dwCacheSpillRatio;
DWORD dwRes = m_Cache.Init((const wchar_t *)sMainFile,
m_dwPageSize,
dwCacheSize,
m_dwCacheSpillRatio);
return dwRes;
}
DWORD CPageFile::DeInit()
{
m_Cache.DeInit();
m_aPageMapA.clear();
m_aPhysFreeListA.clear();
m_aLogicalFreeListA.clear();
m_aReplacedPagesA.clear();
m_dwPhysPagesA = 0;
m_aPageMapB.clear();
m_aPhysFreeListB.clear();
m_aLogicalFreeListB.clear();
m_aReplacedPagesB.clear();
m_dwPhysPagesB = 0;
m_aDeferredFreeList.clear();
m_dwTransVersion = 0;
return ERROR_SUCCESS;
}
//***************************************************************************
//
// CPageFile::CompactPages
//
// Moves the last dwNumPages pages from the end of the file to an empty space
// somewhere earlier in the file.
//
//***************************************************************************
//
DWORD CPageFile::CompactPages(DWORD dwNumPages)
{
DWORD dwRet = NO_ERROR;
//Need to loop though this little operation for the number of pages required
//or until we exit the loop prematurely because we are compacted
for (DWORD dwIter = 0; dwIter != dwNumPages; dwIter++)
{
//Now we need to find the physical page with the highest ID as this is the
//next candidate to be moved
DWORD dwLogicalIdCandidate = 0;
DWORD dwPhysicalIdCandidate = 0;
for (DWORD dwLogicalPageId = 0; dwLogicalPageId != m_aPageMapB.size(); dwLogicalPageId++)
{
if (m_aPageMapB[dwLogicalPageId] == WMIREP_INVALID_PAGE)
continue;
if (m_aPageMapB[dwLogicalPageId] == WMIREP_RESERVED_PAGE)
continue;
if (MERE_PAGE_ID(m_aPageMapB[dwLogicalPageId]) > dwPhysicalIdCandidate)
{
//We found a candidate
dwLogicalIdCandidate = dwLogicalPageId;
dwPhysicalIdCandidate = MERE_PAGE_ID(m_aPageMapB[dwLogicalPageId]);
}
}
//Find the lowest physical page ID available for use
DWORD dwFirstPhysicalFreePage = (DWORD) -1;
for (DWORD dwIndex = 0; dwIndex != m_aPhysFreeListB.size(); dwIndex++)
{
if (m_aPhysFreeListB[dwIndex] < dwFirstPhysicalFreePage)
{
dwFirstPhysicalFreePage = m_aPhysFreeListB[dwIndex];
}
}
if (dwFirstPhysicalFreePage == (DWORD) -1)
{
//Wow! There is no free space, so we will just exist!
break;
}
if (dwFirstPhysicalFreePage > dwPhysicalIdCandidate)
{
//We are done! There are no free pages before the last
//actual physically stored page
break;
}
//If here we have work to do for this iteration.
//Just read and write the page. The write will
//go to a new page that is allocated in this earlier
//page
BYTE *pPage = new BYTE[m_dwPageSize];
if (pPage == NULL)
{
dwRet = ERROR_OUTOFMEMORY;
break;
}
std::auto_ptr <BYTE> _autodelete(pPage);
dwRet = GetPage(dwLogicalIdCandidate, 0, pPage);
if (dwRet != 0)
break;
dwRet = PutPage(dwLogicalIdCandidate, 0, pPage);
if (dwRet != 0)
break;
}
return dwRet;
}
#ifdef WMI_PRIVATE_DBG
DWORD CPageFile::DumpFileInformation(HANDLE hFile)
{
wchar_t wszDebug[60];
DWORD dwPhysicalPageId = 0;
DWORD dwLogicalPageId = 0;
for (DWORD i = 0; i != m_aPageMapA.size(); i++)
{
if ((m_aPageMapA[i] != WMIREP_INVALID_PAGE) && (dwPhysicalPageId < m_aPageMapA[i]))
{
dwPhysicalPageId = m_aPageMapA[i];
dwLogicalPageId = i;
}
}
LARGE_INTEGER dataFileSize;
dataFileSize.QuadPart = 0;
m_Cache.GetFileSize(&dataFileSize);
StringCchPrintfW(wszDebug, 60, L"%s, <0x%X> <0x%X> <0x%X%08X>\n", m_wszStoreName, dwPhysicalPageId, dwLogicalPageId, dataFileSize.HighPart, dataFileSize.LowPart);
ERRORTRACE((LOG_REPDRV, "%S", wszDebug));
OutputDebugStringW(wszDebug);
return ERROR_SUCCESS;
}
#endif
//***************************************************************************
//
// CPageFile::Dump
//
//***************************************************************************
//
void CPageFile::DumpFreeListInfo()
{
int i;
printf("------Free List Info--------\n");
printf(" Phys Free List (B) =\n");
for (i = 0; i < m_aPhysFreeListB.size(); i++)
printf(" 0x%X\n", m_aPhysFreeListB[i]);
printf(" Replaced Pages (B) =\n");
for (i = 0; i < m_aReplacedPagesB.size(); i++)
printf(" 0x%X\n", m_aReplacedPagesB[i]);
printf(" Deferred Free List =\n");
for (i = 0; i < m_aDeferredFreeList.size(); i++)
printf(" 0x%X\n", m_aDeferredFreeList[i]);
printf("-----End Free List Info -----------\n");
printf(" Logical Free List =\n");
for (i = 0; i < m_aLogicalFreeListB.size(); i++)
printf(" 0x%X\n", m_aLogicalFreeListB[i]);
printf("-----End Free List Info -----------\n");
}
//***************************************************************************
//
// CPageFile::Dump
//
//***************************************************************************
// rev2
void CPageFile::Dump(FILE *f)
{
fprintf(f, "---Page File Dump---\n");
fprintf(f, "Ref count = %d\n", m_lRef);
fprintf(f, "Page size = 0x%x\n", m_dwPageSize);
fprintf(f, "In transaction = %d\n", m_bInTransaction);
fprintf(f, "Time since last checkpoint = %d\n", GetCurrentTime() - m_dwLastCheckpoint);
fprintf(f, "Transaction version = %d\n", m_dwTransVersion);
fprintf(f, " ---Logical Page Map <Generation A>---\n");
fprintf(f, " Phys pages = %d\n", m_dwPhysPagesA);
int i;
for (i = 0; i < m_aPageMapA.size(); i++)
fprintf(f, " Page[%d] = phys id 0x%x (%d)\n", i, m_aPageMapA[i], m_aPageMapA[i]);
fprintf(f, " ---<Generation A Physical Free List>---\n");
for (i = 0; i < m_aPhysFreeListA.size(); i++)
fprintf(f, " phys free = %d\n", m_aPhysFreeListA[i]);
fprintf(f, " ---<Generation A Logical Free List>---\n");
for (i = 0; i < m_aLogicalFreeListA.size(); i++)
fprintf(f, " logical free = %d\n", m_aLogicalFreeListA[i]);
fprintf(f, " ---<Generation A Replaced Page List>---\n");
for (i = 0; i < m_aReplacedPagesA.size(); i++)
fprintf(f, " replaced = %d\n", m_aReplacedPagesA[i]);
fprintf(f, " ---END Generation A mapping---\n");
fprintf(f, " ---Logical Page Map <Generation B>---\n");
fprintf(f, " Phys pages = %d\n", m_dwPhysPagesB);
for (i = 0; i < m_aPageMapB.size(); i++)
fprintf(f, " Page[%d] = phys id 0x%x (%d)\n", i, m_aPageMapB[i], m_aPageMapB[i]);
fprintf(f, " ---<Generation B Physical Free List>---\n");
for (i = 0; i < m_aPhysFreeListB.size(); i++)
fprintf(f, " phys free = %d\n", m_aPhysFreeListB[i]);
fprintf(f, " ---<Generation B Logical Free List>---\n");
for (i = 0; i < m_aLogicalFreeListB.size(); i++)
fprintf(f, " logical free = %d\n", m_aLogicalFreeListB[i]);
fprintf(f, " ---<Generation B Replaced Page List>---\n");
for (i = 0; i < m_aReplacedPagesB.size(); i++)
fprintf(f, " replaced = %d\n", m_aReplacedPagesB[i]);
fprintf(f, " ---END Generation B mapping---\n");
fprintf(f, "END Page File Dump\n");
}
//***************************************************************************
//
// CPageSource::GetBTreePageFile
//
//***************************************************************************
// rev2
DWORD CPageSource::GetBTreePageFile(OUT CPageFile **pPF)
{
*pPF = &m_BTreePF;
m_BTreePF.AddRef();
return NO_ERROR;
}
//***************************************************************************
//
// CPageSource::GetObjectHeapPageFile
//
//***************************************************************************
// rev2
DWORD CPageSource::GetObjectHeapPageFile(OUT CPageFile **pPF)
{
*pPF = &m_ObjPF;
m_ObjPF.AddRef();
return NO_ERROR;
}
//***************************************************************************
//
// CPageSource::BeginTrans
//
// If either object gets messed up due to out-of-mem, out-of-disk for
// the cache, etc., then error codes will be returned. Calling this
// forever won't help anything. Rollback may help, but RollbackCheckpoint
// is likely required.
//
//*************************************************************************** //
// rev2
DWORD CPageSource::BeginTrans()
{
DWORD dwRes;
if (m_dwStatus)
return m_dwStatus;
dwRes = m_ObjPF.Trans_Begin();
if (dwRes)
{
_ASSERT(0, L"WinMgmt: BeginTrans failed because of object store\n");
return dwRes;
}
dwRes = m_BTreePF.Trans_Begin();
if (dwRes)
{
_ASSERT(0, L"WinMgmt: BeginTrans failed because of BTree store\n");
return dwRes;
}
return NO_ERROR;
}
//***************************************************************************
//
// CPageSource::Init
//
// Called once at startup
//
//***************************************************************************
//
DWORD CPageSource::Init()
{
DWORD dwRes;
wchar_t *p1 = 0, *p2 = 0;
p1 = new wchar_t[MAX_PATH+1];
if (!p1)
return ERROR_OUTOFMEMORY;
std::auto_ptr <wchar_t> _1(p1);
p2 = new wchar_t[MAX_PATH+1];
if (!p2)
return ERROR_OUTOFMEMORY;
std::auto_ptr <wchar_t> _2(p2);
// Set up working directory, filenames, etc.
// =========================================
HKEY hKey;
long lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
0, KEY_READ, &hKey);
if (lRes)
return ERROR_GEN_FAILURE;
DWORD dwLen = MAX_PATH*2; // in bytes
lRes = RegQueryValueExW(hKey, L"Repository Directory", NULL, NULL,
(LPBYTE)(wchar_t*)p1, &dwLen);
ExpandEnvironmentStringsW(p1, p2, MAX_PATH);
try
{
m_sDirectory = p2;
m_sDirectory += L"\\FS";
m_FileMainData = m_sDirectory;
m_FileMainData += L"\\";
m_FileMainData += WMIREP_OBJECT_DATA;
m_FileMainBtr = m_sDirectory;
m_FileMainBtr += L"\\";
m_FileMainBtr += WMIREP_BTREE_DATA;
m_FileMap1= m_sDirectory;
m_FileMap1 += L"\\";
m_FileMap1 += WMIREP_MAP_1;
m_FileMap2 = m_sDirectory;
m_FileMap2 += L"\\";
m_FileMap2 += WMIREP_MAP_2;
m_FileMapVer = m_sDirectory;
m_FileMapVer += L"\\";
m_FileMapVer += WMIREP_MAP_VER;
}
catch (CX_MemoryException &)
{
RegCloseKey(hKey);
return ERROR_OUTOFMEMORY;
}
// Read cache settings.
// ====================
m_dwPageSize = WMIREP_PAGE_SIZE;
m_dwCacheSize = 32;
m_dwCacheSpillRatio = 4;
DWORD dwTemp = 0;
dwLen = sizeof(DWORD);
lRes = RegQueryValueExW(hKey, L"Repository Page Size", NULL, NULL,
(LPBYTE)&dwTemp, &dwLen);
if (lRes == 0)
m_dwPageSize = dwTemp;
dwLen = sizeof(DWORD);
lRes = RegQueryValueExW(hKey, L"Repository Cache Size", NULL, NULL,
(LPBYTE)&dwTemp, &dwLen);
if (lRes == 0)
m_dwCacheSize = dwTemp;
dwLen = sizeof(DWORD);
lRes = RegQueryValueExW(hKey, L"Repository Cache Spill Ratio", NULL, NULL,
(LPBYTE)&dwTemp, &dwLen);
if (lRes == 0)
m_dwCacheSpillRatio = dwTemp;
RegCloseKey(hKey);
dwRes = Startup();
return dwRes;
}
//***************************************************************************
//
// CPageSource::Shutdown
//
//***************************************************************************
// rev2
DWORD CPageSource::Shutdown(DWORD dwFlags)
{
DWORD dwRes = Checkpoint(false);
m_ObjPF.DeInit();
m_BTreePF.DeInit();
m_ObjPF.Release();
m_BTreePF.Release();
CloseMapFiles();
return dwRes;
}
//***************************************************************************
//
// CPageSource::CommitTrans
//
//***************************************************************************
// rev2
DWORD CPageSource::CommitTrans()
{
DWORD dwRes;
dwRes = m_ObjPF.Trans_Commit();
if (dwRes)
{
_ASSERT(0, L"WinMgmt: CommitTrans failed in object store\n");
return dwRes;
}
dwRes = m_BTreePF.Trans_Commit();
if (dwRes)
{
_ASSERT(0, L"WinMgmt: CommitTrans failed in BTree store\n");
return dwRes;
}
// at this point only increment the Transaction Version
m_BTreePF.IncrementTransVersion();
m_ObjPF.IncrementTransVersion();
if (m_BTreePF.GetTransVersion() != m_ObjPF.GetTransVersion())
{
_ASSERT(0, L"WinMgmt: CommitTrans failed due to transaction missmatch\n");
return ERROR_REVISION_MISMATCH;
}
return dwRes;
}
//***************************************************************************
//
// CPageSource::RollbackTrans
//
// This needs to succeed and clear out the out-of-memory status flag
// once it does.
//
//***************************************************************************
// rev2
DWORD CPageSource::RollbackTrans()
{
DWORD dwRes;
dwRes = m_ObjPF.Trans_Rollback();
if (dwRes)
{
_ASSERT(0, L"WinMgmt: RollbackTrans failed because of object store\n");
return dwRes;
}
dwRes = m_BTreePF.Trans_Rollback();
if (dwRes)
{
_ASSERT(0, L"WinMgmt: RollbackTrans failed because of BTree store\n");
return dwRes;
}
if (m_BTreePF.GetTransVersion() != m_ObjPF.GetTransVersion())
{
_ASSERT(0, L"WinMgmt: RollbackTrans failed because of transaction missmatch\n");
return ERROR_REVISION_MISMATCH;
}
return dwRes;
}
//***************************************************************************
//
// CPageSource::Checkpoint
//
//***************************************************************************
//
DWORD CPageSource::Checkpoint(bool bCompactPages)
{
DWORD dwRes = 0;
m_dwStatus = 0;
if (bCompactPages)
{
CompactPages(10);
}
DWORD dwNextFileMapVer = m_dwFileMapVer==1?2:1;
HANDLE hNextMapFile = m_dwFileMapVer==1?m_hFileMap2:m_hFileMap1;
//Lets move to the start of the file we are going to write to during this operations
LARGE_INTEGER pos;
pos.QuadPart = 0;
if (SetFilePointerEx(hNextMapFile, pos, 0, FILE_BEGIN) == 0)
return m_dwStatus = GetLastError();
dwRes = m_ObjPF.Trans_Checkpoint(hNextMapFile);
if (dwRes)
return m_dwStatus = dwRes;
dwRes = m_BTreePF.Trans_Checkpoint(hNextMapFile);
if (dwRes)
return m_dwStatus = dwRes;
if (FlushFileBuffers(hNextMapFile) == 0)
return m_dwStatus = GetLastError();
if (m_BTreePF.GetTransVersion() != m_ObjPF.GetTransVersion())
{
return m_dwStatus = ERROR_REVISION_MISMATCH;
}
DWORD dwNumBytesWritten = 0;
if (SetFilePointerEx(m_hFileMapVer, pos, 0, FILE_BEGIN) == 0)
return m_dwStatus = GetLastError();
if ((WriteFile(m_hFileMapVer, &dwNextFileMapVer, sizeof(dwNextFileMapVer), &dwNumBytesWritten, NULL) == 0) ||
dwNumBytesWritten != sizeof(dwNextFileMapVer))
return m_dwStatus = GetLastError();
if (FlushFileBuffers(m_hFileMapVer) == 0)
return m_dwStatus = GetLastError();
//Flip to the new version of the MAP file
m_dwFileMapVer = dwNextFileMapVer;
m_dwLastCheckpoint = GetCurrentTime();
return NO_ERROR;
}
//***************************************************************************
//
// CPageSource::Restart
//
//***************************************************************************
//
DWORD CPageSource::Startup()
{
DWORD dwRes = ERROR_SUCCESS;
bool bReadMapFiles = true;
bool bV2RepositoryExists = true;
bool bV1RepositoryExists = false;
bool bRecoveredRepository = false;
// Do rollback or rollforward, depending on last system status.
// ======================================
if (dwRes == ERROR_SUCCESS)
dwRes = V2ReposititoryExists(&bV2RepositoryExists);
if (dwRes == ERROR_SUCCESS)
dwRes = V1ReposititoryExists(&bV1RepositoryExists);
if ((dwRes == ERROR_SUCCESS) && bV1RepositoryExists && bV2RepositoryExists)
{
ERRORTRACE((LOG_REPDRV, "New and old repository MAP files existed so we deleted the repository\n"));
dwRes= DeleteRepository();
}
else if ((dwRes == ERROR_SUCCESS) && !bV2RepositoryExists && bV1RepositoryExists)
{
dwRes = CPageFile::RollForwardV1Maps(m_sDirectory);
if (dwRes == ERROR_SUCCESS)
{
ERRORTRACE((LOG_REPDRV, "Repository version 1 MAP files are being upgraded to version 2\n"));
dwRes = UpgradeV1Maps();
if (dwRes != ERROR_SUCCESS)
{
ERRORTRACE((LOG_REPDRV, "Repository upgrade of the MAP files failed. Deleting repository\n"));
dwRes= DeleteRepository();
}
}
else
{
ERRORTRACE((LOG_REPDRV, "Repository roll-forward of V1 MAPs failed. Deleting repository\n"));
dwRes= DeleteRepository();
}
}
StartupRecovery: //Calls here if we needed to delete the repositorty!
if (dwRes == ERROR_SUCCESS)
{
dwRes = OpenMapFiles();
//This special case failure means we have not written to the file yet!
if (dwRes == ERROR_FILE_NOT_FOUND)
{
bReadMapFiles = false;
dwRes = ERROR_SUCCESS;
}
}
if (dwRes == ERROR_SUCCESS)
{
dwRes = m_ObjPF.Init(
m_FileMainData,
m_dwPageSize,
m_dwCacheSize,
m_dwCacheSpillRatio
);
}
if (dwRes == ERROR_SUCCESS)
{
dwRes = m_BTreePF.Init(
m_FileMainBtr,
m_dwPageSize,
m_dwCacheSize,
m_dwCacheSpillRatio);
}
if (bReadMapFiles)
{
if (dwRes == ERROR_SUCCESS)
dwRes = m_ObjPF.ReadMap(m_dwFileMapVer==1?m_hFileMap1:m_hFileMap2);
if (dwRes == ERROR_SUCCESS)
dwRes = m_BTreePF.ReadMap(m_dwFileMapVer==1?m_hFileMap1:m_hFileMap2);
}
if (dwRes != ERROR_SUCCESS)
{
m_ObjPF.DeInit();
m_BTreePF.DeInit();
CloseMapFiles();
}
if ((dwRes == ERROR_INTERNAL_DB_CORRUPTION) && !bRecoveredRepository)
{
bRecoveredRepository = true;
dwRes = DeleteRepository();
if (dwRes == ERROR_SUCCESS)
goto StartupRecovery;
}
return dwRes;
}
//***************************************************************************
//
// CPageSource::Dump
//
//***************************************************************************
// rev2
void CPageSource::Dump(FILE *f)
{
// no impl
}
//***************************************************************************
//
// CPageSource::CPageSource
//
//***************************************************************************
// rev2
CPageSource::CPageSource()
: m_BTreePF(L"BTree Store"), m_ObjPF(L"Object Store")
{
m_dwStatus = 0;
m_dwPageSize = 0;
m_dwLastCheckpoint = GetCurrentTime();
}
//***************************************************************************
//
// CPageSource::~CPageSource
//
//***************************************************************************
// rev2
CPageSource::~CPageSource()
{
m_BTreePF.Release();
m_ObjPF.Release();
}
//***************************************************************************
//
// CPageSource::EmptyCaches
//
//***************************************************************************
// rev2
DWORD CPageSource::EmptyCaches()
{
DWORD dwRet = ERROR_SUCCESS;
dwRet = m_BTreePF.EmptyCache();
if (dwRet == ERROR_SUCCESS)
dwRet = m_ObjPF.EmptyCache();
return dwRet;
}
//***************************************************************************
//
// CPageSource::CompactPages
//
// Takes the last n pages and shuffles them up to the start of the file.
// Pre-conditions:
// * Write lock held on the repository
// * No active read or write operations (all transactions finished)
// * No unflushed operations, or check-points (although not totally necessary!)
//
//***************************************************************************
HRESULT CPageSource::CompactPages(DWORD dwNumPages)
{
DWORD dwRet = ERROR_SUCCESS;
dwRet = BeginTrans();
if (dwRet == ERROR_SUCCESS)
{
if (dwRet == ERROR_SUCCESS)
dwRet = m_BTreePF.CompactPages(dwNumPages);
if (dwRet == ERROR_SUCCESS)
dwRet = m_ObjPF.CompactPages(dwNumPages);
if (dwRet == ERROR_SUCCESS)
{
dwRet = CommitTrans();
}
else
{
dwRet = RollbackTrans();
}
}
return dwRet;
}
//***************************************************************************
//
// CPageSource::OpenMapFiles
//
//***************************************************************************
//
DWORD CPageSource::OpenMapFiles()
{
m_hFileMap1 = CreateFileW(m_FileMap1, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, &g_SA,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (m_hFileMap1 == INVALID_HANDLE_VALUE)
return GetLastError();
m_hFileMap2 = CreateFileW(m_FileMap2, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, &g_SA,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (m_hFileMap2 == INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFileMap1);
m_hFileMap1 = INVALID_HANDLE_VALUE;
return GetLastError();
}
m_hFileMapVer = CreateFileW(m_FileMapVer, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, &g_SA,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (m_hFileMapVer == INVALID_HANDLE_VALUE)
{
CloseHandle(m_hFileMap1);
m_hFileMap1 = INVALID_HANDLE_VALUE;
CloseHandle(m_hFileMap2);
m_hFileMap2 = INVALID_HANDLE_VALUE;
return GetLastError();
}
DWORD dwNumBytesRead = 0;
m_dwFileMapVer = 1;
if (!ReadFile(m_hFileMapVer, &m_dwFileMapVer, sizeof(m_dwFileMapVer), &dwNumBytesRead, NULL))
{
CloseHandle(m_hFileMap1);
m_hFileMap1 = INVALID_HANDLE_VALUE;
CloseHandle(m_hFileMap2);
m_hFileMap2 = INVALID_HANDLE_VALUE;
return GetLastError();
}
else if (dwNumBytesRead == 0)
return ERROR_FILE_NOT_FOUND;
else if (dwNumBytesRead != sizeof(m_dwFileMapVer))
{
CloseHandle(m_hFileMap1);
m_hFileMap1 = INVALID_HANDLE_VALUE;
CloseHandle(m_hFileMap2);
m_hFileMap2 = INVALID_HANDLE_VALUE;
return ERROR_FILE_INVALID;
}
return ERROR_SUCCESS;
}
//***************************************************************************
//
// CPageSource::CloseMapFiles
//
//***************************************************************************
//
DWORD CPageSource::CloseMapFiles()
{
if (m_hFileMap1 != INVALID_HANDLE_VALUE)
CloseHandle(m_hFileMap1);
if (m_hFileMap2 != INVALID_HANDLE_VALUE)
CloseHandle(m_hFileMap2);
if (m_hFileMapVer != INVALID_HANDLE_VALUE)
CloseHandle(m_hFileMapVer);
m_hFileMap1 = INVALID_HANDLE_VALUE;
m_hFileMap2 = INVALID_HANDLE_VALUE;
m_hFileMapVer = INVALID_HANDLE_VALUE;
return ERROR_SUCCESS;
}
//***************************************************************************
//
// CPageSource::V1ReposititoryExists
//
// Need to check if the V1 MAP files exist.
// WMIREP_OBJECT_MAP
// WMIREP_OBJECT_MAP_NEW
// WMIREP_BTREE_MAP
// WMIREP_BTREE_MAP_NEW
// WMIREP_ROLL_FORWARD
// If any of these exist then we return success
//
//***************************************************************************
DWORD CPageSource::V1ReposititoryExists(bool *pbV1RepositoryExists)
{
WString sSemaphore;
WString sBTreeMap;
WString sBTreeMapNew;
WString sObjMap;
WString sObjMapNew;
try
{
sSemaphore = m_sDirectory;
sSemaphore += L"\\ROLL_FORWARD";
sBTreeMap = m_sDirectory;
sBTreeMap += L"\\" ;
sBTreeMap += WMIREP_BTREE_MAP;
sBTreeMapNew = m_sDirectory;
sBTreeMapNew += L"\\";
sBTreeMapNew += WMIREP_BTREE_MAP_NEW;
sObjMap = m_sDirectory;
sObjMap += L"\\" ;
sObjMap += WMIREP_OBJECT_MAP;
sObjMapNew = m_sDirectory;
sObjMapNew += L"\\";
sObjMapNew += WMIREP_OBJECT_MAP_NEW;
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
NTSTATUS status = STATUS_SUCCESS;
BOOL bSemaphore = FileExists(sSemaphore, status); //(WMIREP_ROLL_FORWARD);
BOOL bExists_BTreeMap = FileExists(sBTreeMap, status); //(WMIREP_BTREE_MAP);
BOOL bExists_BTreeMapNew = FileExists(sBTreeMapNew, status); //(WMIREP_BTREE_MAP_NEW);
BOOL bExists_ObjMap = FileExists(sObjMap, status); //(WMIREP_OBJECT_MAP);
BOOL bExists_ObjMapNew = FileExists(sObjMapNew, status); //(WMIREP_OBJECT_MAP_NEW);
if (bSemaphore|bExists_BTreeMap|bExists_BTreeMapNew|bExists_ObjMap|bExists_ObjMapNew)
*pbV1RepositoryExists = true;
else
*pbV1RepositoryExists = false;
return status;
}
//***************************************************************************
//
// CPageSource::V2ReposititoryExists
//
// Need to check if the V2 MAP files exist.
// WMIREP_MAP_1
// WMIREP_MAP_2
// WMIREP_MAP_VER
// If any of these exist then we return success
//
//***************************************************************************
DWORD CPageSource::V2ReposititoryExists(bool *pbV2RepositoryExists)
{
NTSTATUS status = STATUS_SUCCESS;
BOOL bExists_Map1 = FileExists(m_FileMap1, status);
BOOL bExists_Map2 = FileExists(m_FileMap2, status);
BOOL bExists_MapVer = FileExists(m_FileMapVer, status);
if (bExists_Map1|bExists_Map2|bExists_MapVer)
*pbV2RepositoryExists = true;
else
*pbV2RepositoryExists = false;
return status;
}
//***************************************************************************
//
// CPageSource::UpgradeV1ToV2Maps
//
// Merges the 2 MAP files into 1, and creates a blank second one
//
//***************************************************************************
DWORD CPageSource::UpgradeV1Maps()
{
DWORD dwRes = 0;
//Open the two old files
WString sBTreeMap;
WString sObjMap;
try
{
sBTreeMap = m_sDirectory;
sBTreeMap += L"\\" ;
sBTreeMap += WMIREP_BTREE_MAP;
sObjMap = m_sDirectory;
sObjMap += L"\\" ;
sObjMap += WMIREP_OBJECT_MAP;
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
HANDLE hFileBtreeMap = CreateFile(sBTreeMap, GENERIC_READ, FILE_SHARE_READ, &g_SA, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
HANDLE hFileObjMap = CreateFile(sObjMap, GENERIC_READ, FILE_SHARE_READ, &g_SA, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if ((hFileBtreeMap == INVALID_HANDLE_VALUE) || (hFileObjMap == INVALID_HANDLE_VALUE))
dwRes = ERROR_GEN_FAILURE;
//Open the two new files
if (dwRes == ERROR_SUCCESS)
{
dwRes = OpenMapFiles();
if (dwRes != ERROR_FILE_NOT_FOUND)
{
//It should not be there!
dwRes = ERROR_GEN_FAILURE;
}
else
dwRes = 0;
}
if (dwRes == ERROR_SUCCESS)
{
dwRes = m_ObjPF.Init(
m_FileMainData,
m_dwPageSize,
m_dwCacheSize,
m_dwCacheSpillRatio
);
}
if (dwRes == ERROR_SUCCESS)
{
dwRes = m_BTreePF.Init(
m_FileMainBtr,
m_dwPageSize,
m_dwCacheSize,
m_dwCacheSpillRatio);
}
//read the maps using the old ones
if (dwRes == ERROR_SUCCESS)
dwRes = m_ObjPF.ReadMap(hFileObjMap);
if (dwRes == ERROR_SUCCESS)
dwRes = m_BTreePF.ReadMap(hFileBtreeMap);
//write the maps using the new ones
LARGE_INTEGER pos;
pos.QuadPart = 0;
if (dwRes == ERROR_SUCCESS)
if (SetFilePointerEx(m_hFileMap1, pos, 0, FILE_BEGIN) == 0)
dwRes = GetLastError();
if (dwRes == ERROR_SUCCESS)
dwRes = m_ObjPF.WriteMap(m_hFileMap1);
if (dwRes == ERROR_SUCCESS)
dwRes = m_BTreePF.WriteMap(m_hFileMap1);
if ((dwRes == ERROR_SUCCESS) && (FlushFileBuffers(m_hFileMap1) == 0))
dwRes = GetLastError();
DWORD dwNumBytesWritten = 0;
if (dwRes == ERROR_SUCCESS)
if (SetFilePointerEx(m_hFileMapVer, pos, 0, FILE_BEGIN) == 0)
dwRes = GetLastError();
DWORD dwNextFileMapVer = 1;
if (dwRes == ERROR_SUCCESS)
if ((WriteFile(m_hFileMapVer, &dwNextFileMapVer, sizeof(dwNextFileMapVer), &dwNumBytesWritten, NULL) == 0) ||
dwNumBytesWritten != sizeof(dwNextFileMapVer))
dwRes = GetLastError();
if (dwRes == ERROR_SUCCESS)
if (FlushFileBuffers(m_hFileMapVer) == 0)
dwRes = GetLastError();
//close the files
CloseMapFiles();
m_hFileMap1 = INVALID_HANDLE_VALUE;
m_hFileMap2 = INVALID_HANDLE_VALUE;
if (hFileBtreeMap != INVALID_HANDLE_VALUE)
CloseHandle(hFileBtreeMap);
if (hFileObjMap != INVALID_HANDLE_VALUE)
CloseHandle(hFileObjMap);
hFileBtreeMap = INVALID_HANDLE_VALUE;
hFileObjMap = INVALID_HANDLE_VALUE;
m_ObjPF.DeInit();
m_BTreePF.DeInit();
if (dwRes != ERROR_SUCCESS)
{
//If we failed, delete the new MAP files to keep things consistent
DeleteFile(m_FileMap1);
DeleteFile(m_FileMap2);
DeleteFile(m_FileMapVer);
}
else
{
//otherwise delete the new files
DeleteFile(sBTreeMap);
DeleteFile(sObjMap);
}
//clear all the structures
return dwRes;
}
DWORD CPageSource::DeleteRepository()
{
if ((!DeleteFileW(m_FileMainData)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(m_FileMainBtr)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(m_FileMap1)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(m_FileMap2)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(m_FileMapVer)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
//Lets also delete the old MAP files too! Just in case!
WString sSemaphore;
WString sBTreeMap;
WString sBTreeMapNew;
WString sObjMap;
WString sObjMapNew;
try
{
sSemaphore = m_sDirectory;
sSemaphore += L"\\ROLL_FORWARD";
sBTreeMap = m_sDirectory;
sBTreeMap += L"\\" ;
sBTreeMap += WMIREP_BTREE_MAP;
sBTreeMapNew = m_sDirectory;
sBTreeMapNew += L"\\";
sBTreeMapNew += WMIREP_BTREE_MAP_NEW;
sObjMap = m_sDirectory;
sObjMap += L"\\" ;
sObjMap += WMIREP_OBJECT_MAP;
sObjMapNew = m_sDirectory;
sObjMapNew += L"\\";
sObjMapNew += WMIREP_OBJECT_MAP_NEW;
}
catch (CX_MemoryException &)
{
return ERROR_OUTOFMEMORY;
}
if ((!DeleteFileW(sSemaphore)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(sBTreeMap)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(sBTreeMapNew)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError();
if ((!DeleteFileW(sObjMap)) && (GetLastError() != ERROR_FILE_NOT_FOUND))
return GetLastError(); if ((!DeleteFileW(sObjMapNew)) && (GetLastError() != ERROR_FILE_NOT_FOUND)) return GetLastError(); return ERROR_SUCCESS; }