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

2100 lines
57 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name: group.hxx
  4. Abstract:
  5. Manages cache group.
  6. Author:
  7. Danpo Zhang (DanpoZ) 02-08-98
  8. --*/
  9. #include <cache.hxx>
  10. GroupMgr::GroupMgr()
  11. {
  12. _pContainer = NULL;
  13. }
  14. GroupMgr::~GroupMgr()
  15. {
  16. if( _pContainer )
  17. {
  18. _pContainer->Release(FALSE);
  19. }
  20. }
  21. BOOL
  22. GroupMgr::Init(URL_CONTAINER* pCont)
  23. {
  24. BOOL fRet = TRUE;
  25. if( pCont )
  26. {
  27. _pContainer = pCont;
  28. _pContainer->AddRef();
  29. }
  30. else
  31. {
  32. SetLastError(ERROR_INTERNET_INTERNAL_ERROR);
  33. fRet = FALSE;
  34. }
  35. return fRet;
  36. }
  37. DWORD
  38. GroupMgr::CreateGroup(DWORD dwFlags, GROUPID* pGID)
  39. {
  40. INET_ASSERT(_pContainer);
  41. INET_ASSERT(pGID);
  42. BOOL fMustUnlock;
  43. DWORD dwError;
  44. GROUP_ENTRY* pGroupEntry = NULL;
  45. *pGID = 0;
  46. if( !_pContainer->LockContainer(&fMustUnlock) )
  47. {
  48. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  49. goto exit;
  50. }
  51. if( dwFlags & CACHEGROUP_FLAG_GIDONLY )
  52. {
  53. // only needs to return GID, no group needs to be created
  54. *pGID = ObtainNewGID();
  55. if( *pGID )
  56. dwError = ERROR_SUCCESS;
  57. else
  58. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  59. goto exit;
  60. }
  61. //
  62. // find the first available entry by using FindEntry()
  63. // passing gid = 0 means looking for empty entry
  64. // passing TRUE means create new page if no entry available
  65. //
  66. dwError = FindEntry(0, &pGroupEntry, TRUE );
  67. if( dwError != ERROR_SUCCESS )
  68. {
  69. goto exit;
  70. }
  71. // get a new gid
  72. *pGID = ObtainNewGID();
  73. if( *pGID )
  74. {
  75. // insert gid into the first available entry
  76. // set the sticky bit for non purgable group
  77. if( dwFlags & CACHEGROUP_FLAG_NONPURGEABLE )
  78. {
  79. *pGID = SetStickyBit(*pGID);
  80. }
  81. pGroupEntry->gid = *pGID;
  82. pGroupEntry->dwGroupFlags = dwFlags;
  83. dwError = ERROR_SUCCESS;
  84. }
  85. exit:
  86. if( fMustUnlock )
  87. {
  88. _pContainer->UnlockContainer();
  89. }
  90. return dwError;
  91. }
  92. DWORD
  93. GroupMgr::CreateDefaultGroups()
  94. {
  95. INET_ASSERT(_pContainer);
  96. BOOL fMustUnlock;
  97. DWORD dwError;
  98. GROUP_ENTRY* pGroupEntry = NULL;
  99. DWORD dwOffsetHead = 0;
  100. if( !_pContainer->LockContainer(&fMustUnlock) )
  101. {
  102. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  103. goto exit;
  104. }
  105. if( GetHeaderData( CACHE_HEADER_DATA_ROOTGROUP_OFFSET, &dwOffsetHead)
  106. && dwOffsetHead )
  107. {
  108. BOOL fBadHead = FALSE;
  109. // dwOffsetHead may point to a page which has not actually mapped in
  110. if( _pContainer->_UrlObjStorage->IsBadGroupOffset(dwOffsetHead) )
  111. {
  112. fBadHead = TRUE;
  113. }
  114. else
  115. {
  116. // if offset is too big, invalid
  117. FILEMAP_ENTRY* pFM = NULL;
  118. pFM = (FILEMAP_ENTRY*)
  119. (*_pContainer->_UrlObjStorage->GetHeapStart() +
  120. dwOffsetHead - sizeof(FILEMAP_ENTRY) );
  121. if(pFM->dwSig != SIG_ALLOC || !pFM->nBlocks )
  122. {
  123. fBadHead = TRUE;
  124. }
  125. }
  126. if( fBadHead )
  127. {
  128. // dwOffsetHead is invalid, reset!
  129. SetHeaderData(CACHE_HEADER_DATA_ROOTGROUP_OFFSET, 0);
  130. }
  131. }
  132. // if already created, just return success
  133. dwError = FindEntry(CACHEGROUP_ID_BUILTIN_STICKY, &pGroupEntry, FALSE);
  134. if( dwError == ERROR_SUCCESS )
  135. {
  136. goto exit;
  137. }
  138. //
  139. // not found, need to create new default groups
  140. //
  141. // find the first available entry by using FindEntry()
  142. // passing gid = 0 means looking for empty entry
  143. // passing TRUE means create new page if no entry available
  144. //
  145. dwError = FindEntry(0, &pGroupEntry, TRUE );
  146. if( dwError != ERROR_SUCCESS )
  147. {
  148. goto exit;
  149. }
  150. // set the sticky bit for non purgable group
  151. pGroupEntry->gid = CACHEGROUP_ID_BUILTIN_STICKY;
  152. pGroupEntry->dwGroupFlags = CACHEGROUP_FLAG_NONPURGEABLE;
  153. dwError = ERROR_SUCCESS;
  154. exit:
  155. if( fMustUnlock )
  156. {
  157. _pContainer->UnlockContainer();
  158. }
  159. return dwError;
  160. }
  161. DWORD
  162. GroupMgr::DeleteGroup(GROUPID gid, DWORD dwFlags)
  163. {
  164. INET_ASSERT(_pContainer);
  165. INET_ASSERT(gid);
  166. BOOL fMustUnlock;
  167. DWORD dwError;
  168. GROUP_ENTRY* pGroupEntry = NULL;
  169. GROUP_DATA_ENTRY* pData = NULL;
  170. DWORD hUrlFindHandle = 0;
  171. URL_FILEMAP_ENTRY* pUrlEntry = 0;
  172. DWORD dwFindFilter;
  173. HASH_ITEM* pItem = NULL;
  174. if( !_pContainer->LockContainer(&fMustUnlock) )
  175. {
  176. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  177. goto exit;
  178. }
  179. // find the first available entry
  180. dwError = FindEntry(gid, &pGroupEntry, FALSE);
  181. if( dwError != ERROR_SUCCESS )
  182. {
  183. goto exit;
  184. }
  185. // Look for all the url associated with this group
  186. // mark the groupid to 0
  187. hUrlFindHandle = _pContainer->GetInitialFindHandle();
  188. // set up find filter (do not care about cookie/history)
  189. dwFindFilter = URLCACHE_FIND_DEFAULT_FILTER
  190. & ~COOKIE_CACHE_ENTRY
  191. & ~URLHISTORY_CACHE_ENTRY;
  192. //
  193. // loop find all url belongs to this group
  194. // WARNING: this can be slow!
  195. //
  196. do
  197. {
  198. // next url in this group
  199. pUrlEntry = (URL_FILEMAP_ENTRY*)
  200. _pContainer->_UrlObjStorage->FindNextEntry(
  201. &hUrlFindHandle, dwFindFilter, gid);
  202. if( pUrlEntry )
  203. {
  204. INET_ASSERT(hUrlFindHandle);
  205. pItem = (HASH_ITEM*)(
  206. (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart() +
  207. hUrlFindHandle );
  208. if( pItem->HasMultiGroup() )
  209. {
  210. //
  211. // examing the group list and remove this group
  212. // from the list
  213. //
  214. DWORD dwNewHeaderOffset = pUrlEntry->dwGroupOffset;
  215. DWORD dwGroupEntryOffset = PtrDiff32(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
  216. //
  217. // find the to be deleted group entry in the list
  218. // of groups associated with this url, we need to
  219. // fix this by removing the to be dead group from
  220. // the list
  221. //
  222. DWORD Error = RemoveFromGroupList(
  223. pUrlEntry->dwGroupOffset,
  224. dwGroupEntryOffset,
  225. &dwNewHeaderOffset
  226. );
  227. //
  228. // found the entry and head offset has been changed
  229. //
  230. if( Error == ERROR_SUCCESS &&
  231. dwNewHeaderOffset != pUrlEntry->dwGroupOffset )
  232. {
  233. pUrlEntry->dwGroupOffset = dwNewHeaderOffset;
  234. //
  235. // no more group associated with this url
  236. // let's update the hash flags
  237. //
  238. if( !dwNewHeaderOffset )
  239. {
  240. pItem->ClearMultGroup();
  241. pItem->ClearGroup();
  242. }
  243. }
  244. // sticky bit
  245. if(!pUrlEntry->dwExemptDelta && IsStickyGroup(gid) )
  246. {
  247. //
  248. // unset sticky bit for this url IFF
  249. // 1) we are about to delete the last group of this url
  250. // 2) there is no more sticky group associated with this
  251. // url other than the to be deleted group
  252. //
  253. if( !pUrlEntry->dwGroupOffset ||
  254. ( pUrlEntry->dwGroupOffset &&
  255. NoMoreStickyEntryOnList(pUrlEntry->dwGroupOffset)))
  256. {
  257. _pContainer->UpdateStickness(
  258. pUrlEntry,
  259. URLCACHE_OP_UNSET_STICKY,
  260. hUrlFindHandle
  261. );
  262. }
  263. }
  264. }
  265. else
  266. {
  267. //
  268. // do not move the url entry now, so we just
  269. // need to reset the GroupOffset and re-exam the
  270. // stick bit
  271. //
  272. pUrlEntry->dwGroupOffset = 0;
  273. // sticky bit
  274. if(!pUrlEntry->dwExemptDelta && IsStickyGroup(gid) )
  275. {
  276. _pContainer->UpdateStickness(
  277. pUrlEntry,
  278. URLCACHE_OP_UNSET_STICKY,
  279. hUrlFindHandle
  280. );
  281. }
  282. }
  283. if( dwFlags & CACHEGROUP_FLAG_FLUSHURL_ONDELETE)
  284. {
  285. //
  286. // Container's DeleteUrlEntry method takes two
  287. // param, the url entry and hash item.
  288. // The hUrlFindHandle actually contains the
  289. // offset of the Hash Item, so we can get
  290. // the hash item from there.
  291. //
  292. // if this url belongs to other groups,
  293. // do not delete it
  294. if( !pItem->HasMultiGroup() )
  295. {
  296. _pContainer->DeleteUrlEntry(pUrlEntry, pItem, SIG_DELETE);
  297. }
  298. }
  299. } // find next url
  300. } while( pUrlEntry);
  301. // if data entry exists, we should free them as well
  302. if( pGroupEntry->dwGroupNameOffset )
  303. {
  304. dwError = FindDataEntry(pGroupEntry, &pData, FALSE);
  305. if( dwError == ERROR_SUCCESS )
  306. {
  307. FreeDataEntry(pData);
  308. }
  309. }
  310. memset(pGroupEntry, 0, sizeof(GROUP_ENTRY) );
  311. dwError = ERROR_SUCCESS;
  312. exit:
  313. if( fMustUnlock )
  314. {
  315. _pContainer->UnlockContainer();
  316. }
  317. return dwError;
  318. }
  319. DWORD
  320. GroupMgr::GetGroup(
  321. GROUPID gid,
  322. DWORD dwAttrib,
  323. INTERNET_CACHE_GROUP_INFOA* pOutGroupInfo,
  324. DWORD* pdwOutGroupInfoSize
  325. )
  326. {
  327. INET_ASSERT(_pContainer);
  328. INET_ASSERT(gid && pOutGroupInfo && pdwOutGroupInfoSize);
  329. BOOL fMustUnlock;
  330. DWORD dwError;
  331. GROUP_ENTRY* pGroupEntry = NULL;
  332. if( !_pContainer->LockContainer(&fMustUnlock) )
  333. {
  334. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  335. goto exit;
  336. }
  337. *pdwOutGroupInfoSize = 0;
  338. // find the entry
  339. dwError = FindEntry(gid, &pGroupEntry, FALSE);
  340. if( dwError != ERROR_SUCCESS )
  341. {
  342. goto exit;
  343. }
  344. // init out param
  345. memset(pOutGroupInfo, 0, sizeof(INTERNET_CACHE_GROUP_INFOA) );
  346. // copy over GROUP_ENTRY -> GROUP_INFO
  347. Translate(
  348. dwAttrib,
  349. pOutGroupInfo,
  350. pGroupEntry,
  351. GROUP_ENTRY_TO_INFO,
  352. pdwOutGroupInfoSize
  353. );
  354. dwError = ERROR_SUCCESS;
  355. exit:
  356. if( fMustUnlock )
  357. {
  358. _pContainer->UnlockContainer();
  359. }
  360. return dwError;
  361. }
  362. DWORD
  363. GroupMgr::SetGroup(
  364. GROUPID gid,
  365. DWORD dwAttrib,
  366. INTERNET_CACHE_GROUP_INFOA* pGroupInfo
  367. )
  368. {
  369. INET_ASSERT(_pContainer);
  370. INET_ASSERT(pGroupInfo && gid);
  371. BOOL fMustUnlock;
  372. DWORD dwError;
  373. GROUP_ENTRY* pGroupEntry;
  374. if( !_pContainer->LockContainer(&fMustUnlock) )
  375. {
  376. dwError = GetLastError();
  377. goto Cleanup;
  378. }
  379. pGroupEntry = NULL;
  380. INET_ASSERT(pGroupInfo);
  381. if( dwAttrib & ~(CACHEGROUP_READWRITE_MASK) )
  382. {
  383. //
  384. // read only fields are being requested
  385. //
  386. dwError = ERROR_INVALID_PARAMETER;
  387. goto Cleanup;
  388. }
  389. if( (dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME) &&
  390. (strlen(pGroupInfo->szGroupName) >= GROUPNAME_MAX_LENGTH ) )
  391. {
  392. //
  393. // name too long, exceed the buffer limit
  394. //
  395. dwError = ERROR_INVALID_PARAMETER;
  396. goto Cleanup;
  397. }
  398. // find the entry
  399. dwError = FindEntry(gid, &pGroupEntry, FALSE);
  400. if( dwError != ERROR_SUCCESS )
  401. {
  402. goto Cleanup;
  403. }
  404. // copy over GROUP_INFO -> GROUP_ENTRY
  405. Translate(
  406. dwAttrib,
  407. pGroupInfo,
  408. pGroupEntry,
  409. GROUP_INFO_TO_ENTRY,
  410. 0
  411. );
  412. dwError = ERROR_SUCCESS;
  413. Cleanup:
  414. if( fMustUnlock )
  415. {
  416. _pContainer->UnlockContainer();
  417. }
  418. return dwError;
  419. }
  420. DWORD
  421. GroupMgr::GetNextGroup(
  422. DWORD* pdwLastItemOffset,
  423. GROUPID* pOutGroupId
  424. )
  425. {
  426. INET_ASSERT(_pContainer);
  427. INET_ASSERT(pOutGroupId);
  428. BOOL fMustUnlock;
  429. BOOL fEndOfGroups;
  430. GROUP_ENTRY* pGroupEntry;
  431. DWORD dwNewOffset;
  432. DWORD dwError;
  433. if( !_pContainer->LockContainer(&fMustUnlock) )
  434. {
  435. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  436. fEndOfGroups = TRUE;
  437. goto Cleanup;
  438. }
  439. pGroupEntry = NULL;
  440. dwNewOffset = 0;
  441. fEndOfGroups = FALSE;
  442. if( *pdwLastItemOffset == 0 )
  443. {
  444. // get root
  445. dwError = FindRootEntry(&pGroupEntry, FALSE );
  446. if( dwError != ERROR_SUCCESS )
  447. {
  448. //
  449. // new find and we can not get the root entry
  450. // this means there are no group at all.
  451. //
  452. fEndOfGroups = TRUE;
  453. goto Cleanup;
  454. }
  455. } // IF: no previous offset, this is a new Find
  456. else if( *pdwLastItemOffset == OFFSET_NO_MORE_GROUP )
  457. {
  458. // this group of search has completed already
  459. fEndOfGroups = TRUE;
  460. dwError = ERROR_FILE_NOT_FOUND;
  461. goto Cleanup;
  462. } // ELSE IF: previous FindNext has already reached the end of the groups
  463. else
  464. {
  465. //
  466. // use the offset to jump to the last returned item's entry
  467. //
  468. pGroupEntry = (GROUP_ENTRY*)
  469. (*_pContainer->_UrlObjStorage->GetHeapStart() + *pdwLastItemOffset);
  470. //
  471. // one step forward
  472. //
  473. INET_ASSERT(pGroupEntry); // can't be null
  474. INET_ASSERT( !IsIndexToNewPage(pGroupEntry) ); // can't be index item
  475. pGroupEntry++;
  476. } // ELSE: walk to the item which has been returned by previous FindNext()
  477. // loop for next entry
  478. while(pGroupEntry)
  479. {
  480. //
  481. // if this entry is the last one of the page
  482. // it contains offset pointing to the next page
  483. //
  484. if( IsIndexToNewPage(pGroupEntry) )
  485. {
  486. //
  487. // BUGBUG
  488. // we currently use dwFlags to indicating if
  489. // this is pointing to the next offset
  490. //
  491. if( pGroupEntry->dwGroupFlags )
  492. {
  493. //
  494. // walk to next page
  495. //
  496. pGroupEntry = (GROUP_ENTRY*)
  497. ( *_pContainer->_UrlObjStorage->GetHeapStart()
  498. + pGroupEntry->dwGroupFlags );
  499. } // IF: index entry point to next page
  500. else
  501. {
  502. //
  503. // we are done
  504. //
  505. fEndOfGroups = TRUE;
  506. dwError = ERROR_FILE_NOT_FOUND;
  507. break;
  508. } // ELSE: index page contains nothing (this is the last page)
  509. } // special case: current entry is the index(point to next page)
  510. //
  511. // using gid to test if the entry is empty, if not,
  512. // walk to the next entry
  513. //
  514. if( !pGroupEntry->gid )
  515. {
  516. pGroupEntry++;
  517. }
  518. else
  519. {
  520. break;
  521. }
  522. } // while(pGroupEntry)
  523. Cleanup:
  524. // update LastItemOffset
  525. if (!fEndOfGroups
  526. && pGroupEntry)
  527. {
  528. LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
  529. dwNewOffset = PtrDiff32(pGroupEntry, lpbBase);
  530. *pdwLastItemOffset = dwNewOffset;
  531. // copy over GROUP_ENTRY -> GROUP_INFO
  532. *pOutGroupId = pGroupEntry->gid;
  533. dwError = ERROR_SUCCESS;
  534. } // IF: find the item
  535. else
  536. {
  537. *pdwLastItemOffset = OFFSET_NO_MORE_GROUP;
  538. dwError = ERROR_FILE_NOT_FOUND;
  539. } // ELSE: not find
  540. if( fMustUnlock )
  541. {
  542. _pContainer->UnlockContainer();
  543. }
  544. return dwError;
  545. }
  546. DWORD
  547. GroupMgr::FindRootEntry(
  548. GROUP_ENTRY** ppOut, // OUT: first empty entry
  549. BOOL fCreate // allocate new page if needed
  550. )
  551. {
  552. INET_ASSERT(ppOut);
  553. *ppOut = NULL;
  554. GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
  555. DWORD dwError;
  556. DWORD dwOffsetToRootEntry = 0;
  557. // get base offset
  558. if( GetHeaderData( CACHE_HEADER_DATA_ROOTGROUP_OFFSET, &dwOffsetToRootEntry))
  559. {
  560. if( !dwOffsetToRootEntry && fCreate )
  561. {
  562. dwError = CreateNewPage(&dwOffsetToRootEntry, TRUE);
  563. if( dwError != ERROR_SUCCESS)
  564. {
  565. goto Cleanup;
  566. }
  567. }
  568. else if( !dwOffsetToRootEntry && !fCreate )
  569. {
  570. //
  571. // there is no offset infomation on the mem file
  572. // however, the flag says do not create a new page
  573. // failure is the only option here
  574. //
  575. dwError = ERROR_FILE_NOT_FOUND;
  576. goto Cleanup;
  577. }
  578. } // IF: retrieve base offset
  579. else
  580. {
  581. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  582. goto Cleanup;
  583. } // ELSE: failed to get base offset
  584. //
  585. // At this point, we should either:
  586. // 1. retrieved valid dwOffsetToRootEntry or
  587. // 2. get the new dwOffsetToRootEntry via CreateNewPage() call
  588. //
  589. INET_ASSERT( dwOffsetToRootEntry );
  590. *ppOut = (GROUP_ENTRY*)
  591. ( *_pContainer->_UrlObjStorage->GetHeapStart() + dwOffsetToRootEntry);
  592. dwError = ERROR_SUCCESS;
  593. Cleanup:
  594. return dwError;
  595. }
  596. DWORD
  597. GroupMgr::FindEntry(
  598. GROUPID gid, // gid, 0 means find first empty seat
  599. GROUP_ENTRY** ppOut, // OUT: entry with gid specified
  600. BOOL fCreate // allocate new page if needed
  601. // (applied for searching empty seat only)
  602. )
  603. {
  604. INET_ASSERT(ppOut);
  605. // fCreate can only be associated with gid == 0
  606. INET_ASSERT( (fCreate && !gid ) || (!fCreate && gid ) );
  607. GROUP_ENTRY* pGroupEntry = NULL;
  608. DWORD dwError;
  609. // get Root Entry
  610. dwError = FindRootEntry(&pGroupEntry, fCreate);
  611. if( dwError != ERROR_SUCCESS )
  612. {
  613. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  614. goto Cleanup;
  615. } // failed to get the root entry
  616. INET_ASSERT(pGroupEntry); // pGroupEntry should be available now
  617. while(1)
  618. {
  619. // special case for end of this page
  620. if( IsIndexToNewPage(pGroupEntry) )
  621. {
  622. //
  623. // BUGBUG
  624. // we currently use the dwFlags to indicating
  625. // if this is pointing to the next offset
  626. //
  627. if( pGroupEntry->dwGroupFlags )
  628. {
  629. // walk to next page
  630. pGroupEntry = (GROUP_ENTRY*)
  631. ( *_pContainer->_UrlObjStorage->GetHeapStart()
  632. + pGroupEntry->dwGroupFlags );
  633. } // IF: index entry points to next page
  634. else if( fCreate)
  635. {
  636. //////////////////////////////////////////////////////////////////
  637. // BEGIN WARNING: The file might be grown and remapped, so all //
  638. // pointers into the file before this point may be invalidated. //
  639. //////////////////////////////////////////////////////////////////
  640. DWORD dwOffsetToFirstEntry = 0;
  641. LPBYTE lpbBase = NULL;
  642. // remember the old offset for pGroupEntry
  643. DWORD_PTR dwpEntryOffset = PtrDifference(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
  644. // create new page!
  645. dwError = CreateNewPage(&dwOffsetToFirstEntry, FALSE);
  646. if( dwError != ERROR_SUCCESS )
  647. {
  648. goto Cleanup;
  649. }
  650. // recalculate pGroupEntry using the offset remembered
  651. lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
  652. pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwpEntryOffset);
  653. //////////////////////////////////////////////////////////////////
  654. // END WARNING: The file might be grown and remapped, so all //
  655. // pointers into the file before this point may be invalidated. //
  656. //////////////////////////////////////////////////////////////////
  657. //
  658. // pGroupEntry currently is the index item, insert
  659. // the offset of the first item to the newly created page
  660. //
  661. pGroupEntry->dwGroupFlags = dwOffsetToFirstEntry;
  662. // walk to the new page
  663. pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwOffsetToFirstEntry);
  664. } // ELSE IF: index entry not point to new page, fCreate is
  665. // set, a new page is being created
  666. else
  667. {
  668. // this is the end of all groups, item still not found,
  669. dwError = ERROR_FILE_NOT_FOUND;
  670. break;
  671. } // ELSE: index entry not point to new page, fCreate not set
  672. } // IF: this entry is an index entry
  673. //
  674. // now pGroupEntry must point to a normal group entry
  675. //
  676. INET_ASSERT( !IsIndexToNewPage(pGroupEntry) );
  677. if( pGroupEntry->gid != gid )
  678. {
  679. // not found, walk to next entry
  680. pGroupEntry++;
  681. }
  682. else
  683. {
  684. // found entry
  685. dwError = ERROR_SUCCESS;
  686. break;
  687. }
  688. } // WHILE: (loop over all page)
  689. Cleanup:
  690. if( dwError == ERROR_SUCCESS )
  691. {
  692. *ppOut = pGroupEntry;
  693. }
  694. else
  695. {
  696. *ppOut = NULL;
  697. }
  698. return dwError;
  699. }
  700. DWORD
  701. GroupMgr::CreateNewPage(DWORD* dwOffsetToFirstEntry, BOOL fIsFirstPage)
  702. {
  703. DWORD dwError;
  704. GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
  705. DWORD cbSize = sizeof(GROUPS_ALLOC_FILEMAP_ENTRY);
  706. pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*)
  707. _pContainer->_UrlObjStorage->AllocateEntry(cbSize);
  708. if( pPage )
  709. {
  710. // clean up allocated page
  711. cbSize = PAGE_SIZE_FOR_GROUPS;
  712. memset(pPage->pGroupBlock, 0, cbSize );
  713. // calculate the group base offset
  714. LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
  715. *dwOffsetToFirstEntry = PtrDiff32(pPage->pGroupBlock, lpbBase);
  716. //
  717. // mark the last entry as index to next page
  718. // (gid == GID_INDEX_TO_NEXT_PAGE) is the mark,
  719. // the actual offset is stored at dwGroupFlags field
  720. //
  721. GROUP_ENTRY* pEnd = (GROUP_ENTRY*) pPage->pGroupBlock;
  722. pEnd = pEnd + (GROUPS_PER_PAGE - 1);
  723. pEnd->gid = GID_INDEX_TO_NEXT_PAGE;
  724. if( fIsFirstPage )
  725. {
  726. //
  727. // for first page, we would have to set the offset
  728. // back to the CacheHeader
  729. //
  730. if( !SetHeaderData(
  731. CACHE_HEADER_DATA_ROOTGROUP_OFFSET, *dwOffsetToFirstEntry))
  732. {
  733. // free allocated page
  734. _pContainer->_UrlObjStorage->FreeEntry(pPage);
  735. // set error and go
  736. *dwOffsetToFirstEntry = 0;
  737. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  738. goto Cleanup;
  739. } // IF: failed to set the offset
  740. }
  741. // return the offset to the first entry of the new page
  742. dwError = ERROR_SUCCESS;
  743. } // IF: Allocate new page succeed
  744. else
  745. {
  746. dwError = ERROR_NOT_ENOUGH_MEMORY;
  747. } // ELSE: failed to allocate new page
  748. Cleanup:
  749. return dwError;
  750. }
  751. GROUPID
  752. GroupMgr::ObtainNewGID()
  753. {
  754. SYSTEMTIME st;
  755. DWORD dwC[2] = {0, 0};
  756. GROUPID gid = 0;
  757. // get counter from index file
  758. if( GetHeaderData(CACHE_HEADER_DATA_GID_LOW, &dwC[0]) &&
  759. GetHeaderData(CACHE_HEADER_DATA_GID_HIGH, &dwC[1]) )
  760. {
  761. if( !dwC[0] && !dwC[1] )
  762. {
  763. // need to get the current system time
  764. GetSystemTime( &st );
  765. SystemTimeToFileTime(&st, (FILETIME*)dwC);
  766. } // IF: counter not initialized
  767. else
  768. {
  769. // increment
  770. if( dwC[0] != 0xffffffff )
  771. {
  772. dwC[0] ++;
  773. }
  774. else
  775. {
  776. dwC[0] = 0;
  777. dwC[1] ++;
  778. }
  779. } // ELSE: counter initialized
  780. // send data back to cache
  781. if( SetHeaderData(CACHE_HEADER_DATA_GID_LOW, dwC[0] ) &&
  782. SetHeaderData(CACHE_HEADER_DATA_GID_HIGH, dwC[1] ) )
  783. {
  784. //memcpy(&gid, dwC, sizeof(GROUPID) );
  785. gid = *((GROUPID *)dwC);
  786. }
  787. }
  788. // apply the mask to newly created gid
  789. // the first 4 bits are reserved (one bit is used for stickness)
  790. return (gid & GID_MASK);
  791. }
  792. BOOL
  793. GroupMgr::Translate(
  794. DWORD dwAttrib,
  795. INTERNET_CACHE_GROUP_INFOA* pGroupInfo,
  796. GROUP_ENTRY* pGroupEntry,
  797. DWORD dwFlag,
  798. DWORD* pdwSize
  799. )
  800. {
  801. INET_ASSERT(pGroupInfo && pGroupEntry);
  802. BOOL fRet = TRUE;
  803. GROUP_DATA_ENTRY* pData = NULL;
  804. DWORD dwError;
  805. if( dwFlag == GROUP_ENTRY_TO_INFO )
  806. {
  807. INET_ASSERT(pdwSize);
  808. // clear
  809. memset(pGroupInfo, 0, sizeof(INTERNET_CACHE_GROUP_INFOA) );
  810. *pdwSize = 0;
  811. // basic entries
  812. if( dwAttrib & CACHEGROUP_ATTRIBUTE_BASIC )
  813. {
  814. pGroupInfo->dwGroupSize = sizeof(INTERNET_CACHE_GROUP_INFOA);
  815. pGroupInfo->dwGroupFlags = pGroupEntry->dwGroupFlags;
  816. pGroupInfo->dwGroupType = pGroupEntry->dwGroupType;
  817. pGroupInfo->dwDiskUsage = (DWORD)(pGroupEntry->llDiskUsage / 1024);
  818. pGroupInfo->dwDiskQuota = pGroupEntry->dwDiskQuota;
  819. }
  820. // user friendly name
  821. if( ( (dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME) |
  822. (dwAttrib & CACHEGROUP_ATTRIBUTE_STORAGE ) ) &&
  823. pGroupEntry->dwGroupNameOffset )
  824. {
  825. dwError = FindDataEntry(pGroupEntry, &pData, FALSE);
  826. if( dwError != ERROR_SUCCESS )
  827. {
  828. fRet = FALSE;
  829. }
  830. else
  831. {
  832. DWORD dwLen = strlen(pData->szName) + 1;
  833. INET_ASSERT( dwLen > GROUPNAME_MAX_LENGTH );
  834. memcpy( pGroupInfo->szGroupName,
  835. pData->szName,
  836. dwLen );
  837. memcpy( pGroupInfo->dwOwnerStorage,
  838. pData->dwOwnerStorage,
  839. sizeof(DWORD) * GROUP_OWNER_STORAGE_SIZE );
  840. }
  841. }
  842. // set size
  843. *pdwSize = sizeof(INTERNET_CACHE_GROUP_INFOA);
  844. }
  845. else
  846. if( dwFlag == GROUP_INFO_TO_ENTRY )
  847. {
  848. // copy
  849. if( dwAttrib & CACHEGROUP_ATTRIBUTE_FLAG )
  850. {
  851. pGroupEntry->dwGroupFlags = pGroupInfo->dwGroupFlags;
  852. }
  853. if( dwAttrib & CACHEGROUP_ATTRIBUTE_TYPE )
  854. {
  855. pGroupEntry->dwGroupType = pGroupInfo->dwGroupType;
  856. }
  857. if( dwAttrib & CACHEGROUP_ATTRIBUTE_QUOTA )
  858. {
  859. pGroupEntry->dwDiskQuota = pGroupInfo->dwDiskQuota;
  860. }
  861. if( (dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME) |
  862. (dwAttrib & CACHEGROUP_ATTRIBUTE_STORAGE ) )
  863. {
  864. dwError = FindDataEntry(pGroupEntry, &pData, TRUE);
  865. if( dwError != ERROR_SUCCESS )
  866. {
  867. fRet = FALSE;
  868. }
  869. else
  870. {
  871. if( dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME )
  872. {
  873. DWORD dwLen = strlen(pGroupInfo->szGroupName) + 1;
  874. INET_ASSERT(dwLen > GROUPNAME_MAX_LENGTH);
  875. memcpy( pData->szName,
  876. pGroupInfo->szGroupName,
  877. dwLen );
  878. }
  879. if( dwAttrib & CACHEGROUP_ATTRIBUTE_STORAGE )
  880. {
  881. memcpy( pData->dwOwnerStorage,
  882. pGroupInfo->dwOwnerStorage,
  883. sizeof(DWORD) * GROUP_OWNER_STORAGE_SIZE );
  884. }
  885. // BUGBUG
  886. // if both fields are set to be empty, we should free
  887. // the allocated data itam
  888. }
  889. }
  890. }
  891. else
  892. {
  893. fRet = FALSE;
  894. }
  895. return fRet;
  896. }
  897. BOOL
  898. GroupMgr::IsPageEmpty(GROUP_ENTRY* pHead)
  899. {
  900. BOOL fRet = FALSE;
  901. GROUP_ENTRY* pGroupEntry = pHead;
  902. for( int i = 0; i < (GROUPS_PER_PAGE - 1); i ++)
  903. {
  904. if( pGroupEntry->gid )
  905. {
  906. break;
  907. }
  908. else
  909. {
  910. pGroupEntry++;
  911. }
  912. }
  913. // there is no item found on this page
  914. if( !pGroupEntry->gid && i == GROUPS_PER_PAGE - 1 )
  915. {
  916. fRet = TRUE;
  917. }
  918. return fRet;
  919. }
  920. BOOL
  921. GroupMgr::IsLastPage(GROUP_ENTRY* pHead)
  922. {
  923. BOOL fRet = FALSE;
  924. GROUP_ENTRY* pEnd = NULL;
  925. // jump to last item
  926. pEnd = pHead + GROUPS_PER_PAGE;
  927. //
  928. // the gid has to be marked as GID_INDEX_TO_NEXT_PAGE
  929. // for index entry, and if the dwGroupFlags is 0,
  930. // that means we are not pointing to any
  931. // other page, this is the last page indeed.
  932. //
  933. if( pEnd->gid == GID_INDEX_TO_NEXT_PAGE && !pEnd->dwGroupFlags )
  934. {
  935. fRet = TRUE;
  936. }
  937. return fRet;
  938. }
  939. BOOL
  940. GroupMgr::FreeEmptyPages(DWORD dwFlags)
  941. {
  942. INET_ASSERT(_pContainer);
  943. BOOL fMustUnlock;
  944. BOOL fRet = TRUE;
  945. GROUP_ENTRY* pHead = NULL;
  946. GROUP_ENTRY* pPrevHead = NULL;
  947. GROUP_ENTRY* pEnd = NULL;
  948. GROUP_ENTRY* pTobeDeleted = NULL;
  949. BOOL fFirstPage = TRUE;
  950. if( !_pContainer->LockContainer(&fMustUnlock) )
  951. {
  952. fRet = FALSE;
  953. goto Cleanup;
  954. }
  955. // BUGBUG FindRootEntry changed the return code, check for dwError
  956. if( FindRootEntry(&pHead, FALSE ) )
  957. {
  958. pPrevHead = pHead;
  959. while(pHead)
  960. {
  961. pTobeDeleted = NULL;
  962. if( IsPageEmpty(pHead) )
  963. {
  964. pTobeDeleted = pHead;
  965. //
  966. // find the offset of the next page
  967. // 0 which means the current page is the last one
  968. //
  969. DWORD dwOffsetNextPage = 0;
  970. pEnd = pHead + GROUPS_PER_PAGE;
  971. dwOffsetNextPage = pEnd->dwGroupFlags;
  972. //
  973. // if the first page is to be deleted, we have to
  974. // update the offset which points to the next page
  975. //
  976. if( fFirstPage)
  977. {
  978. if( !SetHeaderData(
  979. CACHE_HEADER_DATA_ROOTGROUP_OFFSET, dwOffsetNextPage))
  980. {
  981. fRet = FALSE;
  982. goto Cleanup;
  983. }
  984. }
  985. else
  986. {
  987. //
  988. // Link Prev page to Next page
  989. //
  990. GROUP_ENTRY* pPrevEnd = pPrevHead + GROUPS_PER_PAGE;
  991. pPrevEnd->dwGroupFlags = dwOffsetNextPage;
  992. }
  993. }
  994. //
  995. // update pHead make it point to the next page
  996. //
  997. if( !IsLastPage(pHead) )
  998. {
  999. // remember pPrev
  1000. pPrevHead = pHead;
  1001. // walk to next page
  1002. pEnd = pHead + GROUPS_PER_PAGE;
  1003. pHead = (GROUP_ENTRY*)
  1004. ( *_pContainer->_UrlObjStorage->GetHeapStart()
  1005. + pEnd->dwGroupFlags );
  1006. // not first page anymore
  1007. fFirstPage = FALSE;
  1008. }
  1009. else
  1010. {
  1011. // this is the last page
  1012. pHead = NULL;
  1013. }
  1014. //
  1015. // free the tobe deleted page
  1016. //
  1017. if( pTobeDeleted )
  1018. {
  1019. GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
  1020. pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*) ((LPBYTE)pTobeDeleted - sizeof(FILEMAP_ENTRY));
  1021. _pContainer->_UrlObjStorage->FreeEntry(pPage);
  1022. }
  1023. }
  1024. }
  1025. Cleanup:
  1026. if( fMustUnlock )
  1027. {
  1028. _pContainer->UnlockContainer();
  1029. }
  1030. return fRet;
  1031. }
  1032. DWORD
  1033. GroupMgr::FindDataEntry(
  1034. GROUP_ENTRY* pGroupEntry,
  1035. GROUP_DATA_ENTRY** pOutData,
  1036. BOOL fCreate
  1037. )
  1038. {
  1039. INET_ASSERT(_pContainer);
  1040. INET_ASSERT(pGroupEntry && pOutData );
  1041. *pOutData = NULL;
  1042. BOOL fMustUnlock;
  1043. DWORD dwError;
  1044. LPBYTE lpbBase = NULL;
  1045. if( !_pContainer->LockContainer(&fMustUnlock) )
  1046. {
  1047. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1048. goto exit;
  1049. }
  1050. if( pGroupEntry->dwGroupNameOffset )
  1051. {
  1052. lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
  1053. *pOutData = (GROUP_DATA_ENTRY*) (lpbBase + pGroupEntry->dwGroupNameOffset);
  1054. dwError = ERROR_SUCCESS;
  1055. }
  1056. else if( fCreate)
  1057. {
  1058. //////////////////////////////////////////////////////////////////
  1059. // BEGIN WARNING: The file might be grown and remapped, so all //
  1060. // pointers into the file before this point may be invalidated. //
  1061. //////////////////////////////////////////////////////////////////
  1062. // remember the old offset for pGroupEntry
  1063. DWORD_PTR dwpEntryOffset = PtrDifference(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
  1064. // create new data entry
  1065. *pOutData = GetHeadDataEntry(TRUE);
  1066. if( *pOutData )
  1067. {
  1068. //
  1069. // re-calc pGroupEntry
  1070. //
  1071. lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1072. pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwpEntryOffset);
  1073. //
  1074. // set entry's filename offset field
  1075. //
  1076. pGroupEntry->dwGroupNameOffset = PtrDiff32(*pOutData, lpbBase);
  1077. // succeed
  1078. dwError = ERROR_SUCCESS;
  1079. }
  1080. else
  1081. {
  1082. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1083. }
  1084. //////////////////////////////////////////////////////////////////
  1085. // END WARNING: The file might be grown and remapped, so all //
  1086. // pointers into the file before this point may be invalidated. //
  1087. //////////////////////////////////////////////////////////////////
  1088. }
  1089. else
  1090. {
  1091. dwError = ERROR_FILE_NOT_FOUND;
  1092. }
  1093. exit:
  1094. if( fMustUnlock )
  1095. {
  1096. _pContainer->UnlockContainer();
  1097. }
  1098. if( fCreate && (dwError == ERROR_SUCCESS) )
  1099. {
  1100. // for new item, it's nice to mark the next link to 0
  1101. (*pOutData)->dwOffsetNext = 0;
  1102. }
  1103. return dwError;
  1104. }
  1105. VOID
  1106. GroupMgr::FreeDataEntry(GROUP_DATA_ENTRY* pDataEntry)
  1107. {
  1108. // get the head entry
  1109. GROUP_ENTRY* pGroupEntry = NULL;
  1110. DWORD dwError = FindRootEntry(&pGroupEntry, FALSE );
  1111. if( dwError != ERROR_SUCCESS )
  1112. {
  1113. return;
  1114. }
  1115. //
  1116. // walk to the index item whose dwGroupNameOffset
  1117. // contains offset the the head of free list
  1118. //
  1119. pGroupEntry += (GROUPS_PER_PAGE - 1);
  1120. INET_ASSERT( pGroupEntry->gid == GID_INDEX_TO_NEXT_PAGE);
  1121. // memset the freed data entry
  1122. memset(pDataEntry, 0, sizeof(GROUP_DATA_ENTRY) );
  1123. // make data item's next link points to current head
  1124. pDataEntry->dwOffsetNext = pGroupEntry->dwGroupNameOffset;
  1125. // make the current head to be the just freed item's offset
  1126. LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
  1127. pGroupEntry->dwGroupNameOffset = PtrDiff32(pDataEntry, lpbBase);
  1128. }
  1129. LPGROUP_DATA_ENTRY
  1130. GroupMgr::GetHeadDataEntry(BOOL fCreate)
  1131. {
  1132. GROUP_DATA_ENTRY* pDataEntry = NULL;
  1133. GROUP_ENTRY* pGroupEntry = NULL;
  1134. LPBYTE lpbBase = NULL;
  1135. // get the head entry
  1136. DWORD dwError = FindRootEntry(&pGroupEntry, FALSE );
  1137. if( dwError != ERROR_SUCCESS )
  1138. {
  1139. goto exit;
  1140. }
  1141. // walk to the index item
  1142. pGroupEntry += (GROUPS_PER_PAGE - 1);
  1143. INET_ASSERT( pGroupEntry->gid == GID_INDEX_TO_NEXT_PAGE);
  1144. // the dwGroupNameOffset contains offset the the head of free list
  1145. if( pGroupEntry->dwGroupNameOffset)
  1146. {
  1147. // get the head
  1148. lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1149. pDataEntry = (GROUP_DATA_ENTRY*) (lpbBase + pGroupEntry->dwGroupNameOffset);
  1150. // reset head to next one
  1151. pGroupEntry->dwGroupNameOffset = pDataEntry->dwOffsetNext;
  1152. }
  1153. else if( fCreate )
  1154. {
  1155. //////////////////////////////////////////////////////////////////
  1156. // BEGIN WARNING: The file might be grown and remapped, so all //
  1157. // pointers into the file before this point may be invalidated. //
  1158. //////////////////////////////////////////////////////////////////
  1159. // remember the old offset for pGroupEntry
  1160. DWORD_PTR dwpEntryOffset = PtrDifference(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
  1161. // create a new page
  1162. GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
  1163. DWORD cbSize = sizeof(GROUPS_ALLOC_FILEMAP_ENTRY);
  1164. pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*)
  1165. _pContainer->_UrlObjStorage->AllocateEntry(cbSize);
  1166. if( !pPage )
  1167. {
  1168. goto exit;
  1169. }
  1170. // memset
  1171. memset(pPage->pGroupBlock, 0, PAGE_SIZE_FOR_GROUPS);
  1172. lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1173. GROUP_DATA_ENTRY* pHead = (GROUP_DATA_ENTRY*)pPage->pGroupBlock;
  1174. pDataEntry = pHead;
  1175. // init list on the newly created page
  1176. for(int i = 0; i < GROUPS_DATA_PER_PAGE - 1; i++)
  1177. {
  1178. // point to next offset
  1179. GROUP_DATA_ENTRY* pNext = pHead + 1;
  1180. pHead->dwOffsetNext = PtrDiff32(pNext, lpbBase);
  1181. pHead = pNext;
  1182. }
  1183. //
  1184. // pGroupEntry needs to be re-calc!
  1185. //
  1186. pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwpEntryOffset);
  1187. //
  1188. // pGroupEntry currently is the index entry of the first
  1189. // page, it's dwGroupNameOffset field points the head of
  1190. // the list of a free group data entry
  1191. //
  1192. pGroupEntry->dwGroupNameOffset = pDataEntry->dwOffsetNext;
  1193. //////////////////////////////////////////////////////////////////
  1194. // END WARNING: The file might be grown and remapped, so all //
  1195. // pointers into the file before this point may be invalidated. //
  1196. //////////////////////////////////////////////////////////////////
  1197. }
  1198. else
  1199. {
  1200. goto exit;
  1201. }
  1202. exit:
  1203. return pDataEntry;
  1204. }
  1205. DWORD
  1206. GroupMgr::GetOffsetFromList(DWORD dwHeadOffset, GROUPID gid, DWORD* pdwOffset)
  1207. {
  1208. DWORD dwError;
  1209. LIST_GROUP_ENTRY* pListGroup = NULL;
  1210. GROUP_ENTRY* pGroupEntry = NULL;
  1211. *pdwOffset = 0;
  1212. pListGroup =
  1213. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
  1214. if( !pListGroup )
  1215. {
  1216. dwError = ERROR_FILE_NOT_FOUND;
  1217. goto Cleanup;
  1218. }
  1219. while(1)
  1220. {
  1221. if(!_pContainer->_UrlObjStorage->IsBadGroupOffset(pListGroup->dwGroupOffset))
  1222. {
  1223. pGroupEntry = (GROUP_ENTRY*)
  1224. ( *_pContainer->_UrlObjStorage->GetHeapStart()
  1225. + pListGroup->dwGroupOffset );
  1226. }
  1227. else
  1228. {
  1229. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1230. goto Cleanup;
  1231. }
  1232. if( pGroupEntry && pGroupEntry->gid == gid )
  1233. {
  1234. *pdwOffset = pListGroup->dwGroupOffset;
  1235. break;
  1236. }
  1237. // end of list, not found
  1238. if( !pListGroup->dwNext )
  1239. {
  1240. dwError = ERROR_FILE_NOT_FOUND;
  1241. break;
  1242. }
  1243. // walk to next
  1244. pListGroup =
  1245. _pContainer->_UrlObjStorage->ValidateListGroupOffset(
  1246. pListGroup->dwNext);
  1247. if( !pListGroup )
  1248. {
  1249. dwError = ERROR_FILE_NOT_FOUND;
  1250. goto Cleanup;
  1251. }
  1252. }
  1253. if( *pdwOffset )
  1254. {
  1255. dwError = ERROR_SUCCESS;
  1256. }
  1257. else
  1258. {
  1259. dwError = ERROR_FILE_NOT_FOUND;
  1260. }
  1261. Cleanup:
  1262. return dwError;
  1263. }
  1264. DWORD
  1265. GroupMgr::CreateNewGroupList(DWORD* pdwHeadOffset)
  1266. {
  1267. DWORD dwError;
  1268. // Find empty slot
  1269. *pdwHeadOffset = 0;
  1270. dwError = FindEmptySlotInListPage(pdwHeadOffset);
  1271. if( ERROR_SUCCESS != dwError )
  1272. {
  1273. goto Cleanup;
  1274. }
  1275. Cleanup:
  1276. return dwError;
  1277. }
  1278. DWORD
  1279. GroupMgr::AddToGroupList(DWORD dwHeadOffset, DWORD dwOffset)
  1280. {
  1281. DWORD dwError;
  1282. DWORD dwEmptySlot;
  1283. LIST_GROUP_ENTRY* pListGroup = NULL;
  1284. LIST_GROUP_ENTRY* pListGroupEmpty = NULL;
  1285. // if the item already on the list, return success
  1286. if( IsGroupOnList(dwHeadOffset, dwOffset) )
  1287. {
  1288. dwError = ERROR_SUCCESS;
  1289. goto Cleanup;
  1290. }
  1291. pListGroup =
  1292. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
  1293. if( !pListGroup )
  1294. {
  1295. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1296. goto Cleanup;
  1297. }
  1298. if( !pListGroup->dwGroupOffset )
  1299. {
  1300. // list is empty, just need to fill up the Head
  1301. pListGroup->dwGroupOffset = dwOffset;
  1302. }
  1303. else
  1304. {
  1305. // List is not empty, we have to walk to end of the list
  1306. // also need to get another empty slot
  1307. //////////////////////////////////////////////////////////////////
  1308. // BEGIN WARNING: The file might be grown and remapped, so all //
  1309. // pointers into the file before this point may be invalidated. //
  1310. //////////////////////////////////////////////////////////////////
  1311. // remember the old offset for pListGroup
  1312. DWORD_PTR dwpListGroupOffset = PtrDifference(pListGroup, *_pContainer->_UrlObjStorage->GetHeapStart());
  1313. // find empty slot
  1314. dwError = FindEmptySlotInListPage(&dwEmptySlot);
  1315. if( ERROR_SUCCESS != dwError )
  1316. {
  1317. goto Cleanup;
  1318. }
  1319. // recalculate pListGroup using the offset remembered
  1320. LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
  1321. pListGroup = (LIST_GROUP_ENTRY*)(lpbBase + dwpListGroupOffset);
  1322. //////////////////////////////////////////////////////////////////
  1323. // END WARNING: The file might be grown and remapped, so all //
  1324. // pointers into the file before this point may be invalidated. //
  1325. //////////////////////////////////////////////////////////////////
  1326. // walk to end of list
  1327. while( pListGroup->dwNext )
  1328. {
  1329. pListGroup =
  1330. _pContainer->_UrlObjStorage->ValidateListGroupOffset(
  1331. pListGroup->dwNext);
  1332. if( !pListGroup )
  1333. {
  1334. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1335. goto Cleanup;
  1336. }
  1337. }
  1338. // Get ListGroupEmpty Object from the empty slot
  1339. pListGroupEmpty =
  1340. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwEmptySlot);
  1341. if( !pListGroupEmpty )
  1342. {
  1343. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1344. goto Cleanup;
  1345. }
  1346. // assign the new offset
  1347. pListGroupEmpty->dwGroupOffset = dwOffset;
  1348. // append empty slot at the end of the list
  1349. // this need to be done at last to prevent some invalid
  1350. // object get on the list
  1351. pListGroup->dwNext = dwEmptySlot;
  1352. }
  1353. dwError = ERROR_SUCCESS;
  1354. Cleanup:
  1355. return dwError;
  1356. }
  1357. DWORD
  1358. GroupMgr::RemoveFromGroupList(
  1359. DWORD dwHeadOffset,
  1360. DWORD dwOffset,
  1361. LPDWORD pdwNewHeadOffset
  1362. )
  1363. {
  1364. DWORD dwError = ERROR_SUCCESS;
  1365. LIST_GROUP_ENTRY* pListGroup = NULL;
  1366. LIST_GROUP_ENTRY* pListGroupPrev = NULL;
  1367. LPBYTE lpbBase = NULL;
  1368. pListGroup =
  1369. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
  1370. if( !pListGroup )
  1371. {
  1372. dwError = ERROR_FILE_NOT_FOUND;
  1373. goto Cleanup;
  1374. }
  1375. lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1376. // header is the one we need, we will have to assign new header
  1377. if( pListGroup->dwGroupOffset == dwOffset )
  1378. {
  1379. // new head
  1380. *pdwNewHeadOffset = pListGroup->dwNext;
  1381. // empty removed head and added to free list
  1382. pListGroup->dwGroupOffset = 0;
  1383. pListGroup->dwNext= 0;
  1384. AddToFreeList(pListGroup);
  1385. // done
  1386. dwError = ERROR_SUCCESS;
  1387. goto Cleanup;
  1388. }
  1389. if( !pListGroup->dwNext )
  1390. {
  1391. dwError = ERROR_FILE_NOT_FOUND;
  1392. goto Cleanup;
  1393. }
  1394. pListGroupPrev = pListGroup;
  1395. pListGroup =
  1396. _pContainer->_UrlObjStorage->ValidateListGroupOffset(pListGroup->dwNext);
  1397. if( !pListGroup)
  1398. {
  1399. dwError = ERROR_FILE_NOT_FOUND;
  1400. goto Cleanup;
  1401. }
  1402. while( pListGroup )
  1403. {
  1404. INET_ASSERT(pListGroup->dwGroupOffset);
  1405. if( pListGroup->dwGroupOffset == dwOffset )
  1406. {
  1407. pListGroupPrev->dwNext = pListGroup->dwNext;
  1408. // empty removed item and added it to free list
  1409. pListGroup->dwGroupOffset = 0;
  1410. pListGroup->dwNext= 0;
  1411. AddToFreeList(pListGroup);
  1412. dwError = ERROR_SUCCESS;
  1413. break;
  1414. }
  1415. if( pListGroup->dwNext )
  1416. {
  1417. pListGroupPrev = pListGroup;
  1418. pListGroup =
  1419. _pContainer->_UrlObjStorage->ValidateListGroupOffset(pListGroup->dwNext);
  1420. }
  1421. else
  1422. {
  1423. dwError = ERROR_FILE_NOT_FOUND;
  1424. break;
  1425. }
  1426. }
  1427. Cleanup:
  1428. return dwError;
  1429. }
  1430. DWORD
  1431. GroupMgr::FindEmptySlotInListPage(DWORD* pdwOffsetToSlot)
  1432. {
  1433. DWORD dwError;
  1434. DWORD dwOffsetRoot = 0;
  1435. LPBYTE lpbBase = NULL;
  1436. LIST_GROUP_ENTRY* pListGroupFreeHead = NULL;
  1437. LIST_GROUP_ENTRY* pListGroupEmpty = NULL;
  1438. if( !GetHeaderData( CACHE_HEADER_DATA_ROOT_GROUPLIST_OFFSET, &dwOffsetRoot))
  1439. {
  1440. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1441. goto Cleanup;
  1442. }
  1443. if( !dwOffsetRoot)
  1444. {
  1445. // new page needs to be created
  1446. dwError = CreateNewListPage(&dwOffsetRoot, TRUE);
  1447. if( dwError != ERROR_SUCCESS)
  1448. goto Cleanup;
  1449. }
  1450. //
  1451. // At this point, we've got the root entry
  1452. // 1. retrieved valid dwOffsetToRootEntry or
  1453. // 2. get the new dwOffsetToRootEntry via CreateNewPage() call
  1454. //
  1455. INET_ASSERT( dwOffsetRoot);
  1456. lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1457. pListGroupFreeHead = (LIST_GROUP_ENTRY*) (lpbBase + dwOffsetRoot);
  1458. if( !pListGroupFreeHead )
  1459. {
  1460. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1461. goto Cleanup;
  1462. }
  1463. //////////////////////////////////////////////////////////////////
  1464. // BEGIN WARNING: The file might be grown and remapped, so all //
  1465. // pointers into the file before this point may be invalidated. //
  1466. //////////////////////////////////////////////////////////////////
  1467. // get the next free item from the list
  1468. if( !pListGroupFreeHead->dwNext )
  1469. {
  1470. // no free slot left!, let's create a new page!
  1471. // remember the old offset free list head entry
  1472. DWORD_PTR dwpFreeHeadOffset = PtrDifference(pListGroupFreeHead, lpbBase);
  1473. // create a new page
  1474. DWORD dwNewList;
  1475. dwError = CreateNewListPage(&dwNewList, FALSE);
  1476. if( dwError != ERROR_SUCCESS)
  1477. goto Cleanup;
  1478. // restore
  1479. lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1480. pListGroupFreeHead = (LIST_GROUP_ENTRY*) (lpbBase + dwpFreeHeadOffset);
  1481. //
  1482. // add the newly created page contains a list of empty
  1483. // slot (already chained together), now update the head
  1484. // of free list pointing to the head of the newly created
  1485. // list
  1486. //
  1487. pListGroupFreeHead->dwNext = dwNewList;
  1488. }
  1489. //////////////////////////////////////////////////////////////////
  1490. // END WARNING: The file might be grown and remapped, so all //
  1491. // pointers into the file before this point may be invalidated. //
  1492. //////////////////////////////////////////////////////////////////
  1493. // get the empty slot offset
  1494. *pdwOffsetToSlot = pListGroupFreeHead->dwNext;
  1495. // update the free list to point to the next slot
  1496. pListGroupEmpty = (LIST_GROUP_ENTRY*)(lpbBase + pListGroupFreeHead->dwNext);
  1497. pListGroupFreeHead->dwNext = pListGroupEmpty->dwNext;
  1498. memset(pListGroupEmpty, 0, sizeof(LIST_GROUP_ENTRY) );
  1499. dwError = ERROR_SUCCESS;
  1500. Cleanup:
  1501. return dwError;
  1502. }
  1503. DWORD
  1504. GroupMgr::CreateNewListPage(DWORD* pdwOffsetToFirstEntry, BOOL fIsFirstPage)
  1505. {
  1506. DWORD dwError;
  1507. GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
  1508. DWORD cbSize = sizeof(GROUPS_ALLOC_FILEMAP_ENTRY);
  1509. pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*)
  1510. _pContainer->_UrlObjStorage->AllocateEntry(cbSize);
  1511. if( pPage )
  1512. {
  1513. // clean up allocated page
  1514. cbSize = PAGE_SIZE_FOR_GROUPS;
  1515. memset(pPage->pGroupBlock, 0, cbSize );
  1516. // calculate the group base offset
  1517. LPBYTE lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
  1518. *pdwOffsetToFirstEntry = PtrDiff32(pPage->pGroupBlock, lpbBase);
  1519. //
  1520. // chain all items together
  1521. // (Last item will have dwNext == 0 since we have alredy memset
  1522. // the whole page )
  1523. //
  1524. LIST_GROUP_ENTRY* pList = (LIST_GROUP_ENTRY*) pPage->pGroupBlock;
  1525. for( DWORD dwi = 0; dwi < (LIST_GROUPS_PER_PAGE -1); dwi++)
  1526. {
  1527. pList->dwNext = PtrDiff32(pList+1, lpbBase);
  1528. pList++ ;
  1529. }
  1530. if( fIsFirstPage )
  1531. {
  1532. //
  1533. // for first page, we would have to set the offset
  1534. // back to the CacheHeader
  1535. //
  1536. if( !SetHeaderData(
  1537. CACHE_HEADER_DATA_ROOT_GROUPLIST_OFFSET,
  1538. *pdwOffsetToFirstEntry))
  1539. {
  1540. // free allocated page
  1541. _pContainer->_UrlObjStorage->FreeEntry(pPage);
  1542. // set error and go
  1543. *pdwOffsetToFirstEntry = 0;
  1544. dwError = ERROR_INTERNET_INTERNAL_ERROR;
  1545. goto Cleanup;
  1546. } // IF: failed to set the offset
  1547. }
  1548. // return the offset to the first entry of the new page
  1549. dwError = ERROR_SUCCESS;
  1550. } // IF: Allocate new page succeed
  1551. else
  1552. {
  1553. dwError = ERROR_NOT_ENOUGH_MEMORY;
  1554. } // ELSE: failed to allocate new page
  1555. Cleanup:
  1556. return dwError;
  1557. }
  1558. BOOL
  1559. GroupMgr::IsGroupOnList(DWORD dwHeadOffset, DWORD dwGrpOffset)
  1560. {
  1561. BOOL fRet = FALSE;
  1562. LIST_GROUP_ENTRY* pListGroup = NULL;
  1563. LIST_GROUP_ENTRY *pMilestone = NULL; // used for detecting cycles
  1564. unsigned long dwNodeCount = 1;
  1565. pListGroup =
  1566. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
  1567. if( !pListGroup )
  1568. {
  1569. goto Cleanup;
  1570. }
  1571. while( pListGroup )
  1572. {
  1573. //INET_ASSERT(pListGroup->dwGroupOffset);
  1574. if( pListGroup->dwGroupOffset == dwGrpOffset )
  1575. {
  1576. fRet = TRUE;
  1577. break;
  1578. }
  1579. if( pListGroup->dwNext )
  1580. {
  1581. LIST_GROUP_ENTRY* plgTemp =
  1582. _pContainer->_UrlObjStorage->ValidateListGroupOffset(
  1583. pListGroup->dwNext);
  1584. // Sometimes the list is corrupted and contains a cycle
  1585. // This is detected by comparing against the saved pointer
  1586. // (Revisiting an earlier milestone indicates a cycle)
  1587. if (plgTemp==pMilestone)
  1588. break;
  1589. // Also check (and fix) simple self-loops
  1590. if (plgTemp==pListGroup)
  1591. {
  1592. pListGroup->dwNext = 0;
  1593. break;
  1594. }
  1595. // Advance to next node
  1596. pListGroup = plgTemp;
  1597. // Choose new milestone when node count is power of 2
  1598. dwNodeCount++;
  1599. if ((dwNodeCount & (dwNodeCount-1)) == 0)
  1600. pMilestone = pListGroup;
  1601. }
  1602. else
  1603. {
  1604. break;
  1605. }
  1606. }
  1607. Cleanup:
  1608. return fRet;
  1609. }
  1610. BOOL
  1611. GroupMgr::NoMoreStickyEntryOnList(DWORD dwHeadOffset)
  1612. {
  1613. BOOL fRet = FALSE;
  1614. LIST_GROUP_ENTRY* pListGroup = NULL;
  1615. GROUP_ENTRY* pGroupEntry = NULL;
  1616. pListGroup =
  1617. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
  1618. if( !pListGroup )
  1619. {
  1620. goto Cleanup;
  1621. }
  1622. while( pListGroup )
  1623. {
  1624. //INET_ASSERT(pListGroup->dwGroupOffset);
  1625. // get the GroupEntry structure
  1626. if( !_pContainer->_UrlObjStorage->IsBadGroupOffset(
  1627. pListGroup->dwGroupOffset) )
  1628. {
  1629. pGroupEntry = (GROUP_ENTRY*)
  1630. ( *_pContainer->_UrlObjStorage->GetHeapStart() +
  1631. pListGroup->dwGroupOffset );
  1632. // IsSticky?
  1633. if( IsStickyGroup(pGroupEntry->gid) )
  1634. {
  1635. goto Cleanup;
  1636. }
  1637. }
  1638. // end of list
  1639. if( !pListGroup->dwNext )
  1640. {
  1641. break;
  1642. }
  1643. // next item on list
  1644. pListGroup = _pContainer->_UrlObjStorage->ValidateListGroupOffset(
  1645. pListGroup->dwNext);
  1646. }
  1647. //
  1648. // reach here means we are at end of the list and can not find
  1649. // any sticky group, return TRUE
  1650. //
  1651. fRet = TRUE;
  1652. Cleanup:
  1653. return fRet;
  1654. }
  1655. void
  1656. GroupMgr::AdjustUsageOnList(DWORD dwHeadOffset, LONGLONG llDelta)
  1657. {
  1658. LIST_GROUP_ENTRY* pListGroup = NULL;
  1659. GROUP_ENTRY* pGroupEntry = NULL;
  1660. pListGroup =
  1661. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
  1662. if( !pListGroup )
  1663. {
  1664. goto Cleanup;
  1665. }
  1666. while( pListGroup )
  1667. {
  1668. // INET_ASSERT(pListGroup->dwGroupOffset);
  1669. // get the GroupEntry structure
  1670. if( !_pContainer->_UrlObjStorage->IsBadGroupOffset(
  1671. pListGroup->dwGroupOffset) )
  1672. {
  1673. pGroupEntry = (GROUP_ENTRY*)
  1674. ( *_pContainer->_UrlObjStorage->GetHeapStart() +
  1675. pListGroup->dwGroupOffset );
  1676. // AdjustUsage
  1677. _pContainer->AdjustGroupUsage(pGroupEntry, llDelta);
  1678. }
  1679. // end of list
  1680. if( !pListGroup->dwNext )
  1681. {
  1682. goto Cleanup;
  1683. }
  1684. // next item on list
  1685. pListGroup = _pContainer->_UrlObjStorage->ValidateListGroupOffset(
  1686. pListGroup->dwNext);
  1687. }
  1688. Cleanup:
  1689. return;
  1690. }
  1691. void
  1692. GroupMgr::AddToFreeList(LIST_GROUP_ENTRY* pFreeListGroup)
  1693. {
  1694. DWORD dwOffsetRoot = 0;
  1695. LIST_GROUP_ENTRY* pFreeListHead = NULL;
  1696. if( GetHeaderData( CACHE_HEADER_DATA_ROOT_GROUPLIST_OFFSET, &dwOffsetRoot))
  1697. {
  1698. pFreeListHead =
  1699. _pContainer->_UrlObjStorage->ValidateListGroupOffset(dwOffsetRoot);
  1700. if( pFreeListHead && pFreeListGroup )
  1701. {
  1702. pFreeListGroup->dwNext = pFreeListHead->dwNext;
  1703. pFreeListHead->dwNext = PtrDiff32(pFreeListGroup,
  1704. *_pContainer->_UrlObjStorage->GetHeapStart());
  1705. }
  1706. }
  1707. return;
  1708. }