475 lines
18 KiB
C
475 lines
18 KiB
C
|
#ifndef _INC_DSKQUOTA_SIDCACHE_H
|
||
|
#define _INC_DSKQUOTA_SIDCACHE_H
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
/* File: sidcache.h
|
||
|
|
||
|
Description: Header for classes associated with the SID/Name cache.
|
||
|
|
||
|
|
||
|
Here's a ER-style diagram of the SID cache. The cache consists
|
||
|
of a data file and index file. The index is a hash table consisting
|
||
|
of an array of hash buckets, each containing 0 or more hash bucket entries.
|
||
|
Each hash bucket entry contains the index of it's corresponding user record
|
||
|
in the data file. The index hash code is based on the user SID.
|
||
|
|
||
|
The index is designed for fast lookup of queue entries. Hash values are
|
||
|
calculated by simply summing the value of the bytes in a SID and
|
||
|
performing modulo division with the number of hash buckets. Each hash
|
||
|
bucket contains a doubly-linked list to handle hash value collisions
|
||
|
and removal of expired entries. Each hash bucket entry contains the index
|
||
|
of the starting block for the user's record in the data file.
|
||
|
|
||
|
|
||
|
+-----------+ +--------+<---> User SID
|
||
|
+-->| data file |<--->>| record |<---> User Domain
|
||
|
| +-----------+ +--------+<---> User Name
|
||
|
| ^ <---> User Full User Name
|
||
|
+-----------+<---+ |
|
||
|
| cache | |
|
||
|
+-----------+<---+ +--- points to ----+
|
||
|
| |
|
||
|
| |
|
||
|
| +----------+ +-------------+ +--------------+
|
||
|
+-->| ndx file |<--->>| hash bucket |<-->>| bucket entry |
|
||
|
+----------+ +-------------+ +--------------+
|
||
|
|
||
|
<---> = One-to-one
|
||
|
<-->> = One-to-many
|
||
|
|
||
|
|
||
|
Notes:
|
||
|
1. Because SID->Name resolution over the network is very slow
|
||
|
(0-10 seconds), I anticipate that this cache will be used heavily
|
||
|
and may contain 100's or 1000's of entries depending upon the
|
||
|
volume's usage.
|
||
|
|
||
|
Index file structure ------------------------------------------------------
|
||
|
|
||
|
+------------------------+
|
||
|
| Header | <--- Type INDEX_FILE_HDR
|
||
|
+------------------------+
|
||
|
| |
|
||
|
| | <--- Array of DWORDs. Count should be prime.
|
||
|
| Hash bucket array | Each element represents a hash bucket.
|
||
|
| | Unused elements contain NULL.
|
||
|
| | Used elements contain address of first
|
||
|
| | entry in hash bucket's entry list.
|
||
|
+------------------------+
|
||
|
| |
|
||
|
| |
|
||
|
| | <--- Array of INDEX_ENTRY.
|
||
|
| Index entries | Each entry is a node in a linked list.
|
||
|
| | Initially, all are on the "free list".
|
||
|
| | As entries are allocated, they are
|
||
|
| | transfered to the appropriate hash bucket's
|
||
|
| | list. Each used entry contains the
|
||
|
| | starting block number of the corresponding
|
||
|
| | record in the data file.
|
||
|
+------------------------+
|
||
|
|
||
|
Data file structure -------------------------------------------------------
|
||
|
|
||
|
+------------------------+
|
||
|
| Header | <--- Type DATA_FILE_HDR
|
||
|
+------------------------+
|
||
|
| |
|
||
|
| Block alloc map | <--- Array of DWORDs. Each bit represents the
|
||
|
| | allocation state of a record block.
|
||
|
| | 0 = free, 1 = allocated.
|
||
|
+------------------------+
|
||
|
| |
|
||
|
| |
|
||
|
| | <--- Array of BLOCKs. (32-bytes each)
|
||
|
| Record blocks | Each data record consists of one or more
|
||
|
| | blocks. The first block in each record
|
||
|
| | is of type RECORD_HDR. The remaining
|
||
|
| | blocks contain the variable-length SID,
|
||
|
| | Domain name, User name and Full User
|
||
|
| | name. Blocks are aligned on quadword
|
||
|
| | boundaries for the FILETIME structure
|
||
|
| | in RECORD_HDR. The 3 name string fields
|
||
|
| | are UNICODE and are WORD-aligned.
|
||
|
| | Unused blocks are filled with 0xCC.
|
||
|
| | A BLOCK is the smallest allocation unit.
|
||
|
+------------------------+
|
||
|
|
||
|
|
||
|
Revision History:
|
||
|
|
||
|
Date Description Programmer
|
||
|
-------- --------------------------------------------------- ----------
|
||
|
07/12/96 Initial creation. BrianAu
|
||
|
08/14/96 Added SidCacheQueueIterator. BrianAu
|
||
|
09/05/96 Added domain name string. 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.
|
||
|
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".
|
||
|
*/
|
||
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
const DWORD BITS_IN_BYTE = 8;
|
||
|
const DWORD BITS_IN_DWORD = BITS_IN_BYTE * sizeof(DWORD);
|
||
|
|
||
|
|
||
|
//
|
||
|
// On-disk structure of a single index entry.
|
||
|
// There's an entry for each record in the cache.
|
||
|
// Length = 16 bytes.
|
||
|
//
|
||
|
typedef struct index_entry
|
||
|
{
|
||
|
index_entry *pPrev;
|
||
|
index_entry *pNext;
|
||
|
DWORD iBlock;
|
||
|
DWORD iBucket;
|
||
|
|
||
|
} INDEX_ENTRY, *PINDEX_ENTRY;
|
||
|
|
||
|
//
|
||
|
// On-disk structure of index file header.
|
||
|
// Length = 48 bytes
|
||
|
// Must be even multiple of 8 bytes (quadword-align).
|
||
|
//
|
||
|
typedef struct index_file_hdr
|
||
|
{
|
||
|
DWORD dwSignature; // ID's file as a quota cache index.
|
||
|
DWORD dwVersion; // SW version that created file.
|
||
|
GUID guid; // Verifies validity of data.
|
||
|
DWORD cBuckets; // Number of hash buckets.
|
||
|
DWORD cMaxEntries; // Max number of entries.
|
||
|
DWORD cEntries; // Number of used entries.
|
||
|
PINDEX_ENTRY *pBuckets; // Address of first hash bucket.
|
||
|
PINDEX_ENTRY pEntries; // Offset to first entry.
|
||
|
PINDEX_ENTRY pFirstFree; // Offset to first entry in free list.
|
||
|
|
||
|
} INDEX_FILE_HDR, *PINDEX_FILE_HDR;
|
||
|
|
||
|
//
|
||
|
// Define what a data file "block" is.
|
||
|
// Keep the block size a power of 2.
|
||
|
//
|
||
|
const DWORD BLOCK_SIZE = 32; // Size is in bytes.
|
||
|
|
||
|
typedef BYTE BLOCK[BLOCK_SIZE];
|
||
|
typedef BLOCK *PBLOCK;
|
||
|
|
||
|
//
|
||
|
// On-disk structure of data file header.
|
||
|
// Length = 48 bytes.
|
||
|
//
|
||
|
typedef struct data_file_hdr
|
||
|
{
|
||
|
DWORD dwSignature; // ID's file as quota cache data.
|
||
|
DWORD dwVersion; // SW version that created file.
|
||
|
GUID guid; // Verifies validity of data.
|
||
|
DWORD cBlocks; // Total number of allocation blocks.
|
||
|
DWORD cBlocksUsed; // Number of used allocation blocks.
|
||
|
DWORD cMapElements; // Number of alloc map elements.
|
||
|
DWORD iFirstFree; // Index of first free block.
|
||
|
LPDWORD pdwMap; // Address of alloc map.
|
||
|
PBLOCK pBlocks; // Address of block pool.
|
||
|
|
||
|
} DATA_FILE_HDR, *PDATA_FILE_HDR;
|
||
|
|
||
|
//
|
||
|
// On-disk structure of the header info for each record.
|
||
|
// This fixed length structure precedes the variable-length part.
|
||
|
// Quad-word aligned.
|
||
|
// Length = 32 bytes (1 block).
|
||
|
// Strings are UNICODE, WORD-aligned.
|
||
|
//
|
||
|
typedef struct record_hdr
|
||
|
{
|
||
|
DWORD dwSignature; // Rec hdr signature. (0xAAAA5555)
|
||
|
DWORD cBlocks; // Blks in record.
|
||
|
FILETIME Birthday; // When was record added to cache?
|
||
|
DWORD cbOfsSid; // Ofs from start of record.
|
||
|
DWORD cbOfsContainer; // Ofs from start of record.
|
||
|
DWORD cbOfsLogonName; // Ofs from start of record.
|
||
|
DWORD cbOfsDisplayName; // Ofs from start of record.
|
||
|
|
||
|
} RECORD_HDR, *PRECORD_HDR;
|
||
|
|
||
|
|
||
|
class SidNameCache
|
||
|
{
|
||
|
private:
|
||
|
//
|
||
|
// Private class encapsulating the functions of the cache index.
|
||
|
//
|
||
|
class IndexMgr
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
SidNameCache& m_refCache; // Ref to outer cache object.
|
||
|
PINDEX_FILE_HDR m_pFileHdr; // Same as g_pbMappedIndexFile.
|
||
|
HANDLE m_hFile; // Handle to index file.
|
||
|
HANDLE m_hFileMapping; // Mapped file.
|
||
|
CString m_strFileName; // Full path\name of index file.
|
||
|
|
||
|
|
||
|
LPBYTE CreateIndexFile(LPCTSTR pszFile,
|
||
|
DWORD cbFileHigh,
|
||
|
DWORD cbFileLow);
|
||
|
|
||
|
LPBYTE OpenIndexFile(LPCTSTR pszFile);
|
||
|
|
||
|
LPBYTE GrowIndexFile(DWORD cGrowEntries);
|
||
|
|
||
|
VOID CloseIndexFile(VOID);
|
||
|
|
||
|
VOID InitNewIndexFile(DWORD cBuckets, DWORD cMaxEntries);
|
||
|
|
||
|
PINDEX_ENTRY AllocEntry(VOID);
|
||
|
|
||
|
VOID FreeEntry(PINDEX_ENTRY pEntry);
|
||
|
VOID AddEntryToFreeList(PINDEX_ENTRY pEntry);
|
||
|
PINDEX_ENTRY DetachEntry(PINDEX_ENTRY pEntry);
|
||
|
|
||
|
PINDEX_ENTRY Find(PSID pSid);
|
||
|
PINDEX_ENTRY Find(PSID pSid, DWORD dwHashCode);
|
||
|
PINDEX_ENTRY Find(LPCTSTR pszLogonName);
|
||
|
|
||
|
DWORD Hash(PSID pSid);
|
||
|
|
||
|
PINDEX_ENTRY GetHashBucketValue(DWORD iBucket);
|
||
|
VOID SetHashBucketValue(DWORD iBucket, PINDEX_ENTRY pEntry);
|
||
|
|
||
|
//
|
||
|
// Prevent copy.
|
||
|
//
|
||
|
IndexMgr(const IndexMgr& rhs);
|
||
|
IndexMgr& operator = (const IndexMgr& rhs);
|
||
|
|
||
|
|
||
|
public:
|
||
|
IndexMgr(SidNameCache& refCache);
|
||
|
~IndexMgr(VOID);
|
||
|
|
||
|
LPBYTE Initialize(LPCTSTR pszFile,
|
||
|
DWORD cBuckets = 0,
|
||
|
DWORD cMaxEntries = 0);
|
||
|
|
||
|
DWORD Lookup(PSID pSid);
|
||
|
DWORD Lookup(LPCTSTR pszLogonName);
|
||
|
|
||
|
PINDEX_ENTRY Add(PSID pSid, DWORD iBlock);
|
||
|
|
||
|
static UINT64 FileSize(DWORD cMaxEntries, DWORD cBuckets)
|
||
|
{
|
||
|
return (UINT64)(sizeof(INDEX_FILE_HDR)) +
|
||
|
(UINT64)(sizeof(PINDEX_ENTRY) * cBuckets) +
|
||
|
(UINT64)(sizeof(INDEX_ENTRY) * cMaxEntries);
|
||
|
}
|
||
|
|
||
|
VOID SetFileGUID(const GUID *pguid);
|
||
|
VOID GetFileGUID(LPGUID pguid);
|
||
|
VOID FreeEntry(PSID pSid);
|
||
|
VOID Clear(VOID);
|
||
|
#ifdef DBG
|
||
|
VOID Dump(VOID);
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Private class that manages the cache's stored data records.
|
||
|
//
|
||
|
class RecordMgr
|
||
|
{
|
||
|
private:
|
||
|
SidNameCache& m_refCache; // Ref to outer cache object.
|
||
|
PDATA_FILE_HDR m_pFileHdr; // Same as g_pbMappedDataFile
|
||
|
HANDLE m_hFile; // Handle to data file.
|
||
|
HANDLE m_hFileMapping; // Mapped data file.
|
||
|
DWORD m_cDaysRecLifeMin; // Min and range are used to
|
||
|
DWORD m_cDaysRecLifeRange; // calc lifetime of records.
|
||
|
CString m_strFileName; // Full path\name of data file.
|
||
|
|
||
|
BOOL ValidBlockNumber(DWORD iBlock);
|
||
|
VOID FillBlocks(DWORD iBlock, DWORD cBlocks, BYTE b);
|
||
|
BOOL IsBitSet(LPDWORD pdwBase, DWORD iBit);
|
||
|
VOID SetBit(LPDWORD pdwBase, DWORD iBit);
|
||
|
VOID ClrBit(LPDWORD pdwBase, DWORD iBit);
|
||
|
BOOL IsBlockUsed(DWORD iBlock);
|
||
|
VOID MarkBlockUsed(DWORD iBlock);
|
||
|
VOID MarkBlockUnused(DWORD iBlock);
|
||
|
DWORD BlocksRequired(DWORD cb);
|
||
|
DWORD BytesRequiredForRecord(
|
||
|
PSID pSid,
|
||
|
LPDWORD pcbSid,
|
||
|
LPCTSTR pszContainer,
|
||
|
LPDWORD pcbContainer,
|
||
|
LPCTSTR pszLogonName,
|
||
|
LPDWORD pcbLogonName,
|
||
|
LPCTSTR pszDisplayName,
|
||
|
LPDWORD pcbDisplayName);
|
||
|
|
||
|
VOID FreeBlock(DWORD iBlock);
|
||
|
VOID FreeBlocks(DWORD iFirstBlock, DWORD cBlocks);
|
||
|
BLOCK *BlockAddress(DWORD iBlock);
|
||
|
DWORD AllocBlocks(DWORD cbRecord);
|
||
|
LPBYTE CreateDataFile(LPCTSTR pszFile, DWORD cbFileHigh, DWORD cbFileLow);
|
||
|
LPBYTE OpenDataFile(LPCTSTR pszFile);
|
||
|
LPBYTE GrowDataFile(DWORD cGrowBlocks);
|
||
|
VOID InitNewDataFile(DWORD cBlocks);
|
||
|
VOID CloseDataFile(VOID);
|
||
|
|
||
|
//
|
||
|
// Prevent copy.
|
||
|
//
|
||
|
RecordMgr(const RecordMgr& rhs);
|
||
|
RecordMgr& operator = (const RecordMgr& rhs);
|
||
|
|
||
|
|
||
|
public:
|
||
|
|
||
|
RecordMgr(SidNameCache& refCache);
|
||
|
~RecordMgr(VOID);
|
||
|
|
||
|
LPBYTE Initialize(
|
||
|
LPCTSTR pszFile,
|
||
|
DWORD cBlocks = 0);
|
||
|
|
||
|
static UINT64 FileSize(DWORD cBlocks);
|
||
|
|
||
|
DWORD Store(
|
||
|
PSID pSid,
|
||
|
LPCTSTR pszContainer,
|
||
|
LPCTSTR pszLogonName,
|
||
|
LPCTSTR pszDisplayName);
|
||
|
|
||
|
BOOL RecordExpired(DWORD iBlock);
|
||
|
|
||
|
HRESULT Retrieve(
|
||
|
DWORD iBlock,
|
||
|
PSID *ppSid,
|
||
|
LPTSTR *ppszContainer,
|
||
|
LPTSTR *ppszLogonName,
|
||
|
LPTSTR *ppszDisplayName);
|
||
|
|
||
|
VOID SetFileGUID(const GUID *pguid);
|
||
|
VOID GetFileGUID(LPGUID pguid);
|
||
|
VOID FreeRecord(DWORD iFirstBlock);
|
||
|
VOID Clear(VOID);
|
||
|
#ifdef DBG
|
||
|
VOID Dump(VOID);
|
||
|
#endif
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Private class for handling locks on cache files that must
|
||
|
// be automatically released when an exception is thrown.
|
||
|
//
|
||
|
class CacheAutoLock
|
||
|
{
|
||
|
public:
|
||
|
explicit CacheAutoLock(SidNameCache& Cache)
|
||
|
: m_Cache(Cache) { };
|
||
|
|
||
|
BOOL Lock(VOID)
|
||
|
{ return m_Cache.Lock(); }
|
||
|
|
||
|
~CacheAutoLock(VOID)
|
||
|
{m_Cache.ReleaseLock(); }
|
||
|
|
||
|
private:
|
||
|
SidNameCache& m_Cache;
|
||
|
//
|
||
|
// Prevent copy.
|
||
|
//
|
||
|
CacheAutoLock(const CacheAutoLock& rhs);
|
||
|
CacheAutoLock& operator = (const CacheAutoLock& rhs);
|
||
|
};
|
||
|
|
||
|
friend class CacheAutoLock;
|
||
|
|
||
|
HANDLE m_hMutex; // For locking during updates.
|
||
|
IndexMgr *m_pIndexMgr; // Manages index file.
|
||
|
RecordMgr *m_pRecordMgr; // Manages data file.
|
||
|
CString m_strFilePath; // Path to cache files.
|
||
|
|
||
|
BOOL Lock(VOID);
|
||
|
VOID ReleaseLock(VOID);
|
||
|
BOOL FilesAreValid(VOID);
|
||
|
VOID ValidateFiles(VOID);
|
||
|
VOID InvalidateFiles(VOID);
|
||
|
VOID SetCacheFilePath(VOID);
|
||
|
BOOL CreateCacheFileDirectory(LPCTSTR pszPath);
|
||
|
|
||
|
//
|
||
|
// Private ctor prevents creation outside of singleton access
|
||
|
// function.
|
||
|
//
|
||
|
SidNameCache(VOID);
|
||
|
//
|
||
|
// The singleton instance access function.
|
||
|
//
|
||
|
friend HRESULT SidNameCache_GetOrDestroy(SidNameCache **ppCache, bool bGet);
|
||
|
|
||
|
public:
|
||
|
~SidNameCache(VOID);
|
||
|
|
||
|
HRESULT Initialize(
|
||
|
BOOL bOpenExisting);
|
||
|
|
||
|
HRESULT Lookup(
|
||
|
PSID pSid,
|
||
|
LPTSTR *ppszContainer,
|
||
|
LPTSTR *ppszLogonName,
|
||
|
LPTSTR *ppszDisplayName);
|
||
|
|
||
|
HRESULT Lookup(
|
||
|
LPCTSTR pszLogonName,
|
||
|
PSID *ppSid);
|
||
|
|
||
|
HRESULT Add(
|
||
|
PSID pSid,
|
||
|
LPCTSTR pszContainer,
|
||
|
LPCTSTR pszLogonName,
|
||
|
LPCTSTR pszDisplayName);
|
||
|
|
||
|
// HRESULT CheckConsistency(VOID);
|
||
|
|
||
|
BOOL Clear(VOID);
|
||
|
|
||
|
HRESULT BeginTransaction(VOID);
|
||
|
VOID EndTransaction(VOID);
|
||
|
|
||
|
static LPBYTE OpenMappedFile(
|
||
|
LPCTSTR pszDataFile,
|
||
|
LPCTSTR pszMapping,
|
||
|
DWORD dwCreation,
|
||
|
DWORD cbFileHigh,
|
||
|
DWORD cbFileLow,
|
||
|
PHANDLE phFile,
|
||
|
PHANDLE phFileMapping);
|
||
|
|
||
|
static BOOL IsQuadAligned(LPVOID pv);
|
||
|
static BOOL IsQuadAligned(DWORD_PTR dw);
|
||
|
static VOID QuadAlign(LPDWORD lpdwValue);
|
||
|
static VOID WordAlign(LPDWORD lpdwValue);
|
||
|
|
||
|
#if DBG
|
||
|
VOID Dump(VOID)
|
||
|
{ m_pIndexMgr->Dump(); m_pRecordMgr->Dump(); }
|
||
|
#endif
|
||
|
|
||
|
friend class IndexMgr;
|
||
|
friend class RecordMgr;
|
||
|
};
|
||
|
|
||
|
|
||
|
HRESULT SidNameCache_Get(SidNameCache **ppCache);
|
||
|
HRESULT SidNameCache_Destroy(void);
|
||
|
|
||
|
|
||
|
#endif // _INC_DSKQUOTA_SIDCACHE_H
|