Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

707 lines
15 KiB

//***************************************************************************
//
// (c) 2000-2001 by Microsoft Corp. All Rights Reserved.
//
// INDEX.CPP
//
// 24-Oct-00 raymcc Integration layer to disk-based B-Tree
//
//***************************************************************************
#include <wbemcomn.h>
#include <reposit.h>
#include "a51tools.h"
#include "index.h"
#include "btr.h"
//***************************************************************************
//
//***************************************************************************
//
//#define DEBUG
static CFlexArray g_aIterators;
class CIteratorBatch
{
CFlexArray m_aStrings;
BOOL m_bDone;
DWORD m_dwCursor;
public:
CIteratorBatch();
~CIteratorBatch();
BOOL Purge(LPSTR pszTarget);
BOOL Add(LPSTR pszSrc); // Acquires memory
void SetDone() { m_bDone = TRUE; }
BOOL Next(LPSTR *pString);
static DWORD PurgeAll(LPSTR pszDoomed);
};
//***************************************************************************
//
// CIteratorBatch::PurgeAll
//
//
// Purges all iterators of a particular string. This happens when
// a DeleteKey succeeds while there are outstanding enumerators; we want
// to remove the key from all enumerators so that deleted objects
// are not reported.
//
// This is required because the enumerators do "prefetch" and may
// have been invoked considerably in advance of the delete
//
// Assumes prior concurrency control.
//
//***************************************************************************
// ok
DWORD CIteratorBatch::PurgeAll(LPSTR pszDoomed)
{
DWORD dwTotal = 0;
for (int i = 0; i < g_aIterators.Size(); i++)
{
CIteratorBatch *p = (CIteratorBatch *) g_aIterators[i];
BOOL bRes = p->Purge(pszDoomed);
if (bRes)
dwTotal++;
}
return dwTotal;
}
//***************************************************************************
//
// CIteratorBatch::CIteratorBatch
//
//***************************************************************************
// ok
CIteratorBatch::CIteratorBatch()
{
m_bDone = FALSE;
m_dwCursor = 0;
g_aIterators.Add(this);
}
//***************************************************************************
//
// CIteratorBatch::Add
//
// Adds a string to the enumerator.
//
//***************************************************************************
//
BOOL CIteratorBatch::Add(LPSTR pszSrc)
{
int nRes = m_aStrings.Add(pszSrc);
if (nRes)
return FALSE;
return TRUE;
}
//***************************************************************************
//
// CIteratorBatch::~CIteratorBatch
//
// Removes all remaining strings and deallocates them and removes
// this iterator from the global list.
//
// Assumes prior concurrency control.
//
//***************************************************************************
//
CIteratorBatch::~CIteratorBatch()
{
for (int i = 0; i < m_aStrings.Size(); i++)
{
_BtrMemFree(m_aStrings[i]);
}
for (i = 0; i < g_aIterators.Size(); i++)
{
if (g_aIterators[i] == this)
{
g_aIterators.RemoveAt(i);
break;
}
}
}
//***************************************************************************
//
// CIteratorBatch::Purge
//
// Removes a specific string from the enumerator. Happens when a concurrent
// delete succeeds; we have to remove the deleted key from the enumeration
// for result set coherence.
//
// Assumes prior concurrency control.
//
// Returns FALSE if the string was not removed, TRUE if it was. The
// return value is mostly a debugging aid.
//
//***************************************************************************
// ok
BOOL CIteratorBatch::Purge(
LPSTR pszTarget
)
{
int nSize = m_aStrings.Size();
if (nSize == 0)
return FALSE;
// First, check the first/last strings against
// the first character of the target. We can
// avoid a lot of strcmp calls if the target
// is lexcially outside the range of the contents of
// the enumerator.
// ==================================================
LPSTR pszFirst = (LPSTR) m_aStrings[0];
LPSTR pszLast = (LPSTR) m_aStrings[nSize-1];
if (*pszTarget > *pszLast)
return FALSE;
if (*pszTarget < *pszFirst)
return FALSE;
// If here, there is a chance that we have the
// string in the enumerator. Since all keys are
// retrieved in lexical order, a simple binary
// search is all we need.
// =============================================
int nPosition = 0;
int l = 0, u = nSize - 1;
while (l <= u)
{
int m = (l + u) / 2;
// m is the current key to consider 0...n-1
LPSTR pszCandidate = (LPSTR) m_aStrings[m];
int nRes = strcmp(pszTarget, pszCandidate);
// Decide which way to cut the array in half.
// ==========================================
if (nRes < 0)
{
u = m - 1;
nPosition = u + 1;
}
else if (nRes > 0)
{
l = m + 1;
nPosition = l;
}
else
{
// If here, we found the darn thing. Life is good.
// Zap it and return!
// ================================================
_BtrMemFree(pszCandidate);
m_aStrings.RemoveAt(m);
return TRUE;
}
}
return FALSE;
}
//***************************************************************************
//
// CIteratorBatch::Next
//
// Returns the next string from the enumeration prefetch.
//
//***************************************************************************
//
BOOL CIteratorBatch::Next(LPSTR *pMem)
{
if (m_aStrings.Size())
{
*pMem = (LPSTR) m_aStrings[0];
m_aStrings.RemoveAt(0);
return TRUE;
}
return FALSE;
}
//***************************************************************************
//
// CBtrIndex::CBtrIndex
//
//***************************************************************************
//
CBtrIndex::CBtrIndex()
{
m_dwPrefixLength = 0;
InitializeCriticalSection(&m_cs);
}
//***************************************************************************
//
// CBtrIndex::~CBtrIndex
//
//***************************************************************************
//
CBtrIndex::~CBtrIndex()
{
Shutdown(WMIDB_SHUTDOWN_NET_STOP);
DeleteCriticalSection(&m_cs);
}
long CBtrIndex::Shutdown(DWORD dwShutDownFlags)
{
long lRes;
lRes = bt.Shutdown(dwShutDownFlags);
if(lRes != ERROR_SUCCESS)
return lRes;
lRes = ps.Shutdown(dwShutDownFlags);
if(lRes != ERROR_SUCCESS)
return lRes;
return ERROR_SUCCESS;
}
//***************************************************************************
//
// CBtrIndex::Initialize
//
//***************************************************************************
//
long CBtrIndex::Initialize(DWORD dwPrefixLength, LPCWSTR wszRepositoryDir
#ifdef A51_STAGE_BELOW_INDEX
, CAbstractFileSource* pSource
#endif
)
{
// Initialize the files in question and map BTree into it.
// =======================================================
wchar_t buf[MAX_PATH];
wcscpy(buf, wszRepositoryDir);
wcscat(buf, L"\\index.btr");
DWORD dwRes = ps.Init(8192, buf
#ifdef A51_STAGE_BELOW_INDEX
, pSource
#endif
);
dwRes |= bt.Init(&ps);
m_dwPrefixLength = dwPrefixLength;
return long(dwRes);
}
//***************************************************************************
//
// CBtrIndex::Create
//
//***************************************************************************
//
long CBtrIndex::Create(LPCWSTR wszFileName)
{
DWORD dwRes;
if (wszFileName == 0)
return ERROR_INVALID_PARAMETER;
wszFileName += m_dwPrefixLength;
// DEBUG
#ifdef DEBUG
FILE *f = fopen("c:\\temp\\index.log","at");
if (f)
{
fprintf(f, "Index::Create =<%S>\n", wszFileName);
fclose(f);
}
#endif
// END DEBUG
// Convert to ANSI
// ================
char *pAnsi = new char[wcslen(wszFileName) + 1];
if (pAnsi == 0)
return ERROR_NOT_ENOUGH_MEMORY;
LPCWSTR pSrc = wszFileName;
char *pDest = pAnsi;
while (*pSrc)
*pDest++ = (char) *pSrc++;
*pDest = 0;
EnterCriticalSection(&m_cs);
try
{
dwRes = bt.InsertKey(pAnsi, 0);
}
catch (...)
{
dwRes = ERROR_BADDB;
}
LeaveCriticalSection(&m_cs);
delete [] pAnsi;
if (dwRes == ERROR_ALREADY_EXISTS)
dwRes = NO_ERROR;
return long(dwRes);
}
//***************************************************************************
//
// CBtrIndex::Delete
//
// Deletes a key from the index
//
//***************************************************************************
//
long CBtrIndex::Delete(LPCWSTR wszFileName)
{
DWORD dwRes = 0;
wszFileName += m_dwPrefixLength;
// DEBUG Code
#ifdef DEBUG
FILE *f = fopen("c:\\temp\\index.log","at");
if (f)
{
fprintf(f, "Index::Delete =<%S>\n", wszFileName);
fclose(f);
}
// END DEBUG Code
#endif
// Convert to ANSI
// ================
char *pAnsi = new char[wcslen(wszFileName) + 1];
if (pAnsi == 0)
return ERROR_NOT_ENOUGH_MEMORY;
LPCWSTR pSrc = wszFileName;
char *pDest = pAnsi;
while (*pSrc)
*pDest++ = (char) *pSrc++;
*pDest = 0;
EnterCriticalSection(&m_cs);
try
{
dwRes = bt.DeleteKey(pAnsi);
if (dwRes == 0)
CIteratorBatch::PurgeAll(pAnsi);
}
catch (...)
{
dwRes = ERROR_BADDB;
}
LeaveCriticalSection(&m_cs);
delete pAnsi;
return long(dwRes);
}
//***************************************************************************
//
// CBtrIndex::CopyStringToWIN32_FIND_DATA
//
// Does an ANSI to UNICODE convert for the key string.
//
//***************************************************************************
//
BOOL CBtrIndex::CopyStringToWIN32_FIND_DATA(
LPSTR pszKey,
WIN32_FIND_DATAW* pfd
)
{
LPSTR pszSuffix = pszKey + strlen(pszKey) - 1;
while (pszSuffix[-1] != '\\' && pszSuffix > pszKey)
{
pszSuffix--;
}
// If here, a clean match.
// =======================
LPWSTR pszDest = pfd->cFileName;
while (*pszSuffix)
*pszDest++ = (wchar_t) *pszSuffix++;
*pszDest = 0;
return TRUE;
}
//***************************************************************************
//
// CBtrIndex::FindFirst
//
// Starts an enumeration
//
//***************************************************************************
//
long CBtrIndex::FindFirst(LPCWSTR wszPrefix, WIN32_FIND_DATAW* pfd,
void** ppHandle)
{
DWORD dwRes;
wszPrefix += m_dwPrefixLength;
if(ppHandle)
*ppHandle = INVALID_HANDLE_VALUE;
pfd->cFileName[0] = 0;
pfd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
// DEBUG
#ifdef DEBUG
FILE *f = fopen("c:\\temp\\index.log","at");
if (f)
{
fprintf(f, "Index::FindFirst Request Prefix=<%S>\n", wszPrefix);
fclose(f);
}
// END DEBUG
#endif
// Convert to ANSI
// ================
char *pAnsi = new char[wcslen(wszPrefix) + 1];
if (pAnsi == 0)
{
return ERROR_NOT_ENOUGH_MEMORY;
}
CVectorDeleteMe<char> vdm(pAnsi);
LPCWSTR pSrc = wszPrefix;
char *pDest = pAnsi;
while (*pSrc)
*pDest++ = (char) *pSrc++;
*pDest = 0;
// Critical-section blocked.
// =========================
EnterCriticalSection(&m_cs);
CBTreeIterator *pIt = new CBTreeIterator;
if (!pIt)
{
LeaveCriticalSection(&m_cs);
return ERROR_NOT_ENOUGH_MEMORY;
}
try
{
dwRes = pIt->Init(&bt, pAnsi);
}
catch (...)
{
dwRes = ERROR_BADDB;
}
if (dwRes)
{
pIt->Release();
LeaveCriticalSection(&m_cs);
return ERROR_FILE_NOT_FOUND;
}
// Create CIteratorBatch.
// ======================
CIteratorBatch *pBatch = new CIteratorBatch;
if (pBatch == 0)
{
pIt->Release();
LeaveCriticalSection(&m_cs);
return ERROR_NOT_ENOUGH_MEMORY;
}
// Iterate and fill batcher.
// =========================
LPSTR pszKey = 0;
int nMatchLen = strlen(pAnsi);
for (;;)
{
dwRes = pIt->Next(&pszKey);
if (dwRes)
break;
// See if prefix matches.
if (strncmp(pAnsi, pszKey, nMatchLen) != 0)
{
pIt->FreeString(pszKey);
pBatch->SetDone();
break;
}
pBatch->Add(pszKey);
}
pIt->Release();
long lRes = FindNext(pBatch, pfd);
if (lRes == ERROR_NO_MORE_FILES)
lRes = ERROR_FILE_NOT_FOUND;
if (lRes == NO_ERROR)
{
if(ppHandle)
{
*ppHandle = (LPVOID *) pBatch;
}
else
{
//
// Only asked for one --- close handle
//
delete pBatch;
}
}
else
{
delete pBatch;
}
LeaveCriticalSection(&m_cs);
return lRes;
}
//***************************************************************************
//
// CBtrIndex::FindNext
//
// Continues an enumeration. Reads from the prefetch buffer.
//
//***************************************************************************
//
long CBtrIndex::FindNext(void* pHandle, WIN32_FIND_DATAW* pfd)
{
LPSTR pszString = 0;
BOOL bRes;
if (pHandle == 0 || pfd == 0 || pHandle == INVALID_HANDLE_VALUE)
return ERROR_INVALID_PARAMETER;
EnterCriticalSection(&m_cs);
CIteratorBatch *pBatch = (CIteratorBatch *) pHandle;
bRes = pBatch->Next(&pszString);
LeaveCriticalSection(&m_cs);
if (bRes == FALSE)
return ERROR_NO_MORE_FILES;
CopyStringToWIN32_FIND_DATA(pszString, pfd);
pfd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
if (pszString)
_BtrMemFree(pszString);
#ifdef DEBUG
FILE *f = fopen("c:\\temp\\index.log","at");
if (f)
{
fprintf(f, "Index::Find Return ------------------> =<%S>\n", pfd->cFileName);
fclose(f);
}
#endif
return ERROR_SUCCESS;
}
//***************************************************************************
//
// CBtrIndex::FindClose
//
// Closes an enumeration by deleting the 'hidden' pointer.
//
//***************************************************************************
// ok
long CBtrIndex::FindClose(void* pHandle)
{
if (pHandle == 0 || pHandle == INVALID_HANDLE_VALUE)
return NO_ERROR;
EnterCriticalSection(&m_cs);
delete (CIteratorBatch *) pHandle;
LeaveCriticalSection(&m_cs);
return ERROR_SUCCESS;
}
long CBtrIndex::InvalidateCache()
{
#ifdef DEBUG
FILE *f = fopen("c:\\temp\\index.log","at");
if (f)
{
fprintf(f, "*** INVALIDATE CACHE OPERATION\n");
fclose(f);
}
#endif
EnterCriticalSection(&m_cs);
//
// Re-read the admin page from disk. NOTE: this will need changing if more
// caching is added!
//
DWORD dwRes = ps.ReadAdminPage();
if (dwRes == NO_ERROR)
dwRes = bt.InvalidateCache();
LeaveCriticalSection(&m_cs);
return long(dwRes);
}
/*
tbd:
(1) Multiple reader / single writer lock
(2) Purge improvements: read-ahead
(3) Macro for tracking checkpoint of mem allocs
(4) page cache hit tracker
Mechanical
(5) Mem leak
(6) Realloc crash
(7) Failure during enum init
(8) Substantial delete test; run stress & try to clean up afterwards
*/