4281 lines
134 KiB
C++
4281 lines
134 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
/* File: sidcache.cpp
|
|
|
|
Description: This module provides the functionality for a cache of user
|
|
SID/Name pairs. See the file header in sidcache.h for details.
|
|
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
07/12/96 Initial creation. BrianAu
|
|
08/14/96 Added SidCacheQueueIterator. BrianAu
|
|
09/20/96 Total redesign. Old design loaded data from file BrianAu
|
|
into an in-memory hash table. New design leaves
|
|
everything on disk and merely maps the file into
|
|
memory. Much more efficient with respect to
|
|
speed and size.
|
|
07/02/97 Added SidNameCache::GetFileNames. Brianau
|
|
Changed logic for identifying cache file path.
|
|
Removed index bucket count param from registry.
|
|
03/18/98 Replaced "domain", "name" and "full name" with BrianAu
|
|
"container", "logon name" and "display name" to
|
|
better match the actual contents. This was in
|
|
reponse to making the quota UI DS-aware. The
|
|
"logon name" is now a unique key as it contains
|
|
both account name and domain-like information.
|
|
i.e. "REDMOND\brianau" or "brianau@microsoft.com".
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.h" // PCH
|
|
#pragma hdrstop
|
|
|
|
#include "dskquota.h"
|
|
#include "sidcache.h"
|
|
#include "registry.h"
|
|
|
|
//
|
|
// Verify that build is UNICODE.
|
|
//
|
|
#if !defined(UNICODE)
|
|
# error This module must be compiled UNICODE.
|
|
#endif
|
|
|
|
//
|
|
// How long (milliseconds) we'll wait to get a lock on the cache.
|
|
//
|
|
const DWORD MUTEX_WAIT_TIMEOUT = 5000;
|
|
//
|
|
// Byte value used to fill in unused blocks in the data file.
|
|
//
|
|
const BYTE RECORD_UNUSED_BYTE = 0xCC;
|
|
//
|
|
// A value to signify the start of a record in the data file.
|
|
// The bit pattern is 1010101010101010 0101010101010101
|
|
// Highly unlikely that any data file data will produce this
|
|
// pattern.
|
|
//
|
|
const DWORD RECORD_SIGNATURE = 0xAAAA5555;
|
|
//
|
|
// Signatures written into the header of the index and data files.
|
|
// For validating a file just in case someone's put another file
|
|
// in their place. The numbers are arbitrary.
|
|
// 2600 means "MS building 26N (where I'm working now).
|
|
// 3209 is my office number.
|
|
// 3210 is BobDay's office number (across the hall).
|
|
// Hey, I had to pick something.
|
|
//
|
|
const DWORD INDEX_FILE_SIGNATURE = 0x26003209;
|
|
const DWORD DATA_FILE_SIGNATURE = 0x26003210;
|
|
//
|
|
// A version number so a future build of the software won't be confused
|
|
// by a change in file formats. Bump this if the file format changes.
|
|
//
|
|
const DWORD FILE_VERSION = 0x00000003;
|
|
//
|
|
// Average number of 32-byte blocks per cache entry.
|
|
// Entries are variable length (SID, Name etc). This average is used
|
|
// in initially sizing the data file. I've found that most entries (by far)
|
|
// require 4 blocks. Both the data file and index file grow independently
|
|
// as needed so it isn't a problem if this isn't always accurate.
|
|
//
|
|
const DWORD AVG_BLOCKS_PER_ENTRY = 4;
|
|
//
|
|
// Create space for this many records in a new data file.
|
|
// Since the data and index files grow automatically as needed,
|
|
// this can change as you see fit.
|
|
//
|
|
#if DBG
|
|
const DWORD NEW_CACHE_ENTRY_COUNT = 4; // Force frequent file growth.
|
|
#else
|
|
const DWORD NEW_CACHE_ENTRY_COUNT = 128;
|
|
#endif
|
|
//
|
|
// The index and data files automatically grow when needed. These
|
|
// values control how much they grow by.
|
|
//
|
|
#if DBG
|
|
const DWORD DATA_FILE_GROW_BLOCKS = 4; // Force frequent file growth.
|
|
#else
|
|
const DWORD DATA_FILE_GROW_BLOCKS = 512;
|
|
#endif
|
|
|
|
const DWORD INDEX_FILE_GROW_ENTRIES = (DATA_FILE_GROW_BLOCKS / AVG_BLOCKS_PER_ENTRY);
|
|
//
|
|
// The number of buckets in the cache index hash table.
|
|
// Number should be prime. Remember, this index is on disk so we can afford to
|
|
// have a reasonably large hash table. While it would be nice to fit the
|
|
// buckets within a single page of memory, that would be too small to be effective
|
|
// 512 / 4 == 64 buckets. There's also no guarantee that all buckets would be
|
|
// mapped to a single physical page.
|
|
//
|
|
const DWORD INDEX_BUCKET_COUNT = 503;
|
|
//
|
|
// Convert between blocks and bytes.
|
|
// BLOCK_SIZE is a power of 2 so the multiply and division
|
|
// can be optimized to shifts.
|
|
//
|
|
#define BYTE_TO_BLOCK(b) ((b) / BLOCK_SIZE)
|
|
#define BLOCK_TO_BYTE(b) ((b) * BLOCK_SIZE)
|
|
//
|
|
// Base pointers for mapped data and index files.
|
|
// When the files are mapped into memory, these globals contain
|
|
// the address of the mapped memory.
|
|
//
|
|
LPBYTE g_pbMappedDataFile;
|
|
LPBYTE g_pbMappedIndexFile;
|
|
|
|
//
|
|
// Macros for working with based pointers.
|
|
// Pointer members in the file structures contain offsets relative
|
|
// to the start of the file. When dereferencing these pointers,
|
|
// they must be converted to "based" pointers which add the file's
|
|
// base address to the pointer value. This results in a true
|
|
// virtual address that can be accessed.
|
|
//
|
|
#if defined(_X86_)
|
|
# define NDX_BASED(t) t __based(g_pbMappedIndexFile)
|
|
# define DAT_BASED(t) t __based(g_pbMappedDataFile)
|
|
# define NDX_BASED_CAST(t,e) (NDX_BASED(t) *)((DWORD)(e))
|
|
# define DAT_BASED_CAST(t,e) (DAT_BASED(t) *)((DWORD)(e))
|
|
#else
|
|
//
|
|
// APPCOMPAT:
|
|
// I think there's a bug in the ALPHA compiler that is preventing __based pointers
|
|
// from working as I have used them.
|
|
// This is a workaround until the bug is fixed or I find out what I'm doing wrong.
|
|
//
|
|
# define NDX_BASED(t) t
|
|
# define DAT_BASED(t) t
|
|
# define NDX_BASED_CAST(t,e) ((NDX_BASED(t) *)(((BYTE *)g_pbMappedIndexFile) + ((DWORD_PTR)(e))))
|
|
# define DAT_BASED_CAST(t,e) ((DAT_BASED(t) *)(((BYTE *)g_pbMappedDataFile) + ((DWORD_PTR)(e))))
|
|
#endif
|
|
|
|
//
|
|
// Macros to verify that the files have been mapped.
|
|
// These are primarily used in assertions.
|
|
//
|
|
#define INDEX_FILE_MAPPED (NULL != g_pbMappedIndexFile)
|
|
#define DATA_FILE_MAPPED (NULL != g_pbMappedDataFile)
|
|
//
|
|
// Names for system objects. Mutex and maps are named so they can
|
|
// be shared between processes.
|
|
//
|
|
const LPCTSTR g_szSidCacheMutex = TEXT("DSKQUOTA_SIDCACHE_MUTEX");
|
|
const LPCTSTR g_pszIndexFileMapping = TEXT("DSKQUOTA_SIDCACHE_INDEX");
|
|
const LPCTSTR g_pszDataFileMapping = TEXT("DSKQUOTA_SIDCACHE_DATA");
|
|
//
|
|
// Use to clear a file's GUID and to test for a NULL guid.
|
|
//
|
|
static const GUID GUID_Null =
|
|
{ 0x00000000, 0x0000, 0x0000, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } };
|
|
|
|
//
|
|
// Registry parameter value names.
|
|
//
|
|
const TCHAR g_szSidCacheRecLifeMin[] = TEXT("SidCacheRecLifeMin");
|
|
const TCHAR g_szSidCacheRecLifeMax[] = TEXT("SidCacheRecLifeMax");
|
|
|
|
|
|
//***********************************************************************************
|
|
//***********************************************************************************
|
|
// C A C H E M A N A G E R
|
|
//***********************************************************************************
|
|
//***********************************************************************************
|
|
|
|
//
|
|
// Called by both SidNameCache_Get and SidNameCache_Destroy to either retrieve
|
|
// the address (and create if necessary) of the singleton cache object or to
|
|
// destroy that object. The reason to have this single function is so that
|
|
// the singleton pointer is a local variable, inaccessible outside of this
|
|
// function. This way the only access to the cache object is through
|
|
// the SidNameCache_Get function.
|
|
//
|
|
HRESULT
|
|
SidNameCache_GetOrDestroy(
|
|
SidNameCache **ppCache,
|
|
bool bGet
|
|
)
|
|
{
|
|
DBGASSERT((NULL != ppCache));
|
|
|
|
HRESULT hr = E_FAIL;
|
|
|
|
//
|
|
// This is the one-and-only pointer to the SID-Name cache object.
|
|
// All code obtains this address through this function.
|
|
//
|
|
static SidNameCache *pTheCache;
|
|
|
|
*ppCache = NULL;
|
|
|
|
HANDLE hMutex = CreateMutex(NULL, FALSE, g_szSidCacheMutex);
|
|
if (NULL != hMutex)
|
|
{
|
|
if (WAIT_OBJECT_0 == WaitForSingleObject(hMutex, INFINITE))
|
|
{
|
|
if (!bGet)
|
|
{
|
|
//
|
|
// Destroy the existing cache object. NULL the static
|
|
// ptr so next request will recreate the cache object.
|
|
//
|
|
delete pTheCache;
|
|
pTheCache = NULL;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Retrieve existing or create new cache object.
|
|
//
|
|
SidNameCache *pCache = pTheCache;
|
|
if (NULL != pCache)
|
|
{
|
|
//
|
|
// Use the existing object.
|
|
//
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Doesn't yet exist. Create new one.
|
|
//
|
|
autoptr<SidNameCache> ptrCache(new SidNameCache);
|
|
|
|
//
|
|
// Open/Create new cache data and index files.
|
|
// Will first try to open existing. If either the index or
|
|
// data file doesn't exist or is invalid, new files are created.
|
|
//
|
|
hr = ptrCache->Initialize(TRUE);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pCache = ptrCache.get();
|
|
ptrCache.disown();
|
|
//
|
|
// Save in our static variable for future use.
|
|
//
|
|
pTheCache = pCache;
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("SID cache initialization failed with error 0x%08X"), hr));
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
catch(CSyncException& e)
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Return the pointer to the caller.
|
|
//
|
|
*ppCache = pCache;
|
|
}
|
|
}
|
|
ReleaseMutex(hMutex);
|
|
}
|
|
CloseHandle(hMutex);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Retrieves the address of the singleton SidNameCache object.
|
|
// If the object doesn't exist it is created.
|
|
// Callers do not release or delete this pointer.
|
|
// On Process-Detach, the SidNameCache_Destroy() function is
|
|
// called to delete the singleton object.
|
|
//
|
|
HRESULT SidNameCache_Get(SidNameCache **ppCache)
|
|
{
|
|
const bool bGet = true;
|
|
return SidNameCache_GetOrDestroy(ppCache, bGet);
|
|
}
|
|
|
|
//
|
|
// Called on Process-Detach to destroy the singleton cache
|
|
// object.
|
|
//
|
|
HRESULT SidNameCache_Destroy(void)
|
|
{
|
|
const bool bGet = false;
|
|
SidNameCache *pUnused;
|
|
return SidNameCache_GetOrDestroy(&pUnused, bGet);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::SidNameCache
|
|
|
|
Description: Constructor. Creates an empty SID/Name cache object.
|
|
Call one of the Initialize() methods to either create a new index and
|
|
data file or to open existing ones.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Exceptions: SyncObjErrorCreate.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameCache::SidNameCache(
|
|
VOID
|
|
) : m_hMutex(NULL),
|
|
m_pIndexMgr(NULL),
|
|
m_pRecordMgr(NULL)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::SidNameCache")));
|
|
if (NULL == (m_hMutex = CreateMutex(NULL, // No security
|
|
FALSE, // Non-owned
|
|
g_szSidCacheMutex)))
|
|
{
|
|
throw CSyncException(CSyncException::mutex, CSyncException::create);
|
|
}
|
|
|
|
SetCacheFilePath();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::~SidNameCache
|
|
|
|
Description: Destructor. Destroys the cache object by deleting the
|
|
Index Manager and Record Manager objects. The respective destructor's
|
|
for each of the managers will handle closing their files and mapping
|
|
objects.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameCache::~SidNameCache(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::~SidNameCache")));
|
|
if (NULL != m_hMutex)
|
|
Lock();
|
|
|
|
delete m_pIndexMgr;
|
|
delete m_pRecordMgr;
|
|
|
|
if (NULL != m_hMutex)
|
|
{
|
|
ReleaseLock();
|
|
CloseHandle(m_hMutex);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::Initialize
|
|
|
|
Description: Initializes a new cache object by creating the Index and
|
|
Record manager objects then initializing each. Initialization first
|
|
tries to open existing index and data files. If one (or both) of the
|
|
files does not exist OR one (or both) of the files is considered
|
|
"invalid", new files are created. Need to take a "fail safe" approach
|
|
to this.
|
|
|
|
Arguments:
|
|
bOpenExisting - TRUE = Try to open an existing cache index and data file.
|
|
If it can't, it creates a new one. FALSE = Just create a new one.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
E_FAIL - Could not open nor create required files.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameCache::Initialize(
|
|
BOOL bOpenExisting
|
|
)
|
|
{
|
|
DBGTRACE((DM_CONTROL, DL_MID, TEXT("SidNameCache::Initialize")));
|
|
DBGASSERT((NULL == m_pIndexMgr));
|
|
DBGASSERT((NULL == m_pRecordMgr));
|
|
|
|
HRESULT hResult = E_FAIL;
|
|
CacheAutoLock lock(*this);
|
|
|
|
if (lock.Lock())
|
|
{
|
|
try
|
|
{
|
|
if (m_strFilePath.IsEmpty())
|
|
{
|
|
//
|
|
// If file path is empty, it means we couldn't get the
|
|
// user's profile directory from the registry.
|
|
//
|
|
DBGERROR((TEXT("Error creating SID cache files. No path.")));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Name for our cache data and index files.
|
|
// Will append .DAT and .NDX respectively.
|
|
// This is where you change the file name or extension(s)
|
|
// if you want to do that.
|
|
//
|
|
const TCHAR szSidCacheFile[] = TEXT("NTDiskQuotaSidCache");
|
|
|
|
//
|
|
// Create a fully-qualified path for the cache data and index
|
|
// files. m_strFilePath was set in the cache object ctor.
|
|
//
|
|
CString strDataFile(m_strFilePath);
|
|
CString strIndexFile(m_strFilePath);
|
|
|
|
strDataFile.Format(TEXT("%1\\%2.dat"), (LPCTSTR)m_strFilePath, szSidCacheFile);
|
|
strIndexFile.Format(TEXT("%1\\%2.ndx"),(LPCTSTR)m_strFilePath, szSidCacheFile);
|
|
|
|
//
|
|
// Create the record and index manager objects.
|
|
//
|
|
m_pRecordMgr = new RecordMgr(*this);
|
|
m_pIndexMgr = new IndexMgr(*this);
|
|
|
|
DBGPRINT((DM_CONTROL, DL_MID, TEXT("Create SID cache DataFile = %s IndexFile = %s"),
|
|
(LPCTSTR)strDataFile, (LPCTSTR)strIndexFile));
|
|
|
|
if (bOpenExisting)
|
|
{
|
|
//
|
|
// First try opening existing data and index files.
|
|
//
|
|
if (NULL != m_pRecordMgr->Initialize(strDataFile))
|
|
{
|
|
if (NULL != m_pIndexMgr->Initialize(strIndexFile))
|
|
hResult = NO_ERROR;
|
|
}
|
|
}
|
|
|
|
if (FAILED(hResult) || !FilesAreValid())
|
|
{
|
|
hResult = E_FAIL;
|
|
//
|
|
// Couldn't open existing files, try creating new ones.
|
|
// Any open files/mappings will be closed.
|
|
//
|
|
if (NULL != m_pRecordMgr->Initialize(strDataFile,
|
|
NEW_CACHE_ENTRY_COUNT * AVG_BLOCKS_PER_ENTRY))
|
|
{
|
|
if (NULL != m_pIndexMgr->Initialize(strIndexFile,
|
|
INDEX_BUCKET_COUNT,
|
|
NEW_CACHE_ENTRY_COUNT))
|
|
{
|
|
hResult = NO_ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
delete m_pRecordMgr;
|
|
m_pRecordMgr = NULL;
|
|
delete m_pIndexMgr;
|
|
m_pIndexMgr = NULL;
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
//
|
|
// Mark files as "valid".
|
|
//
|
|
if (SUCCEEDED(hResult))
|
|
ValidateFiles();
|
|
}
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::OpenMappedFile
|
|
|
|
Description: Opens or creates a file, maps the file into memory and
|
|
returns the address of the mapping.
|
|
|
|
Arguments:
|
|
pszFile - Address of name of file to create/open.
|
|
|
|
pszMapping - Name to give the mapping object. This object is
|
|
named so that if multiple processes map the same file (using
|
|
the same mapping name), the file is only mapped once.
|
|
|
|
dwCreation - Creation flag (CREATE_ALWAYS, OPEN_EXISTING);
|
|
|
|
cbFileHigh/Low - If creating a new file or extending an existing
|
|
these two arguments contain the desired size in bytes.
|
|
|
|
phFile - Address of handle variable to receive the open file's
|
|
handle value. Call CloseHandle on this to close the file.
|
|
|
|
phFileMapping - Address of handle variable to receive the open
|
|
file mapping's handle value. Call CloseFileMapping on this
|
|
to close the mapping.
|
|
|
|
Returns:
|
|
Address of the mapped file in memory. NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
07/21/97 Files are now created in user's profile under a BrianAu
|
|
"DiskQuota" subdirectory.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::OpenMappedFile(
|
|
LPCTSTR pszFile,
|
|
LPCTSTR pszMapping,
|
|
DWORD dwCreation,
|
|
DWORD cbFileHigh,
|
|
DWORD cbFileLow,
|
|
PHANDLE phFile,
|
|
PHANDLE phFileMapping
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::OpenMappedFile")));
|
|
LPBYTE pbBase = NULL;
|
|
|
|
DBGASSERT((NULL != pszFile));
|
|
DBGASSERT((NULL != pszMapping));
|
|
DBGASSERT((NULL != phFile));
|
|
DBGASSERT((NULL != phFileMapping));
|
|
|
|
*phFile = CreateFile(pszFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
dwCreation,
|
|
FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN,
|
|
NULL);
|
|
|
|
if (INVALID_HANDLE_VALUE != *phFile)
|
|
{
|
|
if ((*phFileMapping = CreateFileMapping(*phFile,
|
|
NULL,
|
|
PAGE_READWRITE,
|
|
cbFileHigh,
|
|
cbFileLow,
|
|
pszMapping)) != NULL)
|
|
{
|
|
pbBase = (LPBYTE)MapViewOfFile(*phFileMapping,
|
|
FILE_MAP_WRITE,
|
|
0,
|
|
0,
|
|
0);
|
|
if (NULL == pbBase)
|
|
DBGERROR((TEXT("SIDCACHE - Failed to map view of file %s"),
|
|
pszFile));
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("SIDCACHE - Failed to create mapping %s for file %s"),
|
|
pszMapping, pszFile));
|
|
}
|
|
if (NULL == pbBase)
|
|
{
|
|
CloseHandle(*phFile);
|
|
*phFile = NULL;
|
|
}
|
|
}
|
|
else
|
|
DBGERROR((TEXT("SIDCACHE - Failed to open file %s"), pszFile));
|
|
|
|
|
|
return pbBase;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::SetCacheFilePath
|
|
|
|
Description: Obtains the fully-qualified path for the cache's data
|
|
and index files and stores the value in m_strFilePath. The files
|
|
are to be created under in the user's profile under the directory
|
|
\AppData\Microsoft\Windows NT\DiskQuota. We have to read the registry
|
|
to find exactly where this subtree lives for this user.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
On return, m_strFilePath contains the path to the files.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
07/02/97 Initial creation. BrianAu
|
|
07/21/97 Removed default file path. BrianAu
|
|
Files can only be stored in user's profile.
|
|
Can't allow unsecure access to SID/Name pairs.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::SetCacheFilePath(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_HIGH, TEXT("SidNameCache::SetCacheFilePath")));
|
|
//
|
|
// Get the user's %UserProfile%\Application Data directory.
|
|
// Normally, an app gets this through SHGetSpecialFolderLocation or
|
|
// SHGetSpecialFolderPath. However, I don't want to load shell32.dll
|
|
// just for that purpose (I've tried to keep shell32 out of this dll).
|
|
// Therefore, we read the registry values just like the shell does.
|
|
// EricFlo suggested this so it's OK ZAW-wise.
|
|
//
|
|
LONG lResult = ERROR_SUCCESS;
|
|
HKEY hKey = NULL;
|
|
DWORD dwDisposition = 0;
|
|
const TCHAR szKeyNameRoot[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer");
|
|
const TCHAR szMSWinNTDiskQuota[] = TEXT("Microsoft\\Windows NT\\DiskQuota");
|
|
|
|
LPCTSTR rgpszKeys[] = {
|
|
TEXT("\\User Shell Folders"),
|
|
TEXT("\\Shell Folders"),
|
|
};
|
|
|
|
//
|
|
// Start with an empty path buffer.
|
|
//
|
|
m_strFilePath.Empty();
|
|
|
|
for (INT i = 0; i < ARRAYSIZE(rgpszKeys) && m_strFilePath.IsEmpty(); i++)
|
|
{
|
|
//
|
|
// Create the key name.
|
|
//
|
|
CString strKeyName(szKeyNameRoot);
|
|
strKeyName += CString(rgpszKeys[i]);
|
|
|
|
//
|
|
// Open the reg key.
|
|
//
|
|
lResult = RegCreateKeyEx(HKEY_CURRENT_USER,
|
|
strKeyName,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_READ,
|
|
NULL,
|
|
&hKey,
|
|
&dwDisposition);
|
|
|
|
if (ERROR_SUCCESS == lResult)
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Get the path to the user's "Application Data" directory.
|
|
//
|
|
DBGASSERT((NULL != hKey));
|
|
|
|
DWORD dwValueType = 0;
|
|
DWORD cbValue = MAX_PATH * sizeof(TCHAR);
|
|
|
|
lResult = RegQueryValueEx(hKey,
|
|
TEXT("AppData"),
|
|
0,
|
|
&dwValueType,
|
|
(LPBYTE)m_strFilePath.GetBuffer(MAX_PATH),
|
|
&cbValue);
|
|
|
|
m_strFilePath.ReleaseBuffer();
|
|
|
|
if (ERROR_SUCCESS == lResult)
|
|
{
|
|
//
|
|
// Ensure the path has a trailing backslash.
|
|
//
|
|
INT cchPath = m_strFilePath.Length();
|
|
if (0 < cchPath && TEXT('\\') != m_strFilePath[cchPath-1])
|
|
{
|
|
m_strFilePath += CString(TEXT("\\"));
|
|
}
|
|
//
|
|
// Append "Microsoft\Windows NT\DiskQuota" to the path.
|
|
//
|
|
m_strFilePath += CString(szMSWinNTDiskQuota);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Something failed. Ensure m_strFilePath is empty.
|
|
//
|
|
m_strFilePath.Empty();
|
|
if (ERROR_FILE_NOT_FOUND != lResult)
|
|
{
|
|
DBGERROR((TEXT("SIDCACHE - Error %d getting \"AppData\" reg value."), lResult));
|
|
}
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
lResult = ERROR_OUTOFMEMORY;
|
|
}
|
|
RegCloseKey(hKey);
|
|
}
|
|
else if (ERROR_FILE_NOT_FOUND != lResult)
|
|
{
|
|
DBGERROR((TEXT("SIDCACHE - Error %d opening \"\\User Shell Folders\" or \"Shell Folders\" reg key"), lResult));
|
|
}
|
|
}
|
|
|
|
if (!m_strFilePath.IsEmpty())
|
|
{
|
|
//
|
|
// Expand any embedded environment strings.
|
|
//
|
|
m_strFilePath.ExpandEnvironmentStrings();
|
|
|
|
//
|
|
// Ensure the path DOES NOT have a trailing backslash.
|
|
//
|
|
INT cchPath = m_strFilePath.Length();
|
|
if (0 < cchPath && TEXT('\\') == m_strFilePath[cchPath-1])
|
|
{
|
|
m_strFilePath[cchPath-1] = TEXT('\0');
|
|
}
|
|
|
|
if ((DWORD)-1 == ::GetFileAttributes(m_strFilePath))
|
|
{
|
|
//
|
|
// If the directory doesn't exist, try to create it.
|
|
//
|
|
if (0 == CreateCacheFileDirectory(m_strFilePath))
|
|
{
|
|
//
|
|
// Couldn't create the directory, make sure the path
|
|
// is empty so we don't try to write to a non-existent
|
|
// directory.
|
|
//
|
|
DBGERROR((TEXT("SIDCACHE - Error %d creating directory \"%s\""),
|
|
GetLastError(), (LPCTSTR)m_strFilePath));
|
|
m_strFilePath.Empty();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::CreateCacheFileDirectory
|
|
|
|
Description: Creates the directory for the SID/Name cache files. Since
|
|
the directory lives several levels below "Application Data", we
|
|
may need to create several directories before we get to DiskQuota.
|
|
|
|
Arguments: pszPath - This is a fully-qualified directory path.
|
|
|
|
Returns: TRUE = Directory created.
|
|
FALSE = Directory not created.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
07/21/97 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::CreateCacheFileDirectory(
|
|
LPCTSTR pszPath
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_HIGH, TEXT("SidNameCache::CreateCacheFileDirectory")));
|
|
BOOL bResult = TRUE;
|
|
CString s(pszPath); // Local copy we can play with.
|
|
LPTSTR psz = (LPTSTR)s; // Ptr to C string.
|
|
|
|
while(TEXT('\0') != *psz && bResult)
|
|
{
|
|
//
|
|
// Find the next backslash (or end-of-string).
|
|
//
|
|
while(TEXT('\0') != *psz && TEXT('\\') != *psz)
|
|
{
|
|
psz++;
|
|
}
|
|
//
|
|
// Replace backslash with a temporary NUL.
|
|
//
|
|
TCHAR chSaved = *psz;
|
|
*psz = TEXT('\0');
|
|
//
|
|
// See if the directory already exists.
|
|
//
|
|
if ((DWORD)-1 == ::GetFileAttributes(s))
|
|
{
|
|
//
|
|
// It doesn't. Try to create it.
|
|
//
|
|
if (0 == ::CreateDirectory(s, NULL))
|
|
{
|
|
DBGERROR((TEXT("SIDCACHE - Error %d creating directory \"%s\""),
|
|
GetLastError(), (LPCTSTR)s));
|
|
bResult = FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Replace temp NUL with original backslash and advance ptr
|
|
// to next character in path (if we're not at the end of the string).
|
|
//
|
|
*psz = chSaved;
|
|
if (TEXT('\0') != *psz)
|
|
{
|
|
psz++;
|
|
}
|
|
}
|
|
|
|
if (bResult)
|
|
{
|
|
//
|
|
// Created directory. Set SYSTEM & HIDDEN attribute bits on the final
|
|
// subdirectory ("\DiskQuota").
|
|
//
|
|
SetFileAttributes(pszPath,
|
|
GetFileAttributes(pszPath) | (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN));
|
|
}
|
|
|
|
return bResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::Lock
|
|
|
|
Description: Obtains an exclusive lock on the cache. This lock is
|
|
system-wide so that multiple processes can access the cache files.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
TRUE - Exclusive lock obtained or the lock was abandoned.
|
|
No matter how the lock was obtained, the caller should always
|
|
check the validity of the index and data files before trusting
|
|
their contents.
|
|
FALSE - Lock could not be obtained in the required timeout period.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::Lock(
|
|
VOID
|
|
)
|
|
{
|
|
BOOL bResult = FALSE;
|
|
DBGASSERT((NULL != m_hMutex));
|
|
|
|
//
|
|
// IMPORTANT: Don't try handling thread messages with MsgWaitForMultipleObjects.
|
|
// This lock function can be called on the resolver's background
|
|
// thread. If you pull up that thread's messages, it won't receive
|
|
// the WM_QUIT message commanding it to shutdown.
|
|
//
|
|
switch(WaitForSingleObject(m_hMutex, MUTEX_WAIT_TIMEOUT))
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
case WAIT_ABANDONED:
|
|
bResult = TRUE;
|
|
break;
|
|
|
|
case WAIT_FAILED:
|
|
case WAIT_TIMEOUT:
|
|
default:
|
|
break;
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::ReleaseLock
|
|
|
|
Description: Releases the exclusive lock on the cache. This function must
|
|
always be paired with a call to Lock(). Be careful of conditions
|
|
which may throw an exception and bypass releasing the lock.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::ReleaseLock(
|
|
VOID
|
|
)
|
|
{
|
|
DBGASSERT((NULL != m_hMutex));
|
|
ReleaseMutex(m_hMutex);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::BeginTransaction
|
|
|
|
Description: Called at the beginning of any "transaction" to obtain
|
|
exclusive access to the cache and verify the cache files are valid.
|
|
Although this is of course not true transaction processing, it provides
|
|
a simple approximation that is sufficient for this cache implementation.
|
|
Note that before returning, the files are "invalidated". After the
|
|
transaction is completed, the caller must call EndTransaction to
|
|
release the exclusive lock and mark the files as "valid".
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
NO_ERROR - Success. Transaction can be carried out.
|
|
ERROR_INVALID_DATA (hr) - Index or data file is invalid. Caller should
|
|
re-initialize both index and data files.
|
|
ERROR_LOCK_FAILED (hr) - Could not obtain exclusive access to the
|
|
index and data files. Caller can either repeat the call or
|
|
simply fail the cache access.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameCache::BeginTransaction(
|
|
VOID
|
|
)
|
|
{
|
|
HRESULT hResult = NO_ERROR;
|
|
if (Lock())
|
|
{
|
|
if (FilesAreValid())
|
|
{
|
|
InvalidateFiles();
|
|
}
|
|
else
|
|
{
|
|
ReleaseLock();
|
|
hResult = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
}
|
|
}
|
|
else
|
|
hResult = HRESULT_FROM_WIN32(ERROR_LOCK_FAILED);
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::EndTransaction
|
|
|
|
Description: Called at the end of any "transaction" to release
|
|
exclusive access to the cache and validate the cache files.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::EndTransaction(
|
|
VOID
|
|
)
|
|
{
|
|
//
|
|
// If you hit this assertion, probably didn't call BeginTransaction
|
|
// first.
|
|
//
|
|
DBGASSERT((!FilesAreValid()));
|
|
|
|
ValidateFiles();
|
|
ReleaseLock();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::Clear
|
|
|
|
Description: Resets both the index and data files to an empty state.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
TRUE - Files reset.
|
|
FALSE - Couldn't obtain lock to reset files or another process
|
|
is also using the cache.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
08/07/00 Clear cache by clearing index and data files. Not BrianAu
|
|
destroying and recreating.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::Clear(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_HIGH, TEXT("SidNameCache::Clear")));
|
|
BOOL bResult = FALSE;
|
|
CacheAutoLock lock(*this);
|
|
if (lock.Lock())
|
|
{
|
|
if (NULL != m_pIndexMgr && NULL != m_pRecordMgr)
|
|
{
|
|
m_pIndexMgr->Clear();
|
|
m_pRecordMgr->Clear();
|
|
ValidateFiles();
|
|
bResult = TRUE;
|
|
}
|
|
}
|
|
return bResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::FilesAreValid
|
|
|
|
Description: Examines the guid field in the index and data files.
|
|
If the guids are non-zero and are equal, the files are considered
|
|
"valid". BeginTransaction sets each guid to all 0's while
|
|
EndTransaction fills them with a new GUID.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
TRUE/FALSE indicating file validity.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::FilesAreValid(
|
|
VOID
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
GUID guidIndexFile;
|
|
GUID guidDataFile;
|
|
|
|
m_pIndexMgr->GetFileGUID(&guidIndexFile);
|
|
if (GUID_Null != guidIndexFile)
|
|
{
|
|
m_pRecordMgr->GetFileGUID(&guidDataFile);
|
|
return guidDataFile == guidIndexFile;
|
|
}
|
|
return FALSE; // At least one GUID was all 0's
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::ValidateFiles
|
|
|
|
Description: Generates a new GUID and writes it to the header of the
|
|
index and data files. This should only be called when a transaction
|
|
has been successfully completed. EndTransaction calls this method
|
|
to mark the files as "valid".
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
05/18/00 Check return value from CoCreateGuid. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::ValidateFiles(
|
|
VOID
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
//
|
|
// If GUID creation fails, the cache will be considered invalid.
|
|
// That's a reasonable system response. Failure of GUID creation
|
|
// is highly unlikely.
|
|
//
|
|
GUID guid;
|
|
if (SUCCEEDED(CoCreateGuid(&guid)))
|
|
{
|
|
m_pRecordMgr->SetFileGUID(&guid);
|
|
m_pIndexMgr->SetFileGUID(&guid);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::InvalidateFiles
|
|
|
|
Description: Sets the guid field in each file to all 0's. This marks
|
|
a file as "invalid". BeginTransaction calls this.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::InvalidateFiles(
|
|
VOID
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
m_pRecordMgr->SetFileGUID(&GUID_Null);
|
|
m_pIndexMgr->SetFileGUID(&GUID_Null);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::Lookup
|
|
|
|
Description: Given a user SID, this method locates the corresponding
|
|
data record in the cache and returns the requested information.
|
|
(i.e. container, name, full name). Several conditions will cause
|
|
the lookup to fail.
|
|
1. SID not found in cache.
|
|
2. Can't get exclusive lock on cache.
|
|
3. Index or data file (or both) are invalid.
|
|
4. Record found but expired.
|
|
|
|
Arguments:
|
|
pKeySid - Address of SID to use as lookup key.
|
|
|
|
ppszContainer [optional] - Address of pointer variable to receive the
|
|
address of buffer containing the "container" name string. Caller is
|
|
responsible for freeing the buffer with delete[].
|
|
May be NULL if the container is not desired.
|
|
|
|
ppszLogonName [optional] - Address of pointer variable to receive the
|
|
address of buffer containing logon name string. Caller is
|
|
responsible for freeing the buffer with delete[].
|
|
May be NULL if logon name is not desired.
|
|
|
|
ppszDisplayName [optional] - Address of pointer variable to receive the
|
|
address of buffer containing account display string. Caller is
|
|
responsible for freeing the buffer with delete[].
|
|
May be NULL if display name is not desired.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
ERROR_FILE_NOT_FOUND (hr) - Sid not found in cache.
|
|
ERROR_LOCK_FAILED (hr) - Couldn't get exclusive lock on cache.
|
|
ERROR_INVALID_DATA (hr) - Index or data file is invalid.
|
|
|
|
Exceptions: OutOfMemory
|
|
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameCache::Lookup(
|
|
PSID pKeySid,
|
|
LPTSTR *ppszContainer,
|
|
LPTSTR *ppszLogonName,
|
|
LPTSTR *ppszDisplayName
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::Lookup [SID]")));
|
|
DBGASSERT((NULL != pKeySid));
|
|
|
|
HRESULT hResult = BeginTransaction();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
try
|
|
{
|
|
DWORD iBlock = m_pIndexMgr->Lookup(pKeySid);
|
|
|
|
if ((DWORD)-1 != iBlock)
|
|
{
|
|
if (!m_pRecordMgr->RecordExpired(iBlock))
|
|
{
|
|
PSID pSid = NULL;
|
|
//
|
|
// This can throw OutOfMemory.
|
|
//
|
|
hResult = m_pRecordMgr->Retrieve(iBlock,
|
|
&pSid,
|
|
ppszContainer,
|
|
ppszLogonName,
|
|
ppszDisplayName);
|
|
if (SUCCEEDED(hResult))
|
|
DBGASSERT((EqualSid(pSid, pKeySid)));
|
|
|
|
if (NULL != pSid)
|
|
delete[] pSid;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Record is outdated. Delete it from the cache.
|
|
// Returning "not found" will cause the caller to get
|
|
// a fresh one from the domain controller - which will
|
|
// then again be added to the cache.
|
|
//
|
|
DBGPRINT((DM_SIDCACHE, DL_HIGH,
|
|
TEXT("SIDCACHE - Record at block %d has expired."),
|
|
iBlock));
|
|
|
|
m_pIndexMgr->FreeEntry(pKeySid);
|
|
m_pRecordMgr->FreeRecord(iBlock);
|
|
hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // SID not in cache.
|
|
}
|
|
}
|
|
catch(CAllocException &e)
|
|
{
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
EndTransaction();
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::Lookup
|
|
|
|
Description: Given a user's logon name name, this method locates
|
|
the corresponding SID in the cache. All comments in the previous
|
|
(above) version of this method apply.
|
|
|
|
Arguments:
|
|
pszLogonName - Address of account logon name string.
|
|
|
|
ppSid - Address of pointer variable to receive the address of buffer
|
|
containing the SID. Caller is responsible for freeing the buffer
|
|
with delete[].
|
|
|
|
Returns:
|
|
See list in previous method.
|
|
|
|
Exception: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
03/18/98 Replaced domain\name key arguments with single BrianAu
|
|
logon name key.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameCache::Lookup(
|
|
LPCTSTR pszLogonName,
|
|
PSID *ppSid
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::Lookup [name]")));
|
|
DBGASSERT((NULL != pszLogonName));
|
|
DBGASSERT((NULL != ppSid));
|
|
|
|
HRESULT hResult = BeginTransaction();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Can throw OutOfMemory.
|
|
//
|
|
DWORD iBlock = m_pIndexMgr->Lookup(pszLogonName);
|
|
|
|
if ((DWORD)-1 != iBlock)
|
|
{
|
|
//
|
|
// Can throw OutOfMemory.
|
|
//
|
|
hResult = m_pRecordMgr->Retrieve(iBlock,
|
|
ppSid,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (m_pRecordMgr->RecordExpired(iBlock))
|
|
{
|
|
//
|
|
// Record is outdated. Delete it from the cache.
|
|
// Returning "not found" will cause the caller to get
|
|
// a fresh one from the domain controller - which will
|
|
// then again be added to the cache.
|
|
//
|
|
DBGASSERT((NULL != *ppSid));
|
|
m_pIndexMgr->FreeEntry(*ppSid);
|
|
m_pRecordMgr->FreeRecord(iBlock);
|
|
delete[] *ppSid;
|
|
*ppSid = NULL;
|
|
hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hResult = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); // SID not in cache.
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
EndTransaction();
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::Add
|
|
|
|
Description: Add user's information to the cache. Information consists
|
|
of the SID (key), container name, account logon name and
|
|
account display name.
|
|
|
|
Arguments:
|
|
pSid - Address of user's SID.
|
|
|
|
pszContainer - Address of account container name string.
|
|
i.e. "REDMOND" or "ntdev.microsoft.com\US-SOS ....."
|
|
|
|
pszLogonName - Address of account logon name string.
|
|
i.e. "REDMOND\brianau" or "brianau@microsoft.com"
|
|
|
|
pszDisplayName - Address of display name string.
|
|
i.e. "Brian Aust"
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
S_FALSE - Already exists in cache. Not added.
|
|
ERROR_LOCK_FAILED (hr) - Couldn't get exclusive lock on cache.
|
|
ERROR_INVALID_DATA (hr) - Index or data file is invalid.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameCache::Add(
|
|
PSID pSid,
|
|
LPCTSTR pszContainer,
|
|
LPCTSTR pszLogonName,
|
|
LPCTSTR pszDisplayName
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::Add")));
|
|
|
|
DBGASSERT((NULL != pSid));
|
|
DBGASSERT((NULL != pszContainer));
|
|
DBGASSERT((NULL != pszLogonName));
|
|
DBGASSERT((NULL != pszDisplayName));
|
|
|
|
HRESULT hResult = BeginTransaction();
|
|
|
|
if (SUCCEEDED(hResult))
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Can throw OutOfMemory.
|
|
//
|
|
if ((DWORD)-1 == m_pIndexMgr->Lookup(pSid))
|
|
{
|
|
DWORD iBlock = m_pRecordMgr->Store(pSid,
|
|
pszContainer,
|
|
pszLogonName,
|
|
pszDisplayName);
|
|
|
|
if ((DWORD)-1 != iBlock)
|
|
{
|
|
m_pIndexMgr->Add(pSid, iBlock);
|
|
hResult = NO_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hResult = S_FALSE; // Already exists. Not a failure.
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
hResult = E_OUTOFMEMORY;
|
|
}
|
|
EndTransaction();
|
|
}
|
|
|
|
return hResult;
|
|
}
|
|
|
|
|
|
|
|
//***********************************************************************************
|
|
//***********************************************************************************
|
|
// I N D E X F I L E M A N A G E R
|
|
//***********************************************************************************
|
|
//***********************************************************************************
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::IndexMgr
|
|
|
|
Description: Index manager constructor.
|
|
|
|
Arguments:
|
|
refCache - Reference to containing cache object. Used to call
|
|
record manager and cache manager public methods.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameCache::IndexMgr::IndexMgr(
|
|
SidNameCache& refCache
|
|
) : m_refCache(refCache),
|
|
m_pFileHdr(NULL),
|
|
m_hFile(NULL),
|
|
m_hFileMapping(NULL)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::SidNameCache::IndexMgr")));
|
|
//
|
|
// Nothing to do.
|
|
//
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::~IndexMgr
|
|
|
|
Description: Index manager destructor.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameCache::IndexMgr::~IndexMgr(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::SidNameCache::~IndexMgr")));
|
|
CloseIndexFile();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Initialize
|
|
|
|
Description: Initializes a new index manager object. If both cBuckets
|
|
and cMaxEntries are 0 (default), existing cache files are opened.
|
|
Otherwise, new cache files are created.
|
|
|
|
Arguments:
|
|
pszFile - Address of full path for new file.
|
|
|
|
cBuckets - Number of hash table buckets in index file. Should be
|
|
prime.
|
|
|
|
cMaxEntries - Initial max number of entries for the index. Note
|
|
that the index file grows automatically as required.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::IndexMgr::Initialize(
|
|
LPCTSTR pszFile,
|
|
DWORD cBuckets,
|
|
DWORD cMaxEntries
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_HIGH, TEXT("SidNameCache::IndexMgr::Initialize")));
|
|
DBGASSERT((NULL != pszFile));
|
|
|
|
//
|
|
// Store the file name in our CString object.
|
|
//
|
|
m_strFileName = pszFile;
|
|
|
|
if (0 == cBuckets && 0 == cMaxEntries)
|
|
{
|
|
//
|
|
// Initialize manager using existing cache files.
|
|
//
|
|
m_pFileHdr = (PINDEX_FILE_HDR)OpenIndexFile(pszFile);
|
|
if (NULL != m_pFileHdr)
|
|
{
|
|
if (FILE_VERSION != m_pFileHdr->dwVersion ||
|
|
INDEX_FILE_SIGNATURE != m_pFileHdr->dwSignature)
|
|
{
|
|
//
|
|
// This version of the software doesn't understand this
|
|
// version of the file or file has an invalid signature.
|
|
// Don't take any chances. We'll just create a new one.
|
|
//
|
|
DBGERROR((TEXT("SIDCACHE - Index file is invalid or incorrect version. A new index file will be created.")));
|
|
|
|
CloseIndexFile();
|
|
m_pFileHdr = NULL;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Initialize manager by creating new cache files.
|
|
//
|
|
ULARGE_INTEGER uliFileSize;
|
|
uliFileSize.QuadPart = FileSize(cMaxEntries, cBuckets);
|
|
|
|
m_pFileHdr = (PINDEX_FILE_HDR)CreateIndexFile(pszFile,
|
|
uliFileSize.HighPart,
|
|
uliFileSize.LowPart);
|
|
if (NULL != m_pFileHdr)
|
|
{
|
|
InitNewIndexFile(cBuckets, cMaxEntries);
|
|
}
|
|
}
|
|
return (LPBYTE)m_pFileHdr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::CreateIndexFile
|
|
|
|
Description: Creates and initializes a new index file.
|
|
|
|
Arguments:
|
|
pszFile - Address of full path for new file.
|
|
|
|
cbFileHigh/Low - Size of file in bytes.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::IndexMgr::CreateIndexFile(
|
|
LPCTSTR pszFile,
|
|
DWORD cbFileHigh,
|
|
DWORD cbFileLow
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::IndexMgr::CreateIndexFile")));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("\tFile: \"%s\""), pszFile ? pszFile : TEXT("<null>")));
|
|
|
|
DBGASSERT((NULL != pszFile));
|
|
|
|
CloseIndexFile(); // Make sure any existing index file is closed.
|
|
|
|
g_pbMappedIndexFile = SidNameCache::OpenMappedFile(
|
|
pszFile,
|
|
g_pszIndexFileMapping,
|
|
CREATE_ALWAYS,
|
|
cbFileHigh,
|
|
cbFileLow,
|
|
&m_hFile,
|
|
&m_hFileMapping);
|
|
|
|
return g_pbMappedIndexFile;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::OpenIndexFile
|
|
|
|
Description: Opens an existing index file.
|
|
|
|
Arguments:
|
|
pszFile - Address of full path for new file.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::IndexMgr::OpenIndexFile(
|
|
LPCTSTR pszFile
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::IndexMgr::OpenIndexFile")));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("\tFile: \"%s\""), pszFile ? pszFile : TEXT("<null>")));
|
|
DBGASSERT((NULL != pszFile));
|
|
|
|
CloseIndexFile(); // Make sure any existing index file is closed.
|
|
|
|
g_pbMappedIndexFile = SidNameCache::OpenMappedFile(
|
|
pszFile,
|
|
g_pszIndexFileMapping,
|
|
OPEN_EXISTING,
|
|
0,
|
|
0,
|
|
&m_hFile,
|
|
&m_hFileMapping);
|
|
return g_pbMappedIndexFile;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::CloseIndexFile
|
|
|
|
Description: Closes the current index mapping and file.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::CloseIndexFile(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::IndexMgr::CloseIndexFile")));
|
|
if (NULL != g_pbMappedIndexFile)
|
|
{
|
|
UnmapViewOfFile(g_pbMappedIndexFile);
|
|
g_pbMappedIndexFile = NULL;
|
|
m_pFileHdr = NULL;
|
|
}
|
|
if (NULL != m_hFileMapping)
|
|
{
|
|
CloseHandle(m_hFileMapping);
|
|
m_hFileMapping = NULL;
|
|
}
|
|
if (NULL != m_hFile && INVALID_HANDLE_VALUE != m_hFile)
|
|
{
|
|
CloseHandle(m_hFile);
|
|
m_hFile = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::GrowIndexFile
|
|
|
|
Description: Increases the size of the current index file.
|
|
|
|
Arguments:
|
|
cGrowEntries - Make room for this many more entries. Note that the
|
|
size of the hash table is fixed. If we were to allow this to
|
|
change, it would invalidate any existing hash values in the
|
|
table (hash code is a function of the SID and table size).
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::IndexMgr::GrowIndexFile(
|
|
DWORD cGrowEntries
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::IndexMgr::GrowIndexFile")));
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
DWORD cOldMaxEntries = m_pFileHdr->cMaxEntries;
|
|
DWORD cNewMaxEntries = cOldMaxEntries + cGrowEntries;
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_MID,
|
|
TEXT("Growing SID cache index %d -> %d entries."),
|
|
cOldMaxEntries, cNewMaxEntries));
|
|
|
|
//
|
|
// Open the existing file and map with a new larger size.
|
|
// Must calc new size BEFORE closing current index file so that m_pFileHdr
|
|
// is still valid.
|
|
//
|
|
ULARGE_INTEGER uliFileSize;
|
|
uliFileSize.QuadPart = FileSize(cNewMaxEntries, m_pFileHdr->cBuckets);
|
|
|
|
CloseIndexFile();
|
|
|
|
g_pbMappedIndexFile = SidNameCache::OpenMappedFile(
|
|
m_strFileName,
|
|
g_pszIndexFileMapping,
|
|
OPEN_EXISTING,
|
|
uliFileSize.HighPart,
|
|
uliFileSize.LowPart,
|
|
&m_hFile,
|
|
&m_hFileMapping);
|
|
|
|
m_pFileHdr = (PINDEX_FILE_HDR)g_pbMappedIndexFile;
|
|
|
|
if (NULL != g_pbMappedIndexFile)
|
|
{
|
|
m_pFileHdr->cMaxEntries = cNewMaxEntries;
|
|
|
|
//
|
|
// Growing the index only expands the number of index
|
|
// pool entries. The index hash table is left alone.
|
|
// Good reason to make it large to start with. If we changed
|
|
// the hash table size, existing hash codes would be
|
|
// invalid.
|
|
//
|
|
PINDEX_ENTRY pEntry = m_pFileHdr->pEntries + cOldMaxEntries;
|
|
for (UINT i = 0; i < cGrowEntries; i++)
|
|
{
|
|
AddEntryToFreeList(pEntry++);
|
|
}
|
|
DBGPRINT((DM_SIDCACHE, DL_HIGH, TEXT("SIDCACHE - Index growth complete.")));
|
|
}
|
|
|
|
return g_pbMappedIndexFile;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::InitNewIndexFile
|
|
|
|
Description: Initializes a new index file filling in the header information
|
|
and clearing the index entries.
|
|
|
|
Arguments:
|
|
cBuckets - Number of hash table buckets in index file. Should be
|
|
prime.
|
|
|
|
cMaxEntries - Initial max number of entries for the index. Note
|
|
that the index file grows automatically as required.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::InitNewIndexFile(
|
|
DWORD cBuckets,
|
|
DWORD cMaxEntries
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::IndexMgr::InitNewIndexFile")));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("\tcBuckets = %d, cMaxEntries = %d"),
|
|
cBuckets, cMaxEntries));
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
m_pFileHdr->dwSignature = INDEX_FILE_SIGNATURE;
|
|
m_pFileHdr->dwVersion = FILE_VERSION;
|
|
m_pFileHdr->cBuckets = cBuckets;
|
|
m_pFileHdr->cMaxEntries = cMaxEntries;
|
|
m_pFileHdr->pBuckets = (PINDEX_ENTRY *)(sizeof(INDEX_FILE_HDR));
|
|
m_pFileHdr->pEntries = (PINDEX_ENTRY)(m_pFileHdr->pBuckets + cBuckets);
|
|
|
|
//
|
|
// Initialize the hash table and return all entries to the free list.
|
|
//
|
|
Clear();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Clear
|
|
|
|
Description: Fills the hash table with NULL pointers and returns all
|
|
entry nodes to the free list.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::Clear(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::IndexMgr::Clear")));
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
m_pFileHdr->cEntries = 0;
|
|
SetFileGUID(&GUID_Null);
|
|
|
|
//
|
|
// Initialize all hash buckets to NULL.
|
|
//
|
|
DBGASSERT((0 < m_pFileHdr->cBuckets));
|
|
ZeroMemory(NDX_BASED_CAST(BYTE, m_pFileHdr->pBuckets),
|
|
m_pFileHdr->cBuckets * sizeof(PINDEX_ENTRY *));
|
|
|
|
//
|
|
// Return all index entry nodes to the free list.
|
|
//
|
|
PINDEX_ENTRY pEntry = m_pFileHdr->pEntries;
|
|
DBGASSERT((0 < m_pFileHdr->cMaxEntries));
|
|
for (UINT i = 0; i < m_pFileHdr->cMaxEntries; i++)
|
|
{
|
|
//
|
|
// We're iterating through all entries. No need to detach first.
|
|
//
|
|
AddEntryToFreeList(pEntry++);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::SetFileGUID
|
|
Function: SidNameCache::IndexMgr::GetFileGUID
|
|
|
|
Description: These functions manipulate the guid field in the index file's
|
|
header.
|
|
The GUID is used to ensure integrity between
|
|
the data and index files. Before any change to either file, the
|
|
GUID's are both set to 0. When the change operation is complete,
|
|
a new GUID is generated and written to both files. Therefore, before
|
|
any transaction, we can validate the data and index files by reading
|
|
and comparing GUIDs. If the GUIDs are not 0 and are equal, the
|
|
file can be assumed to be valid.
|
|
|
|
Arguments:
|
|
pguid - Address of source or destination GUID.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::SetFileGUID(
|
|
const GUID *pguid
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pguid));
|
|
CopyMemory(&m_pFileHdr->guid, pguid, sizeof(GUID));
|
|
}
|
|
|
|
VOID
|
|
SidNameCache::IndexMgr::GetFileGUID(
|
|
LPGUID pguid
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pguid));
|
|
CopyMemory(pguid, &m_pFileHdr->guid, sizeof(GUID));
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::AllocEntry
|
|
|
|
Description: Allocates an entry from the free list.
|
|
|
|
Arguments: None.
|
|
|
|
Returns:
|
|
Address of new entry node or NULL if free list is empty.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::AllocEntry(
|
|
VOID
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
PINDEX_ENTRY pEntry = m_pFileHdr->pFirstFree;
|
|
|
|
if (NULL != pEntry)
|
|
{
|
|
NDX_BASED(INDEX_ENTRY) *pBasedEntry = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
|
|
//
|
|
// Unlink the entry from the free list.
|
|
//
|
|
m_pFileHdr->pFirstFree = pBasedEntry->pNext;
|
|
|
|
//
|
|
// Clear it's "prev" and "next" pointers.
|
|
//
|
|
pBasedEntry->pNext = pBasedEntry->pPrev = NULL;
|
|
|
|
if (NULL != m_pFileHdr->pFirstFree)
|
|
{
|
|
//
|
|
// If there is at least one entry in the free list, set the "prev"
|
|
// pointer of the new "first" entry to NULL.
|
|
//
|
|
pBasedEntry = NDX_BASED_CAST(INDEX_ENTRY, m_pFileHdr->pFirstFree);
|
|
pBasedEntry->pPrev = NULL;
|
|
}
|
|
|
|
m_pFileHdr->cEntries++;
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::FreeEntry
|
|
|
|
Description: Removes an entry from it's current list and returns it to the
|
|
free list. Two versions are provided. One accepts a SID as an entry
|
|
identifier, the other accepts the address of the index entry.
|
|
|
|
Arguments:
|
|
pSid - Address of SID associated with entry to be free'd.
|
|
|
|
pEntry - Address of index entry to be free'd
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::FreeEntry(
|
|
PSID pSid
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pSid));
|
|
PINDEX_ENTRY pEntry = Find(pSid);
|
|
if (NULL != pEntry)
|
|
{
|
|
FreeEntry(pEntry);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SidNameCache::IndexMgr::FreeEntry(
|
|
PINDEX_ENTRY pEntry
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pEntry));
|
|
DetachEntry(pEntry);
|
|
AddEntryToFreeList(pEntry);
|
|
|
|
m_pFileHdr->cEntries--;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::AddEntryToFreeList
|
|
|
|
Description: Returns a detached index entry to the free list.
|
|
|
|
Arguments:
|
|
pEntry - Address of index entry to be added to free list.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::AddEntryToFreeList(
|
|
PINDEX_ENTRY pEntry
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pEntry));
|
|
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
|
|
//
|
|
// Insert the node at the head of the free list.
|
|
// Note that double-linking isn't necessary in the free list
|
|
// (we always add and remove free list entries at the head)
|
|
// therefore we don't need to set the next node's "prev" pointer.
|
|
//
|
|
pBased->iBucket = (DWORD)-1;
|
|
pBased->iBlock = (DWORD)-1;
|
|
pBased->pPrev = NULL;
|
|
pBased->pNext = m_pFileHdr->pFirstFree;
|
|
m_pFileHdr->pFirstFree = pEntry;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::DetachEntry
|
|
|
|
Description: Detaches an index entry from its current list.
|
|
Note that this function assumes that the node exists in a valid
|
|
linked list of nodes. Do not call this function on an uninitialized
|
|
index entry.
|
|
|
|
Arguments:
|
|
pEntry - Address of index entry to be detached.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::DetachEntry(
|
|
PINDEX_ENTRY pEntry
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pEntry));
|
|
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
NDX_BASED(INDEX_ENTRY) *pBasedNext;
|
|
NDX_BASED(INDEX_ENTRY) *pBasedPrev;
|
|
|
|
//
|
|
// Unlink the entry from it's list.
|
|
//
|
|
if (NULL != pBased->pPrev)
|
|
{
|
|
pBasedPrev = NDX_BASED_CAST(INDEX_ENTRY, pBased->pPrev);
|
|
pBasedPrev->pNext = pBased->pNext;
|
|
}
|
|
if (NULL != pBased->pNext)
|
|
{
|
|
pBasedNext = NDX_BASED_CAST(INDEX_ENTRY, pBased->pNext);
|
|
pBasedNext->pPrev = pBased->pPrev;
|
|
}
|
|
//
|
|
// If we're detaching the entry that's attached to the hash array element,
|
|
// adjust the element's value.
|
|
//
|
|
if (GetHashBucketValue(pBased->iBucket) == pEntry)
|
|
SetHashBucketValue(pBased->iBucket, pBased->pNext);
|
|
|
|
pBased->pNext = pBased->pPrev = NULL;
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::GetHashBucketValue
|
|
|
|
Description: Returns the address of the first index entry in a hash bucket.
|
|
|
|
Arguments:
|
|
iBucket - Array index of bucket in hash table array.
|
|
|
|
Returns:
|
|
Address of first entry in bucket's entry list.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::GetHashBucketValue(
|
|
DWORD iBucket
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((iBucket < m_pFileHdr->cBuckets));
|
|
|
|
NDX_BASED(PINDEX_ENTRY) *pBased = NDX_BASED_CAST(PINDEX_ENTRY, m_pFileHdr->pBuckets + iBucket);
|
|
return *pBased;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::SetHashBucketValue
|
|
|
|
Description: Sets the address of the first index entry in a hash bucket.
|
|
OK to set it as NULL.
|
|
|
|
Arguments:
|
|
iBucket - Array index of bucket in hash table array.
|
|
|
|
pEntry - Address of entry node.
|
|
|
|
Returns:
|
|
Address of first entry in bucket's entry list.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::IndexMgr::SetHashBucketValue(
|
|
DWORD iBucket,
|
|
PINDEX_ENTRY pEntry
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((iBucket < m_pFileHdr->cBuckets));
|
|
//
|
|
// pEntry == NULL is OK.
|
|
//
|
|
|
|
NDX_BASED(PINDEX_ENTRY) *pBased = NDX_BASED_CAST(PINDEX_ENTRY,
|
|
m_pFileHdr->pBuckets + iBucket);
|
|
*pBased = pEntry;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Find (3 overloaded methods)
|
|
|
|
Description: Given either a SID, a SID and hash value, or a user logon name,
|
|
this method returns the address of the index entry
|
|
representing that cache entry.
|
|
|
|
Arguments:
|
|
pKeySid - Address of SID to use as a lookup key.
|
|
|
|
dwHashCode - Result of calling Hash(pKeySid).
|
|
|
|
pszKeyLogonName - Address of account logon name string.
|
|
|
|
Returns:
|
|
Address of index entry representing the user.
|
|
NULL if not found.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::Find(
|
|
PSID pKeySid
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pKeySid));
|
|
|
|
return Find(pKeySid, Hash(pKeySid)); // Can throw OutOfMemory.
|
|
}
|
|
|
|
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::Find(
|
|
PSID pKeySid,
|
|
DWORD dwHashCode
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pKeySid));
|
|
|
|
BOOL bFound = FALSE;
|
|
PINDEX_ENTRY pEntry = GetHashBucketValue(dwHashCode);
|
|
while(NULL != pEntry && !bFound)
|
|
{
|
|
PSID pSid = NULL;
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
|
|
//
|
|
// This can throw OutOfMemory.
|
|
//
|
|
if (SUCCEEDED(m_refCache.m_pRecordMgr->Retrieve(pBased->iBlock,
|
|
&pSid,
|
|
NULL,
|
|
NULL,
|
|
NULL)))
|
|
{
|
|
if (EqualSid(pKeySid, pSid))
|
|
{
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
if (!bFound)
|
|
{
|
|
pEntry = pBased->pNext;
|
|
}
|
|
|
|
delete[] pSid;
|
|
}
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
|
|
//
|
|
// This version of Find() performs a linear search of the index to locate
|
|
// the specified logon name. The cache is currently indexed only
|
|
// on user SID because this is the only key used for very rapid lookups.
|
|
// The cache implementation could be easily extended to include a
|
|
// logon name index. All that's needed is a second hash bucket array,
|
|
// a hash-on-name function and some adjustments to the placement of
|
|
// file data. I just don't think the benefit is worth the cost.
|
|
//
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::Find(
|
|
LPCTSTR pszKeyLogonName
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DBGASSERT((NULL != pszKeyLogonName));
|
|
|
|
BOOL bFound = FALSE;
|
|
PINDEX_ENTRY pEntry = NULL;
|
|
|
|
for (UINT i = 0; !bFound && (i < m_pFileHdr->cBuckets); i++)
|
|
{
|
|
pEntry = GetHashBucketValue(i);
|
|
while(NULL != pEntry && !bFound)
|
|
{
|
|
array_autoptr<TCHAR> ptrLogonName;
|
|
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
|
|
//
|
|
// This can throw OutOfMemory.
|
|
//
|
|
if (SUCCEEDED(m_refCache.m_pRecordMgr->Retrieve(pBased->iBlock,
|
|
NULL,
|
|
NULL, // no container.
|
|
ptrLogonName.getaddr(),
|
|
NULL))) // no display name
|
|
{
|
|
DBGASSERT((NULL != ptrLogonName.get()));
|
|
|
|
if (0 == lstrcmpi(ptrLogonName.get(), pszKeyLogonName))
|
|
{
|
|
bFound = TRUE;
|
|
}
|
|
}
|
|
if (!bFound)
|
|
{
|
|
pEntry = pBased->pNext;
|
|
}
|
|
}
|
|
}
|
|
return pEntry;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Lookup (2 overloaded methods)
|
|
|
|
Description: Given either a SID or an account logon name,
|
|
this method returns the starting block index of the corresponding
|
|
record in the data file.
|
|
|
|
Arguments:
|
|
pSid - Address of SID to use as a lookup key.
|
|
|
|
pszLogonName - Address of account logon name string.
|
|
|
|
Returns:
|
|
Index of the starting block for the record in the data file.
|
|
(DWORD)-1 if the record is not found.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameCache::IndexMgr::Lookup(
|
|
PSID pSid
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pSid));
|
|
|
|
//
|
|
// This can throw OutOfMemory.
|
|
//
|
|
PINDEX_ENTRY pEntry = Find(pSid, Hash(pSid));
|
|
|
|
if (NULL != pEntry)
|
|
{
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
return pBased->iBlock;
|
|
}
|
|
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
SidNameCache::IndexMgr::Lookup(
|
|
LPCTSTR pszLogonName
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pszLogonName));
|
|
|
|
//
|
|
// This can throw OutOfMemory.
|
|
//
|
|
PINDEX_ENTRY pEntry = Find(pszLogonName);
|
|
|
|
if (NULL != pEntry)
|
|
{
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
return pBased->iBlock;
|
|
}
|
|
|
|
return (DWORD)-1;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Add
|
|
|
|
Description: Adds a SID/data-file-index pair to the index file. This
|
|
entry can later be used to locate the SID's record in the data file.
|
|
|
|
Arguments:
|
|
pSid - Address of SID to use as a lookup key.
|
|
|
|
iBlock - Index of the starting block for the SID's record in the
|
|
data file.
|
|
|
|
Returns: Address of the item's new index entry.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
PINDEX_ENTRY
|
|
SidNameCache::IndexMgr::Add(
|
|
PSID pSid,
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DWORD dwHashCode = Hash(pSid);
|
|
PINDEX_ENTRY pEntry = Find(pSid, dwHashCode); // Can throw OutOfMemory.
|
|
|
|
//
|
|
// Don't create duplicate entries.
|
|
//
|
|
if (NULL == pEntry)
|
|
{
|
|
//
|
|
// Try to allocate an index entry from the free list.
|
|
//
|
|
pEntry = AllocEntry();
|
|
if (NULL == pEntry)
|
|
{
|
|
//
|
|
// Grow the index file and try again.
|
|
//
|
|
GrowIndexFile(INDEX_FILE_GROW_ENTRIES);
|
|
pEntry = AllocEntry();
|
|
}
|
|
if (NULL != pEntry)
|
|
{
|
|
NDX_BASED(INDEX_ENTRY) *pBasedEntry = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
NDX_BASED(INDEX_ENTRY) *pBasedNext;
|
|
|
|
//
|
|
// Fill in the members of the new entry.
|
|
//
|
|
pBasedEntry->iBucket = dwHashCode;
|
|
pBasedEntry->iBlock = iBlock;
|
|
pBasedEntry->pNext = GetHashBucketValue(dwHashCode);
|
|
pBasedEntry->pPrev = NULL;
|
|
//
|
|
// Now insert it at the head of the hash bucket's entry list.
|
|
//
|
|
if (NULL != pBasedEntry->pNext)
|
|
{
|
|
pBasedNext = NDX_BASED_CAST(INDEX_ENTRY, pBasedEntry->pNext);
|
|
pBasedNext->pPrev = pEntry;
|
|
}
|
|
SetHashBucketValue(dwHashCode, pEntry);
|
|
}
|
|
}
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Hash
|
|
|
|
Description: Given a SID, this method calculates a hash value to be used
|
|
as an offset into the index's hash table bucket array. The
|
|
algorithm simply sums the value of the SID's bytes. The resulting
|
|
hash code is this sum modulo the size of the hash table. For this
|
|
simple algorithm to be effective, it is important that the hash
|
|
table size be a prime number.
|
|
|
|
Here's some representative primes: 101, 503, 1009, 5003, 10007
|
|
|
|
Arguments:
|
|
pSid - Address of SID to use as an index lookup key.
|
|
|
|
Returns: Hashed SID. The value will be between 0 and m_pFileHdr->cBuckets.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameCache::IndexMgr::Hash(
|
|
PSID pSid
|
|
)
|
|
{
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
DWORD dwCode = 0;
|
|
PBYTE pbSid = (PBYTE)pSid;
|
|
PBYTE pbEndSid = pbSid + GetLengthSid(pSid);
|
|
|
|
for ( ;pbSid < pbEndSid; pbSid++)
|
|
dwCode += *pbSid;
|
|
|
|
return dwCode % m_pFileHdr->cBuckets;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IndexMgr::Dump [DEBUG only]
|
|
|
|
Description: Dumps the contents of the index file to the debugger output.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#if DBG
|
|
VOID
|
|
SidNameCache::IndexMgr::Dump(
|
|
VOID
|
|
)
|
|
{
|
|
UINT i, j;
|
|
DBGASSERT((INDEX_FILE_MAPPED));
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("Dumping SidNameCache IndexMgr at 0x%p"), this));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" Base...............: 0x%p"), g_pbMappedIndexFile));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" m_pFileHdr.........: 0x%p"), m_pFileHdr));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" dwSignature......: 0x%08X"), (DWORD)m_pFileHdr->dwSignature));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" dwVersion........: 0x%08X"), (DWORD)m_pFileHdr->dwVersion));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" cBuckets.........: %d"), (DWORD)m_pFileHdr->cBuckets));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" cMaxEntries......: %d"), (DWORD)m_pFileHdr->cMaxEntries));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" cEntries.........: %d"), (DWORD)m_pFileHdr->cEntries));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" pBuckets.........: 0x%p"), m_pFileHdr->pBuckets));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" pEntries.........: 0x%p"), m_pFileHdr->pEntries));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" pFirstFree.......: 0x%p"), m_pFileHdr->pFirstFree));
|
|
|
|
for (i = 0; i < m_pFileHdr->cBuckets; i++)
|
|
{
|
|
PINDEX_ENTRY pEntry = GetHashBucketValue(i);
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" Bucket[%03d] = 0x%p"), i, pEntry));
|
|
|
|
while(NULL != pEntry)
|
|
{
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" Bkt = %3d P = 0x%08X N = 0x%08X Blk = %d"),
|
|
pBased->iBucket,
|
|
pBased->pPrev,
|
|
pBased->pNext,
|
|
pBased->iBlock));
|
|
|
|
pEntry = pBased->pNext;
|
|
}
|
|
}
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" FreeList")));
|
|
PINDEX_ENTRY pEntry = m_pFileHdr->pFirstFree;
|
|
while(NULL != pEntry)
|
|
{
|
|
NDX_BASED(INDEX_ENTRY) *pBased = NDX_BASED_CAST(INDEX_ENTRY, pEntry);
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" Bkt = %3d P = 0x%08X N = 0x%08X Blk = %d"),
|
|
pBased->iBucket,
|
|
pBased->pPrev,
|
|
pBased->pNext,
|
|
pBased->iBlock));
|
|
|
|
pEntry = pBased->pNext;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
//***********************************************************************************
|
|
//***********************************************************************************
|
|
// D A T A F I L E M A N A G E R
|
|
//***********************************************************************************
|
|
//***********************************************************************************
|
|
|
|
//
|
|
// Default cache record life values in days.
|
|
//
|
|
const DWORD DEF_REC_LIFE_MIN = 30;
|
|
const DWORD DEF_REC_LIFE_MAX = 60;
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::RecordMgr
|
|
|
|
Description: Record manager constructor.
|
|
|
|
Arguments:
|
|
refCache - Reference to containing cache object. Used to call
|
|
index manager and cache manager public methods.
|
|
|
|
Returns: Nothing.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameCache::RecordMgr::RecordMgr(
|
|
SidNameCache& refCache
|
|
) : m_refCache(refCache),
|
|
m_pFileHdr(NULL),
|
|
m_hFile(NULL),
|
|
m_hFileMapping(NULL),
|
|
m_cDaysRecLifeMin(DEF_REC_LIFE_MIN),
|
|
m_cDaysRecLifeRange(DEF_REC_LIFE_MAX - DEF_REC_LIFE_MIN)
|
|
{
|
|
DWORD cDaysRecLifeMax = DEF_REC_LIFE_MAX;
|
|
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::SidNameCache::RecordMgr")));
|
|
|
|
//
|
|
// Retrieve the min/max record life (days) from the registry.
|
|
// Note that we store record life min and range since those are
|
|
// what's used in the record life calculation. Seems more
|
|
// self-explanatory to store min/max in the registry rather than
|
|
// min/range.
|
|
//
|
|
RegKey key(HKEY_CURRENT_USER, REGSTR_KEY_DISKQUOTA);
|
|
if (key.Open(KEY_WRITE, true))
|
|
{
|
|
if (FAILED(key.GetValue(g_szSidCacheRecLifeMin, &m_cDaysRecLifeMin)) ||
|
|
65536 <= m_cDaysRecLifeMin)
|
|
{
|
|
m_cDaysRecLifeMin = DEF_REC_LIFE_MIN; // Default;
|
|
key.SetValue(g_szSidCacheRecLifeMin, m_cDaysRecLifeMin);
|
|
}
|
|
if (FAILED(key.GetValue(g_szSidCacheRecLifeMax, &cDaysRecLifeMax)) ||
|
|
65536 <= cDaysRecLifeMax)
|
|
{
|
|
cDaysRecLifeMax = DEF_REC_LIFE_MAX; // Default;
|
|
key.SetValue(g_szSidCacheRecLifeMax, cDaysRecLifeMax);
|
|
}
|
|
}
|
|
|
|
if (cDaysRecLifeMax < m_cDaysRecLifeMin)
|
|
cDaysRecLifeMax = m_cDaysRecLifeMin;
|
|
|
|
m_cDaysRecLifeRange = cDaysRecLifeMax - m_cDaysRecLifeMin;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::~RecordMgr
|
|
|
|
Description: Record manager destructor. Closes the data file.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
SidNameCache::RecordMgr::~RecordMgr(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_MID, TEXT("SidNameCache::SidNameCache::~RecordMgr")));
|
|
CloseDataFile();
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::Initialize
|
|
|
|
Description: Initializes a new record manager object.
|
|
|
|
Arguments:
|
|
pszFile - Address of full path for new file.
|
|
|
|
cBlocks - Number of storage blocks in data file. Each is 32 bytes.
|
|
If this argument is 0, the function tries to open an existing
|
|
cache data file.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::RecordMgr::Initialize(
|
|
LPCTSTR pszFile,
|
|
DWORD cBlocks
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_HIGH, TEXT("SidNameCache::RecordMgr::Initialize")));
|
|
DBGPRINT((DM_SIDCACHE, DL_HIGH, TEXT("\tpszFile = \"%s\", cBlocks = %d"),
|
|
pszFile ? pszFile : TEXT("<null>"), cBlocks));
|
|
//
|
|
// Store the file name in our CString object.
|
|
//
|
|
m_strFileName = pszFile;
|
|
|
|
if (0 != cBlocks)
|
|
{
|
|
//
|
|
// Create a new data file.
|
|
//
|
|
// cBlocks must be a multiple of 32.
|
|
// This satisfies the quadword alignment and makes sizing the
|
|
// allocation bitmap much easier. We let the caller pass in any value
|
|
// they want and we just adjust it upward as needed.
|
|
//
|
|
if (cBlocks & 0x0000001F)
|
|
cBlocks = (cBlocks & 0xFFFFFFE0) + 32;
|
|
|
|
ULARGE_INTEGER uliFileSize;
|
|
uliFileSize.QuadPart = FileSize(cBlocks);
|
|
|
|
m_pFileHdr = (PDATA_FILE_HDR)CreateDataFile(pszFile,
|
|
uliFileSize.HighPart,
|
|
uliFileSize.LowPart);
|
|
if (NULL != m_pFileHdr)
|
|
{
|
|
InitNewDataFile(cBlocks);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Open an existing data file.
|
|
//
|
|
m_pFileHdr = (PDATA_FILE_HDR)OpenDataFile(pszFile);
|
|
if (NULL != m_pFileHdr)
|
|
{
|
|
if (FILE_VERSION != m_pFileHdr->dwVersion ||
|
|
DATA_FILE_SIGNATURE != m_pFileHdr->dwSignature)
|
|
{
|
|
//
|
|
// This version of the software doesn't understand this
|
|
// version of the file or the signature is invalid.
|
|
// Don't take any chances. We'll just create a new one.
|
|
//
|
|
DBGERROR((TEXT("SIDCACHE - Data file is invalid or incorrect version. A new data file will be created.")));
|
|
|
|
CloseDataFile();
|
|
m_pFileHdr = NULL;
|
|
}
|
|
}
|
|
}
|
|
return (LPBYTE)m_pFileHdr;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::CreateDataFile
|
|
|
|
Description: Creates and initializes a new data file.
|
|
|
|
Arguments:
|
|
pszFile - Address of full path for new file.
|
|
|
|
cbFileHigh/Low - Size of file in bytes.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::RecordMgr::CreateDataFile(
|
|
LPCTSTR pszFile,
|
|
DWORD cbFileHigh,
|
|
DWORD cbFileLow
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::RecordMgr::CreateDataFile")));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("\tpszFile = \"%s\""),
|
|
pszFile ? pszFile : TEXT("<null>")));
|
|
|
|
CloseDataFile(); // Make sure any existing data file is closed.
|
|
|
|
g_pbMappedDataFile = SidNameCache::OpenMappedFile(
|
|
pszFile,
|
|
g_pszDataFileMapping,
|
|
CREATE_ALWAYS,
|
|
cbFileHigh,
|
|
cbFileLow,
|
|
&m_hFile,
|
|
&m_hFileMapping);
|
|
|
|
return g_pbMappedDataFile;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::OpenDataFile
|
|
|
|
Description: Opens an existing data file.
|
|
|
|
Arguments:
|
|
pszFile - Address of full path of existing file.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::RecordMgr::OpenDataFile(
|
|
LPCTSTR pszFile
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::RecordMgr::OpenDataFile")));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("\tpszFile = \"%s\""),
|
|
pszFile ? pszFile : TEXT("<null>")));
|
|
|
|
CloseDataFile(); // Make sure any existing data file is closed.
|
|
|
|
g_pbMappedDataFile = SidNameCache::OpenMappedFile(
|
|
pszFile,
|
|
g_pszDataFileMapping,
|
|
OPEN_EXISTING,
|
|
0,
|
|
0,
|
|
&m_hFile,
|
|
&m_hFileMapping);
|
|
|
|
return g_pbMappedDataFile;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::CloseDataFile
|
|
|
|
Description: Closes the current data mapping and file.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::CloseDataFile(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::RecordMgr::CloseDataFile")));
|
|
if (NULL != g_pbMappedDataFile)
|
|
{
|
|
UnmapViewOfFile(g_pbMappedDataFile);
|
|
g_pbMappedDataFile = NULL;
|
|
m_pFileHdr = NULL;
|
|
}
|
|
if (NULL != m_hFileMapping)
|
|
{
|
|
CloseHandle(m_hFileMapping);
|
|
m_hFileMapping = NULL;
|
|
}
|
|
if (NULL != m_hFile && INVALID_HANDLE_VALUE != m_hFile)
|
|
{
|
|
CloseHandle(m_hFile);
|
|
m_hFile = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::GrowDataFile
|
|
|
|
Description: Increases the size of the current data file.
|
|
|
|
Arguments:
|
|
cGrowBlocks - Add this many more blocks to the data file. The
|
|
block allocation bitmap is also extended to accomdate the new
|
|
block count.
|
|
|
|
Returns: Address of mapped file or NULL on failure.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
LPBYTE
|
|
SidNameCache::RecordMgr::GrowDataFile(
|
|
DWORD cGrowBlocks
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::RecordMgr::GrowDataFile")));
|
|
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
|
|
DWORD cOldBlocks = m_pFileHdr->cBlocks;
|
|
DWORD cNewBlocks = cOldBlocks + cGrowBlocks;
|
|
DWORD cOldMapEle = m_pFileHdr->cMapElements;
|
|
DWORD cNewMapEle = 0; // Will calc later.
|
|
|
|
//
|
|
// cBlocks must be a multiple of 32.
|
|
// This satisfies the quadword alignment and makes sizing the
|
|
// allocation bitmap much easier.
|
|
//
|
|
if (cNewBlocks & 0x0000001F)
|
|
cNewBlocks = (cNewBlocks & 0xFFFFFFE0) + 32;
|
|
DBGASSERT((cNewBlocks >= cOldBlocks));
|
|
|
|
//
|
|
// Adjust cGrowBlocks for any adjustments in cNewBlocks.
|
|
//
|
|
cGrowBlocks = cNewBlocks - cOldBlocks;
|
|
|
|
//
|
|
// How many alloc map elements (DWORDs) do we need now?
|
|
//
|
|
cNewMapEle = cNewBlocks / BITS_IN_DWORD;
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_MID,
|
|
TEXT("Growing SID cache data file\nMap Ele %d -> %d\nBlocks %d -> %d"),
|
|
cOldMapEle, cNewMapEle, cOldBlocks, cNewBlocks));
|
|
|
|
//
|
|
// Open the mapped file with a new larger size.
|
|
//
|
|
ULARGE_INTEGER uliFileSize;
|
|
uliFileSize.QuadPart = FileSize(cNewBlocks);
|
|
|
|
CloseDataFile();
|
|
|
|
g_pbMappedDataFile = SidNameCache::OpenMappedFile(
|
|
m_strFileName,
|
|
g_pszDataFileMapping,
|
|
OPEN_EXISTING,
|
|
uliFileSize.HighPart,
|
|
uliFileSize.LowPart,
|
|
&m_hFile,
|
|
&m_hFileMapping);
|
|
|
|
m_pFileHdr = (PDATA_FILE_HDR)g_pbMappedDataFile;
|
|
|
|
if (NULL != g_pbMappedDataFile)
|
|
{
|
|
UINT i = 0;
|
|
|
|
//
|
|
// Block count and map size both increase.
|
|
// Since map size increases, blocks must be moved to accomodate new
|
|
// map elements. Since the index file tracks records by block index,
|
|
// this movement doesn't affect existing index file entries.
|
|
//
|
|
m_pFileHdr->cBlocks = cNewBlocks;
|
|
m_pFileHdr->cMapElements = cNewMapEle;
|
|
//
|
|
// Save current block base for when we move the blocks to make room for
|
|
// the growth of the allocation bitmap.
|
|
//
|
|
PBLOCK pBlocksOld = m_pFileHdr->pBlocks;
|
|
//
|
|
// Calculate the new address of block 0.
|
|
// We want all of the data blocks quadword aligned because they contain
|
|
// a FILETIME structure (64-bits).
|
|
//
|
|
m_pFileHdr->pBlocks = (PBLOCK)(m_pFileHdr->pdwMap + m_pFileHdr->cMapElements);
|
|
QuadAlign((LPDWORD)(&m_pFileHdr->pBlocks));
|
|
|
|
//
|
|
// Move all of the existing blocks to their new locations.
|
|
//
|
|
MoveMemory(DAT_BASED_CAST(BLOCK, m_pFileHdr->pBlocks),
|
|
DAT_BASED_CAST(BLOCK, pBlocksOld),
|
|
cOldBlocks * sizeof(BLOCK));
|
|
//
|
|
// Initialize the new map elements to 0 (un-allocated).
|
|
//
|
|
ZeroMemory(DAT_BASED_CAST(BYTE, m_pFileHdr->pdwMap + cOldMapEle),
|
|
(cNewMapEle - cOldMapEle) * sizeof(DWORD));
|
|
//
|
|
// Initialize the new data blocks to 0xCC pattern.
|
|
//
|
|
FillBlocks(cOldBlocks, cGrowBlocks, RECORD_UNUSED_BYTE);
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_MID, TEXT("SIDCACHE - Data file growth complete.")));
|
|
}
|
|
return g_pbMappedDataFile;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::SetFileGUID
|
|
Function: SidNameCache::RecordMgr::GetFileGUID
|
|
|
|
Description: These functions manipulate the guid field in the data file's
|
|
header.
|
|
The GUID is used to ensure integrity between
|
|
the data and index files. Before any change to either file, the
|
|
GUID's are both set to 0. When the change operation is complete,
|
|
a new GUID is generated and written to both files. Therefore, before
|
|
any transaction, we can validate the data and index files by reading
|
|
and comparing GUIDs. If the GUIDs are not 0 and are equal, the
|
|
file can be assumed to be valid.
|
|
|
|
Arguments:
|
|
pguid - Address of source or destination GUID.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::SetFileGUID(
|
|
const GUID *pguid
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((NULL != pguid));
|
|
CopyMemory(&m_pFileHdr->guid, pguid, sizeof(GUID));
|
|
}
|
|
|
|
|
|
VOID
|
|
SidNameCache::RecordMgr::GetFileGUID(
|
|
LPGUID pguid
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((NULL != pguid));
|
|
CopyMemory(pguid, &m_pFileHdr->guid, sizeof(GUID));
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::InitNewDataFile
|
|
|
|
Description: Initializes a new data file filling in the header information
|
|
and writing 0xCC in all the data block bytes.
|
|
|
|
Arguments:
|
|
cBlocks - Number of data blocks (32 bytes each) in data file.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::InitNewDataFile(
|
|
DWORD cBlocks
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::RecordMgr::InitNewDataFile")));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("\tcBlocks = %d"), cBlocks));
|
|
UINT i = 0;
|
|
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
|
|
//
|
|
// Initialize file header.
|
|
//
|
|
m_pFileHdr->dwSignature = DATA_FILE_SIGNATURE;
|
|
m_pFileHdr->dwVersion = FILE_VERSION;
|
|
m_pFileHdr->cBlocks = cBlocks;
|
|
m_pFileHdr->cMapElements = cBlocks / BITS_IN_DWORD;
|
|
m_pFileHdr->pdwMap = (LPDWORD)(sizeof(DATA_FILE_HDR));
|
|
m_pFileHdr->pBlocks = (PBLOCK)(m_pFileHdr->pdwMap + m_pFileHdr->cMapElements);
|
|
|
|
//
|
|
// We want all of the data blocks quadword aligned because they contain
|
|
// a FILETIME structure (64-bits).
|
|
//
|
|
QuadAlign((LPDWORD)(&m_pFileHdr->pBlocks));
|
|
|
|
//
|
|
// Write 0xCC to all data block bytes.
|
|
//
|
|
Clear();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::Clear
|
|
|
|
Description: Zero's the file header guid, clears all bits in the
|
|
block allocation bitmap and fills the data blocks with 0xCC.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::Clear(
|
|
VOID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SIDCACHE, DL_LOW, TEXT("SidNameCache::RecordMgr::Clear")));
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
|
|
m_pFileHdr->cBlocksUsed = 0;
|
|
m_pFileHdr->iFirstFree = 0;
|
|
SetFileGUID(&GUID_Null);
|
|
|
|
//
|
|
// Initialize all block allocation map bits to 0 (un-allocated).
|
|
//
|
|
ZeroMemory(DAT_BASED_CAST(BYTE, m_pFileHdr->pdwMap),
|
|
m_pFileHdr->cMapElements * sizeof(DWORD));
|
|
//
|
|
// Initialize all data blocks to 0xCC pattern.
|
|
//
|
|
FillBlocks(0, m_pFileHdr->cBlocks, RECORD_UNUSED_BYTE);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::FillBlocks
|
|
|
|
Description: Fills a range of blocks with a specified byte.
|
|
|
|
Arguments:
|
|
iBlock - Index of first block in range.
|
|
|
|
cBlocks - Number of blocks to fill.
|
|
|
|
b - Byte to write to blocks.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::FillBlocks(
|
|
DWORD iBlock,
|
|
DWORD cBlocks,
|
|
BYTE b
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
|
|
DAT_BASED(BYTE) *pb = DAT_BASED_CAST(BYTE, BlockAddress(iBlock));
|
|
DBGASSERT((SidNameCache::IsQuadAligned(pb)));
|
|
|
|
//
|
|
// Just in case the fill request would extend over the
|
|
// end of the file, truncate the requested block count. The assertion
|
|
// will catch it during development.
|
|
//
|
|
DWORD iLastBlock = iBlock + cBlocks - 1;
|
|
DBGASSERT((ValidBlockNumber(iLastBlock)));
|
|
if (iLastBlock >= m_pFileHdr->cBlocks)
|
|
cBlocks = m_pFileHdr->cBlocks - iBlock;
|
|
|
|
FillMemory(pb, sizeof(BLOCK) * cBlocks, b);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::IsBitSet
|
|
Function: SidNameCache::RecordMgr::SetBit
|
|
Function: SidNameCache::RecordMgr::ClrBit
|
|
|
|
Description: Tests and sets bits in the block allocation bitmap.
|
|
|
|
Arguments:
|
|
pdwBase - Address of 1st DWORD in bitmap.
|
|
|
|
iBit - 0-based index of bit in bitmap.
|
|
|
|
Returns: IsBitSet returns TRUE/FALSE indicating the state of the bit.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::RecordMgr::IsBitSet(
|
|
LPDWORD pdwBase,
|
|
DWORD iBit
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pdwBase));
|
|
DWORD b = iBit & 0x0000001F;
|
|
DWORD i = iBit >> 5;
|
|
|
|
return (*(pdwBase + i)) & (1 << b);
|
|
}
|
|
|
|
|
|
VOID
|
|
SidNameCache::RecordMgr::SetBit(
|
|
LPDWORD pdwBase,
|
|
DWORD iBit
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pdwBase));
|
|
DWORD b = iBit & 0x0000001F;
|
|
DWORD i = iBit >> 5;
|
|
|
|
(*(pdwBase + i)) |= (1 << b);
|
|
}
|
|
|
|
VOID
|
|
SidNameCache::RecordMgr::ClrBit(
|
|
LPDWORD pdwBase,
|
|
DWORD iBit
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pdwBase));
|
|
DWORD b = iBit & 0x0000001F;
|
|
DWORD i = iBit >> 5;
|
|
|
|
(*(pdwBase + i)) &= ~(1 << b);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::ValidBlockNumber
|
|
|
|
Description: Determine if a given block number is valid for the
|
|
current data file. Primarily meant for use in assertions.
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of block in data file.
|
|
|
|
Returns: TRUE/FALSE indicating validity of block number.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::RecordMgr::ValidBlockNumber(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
return (iBlock < m_pFileHdr->cBlocks);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::IsBlockUsed
|
|
Function: SidNameCache::RecordMgr::MarkBlockUsed
|
|
Function: SidNameCache::RecordMgr::MarkBlockUnused
|
|
|
|
Description: Examines and changes the allocation state of a block in the
|
|
data file.
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of block in data file.
|
|
|
|
Returns: IsBlockUsed returns TRUE/FALSE indicating the allocation state.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::RecordMgr::IsBlockUsed(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
DAT_BASED(DWORD) *pdwBased = DAT_BASED_CAST(DWORD, m_pFileHdr->pdwMap);
|
|
|
|
return IsBitSet((LPDWORD)pdwBased, iBlock);
|
|
}
|
|
|
|
|
|
VOID
|
|
SidNameCache::RecordMgr::MarkBlockUsed(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
|
|
DAT_BASED(DWORD) *pdwBased = DAT_BASED_CAST(DWORD, m_pFileHdr->pdwMap);
|
|
DBGASSERT((!IsBitSet((LPDWORD)pdwBased, iBlock)));
|
|
SetBit((LPDWORD)pdwBased, iBlock);
|
|
}
|
|
|
|
VOID
|
|
SidNameCache::RecordMgr::MarkBlockUnused(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
|
|
DAT_BASED(DWORD) *pdwBased = DAT_BASED_CAST(DWORD, m_pFileHdr->pdwMap);
|
|
DBGASSERT((IsBitSet((LPDWORD)pdwBased, iBlock)));
|
|
ClrBit((LPDWORD)pdwBased, iBlock);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::BlocksRequired
|
|
|
|
Description: Calculate the number of blocks required to store a given
|
|
number of bytes.
|
|
|
|
Arguments:
|
|
cb - Number of bytes requested.
|
|
|
|
Returns: Number of 32-byte blocks required to store the bytes.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameCache::RecordMgr::BlocksRequired(
|
|
DWORD cb
|
|
)
|
|
{
|
|
//
|
|
// Round byte request up to nearest 32-byte block.
|
|
//
|
|
if (cb & 0x0000001F)
|
|
cb = (cb & 0xFFFFFFE0) + 32;
|
|
|
|
//
|
|
// How many "blocks" are required?
|
|
//
|
|
return BYTE_TO_BLOCK(cb);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::IsQuadAligned
|
|
Function: SidNameCache::QuadAlign
|
|
Function: SidNameCache::WordAlign
|
|
|
|
Description: Methods that determine if a value is quad aligned and for
|
|
updating a value so that it is quad or word aligned.
|
|
|
|
Arguments:
|
|
See the individual methods. It's pretty self-explanatory.
|
|
|
|
Returns:
|
|
See the individual methods. It's pretty self-explanatory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::IsQuadAligned(
|
|
LPVOID pv
|
|
)
|
|
{
|
|
return (IsQuadAligned((DWORD_PTR)pv));
|
|
}
|
|
|
|
|
|
BOOL
|
|
SidNameCache::IsQuadAligned(
|
|
DWORD_PTR dw
|
|
)
|
|
{
|
|
return (0 == (dw & 0x00000007));
|
|
}
|
|
|
|
|
|
VOID
|
|
SidNameCache::QuadAlign(
|
|
LPDWORD pdwValue
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pdwValue));
|
|
if (*pdwValue & 0x00000007)
|
|
{
|
|
//
|
|
// Round up to next whole multiple of 8.
|
|
//
|
|
*pdwValue = (*pdwValue & 0xFFFFFFF8) + 8;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SidNameCache::WordAlign(
|
|
LPDWORD pdwValue
|
|
)
|
|
{
|
|
DBGASSERT((NULL != pdwValue));
|
|
if (*pdwValue & 0x00000001)
|
|
{
|
|
//
|
|
// Round up to next whole multiple of 2.
|
|
//
|
|
*pdwValue = (*pdwValue & 0xFFFFFFFE) + 2;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::BytesRequiredForRecord
|
|
|
|
Description: Calculates the number of bytes required to store a given
|
|
data file record. Optionally returns the required size for each
|
|
field in the record (SID, name, domain etc). The function makes
|
|
allowances for any required data type alignments in the file.
|
|
|
|
Arguments:
|
|
pSid - Address of user's SID.
|
|
|
|
pcbSid [optional] - Address of variable to receive required SID length.
|
|
|
|
pszContainer - Address of user's account container name string.
|
|
|
|
pcbContainer [optional] - Address of variable to receive required length
|
|
of container name string.
|
|
|
|
pszLogonName - Address of user's logon name string.
|
|
|
|
pcbLogonName [optional] - Address of variable to receive required length
|
|
of logon name string.
|
|
|
|
pszDisplayName - Address of user's display name string.
|
|
|
|
pcbDisplayName [optional] - Address of variable to receive required
|
|
length of display name string.
|
|
|
|
Returns: Number of bytes required to store the record.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameCache::RecordMgr::BytesRequiredForRecord(
|
|
PSID pSid,
|
|
LPDWORD pcbSid,
|
|
LPCTSTR pszContainer,
|
|
LPDWORD pcbContainer,
|
|
LPCTSTR pszLogonName,
|
|
LPDWORD pcbLogonName,
|
|
LPCTSTR pszDisplayName,
|
|
LPDWORD pcbDisplayName
|
|
)
|
|
{
|
|
DWORD cb = 0;
|
|
DWORD cbTotal = sizeof(RECORD_HDR);
|
|
|
|
//
|
|
// SID follows the header so it IS quadword aligned.
|
|
// It's a byte structure so it doesn't have to be; but it is anyway.
|
|
//
|
|
cb = GetLengthSid(pSid);
|
|
cbTotal += cb;
|
|
if (NULL != pcbSid)
|
|
*pcbSid = cb;
|
|
|
|
//
|
|
// Strings are UNICODE and must be word-aligned. Just align the first.
|
|
// All subsequent are guaranteed to be properly aligned.
|
|
//
|
|
SidNameCache::WordAlign(&cbTotal);
|
|
cb = (lstrlen(pszContainer) + 1) * sizeof(TCHAR);
|
|
cbTotal += cb;
|
|
if (NULL != pcbContainer)
|
|
*pcbContainer = cb;
|
|
|
|
cb = (lstrlen(pszLogonName) + 1) * sizeof(TCHAR);
|
|
cbTotal += cb;
|
|
if (NULL != pcbLogonName)
|
|
*pcbLogonName = cb;
|
|
|
|
cb = (lstrlen(pszDisplayName) + 1) * sizeof(TCHAR);
|
|
cbTotal += cb;
|
|
if (NULL != pcbDisplayName)
|
|
*pcbDisplayName = cb;
|
|
|
|
return cbTotal;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::FreeBlock
|
|
|
|
Description: Frees a single block in the data file.
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of the block.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::FreeBlock(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
DBGASSERT((IsBlockUsed(iBlock)));
|
|
|
|
MarkBlockUnused(iBlock);
|
|
DBGASSERT((!IsBlockUsed(iBlock)));
|
|
|
|
FillBlocks(iBlock, 1, RECORD_UNUSED_BYTE);
|
|
|
|
//
|
|
// Update the "first free" index if needed.
|
|
//
|
|
if (iBlock < m_pFileHdr->iFirstFree)
|
|
m_pFileHdr->iFirstFree = iBlock;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::FreeBlocks
|
|
|
|
Description: Frees a series of contiguous blocks in the data file.
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of the first block in the series.
|
|
|
|
cBlocks - Number of blocks in the series.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::FreeBlocks(
|
|
DWORD iFirstBlock,
|
|
DWORD cBlocks
|
|
)
|
|
{
|
|
for (UINT i = 0; i < cBlocks; i++)
|
|
FreeBlock(iFirstBlock + i);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::FreeRecord
|
|
|
|
Description: Frees all blocks in a data file record.
|
|
|
|
Arguments:
|
|
iFirstBlock - 0-based index of the first block in the record.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
VOID
|
|
SidNameCache::RecordMgr::FreeRecord(
|
|
DWORD iFirstBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iFirstBlock)));
|
|
DAT_BASED(RECORD_HDR) *pRecHdr = DAT_BASED_CAST(RECORD_HDR, BlockAddress(iFirstBlock));
|
|
|
|
DBGASSERT((RECORD_SIGNATURE == pRecHdr->dwSignature));
|
|
FreeBlocks(iFirstBlock, pRecHdr->cBlocks);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::BlockAddress
|
|
|
|
Description: Calculates the non-based address of a given block in the
|
|
data file.
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of the block.
|
|
|
|
Returns: Address of the block in the data file.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
PBLOCK
|
|
SidNameCache::RecordMgr::BlockAddress(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
|
|
return m_pFileHdr->pBlocks + iBlock;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::FileSize
|
|
|
|
Description: Calculate the data file size required for a given number of
|
|
data blocks. Accounts for data type alignment.
|
|
|
|
Arguments:
|
|
cBlocks - Number of blocks required in the data file.
|
|
|
|
Returns: Bytes required.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/24/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
UINT64
|
|
SidNameCache::RecordMgr::FileSize(
|
|
DWORD cBlocks
|
|
)
|
|
|
|
{
|
|
DWORD dwTemp = sizeof(DATA_FILE_HDR) +
|
|
((cBlocks / BITS_IN_DWORD) * sizeof(DWORD));
|
|
|
|
//
|
|
// Start of blocks must be quad-aligned.
|
|
//
|
|
SidNameCache::QuadAlign(&dwTemp);
|
|
|
|
return (UINT64)(dwTemp) +
|
|
(UINT64)(sizeof(BLOCK) * cBlocks);
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::AllocBlocks
|
|
|
|
Description: Allocates a specified number of contiguous blocks in the
|
|
data file.
|
|
|
|
Arguments:
|
|
cBlocksReqd - Number of blocks required in the allocation.
|
|
|
|
Returns: If successful, returns the index of the first block in the
|
|
allocation. Returns (DWORD)-1 if the block's can't be allocated.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameCache::RecordMgr::AllocBlocks(
|
|
DWORD cBlocksReqd
|
|
)
|
|
{
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
|
|
DWORD iBlock = m_pFileHdr->iFirstFree;
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_MID,
|
|
TEXT("SIDCACHE - AllocBlocks: Allocate %d blocks"),
|
|
cBlocksReqd));
|
|
|
|
while(iBlock < m_pFileHdr->cBlocks)
|
|
{
|
|
DBGPRINT((DM_SIDCACHE, DL_MID,
|
|
TEXT(" Start scan at block %d"), iBlock));
|
|
//
|
|
// Look for cBlocksReqd consecutive free blocks.
|
|
//
|
|
for (UINT j = 0; j < cBlocksReqd && (iBlock + j) < m_pFileHdr->cBlocks ; j++)
|
|
{
|
|
DBGPRINT((DM_SIDCACHE, DL_MID, TEXT(" Checking %d"), iBlock + j));
|
|
if (IsBlockUsed(iBlock + j))
|
|
{
|
|
//
|
|
// This one's used. Start searching again.
|
|
//
|
|
DBGPRINT((DM_SIDCACHE, DL_MID, TEXT(" %d is used"), iBlock + j));
|
|
break;
|
|
}
|
|
#if DBG
|
|
//
|
|
// If a block is marked "unused", it should contain all 0xCC.
|
|
//
|
|
DAT_BASED(BYTE) *pb = DAT_BASED_CAST(BYTE, BlockAddress(iBlock + j));
|
|
for (UINT k = 0; k < sizeof(BLOCK); k++)
|
|
{
|
|
DBGASSERT((RECORD_UNUSED_BYTE == *(pb + k)));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_MID, TEXT(" Scan complete. %d blocks checked"), j));
|
|
if (j == cBlocksReqd)
|
|
{
|
|
//
|
|
// Found a sufficient range of free blocks.
|
|
// Mark the blocks as allocated in the allocation bitmap.
|
|
//
|
|
for (UINT i = 0; i < cBlocksReqd; i++)
|
|
MarkBlockUsed(iBlock + i);
|
|
|
|
if (iBlock == m_pFileHdr->iFirstFree)
|
|
{
|
|
//
|
|
// Now scan to find the next free block.
|
|
// We'll save it's location to help with future free-block searches.
|
|
//
|
|
for (m_pFileHdr->iFirstFree = iBlock + cBlocksReqd;
|
|
m_pFileHdr->iFirstFree < m_pFileHdr->cBlocks && IsBlockUsed(m_pFileHdr->iFirstFree);
|
|
m_pFileHdr->iFirstFree++)
|
|
{
|
|
DBGPRINT((DM_SIDCACHE, DL_MID,
|
|
TEXT("SIDCACHE - Advancing first free %d"),
|
|
m_pFileHdr->iFirstFree));
|
|
NULL;
|
|
}
|
|
}
|
|
DBGPRINT((DM_SIDCACHE, DL_MID, TEXT("SIDCACHE - Found free block range at %d"), iBlock));
|
|
return iBlock;
|
|
}
|
|
|
|
iBlock += (j + 1); // Continue search.
|
|
}
|
|
DBGPRINT((DM_SIDCACHE, DL_MID, TEXT("SIDCACHE - No blocks available")));
|
|
|
|
return (DWORD)-1; // No blocks available of sufficient size.
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::RecordExpired
|
|
|
|
Description: Determine if a given record has expired. A record has
|
|
"expired" if it's expiration date is prior to "today".
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of first block in record.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
BOOL
|
|
SidNameCache::RecordMgr::RecordExpired(
|
|
DWORD iBlock
|
|
)
|
|
{
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
DAT_BASED(RECORD_HDR) *pRec = DAT_BASED_CAST(RECORD_HDR, BlockAddress(iBlock));
|
|
DBGASSERT((SidNameCache::IsQuadAligned(pRec)));
|
|
DBGASSERT((RECORD_SIGNATURE == pRec->dwSignature));
|
|
|
|
SYSTEMTIME SysNow;
|
|
FILETIME FileNow;
|
|
ULARGE_INTEGER uliFileNow;
|
|
ULARGE_INTEGER uliBirthday;
|
|
|
|
uliBirthday.LowPart = pRec->Birthday.dwLowDateTime;
|
|
uliBirthday.HighPart = pRec->Birthday.dwHighDateTime;
|
|
|
|
GetSystemTime(&SysNow);
|
|
SystemTimeToFileTime(&SysNow, &FileNow);
|
|
|
|
uliFileNow.LowPart = FileNow.dwLowDateTime;
|
|
uliFileNow.HighPart = FileNow.dwHighDateTime;
|
|
|
|
DWORD cDaysVariation = 0;
|
|
//
|
|
// Avoid div-by-zero.
|
|
//
|
|
if (0 < m_cDaysRecLifeRange)
|
|
cDaysVariation = SysNow.wMilliseconds % m_cDaysRecLifeRange;
|
|
|
|
//
|
|
// Add time specified in registry to the record's birthday.
|
|
// 864,000,000,000L is the number of 100 nanosecond periods in a day
|
|
// which is the unit the FILETIME structure is based on.
|
|
//
|
|
uliBirthday.QuadPart += ((UINT64)864000000000L *
|
|
(UINT64)(m_cDaysRecLifeMin + cDaysVariation));
|
|
//
|
|
// If it's still less than "now", the record is considered good.
|
|
//
|
|
return uliFileNow.QuadPart > uliBirthday.QuadPart;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::Store
|
|
|
|
Description: Stores a record in the data file.
|
|
|
|
Arguments:
|
|
pSid - Address of user's SID.
|
|
|
|
pszContainer - Address of user's account container name string.
|
|
|
|
pszLogonName - Address of user's logon name string.
|
|
|
|
pszDisplayName - Address of user's display name string.
|
|
|
|
Returns: Index of the first block used for the record.
|
|
(DWORD)-1 if the block couldn't be stored. Could mean out-of-disk
|
|
space.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
DWORD
|
|
SidNameCache::RecordMgr::Store(
|
|
PSID pSid,
|
|
LPCTSTR pszContainer,
|
|
LPCTSTR pszLogonName,
|
|
LPCTSTR pszDisplayName
|
|
)
|
|
{
|
|
DWORD cbSid = 0;
|
|
DWORD cbContainer = 0;
|
|
DWORD cbLogonName = 0;
|
|
DWORD cbDisplayName = 0;
|
|
|
|
DWORD cbRequired = BytesRequiredForRecord(
|
|
pSid,
|
|
&cbSid,
|
|
pszContainer,
|
|
&cbContainer,
|
|
pszLogonName,
|
|
&cbLogonName,
|
|
pszDisplayName,
|
|
&cbDisplayName);
|
|
|
|
DWORD cBlocksRequired = BlocksRequired(cbRequired);
|
|
DBGPRINT((DM_SIDCACHE, DL_MID,
|
|
TEXT("SIDCACHE - Store: %s (%s) in \"%s\" %d bytes, %d blocks"),
|
|
pszDisplayName, pszLogonName, pszContainer, cbRequired, cBlocksRequired));
|
|
|
|
//
|
|
// Try to allocate the required blocks.
|
|
//
|
|
DWORD iBlock = AllocBlocks(cBlocksRequired);
|
|
if ((DWORD)-1 == iBlock)
|
|
{
|
|
//
|
|
// Couldn't allocate blocks. Extend the data file.
|
|
//
|
|
GrowDataFile(DATA_FILE_GROW_BLOCKS);
|
|
iBlock = AllocBlocks(cBlocksRequired);
|
|
}
|
|
if ((DWORD)-1 != iBlock)
|
|
{
|
|
//
|
|
// Got the required number of blocks.
|
|
//
|
|
DBGASSERT((ValidBlockNumber(iBlock)));
|
|
PBLOCK pBlock = BlockAddress(iBlock);
|
|
DAT_BASED(RECORD_HDR) *pRec = DAT_BASED_CAST(RECORD_HDR, pBlock);
|
|
DAT_BASED(BYTE) *pbRec = DAT_BASED_CAST(BYTE, pBlock);
|
|
|
|
//
|
|
// Fill in the record header.
|
|
// Includes storing the item offset values from the start of the record.
|
|
// Storing these values in the record hdr will help with data retrieval.
|
|
//
|
|
pRec->dwSignature = RECORD_SIGNATURE;
|
|
pRec->cBlocks = cBlocksRequired;
|
|
pRec->cbOfsSid = sizeof(RECORD_HDR);
|
|
pRec->cbOfsContainer = pRec->cbOfsSid + cbSid;
|
|
WordAlign(&pRec->cbOfsContainer);
|
|
|
|
pRec->cbOfsLogonName = pRec->cbOfsContainer + cbContainer;
|
|
pRec->cbOfsDisplayName = pRec->cbOfsLogonName + cbLogonName;;
|
|
|
|
//
|
|
// Record the "birthday" so we can age the record.
|
|
//
|
|
SYSTEMTIME SysNow;
|
|
GetSystemTime(&SysNow);
|
|
SystemTimeToFileTime(&SysNow, &pRec->Birthday);
|
|
|
|
//
|
|
// Store the record's data.
|
|
// Use ACTUAL length values for memory transfers.
|
|
//
|
|
CopySid(cbSid, (PSID)(pbRec + pRec->cbOfsSid), pSid);
|
|
lstrcpy((LPTSTR)(pbRec + pRec->cbOfsContainer), pszContainer);
|
|
lstrcpy((LPTSTR)(pbRec + pRec->cbOfsLogonName), pszLogonName);
|
|
lstrcpy((LPTSTR)(pbRec + pRec->cbOfsDisplayName), pszDisplayName);
|
|
|
|
//
|
|
// Update the file's "blocks used" count.
|
|
//
|
|
m_pFileHdr->cBlocksUsed += pRec->cBlocks;
|
|
}
|
|
|
|
return iBlock;
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::Retrieve
|
|
|
|
Description: Retrieves a record in the data file.
|
|
|
|
Arguments:
|
|
iBlock - 0-based index of the starting block in the record.
|
|
|
|
ppSid [optional] - Address of SID pointer variable to receive the
|
|
address of the SID buffer. The caller is responsible for freeing
|
|
the buffer. May be NULL.
|
|
|
|
ppszContainer [optional] - Address of pointer variable to receive the
|
|
address of the container name string buffer. The caller is
|
|
responsible for freeing the buffer. May be NULL.
|
|
|
|
ppszLogonName [optional] - Address of pointer variable to receive the
|
|
address of the logon name string buffer. The caller is
|
|
responsible for freeing the buffer. May be NULL.
|
|
|
|
ppszDisplayName [optional] - Address of pointer variable to receive the
|
|
address of the display name string buffer. The caller is
|
|
responsible for freeing the buffer. May be NULL.
|
|
|
|
Returns:
|
|
NO_ERROR - Success.
|
|
ERROR_INVALID_SID (hr) - The record contains an invalid SID. Probably
|
|
a corrupt record.
|
|
|
|
Exceptions: OutOfMemory.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
HRESULT
|
|
SidNameCache::RecordMgr::Retrieve(
|
|
DWORD iBlock,
|
|
PSID *ppSid,
|
|
LPTSTR *ppszContainer,
|
|
LPTSTR *ppszLogonName,
|
|
LPTSTR *ppszDisplayName
|
|
)
|
|
{
|
|
PBLOCK pBlock = BlockAddress(iBlock);
|
|
DAT_BASED(RECORD_HDR) *pRec = DAT_BASED_CAST(RECORD_HDR, pBlock);
|
|
DAT_BASED(BYTE) *pbRec = DAT_BASED_CAST(BYTE, pBlock);
|
|
|
|
DBGASSERT((SidNameCache::IsQuadAligned(pRec)));
|
|
DBGASSERT((RECORD_SIGNATURE == pRec->dwSignature));
|
|
|
|
if (NULL != ppSid)
|
|
{
|
|
PSID pSid = (PSID)(pbRec + pRec->cbOfsSid);
|
|
if (IsValidSid(pSid))
|
|
{
|
|
*ppSid = SidDup(pSid);
|
|
}
|
|
else
|
|
return HRESULT_FROM_WIN32(ERROR_INVALID_SID);
|
|
}
|
|
|
|
if (NULL != ppszContainer)
|
|
{
|
|
*ppszContainer = StringDup((LPTSTR)(pbRec + pRec->cbOfsContainer));
|
|
}
|
|
|
|
if (NULL != ppszLogonName)
|
|
{
|
|
*ppszLogonName = StringDup((LPTSTR)(pbRec + pRec->cbOfsLogonName));
|
|
}
|
|
|
|
if (NULL != ppszDisplayName)
|
|
{
|
|
*ppszDisplayName = StringDup((LPTSTR)(pbRec + pRec->cbOfsDisplayName));
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
/* Function: SidNameCache::RecordMgr::Dump
|
|
|
|
Description: Dumps the contents of the data file to the debugger output.
|
|
|
|
Arguments: None.
|
|
|
|
Returns: Nothing.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
09/21/96 Initial creation. BrianAu
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#if DBG
|
|
VOID
|
|
SidNameCache::RecordMgr::Dump(
|
|
VOID
|
|
)
|
|
{
|
|
UINT i, j;
|
|
DBGASSERT((DATA_FILE_MAPPED));
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("Dumping SidNameCache RecordMgr at 0x%p"), this));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" Base...............: 0x%p"), g_pbMappedDataFile));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" m_pFileHdr.........: 0x%p"), m_pFileHdr));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" dwSignature......: 0x%08X"), (DWORD)m_pFileHdr->dwSignature));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" dwVersion........: 0x%08X"), (DWORD)m_pFileHdr->dwVersion));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" cBlocks..........: %d"), (DWORD)m_pFileHdr->cBlocks));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" cBlocksUsed......: %d"), (DWORD)m_pFileHdr->cBlocksUsed));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" cMapElements.....: %d"), (DWORD)m_pFileHdr->cMapElements));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" iFirstFree.......: %d"), (DWORD)m_pFileHdr->iFirstFree));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" pdwMap...........: 0x%p"), m_pFileHdr->pdwMap));
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT(" pBlocks..........: 0x%p"), m_pFileHdr->pBlocks));
|
|
|
|
PBLOCK pBlock = m_pFileHdr->pBlocks;
|
|
|
|
for (i = 0; i < m_pFileHdr->cBlocks; i++)
|
|
{
|
|
DAT_BASED(BYTE) *pb = DAT_BASED_CAST(BYTE, pBlock);
|
|
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("BLOCK %d --------------------"), i));
|
|
for (UINT row = 0; row < 2; row++)
|
|
{
|
|
TCHAR szHex[MAX_PATH];
|
|
TCHAR szAscii[MAX_PATH];
|
|
|
|
LPTSTR pszHex = szHex;
|
|
pb = DAT_BASED_CAST(BYTE, pBlock) + (row * (sizeof(BLOCK)/2));
|
|
for (j = 0; j < 16; j++)
|
|
{
|
|
wsprintf(pszHex, TEXT("%02X "), *pb);
|
|
pb++;
|
|
pszHex += 2;
|
|
}
|
|
|
|
LPTSTR pszAscii = szAscii;
|
|
pb = DAT_BASED_CAST(BYTE, pBlock) + (row * (sizeof(BLOCK)/2));
|
|
for (j = 0; j < 16; j++)
|
|
{
|
|
wsprintf(pszAscii, TEXT("%c"), *pb > 31 ? *pb : TEXT('.'));
|
|
pb++;
|
|
pszAscii++;
|
|
}
|
|
DBGPRINT((DM_SIDCACHE, DL_LOW, TEXT("%s %s"), szHex, szAscii));
|
|
}
|
|
pBlock++;
|
|
}
|
|
}
|
|
|
|
#endif // DEBUG
|
|
|