Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1074 lines
24 KiB

  1. // FreeList.cpp -- Implementation for class CFreeList and interface IFreelist
  2. #include "StdAfx.h"
  3. HRESULT CFreeList::CreateFreeList
  4. (IITFileSystem *pITFS, CULINT cbPreallocated, IITFreeList **ppITFreeList)
  5. {
  6. CFreeList *pfl = New CFreeList(NULL);
  7. return FinishSetup(pfl? pfl->m_ImpIFreeList.InitNew(pITFS, cbPreallocated)
  8. : STG_E_INSUFFICIENTMEMORY,
  9. pfl, IID_IFreeListManager, (PPVOID)ppITFreeList
  10. );
  11. }
  12. HRESULT CFreeList::CreateAndSizeFreeList
  13. (IITFileSystem *pITFS, CULINT cbPreallocated, UINT cSlots, IITFreeList **ppITFreeList)
  14. {
  15. CFreeList *pfl = New CFreeList(NULL);
  16. return FinishSetup(pfl? pfl->m_ImpIFreeList.InitNew(pITFS, cbPreallocated, cSlots)
  17. : STG_E_INSUFFICIENTMEMORY,
  18. pfl, IID_IFreeListManager, (PPVOID)ppITFreeList
  19. );
  20. }
  21. HRESULT CFreeList::LoadFreeList(IITFileSystem *pITFS, IITFreeList **ppITFreeList)
  22. {
  23. CFreeList *pfl = New CFreeList(NULL);
  24. return FinishSetup(pfl? pfl->m_ImpIFreeList.InitLoad(pITFS)
  25. : STG_E_INSUFFICIENTMEMORY,
  26. pfl, IID_IFreeListManager, (PPVOID) ppITFreeList
  27. );
  28. }
  29. HRESULT CFreeList::AttachFreeList(IITFileSystem *pITFS, IITFreeList **ppITFreeList)
  30. {
  31. CFreeList *pfl = New CFreeList(NULL);
  32. return FinishSetup(pfl? pfl->m_ImpIFreeList.LazyInitNew(pITFS)
  33. : STG_E_INSUFFICIENTMEMORY,
  34. pfl, IID_IFreeListManager, (PPVOID) ppITFreeList
  35. );
  36. }
  37. CFreeList::CImpIFreeList::CImpIFreeList(CFreeList *pBackObj, IUnknown *punkOuter)
  38. :IITFreeList(pBackObj, punkOuter)
  39. {
  40. m_fInitialed = FALSE;
  41. m_fIsDirty = FALSE;
  42. m_cBlocks = 0;
  43. m_cBlocksMax = 0;
  44. m_cbSpace = 0;
  45. m_cbLost = 0;
  46. m_paFreeItems = NULL;
  47. m_pITFS = NULL;
  48. DEBUGCODE(m_cbPreallocated = 0; )
  49. }
  50. CFreeList::CImpIFreeList::~CImpIFreeList(void)
  51. {
  52. if (!m_fInitialed)
  53. {
  54. RonM_ASSERT(!m_paFreeItems);
  55. RonM_ASSERT(!m_cBlocksMax);
  56. RonM_ASSERT(!m_cBlocks);
  57. RonM_ASSERT(!m_cbSpace.NonZero());
  58. RonM_ASSERT(!m_cbLost.NonZero());
  59. RonM_ASSERT(!m_fIsDirty);
  60. RonM_ASSERT(!m_pITFS);
  61. return;
  62. }
  63. if (m_fIsDirty)
  64. {
  65. if (m_pITFS) RonM_ASSERT(m_paFreeItems);
  66. else RonM_ASSERT(FALSE);
  67. }
  68. if (m_paFreeItems) delete [] m_paFreeItems;
  69. if (m_pITFS) m_pITFS->Release();
  70. }
  71. // IPersist Method:
  72. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::GetClassID(
  73. /* [out] */ CLSID __RPC_FAR *pClassID)
  74. {
  75. *pClassID = CLSID_IFreeListManager_1;
  76. return NO_ERROR;
  77. }
  78. // IPersistStreamInit Methods:
  79. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::IsDirty(void)
  80. {
  81. return m_fIsDirty? S_OK : S_FALSE;
  82. }
  83. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::Load(
  84. /* [in] */ LPSTREAM pStm)
  85. {
  86. // This routine sets the initial state for the free list from
  87. // a stream. It assumes that we've previously called
  88. // Save on this stream at the current seek position.
  89. // We assume we'll find:
  90. //
  91. // -- m_cBlocksMax
  92. // -- m_cBlocks
  93. // -- m_cbSpace
  94. // -- m_cbLost
  95. // -- The first m_cBlocks items from m_paFreeItems
  96. //
  97. // Note: This ordering is part of the interface spec!
  98. RonM_ASSERT(!m_fInitialed);
  99. if (m_fInitialed)
  100. return E_UNEXPECTED;
  101. ULONG cItemsMax, cItemsActual;
  102. CULINT cbSpace, cbLost;
  103. CSyncWith sw(m_cs);
  104. HRESULT hr = ReadIn(pStm, &cItemsMax, sizeof(cItemsMax));
  105. if (SUCCEEDED(hr) && !cItemsMax)
  106. hr = STG_E_DOCFILECORRUPT;
  107. if (SUCCEEDED(hr))
  108. hr = ReadIn(pStm, &cItemsActual, sizeof(cItemsActual));
  109. if (SUCCEEDED(hr))
  110. hr = ReadIn(pStm, &cbSpace, sizeof(cbSpace));
  111. if (SUCCEEDED(hr))
  112. hr = ReadIn(pStm, &cbLost, sizeof(cbLost));
  113. if (SUCCEEDED(hr))
  114. {
  115. RonM_ASSERT(!m_paFreeItems);
  116. m_paFreeItems = New FREEITEM[cItemsMax];
  117. if (!m_paFreeItems)
  118. hr = E_OUTOFMEMORY;
  119. else
  120. if (cItemsActual)
  121. {
  122. UINT cbActual = sizeof(FREEITEM) * cItemsActual;
  123. hr = ReadIn(pStm, m_paFreeItems, cbActual);
  124. if (!SUCCEEDED(hr))
  125. {
  126. delete [] m_paFreeItems;
  127. m_paFreeItems = NULL;
  128. return hr;
  129. }
  130. }
  131. }
  132. if (SUCCEEDED(hr))
  133. {
  134. m_cBlocks = cItemsActual;
  135. m_cBlocksMax = cItemsMax;
  136. m_cbSpace = cbSpace;
  137. m_cbLost = cbLost;
  138. m_fIsDirty = FALSE;
  139. m_fInitialed = TRUE;
  140. }
  141. return hr;
  142. }
  143. HRESULT CFreeList::CImpIFreeList::ReadIn(LPSTREAM pStm, PVOID pv, ULONG cb)
  144. {
  145. ULONG cbRead;
  146. HRESULT hr = pStm->Read(pv, cb, &cbRead);
  147. if (!SUCCEEDED(hr))
  148. return hr;
  149. // In the context of the ITSS files the following
  150. // assert ought to be true.
  151. RonM_ASSERT(cbRead == cb);
  152. return (cbRead != cb)? STG_E_READFAULT : NO_ERROR;
  153. }
  154. HRESULT CFreeList::CImpIFreeList::WriteOut(LPSTREAM pStm, PVOID pv, ULONG cb)
  155. {
  156. ULONG cbWritten;
  157. HRESULT hr = pStm->Write(pv, cb, &cbWritten);
  158. if (!SUCCEEDED(hr))
  159. return hr;
  160. // In the context of the ITSS files the following
  161. // assert ought to be true.
  162. RonM_ASSERT(cbWritten == cb);
  163. return (cbWritten != cb)? STG_E_WRITEFAULT : NO_ERROR;
  164. }
  165. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::Save(
  166. /* [in] */ LPSTREAM pStm,
  167. /* [in] */ BOOL fClearDirty)
  168. {
  169. // This routine stores the state of the free list in a stream.
  170. // Here's the order in which we write our state:
  171. //
  172. // -- m_cBlocksMax
  173. // -- m_cBlocks
  174. // -- m_cbSpace
  175. // -- m_cbLost
  176. // -- The first m_cBlocks items from m_paFreeItems
  177. //
  178. // Note: This ordering is part of the interface spec!
  179. RonM_ASSERT(m_fInitialed);
  180. RonM_ASSERT(!m_cBlocks || m_paFreeItems);
  181. RonM_ASSERT(m_cBlocksMax);
  182. RonM_ASSERT(m_fIsDirty);
  183. CSyncWith sw(m_cs);
  184. if (fClearDirty)
  185. m_fIsDirty = FALSE;
  186. HRESULT hr = WriteOut(pStm, &m_cBlocksMax, sizeof(m_cBlocksMax));
  187. if (SUCCEEDED(hr))
  188. hr = WriteOut(pStm, &m_cBlocks, sizeof(m_cBlocks));
  189. if (SUCCEEDED(hr))
  190. hr = WriteOut(pStm, &m_cbSpace, sizeof(m_cbSpace));
  191. if (SUCCEEDED(hr))
  192. hr = WriteOut(pStm, &m_cbLost, sizeof(m_cbLost));
  193. if (SUCCEEDED(hr) && m_cBlocks)
  194. {
  195. UINT cbActual = sizeof(FREEITEM) * m_cBlocks;
  196. hr = WriteOut(pStm, m_paFreeItems, cbActual);
  197. }
  198. if (FAILED(hr))
  199. m_fIsDirty = TRUE;
  200. return hr;
  201. }
  202. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::GetSizeMax(
  203. /* [out] */ ULARGE_INTEGER __RPC_FAR *pCbSize)
  204. {
  205. RonM_ASSERT(m_fInitialed);
  206. if (!m_fInitialed)
  207. return E_UNEXPECTED;
  208. pCbSize->QuadPart = sizeof(m_cBlocksMax) + sizeof(m_cBlocks)
  209. + sizeof(m_cbSpace)
  210. + sizeof(m_cbLost)
  211. + sizeof(FREEITEM) * m_cBlocks;
  212. return NO_ERROR;
  213. }
  214. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::InitNew()
  215. {
  216. RonM_ASSERT(FALSE);
  217. return E_NOTIMPL;
  218. }
  219. // IFreeList Methods:
  220. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::InitNew
  221. (IITFileSystem *pITFS, CULINT cbPreallocated)
  222. {
  223. return InitNew(pITFS, cbPreallocated, DEFAULT_ENTRIES_MAX);
  224. }
  225. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::InitNew
  226. (IITFileSystem *pITFS, CULINT cbPreallocated, UINT cEntriesMax)
  227. {
  228. RonM_ASSERT(!m_fInitialed);
  229. RonM_ASSERT(!m_paFreeItems);
  230. RonM_ASSERT(pITFS);
  231. m_pITFS = pITFS;
  232. m_pITFS->AddRef();
  233. if (m_fInitialed)
  234. return E_UNEXPECTED;
  235. if (!cEntriesMax)
  236. return E_INVALIDARG;
  237. m_paFreeItems = New FREEITEM[cEntriesMax];
  238. if (!m_paFreeItems)
  239. return E_OUTOFMEMORY;
  240. RonM_ASSERT(!m_fIsDirty);
  241. RonM_ASSERT(!m_cBlocks);
  242. RonM_ASSERT(!m_cbLost.NonZero());
  243. m_fInitialed = TRUE;
  244. m_cBlocksMax = cEntriesMax;
  245. m_cbSpace = cbPreallocated;
  246. m_fIsDirty = TRUE;
  247. DEBUGCODE( m_cbPreallocated = cbPreallocated; )
  248. return NO_ERROR;
  249. }
  250. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::InitLoad(IITFileSystem *pITFS)
  251. {
  252. m_pITFS = pITFS;
  253. m_pITFS->AddRef();
  254. return ConnectToStream();
  255. }
  256. UINT CFreeList::CImpIFreeList::HighestEntryLEQ(CULINT &ullBase)
  257. {
  258. // BugBug(Optimize): 97/01/30
  259. //
  260. // The code below is a close paraphrase of the original free list code from the
  261. // MV file system. It's a lot more complicated than it needs to be. For now I've
  262. // resisted the temptation to rewrite it completely because that might introduce
  263. // bugs. However when the dust settles a bit, I'm going to clean up this code.
  264. INT iLo=0;
  265. INT iHi=m_cBlocks-1;
  266. INT iSpan=iHi-iLo+1;
  267. INT iFound=-1;
  268. while (iSpan>0)
  269. {
  270. INT iMid=(iLo+iHi)/2;
  271. if (ullBase > m_paFreeItems[iMid].ullOffset)
  272. {
  273. if (iSpan==1)
  274. {
  275. iFound=iLo;
  276. break;
  277. }
  278. iLo=min(iHi,iMid+1);
  279. }
  280. else
  281. { if (iSpan==1)
  282. {
  283. iFound=iLo-1;
  284. break;
  285. }
  286. iHi=max(iLo,iMid-1);
  287. }
  288. iSpan=iHi-iLo+1;
  289. }
  290. return iFound;
  291. }
  292. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::PutFreeSpace
  293. (CULINT ullBase, CULINT ullCB)
  294. {
  295. RonM_ASSERT(m_fInitialed);
  296. if (!m_fInitialed)
  297. return E_UNEXPECTED;
  298. HRESULT hr = S_OK;
  299. if (!m_paFreeItems && m_pITFS)
  300. hr = ConnectToStream();
  301. if (hr != S_OK) return hr;
  302. RonM_ASSERT(m_paFreeItems);
  303. if (!ullCB.NonZero())
  304. return NO_ERROR;
  305. RonM_ASSERT(ullBase >= m_cbPreallocated);
  306. CSyncWith sw(m_cs);
  307. RonM_ASSERT(ullCB <= m_cbSpace);
  308. UINT cItems = m_cBlocks;
  309. BOOL fFullList = cItems == m_cBlocksMax;
  310. if (!cItems)
  311. {
  312. // The list is empty.
  313. m_paFreeItems->ullOffset = ullBase;
  314. m_paFreeItems->ullCB = ullCB;
  315. m_cBlocks++;
  316. m_fIsDirty = TRUE;
  317. return NO_ERROR;
  318. }
  319. INT iFound = HighestEntryLEQ(ullBase);
  320. cItems = m_cBlocks - iFound - 1;
  321. // wFound == -1, insert at beginning,
  322. // else insert _after_ wFound
  323. PFREEITEM pfi = m_paFreeItems + (iFound+1);
  324. PFREEITEM pfiPrev = NULL;
  325. if (iFound!=-1)
  326. pfiPrev = pfi - 1;
  327. if ( pfiPrev
  328. && ullBase == (pfiPrev->ullOffset + pfiPrev->ullCB)
  329. )
  330. { // Merge with previous
  331. pfiPrev->ullCB += ullCB;
  332. if (pfiPrev->ullOffset + pfiPrev->ullCB == pfi->ullOffset)
  333. {
  334. // it fills a hole, merge with next
  335. pfiPrev->ullCB += pfi->ullCB;
  336. // Scoot all next blocks back by one if any
  337. if (cItems)
  338. {
  339. MoveMemory(pfi, pfi+1, sizeof(FREEITEM) * cItems);
  340. m_cBlocks--;
  341. }
  342. }
  343. m_fIsDirty = TRUE;
  344. return NO_ERROR;
  345. }
  346. // Can we merge with the following block?
  347. if (cItems && (ullBase + ullCB) == pfi->ullOffset)
  348. {
  349. pfi->ullOffset = ullBase;
  350. pfi->ullCB += ullCB;
  351. m_fIsDirty = TRUE;
  352. return NO_ERROR;
  353. }
  354. // Can't merge with an existing block.
  355. // So we have to insert this block into the list.
  356. if (fFullList)
  357. {
  358. // The list is already full. So before we add a new
  359. // entry, we've got to remove something. The space
  360. // represented by the item we discard will be lost
  361. // forever. So we first look for the smallest item
  362. // in the list.
  363. ULARGE_INTEGER uli;
  364. uli. LowPart = 0xFFFFFFFF;
  365. uli.HighPart = 0xFFFFFFFF;
  366. CULINT ullSmallest = CULINT(uli);
  367. WORD wSmallestBlock = (WORD)-1;
  368. PFREEITEM pfiTemp = m_paFreeItems;
  369. WORD wBlockTemp = 0;
  370. for (wBlockTemp=0; wBlockTemp < m_cBlocks; wBlockTemp++, pfiTemp++)
  371. {
  372. if (ullSmallest > pfiTemp->ullCB)
  373. {
  374. ullSmallest = pfiTemp->ullCB;
  375. wSmallestBlock=wBlockTemp;
  376. }
  377. }
  378. // If our new block is smaller than the smallest block in the list,
  379. // just discard the new block.
  380. if (ullCB < ullSmallest)
  381. {
  382. m_cbLost += ullCB;
  383. m_fIsDirty = TRUE;
  384. return NO_ERROR;
  385. }
  386. m_cbLost += ullSmallest;
  387. // Now we're going to remove the smallest block. If that
  388. // block is at the end of the list, we just decrement the
  389. // count of active blocks.
  390. if (wSmallestBlock != m_cBlocksMax - 1)
  391. {
  392. // When it isn't at the end, we've got to slide subsequent
  393. // list items down by one position.
  394. MoveMemory(m_paFreeItems + wSmallestBlock,
  395. m_paFreeItems + wSmallestBlock + 1,
  396. sizeof(FREEITEM) * (m_cBlocksMax - wSmallestBlock - 1)
  397. );
  398. }
  399. m_cBlocks--;
  400. cItems--;
  401. // If the block we removed was lower than our insertion position,
  402. // we must adjust our insertion postion down by one.
  403. if ((int)wSmallestBlock <= iFound)
  404. {
  405. pfi = pfiPrev;
  406. cItems++;
  407. }
  408. }
  409. // Insert Item
  410. if (cItems)
  411. MoveMemory(pfi + 1, pfi, sizeof(FREEITEM) * cItems);
  412. pfi->ullOffset = ullBase;
  413. pfi->ullCB = ullCB;
  414. m_cBlocks++;
  415. m_fIsDirty = TRUE;
  416. return NO_ERROR;
  417. }
  418. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::GetFreeSpace
  419. (CULINT *pullBase, CULINT *pullcb)
  420. {
  421. CSyncWith sw(m_cs);
  422. RonM_ASSERT(m_fInitialed);
  423. HRESULT hr = S_OK;
  424. if (!m_paFreeItems && m_pITFS)
  425. hr = ConnectToStream();
  426. if (hr != S_OK) return hr;
  427. RonM_ASSERT(m_paFreeItems);
  428. CULINT ullCB = *pullcb;
  429. ULARGE_INTEGER uli;
  430. uli. LowPart = DWORD(-1);
  431. uli.HighPart = DWORD(-1);
  432. CULINT ullMinDiff(uli);
  433. UINT cBlocks = m_cBlocks;
  434. PFREEITEM pfi = m_paFreeItems;
  435. UINT iBlock;
  436. UINT iBlockBest = UINT(-1);
  437. // The loop below does a best-fit sweep through the
  438. // blocks in the list.
  439. // BugBug(Optimize): 97/01/30
  440. //
  441. // Best-fit space management tends toward maximum fragmentation. So we
  442. // might do better with an different selection rule. Such a change would
  443. // mean that the return value of *pullcb will sometimes be larger than its
  444. // entry value. That means that all the clients of this interface must
  445. // keep track of both the offset and the size returned!
  446. for (iBlock= 0; iBlock < m_cBlocks; iBlock++, pfi++)
  447. if (ullCB <= pfi->ullCB)
  448. {
  449. CULINT ullDiff;
  450. ullDiff= pfi->ullCB - ullCB;
  451. if (ullMinDiff > ullDiff)
  452. {
  453. ullMinDiff = ullDiff;
  454. iBlockBest = iBlock;
  455. }
  456. }
  457. if (iBlockBest == UINT(-1)) // Did we find any items that were big enough?
  458. {
  459. // If not, we'll try to allocate from the end of the active space.
  460. CULINT ullSpaceMax;
  461. ullSpaceMax = 0 - *pullcb;
  462. if (ullSpaceMax < *pullcb)
  463. return S_FALSE;
  464. *pullBase = m_cbSpace;
  465. m_cbSpace += *pullcb;
  466. m_fIsDirty = TRUE;
  467. return S_OK;
  468. }
  469. pfi= m_paFreeItems + iBlockBest;
  470. *pullBase = pfi->ullOffset;
  471. // Note that we don't overwrite *pullcb in this code. However that may change in
  472. // a future implementation. For now we return exactly the space requested.
  473. // That means we must record any trailing space left over from this item.
  474. //
  475. // The spec rule is that *pullcb's return value will be >= it's entry value.
  476. pfi->ullOffset += ullCB;
  477. pfi->ullCB -= ullCB;
  478. if (!(pfi->ullCB.NonZero()))
  479. {
  480. // We matched the item size exactly.
  481. if (iBlockBest < m_cBlocks - 1) // Was it the last entry?
  482. {
  483. // If not, we've got to slide down the entries which followed it.
  484. MoveMemory(pfi, pfi+1, sizeof(FREEITEM) * (m_cBlocks - iBlockBest - 1));
  485. }
  486. m_cBlocks--;
  487. }
  488. m_fIsDirty = TRUE;
  489. return NO_ERROR;
  490. }
  491. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::GetFreeSpaceAt
  492. (CULINT ullBase, CULINT *pullcb)
  493. {
  494. CSyncWith sw(m_cs);
  495. HRESULT hr = S_OK;
  496. if (!m_paFreeItems && m_pITFS)
  497. hr = ConnectToStream();
  498. if (hr != S_OK) return hr;
  499. CULINT ullcbReq;
  500. ullcbReq = *pullcb;
  501. if (ullBase == m_cbSpace)
  502. {
  503. CULINT ullcbAvailable;
  504. ullcbAvailable = 0 - m_cbSpace;
  505. HRESULT hr = S_OK;
  506. if (ullcbAvailable >= ullcbReq)
  507. m_cbSpace += ullcbReq;
  508. else hr = S_FALSE;
  509. m_fIsDirty = TRUE;
  510. return hr;
  511. }
  512. UINT iFound = HighestEntryLEQ(ullBase);
  513. if (iFound == UINT(-1))
  514. return S_FALSE;
  515. PFREEITEM pfi= m_paFreeItems + iFound;
  516. CULINT ullLimit;
  517. ullLimit = pfi->ullOffset + pfi->ullCB;
  518. if (ullLimit <= ullBase)
  519. return S_FALSE;
  520. CULINT ullChunk;
  521. ullChunk = ullLimit - ullBase;
  522. if (ullChunk < ullcbReq)
  523. {
  524. HRESULT hr = S_OK;
  525. if (ullLimit == m_cbSpace)
  526. {
  527. CULINT ullcbAvailable;
  528. ullcbAvailable = 0 - m_cbSpace;
  529. if (ullcbAvailable >= ullcbReq - ullChunk)
  530. m_cbSpace += ullcbReq - ullChunk;
  531. else hr = S_FALSE;
  532. }
  533. else hr = S_FALSE;
  534. if (hr == S_FALSE)
  535. return S_FALSE;
  536. }
  537. pfi->ullCB = ullBase - pfi->ullOffset; // Record the prefix space.
  538. if (!(pfi->ullCB).NonZero())
  539. {
  540. // We've used up the entire block.
  541. // Now we need to remove it from the list.
  542. if (iFound < m_cBlocks - 1)
  543. MoveMemory(pfi, pfi + 1, sizeof(FREEITEM) * (m_cBlocks - iFound - 1));
  544. m_cBlocks--;
  545. }
  546. if (ullChunk > ullcbReq) // Check for trailing space in the block
  547. {
  548. CULINT ullBaseTrailing, ullcbTrailing;
  549. ullBaseTrailing = ullBase + ullcbReq;
  550. ullcbTrailing = ullChunk - ullcbReq;
  551. HRESULT hr =
  552. PutFreeSpace(ullBaseTrailing, ullcbTrailing);
  553. RonM_ASSERT(SUCCEEDED(hr));
  554. }
  555. m_fIsDirty = TRUE;
  556. return NO_ERROR;
  557. }
  558. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::GetEndingFreeSpace
  559. (CULINT *pullBase, CULINT *pullcb)
  560. {
  561. HRESULT hr = S_OK;
  562. if (!m_paFreeItems && m_pITFS)
  563. hr = ConnectToStream();
  564. if (hr != S_OK) return hr;
  565. CULINT ullcbReq;
  566. ullcbReq = *pullcb;
  567. CSyncWith sw(m_cs);
  568. if (m_cBlocks)
  569. {
  570. PFREEITEM pfi = m_paFreeItems + m_cBlocks - 1;
  571. CULINT ullBase, ullcb;
  572. ullBase = pfi->ullOffset;
  573. ullcb = pfi->ullCB;
  574. if (m_cbSpace == ullBase + ullcb)
  575. {
  576. if (ullcbReq > ullcb)
  577. {
  578. CULINT ullcbAvailable;
  579. ullcbAvailable = 0 - ullBase;
  580. if (ullcbAvailable < ullcbReq)
  581. return S_FALSE;
  582. m_cbSpace += ullcbReq - ullcb;
  583. }
  584. m_cBlocks--;
  585. *pullBase = ullBase;
  586. if (ullcb > ullcbReq)
  587. {
  588. CULINT ullBaseResidue, ullcbResidue;
  589. ullBaseResidue = ullBase + ullcbReq;
  590. ullcbResidue = ullcb - ullcbReq;
  591. #ifdef _DEBUG
  592. HRESULT hr =
  593. #endif // _DEBUG
  594. PutFreeSpace(ullBaseResidue, ullcbResidue);
  595. RonM_ASSERT(SUCCEEDED(hr));
  596. }
  597. m_fIsDirty = TRUE;
  598. return NO_ERROR;
  599. }
  600. }
  601. CULINT ullcbAvailable;
  602. ullcbAvailable = 0 - m_cbSpace;
  603. if (ullcbAvailable < ullcbReq)
  604. return S_FALSE;
  605. *pullBase = m_cbSpace;
  606. m_cbSpace += ullcbReq;
  607. m_fIsDirty = TRUE;
  608. return NO_ERROR;
  609. }
  610. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::GetStatistics
  611. (CULINT *pcbLost, CULINT *pcbSpace)
  612. {
  613. HRESULT hr = S_OK;
  614. if (!m_paFreeItems && m_pITFS)
  615. hr = ConnectToStream();
  616. if (hr != S_OK) return hr;
  617. *pcbLost = m_cbLost;
  618. *pcbSpace = m_cbSpace;
  619. return hr;
  620. }
  621. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::SetFreeListEmpty()
  622. {
  623. RonM_ASSERT(m_fInitialed);
  624. RonM_ASSERT(m_pITFS);
  625. HRESULT hr = S_OK;
  626. if (!m_cBlocksMax) hr = ConnectToStream();
  627. if (SUCCEEDED(hr))
  628. {
  629. m_cBlocks = 0;
  630. m_fIsDirty = TRUE;
  631. }
  632. return hr;
  633. }
  634. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::SetSpaceSize(ULARGE_INTEGER uliCbSpace)
  635. {
  636. RonM_ASSERT(m_fInitialed);
  637. RonM_ASSERT(m_pITFS);
  638. m_cbSpace = CULINT(uliCbSpace);
  639. m_fIsDirty = TRUE;
  640. return S_OK;
  641. }
  642. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::LazyInitNew(IITFileSystem *pITFS)
  643. {
  644. RonM_ASSERT(!m_fInitialed);
  645. RonM_ASSERT(!m_paFreeItems);
  646. RonM_ASSERT(pITFS);
  647. pITFS->AddRef();
  648. m_pITFS = pITFS;
  649. m_fIsDirty = FALSE;
  650. m_fInitialed = TRUE;
  651. return S_OK;
  652. }
  653. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::RecordFreeList()
  654. {
  655. RonM_ASSERT(m_fInitialed);
  656. RonM_ASSERT(!m_cBlocks || m_paFreeItems);
  657. RonM_ASSERT(m_pITFS);
  658. if (!m_pITFS) return E_FAIL;
  659. HRESULT hr = S_OK;
  660. if (m_fIsDirty)
  661. {
  662. /*
  663. One of the complications here is that the act of recording the free
  664. list may alter the free list. That's because we may need to allocate
  665. or reallocate space within the file system to do the write operations.
  666. The best case, of course, is the situation where the Free List stream
  667. is already exactly the right size to hold the free list information.
  668. In the other possible situations the stream may be empty, or larger than
  669. or smaller than the free list image. Those situations cause allocations
  670. and thus affect the free list. We deal with those possibilities by
  671. 1. Only allowing the stream to grow from its initial size.
  672. 2. Allowing a few iterations to take care of cases where the allocation
  673. operation may grow the free list or change some of its entries and
  674. thus make it "dirty" again.
  675. To catch flaws in this logic we trigger an assertion and return E_FAIL
  676. if the iteration count goes beyond four. If the interactions between the
  677. file system and the free list changes substantially, we may need to
  678. change the iteration limit upward.
  679. */
  680. CULINT ullSizeMax;
  681. CULINT ullSizeCurrent;
  682. ULARGE_INTEGER cbFreeList;
  683. hr = GetSizeMax(&cbFreeList);
  684. RonM_ASSERT(SUCCEEDED(hr));
  685. ullSizeMax = cbFreeList;
  686. ullSizeCurrent = 0;
  687. IStreamITEx *pStrmFreeList = NULL;
  688. hr = m_pITFS->OpenStream(NULL, L"F", STGM_READWRITE, &pStrmFreeList);
  689. if (!SUCCEEDED(hr)) return hr;
  690. UINT iterations = 0;
  691. while (m_fIsDirty) // m_fIsDirty is set TRUE when the free list changes size or content.
  692. // It is set FALSE by the Save call below.
  693. {
  694. hr = GetSizeMax(&cbFreeList);
  695. RonM_ASSERT(SUCCEEDED(hr));
  696. if (ullSizeMax < cbFreeList)
  697. ullSizeMax = cbFreeList;
  698. if (ullSizeCurrent < ullSizeMax)
  699. {
  700. ullSizeCurrent = ullSizeMax;
  701. hr = pStrmFreeList->SetSize(ullSizeMax.Uli());
  702. if (!SUCCEEDED(hr)) break;
  703. }
  704. LARGE_INTEGER uli;
  705. uli.LowPart = 0;
  706. uli.HighPart = 0;
  707. hr = pStrmFreeList->Seek(uli, STREAM_SEEK_SET, NULL);
  708. if (!SUCCEEDED(hr)) break;
  709. hr = Save(pStrmFreeList, TRUE); // TRUE ==> Reset the dirty flag
  710. if (!SUCCEEDED(hr)) break;
  711. hr = GetSizeMax(&cbFreeList);
  712. RonM_ASSERT(SUCCEEDED(hr));
  713. if (ullSizeMax < cbFreeList)
  714. ullSizeMax = cbFreeList;
  715. hr = pStrmFreeList->Flush();
  716. if (!SUCCEEDED(hr)) break;
  717. hr = GetSizeMax(&cbFreeList);
  718. RonM_ASSERT(SUCCEEDED(hr));
  719. if (ullSizeMax < cbFreeList)
  720. ullSizeMax = cbFreeList;
  721. if (++iterations > 4)
  722. {
  723. RonM_ASSERT(FALSE); // This has become an infinite loop.
  724. return E_FAIL;
  725. }
  726. }
  727. pStrmFreeList->Release();
  728. }
  729. return S_OK;
  730. }
  731. HRESULT STDMETHODCALLTYPE CFreeList::CImpIFreeList::ConnectToStream()
  732. {
  733. IStreamITEx *pStrmFreeList = NULL;
  734. HRESULT hr = m_pITFS->OpenStream(NULL, L"F", STGM_READWRITE, &pStrmFreeList);
  735. if (!SUCCEEDED(hr)) return hr;
  736. LARGE_INTEGER uli;
  737. uli.LowPart = 0;
  738. uli.HighPart = 0;
  739. hr = pStrmFreeList->Seek(uli, STREAM_SEEK_SET, NULL);
  740. if (SUCCEEDED(hr))
  741. {
  742. m_fInitialed = FALSE; // To satisfy the Load routine...
  743. hr = Load(pStrmFreeList);
  744. }
  745. pStrmFreeList->Release();
  746. return hr;
  747. }