Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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