You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1829 lines
52 KiB
1829 lines
52 KiB
|
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// File: trksvr.hxx
|
|
//
|
|
// Contents: Definitions local to this directory.
|
|
// Put includes of files liable to change often in this file.
|
|
//
|
|
// Classes: CTrkSvrSvc
|
|
// CTrkSvrConfiguration
|
|
// CDbConnection
|
|
// CIntraDomainTable
|
|
// CCrossDomainTable
|
|
// CVolumeTable
|
|
// CDenialChecker
|
|
// CGhostTable - DBG only
|
|
// CQuotaTable
|
|
//
|
|
// Functions:
|
|
//
|
|
//
|
|
//
|
|
// History: 18-Nov-96 BillMo Created.
|
|
// 18-Nov-97 WeiruC Added CQuotaTable class.
|
|
//
|
|
// Notes: Put includes of files that don't change often into pch.cxx.
|
|
//
|
|
// Codework: Put in services.exe : _hDllReference
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "trklib.hxx"
|
|
#include <trkwks.h>
|
|
#include <trksvr.h>
|
|
#include "resource.h"
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Strings
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
const extern TCHAR tszValueNameSeq[] INIT( TEXT("seqRefresh") );
|
|
|
|
#ifndef EVENT_SOURCE_DEFINED
|
|
#define EVENT_SOURCE_DEFINED
|
|
const extern TCHAR* g_ptszEventSource INIT( TEXT("Distributed Link Tracking Server") );
|
|
#endif
|
|
|
|
//
|
|
// LDAP classes and container names
|
|
//
|
|
|
|
const extern TCHAR s_VolumeTableRDN[] INIT( TEXT("CN=VolumeTable,CN=FileLinks,CN=System,") );
|
|
const extern TCHAR s_ObjectMoveTableRDN[] INIT( TEXT("CN=ObjectMoveTable,CN=FileLinks,CN=System,") );
|
|
|
|
const extern TCHAR s_objectClass[] INIT( TEXT("objectClass") );
|
|
const extern TCHAR s_linkTrackVolumeTable[] INIT( TEXT("linkTrackVolumeTable") );
|
|
const extern TCHAR s_linkTrackObjectMoveTable[] INIT( TEXT("linkTrackObjectMoveTable") );
|
|
|
|
const extern TCHAR s_linkTrackVolEntry[] INIT( TEXT("linkTrackVolEntry") );
|
|
const extern TCHAR s_linkTrackOMTEntry[] INIT( TEXT("linkTrackOMTEntry") );
|
|
const extern TCHAR s_Cn[] INIT( TEXT("CN") );
|
|
|
|
//
|
|
// Tables
|
|
//
|
|
|
|
// Volume Table (voltab.cxx)
|
|
//
|
|
// Design VOLUMEID -> MACHINEID SECRET TIME NotificationSequence
|
|
// Logical DN currMachineId volumeSecret timeVolChange seqNotification
|
|
//
|
|
|
|
// DN is stringized volume id
|
|
const extern TCHAR s_currMachineId[] INIT( TEXT("volTableIdxGUID") ); // was "currMachineId"
|
|
const extern TCHAR s_volumeSecret[] INIT( TEXT("linkTrackSecret") );
|
|
const extern TCHAR s_objectCount[] INIT( TEXT("objectCount") );
|
|
const extern TCHAR s_seqNotification[] INIT( TEXT("seqNotification") );
|
|
const extern TCHAR s_timeRefresh[] INIT( TEXT("timeRefresh") );
|
|
const extern TCHAR s_timeVolChange[] INIT( TEXT("timeVolChange") );
|
|
const extern TCHAR s_volTableGUID[] INIT( TEXT("volTableGUID") );
|
|
//const extern TCHAR s_volTableIdxGUID[] INIT( TEXT("volTableIdxGUID") ); // used for currMachineId
|
|
|
|
#ifdef VOL_REPL
|
|
const extern TCHAR s_timeVolChangeSearch[] INIT( TEXT("(timeVolChange;binary>=") );
|
|
#endif
|
|
|
|
const extern TCHAR s_currMachineIdSearch[] INIT( TEXT("(volTableIdxGUID;binary=") );
|
|
|
|
// Object Move Table (idt_ldap.cxx)
|
|
//
|
|
// Design Birth/Current -> New Birth RefreshSequence
|
|
// Design VOLUMEID:OBJID -> VOLUMEID:OBJID VOLUMEID:BIRTHID ULONG
|
|
// Logical DN currVolumeId:currObjectId birthVolumeId:birthObjectId seqRefresh
|
|
// Actual str(VOLUMEID:OBJID) currMachineId:objId birthMachineId:currReplsetid linkTrackSecret
|
|
//
|
|
|
|
// DN is stringized Birth/Current
|
|
const extern TCHAR s_birthLocation[] INIT( TEXT("birthLocation") );
|
|
const extern TCHAR s_currentLocation[] INIT( TEXT("currentLocation") );
|
|
const extern TCHAR s_oMTGuid[] INIT( TEXT("oMTGuid") );
|
|
const extern TCHAR s_oMTIndxGuid[] INIT( TEXT("oMTIndxGuid") );
|
|
|
|
|
|
const extern TCHAR s_RestoreTime[] INIT( TEXT("System\\CurrentControlSet\\Services\\NtDs\\Parameters\\RestoreTime") );
|
|
const extern TCHAR s_RestoreBegin[] INIT( TEXT("RestoreBegin") );
|
|
|
|
const extern TCHAR s_rIDManagerReference[] INIT( TEXT("rIDManagerReference") );
|
|
const extern TCHAR s_fSMORoleOwner[] INIT( TEXT("fSMORoleOwner") );
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// General defines
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#define MAX_SHORTENABLE_SEGMENTS (NUM_VOLUMES)
|
|
#define CCH_UINT32 10 // Max chars for a DWORD value
|
|
#define CCH_UINT64 20 // Max chars for a QUADWORD value
|
|
|
|
#define MAX_WAIT_FOR_DS_STARTUP_SECONDS 360
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Forward declarations
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CTrkSvrConfiguration;
|
|
class CTrkSvrSvc;
|
|
|
|
//-------------------------------------------------------------------//
|
|
// //
|
|
// Global variables //
|
|
// //
|
|
//-------------------------------------------------------------------//
|
|
|
|
EXTERN CTrkSvrSvc * g_ptrksvr; // For RPC stubs to call service
|
|
EXTERN LONG g_ctrksvr INIT(0); // Used to protect against multiple service instances
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Ldap enumeration
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
typedef enum
|
|
{
|
|
ENUM_DELETE_ENTRY,
|
|
ENUM_KEEP_ENTRY,
|
|
ENUM_DELETE_QUOTAFLAGS,
|
|
ENUM_ABORT
|
|
} ENUM_ACTION;
|
|
|
|
typedef ENUM_ACTION (*PFN_LDAP_ENUMERATE_CALLBACK)(
|
|
LDAP * pLdap,
|
|
LDAPMessage * pResult,
|
|
void* UserParam1,
|
|
void* UserParam2
|
|
);
|
|
|
|
ENUM_ACTION
|
|
GcEnumerateCallback(
|
|
LDAP * pLdap,
|
|
LDAPMessage * pResult,
|
|
PVOID pvContext,
|
|
void*
|
|
);
|
|
|
|
BOOL // FALSE if aborted (ENUM_ABORT returned by callback)
|
|
LdapEnumerate(
|
|
LDAP * pLdap,
|
|
TCHAR * ptszBaseDn,
|
|
ULONG Scope,
|
|
TCHAR * Filter,
|
|
TCHAR * Attributes[],
|
|
PFN_LDAP_ENUMERATE_CALLBACK pCallback,
|
|
PVOID pvContext,
|
|
void* UserParam2 = NULL
|
|
);
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Garbage collection
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CQuotaTable;
|
|
|
|
// This structure holds context during enumeration
|
|
// in a GC
|
|
|
|
typedef struct
|
|
{
|
|
// All entries with lower sequence numbers
|
|
// should be deleted.
|
|
SequenceNumber seqOldestToKeep;
|
|
|
|
// The highest sequence number we should see.
|
|
SequenceNumber seqCurrent;
|
|
|
|
// If true, abort (happens on service stop).
|
|
const BOOL *pfAbort;
|
|
|
|
// How long to sleep between non-noop iterations
|
|
DWORD dwRepetitiveTaskDelay;
|
|
|
|
// The quota table
|
|
CQuotaTable *pqtable;
|
|
|
|
// The number of entries that have been deleted (otherwise).
|
|
ULONG cEntries;
|
|
|
|
} GC_ENUM_CONTEXT;
|
|
|
|
typedef struct
|
|
{
|
|
LONG cDelta;
|
|
DWORD dwRepetitiveTaskDelay;
|
|
DWORD dwPass;
|
|
BOOL fCountAll;
|
|
|
|
enum EPass
|
|
{
|
|
FIRST_PASS, SECOND_PASS
|
|
};
|
|
|
|
} TRUE_COUNT_ENUM_CONTEXT;
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Quota table
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#define QFLAG_UNCOUNTED 0x8
|
|
#define QFLAG_DELETED 0x4
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CDbConnection
|
|
//
|
|
// Purpose: Handles shared connection to database
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CDbConnection
|
|
{
|
|
public:
|
|
|
|
// --------------------> indent 24 characters
|
|
|
|
inline CDbConnection();
|
|
inline ~CDbConnection();
|
|
|
|
void Initialize(CSvcCtrlInterface * psvc, OPTIONAL const TCHAR *ptszHostName = NULL );
|
|
void UnInitialize();
|
|
|
|
inline const TCHAR *GetBaseDn() { Ldap(); return _pszBaseDn; }
|
|
|
|
LDAP * Ldap();
|
|
|
|
private:
|
|
|
|
BOOL _fInitializeCalled:1;
|
|
|
|
CCriticalSection _cs;
|
|
|
|
LDAP * _pldap;
|
|
TCHAR * _pszBaseDn;
|
|
};
|
|
|
|
inline // put all types/attributes on same line as inline
|
|
CDbConnection::CDbConnection() :
|
|
_fInitializeCalled(FALSE)
|
|
{
|
|
// Normally critsecs are initialized in the Initialize method.
|
|
// But for this class the Initialize method doesn't get called right
|
|
// away, so we'll try to initialize here and deal with it in Ldap()
|
|
// if this fails.
|
|
|
|
__try
|
|
{
|
|
_cs.Initialize();
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
}
|
|
}
|
|
|
|
inline
|
|
CDbConnection::~CDbConnection()
|
|
{
|
|
UnInitialize();
|
|
_cs.UnInitialize();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CCrossDomainTable
|
|
//
|
|
// Purpose: Contains cross domain links
|
|
//
|
|
// Notes: Columns
|
|
// -------
|
|
//
|
|
// prev_location new_location birth_id
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CCrossDomainTable
|
|
{
|
|
public:
|
|
|
|
inline CCrossDomainTable(CDbConnection & dbc);
|
|
inline ~CCrossDomainTable();
|
|
|
|
void Initialize();
|
|
void UnInitialize();
|
|
|
|
private:
|
|
|
|
BOOL _fInitializeCalled:1;
|
|
|
|
const CDbConnection &
|
|
_dbc;
|
|
};
|
|
|
|
inline
|
|
CCrossDomainTable::CCrossDomainTable(CDbConnection & dbc) :
|
|
_fInitializeCalled(FALSE),
|
|
_dbc(dbc)
|
|
{
|
|
}
|
|
|
|
inline
|
|
CCrossDomainTable::~CCrossDomainTable()
|
|
{
|
|
UnInitialize();
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CRefreshSequenceStorage
|
|
//
|
|
// Purpose: Stores the refresh sequence number in the DS's restorable
|
|
// table data.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CVolumeTable;
|
|
|
|
class CRefreshSequenceStorage
|
|
{
|
|
public:
|
|
|
|
inline CRefreshSequenceStorage( CVolumeTable * pVolTab, CQuotaTable *pQuotaTab, CTrkSvrConfiguration *psvrconfig );
|
|
inline ~CRefreshSequenceStorage();
|
|
inline void Initialize();
|
|
|
|
public:
|
|
|
|
SequenceNumber GetSequenceNumber();
|
|
|
|
void IncrementSequenceNumber();
|
|
|
|
private:
|
|
|
|
SequenceNumber _seq;
|
|
CCriticalSection _cs;
|
|
|
|
CVolumeTable * _pVolTab;
|
|
CQuotaTable * _pQuotaTab;
|
|
CTrkSvrConfiguration * _psvrconfig;
|
|
CFILETIME _cftLastRead;
|
|
};
|
|
|
|
inline
|
|
CRefreshSequenceStorage::CRefreshSequenceStorage( CVolumeTable * pVolTab,
|
|
CQuotaTable *pQuotaTab,
|
|
CTrkSvrConfiguration *psvrconfig ) :
|
|
_pVolTab(pVolTab),
|
|
_pQuotaTab(pQuotaTab),
|
|
_cftLastRead(0),
|
|
_psvrconfig(psvrconfig)
|
|
{
|
|
}
|
|
|
|
inline void
|
|
CRefreshSequenceStorage::Initialize()
|
|
{
|
|
_cs.Initialize();
|
|
}
|
|
|
|
|
|
inline
|
|
CRefreshSequenceStorage::~CRefreshSequenceStorage()
|
|
{
|
|
_cs.UnInitialize();
|
|
}
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// Class CAbbreviatedIDString
|
|
//
|
|
// Take an ID (e.g. a Droid) and create an abbreviated string for
|
|
// dbg outs.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
class CAbbreviatedIDString
|
|
{
|
|
private:
|
|
|
|
TCHAR _tsz[ 8 ]; // e.g. {01:02}
|
|
|
|
public:
|
|
|
|
inline CAbbreviatedIDString( const CDomainRelativeObjId &droid );
|
|
inline operator const TCHAR *() const;
|
|
};
|
|
|
|
inline
|
|
CAbbreviatedIDString::CAbbreviatedIDString( const CDomainRelativeObjId &droid )
|
|
{
|
|
GUID guidObjId, guidVolId;
|
|
droid.GetVolumeId().SerializeRaw( reinterpret_cast<BYTE*>(&guidVolId) );
|
|
droid.GetObjId().SerializeRaw( reinterpret_cast<BYTE*>(&guidObjId) );
|
|
|
|
_stprintf( _tsz, TEXT("{%02x:%02x}"),
|
|
guidVolId.Data1 & 0xFF, guidObjId.Data1 & 0xFF );
|
|
}
|
|
|
|
inline
|
|
CAbbreviatedIDString::operator const TCHAR *() const
|
|
{
|
|
return( _tsz );
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CIntraDomainTable (a.k.a. Object Move Table)
|
|
//
|
|
// Purpose: Handles updates and queries of the location database.
|
|
//
|
|
// Notes: Columns
|
|
// -------
|
|
//
|
|
// prev_location(indexed) new_location birth_id
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
struct IDT_ENTRY;
|
|
class CQuotaTable;
|
|
|
|
class CIntraDomainTable
|
|
{
|
|
public:
|
|
|
|
inline CIntraDomainTable(
|
|
CDbConnection & dbc,
|
|
CRefreshSequenceStorage * pseqRefresh
|
|
);
|
|
|
|
inline ~CIntraDomainTable();
|
|
|
|
void Initialize( CTrkSvrConfiguration *pTrkSvrConfiguration, CQuotaTable* pqtable );
|
|
void UnInitialize();
|
|
|
|
public:
|
|
BOOL Add(const CDomainRelativeObjId &droidKey,
|
|
const CDomainRelativeObjId &droidNew,
|
|
const CDomainRelativeObjId &droidBirth,
|
|
BOOL *pfQuotaExceeded = NULL OPTIONAL
|
|
);
|
|
//-> indent 4 chars
|
|
|
|
BOOL Delete(const CDomainRelativeObjId &droidKey);
|
|
|
|
ULONG GarbageCollect( SequenceNumber seqCurrent,
|
|
SequenceNumber seqOldestToKeep,
|
|
const BOOL * pfAbort );
|
|
|
|
BOOL Modify(const CDomainRelativeObjId &droidKey,
|
|
const CDomainRelativeObjId &droidNew,
|
|
const CDomainRelativeObjId &droidBirth
|
|
);
|
|
|
|
BOOL Query(const CDomainRelativeObjId &droidKey,
|
|
CDomainRelativeObjId *pdroidNew,
|
|
CDomainRelativeObjId *pdroidBirth,
|
|
BOOL *pfDeleted = NULL OPTIONAL,
|
|
BOOL *pfCounted = NULL OPTIONAL );
|
|
|
|
BOOL Query( LDAP* pLdap,
|
|
LDAPMessage *pEntry,
|
|
const CDomainRelativeObjId ldKey,
|
|
CDomainRelativeObjId *pldNew,
|
|
CDomainRelativeObjId *pldBirth,
|
|
BOOL *pfDeleted = NULL OPTIONAL,
|
|
BOOL *pfCounted = NULL OPTIONAL );
|
|
BOOL Touch(const CDomainRelativeObjId &droidKey);
|
|
|
|
|
|
#if DBG
|
|
void PurgeAll();
|
|
#endif
|
|
|
|
private:
|
|
|
|
inline const TCHAR *GetBaseDn() { return _dbc.GetBaseDn(); }
|
|
|
|
inline LDAP * Ldap() { return _dbc.Ldap(); }
|
|
|
|
private:
|
|
BOOL _fInitializeCalled:1;
|
|
|
|
CDbConnection & _dbc;
|
|
CRegBoolParameter _QuotaReported;
|
|
|
|
CRefreshSequenceStorage *
|
|
_pRefreshSequenceStorage;
|
|
|
|
CQuotaTable* _pqtable;
|
|
CTrkSvrConfiguration
|
|
*_pTrkSvrConfiguration;
|
|
};
|
|
|
|
inline
|
|
CIntraDomainTable::CIntraDomainTable(CDbConnection & dbc,
|
|
CRefreshSequenceStorage * pRefreshSequenceStorage ) :
|
|
_fInitializeCalled(FALSE),
|
|
_dbc(dbc),
|
|
_pRefreshSequenceStorage( pRefreshSequenceStorage ),
|
|
_QuotaReported( TEXT("IntraDomainTableQuotaReported") )
|
|
{
|
|
}
|
|
|
|
inline
|
|
CIntraDomainTable::~CIntraDomainTable()
|
|
{
|
|
UnInitialize();
|
|
}
|
|
|
|
|
|
// Objects to make constructing quota table keys easy.
|
|
|
|
class CLdapSeqNum
|
|
{
|
|
public:
|
|
CLdapSeqNum()
|
|
{
|
|
_tcscpy( _tszSeq, TEXT("0") );
|
|
}
|
|
|
|
CLdapSeqNum(SequenceNumber seq)
|
|
{
|
|
_stprintf( _tszSeq, TEXT("%lu"), seq );
|
|
}
|
|
|
|
operator TCHAR*()
|
|
{
|
|
return( _tszSeq );
|
|
}
|
|
|
|
TCHAR _tszSeq[ CCH_UINT32 ];
|
|
|
|
};
|
|
|
|
|
|
class CLdapQuotaKeyDn
|
|
{
|
|
public:
|
|
|
|
CLdapQuotaKeyDn(const TCHAR* ptszBaseDn, const CMachineId& mcid)
|
|
{
|
|
TCHAR* ptsz;
|
|
|
|
_tcscpy(_tszDn, TEXT("CN="));
|
|
_tcscat(_tszDn, TEXT("QTDC_"));
|
|
ptsz = _tszDn + _tcslen(_tszDn);
|
|
mcid.Stringize(ptsz);
|
|
*ptsz++ = TEXT(',');
|
|
_tcscpy(ptsz, s_VolumeTableRDN);
|
|
_tcscat(_tszDn, ptszBaseDn);
|
|
TrkAssert(_tcslen(_tszDn) < ELEMENTS(_tszDn));
|
|
}
|
|
|
|
inline operator TCHAR* () { return _tszDn; }
|
|
|
|
private:
|
|
|
|
TCHAR _tszDn[MAX_PATH];
|
|
};
|
|
|
|
class CLdapQuotaCounterKeyDn
|
|
{
|
|
public:
|
|
|
|
CLdapQuotaCounterKeyDn(const TCHAR* ptszBaseDn)
|
|
{
|
|
TCHAR* ptsz;
|
|
|
|
_tcscpy(_tszDn, TEXT("CN="));
|
|
_tcscat(_tszDn, TEXT("QT_Counter"));
|
|
ptsz = _tszDn + _tcslen(_tszDn);
|
|
*ptsz++ = TEXT(',');
|
|
_tcscpy(ptsz, s_VolumeTableRDN);
|
|
_tcscat(_tszDn, ptszBaseDn);
|
|
TrkAssert(_tcslen(_tszDn) < ELEMENTS(_tszDn));
|
|
}
|
|
|
|
inline operator TCHAR* () { return _tszDn; }
|
|
|
|
private:
|
|
|
|
TCHAR _tszDn[MAX_PATH];
|
|
};
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Class: CQuotaTable
|
|
//
|
|
// Purpose: Encapsulate functionalities that enforce quota on the
|
|
// tables in the DC (move table, volume table).
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
class CQuotaTable : public PTimerCallback
|
|
{
|
|
|
|
public:
|
|
|
|
// constructor/destructor
|
|
|
|
CQuotaTable(CDbConnection& dbc);
|
|
~CQuotaTable();
|
|
|
|
// initialization/uninitialization
|
|
|
|
void Initialize(CVolumeTable *pvoltab,
|
|
CIntraDomainTable *pidt,
|
|
CTrkSvrSvc *psvrsvc,
|
|
CTrkSvrConfiguration *pcfgsvr );
|
|
void UnInitialize();
|
|
|
|
BOOL IsDesignatedDc( BOOL fRaiseOnError = FALSE );
|
|
|
|
// timer call back
|
|
|
|
virtual TimerContinuation Timer(ULONG);
|
|
|
|
// checking quota
|
|
|
|
BOOL IsMoveQuotaExceeded();
|
|
BOOL IsVolumeQuotaExceeded(const CMachineId& mcid, ULONG cUncountedVolumes);
|
|
|
|
BOOL UpdateFlags(LDAP* pLdap, TCHAR* dnKey, BYTE bFlags);
|
|
void DeleteFlags(LDAP* pLdap, TCHAR* dnKey);
|
|
void OnServiceStopRequest();
|
|
|
|
void IncrementMoveCountCache() { InterlockedIncrement(&_lCachedMoveTableCount); }
|
|
void DecrementMoveCountCache() { InterlockedDecrement(&_lCachedMoveTableCount); }
|
|
|
|
inline void IncrementVolumeCountCache();
|
|
inline void DecrementVolumeCountCache();
|
|
|
|
void InvalidateCache()
|
|
{
|
|
_cftCacheLastUpdated = 0;
|
|
DeleteCounter();
|
|
}
|
|
|
|
void Statistics( TRKSVR_STATISTICS *pTrkSvrStatistics );
|
|
void OnMoveTableGcComplete( ULONG cEntriesDeleted );
|
|
|
|
private:
|
|
|
|
enum EBackgroundForegroundTask
|
|
{
|
|
BACKGROUND = 1,
|
|
FOREGROUND
|
|
};
|
|
|
|
private:
|
|
|
|
void Lock();
|
|
void Unlock();
|
|
DWORD CalculateMoveLimit(); // Doesn't raise
|
|
|
|
|
|
inline LDAP* Ldap() const { return _dbc.Ldap(); }
|
|
inline const TCHAR *GetBaseDn() { return _dbc.GetBaseDn(); }
|
|
|
|
BOOL ReadCounter(DWORD* dwCounter);
|
|
HRESULT DeleteCounter();
|
|
void GetTrueCount( DWORD* pdwTrueCount,
|
|
EBackgroundForegroundTask eBackgroundForegroundTask );
|
|
void WriteCounter(DWORD);
|
|
|
|
void ValidateCache( BOOL fForceHint = FALSE );
|
|
|
|
HRESULT ReadFlags(LDAP* pLdap, TCHAR* dnKey, BYTE* bFlags);
|
|
|
|
BOOL DeleteOrphanedEntries( const CDomainRelativeObjId rgdroidList[], ULONG cSegments,
|
|
const CDomainRelativeObjId &droidBirth,
|
|
const CDomainRelativeObjId &droidCurrent );
|
|
void ShortenString( LDAP* pLdap, LDAPMessage* pMessage, BYTE *pbFlags,
|
|
const CDomainRelativeObjId &droidCurrent );
|
|
|
|
TCHAR * GetFirstCN( LDAP *pLdap, LDAPMessage *pMessage );
|
|
|
|
static ENUM_ACTION
|
|
MoveTableEnumCallback( LDAP * pLdap,
|
|
LDAPMessage * pResult,
|
|
void* cEntries,
|
|
void* pvThis );
|
|
|
|
static ENUM_ACTION
|
|
VolumeTableEnumCallback( LDAP * pLdap,
|
|
LDAPMessage * pResult,
|
|
void* pcEntries,
|
|
void* pvThis );
|
|
|
|
|
|
private:
|
|
|
|
enum
|
|
{
|
|
QUOTA_TIMER = 0,
|
|
QUOTA_CLEANUP_TIMER = 1
|
|
} EQuotaTimer;
|
|
|
|
BOOL _fInitializeCalled:1;
|
|
BOOL _fIsDesignatedDc:1;
|
|
BOOL _fStopping:1;
|
|
|
|
CFILETIME _cftDesignatedDc;
|
|
|
|
CTrkSvrConfiguration *_pcfgsvr;
|
|
CDbConnection& _dbc;
|
|
DWORD _dwMoveLimit;
|
|
CMachineId _mcid;
|
|
LONG _lCachedMoveTableCount;
|
|
LONG _lCachedVolumeTableCount;
|
|
CFILETIME _cftCacheLastUpdated;
|
|
|
|
|
|
CVolumeTable *_pvoltab;
|
|
CIntraDomainTable *_pidt;
|
|
CTrkSvrSvc *_psvrsvc;
|
|
|
|
CNewTimer _timer;
|
|
|
|
CCriticalSection _cs;
|
|
#if DBG
|
|
LONG _cLocks;
|
|
#endif
|
|
};
|
|
|
|
inline void
|
|
CQuotaTable::OnServiceStopRequest()
|
|
{
|
|
_fStopping = TRUE;
|
|
}
|
|
|
|
inline void
|
|
CQuotaTable::Lock()
|
|
{
|
|
_cs.Enter();
|
|
|
|
#if DBG
|
|
_cLocks++;
|
|
#endif
|
|
}
|
|
|
|
inline void
|
|
CQuotaTable::Unlock()
|
|
{
|
|
TrkAssert( 0 < _cLocks );
|
|
_cs.Leave();
|
|
|
|
#if DBG
|
|
_cLocks--;
|
|
#endif
|
|
}
|
|
|
|
inline void
|
|
CQuotaTable::IncrementVolumeCountCache()
|
|
{
|
|
Lock();
|
|
_lCachedVolumeTableCount++;
|
|
_dwMoveLimit = CalculateMoveLimit(); // Doesn't raise
|
|
Unlock();
|
|
}
|
|
|
|
inline void
|
|
CQuotaTable::DecrementVolumeCountCache()
|
|
{
|
|
Lock();
|
|
--_lCachedVolumeTableCount;
|
|
_dwMoveLimit = CalculateMoveLimit(); // Doesn't raise
|
|
Unlock();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CVolumeTable
|
|
//
|
|
// Purpose: Table of volumes
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CVolumeTable
|
|
#ifdef VOL_REPL
|
|
: PTimerCallback
|
|
#endif
|
|
{
|
|
public: // Construction/destruction
|
|
|
|
inline CVolumeTable(
|
|
CDbConnection & dbc,
|
|
CRefreshSequenceStorage * pRefreshSequenceStorage
|
|
);
|
|
|
|
inline ~CVolumeTable();
|
|
|
|
void Initialize(CTrkSvrConfiguration *pconfigSvr,
|
|
CQuotaTable *pqtable);
|
|
void UnInitialize();
|
|
|
|
public: // Public RPC level interface - must be parameter checked
|
|
|
|
HRESULT ClaimVolume( const CMachineId & mcidClient,
|
|
const CVolumeId & volume,
|
|
const CVolumeSecret & secretOld,
|
|
const CVolumeSecret & secretNew,
|
|
SequenceNumber * pseq,
|
|
FILETIME * pftLastRefresh );
|
|
|
|
HRESULT PreCreateVolume( const CMachineId & mcidClient,
|
|
const CVolumeSecret & secret,
|
|
ULONG cUncountedVolumes,
|
|
CVolumeId * pvolume );
|
|
|
|
HRESULT DeleteVolume( const CMachineId & mcidClient,
|
|
const CVolumeId & volume );
|
|
|
|
HRESULT FindVolume( const CVolumeId & volume,
|
|
CMachineId * pmcid );
|
|
|
|
HRESULT QueryVolume( const CMachineId & mcidClient,
|
|
const CVolumeId & volume,
|
|
SequenceNumber * pseq,
|
|
FILETIME * pftLastRefresh );
|
|
//-> indent 4 chars
|
|
|
|
BOOL Touch( const CVolumeId & volume );
|
|
|
|
public:
|
|
|
|
HRESULT SetSequenceNumber(const CVolumeId & volume, SequenceNumber seq);
|
|
|
|
HRESULT SetSecret( const CVolumeId & volume, const CVolumeSecret & secret );
|
|
|
|
int GetAvailableNotificationQuota( const CVolumeId & volid );
|
|
|
|
DWORD CountVolumes( const CMachineId & mcidClient );
|
|
ULONG GarbageCollect( SequenceNumber seqCurrent, SequenceNumber seqOldestToKeep, const BOOL * pfAbort );
|
|
|
|
HRESULT GetVolumeInfo( const CVolumeId & volume,
|
|
CMachineId * pmcid,
|
|
CVolumeSecret * psecret,
|
|
SequenceNumber * pseq,
|
|
CFILETIME *pcftRefresh );
|
|
|
|
HRESULT GetMachine( const CVolumeId & volume, CMachineId * pmcid );
|
|
|
|
HRESULT SetMachine( const CVolumeId & volume, const CMachineId & mcid );
|
|
|
|
HRESULT SetMachineAndSecret(const CVolumeId & volume, const CMachineId & mcid,
|
|
const CVolumeSecret & secret);
|
|
|
|
|
|
inline const TCHAR *GetBaseDn() { return _dbc.GetBaseDn(); }
|
|
|
|
inline LDAP * Ldap() const { return _dbc.Ldap(); }
|
|
|
|
#if DBG
|
|
void PurgeCache( );
|
|
#endif
|
|
|
|
HRESULT AddVolidToTable( const CVolumeId & volume,
|
|
const CMachineId & mcidClient,
|
|
const CVolumeSecret & secret );
|
|
|
|
void PurgeAll();
|
|
|
|
#ifdef VOL_REPL
|
|
void QueryVolumeChanges( const CFILETIME &ftFirstChange, CVolumeMap * pVolMap );
|
|
|
|
void SimpleTimer( DWORD dwTimerId );
|
|
#endif
|
|
|
|
private:
|
|
|
|
HRESULT MapResult(int ldap_err) const;
|
|
|
|
#ifdef VOL_REPL
|
|
void _QueryVolumeChanges( const CFILETIME &ftFirstChange, CVolumeMap * pVolMap );
|
|
#endif
|
|
|
|
private:
|
|
|
|
BOOL _fInitializeCalled:1;
|
|
|
|
CDbConnection & _dbc;
|
|
|
|
CRefreshSequenceStorage *
|
|
_pRefreshSequenceStorage;
|
|
CTrkSvrConfiguration *
|
|
_pconfigSvr;
|
|
|
|
// State for caching the volume table so that
|
|
// synchronizing the workstation's volume tables is efficient (normally the workstation
|
|
// queries will hit the cached query.)
|
|
CQuotaTable* _pqtable;
|
|
|
|
#ifdef VOL_REPL
|
|
CCritcalSection _csQueryCache;
|
|
|
|
CVolumeMap _VolMap; // keeps cache of query since we have many machines
|
|
// getting the updates
|
|
CFILETIME _cftCacheLowest;
|
|
CSimpleTimer _timerQueryCache;
|
|
DWORD _SecondsPreviousToNow;
|
|
|
|
#endif
|
|
};
|
|
|
|
inline
|
|
CVolumeTable::CVolumeTable( CDbConnection & dbc,
|
|
CRefreshSequenceStorage * pRefreshSequenceStorage
|
|
) :
|
|
_dbc(dbc),
|
|
_fInitializeCalled(FALSE),
|
|
_pRefreshSequenceStorage( pRefreshSequenceStorage ),
|
|
_pconfigSvr(NULL)
|
|
{
|
|
}
|
|
|
|
inline
|
|
CVolumeTable::~CVolumeTable()
|
|
{
|
|
UnInitialize();
|
|
}
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CActiveCreates
|
|
//
|
|
// Keep track of Create-Volume requests that are in the callback.
|
|
// This is necessary to ensure that we don't have one machine tying up
|
|
// more than one thread on the server.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
class CActiveCreates
|
|
{
|
|
private:
|
|
|
|
// Keep an array of the active machines.
|
|
CMachineId *_prg;
|
|
ULONG _cEntries;
|
|
|
|
CCriticalSection _cs;
|
|
|
|
public:
|
|
|
|
CActiveCreates()
|
|
{
|
|
_prg = NULL;
|
|
_cEntries = 0;
|
|
}
|
|
|
|
void Initialize( ULONG cEntries )
|
|
{
|
|
// Allocate an array of machine IDs.
|
|
|
|
_prg = new CMachineId[ cEntries ];
|
|
if( NULL == _prg )
|
|
TrkRaiseWin32Error( ERROR_NOT_ENOUGH_MEMORY );
|
|
_cEntries = cEntries;
|
|
|
|
memset( _prg, 0, cEntries * sizeof(CMachineId) );
|
|
|
|
// Initialize the critical section. If this raises, ignore the
|
|
// exception; we'll catch it later whenever we call _cs.Lock.
|
|
|
|
__try
|
|
{
|
|
_cs.Initialize();
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
TrkLog(( TRKDBG_WARNING, TEXT("Exception in CActiveCreates constructor (%08x)"),
|
|
GetExceptionCode() ));
|
|
}
|
|
}
|
|
|
|
~CActiveCreates()
|
|
{
|
|
if( NULL != _prg )
|
|
delete[] _prg;
|
|
_cs.UnInitialize();
|
|
}
|
|
|
|
public:
|
|
|
|
// Add a machine to the list of active creates.
|
|
// This raises if the machine is already in the list.
|
|
|
|
void Add( const CMachineId &mcid )
|
|
{
|
|
_cs.Enter();
|
|
__try // __finally
|
|
{
|
|
int iFree = -1;
|
|
|
|
// Search the array to see if this machine is already in
|
|
// the list. While we're at it, assuming it's not already in
|
|
// the list, find a free entry.
|
|
|
|
for( int i = 0; i < _cEntries; i++ )
|
|
{
|
|
// Is this the machine?
|
|
if( mcid == _prg[i] )
|
|
{
|
|
// Yes, this machine is already in the list. Raise.
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Machine attempting simultaneous creates (%s)"),
|
|
(const TCHAR*) CDebugString(mcid) ));
|
|
TrkRaiseException(TRK_E_DENIAL_OF_SERVICE_ATTACK);
|
|
}
|
|
|
|
// Otherwise, is this a free entry?
|
|
else if( CMachineId() == _prg[i] )
|
|
iFree = i;
|
|
}
|
|
|
|
// If we reach this point, the machine isn't already on the list,
|
|
// so we'll add it.
|
|
if( -1 != iFree )
|
|
// Add the machine to a free entry in the list.
|
|
_prg[ iFree ] = mcid;
|
|
else
|
|
{
|
|
// There was no free entry in the list (this should never
|
|
// happen).
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't find free space in CActiveCreates") ));
|
|
TrkRaiseWin32Error( ERROR_INTERNAL_DB_CORRUPTION );
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
_cs.Leave();
|
|
}
|
|
}
|
|
|
|
// Remove an entry from the list
|
|
|
|
void Remove( const CMachineId &mcid )
|
|
{
|
|
_cs.Enter();
|
|
__try
|
|
{
|
|
for( int i = 0; i < _cEntries; i++ )
|
|
{
|
|
if( mcid == _prg[i] )
|
|
{
|
|
_prg[i] = CMachineId();
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if DBG
|
|
if( i == _cEntries )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("*** Not found in CActiveCreates::Remove ***") ));
|
|
}
|
|
#endif
|
|
}
|
|
__finally
|
|
{
|
|
_cs.Leave();
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CDenialChecker
|
|
//
|
|
// Purpose: Checks for denial of service attacks
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
struct ACTIVECLIENT;
|
|
|
|
class CDenialChecker : public PTimerCallback
|
|
{
|
|
public:
|
|
inline CDenialChecker();
|
|
inline ~CDenialChecker();
|
|
|
|
void Initialize( ULONG ulHistoryPeriod );
|
|
void UnInitialize();
|
|
|
|
void CheckClient(const CMachineId &mcidClient);
|
|
|
|
virtual TimerContinuation Timer( ULONG ulTimerId ); // timeout for clearing out old ACTIVECLIENTs
|
|
|
|
private:
|
|
|
|
BOOL _fInitializeCalled;
|
|
CNewTimer _timer;
|
|
ACTIVECLIENT * _pListHead;
|
|
|
|
CCriticalSection _cs;
|
|
#if DBG
|
|
LONG _lAllocs;
|
|
#endif
|
|
};
|
|
|
|
inline
|
|
CDenialChecker::CDenialChecker() :
|
|
_fInitializeCalled(FALSE)
|
|
{
|
|
}
|
|
|
|
inline
|
|
CDenialChecker::~CDenialChecker()
|
|
{
|
|
UnInitialize();
|
|
}
|
|
|
|
|
|
class CTrkSvrRpcServer : public CRpcServer
|
|
{
|
|
public:
|
|
|
|
void Initialize( SVCHOST_GLOBAL_DATA * pSvcsGlobalData, CTrkSvrConfiguration *pTrkSvrConfig );
|
|
void UnInitialize( SVCHOST_GLOBAL_DATA * pSvcsGlobalData );
|
|
|
|
};
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CTrkSvrSvc
|
|
//
|
|
// Purpose: Tracking (Server) Service
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#define MAX_SVR_THREADS 10
|
|
|
|
class CTrkSvrSvc : public PTimerCallback,
|
|
public IServiceHandler
|
|
{
|
|
public: // Initialization
|
|
|
|
inline CTrkSvrSvc();
|
|
inline ~CTrkSvrSvc();
|
|
|
|
void Initialize(SVCHOST_GLOBAL_DATA * pSvcsGlobalData );
|
|
void UnInitialize(HRESULT hr);
|
|
|
|
public: // RPC server methods
|
|
|
|
void DeleteNotify(
|
|
const CMachineId & mcidClient,
|
|
TRKSVR_CALL_DELETE * pDelete
|
|
);
|
|
|
|
HRESULT MoveNotify(
|
|
const CMachineId & mcidClient,
|
|
TRKSVR_CALL_MOVE_NOTIFICATION * pMove
|
|
);
|
|
|
|
void Search(
|
|
TRK_FILE_TRACKING_INFORMATION * pSearches
|
|
);
|
|
|
|
void old_Search(
|
|
old_TRK_FILE_TRACKING_INFORMATION * pSearches
|
|
);
|
|
|
|
HRESULT SyncVolume(
|
|
const CMachineId & mcidClient,
|
|
TRKSVR_SYNC_VOLUME * pSyncVolume,
|
|
ULONG cUncountedCreates
|
|
);
|
|
|
|
void Refresh(
|
|
const CMachineId & mcidClient,
|
|
TRKSVR_CALL_REFRESH * pRefresh
|
|
);
|
|
|
|
void Statistics(
|
|
TRKSVR_STATISTICS *pTrkSvrStatistics
|
|
);
|
|
|
|
|
|
public: // General publics
|
|
|
|
HRESULT SvrMessage( handle_t IDL_handle, TRKSVR_MESSAGE_UNION * pMsg );
|
|
|
|
inline void CheckClient( const CMachineId & mcid );
|
|
|
|
BOOL CountPrioritizedThread( const TRKSVR_MESSAGE_UNION * pMsg );
|
|
|
|
void ReleasePrioritizedThread( );
|
|
|
|
void DoRefresh( const CRpcClientBinding & rc );
|
|
|
|
inline DWORD GetState();
|
|
virtual TimerContinuation Timer( ULONG ulTimerContext ); // refresh timer
|
|
DWORD ServiceHandler(DWORD dwControl, // IServiceHandler implementation
|
|
DWORD dwEventType,
|
|
PVOID EventData,
|
|
PVOID pData);
|
|
|
|
inline const BOOL * GetStopFlagAddress();
|
|
|
|
BOOL RequireSecureRPC();
|
|
|
|
HRESULT CreateVolume(const CMachineId & mcidClient, const TRKSVR_SYNC_VOLUME& pSyncVolume);
|
|
|
|
inline void SetLastError( HRESULT hr );
|
|
void OnRequestStart( TRKSVR_MESSAGE_TYPE MsgType );
|
|
void OnRequestEnd( TRKSVR_MESSAGE_UNION * pMsg, const CMachineId &mcid, HRESULT hr );
|
|
void RaiseIfStopped();
|
|
|
|
void Scan(
|
|
IN const CDomainRelativeObjId * pdroidNotificationCurrent, OPTIONAL
|
|
IN const CDomainRelativeObjId * pdroidNotificationNew, OPTIONAL
|
|
IN const CDomainRelativeObjId & droidBirth,
|
|
OUT CDomainRelativeObjId * pdroidList,
|
|
IN int cdroidList,
|
|
OUT int * pcSegments,
|
|
IN OUT CDomainRelativeObjId * pdroidScan,
|
|
OUT BOOL *pfStringDeleted
|
|
);
|
|
|
|
public: // testing
|
|
|
|
friend class CTestSvrSvc;
|
|
void TestRestore( const TCHAR * ptszMachine );
|
|
|
|
private:
|
|
|
|
void MoveNotify(const CDomainRelativeObjId &droidCurrent,
|
|
const CDomainRelativeObjId &droidBirth,
|
|
const CDomainRelativeObjId &droidNew,
|
|
BOOL *pfQuotaExceeded );
|
|
|
|
SequenceNumber GetSequenceNumber( const CMachineId & mcidClient,
|
|
const CVolumeId & volume);
|
|
|
|
void SetSequenceNumber( const CVolumeId & volume, // must ensure that validation already done for volume
|
|
SequenceNumber seq );
|
|
|
|
inline BOOL IncrementAndCheckWritesPerHour();
|
|
inline void IncrementWritesPerHour();
|
|
BOOL CheckWritesPerHour();
|
|
inline ULONG NumWritesThisHour() const;
|
|
BOOL VerifyMachineOwnsVolume( const CMachineId &mcid, const CVolumeId & volid );
|
|
|
|
|
|
|
|
|
|
private: // data members
|
|
|
|
BOOL _fInitializeCalled:1;
|
|
|
|
// Indicates if we're hesitating before doing a GC
|
|
// (see DoWork)
|
|
BOOL _fHesitatingBeforeGC:1;
|
|
|
|
// Indicates that service_stop/shutdown has been received
|
|
BOOL _fStopping:1;
|
|
|
|
// If true, and we're in a GC phase, GC the volume table
|
|
// rather than the move table.
|
|
BOOL _fGCVolumeTable:1;
|
|
|
|
SVCHOST_GLOBAL_DATA * _pSvcsGlobalData;
|
|
|
|
// Protect against denail-of-service attacks.
|
|
CDenialChecker _denial;
|
|
|
|
// This protects against a client doing too many creates.
|
|
CActiveCreates _activeCreates;
|
|
|
|
// LDAP manager
|
|
CDbConnection _dbc;
|
|
|
|
CRefreshSequenceStorage
|
|
_refreshSequence;
|
|
CCrossDomainTable _cdt;
|
|
CIntraDomainTable _idt;
|
|
CVolumeTable _voltab;
|
|
|
|
CRegDwordParameter _MoveCounterReset;
|
|
CNewTimer _timerGC;
|
|
LONG _cGarbageCollectionsRunning;
|
|
|
|
CQuotaTable _qtable;
|
|
CTrkSvrRpcServer _rpc;
|
|
CSvcCtrlInterface _svcctrl;
|
|
|
|
CTrkSvrConfiguration
|
|
_configSvr;
|
|
LONG _cAvailableThreads, _cLowestAvailableThreads;
|
|
|
|
// The following values track the number of writes (to the DS)
|
|
// in an hour. If this gets too high, trksvr disallows writes
|
|
// for the rest of the hour.
|
|
|
|
CFILETIME _cftWritesPerHour;
|
|
ULONG _cWritesPerHour;
|
|
CCriticalSection _csWritesPerHour;
|
|
|
|
// Statistics
|
|
struct tagStats
|
|
{
|
|
ULONG cSyncVolumeRequests, cSyncVolumeErrors, cSyncVolumeThreads;
|
|
|
|
ULONG cCreateVolumeRequests, cCreateVolumeErrors;
|
|
ULONG cClaimVolumeRequests, cClaimVolumeErrors;
|
|
ULONG cQueryVolumeRequests, cQueryVolumeErrors;
|
|
ULONG cFindVolumeRequests, cFindVolumeErrors;
|
|
ULONG cTestVolumeRequests, cTestVolumeErrors;
|
|
|
|
ULONG cSearchRequests, cSearchErrors, cSearchThreads;
|
|
ULONG cMoveNotifyRequests, cMoveNotifyErrors, cMoveNotifyThreads;
|
|
ULONG cRefreshRequests, cRefreshErrors, cRefreshThreads;
|
|
ULONG cDeleteNotifyRequests, cDeleteNotifyErrors, cDeleteNotifyThreads;
|
|
|
|
//ULONG ulGCIterationPeriod;
|
|
//SHORT cEntriesToGC;
|
|
SHORT cEntriesGCed;
|
|
SHORT cMaxDsWriteEvents;
|
|
SHORT cCurrentFailedWrites;
|
|
|
|
CFILETIME cftLastSuccessfulRequest;
|
|
CFILETIME cftServiceStartTime;
|
|
HRESULT hrLastError;
|
|
} _Stats;
|
|
|
|
public:
|
|
|
|
COperationLog _OperationLog;
|
|
|
|
};
|
|
|
|
inline
|
|
CTrkSvrSvc::CTrkSvrSvc() :
|
|
_fInitializeCalled(FALSE),
|
|
_fHesitatingBeforeGC(FALSE),
|
|
_fStopping(FALSE),
|
|
_idt(_dbc, &_refreshSequence),
|
|
_cdt(_dbc),
|
|
_voltab(_dbc, &_refreshSequence),
|
|
_qtable(_dbc),
|
|
_cWritesPerHour(0),
|
|
_cGarbageCollectionsRunning(0),
|
|
_fGCVolumeTable(FALSE),
|
|
_MoveCounterReset( TEXT("MoveCounterReset") ),
|
|
_refreshSequence( &_voltab, &_qtable, &_configSvr ) // refreshSequence uses voltab to store its state
|
|
{
|
|
// Statistics
|
|
|
|
memset( &_Stats, 0, sizeof(_Stats) );
|
|
|
|
_Stats.cftLastSuccessfulRequest = 0;
|
|
_Stats.cftServiceStartTime = CFILETIME();
|
|
|
|
}
|
|
|
|
inline
|
|
CTrkSvrSvc::~CTrkSvrSvc()
|
|
{
|
|
TrkAssert(!_fInitializeCalled);
|
|
}
|
|
|
|
inline const BOOL *
|
|
CTrkSvrSvc::GetStopFlagAddress()
|
|
{
|
|
return(_svcctrl.GetStopFlagAddress());
|
|
}
|
|
|
|
inline BOOL
|
|
CTrkSvrSvc::RequireSecureRPC()
|
|
{
|
|
return( _rpc.RpcSecurityEnabled() );
|
|
}
|
|
|
|
inline void
|
|
CTrkSvrSvc::SetLastError( HRESULT hr )
|
|
{
|
|
_Stats.hrLastError = hr;
|
|
}
|
|
|
|
inline void
|
|
CTrkSvrSvc::CheckClient( const CMachineId & mcid )
|
|
{
|
|
_denial.CheckClient( mcid );
|
|
}
|
|
|
|
inline DWORD
|
|
CTrkSvrSvc::GetState()
|
|
{
|
|
return( _svcctrl.GetState() );
|
|
}
|
|
|
|
inline void
|
|
CTrkSvrSvc::IncrementWritesPerHour()
|
|
{
|
|
#if DBG
|
|
LONG l = InterlockedIncrement( (LONG*)&_cWritesPerHour );
|
|
TrkLog(( TRKDBG_SVR, TEXT("WritesPerHour: %d"), l ));
|
|
#else
|
|
InterlockedIncrement( (LONG*)&_cWritesPerHour );
|
|
#endif
|
|
}
|
|
|
|
inline BOOL
|
|
CTrkSvrSvc::IncrementAndCheckWritesPerHour()
|
|
{
|
|
IncrementWritesPerHour();
|
|
return CheckWritesPerHour();
|
|
}
|
|
|
|
inline ULONG
|
|
CTrkSvrSvc::NumWritesThisHour() const
|
|
{
|
|
return _cWritesPerHour;
|
|
}
|
|
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CLdapBinaryMod
|
|
//
|
|
// Purpose: Wrapper for all the structures used by LDAP for binary
|
|
// attribute modifications
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CLdapBinaryMod
|
|
{
|
|
public:
|
|
|
|
inline CLdapBinaryMod();
|
|
|
|
// note: the pszAttrName is not copied!
|
|
inline CLdapBinaryMod(const TCHAR * pszAttrName,
|
|
PCCH bv_val,
|
|
ULONG bv_len,
|
|
int mod_op);
|
|
|
|
// note: the pszAttrName is not copied!
|
|
void Init(const TCHAR * pszAttrName,
|
|
PCCH bv_val,
|
|
ULONG bv_len,
|
|
int mod_op);
|
|
public:
|
|
|
|
LDAPMod _mod;
|
|
|
|
private:
|
|
LDAP_BERVAL _BerVal;
|
|
LDAP_BERVAL * _apBerVals[2];
|
|
};
|
|
|
|
|
|
inline
|
|
CLdapBinaryMod::CLdapBinaryMod()
|
|
{
|
|
}
|
|
|
|
inline
|
|
CLdapBinaryMod::CLdapBinaryMod(const TCHAR * pszAttrName,
|
|
PCCH bv_val,
|
|
ULONG bv_len,
|
|
int mod_op)
|
|
{
|
|
Init(pszAttrName, bv_val, bv_len, mod_op);
|
|
}
|
|
|
|
inline void
|
|
CLdapBinaryMod::Init(const TCHAR * pszAttrName, PCCH bv_val, ULONG bv_len, int mod_op)
|
|
{
|
|
_mod.mod_op = LDAP_MOD_BVALUES | mod_op;
|
|
_mod.mod_type = (TCHAR*)pszAttrName;
|
|
|
|
// bv_val might be null if this is a delete.
|
|
if( NULL != bv_val )
|
|
{
|
|
_BerVal.bv_len = bv_len;
|
|
_BerVal.bv_val = const_cast<PCHAR>(bv_val);
|
|
_apBerVals[0] = &_BerVal;
|
|
_apBerVals[1] = NULL;
|
|
_mod.mod_bvalues = _apBerVals;
|
|
}
|
|
else
|
|
_mod.mod_bvalues = NULL;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CLdapStringMod
|
|
//
|
|
// Purpose: Wrapper for all the structures used by LDAP for string
|
|
// attribute modifications
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
class CLdapStringMod
|
|
{
|
|
public:
|
|
|
|
inline CLdapStringMod();
|
|
|
|
// note: the pszAttrName is not copied!
|
|
inline CLdapStringMod(const TCHAR * pszAttrName,
|
|
const TCHAR * str_val,
|
|
int mod_op);
|
|
|
|
// note: the pszAttrName is not copied!
|
|
void Init(const TCHAR * pszAttrName,
|
|
const TCHAR * str_val,
|
|
int mod_op);
|
|
public:
|
|
|
|
LDAPMod _mod;
|
|
|
|
private:
|
|
|
|
TCHAR * _apsz[2];
|
|
};
|
|
|
|
inline
|
|
CLdapStringMod::CLdapStringMod()
|
|
{
|
|
}
|
|
|
|
inline
|
|
CLdapStringMod::CLdapStringMod(const TCHAR * pszAttrName,
|
|
const TCHAR * str_val,
|
|
int mod_op)
|
|
{
|
|
// if we're specifying the objectClass we don't
|
|
TrkAssert(_tcscmp(pszAttrName, s_objectClass) != 0 ||
|
|
mod_op == 0);
|
|
Init(pszAttrName, str_val, mod_op);
|
|
}
|
|
|
|
inline void
|
|
CLdapStringMod::Init(const TCHAR * pszAttrName,
|
|
const TCHAR * str_val,
|
|
int mod_op)
|
|
{
|
|
_mod.mod_op = mod_op;
|
|
_mod.mod_type = (TCHAR*)pszAttrName;
|
|
_mod.mod_vals.modv_strvals = _apsz;
|
|
_apsz[0] = (TCHAR*)str_val;
|
|
_apsz[1] = NULL;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CLdapTimeValue
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CLdapTimeValue
|
|
{
|
|
public:
|
|
CLdapTimeValue()
|
|
{
|
|
//memset(_abPad,0,sizeof(_abPad));
|
|
_stprintf( _tszTime, TEXT("%I64u"), (LONGLONG)CFILETIME() );
|
|
}
|
|
|
|
CLdapTimeValue(const CFILETIME &cft)
|
|
{
|
|
_stprintf( _tszTime, TEXT("%I64u"), (LONGLONG) cft );
|
|
}
|
|
|
|
// mikehill: is this still necessary?
|
|
void Swap();
|
|
|
|
inline BYTE & Byte(int i)
|
|
{
|
|
return( ((BYTE*)this)[i] );
|
|
}
|
|
|
|
operator TCHAR*()
|
|
{
|
|
return( _tszTime );
|
|
}
|
|
|
|
TCHAR _tszTime[ CCH_UINT64 ];
|
|
|
|
//BYTE _abPad[sizeof(GUID) - sizeof(CFILETIME)];
|
|
};
|
|
|
|
inline void
|
|
CLdapTimeValue::Swap()
|
|
{
|
|
BYTE b;
|
|
|
|
for (int i=0; i<sizeof(*this)/2; i++)
|
|
{
|
|
b = Byte(i);
|
|
Byte(i) = Byte(sizeof(*this)-i-1);
|
|
Byte(sizeof(*this)-i-1) = b;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CLdapRefreshSeq
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CLdapRefresh
|
|
{
|
|
public:
|
|
|
|
CLdapRefresh( SequenceNumber seq )
|
|
{
|
|
_stprintf( _tszSeq, TEXT("%u"), seq );
|
|
}
|
|
|
|
operator TCHAR*()
|
|
{
|
|
return( _tszSeq );
|
|
}
|
|
|
|
TCHAR _tszSeq[ CCH_UINT64 ];
|
|
};
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CLdapOMTAddModify
|
|
//
|
|
// Purpose: Wrapper for all the structures used to set a linkTrackOMTEntry
|
|
// (an entry in the Object Move Table)
|
|
//
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
class CLdapOMTAddModify
|
|
{
|
|
public:
|
|
CLdapOMTAddModify(
|
|
const CDomainRelativeObjId & droidKey,
|
|
const CDomainRelativeObjId & droidNew,
|
|
const CDomainRelativeObjId & droidBirth,
|
|
const ULONG & seqRefresh,
|
|
BYTE bFlags, // Only used on LDAP_MOD_ADD
|
|
int mod_op
|
|
);
|
|
|
|
CLdapStringMod _lsmClass;
|
|
CLdapBinaryMod _lbmCurrentLocation;
|
|
CLdapBinaryMod _lbmBirthLocation;
|
|
CLdapStringMod _lsmRefresh;
|
|
CLdapBinaryMod _lbmFlags;
|
|
|
|
// We need to keep a CLdapTimeValue as a member in order to guarantee that
|
|
// it will remain in memory (_lsmRefresh will point to this data).
|
|
|
|
CLdapRefresh _ltvRefresh;
|
|
|
|
LDAPMod * _mods[7];
|
|
};
|
|
|
|
class CLdapVolumeKeyDn
|
|
{
|
|
public:
|
|
// specific volume
|
|
CLdapVolumeKeyDn( const TCHAR * ptszBaseDn, const CVolumeId & volume )
|
|
{
|
|
// Compose, e.g., the following DN:
|
|
// "CN=e3d954b2-d0a7-11d0-8cb6-00c04fd90f85,CN=VolumeTable,CN=FileLinks,DC=TRKDOM"
|
|
|
|
TCHAR *psz = _szDn;
|
|
|
|
_tcscpy(_szDn, TEXT("CN="));
|
|
psz += _tcslen(_szDn);
|
|
|
|
volume.Stringize(psz);
|
|
|
|
*psz++ = TEXT(',');
|
|
|
|
_tcscpy(psz, s_VolumeTableRDN); // The vol table, relative to the base
|
|
_tcscat(psz, ptszBaseDn);
|
|
TrkAssert(_tcslen(_szDn) < ELEMENTS(_szDn));
|
|
}
|
|
|
|
// all volumes
|
|
CLdapVolumeKeyDn( const TCHAR * ptszBaseDn )
|
|
{
|
|
_tcscpy(_szDn, s_VolumeTableRDN);
|
|
_tcscat(_szDn, ptszBaseDn);
|
|
TrkAssert(_tcslen(_szDn) < ELEMENTS(_szDn));
|
|
}
|
|
|
|
inline operator TCHAR * () { return _szDn; }
|
|
|
|
private:
|
|
TCHAR _szDn[MAX_PATH];
|
|
};
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Class: CLdapIdtKeyDn
|
|
//
|
|
// Purpose: Generates distinguished name for a CDomainRelativeObjId
|
|
//
|
|
// Notes:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
class CLdapIdtKeyDn
|
|
{
|
|
public:
|
|
inline CLdapIdtKeyDn(const TCHAR * pszBaseDn, const CDomainRelativeObjId &ld);
|
|
inline CLdapIdtKeyDn(const TCHAR * pszBaseDn );
|
|
|
|
inline operator TCHAR * ();
|
|
|
|
private:
|
|
TCHAR _szDn[MAX_PATH];
|
|
};
|
|
|
|
inline CLdapIdtKeyDn::CLdapIdtKeyDn(const TCHAR * pszBaseDn, const CDomainRelativeObjId
|
|
&ld)
|
|
{
|
|
// puts in something like "CN=v23487...65239238o23487...48652398"
|
|
|
|
ld.FillLdapIdtKeyBuffer(_szDn, sizeof(_szDn)/sizeof(_szDn[0]));
|
|
|
|
// make sure room for that string and the table name and the base dn
|
|
if (_tcslen(_szDn) +
|
|
1 +
|
|
_tcslen(s_ObjectMoveTableRDN) +
|
|
_tcslen(pszBaseDn) + 1 >
|
|
sizeof(_szDn)/sizeof(_szDn[0]))
|
|
{
|
|
TrkRaiseException(TRK_E_DN_TOO_LONG);
|
|
}
|
|
|
|
_tcscat(_szDn, TEXT(","));
|
|
_tcscat(_szDn, s_ObjectMoveTableRDN);
|
|
_tcscat(_szDn, pszBaseDn);
|
|
TrkAssert(_tcslen(_szDn) < sizeof(_szDn)/sizeof(_szDn[0]));
|
|
}
|
|
|
|
inline CLdapIdtKeyDn::CLdapIdtKeyDn(const TCHAR * pszBaseDn)
|
|
{
|
|
// make sure room for that string and the table name and the base dn
|
|
if (1 +
|
|
_tcslen(s_ObjectMoveTableRDN) +
|
|
_tcslen(pszBaseDn) + 1 >
|
|
sizeof(_szDn)/sizeof(_szDn[0]))
|
|
{
|
|
TrkRaiseException(TRK_E_DN_TOO_LONG);
|
|
}
|
|
|
|
_tcscpy(_szDn, s_ObjectMoveTableRDN);
|
|
_tcscat(_szDn, pszBaseDn);
|
|
TrkAssert(_tcslen(_szDn) < sizeof(_szDn)/sizeof(_szDn[0]));
|
|
}
|
|
|
|
inline CLdapIdtKeyDn::operator TCHAR * ()
|
|
{
|
|
return(_szDn);
|
|
}
|
|
|
|
|