windows-nt/Source/XPSP1/NT/com/svcdlls/trksvcs/trksvr/idt_ldap.cxx
2020-09-26 16:20:57 +08:00

704 lines
21 KiB
C++

// Copyright (c) 1996-1999 Microsoft Corporation
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: idt_ldap.cxx
//
// Contents: Intra-domain table based on LDAP.
//
// Classes:
//
// Functions:
//
//
//
// History: 18-Nov-96 BillMo Created.
//
// Notes:
//
// Codework:
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include "trksvr.hxx"
CLdapOMTAddModify::CLdapOMTAddModify(
const CDomainRelativeObjId & ldKey,
const CDomainRelativeObjId & ldNew,
const CDomainRelativeObjId & ldBirth,
const ULONG & seqRefresh,
BYTE bFlags,
int mod_op )
:
_lsmClass(s_objectClass, s_linkTrackOMTEntry, 0),
_lbmCurrentLocation( s_currentLocation, reinterpret_cast<PCCH>(&ldNew), sizeof(ldNew), mod_op ),
_lbmBirthLocation( s_birthLocation, reinterpret_cast<PCCH>(&ldBirth), sizeof(ldBirth), mod_op ),
_ltvRefresh( seqRefresh ),
_lsmRefresh( s_timeRefresh, _ltvRefresh, mod_op ),
_lbmFlags( s_oMTIndxGuid, reinterpret_cast<PCCH>(&bFlags), sizeof(BYTE), mod_op )
{
int i = 0;
if (mod_op == LDAP_MOD_ADD)
{
_mods[i++] = &_lsmClass._mod;
_mods[i++] = &_lbmFlags._mod;
}
_mods[i++] = &_lbmCurrentLocation._mod;
if (ldKey != ldBirth)
_mods[i++] = &_lbmBirthLocation._mod;
_mods[i++] = &_lsmRefresh._mod;
_mods[i++] = NULL;
TrkAssert(i <= sizeof(_mods)/sizeof(_mods[0]));
}
void
CIntraDomainTable::Initialize( CTrkSvrConfiguration *pTrkSvrConfiguration, CQuotaTable* pqtable )
{
_fInitializeCalled = TRUE;
_pqtable = pqtable;
_pTrkSvrConfiguration = pTrkSvrConfiguration;
_QuotaReported.Initialize();
}
void
CIntraDomainTable::UnInitialize()
{
if (_fInitializeCalled)
{
}
_fInitializeCalled = FALSE;
}
//+----------------------------------------------------------------------------
//
// CIntraDomainTable::Add
//
// Add an entry to the move table. Return TRUE if the entry was added, but
// didn't already exist. Return FALSE if the entry already exists. If
// there's an error, raise an exception.
//
//+----------------------------------------------------------------------------
BOOL
CIntraDomainTable::Add(const CDomainRelativeObjId &ldKey,
const CDomainRelativeObjId &ldNew,
const CDomainRelativeObjId &ldBirth,
BOOL *pfQuotaExceeded OPTIONAL )
{
int err;
BYTE bFlags = QFLAG_UNCOUNTED;
CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey);
// The following constructor sets up the mods array for
// the ldap_add_s call that we're about to do.
CLdapOMTAddModify lam( ldKey, // Key
ldNew, // New droid
ldBirth, // Birth droid
// Current sequence number
_pRefreshSequenceStorage->GetSequenceNumber(),
bFlags, // Flags, will be pointed to
// (so can't e.g. use a #define value)
LDAP_MOD_ADD); // Add this element
// Return FALSE here means that we pretend to the caller that the entry
// already exists. We don't want to raise an exception here. Our caller
// always tries to add the entry, if the add fails because the entry
// already exists, the caller will then try to modify the entry. If the
// quota has been exceeded, we don't want to add more entries, but we
// still want entries already in the table to be modifiable. So instead of
// raising an exception, we really want the caller to try to modify the
// entry instead.
if(_pqtable->IsMoveQuotaExceeded())
{
if( NULL != pfQuotaExceeded )
*pfQuotaExceeded = TRUE;
if( !_QuotaReported.IsSet() )
{
_QuotaReported.Set();
TrkReportEvent( EVENT_TRK_SERVICE_MOVE_QUOTA_EXCEEDED, EVENTLOG_WARNING_TYPE,
TRKREPORT_LAST_PARAM );
}
return FALSE;
}
else
{
_QuotaReported.Clear();
}
err = ldap_add_s(Ldap(), dnKey, lam._mods);
if (err == LDAP_SUCCESS)
{
{
LDAPMod* mods[2];
BYTE bFlags = QFLAG_UNCOUNTED;
CLdapBinaryMod lbm(s_oMTIndxGuid, reinterpret_cast<PCCH>(&bFlags), sizeof(BYTE), LDAP_MOD_REPLACE);
mods[0] = &lbm._mod;
mods[1] = NULL;
err = ldap_modify_s(Ldap(), dnKey, mods);
}
_pqtable->IncrementMoveCountCache();
return(TRUE);
}
else
if (err == LDAP_ALREADY_EXISTS)
{
return(FALSE);
}
else
{
TrkRaiseWin32Error(LdapMapErrorToWin32(err));
return(FALSE);
}
}
// TRUE if found and deleted, FALSE if not found, exception on other errors
BOOL
CIntraDomainTable::Delete(const CDomainRelativeObjId &ldKey)
{
int err;
CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey);
BOOL fFound;
fFound = _pqtable->UpdateFlags(Ldap(), dnKey, QFLAG_DELETED);
if( fFound )
_pqtable->DecrementMoveCountCache();
return fFound;
}
// TRUE if entry exists and modified, FALSE if not existent, exception otherwise
BOOL
CIntraDomainTable::Modify(const CDomainRelativeObjId &ldKey,
const CDomainRelativeObjId &ldNew,
const CDomainRelativeObjId &ldBirth )
{
int err;
CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey);
CLdapOMTAddModify lam( ldKey,
ldNew,
ldBirth,
_pRefreshSequenceStorage->GetSequenceNumber(),
0, // Only used with LDAP_MOD_ADD
LDAP_MOD_REPLACE);
err = ldap_modify_s(Ldap(), dnKey, lam._mods);
if (err == LDAP_SUCCESS)
{
return(TRUE);
}
else
if (err == LDAP_NO_SUCH_OBJECT)
{
return(FALSE);
}
else
{
TrkRaiseWin32Error(LdapMapErrorToWin32(err));
return(FALSE);
}
}
// must leave outputs unchanged if returning FALSE
BOOL
CIntraDomainTable::Query(const CDomainRelativeObjId &ldKey,
CDomainRelativeObjId *pldNew,
CDomainRelativeObjId *pldBirth,
BOOL *pfDeleted OPTIONAL,
BOOL *pfCounted OPTIONAL )
{
int err;
TCHAR *aptszAttrs[] = { const_cast<TCHAR*>(s_currentLocation),
const_cast<TCHAR*>(s_birthLocation),
const_cast<TCHAR*>(s_oMTIndxGuid),
NULL };
LDAPMessage * pRes = NULL;
CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey);
BOOL fFound = FALSE;
__try
{
err = ldap_search_s( Ldap(),
dnKey,
LDAP_SCOPE_BASE,
TEXT("(objectclass=*)"),
aptszAttrs,
0, // attribute types and values are wanted
&pRes );
if (err == LDAP_SUCCESS)
{
// found it, lets get the attributes out
if (ldap_count_entries(Ldap(), pRes) == 1)
{
LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
if (pEntry == NULL)
{
TrkRaiseWin32Error(LdapMapErrorToWin32(Ldap()->ld_errno));
}
fFound = Query( Ldap(), pEntry, ldKey, pldNew, pldBirth, pfDeleted, pfCounted );
}
}
else
if (err != LDAP_NO_SUCH_OBJECT)
{
TrkRaiseWin32Error(LdapMapErrorToWin32(err));
}
}
__finally
{
if (NULL != pRes)
ldap_msgfree(pRes);
}
return(fFound);
}
BOOL
CIntraDomainTable::Query( LDAP* pLdap,
LDAPMessage *pEntry,
const CDomainRelativeObjId ldKey,
CDomainRelativeObjId *pldNew,
CDomainRelativeObjId *pldBirth,
BOOL *pfDeleted OPTIONAL,
BOOL *pfCounted OPTIONAL )
{
BOOL fFound = FALSE;
struct berval **ppbvCurrentLocation = NULL;
struct berval **ppbvBirthLocation = NULL;
struct berval **ppbvQuotaFlags = NULL;
BYTE bQuotaFlags = 0;
if( NULL != pfDeleted )
*pfDeleted = FALSE;
__try
{
ppbvBirthLocation = ldap_get_values_len(pLdap, pEntry,
const_cast<TCHAR*>(s_birthLocation) );
if (NULL != ppbvBirthLocation
&&
sizeof(*pldBirth) > (*ppbvBirthLocation)->bv_len)
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't get current location for %s"),
(const TCHAR*)CDebugString(ldKey) ));
TrkRaiseWin32Error( LdapMapErrorToWin32(pLdap->ld_errno) );
}
ppbvCurrentLocation = ldap_get_values_len(pLdap, pEntry,
const_cast<TCHAR*>(s_currentLocation) );
if (NULL == ppbvCurrentLocation
||
sizeof(*pldNew) > (*ppbvCurrentLocation)->bv_len)
{
TrkLog((TRKDBG_ERROR, TEXT("Couldn't get current location for %s"),
(const TCHAR*)CDebugString(ldKey) ));
TrkRaiseWin32Error( LdapMapErrorToWin32(pLdap->ld_errno) );
}
ppbvQuotaFlags = ldap_get_values_len(pLdap, pEntry,
const_cast<TCHAR*>(s_oMTIndxGuid) );
if (NULL != ppbvQuotaFlags
&&
sizeof(BYTE) <= (*ppbvQuotaFlags)->bv_len)
{
bQuotaFlags = *(BYTE*)(*ppbvQuotaFlags)->bv_val;
}
if( NULL != pfCounted )
{
if( bQuotaFlags & QFLAG_UNCOUNTED )
*pfCounted = FALSE;
else
*pfCounted = TRUE;
}
if( bQuotaFlags & QFLAG_DELETED )
{
if( NULL != pfDeleted )
*pfDeleted = TRUE;
TrkLog(( TRKDBG_IDT, TEXT("IdtQuery: Entry marked deleted will be ignored (0x%x): %s"),
bQuotaFlags,
(const TCHAR*)CDebugString(ldKey) ));
}
else
{
*pldNew = *reinterpret_cast<CDomainRelativeObjId*>( (*ppbvCurrentLocation)->bv_val );
if (NULL != ppbvBirthLocation)
*pldBirth = *reinterpret_cast<CDomainRelativeObjId*>( (*ppbvBirthLocation)->bv_val );
else
*pldBirth = ldKey;
fFound = TRUE;
}
}
__finally
{
if (NULL != ppbvCurrentLocation)
ldap_value_free_len(ppbvCurrentLocation);
if (NULL != ppbvBirthLocation)
ldap_value_free_len(ppbvBirthLocation);
if (NULL != ppbvQuotaFlags)
ldap_value_free_len(ppbvQuotaFlags);
}
return( fFound );
}
//+----------------------------------------------------------------------------
//
// CIntraDomainTable::Touch
//
// Update the refresh attribute for an entry in the move table.
// Return TRUE if the entry exists and was touched, FALSE if it
// doesn't exist, and raise an exception if there's an error.
//
//+----------------------------------------------------------------------------
// BUGBUG: if we ever move to per-user quotas,
// check ownership of entry being touched.
BOOL
CIntraDomainTable::Touch(
const CDomainRelativeObjId &ldKey
)
{
BOOL fReturn = FALSE;
int err;
CLdapTimeValue ltvRefresh( _pRefreshSequenceStorage->GetSequenceNumber());
CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_REPLACE );
CLdapIdtKeyDn dnKey(GetBaseDn(), ldKey);
TCHAR ** pptszRefresh = NULL;
LDAPMessage * pEntry = NULL;
LDAPMessage* pRes = NULL;
LDAPMod * mods[2];
CObjId objZero;
__try
{
//
// if the birth id is zero, then it is invalid and not worth doing a refresh
//
if (ldKey.GetObjId() == objZero)
{
__leave;
}
//
// Check to see if the object already has a recent sequence number.
//
TCHAR* rgptszAttrs[2];
rgptszAttrs[0] = const_cast<TCHAR*>(s_timeRefresh);
rgptszAttrs[1] = NULL;
err = ldap_search_s(Ldap(),
dnKey,
LDAP_SCOPE_BASE,
TEXT("(ObjectClass=*)"),
rgptszAttrs,
0,
&pRes);
if (err == LDAP_SUCCESS)
{
// The search call worked, but did we find an object?
if( 1 == ldap_count_entries(Ldap(), pRes) )
{
// The object already exists
pEntry = ldap_first_entry(Ldap(), pRes);
if( NULL != pEntry )
{
// Get the refresh counter
pptszRefresh = ldap_get_values( Ldap(), pEntry, const_cast<TCHAR*>(s_timeRefresh) );
if( NULL != pptszRefresh )
{
SequenceNumber seqRefresh = 0;
if( 1 == _stscanf( *pptszRefresh, TEXT("%d"), &seqRefresh ))
{
// Is the refresh counter already set to a recent value?
// We'll consider it recent enough if it's within half of the
// refresh cycle (15 days)
// First, how long is the GC timer in seconds?
LONG lGCTimerInSeconds = _pTrkSvrConfiguration->GetGCPeriod() // 30 days in seconds
/ _pTrkSvrConfiguration->GetGCDivisor(); // => 1 day in seconds
// Next, how many ticks is half the period?
LONG lWindow = _pTrkSvrConfiguration->GetGCPeriod() // 30 days (in seconds)
/ 2 // => 15 days (in seconds)
/ lGCTimerInSeconds; // => 15
TrkLog(( TRKDBG_WARNING, TEXT("Window = %d"), lWindow ));
if( seqRefresh + lWindow
>= _pRefreshSequenceStorage->GetSequenceNumber()
)
{
TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
TEXT("Not touching %s with %d, seq %d already set"),
(const TCHAR*)CDebugString(ldKey),
_pRefreshSequenceStorage->GetSequenceNumber(),
seqRefresh ));
__leave;
}
}
}
}
}
}
else if (err == LDAP_NO_SUCH_OBJECT)
{
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
TEXT("Touch: object %s not found"),
(const TCHAR*) CDebugString(ldKey) ));
__leave;
}
//
// Set the correct sequence number
//
mods[0] = &lsmRefresh._mod;
mods[1] = NULL;
err = ldap_modify_s(Ldap(), dnKey, mods);
if (err == LDAP_SUCCESS)
{
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
TEXT("Touch: object %s touched"),
(const TCHAR*) CDebugString(ldKey) ));
fReturn = TRUE;
__leave;
}
else
if (err == LDAP_NO_SUCH_OBJECT)
{
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
TEXT("Touch: object %s not found"),
(const TCHAR*) CDebugString(ldKey) ));
__leave;
}
else
if (err == LDAP_NO_SUCH_ATTRIBUTE)
{
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
TEXT("Touch: object %s attribute not found"),
(const TCHAR*) CDebugString(ldKey) ));
// deal with old server data
CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_ADD );
mods[0] = &lsmRefresh._mod;
err = ldap_modify_s(Ldap(), dnKey, mods);
}
if (err != LDAP_SUCCESS)
{
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
TEXT("Touch: object %s --> exceptional error"),
(const TCHAR*) CDebugString(ldKey) ));
__leave;
}
}
__except( BreakOnDebuggableException() )
{
TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception during IDT::Touch (%08x)"), GetExceptionCode() ));
}
if (pptszRefresh != NULL)
ldap_value_free(pptszRefresh);
if(pRes != NULL)
ldap_msgfree(pRes);
return( fReturn );
}
ULONG
CIntraDomainTable::GarbageCollect( SequenceNumber seqCurrent, SequenceNumber seqOldestToKeep, const BOOL * pfAbort )
{
CLdapIdtKeyDn dn(GetBaseDn());
TCHAR * apszAttrs[3];
GC_ENUM_CONTEXT EnumContext;
apszAttrs[0] = const_cast<TCHAR*>(s_Cn);
apszAttrs[1] = const_cast<TCHAR*>(s_timeRefresh);
apszAttrs[2] = 0;
TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT, TEXT("GC-ing move table (%d/%d)"),
seqCurrent, seqOldestToKeep ));
memset( &EnumContext, 0, sizeof(EnumContext) );
EnumContext.seqOldestToKeep = seqOldestToKeep;
EnumContext.seqCurrent = seqCurrent;
EnumContext.pfAbort = pfAbort;
EnumContext.dwRepetitiveTaskDelay = _pTrkSvrConfiguration->GetRepetitiveTaskDelay();
EnumContext.pqtable = _pqtable;
if (!LdapEnumerate(
Ldap(),
dn,
LDAP_SCOPE_ONELEVEL,
TEXT("(objectClass=*)"),
apszAttrs,
GcEnumerateCallback,
&EnumContext ))
{
TrkRaiseException(TRK_E_SERVICE_STOPPING);
}
TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
TEXT("GC-ed %d entries from the move table"),
EnumContext.cEntries ));
_pqtable->OnMoveTableGcComplete( EnumContext.cEntries );
return EnumContext.cEntries;
}
#if DBG
void
CIntraDomainTable::PurgeAll()
{
int err;
TCHAR *apszAttrs[2] = { TEXT("cn"), NULL };
LDAPMessage * pRes;
TCHAR tszObjectMoveTable[MAX_PATH+1];
__try
{
_tcscpy(tszObjectMoveTable, s_ObjectMoveTableRDN);
_tcscat(tszObjectMoveTable, GetBaseDn());
err = ldap_search_s( Ldap(),
tszObjectMoveTable,
LDAP_SCOPE_ONELEVEL,
TEXT("(objectclass=*)"),
apszAttrs,
0, // attribute types and values are wanted
&pRes );
if (err == LDAP_SUCCESS)
{
// found it, lets get the attributes out
int cEntries = ldap_count_entries(Ldap(), pRes);
LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
if (pEntry != NULL)
{
do
{
TCHAR * ptszDn = ldap_get_dn(Ldap(), pEntry);
int errd = ldap_delete_s(Ldap(),ptszDn);
TrkLog((TRKDBG_ERROR, TEXT("Purged %s status=%d"), ptszDn, errd));
ldap_memfree(ptszDn);
} while ( pEntry = ldap_next_entry(Ldap(), pEntry));
}
}
}
__finally
{
if (err == LDAP_SUCCESS)
{
ldap_msgfree(pRes);
}
}
}
#endif
void
CDomainRelativeObjId::FillLdapIdtKeyBuffer(TCHAR * const pchCN,
DWORD cch) const
{
TCHAR *pchBuf = pchCN;
_tcscpy(pchBuf, TEXT("CN="));
pchBuf = pchBuf + 3;
_volume.Stringize(pchBuf);
_object.Stringize(pchBuf);
TrkAssert(pchBuf <= pchCN+cch);
}
void
CDomainRelativeObjId::ReadLdapIdtKeyBuffer(const TCHAR * pchCN )
{
const TCHAR *pchBuf;
Init();
if( 0 == _tcsncmp( pchCN, TEXT("CN="), 3 ))
{
pchBuf = &pchCN[3];
if( !_volume.Unstringize(pchBuf)
||
!_object.Unstringize(pchBuf) )
{
TrkLog(( TRKDBG_ERROR, TEXT("Couldn't unstringize droid from %s"), pchCN ));
Init();
}
}
}
void
CDomainRelativeObjId::InitFromLdapBuffer(char * pVolumeId, int cbVolumeId,
char * pObjId, int cbObjId)
{
DWORD iBuf = 0;
if (cbVolumeId != sizeof(_volume) ||
cbObjId != sizeof(_object))
{
TrkRaiseException(TRK_E_CORRUPT_IDT);
}
memcpy(&_volume, pVolumeId, sizeof(_volume));
memcpy(&_object, pObjId, sizeof(_object));
}