Leaked source code of windows server 2003
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.

1550 lines
46 KiB

  1. // Copyright (c) 1996-1999 Microsoft Corporation
  2. //+-------------------------------------------------------------------------
  3. //
  4. // Microsoft Windows
  5. //
  6. // File: voltab.cxx
  7. //
  8. // Contents: volumes table
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. //
  15. //
  16. // History: 16-Dec-96 MikeHill Created.
  17. // 23-Jan-97 BillMo Added support for synchronizing clients
  18. // after a DC restore.
  19. //
  20. // Notes: There are two sequence numbers that pertain to each volume.
  21. //
  22. // The first sequence number is used to synchronize the machine
  23. // move logs with the object move table. This sequence number
  24. // is relevant to QueryVolume, ClaimVolume, GetVolumeInfo.
  25. //
  26. // The second sequence number is used to synchronize the
  27. // automatic backup of the volume to machine table.
  28. //
  29. // Codework:
  30. //
  31. //--------------------------------------------------------------------------
  32. #include "pch.cxx"
  33. #pragma hdrstop
  34. #include "trksvr.hxx"
  35. #include <commctrl.h>
  36. #include <time.h>
  37. #define MAX_MACHINE_BUF_CHARS 256
  38. TCHAR s_RestoreVolumes[] = TEXT("Software\\Microsoft\\LinkTrack\\RestoreVolumes");
  39. class CLdapTimeVolChange
  40. {
  41. public:
  42. CLdapTimeVolChange()
  43. {
  44. memset(_abPad,0,sizeof(_abPad));
  45. }
  46. CLdapTimeVolChange(const CFILETIME &cft)
  47. {
  48. _cft = cft;
  49. memset(_abPad,0,sizeof(_abPad));
  50. }
  51. void Swap();
  52. inline BYTE & Byte(int i)
  53. {
  54. return( ((BYTE*)this)[i] );
  55. }
  56. CFILETIME _cft;
  57. BYTE _abPad[sizeof(GUID) - sizeof(CFILETIME)];
  58. };
  59. void
  60. CLdapTimeVolChange::Swap()
  61. {
  62. BYTE b;
  63. for (int i=0; i<sizeof(*this)/2; i++)
  64. {
  65. b = Byte(i);
  66. Byte(i) = Byte(sizeof(*this)-i-1);
  67. Byte(sizeof(*this)-i-1) = b;
  68. }
  69. }
  70. class CLdapSecret
  71. {
  72. public:
  73. CLdapSecret()
  74. {
  75. memset(_abPad,0,sizeof(_abPad));
  76. }
  77. CLdapSecret(const CVolumeSecret &secret)
  78. {
  79. _secret = secret;
  80. memset(_abPad,0,sizeof(_abPad));
  81. }
  82. CVolumeSecret _secret;
  83. BYTE _abPad[sizeof(GUID) - sizeof(CVolumeSecret)];
  84. };
  85. void
  86. CVolumeTable::Initialize(CTrkSvrConfiguration *pconfigSvr, CQuotaTable* pqtable)
  87. {
  88. _fInitializeCalled = TRUE;
  89. _pqtable = pqtable;
  90. _pconfigSvr = pconfigSvr;
  91. #ifdef VOL_REPL
  92. // Can raise an NTSTATUS so put before fInitializeCalled=TRUE
  93. InitializeCriticalSection(&_csQueryCache);
  94. if (pwm != NULL)
  95. {
  96. //
  97. // Initialize the cache immediately ready for client queries
  98. // Service start time, query may take a while. Dependency on ldap being available.
  99. // => Make this lazy.
  100. //
  101. _SecondsPreviousToNow = _pconfigSvr->GetVolumeQueryPeriod()
  102. * _pconfigSvr->GetVolumeQueryPeriods();
  103. _cftCacheLowest.SetToUTC();
  104. _cftCacheLowest.DecrementSeconds( _SecondsPreviousToNow );
  105. // search for all changes since now-period*numperiods (may throw on out of memory)
  106. _QueryVolumeChanges( _cftCacheLowest, &_VolMap );
  107. // timer should go off in about 6 hrs
  108. CFILETIME cft;
  109. cft.IncrementSeconds(VolumeQueryPeriodSeconds);
  110. _timerQueryCache.Initialize(this, pwm, 0, VolumeQueryPeriodSeconds, &cft);
  111. }
  112. #endif
  113. }
  114. void
  115. CVolumeTable::UnInitialize()
  116. {
  117. if (_fInitializeCalled)
  118. {
  119. _fInitializeCalled = FALSE;
  120. #ifdef VOL_REPL
  121. _timerQueryCache.UnInitialize();
  122. DeleteCriticalSection(&_csQueryCache);
  123. _VolMap.UnInitialize();
  124. #endif
  125. }
  126. }
  127. #ifdef VOL_REPL
  128. void
  129. CVolumeTable::Timer( DWORD dwTimerId )
  130. {
  131. // redo the query - will leave _VolMap unchanged on error
  132. // On low memory exception we should retry the timer.
  133. Raise if stopped
  134. CFILETIME cftHighest;
  135. CFILETIME cft;
  136. cft.DecrementSeconds( _SecondsPreviousToNow );
  137. CVolumeMap VolMap;
  138. SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
  139. TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE, TEXT("Volume table cache timer")));
  140. // search for all changes since now-period*numperiods (may throw on out of memory)
  141. _QueryVolumeChanges( cft, &VolMap );
  142. EnterCriticalSection(&_csQueryCache);
  143. _cftCacheLowest = cft;
  144. VolMap.MoveTo(&_VolMap);
  145. LeaveCriticalSection(&_csQueryCache);
  146. }
  147. #if DBG
  148. void
  149. CVolumeTable::PurgeCache()
  150. {
  151. EnterCriticalSection(&_csQueryCache);
  152. _cftCacheLowest = CFILETIME(0);
  153. _VolMap.UnInitialize();
  154. LeaveCriticalSection(&_csQueryCache);
  155. }
  156. #endif
  157. #endif
  158. HRESULT
  159. CVolumeTable::MapResult(int err) const
  160. {
  161. if (err == LDAP_SUCCESS)
  162. {
  163. return(S_OK);
  164. }
  165. else
  166. if (err == LDAP_NO_SUCH_OBJECT)
  167. {
  168. return(TRK_S_VOLUME_NOT_FOUND);
  169. }
  170. else
  171. {
  172. TrkRaiseWin32Error(LdapMapErrorToWin32(err));
  173. return(TRK_S_VOLUME_NOT_FOUND);
  174. }
  175. }
  176. HRESULT
  177. CVolumeTable::AddVolidToTable( const CVolumeId & volume,
  178. const CMachineId & mcidClient,
  179. const CVolumeSecret & secret )
  180. {
  181. CVolumeId volidZero;
  182. CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
  183. CLdapStringMod lsmClass(s_objectClass, s_linkTrackVolEntry, LDAP_MOD_ADD);
  184. CLdapSecret ls(secret);
  185. CLdapBinaryMod lbmVolumeSecret(s_volumeSecret, (PCHAR)&ls, sizeof(ls), LDAP_MOD_ADD);
  186. CLdapBinaryMod lbmMachineId(s_currMachineId, (PCHAR)&mcidClient, sizeof(mcidClient), LDAP_MOD_ADD);
  187. CLdapSeqNum lsn;
  188. CLdapStringMod lsmSequence(s_seqNotification, lsn, LDAP_MOD_ADD );
  189. CLdapTimeValue ltv; // current time
  190. CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_ADD);
  191. // When writing the zero volume (the refresh sequence number itself), write a 0 for the
  192. // sequence number. (Calling _pRefreshSequenceStorage would cause an infinite loop).
  193. CLdapRefresh ltvRefresh( volidZero == volume ? 0 : _pRefreshSequenceStorage->GetSequenceNumber() );
  194. CLdapStringMod lsmRefresh(s_timeRefresh, ltvRefresh, LDAP_MOD_ADD);
  195. LDAPMod * mods[7];
  196. mods[0] = &lsmClass._mod;
  197. mods[1] = &lbmVolumeSecret._mod;
  198. mods[2] = &lbmMachineId._mod;
  199. mods[3] = &lsmSequence._mod;
  200. mods[4] = &lsmTimeVolChange._mod;
  201. // Don't write a refresh time for the null volume (this entry is used
  202. // to store the current global refresh counter).
  203. if( CVolumeId() != volume )
  204. {
  205. mods[5] = &lsmRefresh._mod;
  206. mods[6] = 0;
  207. }
  208. else
  209. mods[5] = 0;
  210. int err = ldap_add_s( Ldap(), dnKey, mods );
  211. if( LDAP_ALREADY_EXISTS == err )
  212. {
  213. ldap_delete_s( Ldap(), dnKey );
  214. err = ldap_add_s( Ldap(), dnKey, mods );
  215. }
  216. if( LDAP_SUCCESS == err )
  217. _pqtable->IncrementVolumeCountCache();
  218. else
  219. TrkLog(( TRKDBG_ERROR, TEXT("Failed AddVolidToDs (%d)"), err ));
  220. return MapResult(err);
  221. }
  222. HRESULT
  223. CVolumeTable::PreCreateVolume( const CMachineId & mcidClient,
  224. const CVolumeSecret & secret,
  225. ULONG cUncountedVolumes,
  226. CVolumeId * pvolume )
  227. {
  228. int err;
  229. RPC_STATUS Status;
  230. CMachineId mcidZero(MCID_INVALID);
  231. ULONG cVolumes = 0;
  232. if(mcidClient != mcidZero && _pqtable->IsVolumeQuotaExceeded(mcidClient, cUncountedVolumes))
  233. {
  234. TrkLog((TRKDBG_ERROR, TEXT("Volume quota exceeded for %s"),
  235. (const TCHAR*) CDebugString(mcidClient) ));
  236. return TRK_E_VOLUME_QUOTA_EXCEEDED;
  237. }
  238. Status = pvolume->UuidCreate();
  239. if (Status != RPC_S_OK )
  240. {
  241. // Since we use the randomized-guid generation algorithm,
  242. // we should never get a local guid.
  243. TrkAssert( RPC_S_UUID_LOCAL_ONLY != Status );
  244. TrkRaiseWin32Error(Status);
  245. }
  246. return S_OK;
  247. }
  248. HRESULT
  249. CVolumeTable::QueryVolume( const CMachineId & mcidClient,
  250. const CVolumeId & volume,
  251. SequenceNumber * pseq,
  252. FILETIME * pftLastRefresh )
  253. {
  254. HRESULT hr;
  255. CMachineId mcidTable;
  256. SequenceNumber seq;
  257. CVolumeSecret secret;
  258. CFILETIME cftRefresh(0);
  259. if (volume == CVolumeId())
  260. {
  261. TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
  262. }
  263. hr = GetVolumeInfo(volume, &mcidTable, &secret, &seq, &cftRefresh );
  264. if (S_OK == hr)
  265. {
  266. // if (its not the right machine), or (it is the right machine and a nul secret)
  267. if (mcidTable != mcidClient || secret == CVolumeSecret())
  268. {
  269. return(TRK_S_VOLUME_NOT_OWNED);
  270. }
  271. *pseq = seq;
  272. *pftLastRefresh = cftRefresh;
  273. }
  274. return(hr);
  275. }
  276. HRESULT
  277. CVolumeTable::FindVolume( const CVolumeId & volume, CMachineId * pmcid )
  278. {
  279. HRESULT hr;
  280. CMachineId mcidTable;
  281. SequenceNumber seq;
  282. CVolumeSecret secret;
  283. CFILETIME cftRefresh(0);
  284. if (volume == CVolumeId())
  285. {
  286. TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
  287. }
  288. hr = GetVolumeInfo(volume, &mcidTable, &secret, &seq, &cftRefresh );
  289. if (S_OK == hr)
  290. {
  291. *pmcid = mcidTable;
  292. }
  293. return(hr);
  294. }
  295. #if DBG
  296. void
  297. CVolumeTable::PurgeAll()
  298. {
  299. int err;
  300. TCHAR *apszAttrs[2] = { TEXT("cn"), NULL };
  301. LDAPMessage * pRes;
  302. TCHAR tszVolumeTable[MAX_PATH+1];
  303. __try
  304. {
  305. _tcscpy(tszVolumeTable, s_VolumeTableRDN);
  306. _tcscat(tszVolumeTable, GetBaseDn());
  307. err = ldap_search_s( Ldap(),
  308. tszVolumeTable,
  309. LDAP_SCOPE_ONELEVEL,
  310. TEXT("(objectclass=*)"),
  311. apszAttrs,
  312. 0, // attribute types and values are wanted
  313. &pRes );
  314. if (err == LDAP_SUCCESS)
  315. {
  316. // found it, lets get the attributes out
  317. int cEntries = ldap_count_entries(Ldap(), pRes);
  318. LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
  319. if (pEntry != NULL)
  320. {
  321. do
  322. {
  323. TCHAR * ptszDn = ldap_get_dn(Ldap(), pEntry);
  324. int errd = ldap_delete_s(Ldap(),ptszDn);
  325. TrkLog((TRKDBG_ERROR, TEXT("Purged %s status %d"), ptszDn, errd));
  326. ldap_memfree(ptszDn);
  327. } while ( pEntry = ldap_next_entry(Ldap(), pEntry));
  328. }
  329. }
  330. }
  331. __finally
  332. {
  333. if (err == LDAP_SUCCESS)
  334. {
  335. ldap_msgfree(pRes);
  336. }
  337. }
  338. }
  339. #endif
  340. HRESULT
  341. CVolumeTable::ClaimVolume( const CMachineId & mcidClient,
  342. const CVolumeId & volume,
  343. const CVolumeSecret & secretOld,
  344. const CVolumeSecret & secretNew,
  345. SequenceNumber * pseq,
  346. FILETIME * pftLastRefresh )
  347. {
  348. HRESULT hr;
  349. CMachineId mcidTable;
  350. SequenceNumber seq;
  351. CVolumeSecret secretCurrent;
  352. CVolumeSecret nullSecret;
  353. CFILETIME cftRefresh(0);
  354. if (volume == CVolumeId())
  355. {
  356. TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
  357. }
  358. hr = GetVolumeInfo( volume, &mcidTable, &secretCurrent, &seq, &cftRefresh );
  359. if ( S_OK == hr )
  360. {
  361. hr = TRK_E_VOLUME_ACCESS_DENIED;
  362. if( mcidTable == mcidClient )
  363. hr = SetSecret( volume, secretNew );
  364. else if( secretOld == secretCurrent)
  365. hr = SetMachineAndSecret( volume, mcidClient, secretNew );
  366. }
  367. if ( S_OK == hr )
  368. {
  369. *pseq = seq;
  370. }
  371. TrkAssert( hr == TRK_E_VOLUME_ACCESS_DENIED ||
  372. hr == S_OK ||
  373. hr == TRK_S_VOLUME_NOT_FOUND );
  374. return(hr);
  375. }
  376. //+----------------------------------------------------------------------------
  377. //
  378. // CVolumeTable::DeleteVolume
  379. //
  380. // Delete an entry from the volume table, but only if the volume is owned
  381. // by the calling machine.
  382. //
  383. //+----------------------------------------------------------------------------
  384. HRESULT
  385. CVolumeTable::DeleteVolume( const CMachineId & mcidClient,
  386. const CVolumeId & volume )
  387. {
  388. HRESULT hr;
  389. int LdapError;
  390. CMachineId mcidTable;
  391. SequenceNumber seq;
  392. CVolumeSecret secret;
  393. CLdapVolumeKeyDn dnVolume(GetBaseDn(), volume);
  394. CFILETIME cftRefresh(0);
  395. if (volume == CVolumeId())
  396. {
  397. TrkRaiseException( TRK_E_INVALID_VOLUME_ID );
  398. }
  399. hr = GetVolumeInfo( volume, &mcidTable, &secret, &seq, &cftRefresh );
  400. if ( S_OK == hr )
  401. {
  402. if( mcidTable == mcidClient )
  403. {
  404. TrkLog(( TRKDBG_VOLTAB, TEXT("Deleting volume %s"),
  405. (const TCHAR*) CDebugString(volume) ));
  406. LdapError = ldap_delete_s(Ldap(), dnVolume);
  407. if( LDAP_SUCCESS == LdapError )
  408. {
  409. hr = S_OK;
  410. _pqtable->DecrementVolumeCountCache();
  411. }
  412. else
  413. hr = HRESULT_FROM_WIN32( LdapMapErrorToWin32(LdapError) );
  414. }
  415. else
  416. hr = TRK_E_VOLUME_ACCESS_DENIED;
  417. }
  418. #if DBG
  419. if( FAILED(hr) )
  420. TrkLog(( TRKDBG_ERROR, TEXT("Failed attempt to delete volume %s"),
  421. (const TCHAR*) CDebugString(volume) ));
  422. #endif
  423. return(hr);
  424. }
  425. HRESULT
  426. CVolumeTable::GetMachine(const CVolumeId & volume, CMachineId * pmcid)
  427. {
  428. CVolumeSecret secret;
  429. SequenceNumber seq;
  430. CFILETIME cftRefresh(0);
  431. return GetVolumeInfo( volume, pmcid, &secret, &seq, &cftRefresh );
  432. }
  433. HRESULT
  434. CVolumeTable::SetMachine(const CVolumeId & volume, const CMachineId & mcid)
  435. {
  436. CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
  437. CLdapTimeValue ltv; // Defaults to current UTC
  438. //ltvc.Swap();
  439. CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_REPLACE);
  440. CLdapBinaryMod lbmMachineId(s_currMachineId, (PCHAR)&mcid, sizeof(mcid), LDAP_MOD_REPLACE);
  441. LDAPMod * mods[3];
  442. HRESULT hr;
  443. int err;
  444. mods[0] = &lbmMachineId._mod;
  445. mods[1] = &lsmTimeVolChange._mod;
  446. mods[2] = NULL;
  447. err = ldap_modify_s(Ldap(), dnKey, mods);
  448. hr = MapResult(err);
  449. return(hr);
  450. }
  451. HRESULT
  452. CVolumeTable::SetSecret(const CVolumeId & volume, const CVolumeSecret & secret)
  453. {
  454. CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
  455. CLdapTimeValue ltv; // Defaults to current UTC
  456. CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_REPLACE);
  457. CLdapSecret ls(secret);
  458. CLdapBinaryMod lbmVolumeSecret(s_volumeSecret, (PCHAR)&ls, sizeof(ls), LDAP_MOD_REPLACE);
  459. LDAPMod * mods[3];
  460. HRESULT hr;
  461. int err;
  462. mods[0] = &lbmVolumeSecret._mod;
  463. mods[1] = &lsmTimeVolChange._mod;
  464. mods[2] = NULL;
  465. err = ldap_modify_s(Ldap(), dnKey, mods);
  466. hr = MapResult(err);
  467. return(hr);
  468. }
  469. HRESULT
  470. CVolumeTable::SetMachineAndSecret(const CVolumeId & volume, const CMachineId & mcid, const CVolumeSecret & secret)
  471. {
  472. CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
  473. CLdapTimeValue ltv; // Defaults to current UTC
  474. CLdapStringMod lsmTimeVolChange(s_timeVolChange, ltv, LDAP_MOD_REPLACE);
  475. CLdapBinaryMod lbmMachineId(s_currMachineId, (PCHAR)&mcid, sizeof(mcid), LDAP_MOD_REPLACE);
  476. CLdapSecret ls(secret);
  477. CLdapBinaryMod lbmVolumeSecret(s_volumeSecret, (PCHAR)&ls, sizeof(ls), LDAP_MOD_REPLACE);
  478. LDAPMod * mods[4];
  479. HRESULT hr;
  480. int err;
  481. mods[0] = &lbmMachineId._mod;
  482. mods[1] = &lbmVolumeSecret._mod;
  483. mods[2] = &lsmTimeVolChange._mod;
  484. mods[3] = NULL;
  485. err = ldap_modify_s(Ldap(), dnKey, mods);
  486. hr = MapResult(err);
  487. return(hr);
  488. }
  489. //+----------------------------------------------------------------------------
  490. //
  491. // CVolumeTable::SetSequenceNumber
  492. //
  493. // Set the sequence number of a volume entry. This is the value
  494. // of we expect to get in the next move-notification for this volume.
  495. // (This is used to detect if the trksvr & trkwks get out of sync.)
  496. //
  497. //+----------------------------------------------------------------------------
  498. HRESULT
  499. CVolumeTable::SetSequenceNumber(const CVolumeId & volume, SequenceNumber seq)
  500. {
  501. int err;
  502. HRESULT hr;
  503. CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
  504. CLdapSeqNum lsn(seq);
  505. CLdapStringMod lsmSequence(s_seqNotification, lsn, LDAP_MOD_REPLACE );
  506. LDAPMod * mods[2];
  507. // Set up the MODs array.
  508. mods[0] = &lsmSequence._mod;
  509. mods[1] = 0;
  510. // Perform the modification.
  511. err = ldap_modify_s(Ldap(), dnKey, mods);
  512. // Debug output
  513. #if DBG
  514. if( LDAP_SUCCESS != err )
  515. TrkLog(( TRKDBG_SVR, TEXT("Couldn't set sequence number (%d)"), err ));
  516. else
  517. TrkLog(( TRKDBG_SVR, TEXT("Set seq %d on %s"), seq,
  518. (const TCHAR*) CDebugString(volume) ));
  519. #endif
  520. // Map back to an HRESULT
  521. hr = MapResult(err);
  522. return(hr);
  523. }
  524. HRESULT
  525. CVolumeTable::GetVolumeInfo( const CVolumeId & volume,
  526. CMachineId * pmcid,
  527. CVolumeSecret * psecret,
  528. SequenceNumber * pseq,
  529. CFILETIME *pcftRefresh )
  530. {
  531. // lookup the volume and get the current machine and sequence number if any
  532. HRESULT hr;
  533. int err;
  534. LDAPMessage * pRes = NULL;
  535. CLdapVolumeKeyDn dnKey(GetBaseDn(), volume);
  536. struct berval ** ppbvMachineId = NULL;
  537. //struct berval ** ppbvSeq = NULL;
  538. TCHAR ** pptszSeq = NULL;
  539. struct berval ** ppbvSecret = NULL;
  540. TCHAR ** pptszRefresh = NULL;
  541. TCHAR * apszAttrs[5];
  542. __try
  543. {
  544. apszAttrs[0] = const_cast<TCHAR*>(s_currMachineId);
  545. apszAttrs[1] = const_cast<TCHAR*>(s_seqNotification);
  546. apszAttrs[2] = const_cast<TCHAR*>(s_volumeSecret);
  547. apszAttrs[3] = const_cast<TCHAR*>(s_timeRefresh);
  548. apszAttrs[4] = 0;
  549. err = ldap_search_s( Ldap(),
  550. dnKey,
  551. LDAP_SCOPE_BASE,
  552. TEXT("(objectclass=*)"),
  553. apszAttrs,
  554. 0, // attribute types and values are wanted
  555. &pRes );
  556. hr = MapResult(err);
  557. if (S_OK == hr)
  558. {
  559. // found it, lets get the attributes out
  560. int cEntries;
  561. cEntries = ldap_count_entries(Ldap(), pRes);
  562. // Get the entry from the search results.
  563. if (cEntries < 1)
  564. {
  565. TrkLog(( TRKDBG_ERROR, TEXT("GetVolumeInfo: ldap_search for %s succeeded, but with %d entries"),
  566. (TCHAR*) dnKey /*CDebugString(volume)._tsz*/, cEntries ));
  567. hr = MapResult(LDAP_NO_SUCH_OBJECT);
  568. __leave;
  569. }
  570. LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
  571. if (pEntry == NULL)
  572. {
  573. // This should also never happen. We know at this point that we have
  574. // 1 entry in the search result. We'll pretend that it doesn't exist.
  575. TrkLog(( TRKDBG_ERROR, TEXT("GetVolumeInfo: ldap_search has one entry, but it couldn't be retrieved") ));
  576. hr = MapResult(LDAP_NO_SUCH_OBJECT);
  577. __leave;
  578. }
  579. // Get the machine ID attribute.
  580. ppbvMachineId = ldap_get_values_len(Ldap(), pEntry, const_cast<TCHAR*>(s_currMachineId) );
  581. if( NULL == ppbvMachineId
  582. ||
  583. sizeof(CMachineId) > (*ppbvMachineId)->bv_len )
  584. {
  585. // This entry is corrupt, there should always be a mcid attribute.
  586. // We'll pretend it doesn't exist for now, and let GC clean it up.
  587. hr = MapResult(LDAP_NO_SUCH_OBJECT);
  588. __leave;
  589. }
  590. memcpy( pmcid, (*ppbvMachineId)->bv_val, sizeof(*pmcid) );
  591. // Get the volume secret attribute
  592. ppbvSecret = ldap_get_values_len(Ldap(), pEntry, const_cast<TCHAR*>(s_volumeSecret) );
  593. if( NULL == ppbvSecret
  594. ||
  595. sizeof(CLdapSecret) > (*ppbvSecret)->bv_len )
  596. {
  597. // This entry is corrupt, there should always be a secret attribute.
  598. // We'll pretend it doesn't exist for now, and let GC clean it up.
  599. hr = MapResult(LDAP_NO_SUCH_OBJECT);
  600. __leave;
  601. }
  602. memcpy( psecret, (*ppbvSecret)->bv_val, sizeof(*psecret) );
  603. // Get the sequence number attribute
  604. *pseq = 0;
  605. pptszSeq = ldap_get_values(Ldap(), pEntry, const_cast<TCHAR*>(s_seqNotification) );
  606. if (NULL == pptszSeq || CCH_UINT32 < _tcslen(*pptszSeq))
  607. {
  608. // The sequence number is missing or invalid. We'll just assume it's zero.
  609. TrkLog((TRKDBG_ERROR, TEXT("Sequence number string too long in vol table (vol=%s)"),
  610. (const TCHAR*) CDebugString(volume) ));
  611. }
  612. else if( 1 != _stscanf( *pptszSeq, TEXT("%d"), pseq ))
  613. {
  614. // Again, assume the sequnce number is zero.
  615. TrkLog((TRKDBG_ERROR, TEXT("Invalid sequence number string in vol table (seq=%s, vol=%s)"),
  616. *pptszSeq,
  617. (const TCHAR*) CDebugString(volume) ));
  618. *pseq = 0;
  619. }
  620. // Get the refresh counter, if it exists.
  621. *pcftRefresh = 0;
  622. pptszRefresh = ldap_get_values( Ldap(), pEntry, const_cast<TCHAR*>(s_timeRefresh) );
  623. if( NULL != pptszRefresh )
  624. {
  625. SequenceNumber seqRefresh = 0;
  626. if( 1 == _stscanf( *pptszRefresh, TEXT("%d"), &seqRefresh ))
  627. *pcftRefresh = seqRefresh;
  628. }
  629. }
  630. }
  631. __finally
  632. {
  633. if (NULL != pRes)
  634. {
  635. ldap_msgfree(pRes);
  636. }
  637. if (ppbvMachineId != NULL)
  638. {
  639. ldap_value_free_len(ppbvMachineId);
  640. }
  641. if (pptszSeq != NULL)
  642. {
  643. ldap_value_free(pptszSeq);
  644. }
  645. if (ppbvSecret != NULL)
  646. {
  647. ldap_value_free_len(ppbvSecret);
  648. }
  649. if (pptszRefresh != NULL)
  650. ldap_value_free(pptszRefresh);
  651. if (AbnormalTermination())
  652. {
  653. TrkLog(( TRKDBG_ERROR, TEXT("Exception in CVolumeTable::GetVolumeInfo") ));
  654. }
  655. }
  656. return(hr);
  657. }
  658. // TRUE if exists and touched, FALSE if not existent, exception otherwise.
  659. // BUGBUG P2: check ownership of entry being touched.
  660. BOOL
  661. CVolumeTable::Touch(
  662. const CVolumeId & volid
  663. )
  664. {
  665. if (volid == CVolumeId())
  666. {
  667. TrkLog(( TRKDBG_ERROR, TEXT("Null volid passed to CVolumeTable::Touch") ));
  668. return( FALSE );
  669. }
  670. BOOL fReturn = FALSE;
  671. int err;
  672. CLdapRefresh ltvRefresh( _pRefreshSequenceStorage->GetSequenceNumber());
  673. CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_REPLACE );
  674. CLdapVolumeKeyDn
  675. dnKey(GetBaseDn(), volid);
  676. LDAPMod * mods[2];
  677. TCHAR ** pptszRefresh = NULL;
  678. LDAPMessage * pEntry = NULL;
  679. LDAPMessage* pRes = NULL;
  680. __try
  681. {
  682. //
  683. // Check to see if the object already has this sequence number.
  684. //
  685. TCHAR* rgptszAttrs[2];
  686. rgptszAttrs[0] = const_cast<TCHAR*>(s_timeRefresh);
  687. rgptszAttrs[1] = NULL;
  688. err = ldap_search_s(Ldap(),
  689. dnKey,
  690. LDAP_SCOPE_BASE,
  691. TEXT("(ObjectClass=*)"),
  692. rgptszAttrs,
  693. 0,
  694. &pRes);
  695. if (err == LDAP_SUCCESS)
  696. {
  697. // The search call worked, but did we find an object?
  698. if( 1 == ldap_count_entries(Ldap(), pRes) )
  699. {
  700. // The object already exists
  701. pEntry = ldap_first_entry(Ldap(), pRes);
  702. if( NULL != pEntry )
  703. {
  704. // Get the refresh counter
  705. pptszRefresh = ldap_get_values( Ldap(), pEntry, const_cast<TCHAR*>(s_timeRefresh) );
  706. if( NULL != pptszRefresh )
  707. {
  708. SequenceNumber seqRefresh = 0;
  709. if( 1 == _stscanf( *pptszRefresh, TEXT("%d"), &seqRefresh ))
  710. {
  711. // First, long is the GC timer in seconds?
  712. LONG lGCTimerInSeconds = _pconfigSvr->GetGCPeriod() // 30 days in seconds
  713. / _pconfigSvr->GetGCDivisor(); // 30
  714. // Next, how many ticks is half the period?
  715. LONG lWindow = _pconfigSvr->GetGCPeriod() // 30 days (in seconds)
  716. / 2 // => 15 days (in seconds)
  717. / lGCTimerInSeconds; // => 15
  718. if( seqRefresh + lWindow
  719. >= _pRefreshSequenceStorage->GetSequenceNumber()
  720. )
  721. {
  722. TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_VOLTAB,
  723. TEXT("Not touching volume %s with %d, seq %d already set"),
  724. (const TCHAR*) CDebugString(volid),
  725. _pRefreshSequenceStorage->GetSequenceNumber(),
  726. seqRefresh ));
  727. __leave;
  728. }
  729. }
  730. }
  731. }
  732. }
  733. }
  734. else if (err == LDAP_NO_SUCH_OBJECT)
  735. {
  736. TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_VOLTAB,
  737. TEXT("Touch: volume %s not found"),
  738. (const TCHAR*) CDebugString(volid)));
  739. __leave;
  740. }
  741. //
  742. // Set the correct sequence number
  743. //
  744. mods[0] = &lsmRefresh._mod;
  745. mods[1] = NULL;
  746. err = ldap_modify_s(Ldap(), dnKey, mods);
  747. if (err == LDAP_SUCCESS)
  748. {
  749. TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
  750. TEXT("Touch: volume %s touched"),
  751. (const TCHAR*) CDebugString(volid)));
  752. fReturn = TRUE;
  753. __leave;
  754. }
  755. else
  756. if (err == LDAP_NO_SUCH_OBJECT)
  757. {
  758. TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
  759. TEXT("Touch:: volume %s doesn't exist"),
  760. (const TCHAR*) CDebugString(volid)));
  761. __leave;
  762. }
  763. else
  764. if (err == LDAP_NO_SUCH_ATTRIBUTE)
  765. {
  766. TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
  767. TEXT("Touch: volume %s attribute not found"),
  768. (const TCHAR*) CDebugString(volid)));
  769. // deal with old server data
  770. CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_ADD );
  771. mods[0] = &lsmRefresh._mod;
  772. err = ldap_modify_s(Ldap(), dnKey, mods);
  773. }
  774. if (err != LDAP_SUCCESS)
  775. {
  776. TrkLog((TRKDBG_GARBAGE_COLLECT | TRKDBG_SVR,
  777. TEXT("Touch:: volume %s gives exceptional error"),
  778. (const TCHAR*) CDebugString(volid)));
  779. __leave;
  780. }
  781. }
  782. __except( BreakOnDebuggableException() )
  783. {
  784. TrkLog(( TRKDBG_ERROR, TEXT("Ignoring exception in CVolumeTable::Touch (%08x)"), GetExceptionCode() ));
  785. }
  786. if (pptszRefresh != NULL)
  787. ldap_value_free(pptszRefresh);
  788. if(pRes != NULL)
  789. ldap_msgfree(pRes);
  790. return( fReturn );
  791. }
  792. //+----------------------------------------------------------------------------
  793. //
  794. // CVolumeTable::GarbageCollect
  795. //
  796. // This is called by CTrkSvrSvc when it's time to GC the volume table (daily).
  797. // The entries are enumerated, and if too old they are deleted.
  798. //
  799. //+----------------------------------------------------------------------------
  800. ULONG
  801. CVolumeTable::GarbageCollect( SequenceNumber seqCurrent, SequenceNumber seqOldestToKeep, const BOOL * pfAbort )
  802. {
  803. CLdapVolumeKeyDn dn(GetBaseDn());
  804. TCHAR * apszAttrs[3];
  805. GC_ENUM_CONTEXT EnumContext;
  806. TrkLog(( TRKDBG_VOLTAB | TRKDBG_GARBAGE_COLLECT, TEXT("GC-ing volume table (%d/%d)"),
  807. seqCurrent, seqOldestToKeep ));
  808. // Set up the attributes for the ldap_search_init_page call.
  809. apszAttrs[0] = const_cast<TCHAR*>(s_Cn);
  810. apszAttrs[1] = const_cast<TCHAR*>(s_timeRefresh);
  811. apszAttrs[2] = 0;
  812. // Set up all the info that the LdapEnumerate call needs.
  813. memset( &EnumContext, 0, sizeof(EnumContext) );
  814. EnumContext.seqOldestToKeep = seqOldestToKeep;
  815. EnumContext.seqCurrent = seqCurrent;
  816. EnumContext.pfAbort = pfAbort;
  817. EnumContext.dwRepetitiveTaskDelay = _pconfigSvr->GetRepetitiveTaskDelay();
  818. EnumContext.pqtable = _pqtable;
  819. // Do an ldap_search, calling GcEnumerateCallback for each of the
  820. // returned values.
  821. if (!LdapEnumerate(
  822. Ldap(), // LDAP handle
  823. dn, // Base DN
  824. LDAP_SCOPE_ONELEVEL, // No recursion
  825. TEXT("(objectClass=*)"), // Filter
  826. apszAttrs, // Attributes (get CN & refresh time)
  827. GcEnumerateCallback, // Called for each iteration
  828. &EnumContext )) // Info for GcEnuemrateCallback
  829. {
  830. TrkRaiseException(TRK_E_SERVICE_STOPPING);
  831. }
  832. TrkLog(( TRKDBG_GARBAGE_COLLECT | TRKDBG_IDT,
  833. TEXT("GC-ed %d entries from the volume table"),
  834. EnumContext.cEntries ));
  835. // If we actually deleted anything, the cached values
  836. // in the quota object are no longer valid. Mark it as
  837. // such, so that it will know to re-generate it the next
  838. // time it's needed.
  839. if( 0 != EnumContext.cEntries )
  840. _pqtable->InvalidateCache();
  841. return EnumContext.cEntries;
  842. }
  843. ENUM_ACTION
  844. GcEnumerateCallback( LDAP * pLdap, LDAPMessage *pMessage, PVOID pvContext, PVOID )
  845. {
  846. GC_ENUM_CONTEXT * pContext = (GC_ENUM_CONTEXT *) pvContext;
  847. TCHAR * ptszDn = NULL;
  848. TCHAR ** pptszValue = NULL;
  849. ENUM_ACTION Action = ENUM_KEEP_ENTRY;
  850. ULONG ulSequence = 0;
  851. // See if we should abort. We shouldn't even be here if we're not
  852. // the designated DC. The only way it can happen is if the designated
  853. // DC is changed during the enumeration.
  854. if( *(pContext->pfAbort)
  855. ||
  856. !pContext->pqtable->IsDesignatedDc() )
  857. {
  858. Action = ENUM_ABORT;
  859. goto Exit;
  860. }
  861. // Get the DN of this entry so that we can check for special entries.
  862. ptszDn = ldap_get_dn( pLdap, pMessage );
  863. if (ptszDn == NULL)
  864. {
  865. TrkLog((TRKDBG_GARBAGE_COLLECT,
  866. TEXT("Couldn't get DN during GcEnumerateCallback") ));
  867. goto Exit;
  868. }
  869. // CQuotaTable stores special values in the volume table, all prefixed by "QT".
  870. // Don't delete those.
  871. if( !_tcsnicmp( TEXT("CN=QT"), ptszDn, 5 ))
  872. {
  873. TrkLog(( TRKDBG_GARBAGE_COLLECT,
  874. TEXT("Skipping quota entry in GC (%s)"),
  875. ptszDn ));
  876. goto Exit;
  877. }
  878. // The current value of the Refresh counter is stored in volume ID 0. So
  879. // don't delete that either.
  880. if( !_tcsnicmp( TEXT("CN=00000000000000000000000000000000,"), ptszDn, 35 ))
  881. {
  882. TrkLog(( TRKDBG_GARBAGE_COLLECT,
  883. TEXT("Skipping volid 0 GC (%s)"),
  884. ptszDn ));
  885. goto Exit;
  886. }
  887. // Get the refresh time value.
  888. pptszValue = ldap_get_values( pLdap, pMessage, const_cast<TCHAR*>(s_timeRefresh) );
  889. if (pptszValue == NULL)
  890. {
  891. TrkLog((TRKDBG_GARBAGE_COLLECT,
  892. TEXT("Can't find sequence number in %s"),
  893. ptszDn));
  894. // This is a corrupted entry that will never get GC-ed,
  895. // so we'll delete it now.
  896. pContext->cEntries++;
  897. Action = ENUM_DELETE_ENTRY;
  898. goto Exit;
  899. }
  900. _stscanf( *pptszValue, TEXT("%d"), &ulSequence );
  901. // Determine if we should delete this entry.
  902. if( ulSequence < (ULONG)pContext->seqOldestToKeep )
  903. {
  904. Action = ENUM_DELETE_ENTRY;
  905. pContext->cEntries++;
  906. }
  907. else
  908. Action = ENUM_KEEP_ENTRY;
  909. #if DBG
  910. if( ENUM_DELETE_ENTRY == Action )
  911. TrkLog(( TRKDBG_QUOTA, TEXT("Seq to delete: %d/%d"),
  912. ulSequence, (ULONG)pContext->seqOldestToKeep ));
  913. #endif
  914. // Check to see if the entry has an invalid sequence number. It's
  915. // invalid if it's bigger than the current value. This can happen if the
  916. // special zero entry gets deleted from the volume table for some reason.
  917. if( ulSequence > (ULONG)pContext->seqCurrent )
  918. {
  919. // Reset the entry's sequence number to the current value. Otherwise it
  920. // could be a very long time before it gets GCed. This case should never
  921. // happen, but there's no guarantee that someone won't delete the
  922. // entry accidentally.
  923. CLdapRefresh ltvRefresh( pContext->seqCurrent );
  924. CLdapStringMod lsmRefresh( s_timeRefresh, ltvRefresh, LDAP_MOD_REPLACE );
  925. int err;
  926. LDAPMod * mods[2];
  927. mods[0] = &lsmRefresh._mod;
  928. mods[1] = NULL;
  929. err = ldap_modify_s( pLdap, ptszDn, mods );
  930. TrkLog(( TRKDBG_SVR | TRKDBG_GARBAGE_COLLECT,
  931. TEXT("Touched entry with invalid sequence number (%d, %s)"),
  932. ulSequence, ptszDn ));
  933. }
  934. Exit:
  935. if( NULL != pptszValue )
  936. ldap_value_free( pptszValue );
  937. if( NULL != ptszDn )
  938. ldap_memfree( ptszDn );
  939. // Be nice to the DS
  940. if( 0 != pContext->dwRepetitiveTaskDelay )
  941. Sleep( pContext->dwRepetitiveTaskDelay );
  942. return( Action );
  943. }
  944. // returns FALSE if aborted
  945. BOOL
  946. LdapEnumerate(
  947. LDAP * pLdap,
  948. TCHAR * ptszBaseDn,
  949. ULONG Scope,
  950. TCHAR * Filter,
  951. TCHAR * Attributes[],
  952. PFN_LDAP_ENUMERATE_CALLBACK pCallback,
  953. void* UserParam1,
  954. void* UserParam2)
  955. {
  956. LDAPMessage * pResults;
  957. LDAPSearch * pSearch;
  958. ENUM_ACTION EnumAction = ENUM_KEEP_ENTRY;
  959. // Start a paged enumeration using the specified base DN & filter.
  960. pSearch = ldap_search_init_page( pLdap,
  961. ptszBaseDn,
  962. Scope,
  963. Filter,
  964. Attributes,
  965. FALSE,
  966. NULL,
  967. NULL,
  968. 0,
  969. 20000,
  970. NULL );
  971. if (pSearch != NULL)
  972. {
  973. int err;
  974. ULONG totalCount;
  975. // Get the next page of the enumeration
  976. while ( EnumAction != ENUM_ABORT &&
  977. LDAP_SUCCESS == (err = ldap_get_next_page_s( pLdap,
  978. pSearch,
  979. NULL,
  980. 10,
  981. &totalCount,
  982. &pResults ) && pResults != NULL))
  983. {
  984. LDAPMessage * pMessage;
  985. LDAPMessage * pFirstMessage;
  986. // Loop through the entries on this page.
  987. pFirstMessage = pMessage = ldap_first_entry( pLdap, pResults );
  988. while ( EnumAction != ENUM_ABORT
  989. &&
  990. pMessage != NULL )
  991. {
  992. // Call the callback to process this entry.
  993. EnumAction = (*pCallback)(
  994. pLdap,
  995. pMessage,
  996. UserParam1,
  997. UserParam2);
  998. if ( EnumAction == ENUM_DELETE_ENTRY )
  999. {
  1000. // This entry is to be deleted. Increment the entry
  1001. // count, and if we're not just counting, actually delete it.
  1002. TCHAR * ptszDn = ldap_get_dn( pLdap, pMessage );
  1003. if (ptszDn != NULL)
  1004. {
  1005. TrkLog((TRKDBG_ERROR, TEXT("Deleting Dn=%s"), ptszDn));
  1006. ldap_delete_s( pLdap, ptszDn );
  1007. ldap_memfree( ptszDn );
  1008. }
  1009. }
  1010. else if(EnumAction == ENUM_KEEP_ENTRY)
  1011. {
  1012. }
  1013. else if(EnumAction == ENUM_DELETE_QUOTAFLAGS)
  1014. {
  1015. TCHAR* ptszDn = ldap_get_dn(pLdap, pMessage);
  1016. if(NULL != ptszDn)
  1017. {
  1018. if(UserParam2)
  1019. {
  1020. ((CQuotaTable*)UserParam2)->DeleteFlags(pLdap, ptszDn);
  1021. }
  1022. }
  1023. }
  1024. pMessage = ldap_next_entry( pLdap, pMessage );
  1025. }
  1026. ldap_msgfree( pResults );
  1027. if (pFirstMessage == NULL)
  1028. break;
  1029. }
  1030. ldap_search_abandon_page( pLdap, pSearch );
  1031. }
  1032. return(EnumAction != ENUM_ABORT);
  1033. }
  1034. #ifdef VOL_REPL
  1035. void
  1036. CVolumeTable::QueryVolumeChanges( const CFILETIME & cftFirstChange, CVolumeMap * pVolMap )
  1037. {
  1038. // protect against the data changing under us
  1039. BOOL fCacheHit;
  1040. __try
  1041. {
  1042. EnterCriticalSection(&_csQueryCache);
  1043. if (fCacheHit = _cftCacheLowest <= cftFirstChange)
  1044. {
  1045. TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
  1046. TEXT("CVolumeTable::QueryVolumeChanges(cftFirstChange=%s) HIT, returning %d change entries from cache"),
  1047. (const TCHAR*) CDebugString(cftFirstChange),
  1048. _VolMap.Count()));
  1049. _VolMap.CopyTo( pVolMap );
  1050. }
  1051. }
  1052. _finally
  1053. {
  1054. LeaveCriticalSection(&_csQueryCache);
  1055. }
  1056. if (!fCacheHit)
  1057. {
  1058. // the cache was missed... go to the real database.
  1059. CFILETIME cftHighest;
  1060. _QueryVolumeChanges( cftFirstChange, pVolMap );
  1061. TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
  1062. TEXT("CVolumeTable::QueryVolumeChanges(cftFirstChange=%s) MISS, returning %d entries from full query"),
  1063. (const TCHAR*) CDebugString(cftFirstChange),
  1064. pVolMap->Count()));
  1065. }
  1066. }
  1067. #endif
  1068. // if there are more than zero volume entries, then pVolMap->SetSize and pVolMap->Add will be called,
  1069. // otherwise pVolMap will not be called and so must be initialized by the caller
  1070. #ifdef VOL_REPL
  1071. void
  1072. CVolumeTable::_QueryVolumeChanges( const CFILETIME & FirstChangeRequested,
  1073. CVolumeMap * pVolMap )
  1074. {
  1075. // lookup the volume and get the current machine and sequence number if any
  1076. int err;
  1077. LDAPMessage * pRes = NULL;
  1078. CLdapVolumeKeyDn dnKey(GetBaseDn());
  1079. struct berval ** ppbvMachineId = NULL;
  1080. TCHAR ** pptszCnVolumeId = NULL;
  1081. TCHAR szSearchFilter[256];
  1082. TCHAR * apszAttrs[4];
  1083. HRESULT hr;
  1084. CLdapTimeValue ltv(FirstChangeRequested);
  1085. __try
  1086. {
  1087. //
  1088. // Build up a search filter looking for objects with timeVolChange >= FirstChangeRequested
  1089. //
  1090. // (timeVolChange=XXX)
  1091. //
  1092. _tcscpy(szSearchFilter, s_timeVolChangeSearch);
  1093. _tcscat(szSearchFilter, ltv);
  1094. _tcscat(szSearchFilter, TEXT(")"));
  1095. //
  1096. // Build up the list of attributes to query
  1097. //
  1098. apszAttrs[0] = s_currMachineId;
  1099. apszAttrs[1] = s_Cn;
  1100. apszAttrs[2] = 0;
  1101. err = ldap_search_s( Ldap(),
  1102. dnKey,
  1103. LDAP_SCOPE_ONELEVEL,
  1104. szSearchFilter,
  1105. apszAttrs,
  1106. 0, // attribute types and values are wanted
  1107. &pRes );
  1108. hr = MapResult(err);
  1109. //
  1110. // Depending on whether DS sets up a maximum query size, we will need to iterate
  1111. // performing multiple searches. Initially just use a single query.
  1112. //
  1113. if (hr == S_OK)
  1114. {
  1115. // found it, lets get the attributes out
  1116. int cEntries = ldap_count_entries(Ldap(), pRes);
  1117. if (cEntries != 0)
  1118. {
  1119. pVolMap->SetSize(cEntries);
  1120. LDAPMessage * pEntry = ldap_first_entry(Ldap(), pRes);
  1121. if (pEntry != NULL)
  1122. {
  1123. do
  1124. {
  1125. //
  1126. // for each entry get the
  1127. // volume id from the CN
  1128. // machine id
  1129. // time of last volume change
  1130. //
  1131. pptszCnVolumeId = ldap_get_values(Ldap(), pEntry, s_Cn);
  1132. if (pptszCnVolumeId == NULL)
  1133. {
  1134. TrkRaiseException(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
  1135. }
  1136. if (_tcslen(*pptszCnVolumeId) != 32) // length of stringized volume id
  1137. {
  1138. // Add code to recover from this.
  1139. TrkRaiseException(TRK_E_CORRUPT_VOLTAB);
  1140. }
  1141. ppbvMachineId = ldap_get_values_len(Ldap(), pEntry, s_currMachineId);
  1142. if (ppbvMachineId == NULL)
  1143. {
  1144. TrkRaiseException(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
  1145. }
  1146. if ((*ppbvMachineId)->bv_len < sizeof(CMachineId))
  1147. {
  1148. // Add code to recover from this
  1149. TrkRaiseException(TRK_E_CORRUPT_VOLTAB);
  1150. }
  1151. CVolumeId volume( *pptszCnVolumeId, TRK_E_CORRUPT_VOLTAB );
  1152. CMachineId machine( (*ppbvMachineId)->bv_val,
  1153. (*ppbvMachineId)->bv_len,
  1154. TRK_E_CORRUPT_VOLTAB );
  1155. pVolMap->Add( volume, machine );
  1156. ldap_value_free(pptszCnVolumeId);
  1157. pptszCnVolumeId = NULL;
  1158. ldap_value_free_len(ppbvMachineId);
  1159. ppbvMachineId = NULL;
  1160. } while ( pEntry = ldap_next_entry(Ldap(), pEntry));
  1161. }
  1162. pVolMap->Compact();
  1163. }
  1164. }
  1165. }
  1166. __finally
  1167. {
  1168. if (pRes != NULL)
  1169. {
  1170. ldap_msgfree(pRes);
  1171. }
  1172. if (pptszCnVolumeId != NULL)
  1173. {
  1174. ldap_value_free(pptszCnVolumeId);
  1175. }
  1176. if (ppbvMachineId != NULL)
  1177. {
  1178. ldap_value_free_len(ppbvMachineId);
  1179. }
  1180. }
  1181. TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
  1182. TEXT("_QueryVolumeChanges(filter=%s) got %d changes since %s"),
  1183. szSearchFilter,
  1184. pVolMap->Count(),
  1185. (const TCHAR*) CDebugString(FirstChangeRequested)));
  1186. }
  1187. #endif
  1188. // raises on error, returns number of volumes on this machine
  1189. DWORD
  1190. CVolumeTable::CountVolumes( const CMachineId & mcid )
  1191. {
  1192. // lookup the volume and get the current machine and sequence number if any
  1193. int err;
  1194. LDAPMessage * pRes = NULL;
  1195. CLdapVolumeKeyDn dnKey(GetBaseDn());
  1196. TCHAR szSearchFilter[256];
  1197. TCHAR * pszAppend;
  1198. TCHAR * aptszAttrs[2];
  1199. HRESULT hr;
  1200. DWORD cVolumes = 0;
  1201. __try
  1202. {
  1203. //
  1204. // Build up a search filter looking for objects with currMachineId == mcid
  1205. //
  1206. // (volTableIdxGUID;binary=XXX)
  1207. //
  1208. _tcscpy(szSearchFilter, s_currMachineIdSearch);
  1209. pszAppend = szSearchFilter + _tcslen(szSearchFilter);
  1210. // mcid.Stringize(pszAppend);
  1211. mcid.StringizeAsGuid(pszAppend);
  1212. *pszAppend++ = TEXT(')');
  1213. *pszAppend++ = TEXT('\0');
  1214. TrkAssert(_tcslen(szSearchFilter)+1 < ELEMENTS(szSearchFilter));
  1215. //
  1216. // Build up the list of attributes to query
  1217. // If we ever update to allow large numbers of volumes on a machine,
  1218. // this should be a paged enumeration.
  1219. //
  1220. aptszAttrs[0] = const_cast<TCHAR*>(s_Cn);
  1221. aptszAttrs[1] = 0;
  1222. err = ldap_search_s( Ldap(),
  1223. dnKey,
  1224. LDAP_SCOPE_ONELEVEL,
  1225. szSearchFilter,
  1226. aptszAttrs,
  1227. 0, // attribute types and values are wanted
  1228. &pRes );
  1229. hr = MapResult(err);
  1230. if (hr == S_OK)
  1231. {
  1232. // found it, lets get the attributes out
  1233. cVolumes = ldap_count_entries(Ldap(), pRes);
  1234. }
  1235. }
  1236. __finally
  1237. {
  1238. if (pRes != NULL)
  1239. {
  1240. ldap_msgfree(pRes);
  1241. }
  1242. }
  1243. TrkLog((TRKDBG_VOLTAB | TRKDBG_VOLTAB_RESTORE,
  1244. TEXT("CountVolumes(filter=%s) got %d volumes"),
  1245. szSearchFilter,
  1246. cVolumes));
  1247. return(cVolumes);
  1248. }