1169 lines
33 KiB
C++
1169 lines
33 KiB
C++
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Microsoft Windows
|
||
|
// Copyright (C) Microsoft Corporation, 1991 - 2000.
|
||
|
//
|
||
|
// File: PropStor.hxx
|
||
|
//
|
||
|
// Contents: Persistent property store (external to docfile)
|
||
|
//
|
||
|
// Classes: CPropertyStore
|
||
|
//
|
||
|
// History: 27-Dec-19 KyleP Created
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include <prcstob.hxx>
|
||
|
#include <phystr.hxx>
|
||
|
#include <rcstxact.hxx>
|
||
|
#include <propdesc.hxx>
|
||
|
#include <proplock.hxx>
|
||
|
#include <cistore.hxx>
|
||
|
#include <propbkp.hxx>
|
||
|
#include <fsciexps.hxx>
|
||
|
|
||
|
#include <enumstr.hxx>
|
||
|
|
||
|
#include <imprsnat.hxx>
|
||
|
|
||
|
class COnDiskPropertyRecord;
|
||
|
class CBorrowed;
|
||
|
class CPropRecordNoLock;
|
||
|
class CPropRecord;
|
||
|
class CPropRecordForWrites;
|
||
|
class CEnumString;
|
||
|
class CPropStoreBackupStream;
|
||
|
class CPropStoreManager;
|
||
|
class CCompositePropRecordForWrites;
|
||
|
|
||
|
enum ERecordFormat {
|
||
|
eNormal = 0,
|
||
|
eLean = 1
|
||
|
};
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CPropStoreInfo
|
||
|
//
|
||
|
// Purpose: Global persistent state for property store
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
class CPropStoreInfo
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
fDirtyPropStore = 1,
|
||
|
fNotBackedUp = 2,
|
||
|
};
|
||
|
|
||
|
//
|
||
|
// Constructors and destructor
|
||
|
//
|
||
|
|
||
|
CPropStoreInfo(DWORD dwStoreLevel);
|
||
|
|
||
|
CPropStoreInfo( CPropStoreInfo const & psi );
|
||
|
|
||
|
~CPropStoreInfo() { Shutdown( FALSE ); }
|
||
|
|
||
|
void Init( XPtr<PRcovStorageObj> & xObj,
|
||
|
DWORD dwStoreLevel );
|
||
|
|
||
|
void Empty();
|
||
|
|
||
|
void FastTransfer( CPropStoreInfo const & psi );
|
||
|
|
||
|
void Accept( XPtr<PRcovStorageObj> & xObj )
|
||
|
{
|
||
|
Win4Assert( _xrsoPropStore.IsNull() );
|
||
|
_xrsoPropStore.Set( xObj.Acquire() );
|
||
|
_fOwned = TRUE;
|
||
|
}
|
||
|
|
||
|
void Shutdown( BOOL fMarkClean = TRUE )
|
||
|
{
|
||
|
if ( _fOwned && !_xrsoPropStore.IsNull() )
|
||
|
{
|
||
|
if ( fMarkClean )
|
||
|
MarkClean();
|
||
|
|
||
|
_xrsoPropStore.Free();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_xrsoPropStore.Acquire();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Global metadata
|
||
|
//
|
||
|
|
||
|
WORKID WorkId() { return _info.widStream; }
|
||
|
WORKID NextWorkId(CiStorage & storage);
|
||
|
WORKID InitWorkId(CiStorage & storage);
|
||
|
|
||
|
WORKID MaxWorkId() const { return _info.widMax; }
|
||
|
WORKID FreeListHead() const { return _info.widFreeHead; }
|
||
|
WORKID FreeListTail() const { return _info.widFreeTail; }
|
||
|
ULONG RecordSize() const { return _info.culRecord; }
|
||
|
ULONG RecordsPerPage() const { return _cRecPerPage; }
|
||
|
ULONG FixedRecordSize() const { return _info.culFixed; }
|
||
|
ULONG CountProps() const { return _info.cTotal; }
|
||
|
ULONG CountFixedProps() const { return _info.cFixed; }
|
||
|
BOOL IsDirty() const
|
||
|
{
|
||
|
return ( 0 != ( _info.fDirty & fDirtyPropStore ) );
|
||
|
}
|
||
|
BOOL IsBackedUp() const
|
||
|
{
|
||
|
return ( 0 == ( _info.fDirty & fNotBackedUp ) );
|
||
|
}
|
||
|
|
||
|
ULONG OSPageSize() const { return _ulOSPageSize; }
|
||
|
ULONG OSPagesPerPage() const { return _cOSPagesPerLargePage; }
|
||
|
|
||
|
inline void MarkDirty();
|
||
|
inline void MarkClean();
|
||
|
inline void MarkNotBackedUp();
|
||
|
inline void MarkBackedUp();
|
||
|
|
||
|
void SetMaxWorkId( WORKID wid ) { _info.widMax = wid; MarkDirty(); }
|
||
|
void SetFreeListHead( WORKID wid ) { _info.widFreeHead = wid; MarkDirty(); }
|
||
|
void SetFreeListTail( WORKID wid ) { _info.widFreeTail = wid; MarkDirty(); }
|
||
|
|
||
|
ULONG CountRecordsInUse() const { return _info.cTopLevel; }
|
||
|
void IncRecordsInUse() { _info.cTopLevel++; }
|
||
|
void DecRecordsInUse() { _info.cTopLevel--; }
|
||
|
void SetRecordsInUse( ULONG count ) { _info.cTopLevel = count; }
|
||
|
void SetStoreLevel(DWORD dwLevel) { _info.dwStoreLevel = dwLevel; }
|
||
|
DWORD GetStoreLevel() { return _info.dwStoreLevel; }
|
||
|
|
||
|
//
|
||
|
// Lean record support
|
||
|
//
|
||
|
ERecordFormat GetRecordFormat() const { return _info.eRecordFormat; }
|
||
|
void SetRecordFormat(ERecordFormat type) { _info.eRecordFormat = type; }
|
||
|
|
||
|
//
|
||
|
// Per-property metadata
|
||
|
//
|
||
|
|
||
|
inline BOOL CanStore( PROPID pid );
|
||
|
|
||
|
inline unsigned Size( PROPID pid );
|
||
|
|
||
|
inline ULONG Type( PROPID pid );
|
||
|
|
||
|
inline BOOL CanBeModified( PROPID pid );
|
||
|
|
||
|
inline CPropDesc const * GetDescription( PROPID pid );
|
||
|
|
||
|
inline CPropDesc const * GetDescriptionByOrdinal( ULONG ordinal );
|
||
|
|
||
|
BOOL Add( PROPID pid, ULONG vt, unsigned cbMaxLen,
|
||
|
BOOL fCanBeModified, CiStorage & storage );
|
||
|
|
||
|
BOOL Delete( PROPID pid, CiStorage & storage );
|
||
|
|
||
|
void Commit( CPropStoreInfo & psi, CRcovStrmWriteTrans & xact );
|
||
|
|
||
|
PRcovStorageObj * GetRcovObj() { return _xrsoPropStore.GetPointer(); }
|
||
|
|
||
|
void DetectFormat();
|
||
|
|
||
|
private:
|
||
|
|
||
|
friend class CPropertyStore;
|
||
|
|
||
|
void ChangeDirty( int fDirty );
|
||
|
unsigned Lookup( PROPID pid );
|
||
|
unsigned LookupNew( PROPID pid );
|
||
|
|
||
|
inline CPropDesc const * GetDescription( unsigned i );
|
||
|
ULONG CountDescription() { return _aProp.Count(); }
|
||
|
|
||
|
struct SPropInfo
|
||
|
{
|
||
|
ULONG Version;
|
||
|
ULONG culRecord;
|
||
|
ULONG culFixed;
|
||
|
int fDirty;
|
||
|
ULONG cTotal;
|
||
|
ULONG cFixed;
|
||
|
ULONG cHash;
|
||
|
WORKID widStream;
|
||
|
WORKID widMax;
|
||
|
WORKID widFreeHead;
|
||
|
ULONG cTopLevel;
|
||
|
WORKID widFreeTail;
|
||
|
DWORD dwStoreLevel;
|
||
|
ERecordFormat eRecordFormat;
|
||
|
};
|
||
|
|
||
|
ULONG _cRecPerPage; // # records per 64K page
|
||
|
SPropInfo _info; // Non-repeated info, stored in header
|
||
|
XPtr<PRcovStorageObj> _xrsoPropStore; // The persistent storage itself
|
||
|
BOOL _fOwned; // Set to TRUE if propstore is owned.
|
||
|
|
||
|
XArray<CPropDesc> _aProp;
|
||
|
|
||
|
ULONG _ulOSPageSize; // GetSystemInfo returns this
|
||
|
ULONG _cOSPagesPerLargePage;// number of OS pages per COMMON_PAGE_SIZE sized large page
|
||
|
};
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CPhysPropertyStore
|
||
|
//
|
||
|
// Purpose: Persistent property store
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
class CPhysPropertyStore : public CPhysStorage
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
inline CPhysPropertyStore( PStorage & storage,
|
||
|
PStorageObject& obj,
|
||
|
WORKID objectId,
|
||
|
PMmStream * stream,
|
||
|
PStorage::EOpenMode mode,
|
||
|
unsigned cMappedItems );
|
||
|
private:
|
||
|
|
||
|
virtual void ReOpenStream();
|
||
|
|
||
|
};
|
||
|
|
||
|
// Backup support
|
||
|
enum EField { eFieldNone = 0,
|
||
|
eTopLevelField = 1};
|
||
|
|
||
|
//+-------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CPropertyStore
|
||
|
//
|
||
|
// Purpose: Persistent property store
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created
|
||
|
//
|
||
|
//--------------------------------------------------------------------------
|
||
|
|
||
|
class CPropertyStore
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
CPropertyStore(CPropStoreManager& propStoreMgr, DWORD dwLevel);
|
||
|
~CPropertyStore();
|
||
|
|
||
|
//
|
||
|
// Two phase construction (to accomadate late-bound storage)
|
||
|
//
|
||
|
|
||
|
void FastInit( CiStorage * pStorage);
|
||
|
void LongInit( BOOL & fWasDirty, ULONG & cInconsistencies,
|
||
|
T_UpdateDoc pfnUpdateCallback, void const *pUserData );
|
||
|
BOOL IsDirty() const { return _PropStoreInfo.IsDirty(); }
|
||
|
|
||
|
void Empty();
|
||
|
|
||
|
//
|
||
|
// Schema manipulation
|
||
|
//
|
||
|
|
||
|
inline BOOL CanStore( PROPID pid );
|
||
|
|
||
|
inline unsigned Size( PROPID pid );
|
||
|
|
||
|
inline ULONG Type( PROPID pid );
|
||
|
|
||
|
inline BOOL CanBeModified( PROPID pid );
|
||
|
|
||
|
ULONG_PTR BeginTransaction();
|
||
|
|
||
|
void Setup( PROPID pid, ULONG vt, DWORD cbMaxLen,
|
||
|
ULONG_PTR ulToken, BOOL fModifiable = TRUE );
|
||
|
|
||
|
void EndTransaction( ULONG_PTR ulToken, BOOL fCommit,
|
||
|
PROPID pidFixed );
|
||
|
|
||
|
//
|
||
|
// Backup/Load
|
||
|
//
|
||
|
void MakeBackupCopy( IProgressNotify * pIProgressNotify,
|
||
|
BOOL & fAbort,
|
||
|
CiStorage & dstStorage,
|
||
|
ICiEnumWorkids * pIWorkIds,
|
||
|
IEnumString **ppFileList);
|
||
|
//
|
||
|
// Property storage/retrieval.
|
||
|
//
|
||
|
|
||
|
SCODE WriteProperty( WORKID wid,
|
||
|
PROPID pid,
|
||
|
CStorageVariant const & var,
|
||
|
BOOL fBackup = TRUE);
|
||
|
SCODE WriteProperty( CPropRecordForWrites &PropRecord, PROPID pid,
|
||
|
CStorageVariant const & var, BOOL fBackup);
|
||
|
|
||
|
inline WORKID WritePropertyInNewRecord( PROPID pid,
|
||
|
CStorageVariant const & var,
|
||
|
BOOL fBackup = TRUE);
|
||
|
|
||
|
BOOL ReadProperty( WORKID wid, PROPID pid, PROPVARIANT & var );
|
||
|
BOOL ReadProperty( WORKID wid, PROPID pid, PROPVARIANT & var, BYTE * pbExtra, unsigned * pcbExtra );
|
||
|
BOOL ReadProperty( CPropRecordNoLock & PropRecord, PROPID pid, PROPVARIANT * pbData, unsigned * pcb);
|
||
|
BOOL ReadProperty( COnDiskPropertyRecord *prec, PROPID pid, PROPVARIANT & var,
|
||
|
BYTE * pbExtra, unsigned * pcbExtra );
|
||
|
BOOL ReadProperty( CPropRecordNoLock & PropRecord, PROPID pid, PROPVARIANT & var,
|
||
|
BYTE * pbExtra, unsigned * pcbExtra );
|
||
|
|
||
|
//
|
||
|
// Special path/wid support
|
||
|
//
|
||
|
|
||
|
inline WORKID MaxWorkId();
|
||
|
inline ULONG RecordsPerPage() const
|
||
|
{
|
||
|
return _PropStoreInfo.RecordsPerPage();
|
||
|
}
|
||
|
|
||
|
void DeleteRecord( WORKID wid, BOOL fBackup = TRUE );
|
||
|
|
||
|
inline ULONG CountRecordsInUse() const;
|
||
|
|
||
|
void Shutdown()
|
||
|
{
|
||
|
_fAbort = TRUE;
|
||
|
_PropStoreInfo.Shutdown();
|
||
|
}
|
||
|
|
||
|
BOOL Flush();
|
||
|
|
||
|
PStorage & GetStorage()
|
||
|
{
|
||
|
Win4Assert ( 0 != _pStorage );
|
||
|
|
||
|
return *_pStorage;
|
||
|
}
|
||
|
|
||
|
CiStorage & GetCiStorage()
|
||
|
{
|
||
|
Win4Assert ( 0 != _pStorage );
|
||
|
|
||
|
return *_pStorage;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Some info from CPropertyStoreInfo
|
||
|
//
|
||
|
|
||
|
inline ULONG OSPagesPerPage() const
|
||
|
{
|
||
|
return _PropStoreInfo.OSPagesPerPage();
|
||
|
}
|
||
|
|
||
|
inline ULONG OSPageSize() const
|
||
|
{
|
||
|
return _PropStoreInfo.OSPageSize();
|
||
|
}
|
||
|
|
||
|
inline ULONG RecordSize() const
|
||
|
{
|
||
|
return _PropStoreInfo.RecordSize();
|
||
|
}
|
||
|
|
||
|
inline CPropStoreBackupStream * BackupStream()
|
||
|
{
|
||
|
return _xPSBkpStrm.GetPointer();
|
||
|
}
|
||
|
|
||
|
inline CPhysPropertyStore * PhysStore()
|
||
|
{
|
||
|
return _xPhysStore.GetPointer();
|
||
|
}
|
||
|
|
||
|
inline WORKID MaxWorkId() const
|
||
|
{
|
||
|
return _PropStoreInfo.MaxWorkId();
|
||
|
}
|
||
|
|
||
|
inline void IncRecordsInUse()
|
||
|
{
|
||
|
_PropStoreInfo.IncRecordsInUse();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// get and set parameters
|
||
|
//
|
||
|
|
||
|
void SetBackupSize(ULONG ulBackupSizeInPages)
|
||
|
{
|
||
|
// range will be enforced when it is actually
|
||
|
// used.
|
||
|
_ulBackupSizeInPages = ulBackupSizeInPages;
|
||
|
}
|
||
|
|
||
|
ULONG GetDesiredBackupSize()
|
||
|
{
|
||
|
return _ulBackupSizeInPages;
|
||
|
}
|
||
|
|
||
|
ULONG GetActualBackupSize()
|
||
|
{
|
||
|
Win4Assert(!_xPSBkpStrm.IsNull());
|
||
|
return _xPSBkpStrm->MaxPages();
|
||
|
}
|
||
|
|
||
|
void SetMappedCacheSize(ULONG ulPSMappedCache)
|
||
|
{
|
||
|
// Enforce ranges
|
||
|
C_ASSERT(CI_PROPERTY_STORE_MAPPED_CACHE_MIN == 0);
|
||
|
if (ulPSMappedCache > CI_PROPERTY_STORE_MAPPED_CACHE_MAX)
|
||
|
_ulPSMappedCache = CI_PROPERTY_STORE_MAPPED_CACHE_MAX;
|
||
|
else
|
||
|
_ulPSMappedCache = ulPSMappedCache;
|
||
|
}
|
||
|
|
||
|
ULONG GetMappedCacheSize() { return _ulPSMappedCache; };
|
||
|
ULONG GetTotalSizeInKB();
|
||
|
|
||
|
inline DWORD GetStoreLevel() { return _PropStoreInfo.GetStoreLevel(); }
|
||
|
|
||
|
//
|
||
|
// Lean record support
|
||
|
//
|
||
|
ERecordFormat GetRecordFormat() const { return _PropStoreInfo.GetRecordFormat(); }
|
||
|
void SetRecordFormat(ERecordFormat type) { _PropStoreInfo.SetRecordFormat( type ); }
|
||
|
|
||
|
void MarkBackedUpMode()
|
||
|
{
|
||
|
_PropStoreInfo.MarkBackedUp();
|
||
|
}
|
||
|
|
||
|
void MarkNotBackedUpMode()
|
||
|
{
|
||
|
_PropStoreInfo.MarkNotBackedUp();
|
||
|
}
|
||
|
|
||
|
BOOL IsBackedUpMode() { return _PropStoreInfo.IsBackedUp(); }
|
||
|
|
||
|
//
|
||
|
// Clean Up
|
||
|
//
|
||
|
void ClearNonStorageProperties( CCompositePropRecordForWrites & rec );
|
||
|
|
||
|
private:
|
||
|
|
||
|
friend class CPropertyStoreWids;
|
||
|
friend class CPropRecordNoLock;
|
||
|
friend class CPropRecord;
|
||
|
friend class CPropRecordForWrites;
|
||
|
friend class CPropertyStoreRecovery;
|
||
|
friend class CPropStoreManager;
|
||
|
friend class CLockRecordForRead;
|
||
|
friend class CLockRecordForWrite;
|
||
|
friend class CLockAllRecordsForWrite;
|
||
|
friend class CBackupWid;
|
||
|
|
||
|
CPropertyStore( CPropertyStore & psi, CiStorage * pStorage );
|
||
|
|
||
|
WORKID CreateStorage( WORKID wid = widInvalid );
|
||
|
|
||
|
void InitNewRecord( WORKID wid,
|
||
|
ULONG cWid,
|
||
|
COnDiskPropertyRecord * prec,
|
||
|
BOOL fTopLevel,
|
||
|
BOOL fBackup);
|
||
|
|
||
|
void InitFreeList( );
|
||
|
|
||
|
void RecycleFreeList( WORKID wid );
|
||
|
|
||
|
void LokFreeRecord( WORKID wid, ULONG cFree,
|
||
|
COnDiskPropertyRecord * prec, BOOL fBackup );
|
||
|
|
||
|
WORKID LokAllocRecord( ULONG cFree );
|
||
|
|
||
|
void WritePropertyInSpecificNewRecord( WORKID wid, PROPID pid,
|
||
|
CStorageVariant const & var,
|
||
|
BOOL fBackup = TRUE );
|
||
|
|
||
|
WORKID NewWorkId( ULONG cWid, BOOL fBackup )
|
||
|
{
|
||
|
return LokNewWorkId( cWid, TRUE, fBackup );
|
||
|
}
|
||
|
|
||
|
WORKID LokNewWorkId( ULONG cWid, BOOL fTopLevel, BOOL fBackup );
|
||
|
|
||
|
//
|
||
|
// Record locking.
|
||
|
//
|
||
|
|
||
|
void AcquireRead( CReadWriteLockRecord & record );
|
||
|
void SyncRead( CReadWriteLockRecord & record );
|
||
|
void SyncReadDecrement( CReadWriteLockRecord & record );
|
||
|
void ReleaseRead( CReadWriteLockRecord & record );
|
||
|
|
||
|
void AcquireWrite( CReadWriteLockRecord & record );
|
||
|
void ReleaseWrite( CReadWriteLockRecord & record );
|
||
|
|
||
|
void AcquireWrite2( CReadWriteLockRecord & record );
|
||
|
void ReleaseWrite2( CReadWriteLockRecord & record );
|
||
|
|
||
|
void AcquireWriteOnAllRecords();
|
||
|
void ReleaseWriteOnAllRecords();
|
||
|
|
||
|
CPropertyLockMgr & LockMgr() { return _lockMgr; }
|
||
|
|
||
|
CReadWriteAccess & GetReadWriteAccess() { return _rwAccess; }
|
||
|
|
||
|
void Transfer( CPropertyStore & Target,
|
||
|
PROPID pidFixed,
|
||
|
BOOL & fAbort,
|
||
|
IProgressNotify * pProgress = 0 );
|
||
|
|
||
|
void FastTransfer( CPropertyStore & Target,
|
||
|
BOOL & fAbort,
|
||
|
IProgressNotify * pProgress = 0 );
|
||
|
|
||
|
CiStorage * _pStorage; // Persistent storage object.
|
||
|
CPropStoreInfo _PropStoreInfo; // Global persistent state for property store.
|
||
|
|
||
|
WORKID * _aFreeBlocks; // Pointers into free list by rec. size
|
||
|
|
||
|
BOOL _fAbort; // Set to TRUE when aborting
|
||
|
BOOL _fIsConsistent; // Set to TRUE as long as the data is
|
||
|
// consistent
|
||
|
|
||
|
//
|
||
|
// Record locking
|
||
|
//
|
||
|
CMutexSem _mtxWrite; // Taken to add/remove/modify records
|
||
|
CMutexSem _mtxRW; // Sometimes used during per-record locking
|
||
|
CEventSem _evtRead; // Sometimes used during per-record locking
|
||
|
CEventSem _evtWrite; // Sometimes used during per-record locking
|
||
|
CPropertyLockMgr _lockMgr; // arbitrates record-level locking
|
||
|
CReadWriteAccess _rwAccess; // Controls access to resources
|
||
|
|
||
|
XPtr<CPhysPropertyStore> _xPhysStore; // Main data stream.
|
||
|
|
||
|
//
|
||
|
// For multi-property changes to metadata
|
||
|
//
|
||
|
|
||
|
CPropertyStore * _ppsNew; // New metadata stored here
|
||
|
BOOL _fNew; // TRUE if something really changed
|
||
|
|
||
|
|
||
|
XPtr<CPropStoreBackupStream> _xPSBkpStrm; // prop store backup stream
|
||
|
ULONG _ulBackupSizeInPages; // backup size in pages
|
||
|
ULONG _ulPSMappedCache; // size of the mapped cache
|
||
|
|
||
|
DWORD _dwStoreLevel; // primary or secondary?
|
||
|
|
||
|
CPropStoreManager& _propStoreMgr; // Manager controlling this.
|
||
|
|
||
|
#if CIDBG==1
|
||
|
|
||
|
_int64 _sigPSDebug;
|
||
|
|
||
|
DWORD _tidReadSet;
|
||
|
DWORD _tidReadReset;
|
||
|
DWORD _tidWriteSet;
|
||
|
DWORD _tidWriteReset;
|
||
|
BYTE * _pbRecordLockTracker; // tracks write locking for en masse record lockup
|
||
|
|
||
|
void _SetReadTid()
|
||
|
{
|
||
|
_tidReadSet = GetCurrentThreadId();
|
||
|
}
|
||
|
|
||
|
void _ReSetReadTid()
|
||
|
{
|
||
|
_tidReadReset = GetCurrentThreadId();
|
||
|
}
|
||
|
|
||
|
void _SetWriteTid()
|
||
|
{
|
||
|
_tidWriteSet = GetCurrentThreadId();
|
||
|
}
|
||
|
|
||
|
void _ReSetWriteTid()
|
||
|
{
|
||
|
_tidWriteReset = GetCurrentThreadId();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Deadlock detection for the first 256 threads.
|
||
|
//
|
||
|
|
||
|
enum { cTrackThreads = 2048 };
|
||
|
|
||
|
XArray<long> _xPerThreadReadCounts;
|
||
|
XArray<long> _xPerThreadWriteCounts;
|
||
|
|
||
|
#else
|
||
|
|
||
|
void _SetReadTid() { }
|
||
|
void _ReSetReadTid() { }
|
||
|
void _SetWriteTid() { }
|
||
|
void _ReSetWriteTid() { }
|
||
|
|
||
|
#endif // CIDBG==1
|
||
|
};
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropStoreInfo::CanStore, public
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: TRUE if [pid] can exist in property store (e.g. has been
|
||
|
// registered).
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline BOOL CPropStoreInfo::CanStore( PROPID pid )
|
||
|
{
|
||
|
return (0 != _info.cHash) && _aProp[Lookup(pid)].IsInUse();
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropStoreInfo::Size, public
|
||
|
//
|
||
|
// Synopsis: Returns size in cache for this property.
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: TRUE if [pid] can exist in property store (e.g. has been
|
||
|
// registered).
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline unsigned CPropStoreInfo::Size( PROPID pid )
|
||
|
{
|
||
|
if (0 == _info.cHash)
|
||
|
return 0;
|
||
|
|
||
|
unsigned hash = Lookup(pid);
|
||
|
|
||
|
if ( _aProp[hash].IsInUse() )
|
||
|
return _aProp[hash].Size();
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
inline BOOL CPropStoreInfo::CanBeModified( PROPID pid )
|
||
|
{
|
||
|
if (0 == _info.cHash)
|
||
|
return TRUE;
|
||
|
|
||
|
unsigned hash = Lookup(pid);
|
||
|
|
||
|
if ( _aProp[hash].IsInUse() )
|
||
|
return _aProp[hash].Modifiable();
|
||
|
else
|
||
|
{ // if it not in the store, it doesn't matter what the
|
||
|
// reply is. But we will return TRUE so clients like Admin know that
|
||
|
// they can allow the property metadata to be modified.
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropStoreInfo::Type, public
|
||
|
//
|
||
|
// Synopsis: Returns type in cache for this property.
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: TRUE if [pid] can exist in property store (e.g. has been
|
||
|
// registered).
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG CPropStoreInfo::Type( PROPID pid )
|
||
|
{
|
||
|
if (0 == _info.cHash)
|
||
|
return VT_EMPTY;
|
||
|
|
||
|
unsigned hash = Lookup(pid);
|
||
|
|
||
|
if ( _aProp[hash].IsInUse() )
|
||
|
return _aProp[hash].Type();
|
||
|
else
|
||
|
return VT_EMPTY;
|
||
|
}
|
||
|
|
||
|
void CPropStoreInfo::MarkDirty()
|
||
|
{
|
||
|
#if 0 // there are too many clients who get this wrong...
|
||
|
CImpersonateSystem impersonate(FALSE); // FALSE = don't impersonate
|
||
|
|
||
|
Win4Assert(!impersonate.IsImpersonated());
|
||
|
#endif // cidbg == 1
|
||
|
|
||
|
if ( 0 == ( _info.fDirty & fDirtyPropStore ) )
|
||
|
{
|
||
|
CImpersonateSystem impersonate;
|
||
|
ChangeDirty( _info.fDirty | fDirtyPropStore );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPropStoreInfo::MarkClean()
|
||
|
{
|
||
|
if ( 0 != ( _info.fDirty & fDirtyPropStore ) )
|
||
|
{
|
||
|
CImpersonateSystem impersonate;
|
||
|
ChangeDirty( _info.fDirty & ~fDirtyPropStore );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CPropStoreInfo::MarkNotBackedUp()
|
||
|
{
|
||
|
if ( 0 == ( _info.fDirty & fNotBackedUp ) )
|
||
|
ChangeDirty( _info.fDirty | fNotBackedUp );
|
||
|
}
|
||
|
|
||
|
void CPropStoreInfo::MarkBackedUp()
|
||
|
{
|
||
|
if ( 0 != ( _info.fDirty & fNotBackedUp ) )
|
||
|
ChangeDirty( _info.fDirty & ~fNotBackedUp );
|
||
|
}
|
||
|
|
||
|
inline CPhysPropertyStore::CPhysPropertyStore( PStorage & storage,
|
||
|
PStorageObject& obj,
|
||
|
WORKID objectId,
|
||
|
PMmStream * stream,
|
||
|
PStorage::EOpenMode mode,
|
||
|
unsigned cMappedItems )
|
||
|
: CPhysStorage( storage,
|
||
|
obj,
|
||
|
objectId,
|
||
|
stream,
|
||
|
mode,
|
||
|
TRUE,
|
||
|
cMappedItems )
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::CanStore, public
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: TRUE if [pid] can exist in property store (e.g. has been
|
||
|
// registered).
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline BOOL CPropertyStore::CanStore( PROPID pid )
|
||
|
{
|
||
|
return _PropStoreInfo.CanStore( pid );
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::Size, public
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: Size of property in store, or 0 if it isn't in store.
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline unsigned CPropertyStore::Size( PROPID pid )
|
||
|
{
|
||
|
return _PropStoreInfo.Size( pid );
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::Type, public
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: Type of property in store, or VT_EMPTY if it isn't in store.
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG CPropertyStore::Type( PROPID pid )
|
||
|
{
|
||
|
return _PropStoreInfo.Type( pid );
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::WritePropertyInNewRecord, public
|
||
|
//
|
||
|
// Synopsis: Like WriteProperty, but also allocates record.
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to write.
|
||
|
// [var] -- Property value
|
||
|
//
|
||
|
// Returns: Workid of new record.
|
||
|
//
|
||
|
// History: 27-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline WORKID CPropertyStore::WritePropertyInNewRecord( PROPID pid,
|
||
|
CStorageVariant const & var,
|
||
|
BOOL fBackup )
|
||
|
{
|
||
|
WORKID wid = NewWorkId( 1, fBackup );
|
||
|
|
||
|
ciDebugOut(( DEB_PROPSTORE, "New short record at %d\n", wid ));
|
||
|
|
||
|
WriteProperty( wid, pid, var, fBackup );
|
||
|
|
||
|
_PropStoreInfo.IncRecordsInUse();
|
||
|
|
||
|
return wid;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::CountRecordsInUse, public
|
||
|
//
|
||
|
// Returns: Count of 'top level' records (correspond to user wids)
|
||
|
//
|
||
|
// History: 15-Feb-96 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline ULONG CPropertyStore::CountRecordsInUse() const
|
||
|
{
|
||
|
return _PropStoreInfo.CountRecordsInUse();
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::MaxWorkId, public
|
||
|
//
|
||
|
// Returns: Maximum workid which has been allocated.
|
||
|
//
|
||
|
// History: 28-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline WORKID CPropertyStore::MaxWorkId()
|
||
|
{
|
||
|
return _PropStoreInfo.MaxWorkId();
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStore::CanBeModified, public
|
||
|
//
|
||
|
// Returns: Can the property info be modifed for the given property?
|
||
|
//
|
||
|
// History: 28-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline BOOL CPropertyStore::CanBeModified( PROPID pid )
|
||
|
{
|
||
|
return _PropStoreInfo.CanBeModified( pid );
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStoreInfo::GetDescription, public
|
||
|
//
|
||
|
// Arguments: [pid] -- Propid to check.
|
||
|
//
|
||
|
// Returns: Metadata descriptor for specified property.
|
||
|
//
|
||
|
// History: 28-Dec-95 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline CPropDesc const * CPropStoreInfo::GetDescription( PROPID pid )
|
||
|
{
|
||
|
if (0 == _info.cHash)
|
||
|
return 0;
|
||
|
|
||
|
unsigned hash = Lookup( pid );
|
||
|
|
||
|
if ( _aProp[hash].IsInUse() )
|
||
|
return &_aProp[hash];
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStoreInfo::GetDescription, public
|
||
|
//
|
||
|
// Arguments: [i] -- the index position in the array.
|
||
|
//
|
||
|
// Returns: Metadata descriptor for the i-th property in the schema.
|
||
|
//
|
||
|
// History: 17-Oct-2000 KitmanH Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline CPropDesc const * CPropStoreInfo::GetDescription( unsigned i )
|
||
|
{
|
||
|
if (0 == _info.cHash)
|
||
|
return 0;
|
||
|
|
||
|
if ( i < _aProp.Count() )
|
||
|
{
|
||
|
if ( pidInvalid != _aProp[i].Pid() )
|
||
|
return &_aProp[i];
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Member: CPropertyStoreInfo::GetDescriptionByOrdinal, public
|
||
|
//
|
||
|
// Arguments: [ordinal] -- Ordinal
|
||
|
//
|
||
|
// Returns: Metadata descriptor for specified property.
|
||
|
//
|
||
|
// History: 16-Jan-96 KyleP Created.
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
inline CPropDesc const * CPropStoreInfo::GetDescriptionByOrdinal( ULONG ordinal )
|
||
|
{
|
||
|
Win4Assert( 0 != _info.cHash );
|
||
|
|
||
|
for ( unsigned i = 0; i < _aProp.Count(); i++ )
|
||
|
{
|
||
|
if ( _aProp[i].Pid() != pidInvalid && _aProp[i].Ordinal() == ordinal )
|
||
|
return &_aProp[i];
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CPropertyStoreRecovery
|
||
|
//
|
||
|
// Purpose: Recovers the property store from a dirty shutdown
|
||
|
//
|
||
|
// History: 4-10-96 srikants Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
class CPropertyStoreRecovery
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
CPropertyStoreRecovery( CPropertyStore & propStore,
|
||
|
T_UpdateDoc pfnUpdateCallback,
|
||
|
void const *pUserData );
|
||
|
~CPropertyStoreRecovery( );
|
||
|
|
||
|
void DoRecovery();
|
||
|
ULONG GetInconsistencyCount() const { return _cInconsistencies; }
|
||
|
|
||
|
private:
|
||
|
|
||
|
void Pass0();
|
||
|
void Pass1();
|
||
|
void Pass2();
|
||
|
|
||
|
void Complete();
|
||
|
void SetFree();
|
||
|
|
||
|
BOOL CheckOverflowChain();
|
||
|
void FreeChain();
|
||
|
|
||
|
WORKID AddToFreeList( WORKID widFree,
|
||
|
ULONG cFree,
|
||
|
COnDiskPropertyRecord * precFree,
|
||
|
WORKID widListHead );
|
||
|
|
||
|
CPropertyStore & _propStore;
|
||
|
CPropStoreInfo & _PropStoreInfo;
|
||
|
|
||
|
CPhysPropertyStore * _pPhysStore;
|
||
|
|
||
|
WORKID _wid; // Wid being processed currently
|
||
|
COnDiskPropertyRecord * _pRec; // Pointer to _wid's on disk rec
|
||
|
WORKID _cRec; // Count of records for this wid
|
||
|
|
||
|
XPtr<CPropStoreBackupStream> _xPSBkpStrm; // prop store backup stream
|
||
|
|
||
|
//
|
||
|
// Cumulative values during recovery.
|
||
|
//
|
||
|
WORKID * _aFreeBlocks; // Array of free lists by size
|
||
|
WORKID _widMax; // Current wid max
|
||
|
ULONG _cTopLevel; // Total top level records
|
||
|
|
||
|
ULONG _cRecPerPage; // Records per long page
|
||
|
ULONG _cInconsistencies;
|
||
|
// Number of inconsistencies.
|
||
|
ULONG _cForceFreed; // Number of records forecefully
|
||
|
// freed.
|
||
|
|
||
|
THashTable<ULONG> _pageTable; // Table of pages in backup.
|
||
|
T_UpdateDoc _fnUpdateCallback; // callback to update the wid
|
||
|
void const * _pUserData; // user data echoed back along with callback
|
||
|
|
||
|
class CGraftPage
|
||
|
{
|
||
|
public:
|
||
|
~CGraftPage()
|
||
|
{
|
||
|
_pPhysStore->ReturnLargeBuffer(_ulPageInPS / _cPagesPerLargePage);
|
||
|
}
|
||
|
|
||
|
CGraftPage(ULONG ulPageInBkp, ULONG ulPageInPS, ULONG cPageSize,
|
||
|
ULONG cCustomPagesPerLargePage, CPhysPropertyStore *pPhysStore,
|
||
|
CPropStoreBackupStream *pPSBkpStrm):
|
||
|
_cPagesPerLargePage( cCustomPagesPerLargePage ),
|
||
|
_ulPageInPS( ulPageInPS ),
|
||
|
_pPhysStore( pPhysStore )
|
||
|
{
|
||
|
PBYTE pLargePage = (PBYTE)pPhysStore->BorrowLargeBuffer(
|
||
|
ulPageInPS/cCustomPagesPerLargePage,
|
||
|
TRUE);
|
||
|
BYTE *pbPageLoc = (pLargePage + cPageSize*(ulPageInPS%cCustomPagesPerLargePage));
|
||
|
|
||
|
// Zap the page from backup file to property store
|
||
|
pPSBkpStrm->ReadPage(ulPageInBkp, &ulPageInPS, pbPageLoc);
|
||
|
ciDebugOut((DEB_PROPSTORE, "Successfully grafted backup page %d to %d in PS at location 0x%8x.\n",
|
||
|
ulPageInBkp, ulPageInPS, pbPageLoc));
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
ULONG _cPagesPerLargePage;
|
||
|
ULONG _ulPageInPS;
|
||
|
CPhysPropertyStore *_pPhysStore;
|
||
|
};
|
||
|
};
|
||
|
|
||
|
//+---------------------------------------------------------------------------
|
||
|
//
|
||
|
// Class: CBackupWid
|
||
|
//
|
||
|
// Purpose: Backs up a wid to the backup
|
||
|
//
|
||
|
// History: 6-20-97 KrishnaN Created
|
||
|
//
|
||
|
// Notes:
|
||
|
//
|
||
|
//----------------------------------------------------------------------------
|
||
|
|
||
|
class CBackupWid
|
||
|
{
|
||
|
public:
|
||
|
|
||
|
CBackupWid(CPropertyStore *pPropStor,
|
||
|
WORKID wid,
|
||
|
ULONG cRecsInWid):
|
||
|
_pPropStor( pPropStor ),
|
||
|
_nLargePage( 0xFFFFFFFF ),
|
||
|
_ulFirstPage( 0xFFFFFFFF ),
|
||
|
_ulLastPage( 0xFFFFFFFF ),
|
||
|
_pbFirstPage( 0 )
|
||
|
{
|
||
|
BackupWid(wid, cRecsInWid);
|
||
|
}
|
||
|
|
||
|
CBackupWid(CPropertyStore *pPropStor,
|
||
|
WORKID wid,
|
||
|
ULONG cRecsInWid,
|
||
|
EField FieldToCommit,
|
||
|
ULONG ulValue,
|
||
|
COnDiskPropertyRecord const *pRec):
|
||
|
_pPropStor( pPropStor ),
|
||
|
_nLargePage( 0xFFFFFFFF ),
|
||
|
_ulFirstPage( 0xFFFFFFFF ),
|
||
|
_ulLastPage( 0xFFFFFFFF ),
|
||
|
_pbFirstPage( 0 )
|
||
|
{
|
||
|
BackupWid(wid, cRecsInWid,
|
||
|
FieldToCommit, ulValue, pRec);
|
||
|
}
|
||
|
|
||
|
~CBackupWid()
|
||
|
{
|
||
|
_pPropStor->PhysStore()->ReturnLargeBuffer(_nLargePage);
|
||
|
}
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
//
|
||
|
// TryDescribeWidInAPage should be called first even when you
|
||
|
// know that you may not be able to describe the wid in a single
|
||
|
// page. This figures out the large page and borrows it. It also
|
||
|
// figures out how many pages you might need, so you can allocate
|
||
|
// the size and pass it to DescribeWid
|
||
|
//
|
||
|
inline ULONG TryDescribeWidInAPage(WORKID wid,
|
||
|
ULONG cRecsInWid);
|
||
|
inline void DescribeWid( WORKID wid,
|
||
|
ULONG cRecsInWid,
|
||
|
CDynArrayInPlace<ULONG> &pulPages,
|
||
|
CDynArrayInPlace<void *> &ppvPages);
|
||
|
|
||
|
|
||
|
void BackupWid(WORKID wid,
|
||
|
ULONG cRecsInWid,
|
||
|
EField FieldToCommit = eFieldNone,
|
||
|
ULONG ulValue = 0,
|
||
|
COnDiskPropertyRecord const *pRec = 0);
|
||
|
|
||
|
BOOL BackupPages(ULONG cPages,
|
||
|
ULONG const *pSlots,
|
||
|
void const * const * ppvPages);
|
||
|
|
||
|
ULONG DescribeField( EField FieldToCommit,
|
||
|
COnDiskPropertyRecord const *pRec,
|
||
|
ULONG cPages,
|
||
|
void const * const* ppvPages,
|
||
|
ULONG &pulOffset);
|
||
|
|
||
|
inline void ComputeOSPageLocations(WORKID wid, ULONG cRecsInWid,
|
||
|
ULONG *pFirstPage, ULONG *pLastPage);
|
||
|
inline BYTE * GetOSPagePointer(ULONG ulPage);
|
||
|
|
||
|
CPropertyStore * _pPropStor;
|
||
|
ULONG _nLargePage; // large page containing this wid
|
||
|
ULONG _ulFirstPage, _ulLastPage; // OS pages spanned by this wid
|
||
|
BYTE * _pbFirstPage; // address of the first page
|
||
|
};
|
||
|
|