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.

707 lines
15 KiB

  1. //***************************************************************************
  2. //
  3. // (c) 2000-2001 by Microsoft Corp. All Rights Reserved.
  4. //
  5. // INDEX.CPP
  6. //
  7. // 24-Oct-00 raymcc Integration layer to disk-based B-Tree
  8. //
  9. //***************************************************************************
  10. #include <wbemcomn.h>
  11. #include <reposit.h>
  12. #include "a51tools.h"
  13. #include "index.h"
  14. #include "btr.h"
  15. //***************************************************************************
  16. //
  17. //***************************************************************************
  18. //
  19. //#define DEBUG
  20. static CFlexArray g_aIterators;
  21. class CIteratorBatch
  22. {
  23. CFlexArray m_aStrings;
  24. BOOL m_bDone;
  25. DWORD m_dwCursor;
  26. public:
  27. CIteratorBatch();
  28. ~CIteratorBatch();
  29. BOOL Purge(LPSTR pszTarget);
  30. BOOL Add(LPSTR pszSrc); // Acquires memory
  31. void SetDone() { m_bDone = TRUE; }
  32. BOOL Next(LPSTR *pString);
  33. static DWORD PurgeAll(LPSTR pszDoomed);
  34. };
  35. //***************************************************************************
  36. //
  37. // CIteratorBatch::PurgeAll
  38. //
  39. //
  40. // Purges all iterators of a particular string. This happens when
  41. // a DeleteKey succeeds while there are outstanding enumerators; we want
  42. // to remove the key from all enumerators so that deleted objects
  43. // are not reported.
  44. //
  45. // This is required because the enumerators do "prefetch" and may
  46. // have been invoked considerably in advance of the delete
  47. //
  48. // Assumes prior concurrency control.
  49. //
  50. //***************************************************************************
  51. // ok
  52. DWORD CIteratorBatch::PurgeAll(LPSTR pszDoomed)
  53. {
  54. DWORD dwTotal = 0;
  55. for (int i = 0; i < g_aIterators.Size(); i++)
  56. {
  57. CIteratorBatch *p = (CIteratorBatch *) g_aIterators[i];
  58. BOOL bRes = p->Purge(pszDoomed);
  59. if (bRes)
  60. dwTotal++;
  61. }
  62. return dwTotal;
  63. }
  64. //***************************************************************************
  65. //
  66. // CIteratorBatch::CIteratorBatch
  67. //
  68. //***************************************************************************
  69. // ok
  70. CIteratorBatch::CIteratorBatch()
  71. {
  72. m_bDone = FALSE;
  73. m_dwCursor = 0;
  74. g_aIterators.Add(this);
  75. }
  76. //***************************************************************************
  77. //
  78. // CIteratorBatch::Add
  79. //
  80. // Adds a string to the enumerator.
  81. //
  82. //***************************************************************************
  83. //
  84. BOOL CIteratorBatch::Add(LPSTR pszSrc)
  85. {
  86. int nRes = m_aStrings.Add(pszSrc);
  87. if (nRes)
  88. return FALSE;
  89. return TRUE;
  90. }
  91. //***************************************************************************
  92. //
  93. // CIteratorBatch::~CIteratorBatch
  94. //
  95. // Removes all remaining strings and deallocates them and removes
  96. // this iterator from the global list.
  97. //
  98. // Assumes prior concurrency control.
  99. //
  100. //***************************************************************************
  101. //
  102. CIteratorBatch::~CIteratorBatch()
  103. {
  104. for (int i = 0; i < m_aStrings.Size(); i++)
  105. {
  106. _BtrMemFree(m_aStrings[i]);
  107. }
  108. for (i = 0; i < g_aIterators.Size(); i++)
  109. {
  110. if (g_aIterators[i] == this)
  111. {
  112. g_aIterators.RemoveAt(i);
  113. break;
  114. }
  115. }
  116. }
  117. //***************************************************************************
  118. //
  119. // CIteratorBatch::Purge
  120. //
  121. // Removes a specific string from the enumerator. Happens when a concurrent
  122. // delete succeeds; we have to remove the deleted key from the enumeration
  123. // for result set coherence.
  124. //
  125. // Assumes prior concurrency control.
  126. //
  127. // Returns FALSE if the string was not removed, TRUE if it was. The
  128. // return value is mostly a debugging aid.
  129. //
  130. //***************************************************************************
  131. // ok
  132. BOOL CIteratorBatch::Purge(
  133. LPSTR pszTarget
  134. )
  135. {
  136. int nSize = m_aStrings.Size();
  137. if (nSize == 0)
  138. return FALSE;
  139. // First, check the first/last strings against
  140. // the first character of the target. We can
  141. // avoid a lot of strcmp calls if the target
  142. // is lexcially outside the range of the contents of
  143. // the enumerator.
  144. // ==================================================
  145. LPSTR pszFirst = (LPSTR) m_aStrings[0];
  146. LPSTR pszLast = (LPSTR) m_aStrings[nSize-1];
  147. if (*pszTarget > *pszLast)
  148. return FALSE;
  149. if (*pszTarget < *pszFirst)
  150. return FALSE;
  151. // If here, there is a chance that we have the
  152. // string in the enumerator. Since all keys are
  153. // retrieved in lexical order, a simple binary
  154. // search is all we need.
  155. // =============================================
  156. int nPosition = 0;
  157. int l = 0, u = nSize - 1;
  158. while (l <= u)
  159. {
  160. int m = (l + u) / 2;
  161. // m is the current key to consider 0...n-1
  162. LPSTR pszCandidate = (LPSTR) m_aStrings[m];
  163. int nRes = strcmp(pszTarget, pszCandidate);
  164. // Decide which way to cut the array in half.
  165. // ==========================================
  166. if (nRes < 0)
  167. {
  168. u = m - 1;
  169. nPosition = u + 1;
  170. }
  171. else if (nRes > 0)
  172. {
  173. l = m + 1;
  174. nPosition = l;
  175. }
  176. else
  177. {
  178. // If here, we found the darn thing. Life is good.
  179. // Zap it and return!
  180. // ================================================
  181. _BtrMemFree(pszCandidate);
  182. m_aStrings.RemoveAt(m);
  183. return TRUE;
  184. }
  185. }
  186. return FALSE;
  187. }
  188. //***************************************************************************
  189. //
  190. // CIteratorBatch::Next
  191. //
  192. // Returns the next string from the enumeration prefetch.
  193. //
  194. //***************************************************************************
  195. //
  196. BOOL CIteratorBatch::Next(LPSTR *pMem)
  197. {
  198. if (m_aStrings.Size())
  199. {
  200. *pMem = (LPSTR) m_aStrings[0];
  201. m_aStrings.RemoveAt(0);
  202. return TRUE;
  203. }
  204. return FALSE;
  205. }
  206. //***************************************************************************
  207. //
  208. // CBtrIndex::CBtrIndex
  209. //
  210. //***************************************************************************
  211. //
  212. CBtrIndex::CBtrIndex()
  213. {
  214. m_dwPrefixLength = 0;
  215. InitializeCriticalSection(&m_cs);
  216. }
  217. //***************************************************************************
  218. //
  219. // CBtrIndex::~CBtrIndex
  220. //
  221. //***************************************************************************
  222. //
  223. CBtrIndex::~CBtrIndex()
  224. {
  225. Shutdown(WMIDB_SHUTDOWN_NET_STOP);
  226. DeleteCriticalSection(&m_cs);
  227. }
  228. long CBtrIndex::Shutdown(DWORD dwShutDownFlags)
  229. {
  230. long lRes;
  231. lRes = bt.Shutdown(dwShutDownFlags);
  232. if(lRes != ERROR_SUCCESS)
  233. return lRes;
  234. lRes = ps.Shutdown(dwShutDownFlags);
  235. if(lRes != ERROR_SUCCESS)
  236. return lRes;
  237. return ERROR_SUCCESS;
  238. }
  239. //***************************************************************************
  240. //
  241. // CBtrIndex::Initialize
  242. //
  243. //***************************************************************************
  244. //
  245. long CBtrIndex::Initialize(DWORD dwPrefixLength, LPCWSTR wszRepositoryDir
  246. #ifdef A51_STAGE_BELOW_INDEX
  247. , CAbstractFileSource* pSource
  248. #endif
  249. )
  250. {
  251. // Initialize the files in question and map BTree into it.
  252. // =======================================================
  253. wchar_t buf[MAX_PATH];
  254. wcscpy(buf, wszRepositoryDir);
  255. wcscat(buf, L"\\index.btr");
  256. DWORD dwRes = ps.Init(8192, buf
  257. #ifdef A51_STAGE_BELOW_INDEX
  258. , pSource
  259. #endif
  260. );
  261. dwRes |= bt.Init(&ps);
  262. m_dwPrefixLength = dwPrefixLength;
  263. return long(dwRes);
  264. }
  265. //***************************************************************************
  266. //
  267. // CBtrIndex::Create
  268. //
  269. //***************************************************************************
  270. //
  271. long CBtrIndex::Create(LPCWSTR wszFileName)
  272. {
  273. DWORD dwRes;
  274. if (wszFileName == 0)
  275. return ERROR_INVALID_PARAMETER;
  276. wszFileName += m_dwPrefixLength;
  277. // DEBUG
  278. #ifdef DEBUG
  279. FILE *f = fopen("c:\\temp\\index.log","at");
  280. if (f)
  281. {
  282. fprintf(f, "Index::Create =<%S>\n", wszFileName);
  283. fclose(f);
  284. }
  285. #endif
  286. // END DEBUG
  287. // Convert to ANSI
  288. // ================
  289. char *pAnsi = new char[wcslen(wszFileName) + 1];
  290. if (pAnsi == 0)
  291. return ERROR_NOT_ENOUGH_MEMORY;
  292. LPCWSTR pSrc = wszFileName;
  293. char *pDest = pAnsi;
  294. while (*pSrc)
  295. *pDest++ = (char) *pSrc++;
  296. *pDest = 0;
  297. EnterCriticalSection(&m_cs);
  298. try
  299. {
  300. dwRes = bt.InsertKey(pAnsi, 0);
  301. }
  302. catch (...)
  303. {
  304. dwRes = ERROR_BADDB;
  305. }
  306. LeaveCriticalSection(&m_cs);
  307. delete [] pAnsi;
  308. if (dwRes == ERROR_ALREADY_EXISTS)
  309. dwRes = NO_ERROR;
  310. return long(dwRes);
  311. }
  312. //***************************************************************************
  313. //
  314. // CBtrIndex::Delete
  315. //
  316. // Deletes a key from the index
  317. //
  318. //***************************************************************************
  319. //
  320. long CBtrIndex::Delete(LPCWSTR wszFileName)
  321. {
  322. DWORD dwRes = 0;
  323. wszFileName += m_dwPrefixLength;
  324. // DEBUG Code
  325. #ifdef DEBUG
  326. FILE *f = fopen("c:\\temp\\index.log","at");
  327. if (f)
  328. {
  329. fprintf(f, "Index::Delete =<%S>\n", wszFileName);
  330. fclose(f);
  331. }
  332. // END DEBUG Code
  333. #endif
  334. // Convert to ANSI
  335. // ================
  336. char *pAnsi = new char[wcslen(wszFileName) + 1];
  337. if (pAnsi == 0)
  338. return ERROR_NOT_ENOUGH_MEMORY;
  339. LPCWSTR pSrc = wszFileName;
  340. char *pDest = pAnsi;
  341. while (*pSrc)
  342. *pDest++ = (char) *pSrc++;
  343. *pDest = 0;
  344. EnterCriticalSection(&m_cs);
  345. try
  346. {
  347. dwRes = bt.DeleteKey(pAnsi);
  348. if (dwRes == 0)
  349. CIteratorBatch::PurgeAll(pAnsi);
  350. }
  351. catch (...)
  352. {
  353. dwRes = ERROR_BADDB;
  354. }
  355. LeaveCriticalSection(&m_cs);
  356. delete pAnsi;
  357. return long(dwRes);
  358. }
  359. //***************************************************************************
  360. //
  361. // CBtrIndex::CopyStringToWIN32_FIND_DATA
  362. //
  363. // Does an ANSI to UNICODE convert for the key string.
  364. //
  365. //***************************************************************************
  366. //
  367. BOOL CBtrIndex::CopyStringToWIN32_FIND_DATA(
  368. LPSTR pszKey,
  369. WIN32_FIND_DATAW* pfd
  370. )
  371. {
  372. LPSTR pszSuffix = pszKey + strlen(pszKey) - 1;
  373. while (pszSuffix[-1] != '\\' && pszSuffix > pszKey)
  374. {
  375. pszSuffix--;
  376. }
  377. // If here, a clean match.
  378. // =======================
  379. LPWSTR pszDest = pfd->cFileName;
  380. while (*pszSuffix)
  381. *pszDest++ = (wchar_t) *pszSuffix++;
  382. *pszDest = 0;
  383. return TRUE;
  384. }
  385. //***************************************************************************
  386. //
  387. // CBtrIndex::FindFirst
  388. //
  389. // Starts an enumeration
  390. //
  391. //***************************************************************************
  392. //
  393. long CBtrIndex::FindFirst(LPCWSTR wszPrefix, WIN32_FIND_DATAW* pfd,
  394. void** ppHandle)
  395. {
  396. DWORD dwRes;
  397. wszPrefix += m_dwPrefixLength;
  398. if(ppHandle)
  399. *ppHandle = INVALID_HANDLE_VALUE;
  400. pfd->cFileName[0] = 0;
  401. pfd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
  402. // DEBUG
  403. #ifdef DEBUG
  404. FILE *f = fopen("c:\\temp\\index.log","at");
  405. if (f)
  406. {
  407. fprintf(f, "Index::FindFirst Request Prefix=<%S>\n", wszPrefix);
  408. fclose(f);
  409. }
  410. // END DEBUG
  411. #endif
  412. // Convert to ANSI
  413. // ================
  414. char *pAnsi = new char[wcslen(wszPrefix) + 1];
  415. if (pAnsi == 0)
  416. {
  417. return ERROR_NOT_ENOUGH_MEMORY;
  418. }
  419. CVectorDeleteMe<char> vdm(pAnsi);
  420. LPCWSTR pSrc = wszPrefix;
  421. char *pDest = pAnsi;
  422. while (*pSrc)
  423. *pDest++ = (char) *pSrc++;
  424. *pDest = 0;
  425. // Critical-section blocked.
  426. // =========================
  427. EnterCriticalSection(&m_cs);
  428. CBTreeIterator *pIt = new CBTreeIterator;
  429. if (!pIt)
  430. {
  431. LeaveCriticalSection(&m_cs);
  432. return ERROR_NOT_ENOUGH_MEMORY;
  433. }
  434. try
  435. {
  436. dwRes = pIt->Init(&bt, pAnsi);
  437. }
  438. catch (...)
  439. {
  440. dwRes = ERROR_BADDB;
  441. }
  442. if (dwRes)
  443. {
  444. pIt->Release();
  445. LeaveCriticalSection(&m_cs);
  446. return ERROR_FILE_NOT_FOUND;
  447. }
  448. // Create CIteratorBatch.
  449. // ======================
  450. CIteratorBatch *pBatch = new CIteratorBatch;
  451. if (pBatch == 0)
  452. {
  453. pIt->Release();
  454. LeaveCriticalSection(&m_cs);
  455. return ERROR_NOT_ENOUGH_MEMORY;
  456. }
  457. // Iterate and fill batcher.
  458. // =========================
  459. LPSTR pszKey = 0;
  460. int nMatchLen = strlen(pAnsi);
  461. for (;;)
  462. {
  463. dwRes = pIt->Next(&pszKey);
  464. if (dwRes)
  465. break;
  466. // See if prefix matches.
  467. if (strncmp(pAnsi, pszKey, nMatchLen) != 0)
  468. {
  469. pIt->FreeString(pszKey);
  470. pBatch->SetDone();
  471. break;
  472. }
  473. pBatch->Add(pszKey);
  474. }
  475. pIt->Release();
  476. long lRes = FindNext(pBatch, pfd);
  477. if (lRes == ERROR_NO_MORE_FILES)
  478. lRes = ERROR_FILE_NOT_FOUND;
  479. if (lRes == NO_ERROR)
  480. {
  481. if(ppHandle)
  482. {
  483. *ppHandle = (LPVOID *) pBatch;
  484. }
  485. else
  486. {
  487. //
  488. // Only asked for one --- close handle
  489. //
  490. delete pBatch;
  491. }
  492. }
  493. else
  494. {
  495. delete pBatch;
  496. }
  497. LeaveCriticalSection(&m_cs);
  498. return lRes;
  499. }
  500. //***************************************************************************
  501. //
  502. // CBtrIndex::FindNext
  503. //
  504. // Continues an enumeration. Reads from the prefetch buffer.
  505. //
  506. //***************************************************************************
  507. //
  508. long CBtrIndex::FindNext(void* pHandle, WIN32_FIND_DATAW* pfd)
  509. {
  510. LPSTR pszString = 0;
  511. BOOL bRes;
  512. if (pHandle == 0 || pfd == 0 || pHandle == INVALID_HANDLE_VALUE)
  513. return ERROR_INVALID_PARAMETER;
  514. EnterCriticalSection(&m_cs);
  515. CIteratorBatch *pBatch = (CIteratorBatch *) pHandle;
  516. bRes = pBatch->Next(&pszString);
  517. LeaveCriticalSection(&m_cs);
  518. if (bRes == FALSE)
  519. return ERROR_NO_MORE_FILES;
  520. CopyStringToWIN32_FIND_DATA(pszString, pfd);
  521. pfd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
  522. if (pszString)
  523. _BtrMemFree(pszString);
  524. #ifdef DEBUG
  525. FILE *f = fopen("c:\\temp\\index.log","at");
  526. if (f)
  527. {
  528. fprintf(f, "Index::Find Return ------------------> =<%S>\n", pfd->cFileName);
  529. fclose(f);
  530. }
  531. #endif
  532. return ERROR_SUCCESS;
  533. }
  534. //***************************************************************************
  535. //
  536. // CBtrIndex::FindClose
  537. //
  538. // Closes an enumeration by deleting the 'hidden' pointer.
  539. //
  540. //***************************************************************************
  541. // ok
  542. long CBtrIndex::FindClose(void* pHandle)
  543. {
  544. if (pHandle == 0 || pHandle == INVALID_HANDLE_VALUE)
  545. return NO_ERROR;
  546. EnterCriticalSection(&m_cs);
  547. delete (CIteratorBatch *) pHandle;
  548. LeaveCriticalSection(&m_cs);
  549. return ERROR_SUCCESS;
  550. }
  551. long CBtrIndex::InvalidateCache()
  552. {
  553. #ifdef DEBUG
  554. FILE *f = fopen("c:\\temp\\index.log","at");
  555. if (f)
  556. {
  557. fprintf(f, "*** INVALIDATE CACHE OPERATION\n");
  558. fclose(f);
  559. }
  560. #endif
  561. EnterCriticalSection(&m_cs);
  562. //
  563. // Re-read the admin page from disk. NOTE: this will need changing if more
  564. // caching is added!
  565. //
  566. DWORD dwRes = ps.ReadAdminPage();
  567. if (dwRes == NO_ERROR)
  568. dwRes = bt.InvalidateCache();
  569. LeaveCriticalSection(&m_cs);
  570. return long(dwRes);
  571. }
  572. /*
  573. tbd:
  574. (1) Multiple reader / single writer lock
  575. (2) Purge improvements: read-ahead
  576. (3) Macro for tracking checkpoint of mem allocs
  577. (4) page cache hit tracker
  578. Mechanical
  579. (5) Mem leak
  580. (6) Realloc crash
  581. (7) Failure during enum init
  582. (8) Substantial delete test; run stress & try to clean up afterwards
  583. */