716 lines
17 KiB
C++
716 lines
17 KiB
C++
/*++
|
|
|
|
Copyright (C) 2000-2001 Microsoft Corporation
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <wbemcomn.h>
|
|
#include <reposit.h>
|
|
#include "filecach.h"
|
|
#include "creposit.h"
|
|
|
|
//
|
|
// assumes inside m_cs
|
|
//
|
|
long CFileCache::InnerInitialize(LPCWSTR wszBaseName)
|
|
{
|
|
long lRes;
|
|
|
|
wcscpy(m_wszBaseName, wszBaseName);
|
|
wcscat(m_wszBaseName, L"\\");
|
|
m_dwBaseNameLen = wcslen(m_wszBaseName);
|
|
|
|
//
|
|
// Read the maximum stage-file size from the registry
|
|
//
|
|
|
|
HKEY hKey;
|
|
lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
|
|
L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
|
|
0, KEY_READ | KEY_WRITE, &hKey);
|
|
if(lRes)
|
|
return lRes;
|
|
CRegCloseMe cm(hKey);
|
|
|
|
DWORD dwLen = sizeof(DWORD);
|
|
DWORD dwMaxLen;
|
|
lRes = RegQueryValueExW(hKey, L"Max Stage File Size", NULL, NULL,
|
|
(LPBYTE)&dwMaxLen, &dwLen);
|
|
|
|
//
|
|
// If not there, set to default and write the default into the registry
|
|
//
|
|
|
|
if(lRes != ERROR_SUCCESS)
|
|
{
|
|
dwMaxLen = 5000000;
|
|
lRes = RegSetValueExW(hKey, L"Max Stage File Size", 0, REG_DWORD,
|
|
(LPBYTE)&dwMaxLen, sizeof(DWORD));
|
|
}
|
|
|
|
dwLen = sizeof(DWORD);
|
|
DWORD dwAbortTransactionLen;
|
|
lRes = RegQueryValueExW(hKey, L"Absolute Max Stage File Size", NULL, NULL,
|
|
(LPBYTE)&dwAbortTransactionLen, &dwLen);
|
|
|
|
//
|
|
// If not there, set to default and write the default into the registry
|
|
//
|
|
|
|
if(lRes != ERROR_SUCCESS || dwAbortTransactionLen == dwMaxLen * 10)
|
|
{
|
|
dwAbortTransactionLen = 0x7FFFFFFF;
|
|
lRes = RegSetValueExW(hKey, L"Absolute Max Stage File Size", 0,
|
|
REG_DWORD, (LPBYTE)&dwAbortTransactionLen, sizeof(DWORD));
|
|
}
|
|
|
|
if(dwMaxLen == 0)
|
|
{
|
|
//
|
|
// Staged writes are disabled!
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Create the main staging area
|
|
//
|
|
|
|
CFileName wszStagingName;
|
|
if (wszStagingName == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
swprintf(wszStagingName, L"%s\\LowStage.dat", wszBaseName);
|
|
|
|
lRes = m_AbstractSource.Create(wszStagingName, dwMaxLen,
|
|
dwAbortTransactionLen);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
}
|
|
|
|
CFileName wszObjHeapName;
|
|
if(wszObjHeapName == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
swprintf(wszObjHeapName, L"%s\\ObjHeap", wszBaseName);
|
|
|
|
lRes = m_ObjectHeap.Initialize(&m_AbstractSource,
|
|
(WCHAR*)wszObjHeapName,
|
|
(WCHAR*)wszBaseName,
|
|
m_dwBaseNameLen);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
|
|
lRes = m_AbstractSource.Start();
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// assumes inside m_cs
|
|
//
|
|
long CFileCache::RepositoryExists(LPCWSTR wszBaseName)
|
|
{
|
|
CFileName wszStagingName;
|
|
if (wszStagingName == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
|
|
swprintf(wszStagingName, L"%s\\LowStage.dat", wszBaseName);
|
|
|
|
DWORD dwAttributes = GetFileAttributesW(wszStagingName);
|
|
if (dwAttributes == -1)
|
|
return ERROR_FILE_NOT_FOUND;
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
long CFileCache::Initialize(LPCWSTR wszBaseName)
|
|
{
|
|
long lRes;
|
|
|
|
CInCritSec ics(&m_cs);
|
|
if (m_bInit)
|
|
return ERROR_SUCCESS;
|
|
|
|
lRes = RepositoryExists(wszBaseName);
|
|
if (ERROR_FILE_NOT_FOUND == lRes)
|
|
{
|
|
//If we have a database restore to do, go ahead and do it...
|
|
lRes = DoAutoDatabaseRestore();
|
|
}
|
|
|
|
//
|
|
// Initialize file cache. It will read the registry itself to find out
|
|
// its size limitations
|
|
//
|
|
if (SUCCEEDED(lRes))
|
|
{
|
|
lRes = InnerInitialize(wszBaseName);
|
|
if (ERROR_SUCCESS == lRes)
|
|
{
|
|
m_bInit = TRUE;
|
|
}
|
|
}
|
|
|
|
return lRes;
|
|
}
|
|
|
|
CFileCache::CFileCache()
|
|
: m_lRef(1), m_bInit(FALSE)
|
|
{
|
|
}
|
|
|
|
CFileCache::~CFileCache()
|
|
{
|
|
}
|
|
|
|
void CFileCache::Clear(DWORD dwShutDownFlags)
|
|
{
|
|
m_AbstractSource.Stop(dwShutDownFlags);
|
|
|
|
m_ObjectHeap.Uninitialize(dwShutDownFlags);
|
|
|
|
//for(int i = 0; i < m_apStages.GetSize(); i++)
|
|
// m_apStages[i]->Stop(g_ShutDownFlags);
|
|
|
|
//if (WMIDB_SHUTDOWN_MACHINE_DOWN != g_ShutDownFlags)
|
|
//{
|
|
// m_apStages.RemoveAll();
|
|
//}
|
|
}
|
|
|
|
long
|
|
CFileCache::Uninitialize(DWORD dwShutDownFlags)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
|
|
if (!m_bInit)
|
|
return 0;
|
|
|
|
Clear(dwShutDownFlags);
|
|
|
|
m_bInit = FALSE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool CFileCache::IsFullyFlushed()
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return false;
|
|
|
|
//for(int i = 0; i < m_apStages.GetSize(); i++)
|
|
//{
|
|
// if(!m_apStages[i]->IsFullyFlushed())
|
|
// return false;
|
|
//}
|
|
return true;
|
|
}
|
|
|
|
bool CFileCache::GetFlushFailure(long* plFlushStatus)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
|
|
*plFlushStatus = 0;
|
|
//for(int i = 0; i < m_apStages.GetSize(); i++)
|
|
//{
|
|
// if(m_apStages[i]->GetFailedBefore())
|
|
// {
|
|
// *plFlushStatus = m_apStages[i]->GetStatus();
|
|
// return true;
|
|
// }
|
|
//}
|
|
return false;
|
|
}
|
|
|
|
long CFileCache::WriteFile(LPCWSTR wszFileName, DWORD dwLen, BYTE* pBuffer)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
//
|
|
// Write the file into the main staging file
|
|
//
|
|
|
|
//if(GetMainStagingFile())
|
|
//{
|
|
// return GetMainStagingFile()->WriteFile(wszFileName + m_dwBaseNameLen,
|
|
// dwLen, pBuffer);
|
|
//}
|
|
//else
|
|
{
|
|
return m_ObjectHeap.WriteFile(wszFileName, dwLen, pBuffer);
|
|
}
|
|
}
|
|
|
|
long CFileCache::DeleteFile(LPCWSTR wszFileName)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
//
|
|
// Write the file into the main staging file
|
|
//
|
|
|
|
//if(GetMainStagingFile())
|
|
//{
|
|
// return GetMainStagingFile()->DeleteFile(wszFileName + m_dwBaseNameLen);
|
|
//}
|
|
//else
|
|
{
|
|
return m_ObjectHeap.DeleteFile(wszFileName);
|
|
}
|
|
|
|
}
|
|
|
|
long CFileCache::RemoveDirectory(LPCWSTR wszFileName, bool bMustSucceed)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
//
|
|
// Write the file into the main staging file
|
|
//
|
|
|
|
//if(GetMainStagingFile())
|
|
//{
|
|
// return GetMainStagingFile()->
|
|
// RemoveDirectory(wszFileName + m_dwBaseNameLen);
|
|
//}
|
|
//else
|
|
{
|
|
// No need to remove "directories" in the index
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
HRESULT CFileCache::ReadFile(LPCWSTR wszFileName, DWORD* pdwLen,
|
|
BYTE** ppBuffer, bool bMustBeThere)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
long lRes;
|
|
|
|
//
|
|
// Search all staging files in order
|
|
//
|
|
|
|
//for(int i = 0; i < m_apStages.GetSize(); i++)
|
|
//{
|
|
// lRes = m_apStages[i]->ReadFile(wszFileName + m_dwBaseNameLen, pdwLen,
|
|
// ppBuffer, bMustBeThere);
|
|
// if(lRes != ERROR_NO_INFORMATION)
|
|
// {
|
|
// if(lRes != ERROR_SUCCESS && lRes != ERROR_FILE_NOT_FOUND &&
|
|
// lRes != ERROR_PATH_NOT_FOUND)
|
|
// {
|
|
// ERRORTRACE((LOG_WBEMCORE, "Repository driver cannot read file "
|
|
// "'%S' from the stage with error code %d\n", wszFileName,
|
|
// lRes));
|
|
// }
|
|
// return lRes;
|
|
// }
|
|
//}
|
|
|
|
//
|
|
// Not in the staging areas --- get from disk!
|
|
//
|
|
return m_ObjectHeap.ReadFile(wszFileName, pdwLen, ppBuffer);
|
|
}
|
|
|
|
|
|
long CFileCache::FindFirst(LPCWSTR wszFilePrefix, WIN32_FIND_DATAW* pfd,
|
|
void** ppHandle)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
//
|
|
// Construct an enumerator
|
|
//
|
|
|
|
CFileEnumerator* pEnum = new CFileEnumerator(this, m_dwBaseNameLen);
|
|
if (pEnum == NULL)
|
|
return E_OUTOFMEMORY;
|
|
long lRes = pEnum->GetFirst(wszFilePrefix, pfd, (ppHandle != NULL));
|
|
if(lRes != ERROR_SUCCESS)
|
|
{
|
|
delete pEnum;
|
|
return lRes;
|
|
}
|
|
|
|
if(ppHandle)
|
|
*ppHandle = (void*)pEnum;
|
|
else
|
|
delete pEnum;
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
long CFileCache::FindNext(void* pHandle, WIN32_FIND_DATAW* pfd)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
CFileEnumerator* pEnum = (CFileEnumerator*)pHandle;
|
|
return pEnum->GetNext(pfd, true);
|
|
}
|
|
|
|
|
|
void CFileCache::FindClose(void* pHandle)
|
|
{
|
|
CInCritSec ics(&m_cs);
|
|
|
|
// do not bail out if un-initialized
|
|
// we always want to free memory via the scoped obejct
|
|
|
|
delete (CFileEnumerator*)pHandle;
|
|
}
|
|
|
|
//
|
|
//
|
|
// this is a shortcut chain for going straight to the BtrIdx
|
|
//
|
|
///////////////////////////////////////////////////////////////////////
|
|
|
|
long CFileCache::FindFirstIdx(LPCWSTR wszFilePrefix, WIN32_FIND_DATAW* pfd,
|
|
void** ppHandle)
|
|
{
|
|
return m_ObjectHeap.FindFirst(wszFilePrefix, pfd, ppHandle);
|
|
}
|
|
|
|
long CFileCache::FindNextIdx(void* pHandle, WIN32_FIND_DATAW* pfd)
|
|
{
|
|
return m_ObjectHeap.FindNext(pHandle, pfd);
|
|
}
|
|
|
|
|
|
void CFileCache::FindCloseIdx(void* pHandle)
|
|
{
|
|
m_ObjectHeap.FindClose(pHandle);
|
|
}
|
|
|
|
|
|
|
|
long CFileCache::BeginTransaction()
|
|
{
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
//if(GetMainStagingFile())
|
|
// return GetMainStagingFile()->BeginTransaction();
|
|
//else
|
|
return m_AbstractSource.Begin(NULL);
|
|
}
|
|
|
|
long CFileCache::CommitTransaction()
|
|
{
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
A51TRACE(("Committing Transaction!\n"));
|
|
//if(GetMainStagingFile())
|
|
// return GetMainStagingFile()->CommitTransaction();
|
|
//else
|
|
return m_AbstractSource.Commit(0);
|
|
}
|
|
|
|
long CFileCache::AbortTransaction()
|
|
{
|
|
if (!m_bInit)
|
|
return -1;
|
|
|
|
A51TRACE(("Aborting Transaction!\n"));
|
|
//if(GetMainStagingFile())
|
|
// return GetMainStagingFile()->AbortTransaction(NULL);
|
|
//else
|
|
{
|
|
//
|
|
// Actually rollback the staging file
|
|
//
|
|
|
|
bool bNonEmpty = false;
|
|
long lRes = m_AbstractSource.Rollback(0, &bNonEmpty);
|
|
if(lRes != ERROR_SUCCESS)
|
|
return lRes;
|
|
|
|
if(bNonEmpty)
|
|
{
|
|
//
|
|
// We rolled back a non-empty transaction, which means that the
|
|
// in-memory caches (which already took the transaction into
|
|
// account) may no longer be valid. Invalidate all caches
|
|
//
|
|
|
|
m_ObjectHeap.InvalidateCache();
|
|
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
}
|
|
|
|
CFileCache::CFileEnumerator::~CFileEnumerator()
|
|
{
|
|
//if(m_pStageEnumerator)
|
|
// m_pCache->GetStageFile(m_nCurrentStage)->FindClose(m_pStageEnumerator);
|
|
|
|
if(m_hFileEnum)
|
|
m_pCache->m_ObjectHeap.FindClose(m_hFileEnum);
|
|
}
|
|
|
|
long CFileCache::CFileEnumerator::GetFirst(LPCWSTR wszPrefix,
|
|
WIN32_FIND_DATAW* pfd,
|
|
bool bNeedToContinue)
|
|
{
|
|
long lRes;
|
|
|
|
wcscpy(m_wszPrefix, wszPrefix);
|
|
|
|
WCHAR* pwcLastSlash = wcsrchr(m_wszPrefix, L'\\');
|
|
if(pwcLastSlash == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
m_dwPrefixDirLen = pwcLastSlash - m_wszPrefix;
|
|
|
|
//
|
|
// We are going to start with the first staging area
|
|
//
|
|
|
|
m_nCurrentStage = 0;
|
|
m_bUseFiles = false;
|
|
|
|
//
|
|
// Everything is set up to indicate that we are at the very beginning ---
|
|
// GetNext will retrieve the first
|
|
//
|
|
|
|
lRes = GetNext(pfd, bNeedToContinue);
|
|
|
|
//
|
|
// One last thing --- absense of files is ERROR_NO_MORE_FILES for GetNext,
|
|
// but ERROR_FILE_NOT_FOUND for GetFirst, so translate
|
|
//
|
|
|
|
if(lRes == ERROR_NO_MORE_FILES)
|
|
lRes = ERROR_FILE_NOT_FOUND;
|
|
|
|
return lRes;
|
|
}
|
|
|
|
long CFileCache::CFileEnumerator::GetFirstFile(WIN32_FIND_DATAW* pfd,
|
|
bool bNeedToContinue)
|
|
{
|
|
long lRes;
|
|
|
|
m_bUseFiles = true;
|
|
|
|
if(bNeedToContinue)
|
|
lRes = m_pCache->FindFirstIdx(m_wszPrefix, pfd, &m_hFileEnum);
|
|
else
|
|
lRes = m_pCache->FindFirstIdx(m_wszPrefix, pfd, NULL);
|
|
|
|
A51TRACE(("Actual FindFirstFileW on %S returning %p %d\n",
|
|
wszMask, (void*)m_hFileEnum, lRes));
|
|
if(lRes != ERROR_SUCCESS)
|
|
{
|
|
if(lRes == ERROR_PATH_NOT_FOUND)
|
|
return ERROR_FILE_NOT_FOUND;
|
|
else
|
|
return lRes;
|
|
}
|
|
else
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
void CFileCache::CFileEnumerator::ComputeCanonicalName(WIN32_FIND_DATAW* pfd,
|
|
wchar_t *wszFilePath)
|
|
{
|
|
wcsncpy(wszFilePath, m_wszPrefix, m_dwPrefixDirLen+1);
|
|
wbem_wcsupr(wszFilePath+m_dwPrefixDirLen+1, pfd->cFileName);
|
|
}
|
|
|
|
long CFileCache::CFileEnumerator::GetNext(WIN32_FIND_DATAW* pfd,
|
|
bool bNeedToContinue)
|
|
{
|
|
//
|
|
// Need-to-continue optimizations are not possible if staging is used at
|
|
// this level because even though the caller is only asking for one object,
|
|
// we may need to retrieve more than that from the store since some of the
|
|
// objects in the store may be overriden by the stage file deletions
|
|
//
|
|
|
|
//if(m_pCache->GetNumStages() > 0)
|
|
// bNeedToContinue = true;
|
|
|
|
long lRes;
|
|
|
|
//
|
|
// Go through the files in the enumerator until we find a new and valid one
|
|
//
|
|
|
|
while((lRes = GetRawNext(pfd, bNeedToContinue)) == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Compute the full name
|
|
//
|
|
|
|
CFileName wszFullName;
|
|
if (wszFullName == NULL)
|
|
return ERROR_OUTOFMEMORY;
|
|
ComputeCanonicalName(pfd, wszFullName);
|
|
|
|
//
|
|
// Check if it is already in our map of returned files
|
|
//
|
|
|
|
if(m_setSent.find((const wchar_t*)wszFullName) != m_setSent.end())
|
|
continue;
|
|
|
|
//
|
|
// Check if this file is deleted
|
|
//
|
|
|
|
bool bDeleted = false;
|
|
/*
|
|
for(int i = 0; i < m_nCurrentStage; i++)
|
|
{
|
|
long hres = m_pCache->GetStageFile(i)->IsDeleted(wszFullName + m_dwBaseNameLen);
|
|
if (hres == S_OK)
|
|
{
|
|
bDeleted = true;
|
|
break;
|
|
}
|
|
else if (FAILED(hres))
|
|
{
|
|
return hres;
|
|
}
|
|
}
|
|
*/
|
|
|
|
if(bDeleted)
|
|
continue;
|
|
|
|
//
|
|
// All clear!
|
|
//
|
|
|
|
if(!m_bUseFiles)
|
|
m_setSent.insert((const wchar_t*)wszFullName);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
return lRes;
|
|
}
|
|
|
|
long CFileCache::CFileEnumerator::GetRawNext(WIN32_FIND_DATAW* pfd,
|
|
bool bNeedToContinue)
|
|
{
|
|
long lRes;
|
|
|
|
if(m_bUseFiles)
|
|
{
|
|
_ASSERT(bNeedToContinue, L"Continuing without need?");
|
|
|
|
//
|
|
// Get the next file
|
|
//
|
|
lRes = m_pCache->FindNextIdx(m_hFileEnum, pfd);
|
|
if(lRes != ERROR_SUCCESS)
|
|
{
|
|
m_pCache->FindCloseIdx(m_hFileEnum);
|
|
m_hFileEnum = NULL;
|
|
return lRes;
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if we even have a stage enumerator
|
|
//
|
|
|
|
/*
|
|
if(m_pStageEnumerator)
|
|
{
|
|
_ASSERT(bNeedToContinue, L"Continuing without need?");
|
|
|
|
//
|
|
// Get the next file from the same stage
|
|
//
|
|
|
|
lRes = m_pCache->GetStageFile(m_nCurrentStage)->
|
|
FindNext(m_pStageEnumerator, pfd);
|
|
if(lRes != ERROR_NO_MORE_FILES)
|
|
return lRes;
|
|
|
|
//
|
|
// Advance to the next one
|
|
//
|
|
|
|
m_pCache->GetStageFile(m_nCurrentStage)->
|
|
FindClose(m_pStageEnumerator);
|
|
m_pStageEnumerator = NULL;
|
|
m_nCurrentStage++;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This is our first time --- we are all set up to pick up the first
|
|
// file from the first stage
|
|
//
|
|
}
|
|
*/
|
|
|
|
while(1)
|
|
{
|
|
//if(m_nCurrentStage >= m_pCache->GetNumStages())
|
|
{
|
|
//
|
|
// Go to files
|
|
//
|
|
|
|
lRes = GetFirstFile(pfd, bNeedToContinue);
|
|
if(lRes == ERROR_FILE_NOT_FOUND)
|
|
return ERROR_NO_MORE_FILES;
|
|
else
|
|
return lRes;
|
|
}
|
|
/*
|
|
else
|
|
{
|
|
_ASSERT(bNeedToContinue, L"Continuing without need?");
|
|
|
|
//
|
|
// Initialize the next stage
|
|
//
|
|
|
|
lRes = m_pCache->GetStageFile(m_nCurrentStage)->
|
|
FindFirst(m_wszPrefix + m_dwBaseNameLen, pfd, &m_pStageEnumerator);
|
|
if(lRes == ERROR_FILE_NOT_FOUND)
|
|
{
|
|
//
|
|
// This stage has nothing to contribute --- move along
|
|
//
|
|
|
|
m_nCurrentStage++;
|
|
continue;
|
|
}
|
|
else
|
|
return lRes;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|