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.

2870 lines
78 KiB

  1. //***************************************************************************
  2. //
  3. // (c) 2001 by Microsoft Corp. All Rights Reserved.
  4. //
  5. // PAGEMGR.CPP
  6. //
  7. // Declarations for CPageFile, CPageSource for WMI repository for
  8. // Windows XP. This is a fully transacted high-speed page manager.
  9. //
  10. // 21-Feb-01 raymcc first draft of interfaces
  11. // 28-Feb-01 raymcc first complete working model
  12. // 18-Apr-01 raymcc Final fixes for rollback; page reuse
  13. //
  14. //***************************************************************************
  15. #include <windows.h>
  16. #include <stdio.h>
  17. #include <pagemgr.h>
  18. #include <sync.h>
  19. #include <wbemcomn.h>
  20. #define MAP_LEADING_SIGNATURE 0xABCD
  21. #define MAP_TRAILING_SIGNATURE 0xDCBA
  22. #define CURRENT_TRANSACTION 0x80000000
  23. #define PREVIOUS_TRANSACTION 0x40000000
  24. #define ALL_REPLACED_FLAGS (CURRENT_TRANSACTION | PREVIOUS_TRANSACTION)
  25. #define MERE_PAGE_ID(x) (x & 0x3FFFFFFF)
  26. void StripHiBits(std::vector <DWORD, wbem_allocator<DWORD> > &Array);
  27. void MoveCurrentToPrevious(std::vector <DWORD, wbem_allocator<DWORD> > &Array);
  28. /*
  29. Map & cache orientation
  30. (a) There are two types of page ids, logical and physical. The logical
  31. ID is what is used by the external user, such as the B-tree or
  32. object heap. The physical ID is the 0-origin page number into the
  33. file itself. For each logical id, there is a corresponding physical
  34. id. The decoupling is to allow transacted writes without multiple
  35. physical writes during commit/rollback, but to 'simulate' transactions
  36. by interchanging physical-to-logical ID mapping instead. The
  37. physical offset into the file is the ID * the page size.
  38. (b) Logical ID is implied, and is the offset into the PhysicalID array
  39. (c) Cache operation is by physical id only
  40. (d) The physical IDs of the pages contain other transaction information.
  41. The MS 2 bits are transaction-related and only the lower 30 bits is the
  42. physical page id, and so must be masked out when computing offsets.
  43. The bits are manipulated during commit/rollback, etc.
  44. (d) 0xFFFFFFFE is a reserved page, meaning a page that was allocated
  45. by NewPage, but has not yet been written for the first time using PutPage.
  46. This is merely validation technique to ensure pages are written only
  47. after they were requested.
  48. (e) Cache is agnostic to the transaction methodology. It is
  49. simply a physical page cache and has no knowledge of anything
  50. else. It is promote-to-front on all accesses. For optimization
  51. purposes, there is no real movement if the promotion would
  52. move the page from a near-to-front to absolute-front location.
  53. This is the 'PromoteToFrontThreshold' in the Init function.
  54. Note that the cache ordering is by access and not sorted
  55. in any other way. Lookups require a linear scan.
  56. It is possible that during writes new physical pages
  57. are added to the cache which are 'new extent' pages. These
  58. are harmless.
  59. Map PhysId Cache
  60. [0] 5 /-> 2 -> bytes
  61. [1] 6 / 3 -> bytes
  62. [2] -1 / 4 -> bytes
  63. [3] 3 / 5 -> bytes
  64. [4] 2 <-/
  65. [5] 4
  66. (f) Transaction & checkpoint algorithms
  67. First, the general process is this:
  68. 1. Begin transaction:
  69. (a) Generation A mapping and Generation B mapping member
  70. variables are identical. Each contains a page map
  71. 2. Operations within the transaction occur on Generation B
  72. mapping. Cache spills are allowed to disk, as they are harmless.
  73. 3. At rollback, Generation B mapping is copied from Generation A.
  74. 4. At commit, Generation A is copied from Generation B.
  75. 5. At checkpoint, Generation A/B are identical and written to disk
  76. Cache spills & usage are irrelevant to intermediate transactions.
  77. There are special cases in terms of how pages are reused. First,
  78. as pages are freed within a transaction, we cannot just blindly add them
  79. to the free list, since they might be part of the previous checkpoint
  80. page set. This would allow them to be accidentally reused and then
  81. a checkpoint rollback could never succeed (the original page
  82. having been destroyed).
  83. As pages committed during the previous checkpoint are updated, they
  84. are written to new physical pages under the same logical id. The old
  85. physical pages are added to the "Replaced Pages" array. This allows
  86. them to be identified as new free list pages once the checkpoint
  87. occurs. So, during the checkpoint, replaced pages are merged
  88. into the current free list. Until that time, they are 'off limits',
  89. since we need them for a checkpoint rollback in an emergency.
  90. Within a transaction, as pages are acquired, they are acquired
  91. from the physical free list, or if there is no free list, new
  92. pages are requested. It can happen that during the next transaction (still
  93. within the checkpoint), those pages need updating, whether for rewrite
  94. or delete. Now, because we may have to roll back, we cannot simply
  95. add those replaced pages directly to the free list (allowing them
  96. to be reused by some other operation). Instead, they
  97. have to be part of a 'deferred free list'. Once the current
  98. transaction is committed, they can safely be part of the
  99. regular free list.
  100. The algorithm is this:
  101. (a) The physical Page ID is the lower 30 bits of the entry. The two high
  102. bits have a special meaning.
  103. (b) Writes result either in an update or a new allocation [whether
  104. from an extent or a reuse of the free list]. Any such page is marked
  105. with the CURRENT_TRANSACTION bit (the ms bit) which is merged into
  106. the phyiscal id. If this page is encountered again, we know it
  107. was allocated during the current transaction.
  108. (c) For UPDATES
  109. 1. If the page ID is equal to 0xFFFFFFFE, then we need to
  110. allocate a new page, which is marked with CURRENT_TRANSACTION.
  111. 2. If the physical page ID written has both high bits clear,
  112. it is a page being updated which was inherited from the previous
  113. checkpoint page set. We allocate and write to a new physical
  114. page, marking this new page with CURRENT_TRANSACTION.
  115. We add the old physical page ID directly to the m_xReplacedPages
  116. array. It is off-limits until the next checkpoint, at which
  117. point it is merged into the free list.
  118. 3. If the physical page id already has CURRENT_TRANSACTION, we
  119. simply update the page in place.
  120. 4. If the page has the PREVIOUS_TRANSACTION bit set, we allocate
  121. a new page so a rollback will protect it, and add this page
  122. to the DeferredFreeList array. During a commit, these
  123. pages are merged into the FreeList. During a rollback,
  124. we simply throw this array away.
  125. (d) For DELETE
  126. 1. If the page has both hi bits clear, we add it to the ReplacedPages
  127. array.
  128. 2. If the page has the CURRENT_TRANSACTION bit set, we add it to the free list.
  129. This page was never part of any previous operation and can be reused
  130. right away.
  131. 3. If the page has the PREVIOUS_TRANSACTION bit set, it is added to the
  132. DeferredFreeList.
  133. (e) For COMMIT
  134. 1. All pages with the CURRENT_TRANSACTION bit set are changed to clear that
  135. bit and set the PREVIOUS_TRANSACTION bit instead.
  136. 2. All pages with the PREVIOUS_TRANSACTION bit are left intact.
  137. 3. All pages in the DeferredFreeList are added to the FreeList.
  138. (f) For ROLLBACK
  139. 1. All pages with the CURRENT_TRANSACTION bit are moved back into the
  140. free list (the bits are cleared).
  141. 2. All pages with the PREVIOUS_TRANSACTION bit are left intact.
  142. (g) For Checkpoint
  143. 1. All DeferredFreeList entries are added to FreeList.
  144. 2. All ReplacedPages are added to FreeList.
  145. 3. All ms bits are cleared for physical IDs.
  146. Proof:
  147. T1 = page set touched for transaction 1, T2=page set touched for transaction 2, etc.
  148. After T1-START, all new pages are marked CURRENT. If all are updated n times,
  149. no new pages are required, since rollback brings us back to zero pages
  150. anyway.
  151. At T1-COMMIT, all CURRENT pages are marked PREVIOUS. This is the T1a
  152. page set.
  153. After T2-BEGIN, all new pages are marked CURRENT. Again, updating
  154. all T2 pages infinitely never extends the file beyond the size
  155. of T1a+T2 page sets. Further deleting and reusing T2 pages
  156. never affects T1a pages, proving that deleting a CURRENT page
  157. merits direct addition to the free list.
  158. Updating n pages from T1 requires n new pages. As we update
  159. all the T1 pages, we need a total file size of T2+T1*2. As
  160. we encounter T1a pages marked as PREVIOUS, we allocate CURRENT
  161. pages for T1b and then reuse those indefinitely. Whether we update
  162. all T1a or delete all T1a, we must still protect the original T1a
  163. page set in case of rollback. Therefore all touched pages of T1a
  164. are posted the deferred free list as they become replaced by T1b
  165. equivalents.
  166. At rollback, we simply throw away the deferred free list,
  167. and T1a is intact, since those physical pages were never touched.
  168. At commit, all T1a pages are now in T1b, and al T1a
  169. pages are now reusable, and of course were in fact added
  170. to the deferred free list, which is merged with the
  171. general free list at commit time. T1b and T2 pages are now
  172. protected and marked PREVIOUS.
  173. On the transitive nature of PREVIOUS:
  174. Assume zero T1 pages are touched at T2-BEGIN, and there
  175. are only T2 updates, commit and then T3 is begun. At
  176. this point, both T1/T2 sets are marked as PREVIOUS
  177. and not distinguished. T3 new allocations follow the
  178. same rules. Obviously if all T1 pages are deleted, we
  179. still cannot afford to reuse them, since a rollback
  180. would need to see T1 pages intact. Therefore at
  181. each commit, there is an effective identity shift where
  182. T1 = T2 UNION T1
  183. T3 becomes T2
  184. And we are in the 2-transaction problem space again.
  185. Unmarking pages completely makes them equal to the
  186. previous checkpoint page set. They can be completely
  187. replaced with new physical pages, but never reused until
  188. the next checkpoint, which wastes space.
  189. */
  190. /////////////////////////////////////////////////////////////////////////////
  191. /////////////////////////////////////////////////////////////////////////////
  192. /////////////////////////////////////////////////////////////////////////////
  193. //
  194. // CPageCache
  195. //***************************************************************************
  196. //
  197. // CPageCache::CPageCache
  198. //
  199. //***************************************************************************
  200. // rev2
  201. CPageCache::CPageCache()
  202. {
  203. m_dwStatus = NO_ERROR;
  204. m_hFile = 0;
  205. m_dwPageSize = 0;
  206. m_dwCacheSize = 0;
  207. m_dwCachePromoteThreshold = 0;
  208. m_dwCacheSpillRatio = 0;
  209. m_dwLastFlushTime = GetCurrentTime();
  210. m_dwWritesSinceFlush = 0;
  211. m_dwLastCacheAccess = 0;
  212. m_dwReadHits = 0;
  213. m_dwReadMisses = 0;
  214. m_dwWriteHits = 0;
  215. m_dwWriteMisses = 0;
  216. }
  217. //***************************************************************************
  218. //
  219. // CPageCache::Init
  220. //
  221. // Initializes the cache. Called once during startup. If the file
  222. // can't be opened, the cache becomes invalid right at the start.
  223. //
  224. //***************************************************************************
  225. // rev2
  226. DWORD CPageCache::Init(
  227. LPCWSTR pszFilename, // File
  228. DWORD dwPageSize, // In bytes
  229. DWORD dwCacheSize, // Pages in cache
  230. DWORD dwCachePromoteThreshold, // When to ignore promote-to-front
  231. DWORD dwCacheSpillRatio // How many additional pages to write on cache write-through
  232. )
  233. {
  234. m_dwPageSize = dwPageSize;
  235. m_dwCacheSize = dwCacheSize;
  236. m_dwCachePromoteThreshold = dwCachePromoteThreshold;
  237. m_dwCacheSpillRatio = dwCacheSpillRatio;
  238. m_hFile = CreateFileW(pszFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
  239. OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  240. if (m_hFile == INVALID_HANDLE_VALUE)
  241. {
  242. return GetLastError();
  243. }
  244. return NO_ERROR;
  245. }
  246. //***************************************************************************
  247. //
  248. // CPageCache::~CPageCache
  249. //
  250. // Empties cache during destruct; called once at shutdown.
  251. //
  252. //***************************************************************************
  253. // rev2
  254. CPageCache::~CPageCache()
  255. {
  256. if (m_hFile != INVALID_HANDLE_VALUE)
  257. CloseHandle(m_hFile);
  258. try
  259. {
  260. Empty();
  261. }
  262. catch(...)
  263. {
  264. // Situation is hopeless
  265. }
  266. }
  267. //***************************************************************************
  268. //
  269. // CPageCache::Write()
  270. //
  271. // Writes a page to the cache and promotes it to the front. If the
  272. // page is already present, it is simply marked as dirty and promoted
  273. // to the front (if it is outside the promote-to-front threshold).
  274. // If cache is full and this causes overflow, it is handled by the
  275. // Cache_Spill() at the end. Physical writes occur in Spill() only if
  276. // there is cache overflow.
  277. //
  278. // Errors: Return codes only, sets permanent error status of object
  279. // once error has occurred.
  280. //
  281. // On failure, the caller must invoked Cache::Reinit() before using
  282. // it any further. On error DISK_FULL, there is not much of a point
  283. // in a Reinit() even though it is safe.
  284. //
  285. //***************************************************************************
  286. // rev2
  287. DWORD CPageCache::Write(
  288. DWORD dwPhysId,
  289. LPBYTE pPageMem
  290. )
  291. {
  292. if (m_dwStatus != NO_ERROR)
  293. return m_dwStatus;
  294. m_dwWritesSinceFlush++;
  295. m_dwLastCacheAccess = GetCurrentTime();
  296. // Search current cache.
  297. // =====================
  298. DWORD dwSize = m_aCache.size();
  299. for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
  300. {
  301. SCachePage *pTest = m_aCache[dwIx];
  302. if (pTest->m_dwPhysId == dwPhysId)
  303. {
  304. delete [] pTest->m_pPage;
  305. pTest->m_pPage = pPageMem;
  306. pTest->m_bDirty = TRUE;
  307. // Promote to front?
  308. // =================
  309. if (dwIx > m_dwCachePromoteThreshold)
  310. {
  311. try
  312. {
  313. m_aCache.erase(m_aCache.begin()+dwIx);
  314. m_aCache.insert(m_aCache.begin(), pTest);
  315. }
  316. catch (CX_MemoryException &)
  317. {
  318. pTest->m_pPage = 0;
  319. m_dwStatus = ERROR_OUTOFMEMORY;
  320. return m_dwStatus;
  321. }
  322. }
  323. m_dwWriteHits++;
  324. return NO_ERROR;
  325. }
  326. }
  327. // If here, no cache hit, so we create a new entry.
  328. // ================================================
  329. SCachePage *pCP = new SCachePage;
  330. if (!pCP)
  331. {
  332. m_dwStatus = ERROR_OUTOFMEMORY;
  333. return m_dwStatus;
  334. }
  335. pCP->m_dwPhysId = dwPhysId;
  336. pCP->m_pPage = pPageMem;
  337. pCP->m_bDirty = TRUE;
  338. try
  339. {
  340. m_aCache.insert(m_aCache.begin(), pCP);
  341. }
  342. catch(CX_MemoryException &)
  343. {
  344. pCP->m_pPage = 0;
  345. delete pCP;
  346. m_dwStatus = ERROR_OUTOFMEMORY;
  347. return m_dwStatus;
  348. }
  349. m_dwWriteMisses++;
  350. return Spill();
  351. }
  352. //***************************************************************************
  353. //
  354. // CPageCache::Read
  355. //
  356. // Reads the requested page from the cache. If the page isn't found
  357. // it is loaded from the disk file. The cache size cannot change, but
  358. // the referenced page is promoted to the front if it is outside of
  359. // the no-promote-threshold.
  360. //
  361. // A pointer directly into the cache mem is returned in <pMem>, so
  362. // the contents should be copied as soon as possible.
  363. //
  364. // Errors: If the read fails due to ERROR_OUTOFMEMORY, then the
  365. // cache is permanently in an error state until Cache->Reinit()
  366. // is called.
  367. //
  368. //***************************************************************************
  369. // rev2
  370. DWORD CPageCache::Read(
  371. IN DWORD dwPhysId,
  372. OUT LPBYTE *pMem // Read-only!
  373. )
  374. {
  375. if (pMem == 0)
  376. return ERROR_INVALID_PARAMETER;
  377. if (m_dwStatus != NO_ERROR)
  378. return m_dwStatus;
  379. m_dwLastCacheAccess = GetCurrentTime();
  380. // Search current cache.
  381. // =====================
  382. DWORD dwSize = m_aCache.size();
  383. for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
  384. {
  385. SCachePage *pTest = m_aCache[dwIx];
  386. if (pTest->m_dwPhysId == dwPhysId)
  387. {
  388. // Promote to front?
  389. if (dwIx > m_dwCachePromoteThreshold)
  390. {
  391. try
  392. {
  393. m_aCache.erase(m_aCache.begin()+dwIx);
  394. m_aCache.insert(m_aCache.begin(), pTest);
  395. }
  396. catch (CX_MemoryException &)
  397. {
  398. m_dwStatus = ERROR_OUTOFMEMORY;
  399. return m_dwStatus;
  400. }
  401. }
  402. *pMem = pTest->m_pPage;
  403. m_dwReadHits++;
  404. return NO_ERROR;
  405. }
  406. }
  407. // If here, not found, so we have to read in from disk
  408. // and do a spill test.
  409. // ====================================================
  410. SCachePage *pCP = new SCachePage;
  411. if (!pCP)
  412. {
  413. m_dwStatus = ERROR_OUTOFMEMORY;
  414. return m_dwStatus;
  415. }
  416. pCP->m_dwPhysId = dwPhysId;
  417. pCP->m_bDirty = FALSE;
  418. m_dwReadMisses++;
  419. DWORD dwRes = ReadPhysPage(dwPhysId, &pCP->m_pPage);
  420. if (dwRes)
  421. {
  422. delete pCP;
  423. m_dwStatus = dwRes;
  424. return dwRes;
  425. }
  426. try
  427. {
  428. m_aCache.insert(m_aCache.begin(), pCP);
  429. }
  430. catch(CX_MemoryException &)
  431. {
  432. delete pCP;
  433. m_dwStatus = ERROR_OUTOFMEMORY;
  434. return m_dwStatus;
  435. }
  436. dwRes = Spill();
  437. if (dwRes)
  438. return dwRes;
  439. *pMem = pCP->m_pPage;
  440. return dwRes;
  441. }
  442. //***************************************************************************
  443. //
  444. // CPageCache::Spill
  445. //
  446. // Checks for cache overflow and implements the spill-to-disk
  447. // algorithm.
  448. //
  449. // Precondition: The cache is either within bounds or 1 page too large.
  450. //
  451. // If the physical id of the pages written exceeds the physical extent
  452. // of the file, WritePhysPage will properly extend the file to handle it.
  453. //
  454. // Note that if no write occurs during the spill, additional pages are
  455. // not spilled or written.
  456. //
  457. //***************************************************************************
  458. // rev2
  459. DWORD CPageCache::Spill()
  460. {
  461. BOOL bWritten = FALSE;
  462. DWORD dwRes = 0;
  463. DWORD dwSize = m_aCache.size();
  464. // See if the cache has exceeded its limit.
  465. // ========================================
  466. if (dwSize <= m_dwCacheSize)
  467. return NO_ERROR;
  468. // If here, the cache is too large by 1 element (precondition).
  469. // We remove the last page after checking to see if it is
  470. // dirty and needs writing.
  471. // ============================================================
  472. SCachePage *pDoomed = *(m_aCache.end()-1);
  473. if (pDoomed->m_bDirty)
  474. {
  475. dwRes = WritePhysPage(pDoomed->m_dwPhysId, pDoomed->m_pPage);
  476. if (dwRes != NO_ERROR)
  477. {
  478. m_dwStatus = dwRes;
  479. return dwRes;
  480. }
  481. bWritten = TRUE;
  482. }
  483. delete pDoomed;
  484. try
  485. {
  486. m_aCache.erase(m_aCache.end()-1);
  487. }
  488. catch(CX_MemoryException &)
  489. {
  490. m_dwStatus = ERROR_OUTOFMEMORY;
  491. return m_dwStatus;
  492. }
  493. if (!bWritten)
  494. return NO_ERROR;
  495. // If here, we had a write.
  496. // Next, work through the cache from the end and write out
  497. // a few more pages, based on the spill ratio. We don't
  498. // remove these from the cache, we simply write them and
  499. // clear the dirty bit.
  500. // ========================================================
  501. DWORD dwWriteCount = 0;
  502. try
  503. {
  504. std::vector <SCachePage *>::reverse_iterator rit;
  505. rit = m_aCache.rbegin();
  506. while (rit != m_aCache.rend() && dwWriteCount < m_dwCacheSpillRatio)
  507. {
  508. SCachePage *pTest = *rit;
  509. if (pTest->m_bDirty)
  510. {
  511. dwRes = WritePhysPage(pTest->m_dwPhysId, pTest->m_pPage);
  512. if (dwRes)
  513. return dwRes;
  514. pTest->m_bDirty = FALSE;
  515. dwWriteCount++;
  516. }
  517. rit++;
  518. }
  519. }
  520. catch(CX_MemoryException &)
  521. {
  522. m_dwStatus = ERROR_GEN_FAILURE;
  523. return m_dwStatus;
  524. }
  525. return NO_ERROR;
  526. }
  527. //***************************************************************************
  528. //
  529. // CPageCache::WritePhysPage
  530. //
  531. // Writes a physical page.
  532. //
  533. //***************************************************************************
  534. // rev2
  535. DWORD CPageCache::WritePhysPage(
  536. IN DWORD dwPageId,
  537. IN LPBYTE pPageMem
  538. )
  539. {
  540. if (m_dwStatus != NO_ERROR)
  541. return m_dwStatus;
  542. DWORD dwOffset = m_dwPageSize * dwPageId;
  543. DWORD dwRes = SetFilePointer(m_hFile, dwOffset, 0, FILE_BEGIN);
  544. if (dwRes != dwOffset)
  545. {
  546. m_dwStatus = ERROR_DISK_FULL;
  547. return m_dwStatus;
  548. }
  549. // Commit the page to the disk
  550. DWORD dwWritten = 0;
  551. BOOL bRes = WriteFile(m_hFile, pPageMem, m_dwPageSize, &dwWritten, 0);
  552. if (bRes == FALSE || dwWritten != m_dwPageSize)
  553. {
  554. // Probably out of disk space
  555. m_dwStatus = ERROR_DISK_FULL;
  556. return m_dwStatus;
  557. }
  558. return NO_ERROR;
  559. }
  560. //***************************************************************************
  561. //
  562. // CPageCache::Empty
  563. //
  564. // Does no checking to see if a flush should have occurred.
  565. // Callers have this in a try/catch block.
  566. //
  567. //***************************************************************************
  568. // rev2
  569. DWORD CPageCache::Empty()
  570. {
  571. DWORD dwSize = m_aCache.size();
  572. for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
  573. {
  574. SCachePage *pTest = m_aCache[dwIx];
  575. delete pTest;
  576. }
  577. try
  578. {
  579. m_aCache.clear();
  580. }
  581. catch(CX_MemoryException &)
  582. {
  583. return ERROR_OUTOFMEMORY;
  584. }
  585. return NO_ERROR;
  586. }
  587. //***************************************************************************
  588. //
  589. // CPageCache::Flush
  590. //
  591. //***************************************************************************
  592. // rev2
  593. DWORD CPageCache::Flush()
  594. {
  595. if (m_dwStatus != NO_ERROR)
  596. return m_dwStatus;
  597. // Short-circuit. If no writes have occurred, just reset
  598. // and return.
  599. // =======================================================
  600. if (m_dwWritesSinceFlush == 0)
  601. {
  602. m_dwLastFlushTime = GetCurrentTime();
  603. m_dwWritesSinceFlush = 0;
  604. return NO_ERROR;
  605. }
  606. // Logical cache flush.
  607. // ====================
  608. DWORD dwRes = 0;
  609. DWORD dwSize = m_aCache.size();
  610. for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
  611. {
  612. SCachePage *pTest = m_aCache[dwIx];
  613. if (pTest->m_bDirty)
  614. {
  615. dwRes = WritePhysPage(pTest->m_dwPhysId, pTest->m_pPage);
  616. if (dwRes)
  617. return m_dwStatus = dwRes;
  618. pTest->m_bDirty = FALSE;
  619. }
  620. }
  621. // Do the disk flush.
  622. // ==================
  623. FlushFileBuffers(m_hFile);
  624. m_dwLastFlushTime = GetCurrentTime();
  625. m_dwWritesSinceFlush = 0;
  626. return NO_ERROR;
  627. }
  628. //***************************************************************************
  629. //
  630. // CPageCache::ReadPhysPage
  631. //
  632. // Reads a physical page from the file.
  633. //
  634. //***************************************************************************
  635. // rev2
  636. DWORD CPageCache::ReadPhysPage(
  637. IN DWORD dwPage,
  638. OUT LPBYTE *pPageMem
  639. )
  640. {
  641. DWORD dwRes;
  642. *pPageMem = 0;
  643. if (m_dwStatus != NO_ERROR)
  644. return m_dwStatus;
  645. if (pPageMem == 0)
  646. return ERROR_INVALID_PARAMETER;
  647. // Allocate some memory
  648. // ====================
  649. LPBYTE pMem = new BYTE[m_dwPageSize];
  650. if (!pMem)
  651. {
  652. m_dwStatus = ERROR_OUTOFMEMORY;
  653. return m_dwStatus;
  654. }
  655. // Where is this page hiding?
  656. // ==========================
  657. DWORD dwOffs = dwPage * m_dwPageSize;
  658. dwRes = SetFilePointer(m_hFile, dwOffs, 0, FILE_BEGIN);
  659. if (dwRes != dwOffs)
  660. {
  661. delete [] pMem;
  662. m_dwStatus = ERROR_DISK_FULL;
  663. return m_dwStatus;
  664. }
  665. // Try to read it.
  666. // ===============
  667. DWORD dwRead = 0;
  668. BOOL bRes = ReadFile(m_hFile, pMem, m_dwPageSize, &dwRead, 0);
  669. if (!bRes || dwRead != m_dwPageSize)
  670. {
  671. delete [] pMem;
  672. // If we can't read it, we probably did a seek past eof,
  673. // meaning the requested page was invalid.
  674. // =====================================================
  675. m_dwStatus = ERROR_INVALID_PARAMETER;
  676. return m_dwStatus;
  677. }
  678. *pPageMem = pMem;
  679. return NO_ERROR;
  680. }
  681. //***************************************************************************
  682. //
  683. // CPageCache::Dump
  684. //
  685. // Dumps cache info to the specified stream.
  686. //
  687. //***************************************************************************
  688. // rev2
  689. void CPageCache::Dump(FILE *f)
  690. {
  691. DWORD dwSize = m_aCache.size();
  692. fprintf(f, "---Begin Cache Dump---\n");
  693. fprintf(f, "Status = %d\n", m_dwStatus);
  694. fprintf(f, "Time since last flush = %d\n", GetCurrentTime() - m_dwLastFlushTime);
  695. fprintf(f, "Writes since last flush = %d\n", m_dwWritesSinceFlush);
  696. fprintf(f, "Read hits = %d\n", m_dwReadHits);
  697. fprintf(f, "Read misses = %d\n", m_dwReadMisses);
  698. fprintf(f, "Write hits = %d\n", m_dwWriteHits);
  699. fprintf(f, "Write misses = %d\n", m_dwWriteMisses);
  700. fprintf(f, "Size = %d\n", dwSize);
  701. for (DWORD dwIx = 0; dwIx < dwSize; dwIx++)
  702. {
  703. SCachePage *pTest = m_aCache[dwIx];
  704. fprintf(f, "Cache[%d] ID=%d dirty=%d pMem=0x%p <%s>\n",
  705. dwIx, pTest->m_dwPhysId, pTest->m_bDirty, pTest->m_pPage, pTest->m_pPage);
  706. }
  707. fprintf(f, "---End Cache Dump---\n");
  708. }
  709. /////////////////////////////////////////////////////////////////////////////
  710. /////////////////////////////////////////////////////////////////////////////
  711. /////////////////////////////////////////////////////////////////////////////
  712. //
  713. // CPageFile
  714. //***************************************************************************
  715. //
  716. // CPageFile::AddRef
  717. //
  718. //***************************************************************************
  719. // rev2
  720. ULONG CPageFile::AddRef()
  721. {
  722. return (ULONG) InterlockedIncrement(&m_lRef);
  723. }
  724. //***************************************************************************
  725. //
  726. // CPageFile::ResyncMaps
  727. //
  728. // Reninitializes B maps from A maps.
  729. //
  730. //***************************************************************************
  731. // rev2
  732. DWORD CPageFile::ResyncMaps()
  733. {
  734. try
  735. {
  736. m_aPageMapB = m_aPageMapA;
  737. m_aPhysFreeListB = m_aPhysFreeListA;
  738. m_aLogicalFreeListB = m_aLogicalFreeListA;
  739. m_aReplacedPagesB = m_aReplacedPagesA;
  740. m_dwPhysPagesB = m_dwPhysPagesA;
  741. m_aDeferredFreeList.clear();
  742. }
  743. catch (CX_MemoryException &)
  744. {
  745. return m_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  746. }
  747. return NO_ERROR;
  748. }
  749. //***************************************************************************
  750. //
  751. // CPageFile::ReadMap
  752. //
  753. // Reads the map file into memory. Format:
  754. //
  755. // DWORD dwLeadingSignature
  756. // DWORD dwTransactionGeneration
  757. // DWORD dwNumMappedPages
  758. // DWORD PhysicalPages[]
  759. // DWORD dwNumFreePages
  760. // DWORD FreePages[]
  761. // DWORD dwTrailingSignature
  762. //
  763. // The only time the MAP file will not be present is on creation of
  764. // a new map file.
  765. //
  766. // This function is retry-compatible.
  767. //
  768. //***************************************************************************
  769. // rev2
  770. DWORD CPageFile::ReadMap()
  771. {
  772. BOOL bRes;
  773. WString sFilename;
  774. try
  775. {
  776. sFilename = m_sDirectory;
  777. sFilename += L"\\" ;
  778. sFilename += m_sMapFile;
  779. }
  780. catch (CX_MemoryException &)
  781. {
  782. return m_dwStatus = ERROR_OUTOFMEMORY;
  783. }
  784. HANDLE hFile = CreateFileW((const wchar_t *)sFilename, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
  785. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  786. if (hFile == INVALID_HANDLE_VALUE)
  787. {
  788. DWORD dwRet = GetLastError();
  789. if (dwRet != ERROR_FILE_NOT_FOUND)
  790. m_dwStatus = dwRet;
  791. return dwRet;
  792. }
  793. AutoClose _(hFile);
  794. // If here, read it.
  795. // =================
  796. DWORD dwSignature = 0;
  797. DWORD dwRead = 0;
  798. bRes = ReadFile(hFile, &dwSignature, sizeof(DWORD), &dwRead, 0);
  799. if (!bRes || dwRead != sizeof(DWORD) || dwSignature != MAP_LEADING_SIGNATURE)
  800. {
  801. return m_dwStatus = ERROR_INVALID_DATA;
  802. }
  803. // Read transaction version.
  804. // =========================
  805. bRes = ReadFile(hFile, &m_dwTransVersion, sizeof(DWORD), &dwRead, 0);
  806. if (!bRes || dwRead != sizeof(DWORD))
  807. {
  808. return m_dwStatus = ERROR_INVALID_DATA;
  809. }
  810. // Read in physical page count.
  811. // ============================
  812. bRes = ReadFile(hFile, &m_dwPhysPagesA, sizeof(DWORD), &dwRead, 0);
  813. if (!bRes || dwRead != sizeof(DWORD))
  814. {
  815. return m_dwStatus = ERROR_INVALID_DATA;
  816. }
  817. // Read in the page map length and page map.
  818. // =========================================
  819. DWORD dwNumPages = 0;
  820. bRes = ReadFile(hFile, &dwNumPages, sizeof(DWORD), &dwRead, 0);
  821. if (!bRes || dwRead != sizeof(DWORD))
  822. {
  823. return m_dwStatus = ERROR_INVALID_DATA;
  824. }
  825. try
  826. {
  827. m_aPageMapA.resize(dwNumPages);
  828. }
  829. catch (CX_MemoryException &)
  830. {
  831. return m_dwStatus = ERROR_OUTOFMEMORY;
  832. }
  833. bRes = ReadFile(hFile, &m_aPageMapA[0], sizeof(DWORD)*dwNumPages, &dwRead, 0);
  834. if (!bRes || dwRead != sizeof(DWORD)*dwNumPages)
  835. return m_dwStatus = ERROR_INVALID_DATA;
  836. // Now, read in the physical free list.
  837. // ====================================
  838. DWORD dwFreeListSize = 0;
  839. bRes = ReadFile(hFile, &dwFreeListSize, sizeof(DWORD), &dwRead, 0);
  840. if (!bRes || dwRead != sizeof(DWORD))
  841. {
  842. return m_dwStatus = ERROR_INVALID_DATA;
  843. }
  844. try
  845. {
  846. m_aPhysFreeListA.resize(dwFreeListSize);
  847. }
  848. catch (CX_MemoryException &)
  849. {
  850. return m_dwStatus = ERROR_OUTOFMEMORY;
  851. }
  852. bRes = ReadFile(hFile, &m_aPhysFreeListA[0], sizeof(DWORD)*dwFreeListSize, &dwRead, 0);
  853. if (!bRes || dwRead != sizeof(DWORD)*dwFreeListSize)
  854. {
  855. return m_dwStatus = ERROR_INVALID_DATA;
  856. }
  857. // Read trailing signature.
  858. // ========================
  859. bRes = ReadFile(hFile, &dwSignature, sizeof(DWORD), &dwRead, 0);
  860. if (!bRes || dwRead != sizeof(DWORD) || dwSignature != MAP_TRAILING_SIGNATURE)
  861. {
  862. return m_dwStatus = ERROR_INVALID_DATA;
  863. }
  864. // Initialize the logical free list from the page map.
  865. // ===================================================
  866. return m_dwStatus = InitFreeList();
  867. }
  868. //***************************************************************************
  869. //
  870. // CPageFile::WriteMap
  871. //
  872. // Writes the generation A mapping (assuming that we write immediately
  873. // during a checkpoint when A and B generations are the same and that
  874. // the replacement lists have been appended to the free lists, etc., etc.
  875. // This write occurs to a temp file. The renaming occurs externally.
  876. //
  877. // This function is retry compatible.
  878. //
  879. //***************************************************************************
  880. // rev2
  881. DWORD CPageFile::WriteMap(LPCWSTR pszTempPath)
  882. {
  883. BOOL bRes;
  884. DeleteFileW(pszTempPath);
  885. HANDLE hFile = CreateFileW(pszTempPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
  886. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  887. if (hFile == INVALID_HANDLE_VALUE)
  888. {
  889. return m_dwStatus = GetLastError();
  890. }
  891. AutoClose _(hFile);
  892. // If here, write leading signature.
  893. // =================================
  894. DWORD dwSignature = MAP_LEADING_SIGNATURE;
  895. DWORD dwWritten = 0;
  896. bRes = WriteFile(hFile, &dwSignature, sizeof(DWORD), &dwWritten, 0);
  897. if (!bRes || dwWritten != sizeof(DWORD))
  898. {
  899. m_dwStatus = ERROR_DISK_FULL;
  900. return m_dwStatus;
  901. }
  902. // Write transaction version.
  903. // ==========================
  904. bRes = WriteFile(hFile, &m_dwTransVersion, sizeof(DWORD), &dwWritten, 0);
  905. if (!bRes || dwWritten != sizeof(DWORD))
  906. {
  907. m_dwStatus = ERROR_DISK_FULL;
  908. return m_dwStatus;
  909. }
  910. bRes = WriteFile(hFile, &m_dwPhysPagesA, sizeof(DWORD), &dwWritten, 0);
  911. if (!bRes || dwWritten != sizeof(DWORD))
  912. {
  913. m_dwStatus = ERROR_DISK_FULL;
  914. return m_dwStatus;
  915. }
  916. // Write out page map
  917. // ==================
  918. DWORD dwNumPages = m_aPageMapA.size();
  919. bRes = WriteFile(hFile, &dwNumPages, sizeof(DWORD), &dwWritten, 0);
  920. if (!bRes || dwWritten != sizeof(DWORD))
  921. {
  922. m_dwStatus = ERROR_DISK_FULL;
  923. return m_dwStatus;
  924. }
  925. bRes = WriteFile(hFile, &m_aPageMapA[0], sizeof(DWORD)*dwNumPages, &dwWritten, 0);
  926. if (!bRes || dwWritten != sizeof(DWORD)*dwNumPages)
  927. return m_dwStatus = ERROR_DISK_FULL;
  928. // Now, write out physical free list.
  929. // ==================================
  930. DWORD dwFreeListSize = m_aPhysFreeListA.size();
  931. bRes = WriteFile(hFile, &dwFreeListSize, sizeof(DWORD), &dwWritten, 0);
  932. if (!bRes || dwWritten != sizeof(DWORD))
  933. {
  934. return m_dwStatus = ERROR_DISK_FULL;
  935. }
  936. bRes = WriteFile(hFile, &m_aPhysFreeListA[0], sizeof(DWORD)*dwFreeListSize, &dwWritten, 0);
  937. if (!bRes || dwWritten != sizeof(DWORD)*dwFreeListSize)
  938. {
  939. return m_dwStatus = ERROR_DISK_FULL;
  940. }
  941. // Write trailing signature.
  942. // =========================
  943. dwSignature = MAP_TRAILING_SIGNATURE;
  944. bRes = WriteFile(hFile, &dwSignature, sizeof(DWORD), &dwWritten, 0);
  945. if (!bRes || dwWritten != sizeof(DWORD))
  946. {
  947. m_dwStatus = ERROR_DISK_FULL;
  948. return m_dwStatus;
  949. }
  950. bRes = FlushFileBuffers(hFile);
  951. if (!bRes)
  952. {
  953. return m_dwStatus = GetLastError(); // Drastic failure
  954. }
  955. // auto close
  956. return NO_ERROR;
  957. }
  958. //***************************************************************************
  959. //
  960. // CPageFile::Trans_Commit
  961. //
  962. // Rolls the transaction forward (in-memory). A checkpoint may
  963. // occur afterwards (decided outside this function)
  964. //
  965. // The r/w cache contents are not affected except that they may contain
  966. // garbage pages no longer relevant.
  967. //
  968. //***************************************************************************
  969. // rev2
  970. DWORD CPageFile::Trans_Commit()
  971. {
  972. if (m_dwStatus)
  973. return m_dwStatus;
  974. if (!m_bInTransaction)
  975. return ERROR_INVALID_OPERATION;
  976. try
  977. {
  978. MoveCurrentToPrevious(m_aPageMapB);
  979. while (m_aDeferredFreeList.size())
  980. {
  981. m_aPhysFreeListB.push_back(m_aDeferredFreeList.back());
  982. m_aDeferredFreeList.pop_back();
  983. }
  984. m_aPageMapA = m_aPageMapB;
  985. m_aPhysFreeListA = m_aPhysFreeListB;
  986. m_aLogicalFreeListA = m_aLogicalFreeListB;
  987. m_aReplacedPagesA = m_aReplacedPagesB;
  988. m_dwPhysPagesA = m_dwPhysPagesB;
  989. }
  990. catch (CX_MemoryException &)
  991. {
  992. return m_dwStatus = ERROR_NOT_ENOUGH_MEMORY;
  993. }
  994. m_dwTransVersion++;
  995. m_bInTransaction = FALSE;
  996. return NO_ERROR;
  997. }
  998. //***************************************************************************
  999. //
  1000. // CPageFile::Trans_Rollback
  1001. //
  1002. // Rolls back a transaction within the current checkpoint window.
  1003. // If the cache is hosed, try to recover it.
  1004. //
  1005. //***************************************************************************
  1006. // rev2
  1007. DWORD CPageFile::Trans_Rollback()
  1008. {
  1009. if (m_dwStatus)
  1010. return m_dwStatus;
  1011. if (!m_bInTransaction)
  1012. return ERROR_INVALID_OPERATION;
  1013. m_bInTransaction = FALSE;
  1014. return m_dwStatus = ResyncMaps();
  1015. }
  1016. //***************************************************************************
  1017. //
  1018. // CPageFile::Trans_Checkpoint
  1019. //
  1020. //***************************************************************************
  1021. // rev2
  1022. DWORD CPageFile::Trans_Checkpoint()
  1023. {
  1024. DWORD dwRes;
  1025. std::vector <DWORD, wbem_allocator<DWORD> > & ref = m_aPageMapA;
  1026. if (m_dwStatus)
  1027. return m_dwStatus;
  1028. if (m_bInTransaction)
  1029. return ERROR_INVALID_OPERATION;
  1030. // Flush cache. If cache is not in a valid state, it
  1031. // will return the error/state immediately.
  1032. // ===================================================
  1033. dwRes = m_pCache->Flush();
  1034. if (dwRes)
  1035. return m_dwStatus = dwRes;
  1036. // Strip the hi bits from the page maps.
  1037. // =====================================
  1038. StripHiBits(ref);
  1039. // The replaced pages become added to the free list.
  1040. // =================================================
  1041. try
  1042. {
  1043. while (m_aReplacedPagesA.size())
  1044. {
  1045. m_aPhysFreeListA.push_back(m_aReplacedPagesA.back());
  1046. m_aReplacedPagesA.pop_back();
  1047. }
  1048. }
  1049. catch (CX_MemoryException &)
  1050. {
  1051. return m_dwStatus = ERROR_OUTOFMEMORY;
  1052. }
  1053. // Ensure maps are in sync.
  1054. // ========================
  1055. dwRes = ResyncMaps();
  1056. if (dwRes)
  1057. return m_dwStatus = dwRes;
  1058. // Write out temp map file. The atomic rename/rollforward
  1059. // is handled by CPageSource.
  1060. // =======================================================
  1061. try
  1062. {
  1063. WString sFile = m_sDirectory;
  1064. sFile += L"\\";
  1065. sFile += m_sMapFile;
  1066. sFile += L".NEW";
  1067. dwRes = WriteMap((const wchar_t *)sFile);
  1068. }
  1069. catch(CX_MemoryException &)
  1070. {
  1071. return m_dwStatus = ERROR_GEN_FAILURE;
  1072. }
  1073. m_dwLastCheckpoint = GetCurrentTime();
  1074. return m_dwStatus = dwRes; // May reflect WriteMap failure
  1075. }
  1076. //***************************************************************************
  1077. //
  1078. // CPageFile::InitFreeList
  1079. //
  1080. // Initializes the free list by a one-time analysis of the map.
  1081. //
  1082. //***************************************************************************
  1083. // rev2
  1084. DWORD CPageFile::InitFreeList()
  1085. {
  1086. DWORD dwRes = NO_ERROR;
  1087. try
  1088. {
  1089. for (DWORD i = 0; i < m_aPageMapA.size(); i++)
  1090. {
  1091. DWORD dwMapId = m_aPageMapA[i];
  1092. if (dwMapId == WMIREP_INVALID_PAGE)
  1093. m_aLogicalFreeListA.insert(m_aLogicalFreeListA.end(), i);
  1094. }
  1095. }
  1096. catch (CX_MemoryException &)
  1097. {
  1098. m_dwStatus = dwRes = ERROR_GEN_FAILURE;
  1099. }
  1100. return dwRes;
  1101. }
  1102. //***************************************************************************
  1103. //
  1104. // CPageFile::Trans_Begin
  1105. //
  1106. //***************************************************************************
  1107. // rev2
  1108. DWORD CPageFile::Trans_Begin()
  1109. {
  1110. if (m_dwStatus)
  1111. return m_dwStatus;
  1112. if (m_bInTransaction)
  1113. {
  1114. return ERROR_INVALID_OPERATION;
  1115. }
  1116. DWORD dwRes = ResyncMaps();
  1117. if (dwRes)
  1118. {
  1119. return m_dwStatus = ERROR_GEN_FAILURE;
  1120. }
  1121. m_bInTransaction = TRUE;
  1122. return NO_ERROR;
  1123. }
  1124. //***************************************************************************
  1125. //
  1126. // CPageFile::Release
  1127. //
  1128. // No checks for a checkpoint.
  1129. //
  1130. //***************************************************************************
  1131. // rev2
  1132. ULONG CPageFile::Release()
  1133. {
  1134. LONG lRes = InterlockedDecrement(&m_lRef);
  1135. if (lRes != 0)
  1136. return (ULONG) lRes;
  1137. delete this;
  1138. return 0;
  1139. }
  1140. //***************************************************************************
  1141. //
  1142. // ShellSort
  1143. //
  1144. // Generic DWORD sort using Donald Shell algorithm.
  1145. //
  1146. //***************************************************************************
  1147. // rev2
  1148. static void ShellSort(std::vector <DWORD, wbem_allocator<DWORD> > &Array)
  1149. {
  1150. for (int nInterval = 1; nInterval < Array.size() / 9; nInterval = nInterval * 3 + 1);
  1151. while (nInterval)
  1152. {
  1153. for (int iCursor = nInterval; iCursor < Array.size(); iCursor++)
  1154. {
  1155. int iBackscan = iCursor;
  1156. while (iBackscan - nInterval >= 0 && Array[iBackscan] < Array[iBackscan-nInterval])
  1157. {
  1158. DWORD dwTemp = Array[iBackscan-nInterval];
  1159. Array[iBackscan-nInterval] = Array[iBackscan];
  1160. Array[iBackscan] = dwTemp;
  1161. iBackscan -= nInterval;
  1162. }
  1163. }
  1164. nInterval /= 3;
  1165. }
  1166. }
  1167. //***************************************************************************
  1168. //
  1169. // StripHiBits
  1170. //
  1171. // Removes the hi bit from the physical disk ids so that they are no
  1172. // longer marked as 'replaced' in a transaction.
  1173. //
  1174. //***************************************************************************
  1175. // rev2
  1176. void StripHiBits(std::vector <DWORD, wbem_allocator<DWORD> > &Array)
  1177. {
  1178. for (int i = 0; i < Array.size(); i++)
  1179. {
  1180. DWORD dwVal = Array[i];
  1181. if (dwVal != WMIREP_INVALID_PAGE)
  1182. Array[i] = MERE_PAGE_ID(dwVal);
  1183. }
  1184. }
  1185. //***************************************************************************
  1186. //
  1187. // MoveCurrentToPrevious
  1188. //
  1189. // Removes the CURRENT_TRANSACTION bit from the array and makes
  1190. // it the PREVIOUS_TRANSACTION.
  1191. //
  1192. //***************************************************************************
  1193. // rev2
  1194. void MoveCurrentToPrevious(std::vector <DWORD, wbem_allocator<DWORD> > &Array)
  1195. {
  1196. for (int i = 0; i < Array.size(); i++)
  1197. {
  1198. DWORD dwVal = Array[i];
  1199. if (dwVal != WMIREP_INVALID_PAGE && (dwVal & CURRENT_TRANSACTION))
  1200. Array[i] = MERE_PAGE_ID(dwVal) | PREVIOUS_TRANSACTION;
  1201. }
  1202. }
  1203. //***************************************************************************
  1204. //
  1205. // CPageFile::FreePage
  1206. //
  1207. // Frees a page within the current transaction. The logical id
  1208. // is not removed from the map; its entry is simply assigned to
  1209. // 'InvalidPage' (0xFFFFFFFF) and the entry is added to the
  1210. // logical free list.
  1211. //
  1212. // If the associated physical page has been written within
  1213. // the transaction, it is simply added to the free list. If the page
  1214. // was never written to within this transaction, it is added to
  1215. // the replaced list.
  1216. //
  1217. //***************************************************************************
  1218. // rev2
  1219. DWORD CPageFile::FreePage(
  1220. DWORD dwFlags,
  1221. DWORD dwId
  1222. )
  1223. {
  1224. DWORD dwPhysId;
  1225. // Ensure the object is still valid for further operations.
  1226. // ========================================================
  1227. if (m_dwStatus != 0)
  1228. return m_dwStatus;
  1229. if (!m_bInTransaction)
  1230. {
  1231. return ERROR_INVALID_OPERATION;
  1232. }
  1233. // Make sure the page is 'freeable'.
  1234. // =================================
  1235. if (dwId >= m_aPageMapB.size())
  1236. return ERROR_INVALID_PARAMETER;
  1237. // Remove from page map.
  1238. // =====================
  1239. try
  1240. {
  1241. dwPhysId = m_aPageMapB[dwId];
  1242. if (dwPhysId == WMIREP_INVALID_PAGE)
  1243. return ERROR_INVALID_OPERATION; // Freeing a 'freed' page?
  1244. m_aPageMapB[dwId] = WMIREP_INVALID_PAGE;
  1245. m_aLogicalFreeListB.insert(m_aLogicalFreeListB.end(), MERE_PAGE_ID(dwId));
  1246. }
  1247. catch (CX_MemoryException &)
  1248. {
  1249. m_dwStatus = ERROR_OUTOFMEMORY;
  1250. return m_dwStatus;
  1251. }
  1252. if (dwPhysId == WMIREP_RESERVED_PAGE)
  1253. {
  1254. // The logical page was freed without being ever actually committed to
  1255. // a physical page. Legal, but weird. The caller had a change of heart.
  1256. return NO_ERROR;
  1257. }
  1258. // Examine physical page to determine its ancestry. There are
  1259. // three cases.
  1260. // 1. If the page was created within the current transaction,
  1261. // we simply add it back to the free list.
  1262. // 2. If the page was created in a previous transaction, we add
  1263. // it to the deferred free list.
  1264. // 3. If the page was created in the previous checkpoint, we add
  1265. // it to the replaced pages list.
  1266. // ==============================================================
  1267. try
  1268. {
  1269. if (dwPhysId & CURRENT_TRANSACTION)
  1270. m_aPhysFreeListB.insert(m_aPhysFreeListB.end(), MERE_PAGE_ID(dwPhysId));
  1271. else if (dwPhysId & PREVIOUS_TRANSACTION)
  1272. m_aDeferredFreeList.insert(m_aDeferredFreeList.end(), MERE_PAGE_ID(dwPhysId));
  1273. else // previous checkpoint
  1274. m_aReplacedPagesB.insert(m_aReplacedPagesB.end(), MERE_PAGE_ID(dwPhysId));
  1275. }
  1276. catch(...)
  1277. {
  1278. m_dwStatus = ERROR_OUTOFMEMORY;
  1279. }
  1280. return m_dwStatus;
  1281. }
  1282. //***************************************************************************
  1283. //
  1284. // CPageFile::GetPage
  1285. //
  1286. // Gets a page. Doesn't have to be within a transaction. However, the
  1287. // "B" generation map is always used so that getting within a transaction
  1288. // will reference the correct page.
  1289. //
  1290. //***************************************************************************
  1291. // rev2
  1292. DWORD CPageFile::GetPage(
  1293. DWORD dwId,
  1294. DWORD dwFlags,
  1295. LPVOID pPage
  1296. )
  1297. {
  1298. DWORD dwRes;
  1299. if (m_dwStatus != 0)
  1300. return m_dwStatus;
  1301. if (pPage == 0)
  1302. return ERROR_INVALID_PARAMETER;
  1303. CInCritSec _(&m_cs);
  1304. // Determine physical id from logical id.
  1305. // ======================================
  1306. if (dwId >= m_aPageMapB.size())
  1307. return ERROR_FILE_NOT_FOUND;
  1308. DWORD dwPhysId = m_aPageMapB[dwId];
  1309. if (dwPhysId == WMIREP_INVALID_PAGE || dwPhysId == WMIREP_RESERVED_PAGE)
  1310. return ERROR_INVALID_OPERATION;
  1311. try
  1312. {
  1313. LPBYTE pTemp = 0;
  1314. dwRes = m_pCache->Read(MERE_PAGE_ID(dwPhysId), &pTemp);
  1315. if (dwRes == 0)
  1316. memcpy(pPage, pTemp, m_dwPageSize);
  1317. }
  1318. catch(...)
  1319. {
  1320. dwRes = ERROR_GEN_FAILURE;
  1321. }
  1322. return m_dwStatus = dwRes;
  1323. }
  1324. //***************************************************************************
  1325. //
  1326. // CPageFile::PutPage
  1327. //
  1328. // Writes a page. Must be within a transaction. If the page has been
  1329. // written for the first time within the transaction, a new replacement
  1330. // page is allocated and the original page is added to the 'replaced'
  1331. // pages list. If the page has already been updated within this transaction,
  1332. // it is simply updated again. We know this because the physical page
  1333. // id has the ms bit set (MAP_REPLACED_PAGE_FLAG).
  1334. //
  1335. //***************************************************************************
  1336. // rev2
  1337. DWORD CPageFile::PutPage(
  1338. DWORD dwId,
  1339. DWORD dwFlags,
  1340. LPVOID pPage
  1341. )
  1342. {
  1343. DWORD dwRes = 0, dwNewPhysId = WMIREP_INVALID_PAGE;
  1344. if (m_dwStatus != 0)
  1345. return m_dwStatus;
  1346. if (pPage == 0)
  1347. return ERROR_INVALID_PARAMETER;
  1348. if (!m_bInTransaction)
  1349. return ERROR_INVALID_OPERATION;
  1350. // Allocate some memory to hold the page, since we are reading
  1351. // the caller's copy and not acquiring it.
  1352. // ============================================================
  1353. LPBYTE pPageCopy = new BYTE[m_dwPageSize];
  1354. if (pPageCopy == 0)
  1355. return m_dwStatus = ERROR_OUTOFMEMORY;
  1356. memcpy(pPageCopy, pPage, m_dwPageSize);
  1357. std::auto_ptr <BYTE> _autodelete(pPageCopy);
  1358. // Look up the page.
  1359. // =================
  1360. if (dwId >= m_aPageMapB.size())
  1361. return ERROR_INVALID_PARAMETER;
  1362. DWORD dwPhysId = m_aPageMapB[dwId];
  1363. if (dwPhysId == WMIREP_INVALID_PAGE) // Unexpected
  1364. return ERROR_GEN_FAILURE;
  1365. // See if the page has already been written within this transaction.
  1366. // =================================================================
  1367. if ((CURRENT_TRANSACTION & dwPhysId)!= 0 && dwPhysId != WMIREP_RESERVED_PAGE)
  1368. {
  1369. // Just update it again.
  1370. // =====================
  1371. try
  1372. {
  1373. dwRes = m_pCache->Write(MERE_PAGE_ID(dwPhysId), LPBYTE(pPageCopy));
  1374. }
  1375. catch(...)
  1376. {
  1377. dwRes = ERROR_GEN_FAILURE;
  1378. }
  1379. if (dwRes == 0)
  1380. _autodelete.release(); // memory acquired by cache
  1381. return m_dwStatus = dwRes;
  1382. }
  1383. // If here, we are going to have to get a new page for writing, regardless
  1384. // of any special casing. So, we'll do that part first and then decide
  1385. // what to do with the old physical page.
  1386. // ========================================================================
  1387. dwRes = AllocPhysPage(&dwNewPhysId);
  1388. if (dwRes)
  1389. {
  1390. // Horrific. Now what? We have to rollback to checkpoint.
  1391. return m_dwStatus = dwRes;
  1392. }
  1393. m_aPageMapB[dwId] = dwNewPhysId | CURRENT_TRANSACTION;
  1394. try
  1395. {
  1396. dwRes = m_pCache->Write(MERE_PAGE_ID(dwNewPhysId), LPBYTE(pPageCopy));
  1397. }
  1398. catch (...)
  1399. {
  1400. dwRes = ERROR_GEN_FAILURE;
  1401. }
  1402. if (dwRes)
  1403. return m_dwStatus = dwRes;
  1404. _autodelete.release(); // Memory safely acquired by cache
  1405. // If the old page ID was WMIREP_RESERVED_PAGE, we actually were
  1406. // creating a page and there was no old page to update.
  1407. // =====================================================================
  1408. if (dwPhysId != WMIREP_RESERVED_PAGE)
  1409. {
  1410. // If here, the old page was either part of the previous checkpoint
  1411. // or the previous set of transactions (the case of rewriting in the
  1412. // current transaction was handled up above).
  1413. try
  1414. {
  1415. if (dwPhysId & PREVIOUS_TRANSACTION)
  1416. m_aDeferredFreeList.insert(m_aDeferredFreeList.end(), MERE_PAGE_ID(dwPhysId));
  1417. else
  1418. m_aReplacedPagesB.insert(m_aReplacedPagesB.end(), MERE_PAGE_ID(dwPhysId));
  1419. }
  1420. catch (CX_MemoryException &)
  1421. {
  1422. m_dwStatus = ERROR_OUTOFMEMORY;
  1423. return m_dwStatus;
  1424. }
  1425. }
  1426. return dwRes;
  1427. }
  1428. //***************************************************************************
  1429. //
  1430. // CPageFile::ReclaimLogicalPages
  1431. //
  1432. // Reclaims <dwCount> contiguous logical pages from the free list, if possible.
  1433. // This is done by simply sorting the free list, and then seeing if
  1434. // any contiguous entries have successive ids.
  1435. //
  1436. // Returns NO_ERROR on success, ERROR_NOT_FOUND if no sequence could be
  1437. // found, or other errors which are considered critical.
  1438. //
  1439. // Callers verified for proper use of return code.
  1440. //
  1441. //***************************************************************************
  1442. // rev2
  1443. DWORD CPageFile::ReclaimLogicalPages(
  1444. DWORD dwCount,
  1445. DWORD *pdwId
  1446. )
  1447. {
  1448. std::vector <DWORD, wbem_allocator<DWORD> > &v = m_aLogicalFreeListB;
  1449. DWORD dwSize = v.size();
  1450. if (dwSize < dwCount)
  1451. return ERROR_NOT_FOUND;
  1452. // Special case for one page.
  1453. // ==========================
  1454. if (dwCount == 1)
  1455. {
  1456. try
  1457. {
  1458. *pdwId = v.back();
  1459. v.pop_back();
  1460. m_aPageMapB[*pdwId] = WMIREP_RESERVED_PAGE;
  1461. }
  1462. catch(...)
  1463. {
  1464. return m_dwStatus = ERROR_GEN_FAILURE;
  1465. }
  1466. return NO_ERROR;
  1467. }
  1468. // If here, a multi-page sequence was requested.
  1469. // =============================================
  1470. ShellSort(v);
  1471. DWORD dwContiguous = 1;
  1472. DWORD dwStart = 0;
  1473. for (DWORD dwIx = 0; dwIx+1 < dwSize; dwIx++)
  1474. {
  1475. if (v[dwIx]+1 == v[dwIx+1])
  1476. {
  1477. dwContiguous++;
  1478. }
  1479. else
  1480. {
  1481. dwContiguous = 1;
  1482. dwStart = dwIx + 1;
  1483. }
  1484. // Have we achieved our goal?
  1485. if (dwContiguous == dwCount)
  1486. {
  1487. *pdwId = v[dwStart];
  1488. // Remove reclaimed pages from free list.
  1489. // ======================================
  1490. DWORD dwCount2 = dwCount;
  1491. try
  1492. {
  1493. v.erase(v.begin()+dwStart, v.begin()+dwStart+dwCount);
  1494. }
  1495. catch(...)
  1496. {
  1497. m_dwStatus = ERROR_OUTOFMEMORY;
  1498. return m_dwStatus;
  1499. }
  1500. // Change entries in page map to 'reserved'
  1501. // ========================================
  1502. dwCount2 = dwCount;
  1503. for (DWORD i = *pdwId; dwCount2--; i++)
  1504. {
  1505. m_aPageMapB[i] = WMIREP_RESERVED_PAGE;
  1506. }
  1507. return NO_ERROR;
  1508. }
  1509. }
  1510. return ERROR_NOT_FOUND;
  1511. }
  1512. //***************************************************************************
  1513. //
  1514. // CPageFile::AllocPhysPage
  1515. //
  1516. // Finds a free page, first by attempting to reuse the free list,
  1517. // and only if it is zero-length by allocating a new extent to the file.
  1518. //
  1519. // The page is marked as CURRENT_TRANSACTION before being returned.
  1520. //
  1521. //***************************************************************************
  1522. // rev2
  1523. DWORD CPageFile::AllocPhysPage(DWORD *pdwId)
  1524. {
  1525. // Check the physical free list.
  1526. // =============================
  1527. if (m_aPhysFreeListB.size() == 0)
  1528. {
  1529. // No free pages. Allocate a new one.
  1530. // ===================================
  1531. if (m_dwPhysPagesB == 0x3FFFFFFF) // No more room
  1532. {
  1533. *pdwId = WMIREP_INVALID_PAGE;
  1534. return m_dwStatus = ERROR_DISK_FULL;
  1535. }
  1536. *pdwId = m_dwPhysPagesB++;
  1537. return NO_ERROR;
  1538. }
  1539. // Get the free page id.
  1540. // =====================
  1541. *pdwId = m_aPhysFreeListB[0];
  1542. // Remove the entry from the free list.
  1543. // ====================================
  1544. try
  1545. {
  1546. m_aPhysFreeListB.erase(m_aPhysFreeListB.begin());
  1547. }
  1548. catch (CX_MemoryException &)
  1549. {
  1550. return m_dwStatus = ERROR_GEN_FAILURE;
  1551. }
  1552. return NO_ERROR;
  1553. }
  1554. //***************************************************************************
  1555. //
  1556. // CPageFile::NewPage
  1557. //
  1558. // Allocates one or more contiguous logical page ids for writing.
  1559. //
  1560. // This function makes no reference or use of physical pages.
  1561. //
  1562. // First examines the free list. If there aren't any, then a new range
  1563. // of ids is assigned. These pages must be freed, even if they aren't
  1564. // written, once this call is completed.
  1565. //
  1566. //***************************************************************************
  1567. // rev2
  1568. DWORD CPageFile::NewPage(
  1569. DWORD dwFlags,
  1570. DWORD dwRequestedCount,
  1571. DWORD *pdwFirstId
  1572. )
  1573. {
  1574. DWORD dwRes;
  1575. if (m_dwStatus != 0)
  1576. return m_dwStatus;
  1577. if (!m_bInTransaction)
  1578. return ERROR_INVALID_OPERATION;
  1579. // See if the logical free list can satisfy the request.
  1580. // =====================================================
  1581. dwRes = ReclaimLogicalPages(dwRequestedCount, pdwFirstId);
  1582. if (dwRes == NO_ERROR)
  1583. return NO_ERROR;
  1584. if (dwRes != ERROR_NOT_FOUND)
  1585. {
  1586. m_dwStatus = dwRes;
  1587. return m_dwStatus;
  1588. }
  1589. // If here, we have to allocate new pages altogether.
  1590. // We do this by adding them to the map as 'reserved'
  1591. // pages.
  1592. // ===================================================
  1593. DWORD dwStart = m_aPageMapB.size();
  1594. for (DWORD dwIx = 0; dwIx < dwRequestedCount; dwIx++)
  1595. {
  1596. try
  1597. {
  1598. m_aPageMapB.insert(m_aPageMapB.end(), WMIREP_RESERVED_PAGE);
  1599. }
  1600. catch(...)
  1601. {
  1602. m_dwStatus = ERROR_OUTOFMEMORY;
  1603. return m_dwStatus;
  1604. }
  1605. }
  1606. *pdwFirstId = dwStart;
  1607. return NO_ERROR;
  1608. }
  1609. //***************************************************************************
  1610. //
  1611. // CPageFile::CPageFile
  1612. //
  1613. //***************************************************************************
  1614. // rev2
  1615. CPageFile::CPageFile()
  1616. {
  1617. m_lRef = 1;
  1618. m_dwStatus = 0;
  1619. m_dwPageSize = 0;
  1620. m_dwCachePromotionThreshold = 0;
  1621. m_dwCacheSpillRatio = 0;
  1622. m_pCache = 0;
  1623. m_bInTransaction = 0;
  1624. m_dwLastCheckpoint = GetCurrentTime();
  1625. m_dwTransVersion = 0;
  1626. m_dwPhysPagesA = 0;
  1627. m_dwPhysPagesB = 0;
  1628. m_bCsInit = false;
  1629. }
  1630. //***************************************************************************
  1631. //
  1632. // CPageFile::~CPageFile
  1633. //
  1634. //***************************************************************************
  1635. // rev2
  1636. CPageFile::~CPageFile()
  1637. {
  1638. if (m_bCsInit)
  1639. DeleteCriticalSection(&m_cs);
  1640. delete m_pCache;
  1641. }
  1642. //***************************************************************************
  1643. //
  1644. // FileExists
  1645. //
  1646. //***************************************************************************
  1647. // rev2
  1648. static BOOL FileExists(LPCWSTR pszFile)
  1649. {
  1650. HANDLE hTest = CreateFileW(pszFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
  1651. OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  1652. if (hTest == INVALID_HANDLE_VALUE)
  1653. return FALSE;
  1654. CloseHandle(hTest);
  1655. return TRUE;
  1656. }
  1657. //***************************************************************************
  1658. //
  1659. // CPageFile::Map_Startup
  1660. //
  1661. // Since this can only happen *after* a successful checkpoint or
  1662. // a reboot, we won't set any internal status, as it doesn't affect
  1663. // the page set. It only affects rollforward/rollback at the checkpoint
  1664. // level.
  1665. //
  1666. //***************************************************************************
  1667. // rev2
  1668. DWORD CPageFile::Map_Startup(
  1669. LPCWSTR pszDirectory,
  1670. LPCWSTR pszBTreeMap,
  1671. LPCWSTR pszObjMap
  1672. )
  1673. {
  1674. // Compute various filenames.
  1675. // ==========================
  1676. WString sObjMap;
  1677. WString sBTreeMap;
  1678. WString sObjMapNew;
  1679. WString sBTreeMapNew;
  1680. WString sObjData;
  1681. WString SBTreeData;
  1682. WString sSemaphore;
  1683. try
  1684. {
  1685. sObjMap = pszDirectory;
  1686. sObjMap += L"\\";
  1687. sObjMap += pszObjMap;
  1688. sBTreeMap = pszDirectory;
  1689. sBTreeMap += L"\\";
  1690. sBTreeMap += pszBTreeMap;
  1691. sObjMapNew = sObjMap;
  1692. sObjMapNew += L".NEW";
  1693. sBTreeMapNew = sBTreeMap;
  1694. sBTreeMapNew += L".NEW";
  1695. sObjData = pszDirectory;
  1696. sObjData += L"\\" WMIREP_OBJECT_DATA;
  1697. SBTreeData = pszDirectory;
  1698. SBTreeData += L"\\" WMIREP_BTREE_DATA;
  1699. sSemaphore = pszDirectory;
  1700. sSemaphore += L"\\ROLL_FORWARD";
  1701. }
  1702. catch(CX_MemoryException &)
  1703. {
  1704. return ERROR_OUTOFMEMORY;
  1705. }
  1706. // To decide what to do, we need to know which files exist and which don't.
  1707. // ========================================================================
  1708. BOOL bSemaphore = FileExists((const wchar_t *)sSemaphore);
  1709. BOOL bExists_BTreeMap = FileExists((const wchar_t *)sBTreeMap);
  1710. BOOL bExists_BTreeMapNew = FileExists((const wchar_t *)sBTreeMapNew);
  1711. BOOL bExists_ObjMap = FileExists((const wchar_t *)sObjMap);
  1712. BOOL bExists_ObjMapNew = FileExists((const wchar_t *)sObjMapNew);
  1713. if (!bSemaphore)
  1714. {
  1715. // If here, we are not a in a roll-forward. Just clean up any spurious
  1716. // .NEW map files and revert to the old ones, if any.
  1717. // ====================================================================
  1718. DeleteFileW((const wchar_t *)sObjMapNew);
  1719. DeleteFileW((const wchar_t *)sBTreeMapNew);
  1720. //If only one of the MAP files exist then we are in trouble also... so detect that and
  1721. //recover if necessary...
  1722. if (bExists_BTreeMap ^ bExists_ObjMap)
  1723. {
  1724. DeleteFileW((const wchar_t *)sBTreeMap);
  1725. DeleteFileW((const wchar_t *)sObjMap);
  1726. DeleteFileW((const wchar_t *)sObjData);
  1727. DeleteFileW((const wchar_t *)SBTreeData);
  1728. }
  1729. //OK, so now we need to check if we have both MAP files, we should have both of the
  1730. //data files also!
  1731. else if (bExists_BTreeMap &&
  1732. bExists_ObjMap &&
  1733. (!FileExists((const wchar_t *)SBTreeData) || !FileExists((const wchar_t *)sObjData)))
  1734. {
  1735. //We have MAP files and not all the data files! We need to tidy up!
  1736. DeleteFileW((const wchar_t *)sBTreeMap);
  1737. DeleteFileW((const wchar_t *)sObjMap);
  1738. DeleteFileW((const wchar_t *)sObjData);
  1739. DeleteFileW((const wchar_t *)SBTreeData);
  1740. }
  1741. return NO_ERROR;
  1742. }
  1743. //Deal with foll forward of tree map file...
  1744. if (bExists_BTreeMapNew)
  1745. {
  1746. if (bExists_BTreeMap)
  1747. DeleteFileW((const wchar_t *)sBTreeMap);
  1748. MoveFileW((const wchar_t *)sBTreeMapNew,(const wchar_t *)sBTreeMap);
  1749. }
  1750. //Deal with foll forward of object map file...
  1751. if (bExists_ObjMapNew)
  1752. {
  1753. if (bExists_ObjMap)
  1754. DeleteFileW((const wchar_t *)sObjMap);
  1755. MoveFileW((const wchar_t *)sObjMapNew,(const wchar_t *)sObjMap);
  1756. }
  1757. //So we now _think_ we have recovered OK, we need to now check to make sure
  1758. //we have both main MAP files. If now we need to delete and start again
  1759. bExists_BTreeMap = FileExists((const wchar_t *)sBTreeMap);
  1760. bExists_ObjMap = FileExists((const wchar_t *)sObjMap);
  1761. if (bExists_BTreeMap ^ bExists_ObjMap)
  1762. {
  1763. DeleteFileW((const wchar_t *)sBTreeMap);
  1764. DeleteFileW((const wchar_t *)sObjMap);
  1765. DeleteFileW((const wchar_t *)sObjData);
  1766. DeleteFileW((const wchar_t *)SBTreeData);
  1767. }
  1768. //OK, so now we need to check if we have both MAP files, we should have both of the
  1769. //data files also!
  1770. else if (bExists_BTreeMap &&
  1771. bExists_ObjMap &&
  1772. (!FileExists((const wchar_t *)SBTreeData) || !FileExists((const wchar_t *)sObjData)))
  1773. {
  1774. //We have MAP files and not all the data files! We need to tidy up!
  1775. DeleteFileW((const wchar_t *)sBTreeMap);
  1776. DeleteFileW((const wchar_t *)sObjMap);
  1777. DeleteFileW((const wchar_t *)sObjData);
  1778. DeleteFileW((const wchar_t *)SBTreeData);
  1779. }
  1780. DeleteFileW((const wchar_t *)sSemaphore);
  1781. return NO_ERROR;
  1782. }
  1783. //***************************************************************************
  1784. //
  1785. // CPageFile::Init
  1786. //
  1787. // If failure occurs, we assume another call will follow.
  1788. //
  1789. //***************************************************************************
  1790. // rev2
  1791. DWORD CPageFile::Init(
  1792. LPCWSTR pszDirectory,
  1793. LPCWSTR pszMainFile,
  1794. LPCWSTR pszMapFile,
  1795. DWORD dwPageSize,
  1796. DWORD dwCacheSize,
  1797. DWORD dwCachePromotionThreshold,
  1798. DWORD dwCacheSpillRatio
  1799. )
  1800. {
  1801. if (pszDirectory == 0 || pszMainFile == 0 || pszMapFile == 0 ||
  1802. dwPageSize == 0
  1803. )
  1804. return ERROR_INVALID_PARAMETER;
  1805. try
  1806. {
  1807. if (!m_bCsInit)
  1808. {
  1809. InitializeCriticalSection(&m_cs);
  1810. m_bCsInit = true;
  1811. }
  1812. m_dwPageSize = dwPageSize;
  1813. m_sDirectory = pszDirectory;
  1814. m_sMainFile = pszMainFile;
  1815. m_sMapFile = pszMapFile;
  1816. m_dwCacheSpillRatio = dwCacheSpillRatio;
  1817. m_dwCachePromotionThreshold = dwCachePromotionThreshold;
  1818. }
  1819. catch(...)
  1820. {
  1821. return m_dwStatus = ERROR_OUTOFMEMORY;
  1822. }
  1823. return 0;
  1824. }
  1825. DWORD CPageFile::DeInit()
  1826. {
  1827. delete m_pCache;
  1828. m_pCache = NULL;
  1829. m_aPageMapA.clear();
  1830. m_aPhysFreeListA.clear();
  1831. m_aLogicalFreeListA.clear();
  1832. m_aReplacedPagesA.clear();
  1833. m_aPageMapB.clear();
  1834. m_aPhysFreeListB.clear();
  1835. m_aLogicalFreeListB.clear();
  1836. m_aReplacedPagesB.clear();
  1837. m_aDeferredFreeList.clear();
  1838. return NO_ERROR;
  1839. }
  1840. DWORD CPageFile::ReInit()
  1841. {
  1842. //Reset the error!
  1843. m_dwStatus = 0;
  1844. m_pCache = new CPageCache;
  1845. if (!m_pCache)
  1846. return m_dwStatus = ERROR_OUTOFMEMORY;
  1847. try
  1848. {
  1849. WString sFile = m_sDirectory;
  1850. sFile += L"\\";
  1851. sFile += m_sMainFile;
  1852. DWORD dwRet = m_pCache->Init((const wchar_t *)sFile, m_dwPageSize,
  1853. m_dwCachePromotionThreshold, m_dwCacheSpillRatio
  1854. );
  1855. if ((dwRet != 0) && (dwRet != ERROR_FILE_NOT_FOUND))
  1856. m_dwStatus = dwRet;
  1857. }
  1858. catch(CX_MemoryException &)
  1859. {
  1860. m_dwStatus = ERROR_OUTOFMEMORY;
  1861. }
  1862. if (m_dwStatus)
  1863. {
  1864. delete m_pCache;
  1865. m_pCache = NULL;
  1866. return m_dwStatus;
  1867. }
  1868. // Read the map.
  1869. // =============
  1870. m_dwStatus = ReadMap();
  1871. if (m_dwStatus != ERROR_FILE_NOT_FOUND && m_dwStatus != NO_ERROR)
  1872. {
  1873. delete m_pCache;
  1874. m_pCache = NULL;
  1875. return m_dwStatus; // Some kind of error and yet the file exists !?
  1876. }
  1877. m_dwStatus = ResyncMaps();
  1878. if (m_dwStatus != NO_ERROR)
  1879. {
  1880. delete m_pCache;
  1881. m_pCache = NULL;
  1882. }
  1883. m_bInTransaction = FALSE;
  1884. return m_dwStatus;
  1885. }
  1886. //***************************************************************************
  1887. //
  1888. // CPageFile::Dump
  1889. //
  1890. //***************************************************************************
  1891. //
  1892. void CPageFile::DumpFreeListInfo()
  1893. {
  1894. int i;
  1895. printf("------Free List Info--------\n");
  1896. printf(" Phys Free List (B) =\n");
  1897. for (i = 0; i < m_aPhysFreeListB.size(); i++)
  1898. printf(" 0x%X\n", m_aPhysFreeListB[i]);
  1899. printf(" Replaced Pages (B) =\n");
  1900. for (i = 0; i < m_aReplacedPagesB.size(); i++)
  1901. printf(" 0x%X\n", m_aReplacedPagesB[i]);
  1902. printf(" Deferred Free List =\n");
  1903. for (i = 0; i < m_aDeferredFreeList.size(); i++)
  1904. printf(" 0x%X\n", m_aDeferredFreeList[i]);
  1905. printf("-----End Free List Info -----------\n");
  1906. printf(" Logical Free List =\n");
  1907. for (i = 0; i < m_aLogicalFreeListB.size(); i++)
  1908. printf(" 0x%X\n", m_aLogicalFreeListB[i]);
  1909. printf("-----End Free List Info -----------\n");
  1910. }
  1911. //***************************************************************************
  1912. //
  1913. // CPageFile::Dump
  1914. //
  1915. //***************************************************************************
  1916. // rev2
  1917. void CPageFile::Dump(FILE *f)
  1918. {
  1919. fprintf(f, "---Page File Dump---\n");
  1920. fprintf(f, "Ref count = %d\n", m_lRef);
  1921. fprintf(f, "Status = %d\n", m_dwStatus);
  1922. fprintf(f, "Page size = 0x%x\n", m_dwPageSize);
  1923. fprintf(f, "Directory = %S\n", (const wchar_t *)m_sDirectory);
  1924. fprintf(f, "Mainfile = %S\n", (const wchar_t *)m_sMainFile);
  1925. fprintf(f, "Mapfile = %S\n", (const wchar_t *)m_sMapFile);
  1926. fprintf(f, "In transaction = %d\n", m_bInTransaction);
  1927. fprintf(f, "Time since last checkpoint = %d\n", GetCurrentTime() - m_dwLastCheckpoint);
  1928. fprintf(f, "Transaction version = %d\n", m_dwTransVersion);
  1929. fprintf(f, " ---Logical Page Map <Generation A>---\n");
  1930. fprintf(f, " Phys pages = %d\n", m_dwPhysPagesA);
  1931. int i;
  1932. for (i = 0; i < m_aPageMapA.size(); i++)
  1933. fprintf(f, " Page[%d] = phys id 0x%x (%d)\n", i, m_aPageMapA[i], m_aPageMapA[i]);
  1934. fprintf(f, " ---<Generation A Physical Free List>---\n");
  1935. for (i = 0; i < m_aPhysFreeListA.size(); i++)
  1936. fprintf(f, " phys free = %d\n", m_aPhysFreeListA[i]);
  1937. fprintf(f, " ---<Generation A Logical Free List>---\n");
  1938. for (i = 0; i < m_aLogicalFreeListA.size(); i++)
  1939. fprintf(f, " logical free = %d\n", m_aLogicalFreeListA[i]);
  1940. fprintf(f, " ---<Generation A Replaced Page List>---\n");
  1941. for (i = 0; i < m_aReplacedPagesA.size(); i++)
  1942. fprintf(f, " replaced = %d\n", m_aReplacedPagesA[i]);
  1943. fprintf(f, " ---END Generation A mapping---\n");
  1944. fprintf(f, " ---Logical Page Map <Generation B>---\n");
  1945. fprintf(f, " Phys pages = %d\n", m_dwPhysPagesB);
  1946. for (i = 0; i < m_aPageMapB.size(); i++)
  1947. fprintf(f, " Page[%d] = phys id 0x%x (%d)\n", i, m_aPageMapB[i], m_aPageMapB[i]);
  1948. fprintf(f, " ---<Generation B Physical Free List>---\n");
  1949. for (i = 0; i < m_aPhysFreeListB.size(); i++)
  1950. fprintf(f, " phys free = %d\n", m_aPhysFreeListB[i]);
  1951. fprintf(f, " ---<Generation B Logical Free List>---\n");
  1952. for (i = 0; i < m_aLogicalFreeListB.size(); i++)
  1953. fprintf(f, " logical free = %d\n", m_aLogicalFreeListB[i]);
  1954. fprintf(f, " ---<Generation B Replaced Page List>---\n");
  1955. for (i = 0; i < m_aReplacedPagesB.size(); i++)
  1956. fprintf(f, " replaced = %d\n", m_aReplacedPagesB[i]);
  1957. fprintf(f, " ---END Generation B mapping---\n");
  1958. fprintf(f, "END Page File Dump\n");
  1959. }
  1960. //***************************************************************************
  1961. //
  1962. // CPageSource::GetBTreePageFile
  1963. //
  1964. //***************************************************************************
  1965. // rev2
  1966. DWORD CPageSource::GetBTreePageFile(OUT CPageFile **pPF)
  1967. {
  1968. if (m_pBTreePF == 0)
  1969. return ERROR_INVALID_OPERATION;
  1970. *pPF = m_pBTreePF;
  1971. m_pBTreePF->AddRef();
  1972. return NO_ERROR;
  1973. }
  1974. //***************************************************************************
  1975. //
  1976. // CPageSource::GetObjectHeapPageFile
  1977. //
  1978. //***************************************************************************
  1979. // rev2
  1980. DWORD CPageSource::GetObjectHeapPageFile(OUT CPageFile **pPF)
  1981. {
  1982. if (m_pObjPF == 0)
  1983. return ERROR_INVALID_OPERATION;
  1984. *pPF = m_pObjPF;
  1985. m_pObjPF->AddRef();
  1986. return NO_ERROR;
  1987. }
  1988. //***************************************************************************
  1989. //
  1990. // CPageSource::BeginTrans
  1991. //
  1992. // If either object gets messed up due to out-of-mem, out-of-disk for
  1993. // the cache, etc., then error codes will be returned. Calling this
  1994. // forever won't help anything. Rollback may help, but RollbackCheckpoint
  1995. // is likely required.
  1996. //
  1997. //*************************************************************************** //
  1998. // rev2
  1999. DWORD CPageSource::BeginTrans()
  2000. {
  2001. DWORD dwRes;
  2002. //In case of an error state, just return
  2003. dwRes = GetStatus();
  2004. if (dwRes != NO_ERROR)
  2005. return dwRes;
  2006. dwRes = m_pObjPF->Trans_Begin();
  2007. if (dwRes)
  2008. return dwRes;
  2009. dwRes = m_pBTreePF->Trans_Begin();
  2010. if (dwRes)
  2011. return dwRes;
  2012. return dwRes;
  2013. }
  2014. //***************************************************************************
  2015. //
  2016. // CPageSource::Init
  2017. //
  2018. // Called once at startup
  2019. //
  2020. //***************************************************************************
  2021. //
  2022. DWORD CPageSource::Init(
  2023. DWORD dwCacheSize,
  2024. DWORD dwCheckpointTime,
  2025. DWORD dwPageSize
  2026. )
  2027. {
  2028. DWORD dwRes;
  2029. wchar_t *p1 = 0, *p2 = 0;
  2030. p1 = new wchar_t[MAX_PATH+1];
  2031. if (!p1)
  2032. return ERROR_OUTOFMEMORY;
  2033. std::auto_ptr <wchar_t> _1(p1);
  2034. p2 = new wchar_t[MAX_PATH+1];
  2035. if (!p2)
  2036. return ERROR_OUTOFMEMORY;
  2037. std::auto_ptr <wchar_t> _2(p2);
  2038. m_dwCacheSize = dwCacheSize;
  2039. m_dwCheckpointInterval = dwCheckpointTime;
  2040. m_dwPageSize = dwPageSize;
  2041. // Set up working directory, filenames, etc.
  2042. // =========================================
  2043. HKEY hKey;
  2044. long lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  2045. L"SOFTWARE\\Microsoft\\WBEM\\CIMOM",
  2046. 0, KEY_READ, &hKey);
  2047. if (lRes)
  2048. return ERROR_GEN_FAILURE;
  2049. DWORD dwLen = MAX_PATH*2; // in bytes
  2050. lRes = RegQueryValueExW(hKey, L"Repository Directory", NULL, NULL,
  2051. (LPBYTE)(wchar_t*)p1, &dwLen);
  2052. ExpandEnvironmentStringsW(p1, p2, MAX_PATH);
  2053. try
  2054. {
  2055. m_sDirectory = p2;
  2056. m_sDirectory += L"\\FS";
  2057. m_sBTreeMap = WMIREP_BTREE_MAP;
  2058. m_sObjMap = WMIREP_OBJECT_MAP;
  2059. }
  2060. catch (CX_MemoryException &)
  2061. {
  2062. RegCloseKey(hKey);
  2063. return ERROR_GEN_FAILURE;
  2064. }
  2065. // Read cache settings.
  2066. // ====================
  2067. m_dwPageSize = WMIREP_PAGE_SIZE;
  2068. m_dwCacheSize = 64;
  2069. m_dwCachePromotionThreshold = 16;
  2070. m_dwCacheSpillRatio = 4;
  2071. DWORD dwTemp = 0;
  2072. dwLen = sizeof(DWORD);
  2073. lRes = RegQueryValueExW(hKey, L"Repository Page Size", NULL, NULL,
  2074. (LPBYTE)&dwTemp, &dwLen);
  2075. if (lRes == 0)
  2076. m_dwPageSize = dwTemp;
  2077. dwLen = sizeof(DWORD);
  2078. lRes = RegQueryValueExW(hKey, L"Repository Cache Size", NULL, NULL,
  2079. (LPBYTE)&dwTemp, &dwLen);
  2080. if (lRes == 0)
  2081. m_dwCacheSize = dwTemp;
  2082. dwLen = sizeof(DWORD);
  2083. lRes = RegQueryValueExW(hKey, L"Repository Cache Promotion Threshold", NULL, NULL,
  2084. (LPBYTE)&dwTemp, &dwLen);
  2085. if (lRes == 0)
  2086. m_dwCachePromotionThreshold = dwTemp;
  2087. dwLen = sizeof(DWORD);
  2088. lRes = RegQueryValueExW(hKey, L"Repository Cache Spill Ratio", NULL, NULL,
  2089. (LPBYTE)&dwTemp, &dwLen);
  2090. if (lRes == 0)
  2091. m_dwCacheSpillRatio = dwTemp;
  2092. RegCloseKey(hKey);
  2093. dwRes = Restart();
  2094. return dwRes;
  2095. }
  2096. //***************************************************************************
  2097. //
  2098. // CPageSource::Shutdown
  2099. //
  2100. //***************************************************************************
  2101. // rev2
  2102. DWORD CPageSource::Shutdown(DWORD dwFlags)
  2103. {
  2104. DWORD dwRes = Checkpoint();
  2105. if (m_pObjPF)
  2106. m_pObjPF->Release();
  2107. m_pObjPF = 0;
  2108. if (m_pBTreePF)
  2109. m_pBTreePF->Release();
  2110. m_pBTreePF = 0;
  2111. return dwRes;
  2112. }
  2113. //***************************************************************************
  2114. //
  2115. // CPageSource::CommitTrans
  2116. //
  2117. //***************************************************************************
  2118. // rev2
  2119. DWORD CPageSource::CommitTrans()
  2120. {
  2121. DWORD dwRes;
  2122. //In case of an error state, just return
  2123. dwRes = GetStatus();
  2124. if (dwRes != NO_ERROR)
  2125. return dwRes;
  2126. dwRes = m_pObjPF->Trans_Commit();
  2127. if (dwRes)
  2128. return dwRes;
  2129. dwRes = m_pBTreePF->Trans_Commit();
  2130. if (dwRes)
  2131. return dwRes;
  2132. if (m_pBTreePF->GetTransVersion() != m_pObjPF->GetTransVersion())
  2133. {
  2134. return ERROR_GEN_FAILURE;
  2135. }
  2136. return dwRes;
  2137. }
  2138. //***************************************************************************
  2139. //
  2140. // CPageSource::RollbackTrans
  2141. //
  2142. // This needs to succeed and clear out the out-of-memory status flag
  2143. // once it does.
  2144. //
  2145. //***************************************************************************
  2146. // rev2
  2147. DWORD CPageSource::RollbackTrans()
  2148. {
  2149. DWORD dwRes;
  2150. //In case of an error state, just return
  2151. dwRes = GetStatus();
  2152. if (dwRes != NO_ERROR)
  2153. return dwRes;
  2154. dwRes = m_pObjPF->Trans_Rollback();
  2155. if (dwRes)
  2156. return dwRes;
  2157. dwRes = m_pBTreePF->Trans_Rollback();
  2158. if (dwRes)
  2159. return dwRes;
  2160. return dwRes;
  2161. }
  2162. //***************************************************************************
  2163. //
  2164. // CPageSource::Checkpoint
  2165. //
  2166. //***************************************************************************
  2167. //
  2168. DWORD CPageSource::Checkpoint()
  2169. {
  2170. DWORD dwRes = 0;
  2171. //In case of an error state, just return
  2172. dwRes = GetStatus();
  2173. if (dwRes != NO_ERROR)
  2174. return dwRes;
  2175. // This is part of the two phase commit.
  2176. // These two calls are the prepare-to-commit aspect.
  2177. // The Map_Startup call later is the roll-forward to commit.
  2178. // If either of the following two calls fails, we can still
  2179. // roll back.
  2180. // =========================================================
  2181. dwRes = m_pObjPF->Trans_Checkpoint();
  2182. if (dwRes)
  2183. return dwRes;
  2184. dwRes = m_pBTreePF->Trans_Checkpoint();
  2185. if (dwRes)
  2186. return dwRes;
  2187. // Create semaphore 'roll forward file'
  2188. // ====================================
  2189. try
  2190. {
  2191. WString sSemaphore;
  2192. sSemaphore = m_sDirectory;
  2193. sSemaphore += L"\\ROLL_FORWARD";
  2194. HANDLE hSem = CreateFileW((const wchar_t *)sSemaphore, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, 0,
  2195. CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  2196. if (hSem == INVALID_HANDLE_VALUE)
  2197. return ERROR_GEN_FAILURE;
  2198. FlushFileBuffers(hSem);
  2199. CloseHandle(hSem);
  2200. }
  2201. catch (CX_MemoryException &)
  2202. {
  2203. return ERROR_OUTOFMEMORY;
  2204. }
  2205. // Now adjust everything for the next generation.
  2206. // ==============================================
  2207. dwRes = CPageFile::Map_Startup((const wchar_t *)m_sDirectory, (const wchar_t *)m_sBTreeMap, (const wchar_t *)m_sObjMap);
  2208. if (dwRes)
  2209. return dwRes;
  2210. m_dwLastCheckpoint = GetCurrentTime();
  2211. return NO_ERROR;
  2212. }
  2213. //***************************************************************************
  2214. //
  2215. // CPageSource::Checkpoint
  2216. //
  2217. // Effectively a restart, starting with the last checkpoint.
  2218. //
  2219. //***************************************************************************
  2220. //
  2221. DWORD CPageSource::CheckpointRollback()
  2222. {
  2223. // Dump all objects and reinit.
  2224. if (m_pObjPF)
  2225. m_pObjPF->DeInit();
  2226. if (m_pBTreePF)
  2227. m_pBTreePF->DeInit();
  2228. DWORD dwRet = Restart();
  2229. return dwRet;
  2230. }
  2231. //***************************************************************************
  2232. //
  2233. // CPageSource::Restart
  2234. //
  2235. //***************************************************************************
  2236. //
  2237. DWORD CPageSource::Restart()
  2238. {
  2239. DWORD dwRes;
  2240. // Do rollback or rollforward, depending on last system status.
  2241. // ============================================================
  2242. dwRes = CPageFile::Map_Startup((const wchar_t *)m_sDirectory, (const wchar_t *)m_sBTreeMap, (const wchar_t *)m_sObjMap);
  2243. if (dwRes)
  2244. return dwRes;
  2245. // Create new objects.
  2246. // ===================
  2247. for (int i = 0; i != 2; i++)
  2248. {
  2249. if (m_pObjPF == NULL)
  2250. {
  2251. m_pObjPF = new CPageFile;
  2252. if (m_pObjPF)
  2253. {
  2254. dwRes = m_pObjPF->Init(
  2255. (const wchar_t *)m_sDirectory,
  2256. WMIREP_OBJECT_DATA,
  2257. WMIREP_OBJECT_MAP,
  2258. m_dwPageSize,
  2259. m_dwCacheSize,
  2260. m_dwCachePromotionThreshold,
  2261. m_dwCacheSpillRatio
  2262. );
  2263. if (dwRes)
  2264. {
  2265. delete m_pObjPF;
  2266. m_pObjPF = NULL;
  2267. return dwRes;
  2268. }
  2269. }
  2270. }
  2271. if (m_pBTreePF == NULL)
  2272. {
  2273. m_pBTreePF = new CPageFile;
  2274. if (m_pBTreePF)
  2275. {
  2276. dwRes = m_pBTreePF->Init(
  2277. (const wchar_t *)m_sDirectory,
  2278. WMIREP_BTREE_DATA,
  2279. WMIREP_BTREE_MAP,
  2280. m_dwPageSize,
  2281. m_dwCacheSize,
  2282. m_dwCachePromotionThreshold,
  2283. m_dwCacheSpillRatio
  2284. );
  2285. if (dwRes)
  2286. {
  2287. delete m_pObjPF;
  2288. m_pObjPF = NULL;
  2289. delete m_pBTreePF;
  2290. m_pBTreePF = NULL;
  2291. return dwRes;
  2292. }
  2293. }
  2294. }
  2295. if (m_pObjPF == 0 || m_pBTreePF == 0)
  2296. {
  2297. delete m_pObjPF;
  2298. m_pObjPF = NULL;
  2299. delete m_pBTreePF;
  2300. m_pBTreePF = NULL;
  2301. return ERROR_OUTOFMEMORY;
  2302. }
  2303. dwRes = m_pObjPF->ReInit();
  2304. if (dwRes == ERROR_INVALID_DATA)
  2305. {
  2306. try
  2307. {
  2308. //Need to delete repository and start again!
  2309. WString str;
  2310. m_pObjPF->DeInit();
  2311. m_pBTreePF->DeInit();
  2312. str = m_sDirectory;
  2313. str += L"\\";
  2314. str += m_sBTreeMap;
  2315. DeleteFileW(str);
  2316. str = m_sDirectory;
  2317. str += L"\\";
  2318. str += m_sObjMap;
  2319. DeleteFileW(str);
  2320. }
  2321. catch (CX_MemoryException &)
  2322. {
  2323. return ERROR_OUTOFMEMORY;
  2324. }
  2325. continue;
  2326. }
  2327. else if (dwRes)
  2328. {
  2329. m_pObjPF->DeInit();
  2330. m_pBTreePF->DeInit();
  2331. return dwRes;
  2332. }
  2333. dwRes = m_pBTreePF->ReInit();
  2334. if (dwRes == ERROR_INVALID_DATA)
  2335. {
  2336. try
  2337. {
  2338. //Need to delete repository and start again!
  2339. WString str;
  2340. m_pObjPF->DeInit();
  2341. m_pBTreePF->DeInit();
  2342. str = m_sDirectory;
  2343. str += L"\\";
  2344. str += m_sBTreeMap;
  2345. DeleteFileW(str);
  2346. str = m_sDirectory;
  2347. str += L"\\";
  2348. str += m_sObjMap;
  2349. DeleteFileW(str);
  2350. }
  2351. catch (CX_MemoryException &)
  2352. {
  2353. return ERROR_OUTOFMEMORY;
  2354. }
  2355. continue;
  2356. }
  2357. else if (dwRes)
  2358. {
  2359. m_pObjPF->DeInit();
  2360. m_pBTreePF->DeInit();
  2361. return dwRes;
  2362. }
  2363. return NO_ERROR;
  2364. }
  2365. return dwRes;
  2366. }
  2367. //***************************************************************************
  2368. //
  2369. // CPageSource::RequiresCheckpoint
  2370. //
  2371. //***************************************************************************
  2372. // rev2
  2373. BOOL CPageSource::CheckpointRequired()
  2374. {
  2375. DWORD dwTime = GetCurrentTime();
  2376. if (dwTime - m_dwLastCheckpoint >= m_dwCheckpointInterval)
  2377. return TRUE;
  2378. return FALSE;
  2379. }
  2380. //***************************************************************************
  2381. //
  2382. // CPageSource::Dump
  2383. //
  2384. //***************************************************************************
  2385. // rev2
  2386. void CPageSource::Dump(FILE *f)
  2387. {
  2388. // no impl
  2389. }
  2390. //***************************************************************************
  2391. //
  2392. // CPageSource::CPageSource
  2393. //
  2394. //***************************************************************************
  2395. // rev2
  2396. CPageSource::CPageSource()
  2397. {
  2398. m_dwPageSize = 0;
  2399. m_dwLastCheckpoint = GetCurrentTime();
  2400. m_dwCheckpointInterval = 0;
  2401. m_pBTreePF = 0;
  2402. m_pObjPF = 0;
  2403. }
  2404. //***************************************************************************
  2405. //
  2406. // CPageSource::~CPageSource
  2407. //
  2408. //***************************************************************************
  2409. // rev2
  2410. CPageSource::~CPageSource()
  2411. {
  2412. if (m_pBTreePF)
  2413. m_pBTreePF->Release();
  2414. if (m_pObjPF)
  2415. m_pObjPF->Release();
  2416. }
  2417. //***************************************************************************
  2418. //
  2419. // CPageSource::EmptyCaches
  2420. //
  2421. //***************************************************************************
  2422. // rev2
  2423. DWORD CPageSource::EmptyCaches()
  2424. {
  2425. DWORD dwRet = ERROR_SUCCESS;
  2426. if (m_pBTreePF)
  2427. dwRet = m_pBTreePF->EmptyCache();
  2428. if (m_pObjPF && (dwRet == ERROR_SUCCESS))
  2429. dwRet = m_pObjPF->EmptyCache();
  2430. return dwRet;
  2431. }