windows-nt/Source/XPSP1/NT/enduser/stuff/itss/pathmgr.h
2020-09-26 16:20:57 +08:00

585 lines
22 KiB
C++

// PathMgr.h -- Declarations for the Path Manager classes and interfaces
#ifndef __PATHMGR_H__
#define __PATHMGR_H__
/*
The path manager interface maintains a database of PathInfo entries. Entries
are keyed by a Unicode path name. They may be enumerated in Unicode lexical order
starting from a given path name. The path manager interfaces support retrieving,
adding, deleting and modifying entries.
Each entry in the database has a unique path name. Name comparisons are case
insensitive according the rules for the U.S. English locale (0x0409). Path strings
are stored in the database exactly as given on whenever a new record is inserted.
Path names may be up to 259 characters long.
*/
#if 0
// StreamState defines a collection of bit flags which define the
// access and permission states for a stream or storage.
enum StreamState { Readable = 0x00000001, // Stream may be read
Writeable = 0x00000002, // Stream may be written
ShareRead = 0x00000004, // Multiple readers allowed
ShareWrite = 0x00000008, // Multiple writers allowed
TempStream = 0x00000010, // Stream will be deleted when released
Transacted = 0x00000020 // Stream supports Commit and Revert
};
#endif // 0
//Record stucture for garbage collection
typedef struct _SEntry{
CULINT ullcbOffset;
CULINT ullcbData;
UINT ulEntryID;
}SEntry;
typedef SEntry *PSEntry;
class CPathManager1 : public CITUnknown
{
public:
// Destructor:
~CPathManager1(void);
// Creation:
static HRESULT NewPathDatabase(IUnknown *punkOuter, ILockBytes *plb,
UINT cbDirectoryBlock, UINT cCacheBlocksMax,
IITPathManager **pplkb
);
static HRESULT LoadPathDatabase(IUnknown *punkOuter, ILockBytes *plb,
IITPathManager **pplkb
);
private:
CPathManager1(IUnknown *pUnkOuter);
class CImpIPathManager : public IITPathManager
{
public:
// Constructor and Destructor:
CImpIPathManager(CPathManager1 *pBackObj, IUnknown *punkOuter);
~CImpIPathManager(void);
// Initialing routines:
HRESULT InitNewPathDatabase(ILockBytes *plb, UINT cbDirectoryBlock,
UINT cCacheBlocksMax);
HRESULT InitLoadPathDatabase(ILockBytes *plb);
// IPersist Method:
HRESULT STDMETHODCALLTYPE GetClassID(
/* [out] */ CLSID __RPC_FAR *pClassID);
// IITPathManager interfaces:
HRESULT STDMETHODCALLTYPE FlushToLockBytes();
HRESULT STDMETHODCALLTYPE FindEntry (PPathInfo pSI );
HRESULT STDMETHODCALLTYPE CreateEntry(PPathInfo pSINew,
PPathInfo pSIOld,
BOOL fReplace );
HRESULT STDMETHODCALLTYPE DeleteEntry(PPathInfo pSI );
HRESULT STDMETHODCALLTYPE UpdateEntry(PPathInfo pSI );
HRESULT STDMETHODCALLTYPE EnumFromObject
(IUnknown *punkOuter, const WCHAR *pwszPrefix, UINT cwcPrefix,
REFIID riid, PVOID *ppv
);
HRESULT STDMETHODCALLTYPE GetPathDB(IStreamITEx *pTempPDBStrm, BOOL fCompact);
HRESULT STDMETHODCALLTYPE ForceClearDirty();
private:
// The PathInfo data type is used to pass stream information through function
// interfaces within the Path Manager. It is not used as the on-disk layout.
// The on-disk information is kept as follows:
//
// cbPath // Byte length of abPath; Stored as VL32
// abPath // UTF-8 representation of awszStreamPath
// uStateBits // Stored as high order dword of VL64
// iLockedBytesSegment // Stored as low order dword of VL64
// ullcbOffset // Stored as VL64 // Used with ullcbData
// ullcbData // Stored as VL64 // to store storage guids
//
// VL32 and VL64 storage formats, respectively, are 32 and 64 bit variable length
// storage formats. The use the convention that the last byte in the representation
// is less than 0x80. That is, each byte contains seven bits of information.
typedef struct _TaggedPathInfo
{
UINT iDirectoryBlock; // Index of dir block containing this info
UINT cbEntryOffset; // Offset of info within dir block
UINT cbEncoded; // Size of entry within dir block
PathInfo SI;
} TaggedPathInfo, *PTaggedPathInfo;
// The structures below define the on-disk structure of leaf nodes
// and internal nodes. Leaf nodes are the bottom of the B-Tree heirarchy.
// Internal nodes are in the levels above the leaf nodes.
// First we define two type tags for the nodes:
enum { uiMagicLeaf = ('L' << 24 ) | ('G' << 16) | ('M' << 8) | 'P',
uiMagicInternal = ('I' << 24 ) | ('G' << 16) | ('M' << 8) | 'P',
uiMagicUnused = ('U' << 24 ) | ('G' << 16) | ('M' << 8) | 'P'
};
// All nodes begin with a node header:
typedef struct _NodeHeader
{
UINT uiMagic; // A type tag to mark leafs and internal nodes.
UINT cbSlack; // Free space in the trailing portion of the node.
// UINT cEntries; // Now kept at end of node.
} NodeHeader, *PNodeHeader;
// Leaf nodes have additional header information to maintain a chain
// through all the leaf nodes:
typedef struct _LeafChainLinks
{
UINT iLeafSerial; // Serial number for this leaf. Changed when an
// enumeration offset might become invalid.
UINT iLeafPrevious; // Leaf nodes are placed in a lexically ordered chain.
UINT iLeafNext; // These are the links for that double linked chain.
} LeafChainLinks, *PLeafChainLinks;
// Now we can define the structures for leaf nodes and internal nodes.
// Note the use of ab[0]. That's because our nodes size is defined when
// the B-Tree is created. That size includes both the ab space for key
// entries and the header for the node. Since all nodes must be the same
// size than means the ab space for internal nodes is eight-bytes bigger
// than the ab space in leaf nodes. We keep all nodes the same size
// to make the free list mechanism work cleanly. That is, when a leaf
// node is discarded and added to the free list, it may be resurrected
// later and taken out of the free list to become either an internal node
// or a leaf node.
typedef struct _LeafNode
{
NodeHeader nh;
LeafChainLinks lcl;
BYTE ab[0];
} LeafNode, *PLeafNode;
typedef struct _InternalNode
{
NodeHeader nh;
BYTE ab[0];
} InternalNode, *PInternalNode;
// When we read nodes into memory, we embed them in a cache block
// structure so we can add extra state information.
typedef struct _CacheBlock
{
UINT fFlags; // Type and state bits (Defined below).
struct _CacheBlock *pCBPrev; // Previous block in LRU chain.
struct _CacheBlock *pCBNext; // Next block in LRU chain or
// Next block in free chain.
UINT iBlock; // Index of the disk slot corresponding to this node.
UINT cbKeyOffset; // Set by the key scanning routines to mark where
// a key was found or where a new key may be inserted.
UINT cbEntry; // Size in bytes of key plus record.
union
{
LeafNode ldb;
InternalNode idb;
};
} CacheBlock, *PCacheBlock;
enum { FreeBlock = 0x00000001, // Cache block is not in use
InternalBlock = 0x00000002, // Block holds an internal directory
LeafBlock = 0x00000004, // Block holds a leaf directory
BlockTypeMask = 0x00000007, // Bits which define block type
DirtyBlock = 0x00000008, // Block does not match on-disk data.
LockedBlock = 0x00000010, // Block is locked in memory
ReadingIn = 0x00000020, // Block is being read from disk
WritingOut = 0x00000040 // Block is being written to disk
};
typedef struct _SInternalNodeLev{
PInternalNode pINode;
ULONG cbFirstKey;
ULONG cEntries;
}SInternalNodeLev;
HRESULT STDMETHODCALLTYPE CompactPathDB(IStreamITEx *pTempPDBStrm);
HRESULT UpdateHigherLev(IStreamITEx *pTempPDBStrm,
UINT iCurILev,
UINT *piNodeNext,
SInternalNodeLev *rgINode,
PBYTE pbFirstKey,
ULONG cbFirstKey);
UINT PredictNodeID(UINT iCurILev,
UINT iNodeNext,
SInternalNodeLev *rgINode,
PBYTE pbFirstKey,
ULONG cbFirstKey);
HRESULT BinarySearch(UINT uiStart,
UINT uiEnd,
PBYTE pauAccess,
UINT cbAccess,
PTaggedPathInfo ptsi,
PBYTE ab,
PBYTE *ppbOut,
BOOL fSmall);
// These routines manage a key count field at the end of a node.
// The count may be absent if cbSlack is too small. It may be zero
// when the access vector doesn't exist.
void KillKeyCount(LeafNode *pln);
// These routines manage cache block allocation and deallocation.
// When there are no unused blocks, the GetCacheBlocks routine will
// free the oldest unlocked block, writing its content to disk if
// necessary.
HRESULT GetCacheBlock (PCacheBlock *ppCB, UINT fTypeMask);
HRESULT FreeCacheBlock(PCacheBlock pCB);
HRESULT GetFreeBlock(PCacheBlock &pCB);
HRESULT GetActiveBlock(PCacheBlock &pCB, BOOL fIgnoreLocks = FALSE);
void MarkAsMostRecent(PCacheBlock pCB);
void RemoveFromUse (PCacheBlock pCB);
// These routines handle disk I/O for cache blocks.
HRESULT ReadCacheBlock(PCacheBlock pCB, UINT iBlock);
HRESULT WriteCacheBlock(PCacheBlock pCB);
HRESULT FlushToDisk();
// The FindCacheBlock routine finds a cache block which contains
// the image of a particular node block. If that data doesn't
// exist in the cache, it will read it from disk.
HRESULT FindCacheBlock(PCacheBlock *ppCB, UINT iBlock);
// This routine allocate a new node on disk and in the cache blocks.
HRESULT AllocateNode(PCacheBlock *ppCB, UINT fTypeMask);
// This routine adds a node to the free list.
HRESULT DiscardNode(PCacheBlock pCB);
// These routines search through the B-Tree nodes looking for a key
// or a place to insert a new key.
HRESULT FindKeyAndLockBlockSet
(PTaggedPathInfo ptsi, PCacheBlock *papCBSet,
UINT iLevel, UINT cLevels = UINT(~0)
);
HRESULT ScanInternalForKey(PCacheBlock pCacheBlock, PTaggedPathInfo ptsi,
PUINT piChild
);
HRESULT ScanLeafForKey(PCacheBlock pCacheBlock, PTaggedPathInfo ptsi);
// ClearLockFlags turns off lock flags for all cache entries.
void ClearLockFlags(PCacheBlock *papCBSet);
// ClearLockFlagsAbove turns off lock flags for levels above iLevel.
void ClearLockFlagsAbove(PCacheBlock *papCBSet, UINT iLevel);
// These routines insert, delete, and modify entries in a leaf node.
HRESULT InsertEntryIntoLeaf(PTaggedPathInfo ptsi, PCacheBlock *papCBSet);
HRESULT UpdateEntryInLeaf (PTaggedPathInfo ptsi, PCacheBlock *papCBSet);
// These routines insert, delete, and modify entries in any node
HRESULT InsertEntryIntoNode(PTaggedPathInfo ptsi, PBYTE pb, UINT cb, PCacheBlock *papCBSet, UINT iLevel, BOOL fAfter);
HRESULT RemoveEntryFromNode(PTaggedPathInfo ptsi, PCacheBlock *papCBSet, UINT iLevel);
HRESULT ModifyEntryInNode (PTaggedPathInfo ptsi, PBYTE pb, UINT cb, PCacheBlock *papCBSet, UINT iLevel);
// These routines insert a key, remove a key, or modify the information for
// an existing key. The iLevel parameter indicates the level of the tree
// being modified. Level numbers start at the leaves and go upward.
HRESULT InsertAKey (UINT iLevel, PCacheBlock pCacheBlock, PTaggedPathInfo ptsi);
HRESULT RemoveAKey (UINT iLevel, PPathInfo pSI);
HRESULT ModifyKeyData(UINT iLevel, PCacheBlock pCacheBlock, PTaggedPathInfo ptsi);
// This routine splits a node to create more slack space.
HRESULT SplitANode(PCacheBlock *papCBSet, UINT iLevel);
// Routines for encoding and decoding on-disk representations of stream information:
HRESULT DecodePathKey(const BYTE **ppb, PWCHAR pwszPath, PUINT pcwcPath);
PBYTE EncodePathKey( PBYTE pb, const WCHAR *pwszPath, UINT cwcPath);
HRESULT DecodeKeyInfo(const BYTE **ppb, PPathInfo pSI);
PBYTE EncodeKeyInfo( PBYTE pb, const PathInfo *pSI);
PBYTE SkipKeyInfo(PBYTE pb);
HRESULT DecodePathInfo(const BYTE **ppb, PPathInfo pSI);
PBYTE EncodePathInfo( PBYTE pb, const PathInfo *pSI);
BOOL ValidBlockIndex(UINT iBlock);
HRESULT SaveHeader();
enum
{
INVALID_INDEX = UINT(~0),
CB_STREAM_INFO_MAX = MAX_UTF8_PATH + 2 * 10 + 2 * 5 + 2,
PathMagicID = ('P' << 24) | ('S' << 16) | ('T' << 8) | 'I',
PathVersion = 1
};
class CEnumPathMgr1 : public CITUnknown
{
public:
~CEnumPathMgr1();
static HRESULT NewPathEnumeratorObject
(IUnknown *punkOuter, CImpIPathManager *pPM,
const WCHAR *pwszPathPrefix,
UINT cwcPathPrefix,
REFIID riid,
PVOID *ppv
);
static HRESULT NewPathEnumerator(IUnknown *punkOuter, CImpIPathManager *pPM,
const WCHAR *pwszPathPrefix,
UINT cwcPathPrefix,
IEnumSTATSTG **ppEnumSTATSTG
);
private:
CEnumPathMgr1(IUnknown *pUnkOuter);
class CImpIEnumSTATSTG : public IITEnumSTATSTG
{
public:
CImpIEnumSTATSTG(CITUnknown *pBackObj, IUnknown *punkOuter);
~CImpIEnumSTATSTG();
HRESULT STDMETHODCALLTYPE InitPathEnumerator(CImpIPathManager *pPM,
const WCHAR *pwszPathPrefix,
UINT cwcPathPrefix
);
//IITEnumSTATSTG interface methods
HRESULT STDMETHODCALLTYPE GetNextEntryInSeq(ULONG celt, PathInfo *rgelt, ULONG *pceltFetched);
HRESULT STDMETHODCALLTYPE GetFirstEntryInSeq(PathInfo *rgelt);
HRESULT STDMETHODCALLTYPE Next(
/* [in] */ ULONG celt,
/* [in] */ STATSTG __RPC_FAR *rgelt,
/* [out] */ ULONG __RPC_FAR *pceltFetched);
HRESULT STDMETHODCALLTYPE Skip(
/* [in] */ ULONG celt);
HRESULT STDMETHODCALLTYPE Reset( void);
HRESULT STDMETHODCALLTYPE Clone(
/* [out] */ IEnumSTATSTG __RPC_FAR *__RPC_FAR *ppenum);
private:
HRESULT STDMETHODCALLTYPE FindEntry();
HRESULT STDMETHODCALLTYPE NextEntry();
CImpIPathManager *m_pPM; // Context of this enumeration
PathInfo m_SI; // Last entry returned
UINT m_iLeafBlock; // Index for leaf containing last entry
UINT m_cbOffsetLastEntry;// Position of prev entry in that leaf
UINT m_cbLastEntry; // Size of prev entry.
UINT m_iSerialNode; // Leaf Serial number
UINT m_iSerialDatabase; // Database serial number
UINT m_cwcPathPrefix; // Length of starting prefix
WCHAR m_pwszPathPrefix[MAX_PATH]; // Starting prefix
// We use leaf and database serial numbers to determine whether our position
// information is still valid. The serial number for a leaf changes whenever
// a change would invalidate our offset into the leaf. This includes cases
// where the node is split and also cases where an entry changes shape. That
// is, we increment the leaf serial number when we delete an entry or modify
// it so that its content is larger or smaller. Inserting an entry also
// increments the leaf's serial number. However appending an entry to the
// end of the leaf won't change its serial number because that change would
// not invalidate any enumeration offset.
//
// The serial number for the database is incremented whenever we delete a
// leaf node. This may indicate that our recorded m_iLeafBlock is no longer
// valid.
//
// Whenever we have a mismatch with either the leaf serial or the database
// serial, we search for a match rather than relying on our position
// information.
//
// Note also the initial condition where m_iSerialNode and m_iSerialDatabase
// are zero. By convention the serial number sequence skips the value zero.
};
CImpIEnumSTATSTG m_ImpEnumSTATSTG;
};
friend CEnumPathMgr1;
friend CEnumPathMgr1::CImpIEnumSTATSTG;
// We set the minimum directory block size so that we'll always be able
// to handle the longest possible path together with the worst case
// encoding for path information. In the worst case a leaf node can just
// barely accomodate a single item.
// The MIN_CACHE_ENTRIES constant controls the number of directory blocks
// which we will cache in memory. We may actually cache more blocks for
// a deep B-Tree.
enum { MIN_DIRECTORY_BLOCK_SIZE = MAX_UTF8_PATH + 30 + sizeof(LeafNode),
MIN_CACHE_ENTRIES = 2
};
// The database header is a meta directory for the node blocks.
// It is the first thing in the on-disk data stream.
typedef struct _DatabaseHeader
{
ULONG uiMagic; // ID value "ITSP"
ULONG uiVersion; // Revision # for this structure
ULONG cbHeader; // sizeof(DatabaseHeader);
ULONG cCacheBlocksMax; // Number of cache blocks allowed
ULONG cbDirectoryBlock; // Size of a directory block
ULONG cEntryAccessShift; // Base 2 log of Gap in entry access vector
ULONG cDirectoryLevels; // Nesting depth from root to leaf
ULONG iRootDirectory; // The top most internal directory
ULONG iLeafFirst; // Lexically first leaf block
ULONG iLeafLast; // Lexically last leaf block
ULONG iBlockFirstFree; // First block in unused block chain
ULONG cBlocks; // Number of directory blocks in use
LCID lcid; // Locale (sorting conventions, comparision rules)
CLSID clsidEntryHandler; // Interface which understands node entries
UINT cbPrefix; // Size of fixed portion of data base
ULONG iOrdinalMapRoot; // Ordinal map root for when they don't fit in a block
ULONG iOrdinalMapFirst; // First and last Ordinal map for leaf blocks.
ULONG iOrdinalMapLast; // These are linked like the leaf blocks.
// Note instance data for the Entry handler immediately follows the
// database header. That data is counted by cbPrefix.
} DatabaseHeader, PDatabaseHeader;
CITCriticalSection m_cs;
ILockBytes *m_plbPathDatabase; // Disk image of the path database header.
DatabaseHeader m_dbh; // Header info for the path database.
BOOL m_fHeaderIsDirty; // Header doesn't match disk version.
UINT m_cCacheBlocks; // Number of active cache blocks
PCacheBlock m_pCBLeastRecent; // LRU end of the in-use chain
PCacheBlock m_pCBMostRecent; // MRU end of the in-use chain
PCacheBlock m_pCBFreeList; // Chain of unused cache blocks
UINT m_PathSetSerial; // Serial number for the path set data base.
// Incremented whenever we delete a leaf node.
};
CImpIPathManager m_PathManager;
};
typedef CPathManager1 *PCPathManager1;
extern GUID aIID_CPathManager[];
extern UINT cInterfaces_CPathManager;
#pragma warning( disable : 4355 )
inline CPathManager1::CPathManager1(IUnknown *pUnkOuter)
: m_PathManager(this, pUnkOuter),
CITUnknown(aIID_CPathManager, cInterfaces_CPathManager, &m_PathManager)
{
}
inline CPathManager1::~CPathManager1(void)
{
}
inline BOOL CPathManager1::CImpIPathManager::ValidBlockIndex(UINT iBlock)
{
// We use all-ones as an invalid index value.
return ~iBlock;
}
inline CPathManager1::CImpIPathManager::CEnumPathMgr1::CEnumPathMgr1(IUnknown *pUnkOuter)
: m_ImpEnumSTATSTG(this, pUnkOuter),
CITUnknown(&IID_IEnumSTATSTG, 1, &m_ImpEnumSTATSTG)
{
}
inline CPathManager1::CImpIPathManager::CEnumPathMgr1::~CEnumPathMgr1(void)
{
}
// Routines for encoding and decoding variable length
// representations for 32 and 64 bit values:
ULONG DecodeVL32(const BYTE **ppb);
CULINT DecodeVL64(const BYTE **ppb);
ULONG CodeSizeVL32(ULONG ul);
PBYTE EncodeVL32(PBYTE pb, ULONG ul);
PBYTE EncodeVL64(PBYTE pb, CULINT *ull);
PBYTE SkipVL(PBYTE pb);
#endif // __PATHMGR_H__