1551 lines
45 KiB
C++
1551 lines
45 KiB
C++
|
|
// Copyright (c) 1996-1999 Microsoft Corporation
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// File: voltab.cxx
|
|
//
|
|
// Contents: volumes table
|
|
//
|
|
// Classes:
|
|
//
|
|
// Functions:
|
|
//
|
|
//
|
|
//
|
|
// History: 16-Dec-96 MikeHill Created.
|
|
// 23-Jan-97 BillMo Added support for synchronizing clients
|
|
// after a DC restore.
|
|
//
|
|
// Notes: There are two sequence numbers that pertain to each volume.
|
|
//
|
|
// The first sequence number is used to synchronize the machine
|
|
// move logs with the object move table. This sequence number
|
|
// is relevant to QueryVolume, ClaimVolume, GetVolumeInfo.
|
|
//
|
|
// The second sequence number is used to synchronize the
|
|
// automatic backup of the volume to machine table.
|
|
//
|
|
// Codework:
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include "pch.cxx"
|
|
#pragma hdrstop
|
|
|
|
#include "trksvr.hxx"
|
|
#include <commctrl.h>
|
|
#include <time.h>
|
|
|
|
#define MAX_MACHINE_BUF_CHARS 256
|
|
|
|
TCHAR s_RestoreVolumes[] = TEXT("Software\\Microsoft\\LinkTrack\\RestoreVolumes");
|
|
|
|
class CLdapTimeVolChange
|
|
{
|
|
public:
|
|
CLdapTimeVolChange()
|
|
{
|
|
memset(_abPad,0,sizeof(_abPad));
|
|
}
|
|
|
|
CLdapTimeVolChange(const CFILETIME &cft)
|
|
{
|
|
_cft = cft;
|
|
memset(_abPad,0,sizeof(_abPad));
|
|
}
|
|
|
|
void Swap();
|
|
|
|
inline BYTE & Byte(int i)
|
|
{
|
|
return( ((BYTE*)this)[i] );
|
|
}
|
|
|
|
CFILETIME _cft;
|
|
BYTE _abPad[sizeof(GUID) - sizeof(CFILETIME)];
|
|
};
|
|
|
|
void
|
|
CLdapTimeVolChange::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 CLdapSecret
|
|
{
|
|
public:
|
|
CLdapSecret()
|
|
{
|
|
memset(_abPad,0,sizeof(_abPad));
|
|
}
|
|
|
|
CLdapSecret(const CVolumeSecret &secret)
|
|
{
|
|
_secret = secret;
|
|
memset(_abPad,0,sizeof(_abPad));
|
|
}
|
|
|
|
CVolumeSecret _secret;
|
|
BYTE _abPad[sizeof(GUID) - sizeof(CVolumeSecret)];
|
|
};
|
|
|
|
|
|
void
|
|
CVolumeTable::Initialize(CTrkSvrConfiguration *pconfigSvr, CQuotaTable* pqtable)
|
|
{
|
|
_fInitializeCalled = TRUE;
|
|
|
|
_pqtable = pqtable;
|
|
_pconfigSvr = pconfigSvr;
|
|
|
|
#ifdef VOL_REPL
|
|
// Can raise an NTSTATUS so put before fInitializeCalled=TRUE
|
|
InitializeCriticalSection(&_csQueryCache);
|
|
|
|
if (pwm != NULL)
|
|
{
|
|
|
|
//
|
|
// Initialize the cache immediately ready for client queries
|
|
// Service start time, query may take a while. Dependency on ldap being available.
|
|
// => Make this lazy.
|
|
//
|
|
|
|
_SecondsPreviousToNow = _pconfigSvr->GetVolumeQueryPeriod()
|
|
* _pconfigSvr->GetVolumeQueryPeriods();
|
|
|
|
_cftCacheLowest.SetToUTC();
|
|
_cftCacheLowest.DecrementSeconds( _SecondsPreviousToNow );
|
|
|
|
// search for all changes since now-period*numperiods (may throw on out of memory)
|
|
_QueryVolumeChanges( _cftCacheLowest, &_VolMap );
|
|
|
|
// timer should go off in about 6 hrs
|
|
CFILETIME cft;
|
|
cft.IncrementSeconds(VolumeQueryPeriodSeconds);
|
|
_timerQueryCache.Initialize(this, pwm, 0, VolumeQueryPeriodSeconds, &cft);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void
|
|
CVolumeTable::UnInitialize()
|
|
{
|
|
if (_fInitializeCalled)
|
|
{
|
|
_fInitializeCalled = FALSE;
|
|
|
|
#ifdef VOL_REPL
|
|
_timerQueryCache.UnInitialize();
|
|
|
|
DeleteCriticalSection(&_csQueryCache);
|
|
|
|
_VolMap.UnInitialize();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#ifdef VOL_REPL
|
|
void
|
|
CVolumeTable::Timer( DWORD dwTimerId )
|
|
{
|
|
// redo the query - will leave _VolMap unchanged on error
|
|
// On low memory exception we should retry the timer.
|
|
|
|
Raise if stopped
|
|
|
|
CFILETIME cftHighest;
|
|
CFILETIME cft;
|
|
cft.DecrementSeconds( _SecondsPreviousToNow );
|
|
|
|
CVolumeMap VolMap;
|
|
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
|
|
|
|
TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE, TEXT("Volume table cache timer")));
|
|
|
|
// search for all changes since now-period*numperiods (may throw on out of memory)
|
|
_QueryVolumeChanges( cft, &VolMap );
|
|
|
|
EnterCriticalSection(&_csQueryCache);
|
|
|
|
_cftCacheLowest = cft;
|
|
VolMap.MoveTo(&_VolMap);
|
|
|
|
LeaveCriticalSection(&_csQueryCache);
|
|
}
|
|
|
|
#if DBG
|
|
void
|
|
CVolumeTable::PurgeCache()
|
|
{
|
|
EnterCriticalSection(&_csQueryCache);
|
|
|
|
_cftCacheLowest = CFILETIME(0);
|
|
_VolMap.UnInitialize();
|
|
|
|
LeaveCriticalSection(&_csQueryCache);
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
HRESULT
|
|
CVolumeTable::MapResult(int err) const
|
|
{
|
|
if (err == LDAP_SUCCESS)
|
|
{
|
|
return(S_OK);
|
|
}
|
|
else
|
|
if (err == LDAP_NO_SUCH_OBJECT)
|
|
{
|
|
return(TRK_S_VOLUME_NOT_FOUND);
|
|
}
|
|
else
|
|
{
|
|
TrkRaiseWin32Error(LdapMapErrorToWin32(err));
|
|
return(TRK_S_VOLUME_NOT_FOUND);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CVolumeTable::AddVolidToTable( const CVolumeId & volume,
|
|
const CMachineId & mcidClient,
|
|
const CVolumeSecret & secret )
|
|
{
|
|
CVolumeId volidZero;
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
|
|
CLdapStringMod lsmClass(s_objectClass, s_linkTrackVolEntry, LDAP_MOD_ADD);
|
|
CLdapSecret ls(secret);
|
|
CLdapBinaryMod lbmVolumeSecret(s_volumeSecret, (PCHAR)&ls, sizeof(ls), LDAP_MOD_ADD);
|
|
CLdapBinaryMod lbmMachineId(s_currMachineId, (PCHAR)&mcidClient, sizeof(mcidClient), LDAP_MOD_ADD);
|
|
CLdapSeqNum lsn;
|
|
CLdapStringMod lsmSequence(s_seqNotification, lsn, LDAP_MOD_ADD );
|
|
CLdapTimeValue ltv; // current time
|
|
CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_ADD);
|
|
|
|
// When writing the zero volume (the refresh sequence number itself), write a 0 for the
|
|
// sequence number. (Calling _pRefreshSequenceStorage would cause an infinite loop).
|
|
CLdapRefresh ltvRefresh( volidZero == volume ? 0 : _pRefreshSequenceStorage->GetSequenceNumber() );
|
|
CLdapStringMod lsmRefresh(s_timeRefresh, ltvRefresh, LDAP_MOD_ADD);
|
|
|
|
LDAPMod * mods[7];
|
|
|
|
mods[0] = &lsmClass._mod;
|
|
mods[1] = &lbmVolumeSecret._mod;
|
|
mods[2] = &lbmMachineId._mod;
|
|
mods[3] = &lsmSequence._mod;
|
|
mods[4] = &lsmTimeVolChange._mod;
|
|
|
|
// Don't write a refresh time for the null volume (this entry is used
|
|
// to store the current global refresh counter).
|
|
|
|
if( CVolumeId() != volume )
|
|
{
|
|
mods[5] = &lsmRefresh._mod;
|
|
mods[6] = 0;
|
|
}
|
|
else
|
|
mods[5] = 0;
|
|
|
|
int err = ldap_add_s( Ldap(), dnKey, mods );
|
|
|
|
if( LDAP_ALREADY_EXISTS == err )
|
|
{
|
|
ldap_delete_s( Ldap(), dnKey );
|
|
err = ldap_add_s( Ldap(), dnKey, mods );
|
|
}
|
|
|
|
if( LDAP_SUCCESS == err )
|
|
_pqtable->IncrementVolumeCountCache();
|
|
else
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed AddVolidToDs (%d)"), err ));
|
|
|
|
return MapResult(err);
|
|
}
|
|
|
|
HRESULT
|
|
CVolumeTable::PreCreateVolume( const CMachineId & mcidClient,
|
|
const CVolumeSecret & secret,
|
|
ULONG cUncountedVolumes,
|
|
CVolumeId * pvolume )
|
|
{
|
|
int err;
|
|
RPC_STATUS Status;
|
|
CMachineId mcidZero(MCID_INVALID);
|
|
ULONG cVolumes = 0;
|
|
|
|
if(mcidClient != mcidZero && _pqtable->IsVolumeQuotaExceeded(mcidClient, cUncountedVolumes))
|
|
{
|
|
TrkLog((TRKDBG_ERROR, TEXT("Volume quota exceeded for %s"),
|
|
(const TCHAR*) CDebugString(mcidClient) ));
|
|
return TRK_E_VOLUME_QUOTA_EXCEEDED;
|
|
}
|
|
|
|
Status = pvolume->UuidCreate();
|
|
if (Status != RPC_S_OK )
|
|
{
|
|
// Since we use the randomized-guid generation algorithm,
|
|
// we should never get a local guid.
|
|
TrkAssert( RPC_S_UUID_LOCAL_ONLY != Status );
|
|
|
|
TrkRaiseWin32Error(Status);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
CVolumeTable::QueryVolume( const CMachineId & mcidClient,
|
|
const CVolumeId & volume,
|
|
SequenceNumber * pseq,
|
|
FILETIME * pftLastRefresh )
|
|
{
|
|
HRESULT hr;
|
|
CMachineId mcidTable;
|
|
SequenceNumber seq;
|
|
CVolumeSecret secret;
|
|
CFILETIME cftRefresh(0);
|
|
|
|
if (volume == CVolumeId())
|
|
{
|
|
TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
|
|
}
|
|
|
|
hr = GetVolumeInfo(volume, &mcidTable, &secret, &seq, &cftRefresh );
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// if (its not the right machine), or (it is the right machine and a nul secret)
|
|
if (mcidTable != mcidClient || secret == CVolumeSecret())
|
|
{
|
|
return(TRK_S_VOLUME_NOT_OWNED);
|
|
}
|
|
*pseq = seq;
|
|
*pftLastRefresh = cftRefresh;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CVolumeTable::FindVolume( const CVolumeId & volume, CMachineId * pmcid )
|
|
{
|
|
HRESULT hr;
|
|
CMachineId mcidTable;
|
|
SequenceNumber seq;
|
|
CVolumeSecret secret;
|
|
CFILETIME cftRefresh(0);
|
|
|
|
if (volume == CVolumeId())
|
|
{
|
|
TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
|
|
}
|
|
|
|
hr = GetVolumeInfo(volume, &mcidTable, &secret, &seq, &cftRefresh );
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
*pmcid = mcidTable;
|
|
}
|
|
|
|
return(hr);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DBG
|
|
void
|
|
CVolumeTable::PurgeAll()
|
|
{
|
|
int err;
|
|
TCHAR *apszAttrs[2] = { TEXT("cn"), NULL };
|
|
LDAPMessage * pRes;
|
|
TCHAR tszVolumeTable[MAX_PATH+1];
|
|
|
|
__try
|
|
{
|
|
_tcscpy(tszVolumeTable, s_VolumeTableRDN);
|
|
_tcscat(tszVolumeTable, GetBaseDn());
|
|
|
|
err = ldap_search_s( Ldap(),
|
|
tszVolumeTable,
|
|
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
|
|
|
|
HRESULT
|
|
CVolumeTable::ClaimVolume( const CMachineId & mcidClient,
|
|
const CVolumeId & volume,
|
|
const CVolumeSecret & secretOld,
|
|
const CVolumeSecret & secretNew,
|
|
SequenceNumber * pseq,
|
|
FILETIME * pftLastRefresh )
|
|
{
|
|
HRESULT hr;
|
|
CMachineId mcidTable;
|
|
SequenceNumber seq;
|
|
CVolumeSecret secretCurrent;
|
|
CVolumeSecret nullSecret;
|
|
CFILETIME cftRefresh(0);
|
|
|
|
if (volume == CVolumeId())
|
|
{
|
|
TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
|
|
}
|
|
|
|
hr = GetVolumeInfo( volume, &mcidTable, &secretCurrent, &seq, &cftRefresh );
|
|
if ( S_OK == hr )
|
|
{
|
|
hr = TRK_E_VOLUME_ACCESS_DENIED;
|
|
|
|
if( mcidTable == mcidClient )
|
|
hr = SetSecret( volume, secretNew );
|
|
else if( secretOld == secretCurrent)
|
|
hr = SetMachineAndSecret( volume, mcidClient, secretNew );
|
|
}
|
|
|
|
if ( S_OK == hr )
|
|
{
|
|
*pseq = seq;
|
|
}
|
|
|
|
TrkAssert( hr == TRK_E_VOLUME_ACCESS_DENIED ||
|
|
hr == S_OK ||
|
|
hr == TRK_S_VOLUME_NOT_FOUND );
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CVolumeTable::DeleteVolume
|
|
//
|
|
// Delete an entry from the volume table, but only if the volume is owned
|
|
// by the calling machine.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CVolumeTable::DeleteVolume( const CMachineId & mcidClient,
|
|
const CVolumeId & volume )
|
|
{
|
|
HRESULT hr;
|
|
int LdapError;
|
|
CMachineId mcidTable;
|
|
SequenceNumber seq;
|
|
CVolumeSecret secret;
|
|
CLdapVolumeKeyDn dnVolume(GetBaseDn(), volume);
|
|
CFILETIME cftRefresh(0);
|
|
|
|
if (volume == CVolumeId())
|
|
{
|
|
TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
|
|
}
|
|
|
|
hr = GetVolumeInfo( volume, &mcidTable, &secret, &seq, &cftRefresh );
|
|
if ( S_OK == hr )
|
|
{
|
|
if( mcidTable == mcidClient )
|
|
{
|
|
TrkLog(( TRKDBG_VOLTAB, TEXT("Deleting volume %s"),
|
|
(const TCHAR*) CDebugString(volume) ));
|
|
|
|
LdapError = ldap_delete_s(Ldap(), dnVolume);
|
|
if( LDAP_SUCCESS == LdapError )
|
|
{
|
|
hr = S_OK;
|
|
_pqtable->DecrementVolumeCountCache();
|
|
}
|
|
else
|
|
hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(LdapError) );
|
|
}
|
|
else
|
|
hr = TRK_E_VOLUME_ACCESS_DENIED;
|
|
}
|
|
#if DBG
|
|
if( FAILED(hr) )
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Failed attempt to delete volume %s"),
|
|
(const TCHAR*) CDebugString(volume) ));
|
|
#endif
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CVolumeTable::GetMachine(const CVolumeId & volume, CMachineId * pmcid)
|
|
{
|
|
CVolumeSecret secret;
|
|
SequenceNumber seq;
|
|
CFILETIME cftRefresh(0);
|
|
|
|
return GetVolumeInfo( volume, pmcid, &secret, &seq, &cftRefresh );
|
|
}
|
|
|
|
HRESULT
|
|
CVolumeTable::SetMachine(const CVolumeId & volume, const CMachineId & mcid)
|
|
{
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
|
|
CLdapTimeValue ltv; // Defaults to current UTC
|
|
//ltvc.Swap();
|
|
CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_REPLACE);
|
|
CLdapBinaryMod lbmMachineId(s_currMachineId, (PCHAR)&mcid, sizeof(mcid), LDAP_MOD_REPLACE);
|
|
LDAPMod * mods[3];
|
|
HRESULT hr;
|
|
int err;
|
|
|
|
mods[0] = &lbmMachineId._mod;
|
|
mods[1] = &lsmTimeVolChange._mod;
|
|
mods[2] = NULL;
|
|
|
|
err = ldap_modify_s(Ldap(), dnKey, mods);
|
|
|
|
hr = MapResult(err);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CVolumeTable::SetSecret(const CVolumeId & volume, const CVolumeSecret & secret)
|
|
{
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
|
|
CLdapTimeValue ltv; // Defaults to current UTC
|
|
CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_REPLACE);
|
|
CLdapSecret ls(secret);
|
|
CLdapBinaryMod lbmVolumeSecret(s_volumeSecret, (PCHAR)&ls, sizeof(ls), LDAP_MOD_REPLACE);
|
|
LDAPMod * mods[3];
|
|
HRESULT hr;
|
|
int err;
|
|
|
|
mods[0] = &lbmVolumeSecret._mod;
|
|
mods[1] = &lsmTimeVolChange._mod;
|
|
mods[2] = NULL;
|
|
|
|
err = ldap_modify_s(Ldap(), dnKey, mods);
|
|
|
|
hr = MapResult(err);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CVolumeTable::SetMachineAndSecret(const CVolumeId & volume, const CMachineId & mcid, const CVolumeSecret & secret)
|
|
{
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
|
|
CLdapTimeValue ltv; // Defaults to current UTC
|
|
CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_REPLACE);
|
|
CLdapBinaryMod lbmMachineId(s_currMachineId, (PCHAR)&mcid, sizeof(mcid), LDAP_MOD_REPLACE);
|
|
CLdapSecret ls(secret);
|
|
CLdapBinaryMod lbmVolumeSecret(s_volumeSecret, (PCHAR)&ls, sizeof(ls), LDAP_MOD_REPLACE);
|
|
LDAPMod * mods[4];
|
|
HRESULT hr;
|
|
int err;
|
|
|
|
mods[0] = &lbmMachineId._mod;
|
|
mods[1] = &lbmVolumeSecret._mod;
|
|
mods[2] = &lsmTimeVolChange._mod;
|
|
mods[3] = NULL;
|
|
|
|
err = ldap_modify_s(Ldap(), dnKey, mods);
|
|
|
|
hr = MapResult(err);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CVolumeTable::SetSequenceNumber
|
|
//
|
|
// Set the sequence number of a volume entry. This is the value
|
|
// of we expect to get in the next move-notification for this volume.
|
|
// (This is used to detect if the trksvr & trkwks get out of sync.)
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CVolumeTable::SetSequenceNumber(const CVolumeId & volume, SequenceNumber seq)
|
|
{
|
|
int err;
|
|
HRESULT hr;
|
|
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
|
|
CLdapSeqNum lsn(seq);
|
|
CLdapStringMod lsmSequence(s_seqNotification, lsn, LDAP_MOD_REPLACE );
|
|
|
|
LDAPMod * mods[2];
|
|
|
|
// Set up the MODs array.
|
|
|
|
mods[0] = &lsmSequence._mod;
|
|
mods[1] = 0;
|
|
|
|
// Perform the modification.
|
|
|
|
err = ldap_modify_s(Ldap(), dnKey, mods);
|
|
|
|
// Debug output
|
|
|
|
#if DBG
|
|
if( LDAP_SUCCESS != err )
|
|
TrkLog(( TRKDBG_SVR, TEXT("Couldn't set sequence number (%d)"), err ));
|
|
else
|
|
TrkLog(( TRKDBG_SVR, TEXT("Set seq %d on %s"), seq,
|
|
(const TCHAR*) CDebugString(volume) ));
|
|
#endif
|
|
|
|
// Map back to an HRESULT
|
|
|
|
hr = MapResult(err);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
HRESULT
|
|
CVolumeTable::GetVolumeInfo( const CVolumeId & volume,
|
|
CMachineId * pmcid,
|
|
CVolumeSecret * psecret,
|
|
SequenceNumber * pseq,
|
|
CFILETIME *pcftRefresh )
|
|
{
|
|
// lookup the volume and get the current machine and sequence number if any
|
|
HRESULT hr;
|
|
int err;
|
|
LDAPMessage * pRes = NULL;
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
|
|
struct berval ** ppbvMachineId = NULL;
|
|
//struct berval ** ppbvSeq = NULL;
|
|
TCHAR ** pptszSeq = NULL;
|
|
struct berval ** ppbvSecret = NULL;
|
|
TCHAR ** pptszRefresh = NULL;
|
|
|
|
TCHAR * apszAttrs[5];
|
|
|
|
__try
|
|
{
|
|
|
|
apszAttrs[0] = const_cast<TCHAR*>(s_currMachineId);
|
|
apszAttrs[1] = const_cast<TCHAR*>(s_seqNotification);
|
|
apszAttrs[2] = const_cast<TCHAR*>(s_volumeSecret);
|
|
apszAttrs[3] = const_cast<TCHAR*>(s_timeRefresh);
|
|
apszAttrs[4] = 0;
|
|
|
|
err = ldap_search_s( Ldap(),
|
|
dnKey,
|
|
LDAP_SCOPE_BASE,
|
|
TEXT("(objectclass=*)"),
|
|
apszAttrs,
|
|
0, // attribute types and values are wanted
|
|
&pRes );
|
|
|
|
|
|
hr = MapResult(err);
|
|
|
|
if (S_OK == hr)
|
|
{
|
|
// found it, lets get the attributes out
|
|
int cEntries;
|
|
|
|
cEntries = ldap_count_entries(Ldap(), pRes);
|
|
|
|
// Get the entry from the search results.
|
|
|
|
if (cEntries < 1)
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("GetVolumeInfo: ldap_search for %s succeeded, but with %d entries"),
|
|
(TCHAR*) dnKey /*CDebugString(volume)._tsz*/, cEntries ));
|
|
hr = MapResult(LDAP_NO_SUCH_OBJECT);
|
|
__leave;
|
|
}
|
|
|
|
LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
|
|
if (pEntry == NULL)
|
|
{
|
|
// This should also never happen. We know at this point that we have
|
|
// 1 entry in the search result. We'll pretend that it doesn't exist.
|
|
TrkLog(( TRKDBG_ERROR, TEXT("GetVolumeInfo: ldap_search has one entry, but it couldn't be retrieved") ));
|
|
hr = MapResult(LDAP_NO_SUCH_OBJECT);
|
|
__leave;
|
|
}
|
|
|
|
// Get the machine ID attribute.
|
|
|
|
ppbvMachineId = ldap_get_values_len(Ldap(), pEntry, const_cast<TCHAR*>(s_currMachineId) );
|
|
if( NULL == ppbvMachineId
|
|
||
|
|
sizeof(CMachineId) > (*ppbvMachineId)->bv_len )
|
|
{
|
|
// This entry is corrupt, there should always be a mcid attribute.
|
|
// We'll pretend it doesn't exist for now, and let GC clean it up.
|
|
hr = MapResult(LDAP_NO_SUCH_OBJECT);
|
|
__leave;
|
|
}
|
|
memcpy( pmcid, (*ppbvMachineId)->bv_val, sizeof(*pmcid) );
|
|
|
|
// Get the volume secret attribute
|
|
|
|
ppbvSecret = ldap_get_values_len(Ldap(), pEntry, const_cast<TCHAR*>(s_volumeSecret) );
|
|
if( NULL == ppbvSecret
|
|
||
|
|
sizeof(CLdapSecret) > (*ppbvSecret)->bv_len )
|
|
{
|
|
// This entry is corrupt, there should always be a secret attribute.
|
|
// We'll pretend it doesn't exist for now, and let GC clean it up.
|
|
hr = MapResult(LDAP_NO_SUCH_OBJECT);
|
|
__leave;
|
|
}
|
|
memcpy( psecret, (*ppbvSecret)->bv_val, sizeof(*psecret) );
|
|
|
|
// Get the sequence number attribute
|
|
|
|
*pseq = 0;
|
|
pptszSeq = ldap_get_values(Ldap(), pEntry, const_cast<TCHAR*>(s_seqNotification) );
|
|
if (NULL == pptszSeq || CCH_UINT32 < _tcslen(*pptszSeq))
|
|
{
|
|
// The sequence number is missing or invalid. We'll just assume it's zero.
|
|
|
|
TrkLog((TRKDBG_ERROR, TEXT("Sequence number string too long in vol table (vol=%s)"),
|
|
(const TCHAR*) CDebugString(volume) ));
|
|
}
|
|
else if( 1 != _stscanf( *pptszSeq, TEXT("%d"), pseq ))
|
|
{
|
|
// Again, assume the sequnce number is zero.
|
|
TrkLog((TRKDBG_ERROR, TEXT("Invalid sequence number string in vol table (seq=%s, vol=%s)"),
|
|
*pptszSeq,
|
|
(const TCHAR*) CDebugString(volume) ));
|
|
*pseq = 0;
|
|
}
|
|
|
|
// Get the refresh counter, if it exists.
|
|
|
|
*pcftRefresh = 0;
|
|
pptszRefresh = ldap_get_values( Ldap(), pEntry, const_cast<TCHAR*>(s_timeRefresh) );
|
|
if( NULL != pptszRefresh )
|
|
{
|
|
SequenceNumber seqRefresh = 0;
|
|
if( 1 == _stscanf( *pptszRefresh, TEXT("%d"), &seqRefresh ))
|
|
*pcftRefresh = seqRefresh;
|
|
}
|
|
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
if (NULL != pRes)
|
|
{
|
|
ldap_msgfree(pRes);
|
|
}
|
|
|
|
if (ppbvMachineId != NULL)
|
|
{
|
|
ldap_value_free_len(ppbvMachineId);
|
|
}
|
|
|
|
if (pptszSeq != NULL)
|
|
{
|
|
ldap_value_free(pptszSeq);
|
|
}
|
|
|
|
if (ppbvSecret != NULL)
|
|
{
|
|
ldap_value_free_len(ppbvSecret);
|
|
}
|
|
|
|
if (pptszRefresh != NULL)
|
|
ldap_value_free(pptszRefresh);
|
|
|
|
if (AbnormalTermination())
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Exception in CVolumeTable::GetVolumeInfo") ));
|
|
}
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
// TRUE if exists and touched, FALSE if not existent, exception otherwise.
|
|
// BUGBUG P2: check ownership of entry being touched.
|
|
|
|
BOOL
|
|
CVolumeTable::Touch(
|
|
const CVolumeId & volid
|
|
)
|
|
{
|
|
|
|
if (volid == CVolumeId())
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Null volid passed to CVolumeTable::Touch") ));
|
|
return( FALSE );
|
|
}
|
|
|
|
BOOL fReturn = FALSE;
|
|
int err;
|
|
CLdapRefresh ltvRefresh( _pRefreshSequenceStorage->GetSequenceNumber());
|
|
CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_REPLACE );
|
|
CLdapVolumeKeyDn
|
|
dnKey(GetBaseDn(), volid);
|
|
|
|
LDAPMod * mods[2];
|
|
TCHAR ** pptszRefresh = NULL;
|
|
LDAPMessage * pEntry = NULL;
|
|
LDAPMessage* pRes = NULL;
|
|
|
|
|
|
__try
|
|
{
|
|
|
|
|
|
//
|
|
// Check to see if the object already has this 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 ))
|
|
{
|
|
// First, long is the GC timer in seconds?
|
|
LONG lGCTimerInSeconds = _pconfigSvr->GetGCPeriod() // 30 days in seconds
|
|
/ _pconfigSvr->GetGCDivisor(); // 30
|
|
|
|
// Next, how many ticks is half the period?
|
|
LONG lWindow = _pconfigSvr->GetGCPeriod() // 30 days (in seconds)
|
|
/ 2 // => 15 days (in seconds)
|
|
/ lGCTimerInSeconds; // => 15
|
|
if( seqRefresh + lWindow
|
|
>= _pRefreshSequenceStorage->GetSequenceNumber()
|
|
)
|
|
{
|
|
TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_VOLTAB,
|
|
TEXT("Not touching volume %s with %d, seq %d already set"),
|
|
(const TCHAR*) CDebugString(volid),
|
|
_pRefreshSequenceStorage->GetSequenceNumber(),
|
|
seqRefresh ));
|
|
__leave;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (err == LDAP_NO_SUCH_OBJECT)
|
|
{
|
|
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_VOLTAB,
|
|
TEXT("Touch: volume %s not found"),
|
|
(const TCHAR*) CDebugString(volid)));
|
|
__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_SVR,
|
|
TEXT("Touch: volume %s touched"),
|
|
(const TCHAR*) CDebugString(volid)));
|
|
fReturn = TRUE;
|
|
__leave;
|
|
}
|
|
else
|
|
if (err == LDAP_NO_SUCH_OBJECT)
|
|
{
|
|
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
|
|
TEXT("Touch:: volume %s doesn't exist"),
|
|
(const TCHAR*) CDebugString(volid)));
|
|
__leave;
|
|
}
|
|
else
|
|
if (err == LDAP_NO_SUCH_ATTRIBUTE)
|
|
{
|
|
TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
|
|
TEXT("Touch: volume %s attribute not found"),
|
|
(const TCHAR*) CDebugString(volid)));
|
|
|
|
// 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_SVR,
|
|
TEXT("Touch:: volume %s gives exceptional error"),
|
|
(const TCHAR*) CDebugString(volid)));
|
|
|
|
__leave;
|
|
}
|
|
}
|
|
__except( BreakOnDebuggableException() )
|
|
{
|
|
TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolumeTable::Touch (%08x)"), GetExceptionCode() ));
|
|
}
|
|
|
|
if (pptszRefresh != NULL)
|
|
ldap_value_free(pptszRefresh);
|
|
if(pRes != NULL)
|
|
ldap_msgfree(pRes);
|
|
|
|
return( fReturn );
|
|
}
|
|
|
|
|
|
//+----------------------------------------------------------------------------
|
|
//
|
|
// CVolumeTable::GarbageCollect
|
|
//
|
|
// This is called by CTrkSvrSvc when it's time to GC the volume table (daily).
|
|
// The entries are enumerated, and if too old they are deleted.
|
|
//
|
|
//+----------------------------------------------------------------------------
|
|
|
|
ULONG
|
|
CVolumeTable::GarbageCollect( SequenceNumber seqCurrent, SequenceNumber seqOldestToKeep, const BOOL * pfAbort )
|
|
{
|
|
CLdapVolumeKeyDn dn(GetBaseDn());
|
|
TCHAR * apszAttrs[3];
|
|
GC_ENUM_CONTEXT EnumContext;
|
|
|
|
TrkLog(( TRKDBG_VOLTAB | TRKDBG_GARBAGE_COLLECT, TEXT("GC-ing volume table (%d/%d)"),
|
|
seqCurrent, seqOldestToKeep ));
|
|
|
|
// Set up the attributes for the ldap_search_init_page call.
|
|
|
|
apszAttrs[0] = const_cast<TCHAR*>(s_Cn);
|
|
apszAttrs[1] = const_cast<TCHAR*>(s_timeRefresh);
|
|
apszAttrs[2] = 0;
|
|
|
|
// Set up all the info that the LdapEnumerate call needs.
|
|
|
|
memset( &EnumContext, 0, sizeof(EnumContext) );
|
|
EnumContext.seqOldestToKeep = seqOldestToKeep;
|
|
EnumContext.seqCurrent = seqCurrent;
|
|
EnumContext.pfAbort = pfAbort;
|
|
EnumContext.dwRepetitiveTaskDelay = _pconfigSvr->GetRepetitiveTaskDelay();
|
|
EnumContext.pqtable = _pqtable;
|
|
|
|
// Do an ldap_search, calling GcEnumerateCallback for each of the
|
|
// returned values.
|
|
|
|
if (!LdapEnumerate(
|
|
Ldap(), // LDAP handle
|
|
dn, // Base DN
|
|
LDAP_SCOPE_ONELEVEL, // No recursion
|
|
TEXT("(objectClass=*)"), // Filter
|
|
apszAttrs, // Attributes (get CN & refresh time)
|
|
GcEnumerateCallback, // Called for each iteration
|
|
&EnumContext )) // Info for GcEnuemrateCallback
|
|
{
|
|
TrkRaiseException(TRK_E_SERVICE_STOPPING);
|
|
}
|
|
|
|
TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
|
|
TEXT("GC-ed %d entries from the volume table"),
|
|
EnumContext.cEntries ));
|
|
|
|
// If we actually deleted anything, the cached values
|
|
// in the quota object are no longer valid. Mark it as
|
|
// such, so that it will know to re-generate it the next
|
|
// time it's needed.
|
|
|
|
if( 0 != EnumContext.cEntries )
|
|
_pqtable->InvalidateCache();
|
|
|
|
return EnumContext.cEntries;
|
|
}
|
|
|
|
ENUM_ACTION
|
|
GcEnumerateCallback( LDAP * pLdap, LDAPMessage *pMessage, PVOID pvContext, PVOID )
|
|
{
|
|
GC_ENUM_CONTEXT * pContext = (GC_ENUM_CONTEXT *) pvContext;
|
|
TCHAR * ptszDn = NULL;
|
|
TCHAR ** pptszValue = NULL;
|
|
ENUM_ACTION Action = ENUM_KEEP_ENTRY;
|
|
ULONG ulSequence = 0;
|
|
|
|
// See if we should abort. We shouldn't even be here if we're not
|
|
// the designated DC. The only way it can happen is if the designated
|
|
// DC is changed during the enumeration.
|
|
|
|
if( *(pContext->pfAbort)
|
|
||
|
|
!pContext->pqtable->IsDesignatedDc() )
|
|
{
|
|
Action = ENUM_ABORT;
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the DN of this entry so that we can check for special entries.
|
|
|
|
ptszDn = ldap_get_dn( pLdap, pMessage );
|
|
if (ptszDn == NULL)
|
|
{
|
|
TrkLog((TRKDBG_GARBAGE_COLLECT,
|
|
TEXT("Couldn't get DN during GcEnumerateCallback") ));
|
|
goto Exit;
|
|
}
|
|
|
|
// CQuotaTable stores special values in the volume table, all prefixed by "QT".
|
|
// Don't delete those.
|
|
|
|
if( !_tcsnicmp( TEXT("CN=QT"), ptszDn, 5 ))
|
|
{
|
|
TrkLog(( TRKDBG_GARBAGE_COLLECT,
|
|
TEXT("Skipping quota entry in GC (%s)"),
|
|
ptszDn ));
|
|
goto Exit;
|
|
}
|
|
|
|
// The current value of the Refresh counter is stored in volume ID 0. So
|
|
// don't delete that either.
|
|
|
|
if( !_tcsnicmp( TEXT("CN=00000000000000000000000000000000,"), ptszDn, 35 ))
|
|
{
|
|
TrkLog(( TRKDBG_GARBAGE_COLLECT,
|
|
TEXT("Skipping volid 0 GC (%s)"),
|
|
ptszDn ));
|
|
goto Exit;
|
|
}
|
|
|
|
// Get the refresh time value.
|
|
|
|
pptszValue = ldap_get_values( pLdap, pMessage, const_cast<TCHAR*>(s_timeRefresh) );
|
|
|
|
if (pptszValue == NULL)
|
|
{
|
|
TrkLog((TRKDBG_GARBAGE_COLLECT,
|
|
TEXT("Can't find sequence number in %s"),
|
|
ptszDn));
|
|
|
|
// This is a corrupted entry that will never get GC-ed,
|
|
// so we'll delete it now.
|
|
|
|
pContext->cEntries++;
|
|
Action = ENUM_DELETE_ENTRY;
|
|
goto Exit;
|
|
}
|
|
|
|
_stscanf( *pptszValue, TEXT("%d"), &ulSequence );
|
|
|
|
// Determine if we should delete this entry.
|
|
|
|
if( ulSequence < (ULONG)pContext->seqOldestToKeep )
|
|
{
|
|
Action = ENUM_DELETE_ENTRY;
|
|
pContext->cEntries++;
|
|
}
|
|
else
|
|
Action = ENUM_KEEP_ENTRY;
|
|
|
|
|
|
#if DBG
|
|
if( ENUM_DELETE_ENTRY == Action )
|
|
TrkLog(( TRKDBG_QUOTA, TEXT("Seq to delete: %d/%d"),
|
|
ulSequence, (ULONG)pContext->seqOldestToKeep ));
|
|
#endif
|
|
|
|
// Check to see if the entry has an invalid sequence number. It's
|
|
// invalid if it's bigger than the current value. This can happen if the
|
|
// special zero entry gets deleted from the volume table for some reason.
|
|
|
|
if( ulSequence > (ULONG)pContext->seqCurrent )
|
|
{
|
|
// Reset the entry's sequence number to the current value. Otherwise it
|
|
// could be a very long time before it gets GCed. This case should never
|
|
// happen, but there's no guarantee that someone won't delete the
|
|
// entry accidentally.
|
|
|
|
CLdapRefresh ltvRefresh( pContext->seqCurrent );
|
|
CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_REPLACE );
|
|
int err;
|
|
|
|
LDAPMod * mods[2];
|
|
|
|
mods[0] = &lsmRefresh._mod;
|
|
mods[1] = NULL;
|
|
|
|
err = ldap_modify_s( pLdap, ptszDn, mods );
|
|
|
|
TrkLog(( TRKDBG_SVR | TRKDBG_GARBAGE_COLLECT,
|
|
TEXT("Touched entry with invalid sequence number (%d, %s)"),
|
|
ulSequence, ptszDn ));
|
|
|
|
}
|
|
|
|
Exit:
|
|
|
|
if( NULL != pptszValue )
|
|
ldap_value_free( pptszValue );
|
|
|
|
if( NULL != ptszDn )
|
|
ldap_memfree( ptszDn );
|
|
|
|
// Be nice to the DS
|
|
if( 0 != pContext->dwRepetitiveTaskDelay )
|
|
Sleep( pContext->dwRepetitiveTaskDelay );
|
|
|
|
return( Action );
|
|
}
|
|
|
|
// returns FALSE if aborted
|
|
|
|
BOOL
|
|
LdapEnumerate(
|
|
LDAP * pLdap,
|
|
TCHAR * ptszBaseDn,
|
|
ULONG Scope,
|
|
TCHAR * Filter,
|
|
TCHAR * Attributes[],
|
|
PFN_LDAP_ENUMERATE_CALLBACK pCallback,
|
|
void* UserParam1,
|
|
void* UserParam2)
|
|
{
|
|
LDAPMessage * pResults;
|
|
LDAPSearch * pSearch;
|
|
ENUM_ACTION EnumAction = ENUM_KEEP_ENTRY;
|
|
|
|
// Start a paged enumeration using the specified base DN & filter.
|
|
|
|
pSearch = ldap_search_init_page( pLdap,
|
|
ptszBaseDn,
|
|
Scope,
|
|
Filter,
|
|
Attributes,
|
|
FALSE,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
20000,
|
|
NULL );
|
|
|
|
if (pSearch != NULL)
|
|
{
|
|
int err;
|
|
ULONG totalCount;
|
|
|
|
// Get the next page of the enumeration
|
|
|
|
while ( EnumAction != ENUM_ABORT &&
|
|
LDAP_SUCCESS == (err = ldap_get_next_page_s( pLdap,
|
|
pSearch,
|
|
NULL,
|
|
10,
|
|
&totalCount,
|
|
&pResults ) && pResults != NULL))
|
|
{
|
|
|
|
LDAPMessage * pMessage;
|
|
LDAPMessage * pFirstMessage;
|
|
|
|
// Loop through the entries on this page.
|
|
|
|
pFirstMessage = pMessage = ldap_first_entry( pLdap, pResults );
|
|
while ( EnumAction != ENUM_ABORT
|
|
&&
|
|
pMessage != NULL )
|
|
{
|
|
// Call the callback to process this entry.
|
|
|
|
EnumAction = (*pCallback)(
|
|
pLdap,
|
|
pMessage,
|
|
UserParam1,
|
|
UserParam2);
|
|
|
|
if ( EnumAction == ENUM_DELETE_ENTRY )
|
|
{
|
|
// This entry is to be deleted. Increment the entry
|
|
// count, and if we're not just counting, actually delete it.
|
|
|
|
|
|
TCHAR * ptszDn = ldap_get_dn( pLdap, pMessage );
|
|
|
|
if (ptszDn != NULL)
|
|
{
|
|
TrkLog((TRKDBG_ERROR, TEXT("Deleting Dn=%s"), ptszDn));
|
|
ldap_delete_s( pLdap, ptszDn );
|
|
ldap_memfree( ptszDn );
|
|
}
|
|
}
|
|
else if(EnumAction == ENUM_KEEP_ENTRY)
|
|
{
|
|
}
|
|
else if(EnumAction == ENUM_DELETE_QUOTAFLAGS)
|
|
{
|
|
TCHAR* ptszDn = ldap_get_dn(pLdap, pMessage);
|
|
|
|
if(NULL != ptszDn)
|
|
{
|
|
if(UserParam2)
|
|
{
|
|
((CQuotaTable*)UserParam2)->DeleteFlags(pLdap, ptszDn);
|
|
}
|
|
}
|
|
}
|
|
pMessage = ldap_next_entry( pLdap, pMessage );
|
|
}
|
|
|
|
ldap_msgfree( pResults );
|
|
|
|
if (pFirstMessage == NULL)
|
|
break;
|
|
}
|
|
ldap_search_abandon_page( pLdap, pSearch );
|
|
}
|
|
return(EnumAction != ENUM_ABORT);
|
|
}
|
|
|
|
|
|
#ifdef VOL_REPL
|
|
void
|
|
CVolumeTable::QueryVolumeChanges( const CFILETIME & cftFirstChange, CVolumeMap * pVolMap )
|
|
{
|
|
// protect against the data changing under us
|
|
BOOL fCacheHit;
|
|
|
|
__try
|
|
{
|
|
|
|
EnterCriticalSection(&_csQueryCache);
|
|
|
|
if (fCacheHit = _cftCacheLowest <= cftFirstChange)
|
|
{
|
|
TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
|
|
TEXT("CVolumeTable::QueryVolumeChanges(cftFirstChange=%s) HIT, returning %d change entries from cache"),
|
|
(const TCHAR*) CDebugString(cftFirstChange),
|
|
_VolMap.Count()));
|
|
_VolMap.CopyTo( pVolMap );
|
|
}
|
|
}
|
|
_finally
|
|
{
|
|
LeaveCriticalSection(&_csQueryCache);
|
|
}
|
|
|
|
if (!fCacheHit)
|
|
{
|
|
// the cache was missed... go to the real database.
|
|
|
|
CFILETIME cftHighest;
|
|
_QueryVolumeChanges( cftFirstChange, pVolMap );
|
|
|
|
TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
|
|
TEXT("CVolumeTable::QueryVolumeChanges(cftFirstChange=%s) MISS, returning %d entries from full query"),
|
|
(const TCHAR*) CDebugString(cftFirstChange),
|
|
pVolMap->Count()));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// if there are more than zero volume entries, then pVolMap->SetSize and pVolMap->Add will be called,
|
|
// otherwise pVolMap will not be called and so must be initialized by the caller
|
|
|
|
#ifdef VOL_REPL
|
|
|
|
void
|
|
CVolumeTable::_QueryVolumeChanges( const CFILETIME & FirstChangeRequested,
|
|
CVolumeMap * pVolMap )
|
|
{
|
|
// lookup the volume and get the current machine and sequence number if any
|
|
int err;
|
|
LDAPMessage * pRes = NULL;
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn());
|
|
struct berval ** ppbvMachineId = NULL;
|
|
TCHAR ** pptszCnVolumeId = NULL;
|
|
TCHAR szSearchFilter[256];
|
|
TCHAR * apszAttrs[4];
|
|
HRESULT hr;
|
|
CLdapTimeValue ltv(FirstChangeRequested);
|
|
|
|
__try
|
|
{
|
|
//
|
|
// Build up a search filter looking for objects with timeVolChange >= FirstChangeRequested
|
|
//
|
|
// (timeVolChange=XXX)
|
|
//
|
|
|
|
_tcscpy(szSearchFilter, s_timeVolChangeSearch);
|
|
|
|
_tcscat(szSearchFilter, ltv);
|
|
|
|
_tcscat(szSearchFilter, TEXT(")"));
|
|
|
|
//
|
|
// Build up the list of attributes to query
|
|
//
|
|
|
|
apszAttrs[0] = s_currMachineId;
|
|
apszAttrs[1] = s_Cn;
|
|
apszAttrs[2] = 0;
|
|
|
|
err = ldap_search_s( Ldap(),
|
|
dnKey,
|
|
LDAP_SCOPE_ONELEVEL,
|
|
szSearchFilter,
|
|
apszAttrs,
|
|
0, // attribute types and values are wanted
|
|
&pRes );
|
|
|
|
hr = MapResult(err);
|
|
|
|
//
|
|
// Depending on whether DS sets up a maximum query size, we will need to iterate
|
|
// performing multiple searches. Initially just use a single query.
|
|
//
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// found it, lets get the attributes out
|
|
|
|
int cEntries = ldap_count_entries(Ldap(), pRes);
|
|
if (cEntries != 0)
|
|
{
|
|
pVolMap->SetSize(cEntries);
|
|
|
|
LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
|
|
if (pEntry != NULL)
|
|
{
|
|
do
|
|
{
|
|
//
|
|
// for each entry get the
|
|
// volume id from the CN
|
|
// machine id
|
|
// time of last volume change
|
|
//
|
|
|
|
pptszCnVolumeId = ldap_get_values(Ldap(), pEntry, s_Cn);
|
|
if (pptszCnVolumeId == NULL)
|
|
{
|
|
TrkRaiseException(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
|
|
}
|
|
|
|
if (_tcslen(*pptszCnVolumeId) != 32) // length of stringized volume id
|
|
{
|
|
// Add code to recover from this.
|
|
TrkRaiseException(TRK_E_CORRUPT_VOLTAB);
|
|
}
|
|
|
|
ppbvMachineId = ldap_get_values_len(Ldap(), pEntry, s_currMachineId);
|
|
if (ppbvMachineId == NULL)
|
|
{
|
|
TrkRaiseException(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
|
|
}
|
|
|
|
if ((*ppbvMachineId)->bv_len < sizeof(CMachineId))
|
|
{
|
|
// Add code to recover from this
|
|
TrkRaiseException(TRK_E_CORRUPT_VOLTAB);
|
|
}
|
|
|
|
CVolumeId volume( *pptszCnVolumeId, TRK_E_CORRUPT_VOLTAB );
|
|
|
|
CMachineId machine( (*ppbvMachineId)->bv_val,
|
|
(*ppbvMachineId)->bv_len,
|
|
TRK_E_CORRUPT_VOLTAB );
|
|
|
|
pVolMap->Add( volume, machine );
|
|
|
|
ldap_value_free(pptszCnVolumeId);
|
|
pptszCnVolumeId = NULL;
|
|
|
|
ldap_value_free_len(ppbvMachineId);
|
|
ppbvMachineId = NULL;
|
|
|
|
} while ( pEntry = ldap_next_entry(Ldap(), pEntry));
|
|
}
|
|
|
|
pVolMap->Compact();
|
|
}
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
if (pRes != NULL)
|
|
{
|
|
ldap_msgfree(pRes);
|
|
}
|
|
|
|
if (pptszCnVolumeId != NULL)
|
|
{
|
|
ldap_value_free(pptszCnVolumeId);
|
|
}
|
|
|
|
if (ppbvMachineId != NULL)
|
|
{
|
|
ldap_value_free_len(ppbvMachineId);
|
|
}
|
|
}
|
|
|
|
TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
|
|
TEXT("_QueryVolumeChanges(filter=%s) got %d changes since %s"),
|
|
szSearchFilter,
|
|
pVolMap->Count(),
|
|
(const TCHAR*) CDebugString(FirstChangeRequested)));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// raises on error, returns number of volumes on this machine
|
|
|
|
DWORD
|
|
CVolumeTable::CountVolumes( const CMachineId & mcid )
|
|
{
|
|
// lookup the volume and get the current machine and sequence number if any
|
|
int err;
|
|
LDAPMessage * pRes = NULL;
|
|
CLdapVolumeKeyDn dnKey(GetBaseDn());
|
|
TCHAR szSearchFilter[256];
|
|
TCHAR * pszAppend;
|
|
TCHAR * aptszAttrs[2];
|
|
HRESULT hr;
|
|
DWORD cVolumes = 0;
|
|
|
|
__try
|
|
{
|
|
//
|
|
// Build up a search filter looking for objects with currMachineId == mcid
|
|
//
|
|
// (volTableIdxGUID;binary=XXX)
|
|
//
|
|
|
|
_tcscpy(szSearchFilter, s_currMachineIdSearch);
|
|
pszAppend = szSearchFilter + _tcslen(szSearchFilter);
|
|
|
|
// mcid.Stringize(pszAppend);
|
|
mcid.StringizeAsGuid(pszAppend);
|
|
|
|
*pszAppend++ = TEXT(')');
|
|
*pszAppend++ = TEXT('\0');
|
|
|
|
TrkAssert(_tcslen(szSearchFilter)+1 < ELEMENTS(szSearchFilter));
|
|
|
|
//
|
|
// Build up the list of attributes to query
|
|
// If we ever update to allow large numbers of volumes on a machine,
|
|
// this should be a paged enumeration.
|
|
//
|
|
|
|
aptszAttrs[0] = const_cast<TCHAR*>(s_Cn);
|
|
aptszAttrs[1] = 0;
|
|
|
|
err = ldap_search_s( Ldap(),
|
|
dnKey,
|
|
LDAP_SCOPE_ONELEVEL,
|
|
szSearchFilter,
|
|
aptszAttrs,
|
|
0, // attribute types and values are wanted
|
|
&pRes );
|
|
|
|
hr = MapResult(err);
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
// found it, lets get the attributes out
|
|
|
|
cVolumes = ldap_count_entries(Ldap(), pRes);
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
if (pRes != NULL)
|
|
{
|
|
ldap_msgfree(pRes);
|
|
}
|
|
}
|
|
|
|
TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
|
|
TEXT("CountVolumes(filter=%s) got %d volumes"),
|
|
szSearchFilter,
|
|
cVolumes));
|
|
|
|
return(cVolumes);
|
|
}
|
|
|