Source code of Windows XP (NT5)
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.

1333 lines
42 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+-------------------------------------------------------------------------
  3. //
  4. // Microsoft Windows
  5. //
  6. // File: quota.cxx
  7. //
  8. // Contents: quota table
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // Notes: This table is hidden in the volume table
  15. //
  16. // History: 18-Nov-97 WeiruC Created.
  17. //
  18. //--------------------------------------------------------------------------
  19. #include "pch.cxx"
  20. #pragma hdrstop
  21. #include "trksvr.hxx"
  22. #include <commctrl.h>
  23. #include <time.h>
  24. CQuotaTable::CQuotaTable(CDbConnection& dbc) :
  25. _dbc(dbc),
  26. #if DBG
  27. _cLocks(0),
  28. #endif
  29. _mcid(MCID_LOCAL),
  30. _cftCacheLastUpdated(0),
  31. _cftDesignatedDc(0),
  32. _pvoltab(NULL)
  33. {
  34. _fIsDesignatedDc = FALSE;
  35. _fInitializeCalled = FALSE;
  36. _fStopping = FALSE;
  37. _dwMoveLimit = 0;
  38. _lCachedMoveTableCount = _lCachedVolumeTableCount = 0;
  39. }
  40. CQuotaTable::~CQuotaTable()
  41. {
  42. UnInitialize();
  43. }
  44. void
  45. CQuotaTable::Initialize(CVolumeTable *pvoltab,
  46. CIntraDomainTable *pidt,
  47. CTrkSvrSvc *psvrsvc,
  48. CTrkSvrConfiguration *pcfgsvr )
  49. {
  50. CMachineId mcid;
  51. HRESULT hr;
  52. _cs.Initialize();
  53. #if DBG
  54. _cLocks = 0;
  55. #endif
  56. _fInitializeCalled = TRUE;
  57. _pvoltab = pvoltab;
  58. _pcfgsvr = pcfgsvr;
  59. _pidt = pidt;
  60. _psvrsvc = psvrsvc;
  61. _timer.Initialize(this,
  62. TEXT("NextDcSyncTime"), // Name (this is a persistent timer)
  63. QUOTA_TIMER, // Context ID
  64. _pcfgsvr->GetDcUpdateCounterPeriod(), // Period
  65. CNewTimer::NO_RETRY,
  66. 0, 0, 0 ); // Ignored for non-retrying timers
  67. _timer.SetRecurring();
  68. TrkLog(( TRKDBG_VOLUME, TEXT("DC sync timer: %s"),
  69. (const TCHAR*) CDebugString(_timer) ));
  70. // The Reset* routines assume that an entry already exists for
  71. // this DC. If the operation fails because the entry doesn't exist, it
  72. // will add the entry. We ignore any error because if we can't add
  73. // the entry it only means that we are not participating in the race
  74. // for the designated DC.
  75. }
  76. void
  77. CQuotaTable::UnInitialize()
  78. {
  79. if(_fInitializeCalled)
  80. {
  81. _timer.UnInitialize();
  82. _lCachedMoveTableCount = 0;
  83. _lCachedVolumeTableCount = 0;
  84. _cftCacheLastUpdated = CFILETIME(0);
  85. _cs.UnInitialize();
  86. _fInitializeCalled = FALSE;
  87. }
  88. }
  89. //+----------------------------------------------------------------------------
  90. //
  91. // CQuotaTable::Timer
  92. //
  93. // This method is called when _timer fires. If we're the designated
  94. // DC, we'll go through the move table and count the uncounted entries,
  95. // delete the deleted entries, and shorten any moves chains.
  96. //
  97. //+----------------------------------------------------------------------------
  98. PTimerCallback::TimerContinuation
  99. CQuotaTable::Timer(ULONG ulTimer)
  100. {
  101. DWORD dwTrueCount = 0;
  102. Lock();
  103. __try
  104. {
  105. TrkLog((TRKDBG_QUOTA, TEXT("DC synchronization time")));
  106. g_ptrksvr->RaiseIfStopped();
  107. TrkAssert( QUOTA_TIMER == ulTimer );
  108. if( QUOTA_TIMER == ulTimer )
  109. {
  110. if( IsDesignatedDc() )
  111. {
  112. GetTrueCount( &dwTrueCount, BACKGROUND );
  113. TrkLog((TRKDBG_QUOTA, TEXT("Updated counter")));
  114. _psvrsvc->_OperationLog.Add( COperationLog::TRKSVR_QUOTA, S_OK, CMachineId(MCID_INVALID), dwTrueCount );
  115. }
  116. }
  117. }
  118. __except( BreakOnDebuggableException() )
  119. {
  120. // This timer fires often enough that we'll just
  121. // wait until it fires again.
  122. TrkLog(( TRKDBG_WARNING,
  123. TEXT("Ignoring exception in CQuotaTable::Timer (%08x)"),
  124. GetExceptionCode() ));
  125. _psvrsvc->_OperationLog.Add( COperationLog::TRKSVR_QUOTA, GetExceptionCode() );
  126. }
  127. Unlock();
  128. TrkAssert( _timer.IsRecurring() );
  129. return( CONTINUE_TIMER );
  130. }
  131. BOOL
  132. CQuotaTable::IsMoveQuotaExceeded()
  133. {
  134. BOOL fExceeded = TRUE;
  135. Lock();
  136. __try
  137. {
  138. ValidateCache();
  139. // Compare the cached counter with the limit. The cached value should
  140. // never be negative. But if for some reason it is, don't get confused by it
  141. // (i.e. bottom it out at zero). The cached value will get automatically
  142. // corrected when the cache gets refreshed.
  143. if( (DWORD) max(0,_lCachedMoveTableCount) < _dwMoveLimit )
  144. {
  145. fExceeded = FALSE;
  146. }
  147. else
  148. {
  149. // Try to force the cache to be updated
  150. ValidateCache( TRUE );
  151. if( (DWORD) max(0,_lCachedMoveTableCount) < _dwMoveLimit)
  152. {
  153. fExceeded = FALSE;
  154. }
  155. }
  156. if(fExceeded)
  157. {
  158. TrkLog((TRKDBG_QUOTA, TEXT("*** Move table quota exceeded (%li/%lu)"), _lCachedMoveTableCount, _dwMoveLimit ));
  159. }
  160. else
  161. {
  162. TrkLog(( TRKDBG_QUOTA, TEXT("Move quota not exceeded (%li/%lu)"), _lCachedMoveTableCount, _dwMoveLimit ));
  163. }
  164. }
  165. __finally
  166. {
  167. Unlock();
  168. }
  169. return fExceeded;
  170. }
  171. //+----------------------------------------------------------------------------
  172. //
  173. // CQuotaTable::IsVolumeQuotaExceeded
  174. //
  175. // See if this machine is at or over its quota (it can go over in a replicated
  176. // environment). The total number of
  177. // volumes it has is the count of DS entries for the machine, plus
  178. // cUncountedVolumes. cUncountedVolumes is non-zero when a machine
  179. // sends up a request to create multiple volumes, and increments
  180. // as the service iterates through the requests.
  181. //
  182. //+----------------------------------------------------------------------------
  183. BOOL
  184. CQuotaTable::IsVolumeQuotaExceeded( const CMachineId& mcid, ULONG cUncountedVolumes )
  185. {
  186. Lock();
  187. __try
  188. {
  189. // How many entries does this machine have in the DS?
  190. ULONG cVolumes = _pvoltab->CountVolumes(mcid);
  191. // Is it at/over quota?
  192. if( cVolumes + cUncountedVolumes >= _pcfgsvr->GetVolumeLimit() )
  193. {
  194. TrkLog((TRKDBG_QUOTA, TEXT("VOLUME QUOTA EXCEEDED for %s (%d+%d)"),
  195. (const TCHAR*) CDebugString(mcid),
  196. cVolumes, cUncountedVolumes ));
  197. return TRUE;
  198. }
  199. else
  200. TrkLog(( TRKDBG_QUOTA, TEXT("Volume quota not exceeded for %s (%d+%d, %d)"),
  201. (const TCHAR*) CDebugString(mcid),
  202. cVolumes, cUncountedVolumes, _pcfgsvr->GetVolumeLimit() ));
  203. }
  204. __finally
  205. {
  206. Unlock();
  207. }
  208. return FALSE;
  209. }
  210. //+----------------------------------------------------------------------------
  211. //
  212. // ReadAttribute
  213. //
  214. // Given an LDAP pointer, a DN, and an attribute name, read the attribute
  215. // for the entry with that DN.
  216. //
  217. //+----------------------------------------------------------------------------
  218. int
  219. ReadAttribute( LDAP* pldap, const TCHAR *ptszDN, const TCHAR *ptszAttributeName,
  220. TCHAR ***ppptszAttributeValue )
  221. {
  222. int ldapRV = 0;
  223. const TCHAR *rgptszAttrs[] = { ptszAttributeName, NULL };
  224. int cEntries = 0;
  225. LDAPMessage *pRes = NULL;
  226. LDAPMessage *pEntry = NULL;
  227. __try
  228. {
  229. ldapRV = ldap_search_s(pldap,
  230. const_cast<TCHAR*>(ptszDN),
  231. LDAP_SCOPE_BASE,
  232. TEXT("(ObjectClass=*)"),
  233. const_cast<TCHAR**>(rgptszAttrs),
  234. 0,
  235. &pRes);
  236. if( LDAP_SUCCESS != ldapRV )
  237. {
  238. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't find %s (%lu)"), ptszDN, ldapRV ));
  239. __leave;
  240. }
  241. cEntries = ldap_count_entries(pldap, pRes);
  242. if( cEntries < 1 )
  243. {
  244. TrkLog(( TRKDBG_QUOTA, TEXT("No entries returned for %s in %s"), ptszAttributeName, ptszDN ));
  245. ldapRV = LDAP_NO_SUCH_OBJECT;
  246. __leave;
  247. }
  248. pEntry = ldap_first_entry(pldap, pRes);
  249. if(NULL == pEntry)
  250. {
  251. TrkLog(( TRKDBG_QUOTA, TEXT("Entries couldn't be read from result for %s"), ptszDN ));
  252. ldapRV = LDAP_NO_SUCH_OBJECT;
  253. __leave;
  254. }
  255. *ppptszAttributeValue = ldap_get_values(pldap, pEntry, const_cast<TCHAR*>(ptszAttributeName) );
  256. if( NULL == *ppptszAttributeValue )
  257. {
  258. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't find %s in %s"), ptszAttributeName, ptszDN ));
  259. ldapRV = LDAP_NO_SUCH_OBJECT;
  260. __leave;
  261. }
  262. }
  263. __finally
  264. {
  265. if(NULL != pRes)
  266. {
  267. ldap_msgfree(pRes);
  268. }
  269. }
  270. return( ldapRV );
  271. }
  272. //+----------------------------------------------------------------------------
  273. //
  274. // CQuotaTable::IsDesignatedDc
  275. //
  276. // Determine if this machine is the designated DC. (The designated DC is
  277. // responsible for all modifications to link tracking data in the DS that
  278. // requires a single master). The "RID Master" DC is used as the designated
  279. // DC.
  280. //
  281. //+----------------------------------------------------------------------------
  282. BOOL
  283. CQuotaTable::IsDesignatedDc( BOOL fRaiseOnError )
  284. {
  285. BOOL fSuccess = FALSE;
  286. BOOL fDesignatedDc = FALSE;
  287. HRESULT hr = E_FAIL;
  288. int ldapRV = 0;
  289. int cEntries = 0;
  290. TCHAR **pptszRidManagerReference = NULL;
  291. TCHAR **pptszRoleOwner = NULL;
  292. const TCHAR *rgtszAttrs[] = { s_rIDManagerReference, NULL };
  293. TCHAR *ptszDesignatedDC = NULL, *ptszAfterDesignatedDC = NULL;
  294. CMachineId mcidDesignated, mcidLocal(MCID_LOCAL);
  295. CFILETIME cftNow;
  296. LONGLONG llDelta;
  297. // How old (in seconds) is the _fIsDesignatedDc value?
  298. llDelta = (LONGLONG) cftNow - (LONGLONG) _cftDesignatedDc;
  299. llDelta /= 10000000;
  300. if( llDelta < _pcfgsvr->GetDesignatedDcCacheTTL() )
  301. {
  302. // The cached value is young enough.
  303. //TrkLog(( TRKDBG_QUOTA, TEXT("Cache: %s designated DC"),
  304. // _fIsDesignatedDc ? TEXT("is") : TEXT("isn't") ));
  305. return( _fIsDesignatedDc );
  306. }
  307. // The cached value is too old. Recalculate.
  308. __try
  309. {
  310. // Read the "rIDManagerReference" from the root DC=<domain> object.
  311. ldapRV = ReadAttribute(Ldap(), GetBaseDn(), s_rIDManagerReference, &pptszRidManagerReference );
  312. if( LDAP_SUCCESS != ldapRV )
  313. {
  314. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(ldapRV) );
  315. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't get RID manager reference (%lu)"), ldapRV ));
  316. __leave;
  317. }
  318. TrkLog(( TRKDBG_QUOTA, TEXT("RID manager reference: %s"), *pptszRidManagerReference ));
  319. // The value of the rIDManagerReference is a DN. Read the "fSMORoleOwner" attribute
  320. // from that object.
  321. ldapRV = ReadAttribute( Ldap(), *pptszRidManagerReference, s_fSMORoleOwner, &pptszRoleOwner );
  322. if( LDAP_SUCCESS != ldapRV )
  323. {
  324. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(ldapRV) );
  325. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't get RID role owner (%lu)"), ldapRV ));
  326. __leave;
  327. }
  328. TrkLog(( TRKDBG_QUOTA, TEXT("Role owner: %s"), *pptszRoleOwner ));
  329. // The role owner is of the form
  330. // "CN=NTDS Settings,CN=mikehill4,CN=Servers,CN=Default-FirstSite-Name,CN=Sites,CN=Configuration,DC=trkmikehill,DC=nttest,DC=microsoft,DC=com"
  331. // Pull out the DC's machine name by getting the second "CN=".
  332. ptszDesignatedDC = _tcsstr( *pptszRoleOwner, TEXT("CN=") );
  333. if( NULL == ptszDesignatedDC )
  334. {
  335. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(LDAP_NO_SUCH_OBJECT) );
  336. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't find first component of FSMO role owner") ));
  337. __leave;
  338. }
  339. ptszDesignatedDC = _tcsstr( &ptszDesignatedDC[1], TEXT("CN=") );
  340. if( NULL == ptszDesignatedDC )
  341. {
  342. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(LDAP_NO_SUCH_OBJECT) );
  343. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't find second component of FSMO role owner") ));
  344. __leave;
  345. }
  346. ptszDesignatedDC += 3;
  347. ptszAfterDesignatedDC = _tcsstr( ptszDesignatedDC, TEXT(",CN=") );
  348. if( NULL == ptszAfterDesignatedDC )
  349. {
  350. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(LDAP_NO_SUCH_OBJECT) );
  351. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't find third component of FSMO role owner") ));
  352. __leave;
  353. }
  354. *ptszAfterDesignatedDC = TEXT('\0');
  355. // Are we the same (and therefore the designated) DC?
  356. mcidDesignated = CMachineId(ptszDesignatedDC );
  357. if( mcidDesignated == mcidLocal )
  358. fDesignatedDc = TRUE;
  359. TrkLog(( TRKDBG_QUOTA, TEXT("Designated DC is %s %s"), ptszDesignatedDC,
  360. fDesignatedDc ? TEXT("(this DC)") : TEXT("") ));
  361. _fIsDesignatedDc = fDesignatedDc;
  362. _cftDesignatedDc = cftNow;
  363. fSuccess = TRUE;
  364. }
  365. __finally
  366. {
  367. if( NULL != pptszRidManagerReference )
  368. ldap_value_free( pptszRidManagerReference );
  369. if( NULL != pptszRoleOwner )
  370. ldap_value_free( pptszRoleOwner );
  371. }
  372. if( !fSuccess && fRaiseOnError )
  373. TrkRaiseException( hr );
  374. return( fDesignatedDc );
  375. }
  376. // Returns TRUE if successful, FALSE if entry doesn't exist, raise exception
  377. // otherwise.
  378. BOOL
  379. CQuotaTable::ReadCounter(DWORD* pdwCounter)
  380. {
  381. BOOL fSuccess = FALSE;
  382. struct berval** ppbvCounter = NULL;
  383. TCHAR* rgtszAttrs[2];
  384. LDAPMessage* pRes = NULL;
  385. int ldapRV;
  386. int cEntries = 0;
  387. LDAPMessage* pEntry = NULL;
  388. CLdapQuotaCounterKeyDn dnKeyCounter(GetBaseDn());
  389. __try
  390. {
  391. *pdwCounter = 0;
  392. rgtszAttrs[0] = const_cast<TCHAR*>(s_volumeSecret);
  393. rgtszAttrs[1] = NULL;
  394. ldapRV = ldap_search_s(Ldap(),
  395. dnKeyCounter,
  396. LDAP_SCOPE_BASE,
  397. TEXT("(ObjectClass=*)"),
  398. rgtszAttrs,
  399. 0,
  400. &pRes);
  401. if( LDAP_NO_SUCH_OBJECT == ldapRV )
  402. {
  403. TrkLog(( TRKDBG_QUOTA, TEXT("Move table counter doesn't exist") ));
  404. __leave;
  405. }
  406. else if( LDAP_SUCCESS != ldapRV )
  407. {
  408. TrkLog(( TRKDBG_QUOTA, TEXT("Couldn't read move table counter (%d)"), ldapRV ));
  409. __leave;
  410. }
  411. cEntries = ldap_count_entries(Ldap(), pRes);
  412. if( cEntries != 1 )
  413. {
  414. // This should never happen, the counter has an explicit name.
  415. // We'll assume that when the designated DC does a WriteCounter, this
  416. // will be fixed.
  417. TrkLog(( TRKDBG_ERROR, TEXT("Too many move table counters!") ));
  418. __leave;
  419. }
  420. pEntry = ldap_first_entry(Ldap(), pRes);
  421. if(NULL == pEntry)
  422. {
  423. // This should also never happen, we already know the entry count for
  424. // this search result is 1. Again assume that when the designated DC does
  425. // a WriteCounter, this will be fixed.
  426. TrkLog(( TRKDBG_ERROR, TEXT("Entries couldn't be read from result") ));
  427. __leave;
  428. }
  429. ppbvCounter = ldap_get_values_len(Ldap(), pEntry, const_cast<TCHAR*>(s_volumeSecret) );
  430. if (ppbvCounter == NULL)
  431. {
  432. // The designated DC will fix this in WriteCounter.
  433. TrkLog(( TRKDBG_ERROR, TEXT("Move table counter is corrupt, missing %s attribute"),
  434. s_volumeSecret ));
  435. __leave;
  436. }
  437. if ((*ppbvCounter)->bv_len < sizeof(DWORD))
  438. {
  439. // The designated DC will fix this in WriteCounter
  440. TrkLog(( TRKDBG_ERROR, TEXT("Move table counter attribute %s has wrong type (%d)"),
  441. s_volumeSecret, (*ppbvCounter)->bv_len ));
  442. __leave;
  443. }
  444. memcpy( (PCHAR)pdwCounter, (*ppbvCounter)->bv_val, sizeof(DWORD) );
  445. fSuccess = TRUE;
  446. }
  447. __finally
  448. {
  449. if(NULL != pRes)
  450. {
  451. ldap_msgfree(pRes);
  452. }
  453. if (ppbvCounter != NULL)
  454. {
  455. ldap_value_free_len(ppbvCounter);
  456. }
  457. }
  458. return fSuccess;
  459. }
  460. //+----------------------------------------------------------------------------
  461. //
  462. // CQuotaTable::GetTrueCount
  463. //
  464. // Update our cached count of the move table entries. If we're the designated
  465. // DC, this will also clean up the move table: count uncounted entries, delete
  466. // deleted entries, and shorten any move chains.
  467. //
  468. // This routine can be called to run in the background or foreground. In the
  469. // background it does periodic sleeps so that we don't use up the CPU.
  470. //
  471. //+----------------------------------------------------------------------------
  472. void
  473. CQuotaTable::GetTrueCount( DWORD* pdwTrueCount,
  474. EBackgroundForegroundTask eBackgroundForegroundTask )
  475. {
  476. CLdapIdtKeyDn dnKey(GetBaseDn());
  477. int ldapRV;
  478. TCHAR* rgptszAttrs[3];
  479. TCHAR ldapSearchFilter[256];
  480. DWORD dwCounter = 0;
  481. BOOL fDoWriteCounter = FALSE;
  482. BOOL fNoExistingCounter = FALSE;
  483. TRUE_COUNT_ENUM_CONTEXT Context;
  484. __try
  485. {
  486. // Read the current counter.
  487. if( !ReadCounter( &dwCounter ) )
  488. {
  489. // There is no counter. We'll enumerate everything.
  490. TrkLog(( TRKDBG_QUOTA, TEXT("Getting move table count") ));
  491. _tcscpy( ldapSearchFilter, TEXT("(ObjectClass=*)") );
  492. fDoWriteCounter = TRUE;
  493. Context.fCountAll = TRUE;
  494. fNoExistingCounter = TRUE;
  495. }
  496. else
  497. {
  498. // Only enumerate the uncounted and/or deleted entries.
  499. TrkLog(( TRKDBG_QUOTA, TEXT("Getting delta move table count") ));
  500. _tcscpy(ldapSearchFilter, TEXT("("));
  501. _tcscat(ldapSearchFilter, s_oMTIndxGuid);
  502. _tcscat(ldapSearchFilter, TEXT("=*"));
  503. _tcscat(ldapSearchFilter, TEXT(")"));
  504. Context.fCountAll = FALSE;
  505. }
  506. rgptszAttrs[0] = const_cast<TCHAR*>(s_currentLocation);
  507. rgptszAttrs[1] = const_cast<TCHAR*>(s_birthLocation);
  508. rgptszAttrs[2] = NULL;
  509. Context.cDelta = 0;
  510. Context.dwRepetitiveTaskDelay = (BACKGROUND == eBackgroundForegroundTask)
  511. ? _pcfgsvr->GetRepetitiveTaskDelay()
  512. : 0;
  513. Context.dwPass = Context.FIRST_PASS;
  514. // Enumerate the move table, subject to the search filter determined
  515. // above.
  516. if( !LdapEnumerate( Ldap(),
  517. dnKey,
  518. LDAP_SCOPE_ONELEVEL,
  519. ldapSearchFilter,
  520. rgptszAttrs,
  521. MoveTableEnumCallback,
  522. &Context,
  523. this) )
  524. {
  525. TrkRaiseException(TRK_E_SERVICE_STOPPING);
  526. }
  527. // If we're the designated DC, we need to do a second pass.
  528. // The first pass may have done some string shortening,
  529. // and in the process marked some entries for delete. We need to
  530. // go through now and remove those entries.
  531. if( IsDesignatedDc() )
  532. {
  533. TrkLog(( TRKDBG_QUOTA, TEXT("Getting delta move table count (pass 2)") ));
  534. Context.dwPass = Context.SECOND_PASS;
  535. // We only need to count the delta this time
  536. _tcscpy(ldapSearchFilter, TEXT("("));
  537. _tcscat(ldapSearchFilter, s_oMTIndxGuid);
  538. _tcscat(ldapSearchFilter, TEXT("=*"));
  539. _tcscat(ldapSearchFilter, TEXT(")"));
  540. Context.fCountAll = FALSE;
  541. if( !LdapEnumerate( Ldap(),
  542. dnKey,
  543. LDAP_SCOPE_ONELEVEL,
  544. ldapSearchFilter,
  545. rgptszAttrs,
  546. MoveTableEnumCallback,
  547. &Context,
  548. this) )
  549. {
  550. TrkRaiseException(TRK_E_SERVICE_STOPPING);
  551. }
  552. }
  553. TrkLog((TRKDBG_QUOTA, TEXT("Uncounted entries ---- %d"), Context.cDelta));
  554. TrkLog((TRKDBG_QUOTA, TEXT("Counter ---- %d"), dwCounter));
  555. }
  556. __finally
  557. {
  558. if( Context.cDelta != 0 )
  559. fDoWriteCounter = TRUE;
  560. *pdwTrueCount = max( 0, (LONG)dwCounter + Context.cDelta );
  561. // If we're the designated DC, we may need to write the counter.
  562. // But only do so if this is a normal termination, or if there
  563. // wasn't already a counter. This covers the three cases:
  564. //
  565. // 1) The counter didn't already exist, so we were enumerating everything.
  566. // a) Normal termination, so we should update the counter
  567. // with the newly calculated value.
  568. // b) Abnormal termination, so we shouldn't update the counter.
  569. // That way we'll know to do the count again later.
  570. //
  571. // 2) The counter already existed, so we were counting the uncounted
  572. // entries. In this case, the entries were being updated to
  573. // be counted as we went through the enumeration. So whether
  574. // or not we had a normal termination, we need to updated the
  575. // counter with what we did so far.
  576. //
  577. // The only reason we expect an exception is in the case of a
  578. // service stop.
  579. if( IsDesignatedDc()
  580. &&
  581. fDoWriteCounter
  582. &&
  583. ( !AbnormalTermination() || !fNoExistingCounter ) )
  584. {
  585. WriteCounter(*pdwTrueCount);
  586. }
  587. TrkLog((TRKDBG_QUOTA, TEXT("True count ---- %d"), *pdwTrueCount));
  588. }
  589. }
  590. //+----------------------------------------------------------------------------
  591. //
  592. // CQuotaTable::OnMoveTableGcComplete
  593. //
  594. // This method is called after a GC of the move table, telling us how
  595. // many entries were deleted. We use this to update the move counter
  596. // (if it still exists).
  597. //
  598. //+----------------------------------------------------------------------------
  599. void
  600. CQuotaTable::OnMoveTableGcComplete( ULONG cEntriesDeleted )
  601. {
  602. DWORD dwCounter = 0;
  603. if( 0 == cEntriesDeleted )
  604. return;
  605. if( ReadCounter( &dwCounter ) )
  606. {
  607. TrkLog(( TRKDBG_QUOTA, TEXT("Old move counter was %d"), dwCounter ));
  608. if( dwCounter >= cEntriesDeleted )
  609. dwCounter -= cEntriesDeleted;
  610. else
  611. dwCounter = 0;
  612. WriteCounter(dwCounter);
  613. TrkLog(( TRKDBG_QUOTA, TEXT("New move counter is %d"), dwCounter ));
  614. }
  615. }
  616. void
  617. CQuotaTable::WriteCounter(DWORD dwCounter)
  618. {
  619. LDAPMod* mods[3];
  620. int ldapRV;
  621. CLdapQuotaCounterKeyDn dnKeyCounter(GetBaseDn());
  622. CLdapBinaryMod lbmCounter(s_volumeSecret, (PCHAR)&dwCounter, sizeof(DWORD), LDAP_MOD_REPLACE);
  623. mods[0] = &lbmCounter._mod;
  624. mods[1] = NULL;
  625. ldapRV = ldap_modify_s(Ldap(), dnKeyCounter, mods);
  626. if( LDAP_SUCCESS != ldapRV )
  627. {
  628. if( LDAP_NO_SUCH_OBJECT != ldapRV )
  629. {
  630. // There's some kind of problem with the existing counter.
  631. // Delete and re-create it.
  632. ldap_delete_s( Ldap(), dnKeyCounter );
  633. }
  634. CLdapStringMod lsmClass(s_objectClass, s_linkTrackVolEntry, LDAP_MOD_ADD);
  635. CLdapBinaryMod lbmCounter(s_volumeSecret, (PCHAR)&dwCounter, sizeof(DWORD), LDAP_MOD_ADD);
  636. mods[0] = &lsmClass._mod;
  637. mods[1] = &lbmCounter._mod;
  638. mods[2] = NULL;
  639. ldapRV = ldap_add_s(Ldap(), dnKeyCounter, mods);
  640. TrkLog(( TRKDBG_QUOTA, TEXT("Created counter %d, ldap returned %d"), dwCounter, ldapRV ));
  641. }
  642. else
  643. TrkLog((TRKDBG_QUOTA, TEXT("Wrote counter %d, ldap returned %d"), dwCounter, ldapRV));
  644. }
  645. HRESULT
  646. CQuotaTable::DeleteCounter()
  647. {
  648. HRESULT hr = S_OK;
  649. int LdapError = 0;
  650. CLdapQuotaCounterKeyDn dnKeyCounter(GetBaseDn());
  651. LdapError = ldap_delete_s(Ldap(), dnKeyCounter);
  652. if( LDAP_SUCCESS == LdapError )
  653. hr = S_OK;
  654. else
  655. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(LdapError) );
  656. if( FAILED(hr) )
  657. TrkLog(( TRKDBG_ERROR, TEXT("Couldn't delete move-table counter (%08x)"), hr ));
  658. else
  659. TrkLog(( TRKDBG_QUOTA, TEXT("Deleted move-table counter") ));
  660. return( hr );
  661. }
  662. //+----------------------------------------------------------------------------
  663. //
  664. // CQuotaTable::ValidateCache
  665. //
  666. // Validate the cached move and volume counts. They are valid if they exist
  667. // and are newer than the max time-to-live. If they aren't valid, they are
  668. // re-calculated. They are also recalculated if the caller sets the
  669. // fForceHint parameter, and the cache is at least of minimum age.
  670. //
  671. //+----------------------------------------------------------------------------
  672. void
  673. CQuotaTable::ValidateCache( BOOL fForceHint )
  674. {
  675. // How old is the cache in seconds?
  676. DWORD dwDelta = ( (LONGLONG)CFILETIME() - (LONGLONG)_cftCacheLastUpdated ) / 10000000;
  677. // The cache should be updated if any of the following are true
  678. if( 0 == _cftCacheLastUpdated // We have no cached values
  679. ||
  680. dwDelta > _pcfgsvr->GetCacheMaxTimeToLive() // The cached values are too old
  681. ||
  682. // The cached values are old enough, and the
  683. // the caller wants us to be aggressive.
  684. fForceHint && (dwDelta > _pcfgsvr->GetCacheMinTimeToLive()) )
  685. {
  686. // Yes, we need to update the cached values.
  687. CLdapVolumeKeyDn dnKey(GetBaseDn());
  688. TCHAR* rgptszAttrs[2];
  689. DWORD cVolumeTableEntries = 0;
  690. TrkLog(( TRKDBG_QUOTA, TEXT("Updating quota caches (%s)"),
  691. fForceHint ? TEXT("forced") : TEXT("not forced") ));
  692. // Get the true count of move table entries.
  693. GetTrueCount( (DWORD*) &_lCachedMoveTableCount, FOREGROUND );
  694. TrkAssert( _lCachedMoveTableCount >= 0 );
  695. // Get the count of volume table entries
  696. rgptszAttrs[0] = const_cast<TCHAR*>(s_Cn);
  697. rgptszAttrs[1] = NULL;
  698. if( !LdapEnumerate( Ldap(),
  699. dnKey,
  700. LDAP_SCOPE_ONELEVEL,
  701. TEXT("(&(ObjectClass=*)(!(cn=QT*)))"),
  702. rgptszAttrs,
  703. VolumeTableEnumCallback,
  704. &cVolumeTableEntries,
  705. this) )
  706. {
  707. TrkRaiseException( TRK_E_SERVICE_STOPPING );
  708. }
  709. _lCachedVolumeTableCount = (LONG) cVolumeTableEntries;
  710. // Calculate the move table limit
  711. _dwMoveLimit = CalculateMoveLimit();
  712. // Show that the cache is up-to-date
  713. _cftCacheLastUpdated.SetToUTC();
  714. }
  715. TrkLog(( TRKDBG_QUOTA, TEXT("Cache: MoveCount=%d, MoveLimit=%d, VolCount=%d"),
  716. _lCachedMoveTableCount, _dwMoveLimit, _lCachedVolumeTableCount ));
  717. }
  718. DWORD
  719. CQuotaTable::CalculateMoveLimit() // Doesn't raise
  720. {
  721. DWORD dwMoveLimit = 0;
  722. LONG lVolumeCount = max( 0, _lCachedVolumeTableCount );
  723. if( lVolumeCount <= _pcfgsvr->GetMoveLimitTransition() )
  724. dwMoveLimit = lVolumeCount * _pcfgsvr->GetMoveLimitPerVolumeLower();
  725. else
  726. {
  727. dwMoveLimit = _pcfgsvr->GetMoveLimitTransition()
  728. *
  729. _pcfgsvr->GetMoveLimitPerVolumeLower();
  730. dwMoveLimit += ( lVolumeCount - _pcfgsvr->GetMoveLimitTransition() )
  731. *
  732. _pcfgsvr->GetMoveLimitPerVolumeUpper();
  733. }
  734. return( dwMoveLimit );
  735. }
  736. HRESULT
  737. CQuotaTable::ReadFlags(LDAP* pLdap, TCHAR* dnKey, BYTE* bFlags)
  738. {
  739. struct berval** ppbvFlags = NULL;
  740. TCHAR* rgptszAttrs[2];
  741. LDAPMessage* pRes = NULL;
  742. int ldapRV;
  743. int cEntries;
  744. LDAPMessage* pEntry = NULL;
  745. HRESULT hr = S_OK;
  746. __try
  747. {
  748. *bFlags = 0x0;
  749. rgptszAttrs[0] = const_cast<TCHAR*>(s_oMTIndxGuid);
  750. rgptszAttrs[1] = NULL;
  751. ldapRV = ldap_search_s(pLdap,
  752. dnKey,
  753. LDAP_SCOPE_BASE,
  754. TEXT("(ObjectClass=*)"),
  755. rgptszAttrs,
  756. 0,
  757. &pRes);
  758. hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(ldapRV));
  759. if(ldapRV != LDAP_SUCCESS)
  760. __leave;
  761. cEntries = ldap_count_entries(pLdap, pRes);
  762. if(cEntries != 1)
  763. {
  764. // This shouldn't happen. The caller asked for flags on an entry
  765. // which doesn't exist.
  766. TrkLog(( TRKDBG_ERROR, TEXT("ReadFlags, entry doesn't exist: %s"), dnKey ));
  767. hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(LDAP_NO_SUCH_OBJECT));
  768. __leave;
  769. }
  770. pEntry = ldap_first_entry(pLdap, pRes);
  771. if(NULL == pEntry)
  772. {
  773. // This should never happen. We already know that there's an entry.
  774. TrkLog(( TRKDBG_ERROR, TEXT("ReadFlags, couldn't get first entry on %s"), dnKey ));
  775. hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(LDAP_NO_SUCH_OBJECT));
  776. __leave;
  777. }
  778. ppbvFlags = ldap_get_values_len(pLdap, pEntry, const_cast<TCHAR*>(s_oMTIndxGuid) );
  779. if(NULL == ppbvFlags)
  780. {
  781. hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(LDAP_NO_SUCH_ATTRIBUTE));
  782. __leave;
  783. }
  784. if( (*ppbvFlags)->bv_len < sizeof(BYTE)
  785. ||
  786. ((*ppbvFlags)->bv_val == NULL) )
  787. {
  788. // The best we can do is pretend the attribute doesn't exist.
  789. TrkLog(( TRKDBG_ERROR, TEXT("ReadFlags, attribute is wrong type or missing (%d/%p)"),
  790. (*ppbvFlags)->bv_len, (*ppbvFlags)->bv_val ));
  791. hr = HRESULT_FROM_WIN32(LdapMapErrorToWin32(LDAP_NO_SUCH_ATTRIBUTE));
  792. __leave;
  793. }
  794. *bFlags = *(BYTE*)(*ppbvFlags)->bv_val;
  795. hr = S_OK;
  796. }
  797. __finally
  798. {
  799. if(pRes != NULL)
  800. {
  801. ldap_msgfree(pRes);
  802. }
  803. if(ppbvFlags != NULL)
  804. {
  805. ldap_value_free_len(ppbvFlags);
  806. }
  807. }
  808. return hr;
  809. }
  810. void
  811. CQuotaTable::DeleteFlags(LDAP* pLdap, TCHAR* dnKey)
  812. {
  813. BYTE bFlags = 0x0;
  814. int err;
  815. Lock();
  816. __try
  817. {
  818. CLdapBinaryMod lbm(s_oMTIndxGuid, NULL, 0, LDAP_MOD_DELETE );//reinterpret_cast<PCCH>(&bFlags), sizeof(BYTE), LDAP_MOD_DELETE);
  819. LDAPMod* mods[2];
  820. mods[0] = &lbm._mod;
  821. mods[1] = NULL;
  822. err = ldap_modify_s(pLdap, dnKey, mods);
  823. TrkLog((TRKDBG_QUOTA, TEXT("Deleted flag %x on entry %s, ldap returned %d"), bFlags, dnKey, err));
  824. }
  825. __finally
  826. {
  827. Unlock();
  828. }
  829. }
  830. // TRUE => Found, FALSE => Not found, Raise otherwise
  831. BOOL
  832. CQuotaTable::UpdateFlags(LDAP* pLdap, TCHAR* dnKey, BYTE bFlags)
  833. {
  834. BOOL fFound = FALSE;
  835. BYTE bOrigFlags;
  836. HRESULT hr;
  837. LDAPMod* mods[2];
  838. int err;
  839. TrkAssert(!(bFlags & QFLAG_UNCOUNTED && bFlags & QFLAG_DELETED));
  840. Lock();
  841. __try
  842. {
  843. hr = ReadFlags(pLdap, dnKey, &bOrigFlags);
  844. if(hr == (HRESULT) HRESULT_FROM_WIN32(LdapMapErrorToWin32(LDAP_NO_SUCH_ATTRIBUTE)))
  845. {
  846. CLdapBinaryMod lbm(s_oMTIndxGuid, reinterpret_cast<PCCH>(&bFlags), sizeof(BYTE), LDAP_MOD_ADD);
  847. mods[0] = &lbm._mod;
  848. mods[1] = NULL;
  849. err = ldap_modify_s(pLdap, dnKey, mods);
  850. TrkLog((TRKDBG_QUOTA, TEXT("Added flag %01x on entry %s, ldap returned %d"), bFlags, dnKey, err));
  851. }
  852. else if(hr == S_OK)
  853. {
  854. if(bOrigFlags == QFLAG_UNCOUNTED && bFlags == QFLAG_DELETED)
  855. {
  856. bOrigFlags = bOrigFlags | bFlags;
  857. CLdapBinaryMod lbm(s_oMTIndxGuid, reinterpret_cast<PCCH>(&bOrigFlags), sizeof(BYTE), LDAP_MOD_REPLACE);
  858. mods[0] = &lbm._mod;
  859. mods[1] = NULL;
  860. err = ldap_modify_s(pLdap, dnKey, mods);
  861. TrkLog((TRKDBG_QUOTA, TEXT("Updated flag to %01x on entry %s, ldap returned %d"), bOrigFlags, dnKey, err));
  862. }
  863. else if(bOrigFlags == QFLAG_DELETED && bFlags == QFLAG_UNCOUNTED)
  864. {
  865. DeleteFlags(pLdap, dnKey);
  866. }
  867. }
  868. else
  869. {
  870. __leave;
  871. }
  872. fFound = TRUE;
  873. }
  874. __finally
  875. {
  876. Unlock();
  877. }
  878. return( fFound );
  879. }
  880. //+----------------------------------------------------------------------------
  881. //
  882. // CQuotaTable::DeleteOrphanedEntries
  883. //
  884. // This routine is given a list of move table entries. The birth entry has
  885. // been updated by the caller to point to the final entry. As a result, all
  886. // the other entries are orphaned and must be deleted.
  887. //
  888. //+----------------------------------------------------------------------------
  889. BOOL
  890. CQuotaTable::DeleteOrphanedEntries( const CDomainRelativeObjId rgdroidList[], ULONG cSegments,
  891. const CDomainRelativeObjId &droidBirth,
  892. const CDomainRelativeObjId &droidCurrent )
  893. {
  894. BOOL fCurrentNeedsToBeDeleted = FALSE;
  895. for( int j=0; j < cSegments; j++ )
  896. {
  897. // Don't do anything if this is the birth entry
  898. if( rgdroidList[j] != droidBirth )
  899. {
  900. // If this is the current entry we don't delete it, but tell the
  901. // caller about it.
  902. if( rgdroidList[j] == droidCurrent )
  903. {
  904. fCurrentNeedsToBeDeleted = TRUE;
  905. }
  906. // Otherwise, we delete it directly
  907. else if( _pidt->Delete( rgdroidList[j] ) )
  908. {
  909. TrkLog((TRKDBG_QUOTA,
  910. TEXT("Orphaned segment deleted %s"),
  911. static_cast<const TCHAR*>(CAbbreviatedIDString(rgdroidList[j])) ));
  912. }
  913. else
  914. {
  915. TrkLog((TRKDBG_QUOTA|TRKDBG_WARNING,
  916. TEXT("Orphaned segment failed to delete %s"),
  917. static_cast<const TCHAR*>(CAbbreviatedIDString(rgdroidList[j])) ));
  918. }
  919. }
  920. }
  921. return( fCurrentNeedsToBeDeleted );
  922. }
  923. //+----------------------------------------------------------------------------
  924. //
  925. // CQuotaTable::ShortenString
  926. //
  927. // Given an entry in the move table, see if it is part of a string that
  928. // needs to be shortened, and if so do the shortening.
  929. //
  930. //+----------------------------------------------------------------------------
  931. void
  932. CQuotaTable::ShortenString( LDAP* pLdap, LDAPMessage* pMessage, BYTE *pbFlags,
  933. const CDomainRelativeObjId &droidCurrent )
  934. {
  935. CDomainRelativeObjId droidBirth;
  936. CDomainRelativeObjId droidScan;
  937. CDomainRelativeObjId droidNext;
  938. CDomainRelativeObjId rgdroidList[MAX_SHORTENABLE_SEGMENTS];
  939. int cSegments = 0;
  940. TCHAR *ptszCurrentCN = NULL;
  941. __try
  942. {
  943. // Attempt to read the entry and get its birth ID.
  944. if( _pidt->Query( pLdap, pMessage, droidCurrent, &droidNext, &droidBirth ) )
  945. {
  946. // We have a successful read.
  947. BOOL fStringDeleted = FALSE;
  948. // Scan forward from that birth to see if we can find multiple entries.
  949. droidScan = droidBirth;
  950. _psvrsvc->Scan( NULL, NULL, droidBirth,
  951. rgdroidList, ELEMENTS(rgdroidList), &cSegments,
  952. &droidScan, &fStringDeleted );
  953. // If this is a multi-segment string, reduce it to a single segment.
  954. if( cSegments > 1 )
  955. {
  956. // Was the last segment of the string deleted?
  957. if( fStringDeleted )
  958. {
  959. // Yes, that means that the entired string has been deleted.
  960. TrkLog(( TRKDBG_QUOTA, TEXT("Deleting string starting at %s"),
  961. static_cast<const TCHAR*>(CAbbreviatedIDString(droidBirth)) ));
  962. if( droidCurrent == droidBirth )
  963. *pbFlags |= QFLAG_DELETED;
  964. else
  965. _pidt->Delete( droidBirth );
  966. }
  967. // Otherwise, map from the birth to the last droid.
  968. else if( !_pidt->Modify( droidBirth, droidScan, droidBirth ))
  969. {
  970. TrkLog(( TRKDBG_QUOTA|TRKDBG_WARNING, TEXT("Couldn't shorten %s -> %s"),
  971. static_cast<const TCHAR*>(CAbbreviatedIDString(droidBirth)),
  972. static_cast<const TCHAR*>(CAbbreviatedIDString(droidScan)) ));
  973. __leave;
  974. }
  975. else
  976. {
  977. TrkLog(( TRKDBG_QUOTA, TEXT("Shortened %s -> %s [%s]"),
  978. static_cast<const TCHAR*>(CAbbreviatedIDString(droidBirth)),
  979. static_cast<const TCHAR*>(CAbbreviatedIDString(droidScan)),
  980. static_cast<const TCHAR*>(CAbbreviatedIDString(droidBirth)) ));
  981. }
  982. // The birth entry has been shortened, or deleted. So we can delete the
  983. // rest of the entries.
  984. if( DeleteOrphanedEntries( rgdroidList, cSegments,
  985. droidBirth, // Don't delete this one
  986. droidCurrent // Or this one
  987. ))
  988. {
  989. // Amongst the orphans in need of a delete is the current
  990. // entry. Set the deleted flag, and the caller will take
  991. // care of removing the entry.
  992. TrkLog(( TRKDBG_QUOTA, TEXT("Current is orphaned and will be deleted") ));
  993. *pbFlags |= QFLAG_DELETED;
  994. }
  995. } // if( cSegments > 1 )
  996. }
  997. }
  998. __except( BreakOnDebuggableException() )
  999. {
  1000. TrkLog(( TRKDBG_QUOTA, TEXT("Ignoring exception from Scan in FlaggedEntriesEnumCallback (%08x)"),
  1001. GetExceptionCode() ));
  1002. }
  1003. if( NULL != ptszCurrentCN )
  1004. ldap_memfree(ptszCurrentCN);
  1005. }
  1006. //+----------------------------------------------------------------------------
  1007. //
  1008. // CQuotaTable::MoveTableEnumCallback
  1009. //
  1010. // When the move table is enumerated, this routine is passed to LdapEnumerate
  1011. // as the callback function. If we're the designated DC, this function
  1012. // takes care of uncounted/deleted entries and shortens move table strings.
  1013. // Whether or not we're the designated DC, we update the count value,
  1014. // according to the uncounted/deleted entries, in the pvContext structure.
  1015. //
  1016. //+----------------------------------------------------------------------------
  1017. ENUM_ACTION // static
  1018. CQuotaTable::MoveTableEnumCallback(LDAP* pLdap, LDAPMessage* pMessage, void* pvContext, void* pvThis )
  1019. {
  1020. struct berval ** ppbvFlags = NULL;
  1021. BYTE bFlags = 0;
  1022. ENUM_ACTION action = ENUM_KEEP_ENTRY;
  1023. TRUE_COUNT_ENUM_CONTEXT *pContext = (TRUE_COUNT_ENUM_CONTEXT*) pvContext;
  1024. CQuotaTable *pThis = (CQuotaTable*)pvThis;
  1025. TCHAR *ptszCurrentDN = NULL;
  1026. CDomainRelativeObjId droidCurrent;
  1027. if( pThis->_fStopping )
  1028. return( ENUM_ABORT );
  1029. __try
  1030. {
  1031. // Read the quota flags for this entry. We can't read them out of the
  1032. // enumeration buffer, because the flags can get updated by the enumeration
  1033. // and the enumeration buffer doesn't reflect the change.
  1034. ptszCurrentDN = ldap_get_dn( pLdap, pMessage );
  1035. if (ptszCurrentDN == NULL)
  1036. {
  1037. TrkLog((TRKDBG_ERROR, TEXT("Couldn't get DN") ));
  1038. TrkRaiseLastError();
  1039. }
  1040. droidCurrent.ReadLdapIdtKeyBuffer(ptszCurrentDN);
  1041. TrkLog(( TRKDBG_QUOTA, TEXT("Enumerating %s"),
  1042. static_cast<const TCHAR*>(CAbbreviatedIDString(droidCurrent)) ));
  1043. pThis->ReadFlags(pLdap, ptszCurrentDN, &bFlags );
  1044. // Count this entry if we're counting everything, or if we're only
  1045. // counting flagged values and this one is flagged as uncounted.
  1046. if( pContext->fCountAll || (bFlags & QFLAG_UNCOUNTED) )
  1047. pContext->cDelta++;
  1048. // If this is uncounted and we're the designated DC, then it's
  1049. // counted now and we can delete the flags attribute.
  1050. if( (bFlags & QFLAG_UNCOUNTED) && pThis->IsDesignatedDc() )
  1051. action = ENUM_DELETE_QUOTAFLAGS;
  1052. // If we're the designated DC, we can do some additional cleanup here.
  1053. // It's because of this work that we need two passes; one to do the
  1054. // cleanup, and one to delete the entries that this cleanup marked
  1055. // for deletion (by calling _pidt->Delete).
  1056. if( pThis->IsDesignatedDc() && pContext->FIRST_PASS == pContext->dwPass )
  1057. {
  1058. pThis->ShortenString( pLdap, pMessage, &bFlags, droidCurrent );
  1059. }
  1060. // If this entry is marked for delete, decrement the count.
  1061. // Note that if the entry was marked uncounted and deleted,
  1062. // we incremented at the top and will decrement here for the
  1063. // correct change of zero.
  1064. if( bFlags & QFLAG_DELETED )
  1065. {
  1066. pContext->cDelta--;
  1067. // If we're the designated DC, we can delete the entry.
  1068. if( pThis->IsDesignatedDc() )
  1069. action = ENUM_DELETE_ENTRY;
  1070. }
  1071. }
  1072. __finally
  1073. {
  1074. if(ppbvFlags != NULL)
  1075. {
  1076. ldap_value_free_len(ppbvFlags);
  1077. }
  1078. if( NULL != ptszCurrentDN )
  1079. ldap_memfree( ptszCurrentDN );
  1080. }
  1081. // Be nice to the DS
  1082. if( 0 != pContext->dwRepetitiveTaskDelay )
  1083. Sleep( pContext->dwRepetitiveTaskDelay );
  1084. return action;
  1085. }
  1086. ENUM_ACTION // static
  1087. CQuotaTable::VolumeTableEnumCallback( LDAP * pLdap, LDAPMessage * pResult, void* pcEntries, void* pvThis )
  1088. {
  1089. CQuotaTable* pThis = (CQuotaTable*)pvThis;
  1090. if( pThis->_fStopping )
  1091. return( ENUM_ABORT );
  1092. (*((DWORD*)pcEntries))++;
  1093. return ENUM_KEEP_ENTRY;
  1094. }
  1095. void
  1096. CQuotaTable::Statistics( TRKSVR_STATISTICS *pTrkSvrStatistics )
  1097. {
  1098. pTrkSvrStatistics->dwMoveLimit = _dwMoveLimit;
  1099. pTrkSvrStatistics->dwCachedVolumeTableCount = (DWORD) _lCachedVolumeTableCount;
  1100. pTrkSvrStatistics->dwCachedMoveTableCount = (DWORD) _lCachedMoveTableCount;
  1101. pTrkSvrStatistics->ftCacheLastUpdated = _cftCacheLastUpdated;
  1102. pTrkSvrStatistics->fIsDesignatedDc = IsDesignatedDc();
  1103. }