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.

3339 lines
93 KiB

  1. // PathMgr.cpp -- Implmentation for the Path Manager classes and interfaces
  2. #include "StdAfx.h"
  3. //#include <stdio.h>
  4. HRESULT CPathManager1::NewPathDatabase
  5. (IUnknown *punkOuter, ILockBytes *plb,
  6. UINT cbDirectoryBlock, UINT cCacheBlocksMax,
  7. IITPathManager **ppPM
  8. )
  9. {
  10. CPathManager1 *pPM1 = New CPathManager1(punkOuter);
  11. return FinishSetup(pPM1? pPM1->m_PathManager.InitNewPathDatabase
  12. (plb, cbDirectoryBlock, cCacheBlocksMax)
  13. : STG_E_INSUFFICIENTMEMORY,
  14. pPM1, IID_PathManager, (PPVOID) ppPM
  15. );
  16. }
  17. HRESULT CPathManager1::LoadPathDatabase
  18. (IUnknown *punkOuter, ILockBytes *plb, IITPathManager **ppPM)
  19. {
  20. CPathManager1 *pPM1 = New CPathManager1(punkOuter);
  21. return FinishSetup(pPM1? pPM1->m_PathManager.InitLoadPathDatabase(plb)
  22. : STG_E_INSUFFICIENTMEMORY,
  23. pPM1, IID_PathManager, (PPVOID) ppPM
  24. );
  25. }
  26. CPathManager1::CImpIPathManager::CImpIPathManager
  27. (CPathManager1 *pBackObj, IUnknown *punkOuter)
  28. : IITPathManager(pBackObj, punkOuter)
  29. {
  30. ZeroMemory(&m_dbh, sizeof(m_dbh));
  31. m_plbPathDatabase = NULL;
  32. m_cCacheBlocks = 0;
  33. m_fHeaderIsDirty = FALSE;
  34. m_pCBLeastRecent = NULL;
  35. m_pCBMostRecent = NULL;
  36. m_pCBFreeList = NULL;
  37. m_PathSetSerial = 1;
  38. ZeroMemory(&m_dbh, sizeof(m_dbh));
  39. m_dbh.iRootDirectory = INVALID_INDEX;
  40. m_dbh.iLeafFirst = INVALID_INDEX;
  41. m_dbh.iLeafLast = INVALID_INDEX;
  42. }
  43. CPathManager1::CImpIPathManager::~CImpIPathManager()
  44. {
  45. if (m_plbPathDatabase)
  46. {
  47. // We've got a path database open.
  48. // The code below flushes any pending database
  49. // changes out to disk. This will usually work,
  50. // but it's not good practice. The problem is
  51. // that a Destructor has no return value. So
  52. // you won't be notified if something went wrong.
  53. //
  54. // A better approach is to do the flush before
  55. // releasing the interface.
  56. RonM_ASSERT(m_PathSetSerial != 0);
  57. #ifdef _DEBUG
  58. HRESULT hr =
  59. #endif // _DEBUG
  60. FlushToLockBytes();
  61. RonM_ASSERT(SUCCEEDED(hr));
  62. // The code below deallocates the cache blocks.
  63. // First the free blocks:
  64. PCacheBlock pCB;
  65. for (; S_OK == GetFreeBlock(pCB); m_cCacheBlocks--)
  66. delete [] (PBYTE) pCB;
  67. // Then the active blocks:
  68. for (; S_OK == GetActiveBlock(pCB, TRUE); m_cCacheBlocks--)
  69. {
  70. RonM_ASSERT(!(pCB->fFlags & LockedBlock));
  71. delete [] (PBYTE) pCB;
  72. }
  73. RonM_ASSERT(m_cCacheBlocks == 0); // That should be all the cache blocks.
  74. m_plbPathDatabase->Release();
  75. }
  76. }
  77. HRESULT CPathManager1::CImpIPathManager::InitNewPathDatabase
  78. (ILockBytes *plb, UINT cbDirectoryBlock, UINT cCacheBlocksMax)
  79. {
  80. RonM_ASSERT(!m_plbPathDatabase);
  81. m_plbPathDatabase = plb;
  82. if (cbDirectoryBlock < MIN_DIRECTORY_BLOCK_SIZE)
  83. cbDirectoryBlock = MIN_DIRECTORY_BLOCK_SIZE;
  84. if (cCacheBlocksMax < MIN_CACHE_ENTRIES)
  85. cCacheBlocksMax = MIN_CACHE_ENTRIES;
  86. m_dbh.uiMagic = PathMagicID;
  87. m_dbh.uiVersion = PathVersion;
  88. m_dbh.cbHeader = sizeof(m_dbh);
  89. m_dbh.cbDirectoryBlock = cbDirectoryBlock;
  90. m_dbh.cEntryAccessShift = 2;
  91. m_dbh.cCacheBlocksMax = cCacheBlocksMax;
  92. m_dbh.cDirectoryLevels = 0;
  93. m_dbh.iRootDirectory = INVALID_INDEX;
  94. m_dbh.iLeafFirst = INVALID_INDEX;
  95. m_dbh.iLeafLast = INVALID_INDEX;
  96. m_dbh.iBlockFirstFree = INVALID_INDEX;
  97. m_dbh.cBlocks = 0;
  98. m_dbh.lcid = 0x0409; // CLSID_ITStorage uses US English case map rules
  99. m_dbh.clsidEntryHandler = CLSID_ITStorage; // Default entry handler
  100. m_dbh.cbPrefix = sizeof(m_dbh); // CLSID_ITStorage has no instance data
  101. m_dbh.iOrdinalMapRoot = INVALID_INDEX; // Used when the ordinal map doesn't fit in a block
  102. m_dbh.iOrdinalMapFirst = INVALID_INDEX; // First ordinal block (treated like a leaf block)
  103. m_dbh.iOrdinalMapLast = INVALID_INDEX; // Last ordinal block (treated like a leaf block)
  104. m_fHeaderIsDirty = TRUE;
  105. return SaveHeader();
  106. }
  107. HRESULT CPathManager1::CImpIPathManager::InitLoadPathDatabase(ILockBytes *plb)
  108. {
  109. RonM_ASSERT(!m_plbPathDatabase);
  110. m_plbPathDatabase = plb;
  111. ULONG cbRead = 0;
  112. HRESULT hr = m_plbPathDatabase->ReadAt(CULINT(0).Uli(), &m_dbh,
  113. sizeof(m_dbh), &cbRead
  114. );
  115. if (SUCCEEDED(hr) && cbRead != sizeof(m_dbh))
  116. hr = STG_E_WRITEFAULT;
  117. if (SUCCEEDED(hr) && m_dbh.uiMagic != PathMagicID)
  118. hr = STG_E_INVALIDHEADER;
  119. if (SUCCEEDED(hr) && m_dbh.uiVersion > PathVersion)
  120. hr = STG_E_INVALIDHEADER;
  121. if (SUCCEEDED(hr))
  122. {
  123. if (m_dbh.cCacheBlocksMax < 2 * m_dbh.cDirectoryLevels)
  124. m_dbh.cCacheBlocksMax = 2 * m_dbh.cDirectoryLevels;
  125. }
  126. return hr;
  127. }
  128. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::GetClassID
  129. (CLSID __RPC_FAR *pClassID)
  130. {
  131. *pClassID = CLSID_PathManager_1;
  132. return NO_ERROR;
  133. }
  134. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::FlushToLockBytes()
  135. {
  136. CSyncWith sw(m_cs);
  137. RonM_ASSERT(m_plbPathDatabase);
  138. HRESULT hr = NO_ERROR;
  139. for (PCacheBlock pCB = m_pCBLeastRecent; pCB; pCB = pCB->pCBNext)
  140. {
  141. UINT fFlags = pCB->fFlags;
  142. if (pCB->fFlags & DirtyBlock)
  143. {
  144. HRESULT hr = WriteCacheBlock(pCB);
  145. if (!SUCCEEDED(hr))
  146. break;
  147. }
  148. }
  149. if (SUCCEEDED(hr) && m_fHeaderIsDirty)
  150. hr = SaveHeader();
  151. if (SUCCEEDED(hr))
  152. hr = m_plbPathDatabase->Flush();
  153. return hr;
  154. }
  155. HRESULT CPathManager1::CImpIPathManager::SaveHeader()
  156. {
  157. ULONG cbWritten = 0;
  158. HRESULT hr = m_plbPathDatabase->WriteAt(CULINT(0).Uli(), &m_dbh,
  159. sizeof(m_dbh), &cbWritten
  160. );
  161. if (SUCCEEDED(hr) && cbWritten != sizeof(m_dbh))
  162. hr = STG_E_WRITEFAULT;
  163. return hr;
  164. }
  165. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::FindEntry(PPathInfo pSI)
  166. {
  167. HRESULT hr = NO_ERROR;
  168. // LockBytes entries (Paths that don't end with '/') must have uStateBits
  169. // set to zero. Actually letting them go non-zero would be harmless, but it
  170. // does increase the size of the B-Tree entries.
  171. TaggedPathInfo tsi;
  172. tsi.SI = *pSI;
  173. PCacheBlock *papCBSet
  174. = (PCacheBlock *) _alloca(sizeof(PCacheBlock) * m_dbh.cDirectoryLevels);
  175. CSyncWith sw(m_cs);
  176. hr = FindKeyAndLockBlockSet(&tsi, papCBSet, 0);
  177. ClearLockFlags(papCBSet);
  178. if (SUCCEEDED(hr))
  179. *pSI = tsi.SI;
  180. return hr;
  181. }
  182. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CreateEntry
  183. (PPathInfo pSINew,
  184. PPathInfo pSIOld,
  185. BOOL fReplace
  186. )
  187. {
  188. HRESULT hr = NO_ERROR;
  189. // LockBytes entries (Paths that don't end with '/') must have uStateBits
  190. // set to zero. Actually letting them go non-zero would be harmless, but it
  191. // does increase the size of the B-Tree entries.
  192. RonM_ASSERT(( pSINew->cwcStreamPath
  193. && pSINew->awszStreamPath[pSINew->cwcStreamPath - 1] == L'/'
  194. ) || pSINew->uStateBits == 0
  195. );
  196. TaggedPathInfo tsi;
  197. tsi.SI = *pSINew;
  198. // The allocation below includes slots for two levels beyond m_dbh.cDirectoryLevels.
  199. // That's because insert/delete/modify actions may cause a new root node to be
  200. // created twice in the worst case. An insert or modify transaction can cause
  201. // a leaf split which may propagate up through the root node. In addition when
  202. // any of those three actions changes the tag for a node, that tag change may
  203. // propagate back to the root node and cause a split there.
  204. PCacheBlock *papCBSet
  205. = (PCacheBlock *) _alloca(sizeof(PCacheBlock) * (m_dbh.cDirectoryLevels + 2));
  206. CSyncWith sw(m_cs);
  207. hr = FindKeyAndLockBlockSet(&tsi, papCBSet, 0);
  208. if (hr == S_OK) // Does the file already exist?
  209. {
  210. *pSIOld = tsi.SI;
  211. if (fReplace)
  212. {
  213. hr = DeleteEntry(&tsi.SI);
  214. ClearLockFlags(papCBSet);
  215. if (SUCCEEDED(hr))
  216. hr = CreateEntry(pSINew, &tsi.SI, TRUE);
  217. }
  218. else hr = STG_E_FILEALREADYEXISTS;
  219. }
  220. else
  221. if (hr == S_FALSE)
  222. hr = InsertEntryIntoLeaf(&tsi, papCBSet);
  223. ClearLockFlags(papCBSet);
  224. return hr;
  225. }
  226. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::DeleteEntry(PPathInfo pSI)
  227. {
  228. HRESULT hr = NO_ERROR;
  229. TaggedPathInfo tsi;
  230. tsi.SI = *pSI;
  231. // The allocation below includes slots for two levels beyond m_dbh.cDirectoryLevels.
  232. // That's because insert/delete/modify actions may cause a new root node to be
  233. // created twice in the worst case. An insert or modify transaction can cause
  234. // a leaf split which may propagate up through the root node. In addition when
  235. // any of those three actions changes the tag for a node, that tag change may
  236. // propagate back to the root node and cause a split there.
  237. PCacheBlock *papCBSet
  238. = (PCacheBlock *) _alloca(sizeof(PCacheBlock) * (m_dbh.cDirectoryLevels + 2));
  239. CSyncWith sw(m_cs);
  240. hr = FindKeyAndLockBlockSet(&tsi, papCBSet, 0);
  241. if (hr == S_OK)
  242. {
  243. *pSI = tsi.SI;
  244. hr = RemoveEntryFromNode(&tsi, papCBSet, 0);
  245. }
  246. ClearLockFlags(papCBSet);
  247. return hr;
  248. }
  249. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::UpdateEntry(PPathInfo pSI)
  250. {
  251. HRESULT hr = NO_ERROR;
  252. // LockBytes entries (Paths that don't end with '/') must have uStateBits
  253. // set to zero. Actually letting them go non-zero would be harmless, but it
  254. // does increase the size of the B-Tree entries.
  255. RonM_ASSERT(( pSI->cwcStreamPath
  256. && pSI->awszStreamPath[pSI->cwcStreamPath - 1] == L'/'
  257. ) || pSI->uStateBits == 0
  258. );
  259. TaggedPathInfo tsi;
  260. PathInfo si = *pSI;
  261. tsi.SI = *pSI;
  262. // The allocation below includes slots for two levels beyond m_dbh.cDirectoryLevels.
  263. // That's because insert/delete/modify actions may cause a new root node to be
  264. // created twice in the worst case. An insert or modify transaction can cause
  265. // a leaf split which may propagate up through the root node. In addition when
  266. // any of those three actions changes the tag for a node, that tag change may
  267. // propagate back to the root node and cause a split there.
  268. PCacheBlock *papCBSet
  269. = (PCacheBlock *) _alloca(sizeof(PCacheBlock) * (m_dbh.cDirectoryLevels + 2));
  270. CSyncWith sw(m_cs);
  271. hr = FindKeyAndLockBlockSet(&tsi, papCBSet, 0);
  272. if (hr == S_OK)
  273. {
  274. tsi.SI = si; // FindKeyAndLockBlockSet overwrote tsi.SI.
  275. // We're restoring its values.
  276. hr = UpdateEntryInLeaf(&tsi, papCBSet);
  277. }
  278. ClearLockFlags(papCBSet);
  279. return hr;
  280. }
  281. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::EnumFromObject
  282. (IUnknown *punkOuter, const WCHAR *pwszPrefix, UINT cwcPrefix,
  283. REFIID riid, PVOID *ppv
  284. )
  285. {
  286. CSyncWith sw(m_cs);
  287. AddRef(); // Because we pass this object to NewPathEnumeratorObject
  288. HRESULT hr = CEnumPathMgr1::NewPathEnumeratorObject
  289. (punkOuter, this, pwszPrefix, cwcPrefix, riid, ppv);
  290. return hr;
  291. }
  292. HRESULT CPathManager1::CImpIPathManager::InsertEntryIntoLeaf
  293. (PTaggedPathInfo ptsi, PCacheBlock *papCBSet)
  294. {
  295. BYTE abEntry[CB_STREAM_INFO_MAX];
  296. PBYTE pb = EncodePathInfo(abEntry, &ptsi->SI);
  297. UINT cb = UINT(pb - abEntry);
  298. return InsertEntryIntoNode(ptsi, abEntry, cb, papCBSet, 0, FALSE);
  299. }
  300. HRESULT CPathManager1::CImpIPathManager::UpdateEntryInLeaf
  301. (PTaggedPathInfo ptsi, PCacheBlock *papCBSet)
  302. {
  303. BYTE abEntry[CB_STREAM_INFO_MAX];
  304. PBYTE pb = EncodePathInfo(abEntry, &ptsi->SI);
  305. UINT cb = UINT(pb - abEntry);
  306. return ModifyEntryInNode(ptsi, abEntry, cb, papCBSet, 0);
  307. }
  308. void CPathManager1::CImpIPathManager::KillKeyCount(LeafNode *pln)
  309. {
  310. if (m_dbh.cbDirectoryBlock <= 0x10000 && pln->nh.cbSlack >= sizeof(USHORT))
  311. PUSHORT(PBYTE(pln) + m_dbh.cbDirectoryBlock)[-1] = 0;
  312. else
  313. if (m_dbh.cbDirectoryBlock > 0x10000 && pln->nh.cbSlack >= sizeof(UINT))
  314. PUINT(PBYTE(pln) + m_dbh.cbDirectoryBlock)[-1] = 0;
  315. }
  316. HRESULT CPathManager1::CImpIPathManager::InsertEntryIntoNode
  317. (PTaggedPathInfo ptsi, PBYTE pb, UINT cb, PCacheBlock *papCBSet, UINT iLevel, BOOL fAfter)
  318. {
  319. if (iLevel >= m_dbh.cDirectoryLevels)
  320. return NO_ERROR;
  321. PCacheBlock pCBNode = papCBSet[iLevel];
  322. PLeafNode pln = &(pCBNode->ldb);
  323. //invalidate the binary search index
  324. BOOL fSmall = m_dbh.cbDirectoryBlock <= 0x10000;
  325. if (fSmall && (pln->nh.cbSlack >= sizeof(USHORT)))
  326. {
  327. ((PUSHORT)(PBYTE(pln) + m_dbh.cbDirectoryBlock))[-1] = 0;
  328. }
  329. else if (!fSmall && (pln->nh.cbSlack >= sizeof(UINT)))
  330. {
  331. ((PUINT)(PBYTE(pln) + m_dbh.cbDirectoryBlock))[-1] = 0;
  332. }
  333. UINT offKey = pCBNode->cbKeyOffset;
  334. UINT cbSlack = pln->nh.cbSlack;
  335. UINT cbTotal = m_dbh.cbDirectoryBlock - sizeof(NodeHeader);
  336. PBYTE pbInfo = PBYTE(pln) + offKey + sizeof(NodeHeader);
  337. if (iLevel == 0) // Leaf nodes have link pointers in their header.
  338. {
  339. cbTotal -= sizeof(LeafChainLinks);
  340. pbInfo += sizeof(LeafChainLinks);
  341. }
  342. else
  343. if (fAfter)
  344. {
  345. // Insertion point follows the node at pbInfo. So we need
  346. // to adjust pbInfo and offKey.
  347. UINT cbEntry = pCBNode->cbEntry;
  348. offKey += cbEntry;
  349. pbInfo += cbEntry;
  350. }
  351. // UINT cbEntry = ptsi->cbEncoded;
  352. if (cb <= cbSlack) // Do we have enough space in this node?
  353. {
  354. // If so, we just slide down the trailing content and
  355. // copy in the entry.
  356. UINT cbTrailing = cbTotal - cbSlack - offKey;
  357. if (cbTrailing) // > 0 => Insert; == 0 => append
  358. {
  359. MoveMemory(pbInfo + cb, pbInfo, cbTrailing);
  360. if (iLevel == 0) // Insertions into leaf nodes increment the leaf serial.
  361. while (!++(pln->lcl.iLeafSerial)); // Zero isn't a valid serial number.
  362. }
  363. CopyMemory(pbInfo, pb, cb);
  364. // Then we adjust the header info to account for the new
  365. // entry.
  366. pln->nh.cbSlack -= cb;
  367. KillKeyCount(pln);
  368. pCBNode->fFlags |= DirtyBlock;
  369. HRESULT hr = NO_ERROR;
  370. if (offKey == 0 && ++iLevel < m_dbh.cDirectoryLevels)
  371. {
  372. // This key has become the tag for this node.
  373. // Need to fix up nodes higher in the tree.
  374. // First we construct the entry we'll use in the parent node
  375. UINT cbOld = cb;
  376. PBYTE pb2 = pb;
  377. UINT cbKey = DecodeVL32((const BYTE **)&pb2);
  378. cb = UINT(EncodeVL32(pb2 + cbKey, pCBNode->iBlock) - pb);
  379. // We've finished modifying this node. So we don't need to keep it
  380. // locked anymore,
  381. pCBNode->fFlags &= ~LockedBlock;
  382. if (cbSlack == cbTotal) // Was the node originally empty?
  383. InsertEntryIntoNode(ptsi, pb, cb, papCBSet, iLevel, FALSE);
  384. else
  385. {
  386. // The node wasn't empty. The old tag follows our data.
  387. ClearLockFlags(papCBSet);
  388. PBYTE pbOldKey = pbInfo + cbOld;
  389. hr = DecodePathKey((const BYTE **)&pbOldKey, ptsi->SI.awszStreamPath,
  390. (PUINT) &(ptsi->SI. cwcStreamPath)
  391. );
  392. ClearLockFlags(papCBSet);
  393. if (SUCCEEDED(hr))
  394. hr = FindKeyAndLockBlockSet(ptsi, papCBSet, iLevel);
  395. if (SUCCEEDED(hr))
  396. hr = ModifyEntryInNode(ptsi, pb, cb, papCBSet, iLevel);
  397. }
  398. }
  399. return hr;
  400. }
  401. HRESULT hr = SplitANode(papCBSet, iLevel);
  402. if (SUCCEEDED(hr))
  403. hr = FindKeyAndLockBlockSet(ptsi, papCBSet, iLevel);
  404. if (SUCCEEDED(hr))
  405. hr = InsertEntryIntoNode(ptsi, pb, cb, papCBSet, iLevel, fAfter);
  406. return hr;
  407. }
  408. HRESULT CPathManager1::CImpIPathManager::RemoveEntryFromNode
  409. (PTaggedPathInfo ptsi, PCacheBlock *papCBSet, UINT iLevel)
  410. {
  411. PCacheBlock pCBNode = papCBSet[iLevel];
  412. PLeafNode pln = &(pCBNode->ldb);
  413. UINT offKey = pCBNode->cbKeyOffset;
  414. UINT cbSlack = pln->nh.cbSlack;
  415. UINT cbTotal = m_dbh.cbDirectoryBlock - sizeof(NodeHeader);
  416. PBYTE pbInfo = PBYTE(pln) + offKey + sizeof(NodeHeader);
  417. if (iLevel == 0)
  418. {
  419. cbTotal -= sizeof(LeafChainLinks);
  420. pbInfo += sizeof(LeafChainLinks);
  421. }
  422. UINT cbEntry = pCBNode->cbEntry;
  423. MoveMemory(pbInfo, pbInfo + cbEntry, cbTotal - cbSlack - offKey - cbEntry);
  424. cbSlack =
  425. pln->nh.cbSlack += cbEntry;
  426. KillKeyCount(pln);
  427. if (iLevel == 0)
  428. while (!++(pln->lcl.iLeafSerial)); // Zero isn't a valid serial number.
  429. pCBNode->fFlags |= DirtyBlock;
  430. if (cbSlack == cbTotal)
  431. {
  432. // We've removed the last key in this node. This means
  433. // we need to fix up nodes higher in the tree.
  434. DEBUGDEF(UINT iBlockRoot = pCBNode->iBlock)
  435. // First we'll discard the current node.
  436. if (iLevel == 0)
  437. {
  438. while (!++m_PathSetSerial); // Zero isn't a valid serial number!
  439. // If this is a leaf node, we must take it out
  440. // of the leaf chain.
  441. UINT iLeafPrev = pln->lcl.iLeafPrevious;
  442. UINT iLeafNext = pln->lcl.iLeafNext;
  443. if (ValidBlockIndex(iLeafPrev))
  444. {
  445. PCacheBlock pCBPrev = NULL;
  446. HRESULT hr = FindCacheBlock(&pCBPrev, iLeafPrev);
  447. if (!SUCCEEDED(hr))
  448. return hr;
  449. RonM_ASSERT(pCBPrev->ldb.lcl.iLeafNext == pCBNode->iBlock);
  450. pCBPrev->ldb.lcl.iLeafNext = iLeafNext;
  451. pCBPrev->fFlags |= DirtyBlock;
  452. }
  453. else
  454. {
  455. RonM_ASSERT(m_dbh.iLeafFirst == iBlockRoot);
  456. m_dbh.iLeafFirst = iLeafNext;
  457. }
  458. if (ValidBlockIndex(iLeafNext))
  459. {
  460. PCacheBlock pCBNext = NULL;
  461. HRESULT hr = FindCacheBlock(&pCBNext, iLeafNext);
  462. if (!SUCCEEDED(hr))
  463. return hr;
  464. RonM_ASSERT(pCBNext->ldb.lcl.iLeafPrevious == pCBNode->iBlock);
  465. pCBNext->ldb.lcl.iLeafPrevious = iLeafPrev;
  466. pCBNext->fFlags |= DirtyBlock;
  467. }
  468. else
  469. {
  470. RonM_ASSERT(m_dbh.iLeafLast == iBlockRoot);
  471. m_dbh.iLeafLast = iLeafPrev;
  472. }
  473. }
  474. HRESULT hr = DiscardNode(pCBNode);
  475. // Then we'll remove its tag from the parent node.
  476. if (SUCCEEDED(hr))
  477. if (m_dbh.cDirectoryLevels > 1)
  478. if (m_dbh.cDirectoryLevels == iLevel + 1)
  479. {
  480. RonM_ASSERT(m_dbh.iRootDirectory == iBlockRoot);
  481. m_dbh.cDirectoryLevels = 0;
  482. m_dbh.iRootDirectory = INVALID_INDEX;
  483. }
  484. else
  485. hr = RemoveEntryFromNode(ptsi, papCBSet, iLevel + 1);
  486. return hr;
  487. }
  488. else
  489. if (offKey == 0 && m_dbh.cDirectoryLevels > iLevel + 1)
  490. {
  491. // The node isn't empty, but we've removed its original tag.
  492. // So we must construct a new tag and visit our parent node to
  493. // modify the old tag entry there.
  494. BYTE abNewTag[MAX_UTF8_PATH + 6];
  495. PBYTE pbNewTag = pbInfo;
  496. UINT cbNewTag = DecodeVL32((const BYTE **) &pbNewTag);
  497. cbNewTag += UINT(pbNewTag - pbInfo);
  498. RonM_ASSERT(cbNewTag <= MAX_UTF8_PATH + 1); // To catch buffer overflow
  499. // We use the existing pb buffer to construct the new tag.
  500. CopyMemory(abNewTag, pbInfo, cbNewTag);
  501. cbNewTag = UINT(EncodeVL32(abNewTag + cbNewTag, pCBNode->iBlock) - abNewTag);
  502. RonM_ASSERT(cbNewTag <= MAX_UTF8_PATH + 6); // To catch buffer overflow
  503. // problems in logic
  504. return ModifyEntryInNode(ptsi, abNewTag, cbNewTag, papCBSet, iLevel + 1);
  505. }
  506. else return NO_ERROR;
  507. }
  508. HRESULT CPathManager1::CImpIPathManager::ModifyEntryInNode
  509. (PTaggedPathInfo ptsi, PBYTE pb, UINT cb, PCacheBlock *papCBSet, UINT iLevel)
  510. {
  511. if (iLevel >= m_dbh.cDirectoryLevels)
  512. return NO_ERROR;
  513. PCacheBlock pCBNode = papCBSet[iLevel];
  514. PLeafNode pln = &(pCBNode->ldb);
  515. //invalidate the binary search index
  516. BOOL fSmall = m_dbh.cbDirectoryBlock <= 0x10000;
  517. if (fSmall && (pln->nh.cbSlack >= sizeof(USHORT)))
  518. {
  519. ((PUSHORT)(PBYTE(pln) + m_dbh.cbDirectoryBlock))[-1] = 0;
  520. }
  521. else if (!fSmall && (pln->nh.cbSlack >= sizeof(UINT)))
  522. {
  523. ((PUINT)(PBYTE(pln) + m_dbh.cbDirectoryBlock))[-1] = 0;
  524. }
  525. UINT offKey = pCBNode->cbKeyOffset;
  526. UINT cbSlack = pln->nh.cbSlack;
  527. UINT cbTotal = m_dbh.cbDirectoryBlock - sizeof(NodeHeader);
  528. PBYTE pbInfo = PBYTE(pln) + offKey + sizeof(NodeHeader);
  529. if (iLevel == 0)
  530. {
  531. cbTotal -= sizeof(LeafChainLinks);
  532. pbInfo += sizeof(LeafChainLinks);
  533. }
  534. UINT cbOld = pCBNode->cbEntry;
  535. if (cbOld > cb || cbSlack >= cb - cbOld)
  536. {
  537. UINT cbTrailing = cbTotal - cbSlack - offKey - cbOld;
  538. if (cbTrailing)
  539. {
  540. MoveMemory(pbInfo + cb, pbInfo + cbOld, cbTrailing);
  541. if (iLevel == 0)
  542. while(!++(pln->lcl.iLeafSerial)); // Zero isn't a valid serial number!
  543. }
  544. CopyMemory(pbInfo, pb, cb);
  545. pln->nh.cbSlack -= cb - cbOld;
  546. pCBNode->fFlags |= DirtyBlock;
  547. return NO_ERROR;
  548. }
  549. ClearLockFlags(papCBSet);
  550. HRESULT hr = SplitANode(papCBSet, iLevel);
  551. if (SUCCEEDED(hr))
  552. hr = FindKeyAndLockBlockSet(ptsi, papCBSet, iLevel);
  553. if (SUCCEEDED(hr))
  554. hr = ModifyEntryInNode(ptsi, pb, cb, papCBSet, iLevel);
  555. return hr;
  556. }
  557. HRESULT CPathManager1::CImpIPathManager::SplitANode(PCacheBlock *papCBSet, UINT iLevel)
  558. {
  559. RonM_ASSERT(iLevel < m_dbh.cDirectoryLevels);
  560. PCacheBlock pCBNode = papCBSet[iLevel];
  561. PLeafNode pln = &(pCBNode->ldb);
  562. UINT cbSlack = pln->nh.cbSlack;
  563. UINT cbTotal = m_dbh.cbDirectoryBlock - sizeof(NodeHeader);
  564. PBYTE pbBase = PBYTE(pln) + sizeof(NodeHeader);
  565. // Now we scan entries looking for the halfway mark
  566. PBYTE pb;
  567. PBYTE pbGoal;
  568. PBYTE pbLimit = PBYTE(pln) + m_dbh.cbDirectoryBlock - pln->nh.cbSlack;
  569. TaggedPathInfo tsi;
  570. if (iLevel == 0)
  571. {
  572. cbTotal -= sizeof(LeafChainLinks);
  573. pbBase += sizeof(LeafChainLinks);
  574. pb = pbBase;
  575. pbGoal = pbBase + (cbTotal+1)/2;
  576. for ( ; pb < pbLimit; )
  577. {
  578. UINT cbKey = DecodeVL32((const BYTE **) &pb);
  579. pb = SkipKeyInfo(pb + cbKey);
  580. if (pb >= pbGoal)
  581. break;
  582. }
  583. }
  584. else
  585. {
  586. pb = pbBase;
  587. pbGoal = pbBase + (cbTotal+1)/2;
  588. for ( ; pb <= pbLimit; )
  589. {
  590. UINT cbKey = DecodeVL32((const BYTE **) &pb);
  591. pb = SkipVL(pb + cbKey);
  592. if (pb >= pbGoal)
  593. break;
  594. }
  595. }
  596. RonM_ASSERT(pb <= pbLimit);
  597. UINT cbPrefix = UINT(pb - pbBase);
  598. PCacheBlock pCBNode2;
  599. HRESULT hr = AllocateNode(&pCBNode2, (iLevel == 0)? LeafBlock : InternalBlock);
  600. if (S_OK !=hr)
  601. return hr;
  602. PLeafNode pln2 = &(pCBNode2->ldb);
  603. PBYTE pbDest = PBYTE(pln2) + sizeof(NodeHeader);
  604. if (iLevel == 0)
  605. pbDest += sizeof(LeafChainLinks);
  606. CopyMemory(pbDest, pb, UINT(pbLimit - pb));
  607. pln2->nh.cbSlack = cbSlack + cbPrefix;
  608. pln ->nh.cbSlack = cbTotal - cbPrefix;
  609. KillKeyCount(pln );
  610. KillKeyCount(pln2);
  611. // Both of these blocks have changed. So we set the dirty
  612. // flags to ensure they will get written to disk.
  613. pCBNode ->fFlags |= DirtyBlock;
  614. pCBNode2->fFlags |= DirtyBlock;
  615. if (iLevel == 0) // Have we split a leaf node?
  616. {
  617. // If so, we've got to insert the new node into the chain of leaf nodes.
  618. while (!++(pln->lcl.iLeafSerial)); // Zero isn't a valid serial number.
  619. UINT iLeafNew = pCBNode2->iBlock;
  620. UINT iLeafOld = pCBNode ->iBlock;
  621. UINT iLeafNext = pln->lcl.iLeafNext;
  622. pln ->lcl.iLeafNext = iLeafNew;
  623. pln2->lcl.iLeafNext = iLeafNext;
  624. pln2->lcl.iLeafPrevious = iLeafOld;
  625. if (ValidBlockIndex(iLeafNext))
  626. {
  627. PCacheBlock pCBLeafNext;
  628. hr = FindCacheBlock(&pCBLeafNext, iLeafNext);
  629. if (!SUCCEEDED(hr))
  630. return hr;
  631. RonM_ASSERT(pCBLeafNext->ldb.lcl.iLeafPrevious == iLeafOld);
  632. pCBLeafNext->ldb.lcl.iLeafPrevious = iLeafNew;
  633. pCBLeafNext->fFlags |= DirtyBlock;
  634. }
  635. else
  636. {
  637. RonM_ASSERT(m_dbh.iLeafLast == iLeafOld);
  638. m_dbh.iLeafLast = iLeafNew;
  639. m_fHeaderIsDirty = TRUE;
  640. }
  641. }
  642. hr = DecodePathKey((const BYTE **) &pbDest, tsi.SI.awszStreamPath, (PUINT) &tsi.SI.cwcStreamPath);
  643. if (!SUCCEEDED(hr))
  644. return hr;
  645. BYTE abEntry[CB_STREAM_INFO_MAX];
  646. pb = EncodePathKey(abEntry, tsi.SI.awszStreamPath, (UINT) tsi.SI.cwcStreamPath);
  647. pb = EncodeVL32(pb, pCBNode2->iBlock);
  648. UINT cb = UINT(pb - abEntry);
  649. // We've finished with the bottom blocks. So we can
  650. // unlock their cache blocks for reuse.
  651. pCBNode ->fFlags &= ~LockedBlock;
  652. pCBNode2->fFlags &= ~LockedBlock;
  653. RonM_ASSERT(iLevel <= m_dbh.cDirectoryLevels);
  654. if (iLevel == m_dbh.cDirectoryLevels - 1)
  655. {
  656. // Parent node doesn't exist!
  657. // We have to add a new level to the B-Tree.
  658. BYTE abEntryLeft[CB_STREAM_INFO_MAX];
  659. PBYTE pbLeft = pbBase;
  660. UINT cbKeyLeft = DecodeVL32((const BYTE **) &pbLeft);
  661. cbKeyLeft += UINT(pbLeft - pbBase);
  662. CopyMemory(abEntryLeft, pbBase, cbKeyLeft);
  663. pbLeft = EncodeVL32(abEntryLeft + cbKeyLeft, pCBNode->iBlock);
  664. UINT cbEntryLeft = UINT(pbLeft - abEntryLeft);
  665. PCacheBlock pCBNodeR;
  666. HRESULT hr = AllocateNode(&pCBNodeR, InternalBlock);
  667. if (!SUCCEEDED(hr))
  668. return hr;
  669. PInternalNode plnR = &(pCBNodeR->idb);
  670. plnR->nh.cbSlack = m_dbh.cbDirectoryBlock - sizeof(NodeHeader) - cbEntryLeft;
  671. KillKeyCount(PLeafNode(plnR));
  672. CopyMemory(PBYTE(plnR) + sizeof(NodeHeader), abEntryLeft, cbEntryLeft);
  673. pCBNodeR->cbKeyOffset = 0;
  674. pCBNodeR->cbEntry = cbEntryLeft;
  675. pCBNodeR->fFlags |= (DirtyBlock | LockedBlock);
  676. papCBSet[m_dbh.cDirectoryLevels++] = pCBNodeR;
  677. m_dbh.iRootDirectory = pCBNodeR->iBlock;
  678. m_fHeaderIsDirty = TRUE;
  679. }
  680. RonM_ASSERT(iLevel + 1 < m_dbh.cDirectoryLevels); // Parent node exists.
  681. hr = InsertEntryIntoNode(&tsi, abEntry, cb, papCBSet, iLevel + 1, TRUE);
  682. return hr;
  683. }
  684. HRESULT CPathManager1::CImpIPathManager::GetFreeBlock(PCacheBlock &pCB)
  685. {
  686. if (m_pCBFreeList) // Do we have any unused cache blocks?
  687. {
  688. pCB = m_pCBFreeList; // Pull the first item from the list
  689. m_pCBFreeList = pCB->pCBNext;
  690. pCB->pCBNext = NULL;
  691. return S_OK;
  692. }
  693. else return S_FALSE;
  694. }
  695. HRESULT CPathManager1::CImpIPathManager::GetActiveBlock(PCacheBlock &pCB, BOOL fIgnoreLocks)
  696. {
  697. if (m_pCBLeastRecent) // Do we have any allocated cache blocks
  698. {
  699. // If so, scan the list to find one we can use.
  700. for (PCacheBlock pCBNext = m_pCBLeastRecent; pCBNext; pCBNext = pCBNext->pCBNext)
  701. {
  702. // In general locked blocks are unavailable to us.
  703. // However during the destructor for CPathManager1::CImpIPathManager,
  704. // fIgnoreLocks will be set true. Of course we should not have any
  705. // lock bits set when the destructor is called. So the destructor
  706. // will Assert if it sees any locked cache blocks.
  707. if (fIgnoreLocks || !(pCBNext->fFlags & LockedBlock))
  708. {
  709. if (pCBNext->fFlags & DirtyBlock) // Write contents to disk
  710. {
  711. HRESULT hr = WriteCacheBlock(pCBNext); // when necesary
  712. if (!SUCCEEDED(hr)) continue;
  713. }
  714. RemoveFromUse(pCBNext);
  715. pCB = pCBNext;
  716. return S_OK;
  717. }
  718. }
  719. }
  720. return S_FALSE;
  721. }
  722. void CPathManager1::CImpIPathManager::MarkAsMostRecent(PCacheBlock pCB)
  723. {
  724. // This routine inserts a cache block into the MRU end of the chain
  725. // of in-use cache blocks.
  726. RonM_ASSERT((pCB->fFlags & BlockTypeMask) != 0); // Must have a type
  727. RonM_ASSERT((pCB->fFlags & BlockTypeMask) != FreeBlock); // Can't be a free block
  728. RonM_ASSERT(pCB->pCBPrev == NULL);
  729. RonM_ASSERT(pCB->pCBNext == NULL);
  730. RonM_ASSERT(pCB != m_pCBMostRecent);
  731. pCB->pCBPrev = m_pCBMostRecent;
  732. if (m_pCBMostRecent)
  733. {
  734. RonM_ASSERT(m_pCBLeastRecent);
  735. m_pCBMostRecent->pCBNext = pCB;
  736. }
  737. else
  738. {
  739. RonM_ASSERT(!m_pCBLeastRecent);
  740. m_pCBLeastRecent = pCB;
  741. }
  742. pCB->pCBNext = NULL;
  743. m_pCBMostRecent = pCB;
  744. }
  745. void CPathManager1::CImpIPathManager::RemoveFromUse(PCacheBlock pCB)
  746. {
  747. // This routine removes a cache block from the chain of in-use blocks.
  748. RonM_ASSERT((pCB->fFlags & BlockTypeMask) != 0); // Must have a type
  749. RonM_ASSERT((pCB->fFlags & BlockTypeMask) != FreeBlock); // Can't be a free block
  750. PCacheBlock pCBNext = pCB->pCBNext;
  751. PCacheBlock pCBPrev = pCB->pCBPrev;
  752. if (pCBNext)
  753. pCBNext->pCBPrev = pCBPrev;
  754. else m_pCBMostRecent = pCBPrev;
  755. if (pCBPrev)
  756. pCBPrev->pCBNext = pCBNext;
  757. else m_pCBLeastRecent = pCBNext;
  758. pCB->pCBNext = NULL;
  759. pCB->pCBPrev = NULL;
  760. }
  761. HRESULT CPathManager1::CImpIPathManager::GetCacheBlock(PCacheBlock *ppCB, UINT fTypeMask)
  762. {
  763. PCacheBlock pCB = NULL;
  764. HRESULT hr = GetFreeBlock(pCB);
  765. if (hr == S_FALSE) // If we couldn't get a free block
  766. { // We first try to allocate a new block.
  767. // The number of blocks we can allocate is set a creation time.
  768. // However we always require space for two blocks per level in the
  769. // tree. That's because in the worst case we could split a node at
  770. // each level of the tree. Actually we can probably do better than
  771. // that with careful management of the lock bits on the cache blocks.
  772. if (m_dbh.cCacheBlocksMax < 2 * m_dbh.cDirectoryLevels)
  773. m_dbh.cCacheBlocksMax = 2 * m_dbh.cDirectoryLevels;
  774. if (m_cCacheBlocks < m_dbh.cCacheBlocksMax) // Can we create another cache block?
  775. {
  776. // The size of cache block depends on the on-disk block size chosen when the
  777. // path manager object was created. This works because the variable sized
  778. // portion is a trailing byte array in each case. So we allocate each cache
  779. // block as a byte array and then cast its address.
  780. // Note that when we delete a CacheBlock, we must do the cast in the opposite
  781. // direction to keep the object allocator clued in about the real size of this
  782. // object.
  783. RonM_ASSERT(sizeof(LeafNode) >= sizeof(InternalNode));
  784. pCB = (CacheBlock *) New BYTE[sizeof(CacheBlock) + m_dbh.cbDirectoryBlock
  785. - sizeof(LeafNode)
  786. ];
  787. m_cCacheBlocks++;
  788. pCB->pCBNext = NULL;
  789. pCB->pCBPrev = NULL;
  790. hr = NO_ERROR;
  791. }
  792. else hr = GetActiveBlock(pCB); // Finally we look for an active block that we
  793. // take over.
  794. }
  795. if (S_OK != hr) // Did we get a block?
  796. return hr;
  797. pCB->fFlags = fTypeMask; // Set the type flag; clear all other states
  798. pCB->iBlock = INVALID_INDEX; // Mark the on-disk index invalid
  799. MarkAsMostRecent(pCB); // Move to the end of the LRU list.
  800. *ppCB = pCB;
  801. return NO_ERROR;
  802. }
  803. HRESULT CPathManager1::CImpIPathManager::FreeCacheBlock(PCacheBlock pCB)
  804. {
  805. RonM_ASSERT((pCB->fFlags & BlockTypeMask) == FreeBlock);
  806. // pCB->fFlags = FreeBlock;
  807. pCB->pCBNext = m_pCBFreeList;
  808. m_pCBFreeList = pCB;
  809. return NO_ERROR;
  810. }
  811. HRESULT CPathManager1::CImpIPathManager:: ReadCacheBlock(PCacheBlock pCB, UINT iBlock)
  812. {
  813. RonM_ASSERT(!ValidBlockIndex(pCB->iBlock));
  814. pCB->fFlags |= ReadingIn; // For when we put in asynchronous I/O
  815. CULINT ullBase(sizeof(m_dbh)), ullSpan(m_dbh.cbDirectoryBlock), ullOffset;
  816. ullOffset = ullBase + ullSpan * iBlock;
  817. ULONG cbRead = 0;
  818. HRESULT hr = m_plbPathDatabase->ReadAt(ullOffset.Uli(), &(pCB->ldb),
  819. m_dbh.cbDirectoryBlock, &cbRead
  820. );
  821. RonM_ASSERT(SUCCEEDED(hr));
  822. pCB->fFlags &= ~ReadingIn; // For when we put in asynchronous I/O
  823. if (SUCCEEDED(hr) && cbRead != m_dbh.cbDirectoryBlock)
  824. hr = STG_E_READFAULT;
  825. RonM_ASSERT(SUCCEEDED(hr));
  826. if (SUCCEEDED(hr))
  827. {
  828. UINT uiMagic = pCB->ldb.nh.uiMagic;
  829. if (uiMagic == uiMagicLeaf)
  830. pCB->fFlags = LeafBlock;
  831. else
  832. if (uiMagic == uiMagicInternal)
  833. pCB->fFlags = InternalBlock;
  834. else
  835. if (uiMagic == uiMagicUnused)
  836. pCB->fFlags = InternalBlock | LeafBlock;
  837. else return STG_E_DOCFILECORRUPT;
  838. pCB->iBlock = iBlock;
  839. }
  840. RonM_ASSERT(SUCCEEDED(hr));
  841. return hr;
  842. }
  843. HRESULT CPathManager1::CImpIPathManager::WriteCacheBlock
  844. (PCacheBlock pCB)
  845. {
  846. RonM_ASSERT((pCB->fFlags & DirtyBlock));
  847. RonM_ASSERT((pCB->fFlags & (InternalBlock | LeafBlock | FreeBlock)));
  848. RonM_ASSERT(!(pCB->fFlags & (LockedBlock | ReadingIn | WritingOut)));
  849. RonM_ASSERT(ValidBlockIndex(pCB->iBlock) && pCB->iBlock < m_dbh.cBlocks);
  850. UINT fType = pCB->fFlags & BlockTypeMask;
  851. HRESULT hr = NO_ERROR;
  852. if (fType == InternalBlock || fType == LeafBlock)
  853. {
  854. // For these node types we attempt to build an access vector
  855. PLeafNode pln = &(pCB->ldb);
  856. PBYTE pauAccess = PBYTE(pln) + m_dbh.cbDirectoryBlock;
  857. BOOL fSmall = m_dbh.cbDirectoryBlock <= 0x10000;
  858. if ( ( fSmall
  859. && pln->nh.cbSlack >= sizeof(USHORT)
  860. && !((PUSHORT)pauAccess)[-1]
  861. )
  862. || ( !fSmall
  863. && pln->nh.cbSlack >= sizeof(UINT)
  864. && !((PUINT)pauAccess)[-1]
  865. )
  866. )
  867. {
  868. PBYTE pbLimit = PBYTE(pauAccess) - pln->nh.cbSlack;
  869. PBYTE pbEntry = pln->ab;
  870. PBYTE pbStartOffset;
  871. if (fType == InternalBlock)
  872. pbEntry -= sizeof(LeafChainLinks);
  873. UINT cbSlack = pln->nh.cbSlack - (fSmall ? sizeof(USHORT) : sizeof(UINT));
  874. UINT cEntries = 0;
  875. UINT cSearchVEntries = 0;
  876. UINT cChunkSize = (1 << m_dbh.cEntryAccessShift)+ 1;
  877. PathInfo SI;
  878. BOOL fEnoughSpace = TRUE;
  879. UINT cb;
  880. pauAccess -= (fSmall ? sizeof(USHORT) : sizeof(UINT));
  881. pbStartOffset = pbEntry;
  882. for (;(pbEntry < pbLimit) && fEnoughSpace; )
  883. {
  884. if (cEntries && ((cEntries % cChunkSize) == 0))
  885. {
  886. cb = (fSmall ? sizeof(USHORT) : sizeof(UINT));
  887. }
  888. else
  889. {
  890. cb = 0;
  891. }
  892. if (cb && (cbSlack >= cb))
  893. {
  894. cSearchVEntries++;
  895. if (fSmall)
  896. {
  897. ((PUSHORT)pauAccess)[-1] = USHORT(pbEntry - pbStartOffset);
  898. }
  899. else
  900. {
  901. ((PUINT)pauAccess)[-1] = UINT(pbEntry - pbStartOffset);
  902. }
  903. cbSlack -= cb;
  904. pauAccess -= cb;
  905. }
  906. else if (cb)
  907. fEnoughSpace = FALSE;
  908. //advance to the next entry
  909. if (fType == LeafBlock)
  910. {
  911. hr = DecodePathInfo((const BYTE **) &pbEntry, &SI);
  912. }
  913. else
  914. {
  915. hr = DecodePathKey((const BYTE **) &pbEntry, SI.awszStreamPath,
  916. (PUINT) &(SI.cwcStreamPath));
  917. DecodeVL32((const BYTE **) &pbEntry);
  918. }
  919. cEntries++;
  920. } //for
  921. if (fEnoughSpace)
  922. {
  923. if (fSmall)
  924. {
  925. ((PUSHORT)(PBYTE(pln) + m_dbh.cbDirectoryBlock))[-1] = USHORT(cEntries);
  926. }
  927. else
  928. {
  929. ((PUINT)(PBYTE(pln) + m_dbh.cbDirectoryBlock))[-1] = cEntries;
  930. }
  931. RonM_ASSERT(cSearchVEntries == (cEntries/cChunkSize - 1 + ((cEntries%cChunkSize)?1 : 0)));
  932. }
  933. }//access vector not already there
  934. }//for internal and leaf blocks only
  935. CULINT ullBase(sizeof(m_dbh)), ullSpan(m_dbh.cbDirectoryBlock), ullOffset;
  936. ullOffset = ullBase + ullSpan * pCB->iBlock;
  937. ULONG cbWritten = 0;
  938. pCB->fFlags |= WritingOut; // For when we do Asynch I/O
  939. hr = m_plbPathDatabase->WriteAt(ullOffset.Uli(), &(pCB->ldb),
  940. m_dbh.cbDirectoryBlock, &cbWritten
  941. );
  942. RonM_ASSERT(SUCCEEDED(hr));
  943. pCB->fFlags &= ~WritingOut; // For when we do Asynch I/O
  944. if (SUCCEEDED(hr) && cbWritten != m_dbh.cbDirectoryBlock)
  945. hr = STG_E_WRITEFAULT;
  946. if (SUCCEEDED(hr))
  947. pCB->fFlags &= ~DirtyBlock;
  948. RonM_ASSERT(SUCCEEDED(hr));
  949. return hr;
  950. }
  951. HRESULT CPathManager1::CImpIPathManager::FindCacheBlock(PCacheBlock *ppCB, UINT iBlock)
  952. {
  953. RonM_ASSERT(ValidBlockIndex(iBlock) && iBlock < m_dbh.cBlocks);
  954. PCacheBlock pCB;
  955. for (pCB = m_pCBLeastRecent; pCB; pCB = pCB->pCBNext)
  956. {
  957. if (pCB->iBlock == iBlock)
  958. {
  959. // Found it!
  960. RemoveFromUse(pCB);
  961. MarkAsMostRecent(pCB);
  962. *ppCB = pCB;
  963. return NO_ERROR;
  964. }
  965. }
  966. // Didn't find the block in the cache. We need to read it in.
  967. // First we must find a cache block we can use.
  968. pCB = NULL;
  969. HRESULT hr = GetCacheBlock(&pCB, LeafBlock);
  970. if (hr == S_OK)
  971. {
  972. if (SUCCEEDED(hr))
  973. hr = ReadCacheBlock(pCB, iBlock);
  974. if (!SUCCEEDED(hr))
  975. {
  976. RemoveFromUse(pCB);
  977. pCB->fFlags = FreeBlock;
  978. FreeCacheBlock(pCB); pCB = NULL;
  979. }
  980. }
  981. else
  982. if (hr == S_FALSE)
  983. hr = STG_E_INSUFFICIENTMEMORY;
  984. *ppCB = pCB;
  985. return hr;
  986. }
  987. HRESULT CPathManager1::CImpIPathManager::AllocateNode(PCacheBlock *ppCB, UINT fTypeMask)
  988. {
  989. HRESULT hr = NO_ERROR;
  990. PCacheBlock pCB;
  991. RonM_ASSERT(fTypeMask == InternalBlock || fTypeMask == LeafBlock);
  992. if (ValidBlockIndex(m_dbh.iBlockFirstFree))
  993. {
  994. hr = FindCacheBlock(&pCB, m_dbh.iBlockFirstFree);
  995. if (!SUCCEEDED(hr))
  996. return hr;
  997. m_dbh.iBlockFirstFree = pCB->ldb.lcl.iLeafNext;
  998. pCB->ldb.lcl.iLeafNext = INVALID_INDEX;
  999. pCB->fFlags = fTypeMask;
  1000. m_fHeaderIsDirty = TRUE;
  1001. }
  1002. else
  1003. {
  1004. hr = GetCacheBlock(&pCB, fTypeMask);
  1005. if (S_OK != hr)
  1006. return hr;
  1007. pCB->iBlock = m_dbh.cBlocks++;
  1008. m_fHeaderIsDirty = TRUE;
  1009. }
  1010. pCB->ldb.lcl.iLeafPrevious = INVALID_INDEX;
  1011. pCB->idb.nh.uiMagic = (fTypeMask == InternalBlock)? uiMagicInternal : uiMagicLeaf;
  1012. *ppCB = pCB;
  1013. return hr;
  1014. }
  1015. HRESULT CPathManager1::CImpIPathManager::DiscardNode(PCacheBlock pCB)
  1016. {
  1017. // This routine adds a node block to the free list for later use.
  1018. RemoveFromUse(pCB);
  1019. pCB->ldb.lcl.iLeafNext = m_dbh.iBlockFirstFree;
  1020. m_dbh.iBlockFirstFree = pCB->iBlock;
  1021. pCB->fFlags = DirtyBlock | FreeBlock;
  1022. pCB->ldb.nh.uiMagic = uiMagicUnused;
  1023. m_fHeaderIsDirty = TRUE;
  1024. HRESULT hr= WriteCacheBlock(pCB);
  1025. if (SUCCEEDED(hr))
  1026. hr = FreeCacheBlock(pCB);
  1027. return hr;
  1028. }
  1029. ULONG DecodeVL32(const BYTE **ppb)
  1030. {
  1031. const BYTE *pb = *ppb;
  1032. ULONG ul = 0;
  1033. for (;;)
  1034. {
  1035. BYTE b= *pb++;
  1036. ul = (ul << 7) | (b & 0x7f);
  1037. if (b < 0x80)
  1038. break;
  1039. }
  1040. *ppb = pb;
  1041. return ul;
  1042. }
  1043. ULONG CodeSizeVL32(ULONG ul)
  1044. {
  1045. ULONG cb = 1;
  1046. for (; ul >>= 7; cb++);
  1047. return cb;
  1048. }
  1049. PBYTE EncodeVL32(PBYTE pb, ULONG ul)
  1050. {
  1051. BYTE abBuff[8]; // We're only going to use 5 bytes. The 8 is for alignment.
  1052. PBYTE pbNext = abBuff;
  1053. do
  1054. {
  1055. *pbNext++ = 0x80 + (BYTE(ul) & 0x7F);
  1056. ul >>= 7;
  1057. }
  1058. while (ul);
  1059. abBuff[0] &= 0x7F;
  1060. for (UINT c = UINT(pbNext - abBuff); c--; )
  1061. *pb++ = *--pbNext;
  1062. return pb;
  1063. }
  1064. CULINT DecodeVL64(const BYTE **ppb)
  1065. {
  1066. const BYTE *pb = *ppb;
  1067. CULINT ull(0);
  1068. for (;;)
  1069. {
  1070. BYTE b= *pb++;
  1071. ull <<= 7;
  1072. ull |= UINT(b & 0x7f);
  1073. if (b < 0x80)
  1074. break;
  1075. }
  1076. *ppb = pb;
  1077. return ull;
  1078. }
  1079. PBYTE EncodeVL64(PBYTE pb, CULINT *ullValue)
  1080. {
  1081. CULINT ull(*ullValue);
  1082. BYTE abBuff[16]; // We'll use just 10 bytes. The 16 is for alignment.
  1083. PBYTE pbNext = abBuff;
  1084. do
  1085. {
  1086. *pbNext++ = 0x80 + (BYTE(ull.Uli().LowPart) & 0x7F);
  1087. ull >>= 7;
  1088. }
  1089. while (ull.NonZero());
  1090. abBuff[0] &= 0x7F;
  1091. for (UINT c = UINT(pbNext - abBuff); c--; )
  1092. *pb++ = *--pbNext;
  1093. return pb;
  1094. }
  1095. PBYTE SkipVL(PBYTE pb)
  1096. {
  1097. while (0x80 <= *pb++);
  1098. return pb;
  1099. }
  1100. HRESULT CPathManager1::CImpIPathManager::DecodePathKey
  1101. (const BYTE **ppb, PWCHAR pwszPath, PUINT pcwcPath)
  1102. {
  1103. const BYTE *pb = *ppb;
  1104. ULONG cbPath = DecodeVL32(&pb);
  1105. ULONG cwc = UTF8ToWideChar((const char *) pb, INT(cbPath), pwszPath, MAX_PATH - 1);
  1106. RonM_ASSERT(cwc);
  1107. if (!cwc)
  1108. return GetLastError();
  1109. pwszPath[cwc] = 0;
  1110. *pcwcPath = cwc;
  1111. *ppb = pb + cbPath;
  1112. return NO_ERROR;
  1113. }
  1114. PBYTE CPathManager1::CImpIPathManager::EncodePathKey
  1115. (PBYTE pb, const WCHAR *pwszPath, UINT cwcPath)
  1116. {
  1117. BYTE abFileName[MAX_UTF8_PATH];
  1118. INT cb = WideCharToUTF8(pwszPath, cwcPath, PCHAR(abFileName), MAX_UTF8_PATH);
  1119. RonM_ASSERT(cb);
  1120. pb = EncodeVL32(pb, cb);
  1121. CopyMemory(pb, abFileName, cb);
  1122. return pb + cb;
  1123. }
  1124. HRESULT CPathManager1::CImpIPathManager::DecodeKeyInfo
  1125. (const BYTE **ppb, PPathInfo pSI)
  1126. {
  1127. const BYTE *pb = *ppb;
  1128. CULINT ullStateAndSegment;
  1129. ullStateAndSegment = DecodeVL64(&pb);
  1130. pSI->uStateBits = ullStateAndSegment.Uli().HighPart;
  1131. pSI->iLockedBytesSegment = ullStateAndSegment.Uli(). LowPart;
  1132. pSI->ullcbOffset = DecodeVL64(&pb);
  1133. pSI->ullcbData = DecodeVL64(&pb);
  1134. *ppb = pb;
  1135. return NO_ERROR;
  1136. }
  1137. PBYTE CPathManager1::CImpIPathManager::SkipKeyInfo(PBYTE pb)
  1138. {
  1139. for (UINT c= 3; c--; )
  1140. while (0x80 <= *pb++);
  1141. return pb;
  1142. }
  1143. PBYTE CPathManager1::CImpIPathManager::EncodeKeyInfo
  1144. (PBYTE pb, const PathInfo *pSI)
  1145. {
  1146. CULINT ullcbOffset;
  1147. ullcbOffset = pSI->ullcbOffset;
  1148. CULINT ullcbData;
  1149. ullcbData = pSI->ullcbData;
  1150. ULARGE_INTEGER uliStateAndSegment;
  1151. uliStateAndSegment.HighPart = pSI->uStateBits;
  1152. uliStateAndSegment. LowPart = pSI->iLockedBytesSegment;
  1153. CULINT ullStateAndSegment;
  1154. ullStateAndSegment = uliStateAndSegment;
  1155. pb = EncodeVL64(pb, &ullStateAndSegment);
  1156. pb = EncodeVL64(pb, &ullcbOffset);
  1157. pb = EncodeVL64(pb, &ullcbData);
  1158. return pb;
  1159. }
  1160. HRESULT CPathManager1::CImpIPathManager::DecodePathInfo
  1161. (const BYTE **ppb, PPathInfo pSI)
  1162. {
  1163. const BYTE *pb = *ppb;
  1164. HRESULT hr = DecodePathKey(&pb, pSI->awszStreamPath, (PUINT) &(pSI->cwcStreamPath));
  1165. if (!SUCCEEDED(hr))
  1166. return hr;
  1167. hr = DecodeKeyInfo(&pb, pSI);
  1168. if (!SUCCEEDED(hr))
  1169. return hr;
  1170. *ppb = pb;
  1171. return NO_ERROR;
  1172. }
  1173. PBYTE CPathManager1::CImpIPathManager::EncodePathInfo
  1174. (PBYTE pb, const PathInfo *pSI)
  1175. {
  1176. pb = EncodePathKey(pb, pSI->awszStreamPath, pSI->cwcStreamPath);
  1177. pb = EncodeKeyInfo(pb, pSI);
  1178. return pb;
  1179. }
  1180. void CPathManager1::CImpIPathManager::ClearLockFlags(PCacheBlock *papCBSet)
  1181. {
  1182. for (UINT c = m_dbh.cDirectoryLevels; c--; )
  1183. papCBSet[c]->fFlags &= ~LockedBlock;
  1184. }
  1185. void CPathManager1::CImpIPathManager::ClearLockFlagsAbove
  1186. (PCacheBlock *papCBSet, UINT iLevel)
  1187. {
  1188. for (UINT c = m_dbh.cDirectoryLevels; c-- > iLevel; )
  1189. papCBSet[c]->fFlags &= ~LockedBlock;
  1190. }
  1191. HRESULT CPathManager1::CImpIPathManager::FindKeyAndLockBlockSet
  1192. (PTaggedPathInfo ptsi, PCacheBlock *papCBSet, UINT iLevel, UINT cLevels)
  1193. {
  1194. // This routine searches for an instances of a particular key within the
  1195. // B-Tree nodes. It uses a recursive algorithm which loads the nodes
  1196. // in the key path into cache blocks, locks them in memory, and records
  1197. // the sequence of cache blocks in papCBSet.
  1198. //
  1199. // The entries in papCBSet are ordered from leaf node to root node. That
  1200. // is, papCBSet[0] denotes the cache block which contains the leaf node
  1201. // that either contains the key in question or is the correct place to
  1202. // insert a new key. Then papCBSet[1] refers to the internal node which
  1203. // points to the leaf node, papCBSet[2] describes the grandparent node
  1204. // for the leaf, and so on. As you can see, *papCBSet must contain one
  1205. // entry for each level of the B-Tree.
  1206. //
  1207. // If you're just looking up an existing key and retrieving its
  1208. // information record, recording that information in papCBSet and locking
  1209. // the cache blocks is unnecessary. You can prevent that by passing NULL
  1210. // for the papCBSet parameter.
  1211. //
  1212. // However if you need to insert a key, delete a key, or changes its
  1213. // information record, papCBSet gives you a mechanism to follow the
  1214. // key's access path back toward the root.
  1215. //
  1216. // In many cases your changes will affect only the leaf node in the
  1217. // set. There are three situations in which your change may cause changes
  1218. // beyond the leaf node:
  1219. //
  1220. // 1. You've changed the tag key for the leaf node.
  1221. //
  1222. // The tag key is the first key in the leaf. It can change when you're
  1223. // inserting or deleting a key entry. The leaf's tag key is recorded in
  1224. // its parent node. Thus changes to a tag key will propagate to the
  1225. // parent node. If the key is also a tag for the parent node, the
  1226. // change will continue to propagate up the tree.
  1227. //
  1228. // 2. You've deleted the last key entry in the leaf and collapsed its
  1229. // content to nothing.
  1230. //
  1231. // In that case you must add the leaf node to the list of free blocks
  1232. // and remove its tag from its parent node. If that tag happens to
  1233. // be the only entry in the parent node, it too will collapse, and the
  1234. // collapse will propagate up the tree.
  1235. //
  1236. // 3. The leaf nodes doesn't have enough room for your change.
  1237. //
  1238. // This can happen when you're inserting a new key. It can also happen
  1239. // when you're changing the information for an existing key. That's
  1240. // because we're using variable length encodings in the information
  1241. // record. Thus changing a value can change the length of the
  1242. // information.
  1243. //
  1244. // In this case you must split the leaf node. That is, you must allocate
  1245. // a new leaf node and move the trailing half of its key entries into
  1246. // the new node. When you split a leaf node, you have to insert its tag
  1247. // into the tree heirarchy above the leaf.
  1248. //
  1249. // Each of these three scenarios can also occur when you're modifying
  1250. // a tag in one of the internal tree nodes. Thus in the most complicated
  1251. // scenarios changes can proliferate and propagate recursively throughout
  1252. // the tree heirarchy.
  1253. //
  1254. // The *papCBSet information is meant to handle relatively simple cases of
  1255. // change propagation. Whenever a change forks into two side-effects, the
  1256. // correct strategy is to unlock all the cache blocks in the set and then
  1257. // make each change as a separate transaction at the appropriate level within
  1258. // the tree. The important thing to node here is that you must number levels
  1259. // relative to the leaf level. That's because changes propagating through
  1260. // the B-Tree may add or subtract levels above the level where you're working.
  1261. // Note: This function must be executed within a critical section.
  1262. HRESULT hr = NO_ERROR;
  1263. PCacheBlock pCB = NULL;
  1264. if (cLevels == UINT(~0))
  1265. cLevels = m_dbh.cDirectoryLevels;
  1266. RonM_ASSERT(cLevels >= iLevel);
  1267. if (!cLevels)
  1268. {
  1269. // Nothing defined yet. Create the first leaf block.
  1270. RonM_ASSERT(!ValidBlockIndex(m_dbh.iRootDirectory));
  1271. RonM_ASSERT(!ValidBlockIndex(m_dbh.iLeafFirst ));
  1272. RonM_ASSERT(!ValidBlockIndex(m_dbh.iLeafLast ));
  1273. hr = AllocateNode(&pCB, LeafBlock); // To force creation of a node.
  1274. if (!SUCCEEDED(hr))
  1275. return hr;
  1276. if (papCBSet)
  1277. {
  1278. papCBSet[0] = pCB;
  1279. pCB->fFlags |= LockedBlock;
  1280. }
  1281. RonM_ASSERT(m_dbh.cDirectoryLevels == 0);
  1282. // Now we must adjust the database header because we've gone
  1283. // from zero levels to one level -- the leaf level.
  1284. m_dbh.cDirectoryLevels++;
  1285. m_dbh.iLeafFirst = 0; // The leaf change exists now and this
  1286. m_dbh.iLeafLast = 0; // is the only node in the sequence.
  1287. m_fHeaderIsDirty = TRUE; // So our header changes will get copied to
  1288. // disk eventually.
  1289. pCB->fFlags |= LeafBlock;
  1290. RonM_ASSERT(pCB->fFlags & LockedBlock);
  1291. // Here we're setting up the header for the new leaf node.
  1292. pCB->ldb.nh.uiMagic = uiMagicLeaf; // So we can recognize bogus nodes
  1293. pCB->ldb.lcl.iLeafSerial = 1; // Initial version serial number
  1294. pCB->ldb.lcl.iLeafPrevious = INVALID_INDEX; // Nothing else in the leaf chain
  1295. pCB->ldb.lcl.iLeafNext = INVALID_INDEX; // Nothing else in the leaf chain
  1296. pCB->ldb.nh.cbSlack = m_dbh.cbDirectoryBlock - sizeof(NodeHeader)
  1297. - sizeof(LeafChainLinks);
  1298. KillKeyCount(&(pCB->ldb));
  1299. return ScanLeafForKey(pCB, ptsi);
  1300. }
  1301. RonM_ASSERT(cLevels > iLevel);
  1302. if (cLevels == m_dbh.cDirectoryLevels)
  1303. {
  1304. // This is the beginning of the recursive search. We're at the root of
  1305. // the B-Tree.
  1306. if (cLevels > 1)
  1307. {
  1308. // We have at least one internal node.
  1309. RonM_ASSERT(ValidBlockIndex(m_dbh.iRootDirectory));
  1310. hr = FindCacheBlock(&pCB, m_dbh.iRootDirectory); // Get the root node.
  1311. RonM_ASSERT(SUCCEEDED(hr)); // We're in big trouble if this doesn't work!
  1312. RonM_ASSERT(pCB->fFlags & InternalBlock); // Verify that it isn't a leaf
  1313. // or a free node.
  1314. }
  1315. else
  1316. {
  1317. // Must have just a single leaf node.
  1318. RonM_ASSERT(cLevels == 1);
  1319. RonM_ASSERT(ValidBlockIndex(m_dbh.iLeafFirst));
  1320. RonM_ASSERT(ValidBlockIndex(m_dbh.iLeafLast ));
  1321. RonM_ASSERT(m_dbh.iLeafFirst == m_dbh.iLeafLast);
  1322. hr = FindCacheBlock(&pCB, m_dbh.iLeafFirst);
  1323. RonM_ASSERT(SUCCEEDED(hr)); // We're in big trouble if this doesn't work!
  1324. RonM_ASSERT(pCB->fFlags & LeafBlock); // Verify that it isn't an internal
  1325. // node or a free node.
  1326. }
  1327. pCB->fFlags |= LockedBlock;
  1328. papCBSet[--cLevels] = pCB;
  1329. }
  1330. else --cLevels;
  1331. pCB = papCBSet[cLevels];
  1332. if (cLevels == 0)
  1333. {
  1334. // We've gotten to the leaf level.
  1335. RonM_ASSERT(iLevel == 0);
  1336. return ScanLeafForKey(pCB, ptsi);
  1337. }
  1338. else
  1339. {
  1340. UINT iChild;
  1341. hr = ScanInternalForKey(pCB, ptsi, &iChild);
  1342. if (!SUCCEEDED(hr))
  1343. return hr;
  1344. if (cLevels == iLevel) // Stop when we get down to the requested level.
  1345. return hr;
  1346. hr = FindCacheBlock(&pCB, iChild);
  1347. if (!SUCCEEDED(hr))
  1348. return hr;
  1349. RonM_ASSERT(SUCCEEDED(hr)); // We're in big trouble if this doesn't work!
  1350. pCB->fFlags |= LockedBlock;
  1351. papCBSet[cLevels - 1] = pCB;
  1352. hr = FindKeyAndLockBlockSet(ptsi, papCBSet, iLevel, cLevels);
  1353. }
  1354. return hr;
  1355. }
  1356. HRESULT CPathManager1::CImpIPathManager::ScanInternalForKey
  1357. (PCacheBlock pCacheBlock, PTaggedPathInfo ptsi, PUINT piChild)
  1358. {
  1359. RonM_ASSERT((pCacheBlock->fFlags & BlockTypeMask) == InternalBlock);
  1360. PInternalNode pidb = &(pCacheBlock->idb);
  1361. TaggedPathInfo tsi;
  1362. HRESULT hr;
  1363. UINT cEntries = 0;
  1364. if (m_dbh.cbDirectoryBlock <= 0x10000 && pidb->nh.cbSlack >= sizeof(USHORT))
  1365. cEntries = (UINT)(PUSHORT(PBYTE(pidb) + m_dbh.cbDirectoryBlock))[-1];
  1366. else if (m_dbh.cbDirectoryBlock > 0x10000 && pidb->nh.cbSlack >= sizeof(UINT))
  1367. cEntries = (PUINT(PBYTE(pidb) + m_dbh.cbDirectoryBlock))[-1];
  1368. ZeroMemory(&tsi, sizeof(tsi));
  1369. BOOL fSmall = m_dbh.cbDirectoryBlock <= 0x10000;
  1370. PBYTE pauAccess = PBYTE(pidb) + m_dbh.cbDirectoryBlock;
  1371. pauAccess -= (fSmall ? sizeof(USHORT) : sizeof(UINT));
  1372. PBYTE pb = pidb->ab;
  1373. UINT cSeqSearch = 0;
  1374. UINT cSearchVEntries = 0;
  1375. UINT cChunkSize = (1 << m_dbh.cEntryAccessShift)+ 1;
  1376. //Binary search in search index array to get to the closed entry from where
  1377. //sequential search can be started.
  1378. if (cEntries > 0)
  1379. {
  1380. if (cEntries > cChunkSize)
  1381. {
  1382. cSearchVEntries = (cEntries - cChunkSize) / cChunkSize;
  1383. cSearchVEntries += (((cEntries - cChunkSize) % cChunkSize) ? 1 : 0);
  1384. }
  1385. if (cSearchVEntries)
  1386. {
  1387. hr = BinarySearch(0, cSearchVEntries, pauAccess,
  1388. fSmall ? sizeof(USHORT) : sizeof(UINT), ptsi, pidb->ab, &pb, fSmall);
  1389. }
  1390. }
  1391. //start sequential search from here
  1392. RonM_ASSERT(pb >= pidb->ab);
  1393. PBYTE pbLimit = PBYTE(pidb) + m_dbh.cbDirectoryBlock - pidb->nh.cbSlack;
  1394. PBYTE pbLast = pb;
  1395. PBYTE pbKey = pb;
  1396. UINT iChildLast = UINT(~0);
  1397. cSeqSearch = 0;
  1398. for ( ; pb < pbLimit; )
  1399. {
  1400. cSeqSearch++;
  1401. pbLast = pbKey;
  1402. pbKey = pb;
  1403. HRESULT hr = DecodePathKey((const BYTE **) &pb, tsi.SI.awszStreamPath, (UINT *) &tsi.SI.cwcStreamPath);
  1404. if (!SUCCEEDED(hr))
  1405. return hr;
  1406. int icmp = wcsicmp_0x0409(ptsi->SI.awszStreamPath, tsi.SI.awszStreamPath);
  1407. if (icmp > 0)
  1408. {
  1409. iChildLast = DecodeVL32((const BYTE **) &pb);
  1410. if (cEntries)
  1411. RonM_ASSERT(cSeqSearch <= (cChunkSize + 1));
  1412. continue;
  1413. }
  1414. if (icmp == 0)
  1415. {
  1416. cSeqSearch = 0;
  1417. // Found it!
  1418. *piChild = DecodeVL32((const BYTE **) &pb);
  1419. pCacheBlock->cbKeyOffset = UINT(pbKey - pidb->ab);
  1420. pCacheBlock->cbEntry = UINT(pb - pbKey);
  1421. return S_OK;
  1422. }
  1423. // Found the place to insert key data
  1424. if (ValidBlockIndex(iChildLast))
  1425. {
  1426. *piChild = iChildLast;
  1427. pCacheBlock->cbEntry = UINT(pbKey - pbLast);
  1428. }
  1429. else
  1430. {
  1431. *piChild = DecodeVL32((const BYTE **) &pb);
  1432. pCacheBlock->cbEntry = UINT(pb - pbKey);
  1433. }
  1434. pCacheBlock->cbKeyOffset = UINT(pbLast - pidb->ab);
  1435. return S_FALSE;
  1436. }
  1437. RonM_ASSERT(pb == pbLimit); // Verifying that cbSlack is correct.
  1438. RonM_ASSERT(ValidBlockIndex(iChildLast)); // Because we don't keep empty
  1439. // internal empty nodes around.
  1440. *piChild = iChildLast;
  1441. pCacheBlock->cbKeyOffset = UINT(pbKey - pidb->ab);
  1442. pCacheBlock->cbEntry = UINT(pb - pbKey);
  1443. return S_FALSE;
  1444. }
  1445. HRESULT CPathManager1::CImpIPathManager::BinarySearch(
  1446. UINT uiStart,
  1447. UINT uiEnd,
  1448. PBYTE pauAccess,
  1449. UINT cbAccess,
  1450. PTaggedPathInfo ptsi,
  1451. PBYTE ab,
  1452. PBYTE *ppbOut,
  1453. BOOL fSmall)
  1454. {
  1455. TaggedPathInfo tsi;
  1456. ZeroMemory(&tsi, sizeof(tsi));
  1457. PBYTE pbKey;
  1458. HRESULT hr = NO_ERROR;
  1459. int icmp;
  1460. ULONG offset;
  1461. #ifdef _DEBUG
  1462. static int cLoop; // BugBug! This will fail in multithreaded situations!
  1463. cLoop++;
  1464. RonM_ASSERT(cLoop < 100);
  1465. #endif
  1466. if (uiStart == uiEnd)
  1467. {
  1468. if (uiEnd)
  1469. {
  1470. offset = fSmall ? *PUSHORT(pauAccess - uiEnd * cbAccess)
  1471. : *PUINT(pauAccess - uiEnd * cbAccess);
  1472. *ppbOut = ab + offset;
  1473. }
  1474. else
  1475. *ppbOut = ab;
  1476. #ifdef _DEBUG
  1477. cLoop = 0;
  1478. #endif
  1479. return NO_ERROR;
  1480. }
  1481. else
  1482. if (uiStart == (uiEnd -1))
  1483. {
  1484. #ifdef _DEBUG
  1485. cLoop = 0;
  1486. #endif
  1487. offset = fSmall ? *PUSHORT(pauAccess - uiEnd * cbAccess)
  1488. : *PUINT(pauAccess - uiEnd * cbAccess);
  1489. pbKey = ab + offset;
  1490. if (SUCCEEDED(hr = DecodePathKey((const BYTE **) &pbKey, tsi.SI.awszStreamPath, (UINT *) &tsi.SI.cwcStreamPath)))
  1491. {
  1492. icmp = wcsicmp_0x0409(ptsi->SI.awszStreamPath, tsi.SI.awszStreamPath);
  1493. if (icmp >= 0)
  1494. {
  1495. *ppbOut = ab + offset;
  1496. }
  1497. else
  1498. {
  1499. if (uiStart)
  1500. {
  1501. offset = fSmall ? *PUSHORT(pauAccess - uiStart * cbAccess)
  1502. : *PUINT(pauAccess - uiStart * cbAccess);
  1503. *ppbOut = ab + offset;
  1504. }
  1505. else
  1506. {
  1507. *ppbOut = ab;
  1508. }
  1509. }
  1510. }
  1511. return hr;
  1512. }
  1513. UINT uiMid = (uiEnd + uiStart) / 2;
  1514. offset = fSmall ? *PUSHORT(pauAccess - uiMid * cbAccess)
  1515. : *PUINT(pauAccess - uiMid * cbAccess);
  1516. pbKey = ab + offset;
  1517. if (SUCCEEDED(hr = DecodePathKey((const BYTE **) &pbKey, tsi.SI.awszStreamPath, (UINT *) &tsi.SI.cwcStreamPath)))
  1518. {
  1519. icmp = wcsicmp_0x0409(ptsi->SI.awszStreamPath, tsi.SI.awszStreamPath);
  1520. if (icmp < 0)
  1521. {
  1522. return BinarySearch(uiStart, uiMid - 1, pauAccess, cbAccess,
  1523. ptsi, ab, ppbOut, fSmall);
  1524. }
  1525. else
  1526. {
  1527. return BinarySearch(uiMid, uiEnd, pauAccess, cbAccess,
  1528. ptsi, ab, ppbOut, fSmall);
  1529. }
  1530. }
  1531. return hr;
  1532. }
  1533. HRESULT CPathManager1::CImpIPathManager::ScanLeafForKey
  1534. (PCacheBlock pCacheBlock, PTaggedPathInfo ptsi)
  1535. {
  1536. RonM_ASSERT((pCacheBlock->fFlags & BlockTypeMask) == LeafBlock);
  1537. PLeafNode pldb = &(pCacheBlock->ldb);
  1538. UINT cEntries = 0;
  1539. if (m_dbh.cbDirectoryBlock <= 0x10000 && pldb->nh.cbSlack >= sizeof(USHORT))
  1540. cEntries = (UINT)(PUSHORT(PBYTE(pldb) + m_dbh.cbDirectoryBlock))[-1];
  1541. else if (m_dbh.cbDirectoryBlock > 0x10000 && pldb->nh.cbSlack >= sizeof(UINT))
  1542. cEntries = PUINT(PBYTE(pldb) + m_dbh.cbDirectoryBlock)[-1];
  1543. pCacheBlock->cbEntry = 0; // For when the insertion point is at the end.
  1544. TaggedPathInfo tsi;
  1545. HRESULT hr;
  1546. ZeroMemory(&tsi, sizeof(tsi));
  1547. BOOL fSmall = m_dbh.cbDirectoryBlock <= 0x10000;
  1548. PBYTE pauAccess = PBYTE(pldb) + m_dbh.cbDirectoryBlock;
  1549. pauAccess -= (fSmall ? sizeof(USHORT) : sizeof(UINT));
  1550. PBYTE pb = pldb->ab;
  1551. UINT cSeqSearch = 0;
  1552. UINT cChunkSize = (1 << m_dbh.cEntryAccessShift) + 1;
  1553. //Binary search in search index array to get to the closed entry from where
  1554. //sequential search can be started.
  1555. if (cEntries > 0)
  1556. {
  1557. UINT cSearchVEntries = 0;
  1558. if (cEntries > cChunkSize)
  1559. {
  1560. cSearchVEntries = (cEntries - cChunkSize) / cChunkSize;
  1561. cSearchVEntries += (((cEntries - cChunkSize) % cChunkSize) ? 1 : 0);
  1562. }
  1563. if (cSearchVEntries)
  1564. {
  1565. hr = BinarySearch(0, cSearchVEntries, pauAccess,
  1566. fSmall ? sizeof(USHORT) : sizeof(UINT), ptsi, pldb->ab, &pb, fSmall);
  1567. }
  1568. }
  1569. //start sequential search from here
  1570. RonM_ASSERT(pb >= pldb->ab);
  1571. PBYTE pbLimit = PBYTE(pldb) + m_dbh.cbDirectoryBlock - pldb->nh.cbSlack;
  1572. for ( ; pb < pbLimit; )
  1573. {
  1574. cSeqSearch++;
  1575. PBYTE pbKey = pb;
  1576. HRESULT hr = DecodePathKey((const BYTE **) &pb, tsi.SI.awszStreamPath, (UINT *) &tsi.SI.cwcStreamPath);
  1577. if (!SUCCEEDED(hr))
  1578. return hr;
  1579. int icmp = wcsicmp_0x0409(ptsi->SI.awszStreamPath, tsi.SI.awszStreamPath);
  1580. if (icmp > 0)
  1581. {
  1582. pb = SkipKeyInfo(pb);
  1583. if (cEntries)
  1584. RonM_ASSERT(cSeqSearch <= (cChunkSize + 1));
  1585. continue;
  1586. }
  1587. if (icmp == 0)
  1588. {
  1589. // Found it!
  1590. PBYTE pbInfo = pb;
  1591. CopyMemory(ptsi->SI.awszStreamPath, tsi.SI.awszStreamPath,
  1592. sizeof(WCHAR) * (tsi.SI.cwcStreamPath + 1)
  1593. );
  1594. RonM_ASSERT(tsi.SI.cwcStreamPath == ptsi->SI.cwcStreamPath);
  1595. HRESULT hr = DecodeKeyInfo((const BYTE **) &pb, &tsi.SI);
  1596. if (!SUCCEEDED(hr)) return hr;
  1597. pCacheBlock->cbEntry = UINT(pb - pbKey);
  1598. pCacheBlock->cbKeyOffset =
  1599. tsi.cbEntryOffset = UINT(pbKey - pldb->ab);
  1600. tsi.cbEncoded = UINT(pb - pbKey);
  1601. tsi.iDirectoryBlock = pCacheBlock->iBlock;
  1602. *ptsi = tsi;
  1603. return S_OK;
  1604. }
  1605. // Found the place to insert key data
  1606. pCacheBlock->cbEntry = UINT(SkipKeyInfo(pb) - pbKey);
  1607. pb = pbKey;
  1608. break;
  1609. }
  1610. RonM_ASSERT(pb <= pbLimit);
  1611. pCacheBlock->cbEntry = 0;
  1612. pCacheBlock->cbKeyOffset =
  1613. ptsi->cbEntryOffset = UINT(pb - pldb->ab);
  1614. ptsi->cbEncoded = 0;
  1615. ptsi->iDirectoryBlock = pCacheBlock->iBlock;
  1616. return S_FALSE;
  1617. }
  1618. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::ForceClearDirty()
  1619. {
  1620. m_fHeaderIsDirty = FALSE;
  1621. for (PCacheBlock pCB = m_pCBLeastRecent; pCB; pCB = pCB->pCBNext)
  1622. {
  1623. UINT fFlags = pCB->fFlags;
  1624. if (pCB->fFlags & DirtyBlock)
  1625. {
  1626. pCB->fFlags = ~(DirtyBlock | LockedBlock | ReadingIn | WritingOut);
  1627. }
  1628. }
  1629. return NO_ERROR;
  1630. }
  1631. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::GetPathDB(IStreamITEx *pTempPDBStrm, BOOL fCompact)
  1632. {
  1633. if (fCompact)
  1634. return CompactPathDB(pTempPDBStrm);
  1635. //copy m_plbPathDatabase to temp file
  1636. STATSTG statstg;
  1637. HRESULT hr;
  1638. if (SUCCEEDED(hr = m_plbPathDatabase->Stat(&statstg, STATFLAG_NONAME)))
  1639. {
  1640. RonM_ASSERT(pTempPDBStrm != NULL);
  1641. BYTE lpBuf[2048];
  1642. CULINT ullOffset(0);
  1643. if (SUCCEEDED(hr = pTempPDBStrm->Seek(CLINT(0).Li(), STREAM_SEEK_SET, 0)))
  1644. {
  1645. ULONG cbToRead = sizeof(lpBuf);
  1646. ULONG cbWritten;
  1647. ULONG cbRead = cbToRead;
  1648. while (SUCCEEDED(hr) && (cbRead == cbToRead))
  1649. {
  1650. if (SUCCEEDED(hr = m_plbPathDatabase->ReadAt((ullOffset).Uli(), lpBuf, cbToRead, &cbRead)))
  1651. {
  1652. if (SUCCEEDED(hr = pTempPDBStrm->Write(lpBuf, cbRead, &cbWritten)))
  1653. {
  1654. RonM_ASSERT(cbRead == cbWritten);
  1655. ullOffset += cbWritten;
  1656. }//Write
  1657. }//ReadAt
  1658. }//while
  1659. }//seek to the beggining of the stream
  1660. }//get the size of copy operation
  1661. return hr;
  1662. }
  1663. UINT CPathManager1::CImpIPathManager::PredictNodeID(UINT iCurILev,
  1664. UINT iNodeNext,
  1665. SInternalNodeLev *rgINode,
  1666. PBYTE pbKey,
  1667. ULONG cbKey)
  1668. {
  1669. /*
  1670. This routine is called when we can't store an entry in a node within a tree level.
  1671. It's result is a prediction of the node ID which will be assigned to the next node
  1672. in that level. The way that we make that prediction is to traverse the current list
  1673. of higher level nodes to see how many of them will need to be written out before
  1674. the target node.
  1675. The key for the entry in question is denoted by pbKey and cbKey.
  1676. */
  1677. int cChunkSize = (1 << m_dbh.cEntryAccessShift) + 1;
  1678. int cSearchVEntries = 0;;
  1679. ULONG cbTotal;
  1680. PBYTE pbNext, pbLimit;
  1681. for (;iCurILev < 32; iCurILev++, iNodeNext++)
  1682. {
  1683. // At each level we look to see if we have room enough to store
  1684. // a reference to the new target node. If so, need go no further.
  1685. // Otherwise we will need to create a new node at this level and
  1686. // then look to see what will happen when that node is recorded
  1687. // in the next level up.
  1688. SInternalNodeLev *pLevelCurrent = rgINode + iCurILev;
  1689. PInternalNode pNodeCurrent = pLevelCurrent->pINode;
  1690. // When a level is empty we know that we will be able to create
  1691. // a node here and store the key reference because we were able
  1692. // to store it at the level below.
  1693. if (!pNodeCurrent) return iNodeNext;
  1694. if (pLevelCurrent->cEntries > 0) // BugBug: Is this always true?
  1695. {
  1696. UINT cChunkSize = (1 << m_dbh.cEntryAccessShift)+ 1;
  1697. if (pLevelCurrent->cEntries > cChunkSize)
  1698. {
  1699. cSearchVEntries = (pLevelCurrent->cEntries - cChunkSize) / cChunkSize;
  1700. cSearchVEntries += (((pLevelCurrent->cEntries - cChunkSize) % cChunkSize) ? 1 : 0);
  1701. }
  1702. }
  1703. UINT cbBinSearchEntries = (m_dbh.cbDirectoryBlock <= 0x10000)? sizeof(USHORT)
  1704. : sizeof(UINT);
  1705. pbLimit = PBYTE(pNodeCurrent) + m_dbh.cbDirectoryBlock
  1706. - cbBinSearchEntries * (cSearchVEntries + 1);
  1707. pbNext = PBYTE(pNodeCurrent) + m_dbh.cbDirectoryBlock - pNodeCurrent->nh.cbSlack;
  1708. BYTE abEncodedNodeId[5]; // Big enough to encode all 32-bit values.
  1709. PBYTE pb = abEncodedNodeId;
  1710. UINT cb = UINT(EncodeVL32(pb, iNodeNext) - pb);
  1711. cbTotal = (cb + cbKey);
  1712. if (pLevelCurrent->cEntries && !(pLevelCurrent->cEntries % cChunkSize))
  1713. cbTotal += cbBinSearchEntries;
  1714. if ((pbLimit - pbNext) >= cbTotal)
  1715. return iNodeNext;
  1716. }
  1717. RonM_ASSERT(FALSE); // We only get here when we've exhausted through all 32 levels.
  1718. return INVALID_INDEX;
  1719. }
  1720. HRESULT CPathManager1::CImpIPathManager::UpdateHigherLev(IStreamITEx *pTempPDBStrm,
  1721. UINT iCurILev,
  1722. UINT *piNodeNext,
  1723. SInternalNodeLev *rgINode,
  1724. PBYTE pbKey,
  1725. ULONG cbKey)
  1726. {
  1727. RonM_ASSERT(iCurILev < 32);
  1728. RonM_ASSERT((rgINode + iCurILev)->cEntries <= m_dbh.cbDirectoryBlock);
  1729. PBYTE pbNext, pbLimit;
  1730. HRESULT hr = NO_ERROR;
  1731. UINT cChunkSize = (1 << m_dbh.cEntryAccessShift) + 1;
  1732. int cSearchVEntries = 0;
  1733. BOOL fAddSearchV = FALSE;
  1734. UINT cEntries = 0;
  1735. ULONG cbTotal;
  1736. // static UINT cSearchV = 0; // BugBug: Doesn't work with more than one level of
  1737. // internal nodes!
  1738. SInternalNodeLev *pLevelCurrent = rgINode + iCurILev;
  1739. PInternalNode pNodeCurrent = pLevelCurrent->pINode;
  1740. BOOL fSmallBlk = m_dbh.cbDirectoryBlock <= 0x10000;
  1741. UINT cbBinsearchEntries = fSmallBlk? sizeof(USHORT) : sizeof(UINT);
  1742. if (!pNodeCurrent)
  1743. {
  1744. pNodeCurrent = (PInternalNode) New BYTE[m_dbh.cbDirectoryBlock];
  1745. if (pNodeCurrent)
  1746. pLevelCurrent->pINode = pNodeCurrent;
  1747. else
  1748. return STG_E_INSUFFICIENTMEMORY;
  1749. pNodeCurrent->nh.cbSlack = m_dbh.cbDirectoryBlock - sizeof(InternalNode);
  1750. pNodeCurrent->nh.uiMagic = uiMagicInternal;
  1751. pLevelCurrent->cEntries = 0;
  1752. if (fSmallBlk)
  1753. PUSHORT(PBYTE(pNodeCurrent) + m_dbh.cbDirectoryBlock)[-1] = 0;
  1754. else PUINT (PBYTE(pNodeCurrent) + m_dbh.cbDirectoryBlock)[-1] = 0;
  1755. }
  1756. if (pLevelCurrent->cEntries > 0)
  1757. {
  1758. cChunkSize = (1 << m_dbh.cEntryAccessShift)+ 1;
  1759. if (pLevelCurrent->cEntries > cChunkSize)
  1760. {
  1761. cSearchVEntries = (pLevelCurrent->cEntries - cChunkSize) / cChunkSize;
  1762. cSearchVEntries += (((pLevelCurrent->cEntries - cChunkSize) % cChunkSize) ? 1 : 0);
  1763. }
  1764. }
  1765. pbLimit = PBYTE(pNodeCurrent) + m_dbh.cbDirectoryBlock
  1766. - cbBinsearchEntries * (cSearchVEntries + 1);
  1767. pbNext = PBYTE(pNodeCurrent) + m_dbh.cbDirectoryBlock
  1768. - pNodeCurrent->nh.cbSlack;
  1769. BYTE abEncodedNodeId[5]; // Big enough to encode all 32-bit values.
  1770. ULONG nodeId = *piNodeNext - 1;
  1771. ULONG cb = UINT(EncodeVL32(abEncodedNodeId, nodeId) - abEncodedNodeId);
  1772. cbTotal = (cb + cbKey);
  1773. fAddSearchV = FALSE;
  1774. RonM_ASSERT(pbLimit >= pNodeCurrent->ab);
  1775. if (pLevelCurrent->cEntries && !(pLevelCurrent->cEntries % cChunkSize))
  1776. {
  1777. cbTotal += cbBinsearchEntries;
  1778. fAddSearchV = TRUE;
  1779. }
  1780. if ((pbLimit - pbNext) < cbTotal)
  1781. {
  1782. ULONG cbWritten;
  1783. fAddSearchV = FALSE;
  1784. //create new internal node at this level after writing the current one
  1785. if (fSmallBlk)
  1786. PUSHORT((PBYTE)pNodeCurrent + m_dbh.cbDirectoryBlock)[-1] = (USHORT)pLevelCurrent->cEntries;
  1787. else PUINT ((PBYTE)pNodeCurrent + m_dbh.cbDirectoryBlock)[-1] = pLevelCurrent->cEntries;
  1788. hr = pTempPDBStrm->Write((PBYTE)pNodeCurrent, m_dbh.cbDirectoryBlock, &cbWritten);
  1789. if (!SUCCEEDED(hr)) return hr;
  1790. if (cbWritten != m_dbh.cbDirectoryBlock)
  1791. return STG_E_WRITEFAULT;
  1792. // RonM_ASSERT(cSearchV == (pLevelCurrent->cEntries/cChunkSize
  1793. // - 1 + ((pLevelCurrent->cEntries % cChunkSize) ? 1 : 0)));
  1794. // cSearchV = 0;
  1795. pLevelCurrent->cEntries = 0;
  1796. pNodeCurrent->nh.cbSlack = m_dbh.cbDirectoryBlock - sizeof(InternalNode);
  1797. pbNext = (PBYTE)pNodeCurrent + sizeof(InternalNode);
  1798. pbLimit = (PBYTE)pNodeCurrent + m_dbh.cbDirectoryBlock - cbBinsearchEntries;
  1799. if (fSmallBlk)
  1800. PUSHORT((PBYTE)pNodeCurrent + m_dbh.cbDirectoryBlock)[-1] = 0;
  1801. else PUINT ((PBYTE)pNodeCurrent + m_dbh.cbDirectoryBlock)[-1] = 0;
  1802. (*piNodeNext)++;
  1803. RonM_ASSERT(pLevelCurrent->cEntries <= m_dbh.cbDirectoryBlock);
  1804. hr = UpdateHigherLev(pTempPDBStrm, iCurILev+1, piNodeNext, rgINode,
  1805. pNodeCurrent->ab, pLevelCurrent->cbFirstKey);
  1806. if (!SUCCEEDED(hr)) return hr;
  1807. RonM_ASSERT(pLevelCurrent->cEntries <= m_dbh.cbDirectoryBlock);
  1808. }//if current block full
  1809. pLevelCurrent->cEntries++;
  1810. if (pNodeCurrent->nh.cbSlack == (m_dbh.cbDirectoryBlock - sizeof(InternalNode)))
  1811. pLevelCurrent->cbFirstKey = cbKey;
  1812. if (fAddSearchV)
  1813. {
  1814. // cSearchV++;
  1815. if (fSmallBlk)
  1816. ((PUSHORT)pbLimit)[-1] = USHORT(pbNext - pNodeCurrent->ab);
  1817. else ((PUINT )pbLimit)[-1] = UINT(pbNext - pNodeCurrent->ab);
  1818. pbLimit -= cbBinsearchEntries;
  1819. }
  1820. memCpy(pbNext, pbKey, cbKey);
  1821. #if 0//test code
  1822. PathInfo PI;
  1823. PBYTE pKey = pbNext;
  1824. DecodePathKey((const BYTE **)&pKey, (unsigned short *)&PI.awszStreamPath, (unsigned int *)&PI.cwcStreamPath);
  1825. wprintf(L"Lev= %d Adding to internal node key = %s\n", iCurILev, PI.awszStreamPath);
  1826. #endif//end test code
  1827. pbNext += cbKey;
  1828. memCpy(pbNext, abEncodedNodeId, cb);
  1829. pbNext += cb;
  1830. pNodeCurrent->nh.cbSlack -= (cb + cbKey);
  1831. RonM_ASSERT(pNodeCurrent->nh.cbSlack < m_dbh.cbDirectoryBlock);
  1832. RonM_ASSERT(pLevelCurrent->cEntries <= m_dbh.cbDirectoryBlock);
  1833. return S_OK;
  1834. }
  1835. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CompactPathDB(IStreamITEx *pTempPDBStrm)
  1836. {
  1837. BYTE abEntry[CB_STREAM_INFO_MAX];
  1838. PBYTE pb;
  1839. UINT cb;
  1840. PBYTE pbLimit;
  1841. PBYTE pbNext;
  1842. ULONG cbWritten;
  1843. UINT iNextNodeId = 0; // Next available node block index.
  1844. ULONG cbPathKey, cbFirstKey;
  1845. int cnt = 0;
  1846. BOOL fAddSearchV = FALSE;
  1847. UINT cEntries = 0;
  1848. ULONG cbTotal;
  1849. DatabaseHeader dbh;
  1850. SInternalNodeLev rgINodeLev[32];
  1851. SInternalNodeLev *rgINode = rgINodeLev;
  1852. UINT cSearchV = 0;
  1853. UINT cChunkSize = (1 << m_dbh.cEntryAccessShift)+ 1;
  1854. PLeafNode plNode = NULL;
  1855. ULONG celtFetched = 1;
  1856. PathInfo pathInfo;
  1857. BOOL fSmallBlk = m_dbh.cbDirectoryBlock <= 0x10000;
  1858. UINT cbBinSearchEntries = fSmallBlk? sizeof(USHORT) : sizeof(UINT);
  1859. ZeroMemory(rgINodeLev, 32 * sizeof(SInternalNodeLev));
  1860. IITEnumSTATSTG *pEnumPathMgr = NULL;
  1861. HRESULT hr = EnumFromObject(NULL, L"//", 1, IID_IEnumSTATSTG, (PVOID *) &pEnumPathMgr);
  1862. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  1863. hr = pEnumPathMgr->GetFirstEntryInSeq(&pathInfo);
  1864. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  1865. plNode = (PLeafNode)New BYTE[m_dbh.cbDirectoryBlock];
  1866. if (!plNode)
  1867. {
  1868. hr = STG_E_INSUFFICIENTMEMORY;
  1869. goto exit_CompactPathDB;
  1870. }
  1871. //reserve space for the header
  1872. CopyMemory(&dbh, &m_dbh, sizeof(DatabaseHeader));
  1873. dbh.iRootDirectory = INVALID_INDEX;
  1874. dbh.iLeafLast = INVALID_INDEX;
  1875. dbh.iLeafFirst = INVALID_INDEX;
  1876. dbh.iBlockFirstFree = INVALID_INDEX;
  1877. dbh.cBlocks = 0;
  1878. hr = pTempPDBStrm->Write(&dbh, sizeof(m_dbh), &cbWritten);
  1879. RonM_ASSERT(cbWritten == sizeof(dbh));
  1880. plNode->nh.cbSlack = m_dbh.cbDirectoryBlock - sizeof(LeafNode);
  1881. plNode->nh.uiMagic = uiMagicLeaf;
  1882. plNode->lcl.iLeafPrevious = INVALID_INDEX;
  1883. plNode->lcl.iLeafNext = INVALID_INDEX;
  1884. plNode->lcl.iLeafSerial = 0;
  1885. pbLimit = (PBYTE)plNode + m_dbh.cbDirectoryBlock - cbBinSearchEntries;
  1886. if (fSmallBlk)
  1887. PUSHORT(PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = 0;
  1888. else PUINT (PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = 0;
  1889. pbNext = PBYTE(plNode) + sizeof(LeafNode);
  1890. cnt++;
  1891. cEntries++;
  1892. pb = EncodePathKey(abEntry, pathInfo.awszStreamPath, pathInfo.cwcStreamPath);
  1893. cbFirstKey = cbPathKey = ULONG(pb - abEntry);
  1894. pb = EncodeKeyInfo(pb, &pathInfo);
  1895. cb = UINT(pb - abEntry);
  1896. //pb = EncodePathInfo(abEntry, &pathInfo);
  1897. if ((pbLimit - pbNext) >= cb)
  1898. {
  1899. CopyMemory(pbNext, abEntry, cb);
  1900. #if 0
  1901. //************* test code ******************
  1902. PathInfo SI;
  1903. cnt = 0;
  1904. PBYTE pbKey = pbNext;
  1905. hr = DecodePathInfo((const BYTE **) &pbKey, &SI);
  1906. wprintf(L"key %d = %s\n", cnt, SI.awszStreamPath);
  1907. //************* end test code **************
  1908. #endif
  1909. pbNext += cb;
  1910. plNode->nh.cbSlack = UINT((PBYTE)plNode + m_dbh.cbDirectoryBlock - pbNext);
  1911. }
  1912. else
  1913. {
  1914. hr = E_FAIL;
  1915. goto exit_CompactPathDB;
  1916. }
  1917. for (;;)
  1918. {
  1919. celtFetched = 1;
  1920. hr = pEnumPathMgr->GetNextEntryInSeq(1, &pathInfo, &celtFetched);
  1921. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  1922. if (hr == S_FALSE) break;
  1923. RonM_ASSERT(celtFetched == 1);
  1924. cnt++;
  1925. pb = EncodePathKey(abEntry, pathInfo.awszStreamPath, pathInfo.cwcStreamPath);
  1926. cbPathKey = ULONG(pb - abEntry);
  1927. pb = EncodeKeyInfo(pb, &pathInfo);
  1928. cb = UINT(pb - abEntry);
  1929. cbTotal = cb;
  1930. fAddSearchV = FALSE;
  1931. if ((cEntries % cChunkSize) == 0)
  1932. {
  1933. cbTotal += cbBinSearchEntries;
  1934. fAddSearchV = TRUE;
  1935. }
  1936. if ((pbLimit - pbNext) < cbTotal)
  1937. {
  1938. fAddSearchV = FALSE;
  1939. UINT iCurrentLeafId = iNextNodeId++;
  1940. plNode->lcl.iLeafNext = PredictNodeID
  1941. (0, iNextNodeId, rgINode, plNode->ab, cbFirstKey);
  1942. RonM_ASSERT((pbLimit >= plNode->ab) && (pbLimit < ((PBYTE)plNode + m_dbh.cbDirectoryBlock)) );
  1943. if (fSmallBlk)
  1944. PUSHORT(PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = (USHORT)cEntries;
  1945. else PUINT (PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = cEntries;
  1946. hr = pTempPDBStrm->Write((PBYTE)plNode, m_dbh.cbDirectoryBlock, &cbWritten);
  1947. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  1948. RonM_ASSERT(cSearchV == (cEntries/cChunkSize - 1 + ((cEntries % cChunkSize) ? 1 : 0)));
  1949. cSearchV = 0;
  1950. RonM_ASSERT(cbWritten == m_dbh.cbDirectoryBlock);
  1951. plNode->lcl.iLeafPrevious = iCurrentLeafId;
  1952. plNode->lcl.iLeafSerial = 0;
  1953. plNode->lcl.iLeafNext = INVALID_INDEX;
  1954. pbNext = PBYTE(plNode) + sizeof(LeafNode);
  1955. plNode->nh.cbSlack = UINT((PBYTE)plNode + m_dbh.cbDirectoryBlock - pbNext);
  1956. cEntries = 0;
  1957. pbLimit = (PBYTE)plNode + m_dbh.cbDirectoryBlock - cbBinSearchEntries;
  1958. if (fSmallBlk)
  1959. PUSHORT(PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = 0;
  1960. else PUINT (PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = 0;
  1961. RonM_ASSERT((pbLimit >= plNode->ab) && (pbLimit < ((PBYTE)plNode + m_dbh.cbDirectoryBlock)) );
  1962. hr = UpdateHigherLev(pTempPDBStrm, 0, &iNextNodeId, rgINode, plNode->ab, cbFirstKey);
  1963. RonM_ASSERT((pbLimit >= plNode->ab) && (pbLimit < ((PBYTE)plNode + m_dbh.cbDirectoryBlock)) );
  1964. }//if current block full
  1965. cEntries++;
  1966. if (pbNext == (PBYTE(plNode) + sizeof(LeafNode)))
  1967. cbFirstKey = cbPathKey;
  1968. if (fAddSearchV)
  1969. {
  1970. cSearchV++;
  1971. if (fSmallBlk)
  1972. ((PUSHORT)pbLimit)[-1] = USHORT(pbNext - plNode->ab);
  1973. else ((PUINT )pbLimit)[-1] = UINT(pbNext - plNode->ab);
  1974. pbLimit -= cbBinSearchEntries;
  1975. }
  1976. RonM_ASSERT((pbLimit >= plNode->ab) && (pbLimit < ((PBYTE)plNode + m_dbh.cbDirectoryBlock)) );
  1977. memCpy(pbNext, abEntry, cb);
  1978. #if 0
  1979. //************* test code ******************
  1980. PathInfo SI;
  1981. cnt++;
  1982. PBYTE pbKey = pbNext;
  1983. hr = DecodePathInfo((const BYTE **) &pbKey, &SI);
  1984. wprintf(L"key %d = %s\n", cnt, SI.awszStreamPath);
  1985. //************* end test code **************
  1986. #endif
  1987. pbNext += cb;
  1988. plNode->nh.cbSlack -= cb;
  1989. }//while
  1990. //write last leaf node
  1991. if (pbNext == (PBYTE(plNode) + sizeof(LeafNode)))
  1992. {
  1993. // This condition should never happen because we always start with at least
  1994. // an entry for the path "/", and we never start a new leaf unless we have
  1995. // a path which would not fit in the previous leaf node.
  1996. RonM_ASSERT(FALSE);
  1997. hr = E_FAIL;
  1998. goto exit_CompactPathDB;
  1999. }
  2000. RonM_ASSERT(plNode->lcl.iLeafNext == INVALID_INDEX);
  2001. if (fSmallBlk)
  2002. PUSHORT(PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = (USHORT)cEntries;
  2003. else PUINT (PBYTE(plNode) + m_dbh.cbDirectoryBlock)[-1] = cEntries;
  2004. hr = pTempPDBStrm->Write((PBYTE)plNode, m_dbh.cbDirectoryBlock, &cbWritten);
  2005. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  2006. if (cbWritten != m_dbh.cbDirectoryBlock)
  2007. {
  2008. hr = STG_E_WRITEFAULT;
  2009. goto exit_CompactPathDB;
  2010. }
  2011. RonM_ASSERT(cbWritten == m_dbh.cbDirectoryBlock);
  2012. RonM_ASSERT(cSearchV == (cEntries/cChunkSize - 1 + ((cEntries % cChunkSize) ? 1 : 0)));
  2013. dbh.iLeafLast = iNextNodeId++; // To account for the last leaf.
  2014. if (plNode->lcl.iLeafPrevious == INVALID_INDEX) // Do we have any internal nodes?
  2015. {
  2016. dbh.cDirectoryLevels = 1;
  2017. dbh.cBlocks = 1;
  2018. RonM_ASSERT(dbh.iRootDirectory == INVALID_INDEX);
  2019. }
  2020. else
  2021. {
  2022. hr = UpdateHigherLev(pTempPDBStrm, 0, &iNextNodeId, rgINode, plNode->ab, cbFirstKey);
  2023. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  2024. //write all unwritten internal nodes
  2025. SInternalNodeLev *ppINode = &rgINodeLev[0];
  2026. int iLev = 0;
  2027. for (; ppINode->pINode != NULL; ppINode++, iLev++)
  2028. {
  2029. RonM_ASSERT((ppINode->pINode)->nh.cbSlack < (m_dbh.cbDirectoryBlock - sizeof(InternalNode)));
  2030. // The above assertion is true because we only create a node level when we
  2031. // need to put a path entry into it.
  2032. if (fSmallBlk)
  2033. PUSHORT(PBYTE(ppINode->pINode) + m_dbh.cbDirectoryBlock)[-1] = USHORT(ppINode->cEntries);
  2034. else PUINT (PBYTE(ppINode->pINode) + m_dbh.cbDirectoryBlock)[-1] = ppINode->cEntries;
  2035. hr = pTempPDBStrm->Write((PBYTE)(ppINode->pINode),
  2036. m_dbh.cbDirectoryBlock,
  2037. &cbWritten
  2038. );
  2039. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  2040. if (cbWritten != m_dbh.cbDirectoryBlock)
  2041. {
  2042. hr = STG_E_WRITEFAULT;
  2043. goto exit_CompactPathDB;
  2044. }
  2045. //printf("Last most I nodeid = %d cEntries = %d\n", iNextNodeId, ppINode->cEntries);
  2046. iNextNodeId++; // To account for this internal node
  2047. if (rgINodeLev[iLev + 1].pINode == NULL)
  2048. dbh.iRootDirectory = iNextNodeId - 1;
  2049. else
  2050. hr = UpdateHigherLev(pTempPDBStrm, iLev+1, &iNextNodeId, rgINode,
  2051. ppINode->pINode->ab, ppINode->cbFirstKey
  2052. );
  2053. delete ppINode->pINode;
  2054. ppINode->pINode = NULL;
  2055. }
  2056. dbh.cDirectoryLevels = iLev + 1;
  2057. dbh.cBlocks = iNextNodeId;
  2058. }
  2059. hr = pTempPDBStrm->Seek(CLINT(0).Li(), STREAM_SEEK_SET, 0);
  2060. if (!SUCCEEDED(hr)) goto exit_CompactPathDB;
  2061. //update the header again with correct last leaf node
  2062. dbh.iLeafFirst = 0;
  2063. RonM_ASSERT(dbh.iLeafLast != INVALID_INDEX);
  2064. RonM_ASSERT(dbh.iBlockFirstFree == INVALID_INDEX);
  2065. RonM_ASSERT( (dbh.cDirectoryLevels == 1 && dbh.cBlocks == 1
  2066. && dbh.iLeafLast == 0
  2067. && dbh.iRootDirectory == INVALID_INDEX
  2068. )
  2069. || (dbh.cDirectoryLevels > 1 && dbh.cBlocks > 2
  2070. && dbh.iLeafLast != 0
  2071. && dbh.iRootDirectory != INVALID_INDEX
  2072. )
  2073. );
  2074. hr = pTempPDBStrm->Write(&dbh, sizeof(dbh), &cbWritten);
  2075. if (SUCCEEDED(hr) && cbWritten != sizeof(dbh))
  2076. hr = STG_E_WRITEFAULT;
  2077. exit_CompactPathDB:
  2078. for (UINT iLevel = 32; iLevel--; )
  2079. {
  2080. PInternalNode pNode = rgINodeLev[iLevel].pINode;
  2081. RonM_ASSERT(!SUCCEEDED(hr) || !pNode); // Should not have any left over nodes
  2082. // if everything work correctly.
  2083. if (pNode) { delete pNode; rgINodeLev[iLevel].pINode = NULL; }
  2084. }
  2085. if (plNode ) { delete plNode; plNode = NULL; }
  2086. if (pEnumPathMgr) pEnumPathMgr->Release();
  2087. return hr;
  2088. }
  2089. HRESULT CPathManager1::CImpIPathManager::CEnumPathMgr1::NewPathEnumeratorObject
  2090. (IUnknown *punkOuter, CImpIPathManager *pPM,
  2091. const WCHAR *pwszPathPrefix,
  2092. UINT cwcPathPrefix,
  2093. REFIID riid,
  2094. PVOID *ppv
  2095. )
  2096. { if (punkOuter && riid != IID_IUnknown)
  2097. return CLASS_E_NOAGGREGATION;
  2098. CEnumPathMgr1 *pEPM1 = New CEnumPathMgr1(punkOuter);
  2099. if (!pEPM1)
  2100. {
  2101. pPM->Release();
  2102. return STG_E_INSUFFICIENTMEMORY;
  2103. }
  2104. HRESULT hr = pEPM1->m_ImpEnumSTATSTG.InitPathEnumerator
  2105. (pPM, pwszPathPrefix, cwcPathPrefix);
  2106. if (hr == S_OK)
  2107. hr = pEPM1->QueryInterface(riid, ppv);
  2108. if (hr != S_OK)
  2109. delete pEPM1;
  2110. return hr;
  2111. }
  2112. HRESULT CPathManager1::CImpIPathManager::CEnumPathMgr1::NewPathEnumerator
  2113. (IUnknown *punkOuter, CImpIPathManager *pPM,
  2114. const WCHAR *pwszPathPrefix,
  2115. UINT cwcPathPrefix,
  2116. IEnumSTATSTG **ppEnumSTATSTG
  2117. )
  2118. {
  2119. CEnumPathMgr1 *pEPM1 = New CEnumPathMgr1(punkOuter);
  2120. return FinishSetup(pEPM1? pEPM1->m_ImpEnumSTATSTG.InitPathEnumerator
  2121. (pPM, pwszPathPrefix, cwcPathPrefix)
  2122. : STG_E_INSUFFICIENTMEMORY,
  2123. pEPM1, IID_IEnumSTATSTG, (PPVOID) ppEnumSTATSTG
  2124. );
  2125. }
  2126. CPathManager1::CImpIPathManager::CEnumPathMgr1::CImpIEnumSTATSTG::CImpIEnumSTATSTG
  2127. (CITUnknown *pBackObj, IUnknown *punkOuter) : IITEnumSTATSTG(pBackObj, punkOuter)
  2128. {
  2129. m_pPM = NULL;
  2130. m_iLeafBlock = INVALID_INDEX;
  2131. m_cbOffsetLastEntry = 0;
  2132. m_cbLastEntry = 0;
  2133. m_iSerialNode = 0;
  2134. m_iSerialDatabase = 0;
  2135. m_cwcPathPrefix = 0;
  2136. m_pwszPathPrefix[0] = 0;
  2137. }
  2138. CPathManager1::CImpIPathManager::CEnumPathMgr1::CImpIEnumSTATSTG::~CImpIEnumSTATSTG()
  2139. {
  2140. if (m_pPM)
  2141. m_pPM->Release();
  2142. }
  2143. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2144. ::CImpIEnumSTATSTG::InitPathEnumerator
  2145. (CImpIPathManager *pPM,
  2146. const WCHAR *pwszPathPrefix,
  2147. UINT cwcPathPrefix
  2148. )
  2149. {
  2150. m_pPM = pPM;
  2151. if (cwcPathPrefix >= MAX_PATH)
  2152. return STG_E_INVALIDPARAMETER;
  2153. m_cwcPathPrefix = cwcPathPrefix;
  2154. CopyMemory(m_pwszPathPrefix, pwszPathPrefix, sizeof(WCHAR) * cwcPathPrefix);
  2155. m_pwszPathPrefix[cwcPathPrefix] = 0;
  2156. return NO_ERROR;
  2157. }
  2158. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2159. ::CImpIEnumSTATSTG::FindEntry()
  2160. {
  2161. HRESULT hr = NO_ERROR;
  2162. TaggedPathInfo tsi;
  2163. tsi.SI = m_SI;
  2164. PCacheBlock *papCBSet
  2165. = (PCacheBlock *) _alloca(sizeof(PCacheBlock) * m_pPM->m_dbh.cDirectoryLevels);
  2166. hr = m_pPM->FindKeyAndLockBlockSet(&tsi, papCBSet, 0);
  2167. m_pPM->ClearLockFlags(papCBSet);
  2168. PCacheBlock pCBLeaf = papCBSet[0];
  2169. if (SUCCEEDED(hr))
  2170. {
  2171. if (hr == S_FALSE)
  2172. pCBLeaf->cbEntry = 0;
  2173. m_SI = tsi.SI;
  2174. m_iLeafBlock = pCBLeaf->iBlock;
  2175. m_cbOffsetLastEntry = pCBLeaf->cbKeyOffset;
  2176. m_cbLastEntry = pCBLeaf->cbEntry;
  2177. m_iSerialNode = pCBLeaf->ldb.lcl.iLeafSerial;
  2178. m_iSerialDatabase = m_pPM->m_PathSetSerial;
  2179. }
  2180. return hr;
  2181. }
  2182. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2183. ::CImpIEnumSTATSTG::GetNextEntryInSeq
  2184. (ULONG celt, PathInfo *rgelt, ULONG *pceltFetched)
  2185. {
  2186. ULONG celtFetched = 0;
  2187. HRESULT hr = NO_ERROR;
  2188. CSyncWith sw(m_pPM->m_cs);
  2189. //readjust enumerator index in case last entry it has seen has
  2190. // changed.
  2191. PCacheBlock pCBLeaf = NULL;
  2192. if (SUCCEEDED(hr = m_pPM->FindCacheBlock(&pCBLeaf, m_iLeafBlock)))
  2193. {
  2194. PBYTE pbKey = pCBLeaf->ldb.ab + m_cbOffsetLastEntry;
  2195. PathInfo SI;
  2196. if (SUCCEEDED(hr = m_pPM->DecodePathInfo((const BYTE **) &pbKey, &SI)))
  2197. m_cbLastEntry = UINT(pbKey - (pCBLeaf->ldb.ab + m_cbOffsetLastEntry));
  2198. }
  2199. for (; celt--; rgelt++)
  2200. {
  2201. hr = NextEntry();
  2202. if (hr == S_FALSE || !SUCCEEDED(hr))
  2203. break;
  2204. memCpy(rgelt, &m_SI, sizeof(PathInfo));
  2205. celtFetched++;
  2206. }
  2207. if (pceltFetched)
  2208. *pceltFetched = celtFetched;
  2209. return hr;
  2210. }
  2211. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2212. ::CImpIEnumSTATSTG::GetFirstEntryInSeq
  2213. (PathInfo *rgelt)
  2214. {
  2215. HRESULT hr = NO_ERROR;
  2216. CSyncWith sw(m_pPM->m_cs);
  2217. PCacheBlock pCBLeaf = NULL;
  2218. if (!SUCCEEDED(hr = m_pPM->FindCacheBlock(&pCBLeaf, m_pPM->m_dbh.iLeafFirst)))
  2219. return hr;
  2220. PBYTE pbEntry = pCBLeaf->ldb.ab;
  2221. PathInfo SI;
  2222. PBYTE pbKey = pbEntry;
  2223. hr = m_pPM->DecodePathInfo((const BYTE **) &pbEntry, &SI);
  2224. if (SUCCEEDED(hr))
  2225. {
  2226. m_SI = SI;
  2227. m_cbOffsetLastEntry = UINT(pbKey - pCBLeaf->ldb.ab);
  2228. m_cbLastEntry = UINT(pbEntry - pbKey);
  2229. m_iLeafBlock = m_pPM->m_dbh.iLeafFirst;
  2230. m_iSerialNode = pCBLeaf->ldb.lcl.iLeafSerial;
  2231. memCpy(rgelt, &m_SI, sizeof(PathInfo));
  2232. }
  2233. return hr;
  2234. }
  2235. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2236. ::CImpIEnumSTATSTG::NextEntry()
  2237. {
  2238. if (m_iSerialDatabase == 0) // Initial call?
  2239. {
  2240. CopyMemory(m_SI.awszStreamPath, m_pwszPathPrefix,
  2241. sizeof(WCHAR) * (m_cwcPathPrefix + 1)
  2242. );
  2243. m_SI.cwcStreamPath = m_cwcPathPrefix;
  2244. HRESULT hr = FindEntry();
  2245. if (!SUCCEEDED(hr))
  2246. return hr;
  2247. RonM_ASSERT(m_cwcPathPrefix > 0);
  2248. // If the prefix ends with '/', we're enumerating a storage
  2249. // and should skip over any exact match. Otherwise we don't skip
  2250. // anything. This behavior is necessary for IStorage::DestroyElement
  2251. // to work correctly.
  2252. if (m_pwszPathPrefix[m_cwcPathPrefix-1] != L'/')
  2253. m_cbLastEntry = 0;
  2254. }
  2255. if (m_iSerialDatabase != m_pPM->m_PathSetSerial)
  2256. {
  2257. // Database has deleted a leaf node. So m_iLeafBlock may be invalid.
  2258. HRESULT hr = FindEntry();
  2259. }
  2260. PCacheBlock pCBLeaf = NULL;
  2261. HRESULT hr = m_pPM->FindCacheBlock(&pCBLeaf, m_iLeafBlock);
  2262. if (!SUCCEEDED(hr))
  2263. return hr;
  2264. if (m_iSerialNode != pCBLeaf->ldb.lcl.iLeafSerial)
  2265. {
  2266. // Node content has changed in a way that invalidated either
  2267. // m_cbOffsetLastEntry or m_cbLastEntry.
  2268. hr = FindEntry();
  2269. if (!SUCCEEDED(hr))
  2270. return hr;
  2271. }
  2272. RonM_ASSERT(m_pPM->m_PathSetSerial == m_iSerialDatabase);
  2273. UINT iLeafBlock = m_iLeafBlock;
  2274. hr = m_pPM->FindCacheBlock(&pCBLeaf, m_iLeafBlock);
  2275. if (!SUCCEEDED(hr))
  2276. return hr;
  2277. RonM_ASSERT(pCBLeaf->ldb.nh.uiMagic == uiMagicLeaf);
  2278. RonM_ASSERT(pCBLeaf->ldb.lcl.iLeafSerial == m_iSerialNode);
  2279. PBYTE pbLimit = PBYTE(&pCBLeaf->ldb) + m_pPM->m_dbh.cbDirectoryBlock
  2280. - pCBLeaf->ldb.nh.cbSlack;
  2281. PBYTE pbEntry = pCBLeaf->ldb.ab + m_cbOffsetLastEntry + m_cbLastEntry;
  2282. RonM_ASSERT(pbEntry <= pbLimit);
  2283. if (pbEntry == pbLimit) // Are we at the end of this leaf node?
  2284. {
  2285. UINT iNodeNext = pCBLeaf->ldb.lcl.iLeafNext;
  2286. if (m_pPM->ValidBlockIndex(iNodeNext))
  2287. {
  2288. HRESULT hr = m_pPM->FindCacheBlock(&pCBLeaf, iNodeNext);
  2289. if (!SUCCEEDED(hr))
  2290. return hr;
  2291. RonM_ASSERT(pCBLeaf->ldb.nh.uiMagic == uiMagicLeaf);
  2292. // Note that we defer changing m_iLeafBlock until the decode stream operation
  2293. // succeeds. That is, we don't change our state if we fail anywhere.
  2294. iLeafBlock = iNodeNext;
  2295. pbLimit = PBYTE(&pCBLeaf->ldb) + m_pPM->m_dbh.cbDirectoryBlock
  2296. - pCBLeaf->ldb.nh.cbSlack;
  2297. pbEntry = pCBLeaf->ldb.ab;
  2298. RonM_ASSERT(pbEntry < pbLimit); // We don't keep empty leaf nodes around.
  2299. }
  2300. else return S_FALSE; // We've gone through all the leaf entries.
  2301. }
  2302. PBYTE pbKey = pbEntry;
  2303. PathInfo SI;
  2304. hr = m_pPM->DecodePathInfo((const BYTE **) &pbEntry, &SI);
  2305. if (SUCCEEDED(hr))
  2306. {
  2307. m_SI = SI;
  2308. m_cbOffsetLastEntry = UINT(pbKey - pCBLeaf->ldb.ab);
  2309. m_cbLastEntry = UINT(pbEntry - pbKey);
  2310. m_iLeafBlock = iLeafBlock;
  2311. m_iSerialNode = pCBLeaf->ldb.lcl.iLeafSerial;
  2312. }
  2313. return hr;
  2314. }
  2315. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2316. ::CImpIEnumSTATSTG::Next
  2317. (ULONG celt, STATSTG __RPC_FAR *rgelt,
  2318. ULONG __RPC_FAR *pceltFetched
  2319. )
  2320. {
  2321. ULONG celtFetched = 0;
  2322. HRESULT hr = NO_ERROR;
  2323. CSyncWith sw(m_pPM->m_cs);
  2324. for (; celt--; rgelt++)
  2325. {
  2326. hr = NextEntry();
  2327. if (hr == S_FALSE || !SUCCEEDED(hr))
  2328. break;
  2329. rgelt->pwcsName= PWCHAR(OLEHeap()->Alloc((m_SI.cwcStreamPath+1) * sizeof(WCHAR)));
  2330. if (!(rgelt->pwcsName))
  2331. {
  2332. hr = STG_E_INSUFFICIENTMEMORY;
  2333. break;
  2334. }
  2335. CopyMemory(rgelt->pwcsName, m_SI.awszStreamPath,
  2336. sizeof(WCHAR) * (m_SI.cwcStreamPath+1)
  2337. );
  2338. if (m_SI.awszStreamPath[m_SI.cwcStreamPath - 1] == L'/') // Is this a storage?
  2339. {
  2340. rgelt->type = STGTY_STORAGE;
  2341. ULARGE_INTEGER *puli = (ULARGE_INTEGER *) &(rgelt->clsid);
  2342. puli[0] = m_SI.ullcbOffset.Uli();
  2343. puli[1] = m_SI.ullcbData .Uli();
  2344. rgelt->grfStateBits = m_SI.uStateBits;
  2345. rgelt->grfLocksSupported = 0; // We don't support locking with storages.
  2346. }
  2347. else
  2348. {
  2349. rgelt->type = STGTY_STREAM;
  2350. rgelt->cbSize = m_SI.ullcbData.Uli();
  2351. rgelt->grfStateBits = 0; // Not meaningful for streams
  2352. rgelt->grfLocksSupported = LOCK_EXCLUSIVE;
  2353. }
  2354. rgelt->grfMode = 0; // Not meaningful in an enumeration result.
  2355. rgelt->reserved = 0; // All reserved fields must be zero.
  2356. #if 0
  2357. rgelt->mtime = m_pPM->m_ftLastModified; // Need to add these member
  2358. rgelt->ctime = m_pPM->m_ftCreation; // variables to path manager!!!
  2359. rgelt->atime = m_pPM->m_ftLastAccess;
  2360. #else // 0
  2361. rgelt->mtime.dwLowDateTime = 0;
  2362. rgelt->mtime.dwHighDateTime = 0;
  2363. rgelt->ctime.dwLowDateTime = 0;
  2364. rgelt->ctime.dwHighDateTime = 0;
  2365. rgelt->atime.dwLowDateTime = 0;
  2366. rgelt->atime.dwHighDateTime = 0;
  2367. #endif // 0
  2368. celtFetched++;
  2369. }
  2370. if (pceltFetched)
  2371. *pceltFetched = celtFetched;
  2372. return hr;
  2373. }
  2374. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2375. ::CImpIEnumSTATSTG::Skip(ULONG celt)
  2376. {
  2377. HRESULT hr = NO_ERROR;
  2378. CSyncWith sw(m_pPM->m_cs);
  2379. for (; celt--; )
  2380. {
  2381. hr= NextEntry();
  2382. if (hr == S_FALSE || !SUCCEEDED(hr))
  2383. break;
  2384. }
  2385. return hr;
  2386. }
  2387. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2388. ::CImpIEnumSTATSTG::Reset(void)
  2389. {
  2390. CSyncWith sw(m_pPM->m_cs);
  2391. m_iSerialDatabase = 0;
  2392. return NO_ERROR;
  2393. }
  2394. HRESULT STDMETHODCALLTYPE CPathManager1::CImpIPathManager::CEnumPathMgr1
  2395. ::CImpIEnumSTATSTG::Clone
  2396. (IEnumSTATSTG __RPC_FAR *__RPC_FAR *ppenum)
  2397. {
  2398. CImpIEnumSTATSTG *pEnumSTATSTG = NULL;
  2399. CSyncWith sw(m_pPM->m_cs);
  2400. HRESULT hr = m_pPM->EnumFromObject(NULL, (const WCHAR *) m_pwszPathPrefix,
  2401. m_cwcPathPrefix,
  2402. IID_IEnumSTATSTG,
  2403. (PVOID *) &pEnumSTATSTG
  2404. );
  2405. if (!SUCCEEDED(hr))
  2406. {
  2407. return hr;
  2408. }
  2409. pEnumSTATSTG->m_SI = m_SI;
  2410. pEnumSTATSTG->m_iLeafBlock = m_iLeafBlock;
  2411. pEnumSTATSTG->m_cbOffsetLastEntry = m_cbOffsetLastEntry;
  2412. pEnumSTATSTG->m_cbLastEntry = m_cbLastEntry;
  2413. pEnumSTATSTG->m_iSerialNode = m_iSerialNode;
  2414. pEnumSTATSTG->m_iSerialDatabase = m_iSerialDatabase;
  2415. *ppenum = (IEnumSTATSTG *) pEnumSTATSTG;
  2416. return NO_ERROR;
  2417. }