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
2100 lines
57 KiB
/*++
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name: group.hxx
|
|
|
|
Abstract:
|
|
|
|
Manages cache group.
|
|
|
|
Author:
|
|
Danpo Zhang (DanpoZ) 02-08-98
|
|
--*/
|
|
|
|
#include <cache.hxx>
|
|
|
|
GroupMgr::GroupMgr()
|
|
{
|
|
_pContainer = NULL;
|
|
}
|
|
|
|
GroupMgr::~GroupMgr()
|
|
{
|
|
if( _pContainer )
|
|
{
|
|
_pContainer->Release(FALSE);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
GroupMgr::Init(URL_CONTAINER* pCont)
|
|
{
|
|
BOOL fRet = TRUE;
|
|
|
|
if( pCont )
|
|
{
|
|
_pContainer = pCont;
|
|
_pContainer->AddRef();
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_INTERNET_INTERNAL_ERROR);
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
DWORD
|
|
GroupMgr::CreateGroup(DWORD dwFlags, GROUPID* pGID)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
INET_ASSERT(pGID);
|
|
|
|
BOOL fMustUnlock;
|
|
DWORD dwError;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
|
|
*pGID = 0;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if( dwFlags & CACHEGROUP_FLAG_GIDONLY )
|
|
{
|
|
// only needs to return GID, no group needs to be created
|
|
*pGID = ObtainNewGID();
|
|
if( *pGID )
|
|
dwError = ERROR_SUCCESS;
|
|
else
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// find the first available entry by using FindEntry()
|
|
// passing gid = 0 means looking for empty entry
|
|
// passing TRUE means create new page if no entry available
|
|
//
|
|
dwError = FindEntry(0, &pGroupEntry, TRUE );
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// get a new gid
|
|
*pGID = ObtainNewGID();
|
|
|
|
if( *pGID )
|
|
{
|
|
// insert gid into the first available entry
|
|
|
|
// set the sticky bit for non purgable group
|
|
if( dwFlags & CACHEGROUP_FLAG_NONPURGEABLE )
|
|
{
|
|
*pGID = SetStickyBit(*pGID);
|
|
}
|
|
|
|
pGroupEntry->gid = *pGID;
|
|
pGroupEntry->dwGroupFlags = dwFlags;
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
exit:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::CreateDefaultGroups()
|
|
{
|
|
|
|
INET_ASSERT(_pContainer);
|
|
|
|
BOOL fMustUnlock;
|
|
DWORD dwError;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
DWORD dwOffsetHead = 0;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if( GetHeaderData( CACHE_HEADER_DATA_ROOTGROUP_OFFSET, &dwOffsetHead)
|
|
&& dwOffsetHead )
|
|
{
|
|
BOOL fBadHead = FALSE;
|
|
|
|
// dwOffsetHead may point to a page which has not actually mapped in
|
|
if( _pContainer->_UrlObjStorage->IsBadGroupOffset(dwOffsetHead) )
|
|
{
|
|
fBadHead = TRUE;
|
|
}
|
|
else
|
|
{
|
|
|
|
// if offset is too big, invalid
|
|
FILEMAP_ENTRY* pFM = NULL;
|
|
|
|
pFM = (FILEMAP_ENTRY*)
|
|
(*_pContainer->_UrlObjStorage->GetHeapStart() +
|
|
dwOffsetHead - sizeof(FILEMAP_ENTRY) );
|
|
if(pFM->dwSig != SIG_ALLOC || !pFM->nBlocks )
|
|
{
|
|
fBadHead = TRUE;
|
|
}
|
|
}
|
|
|
|
if( fBadHead )
|
|
{
|
|
// dwOffsetHead is invalid, reset!
|
|
SetHeaderData(CACHE_HEADER_DATA_ROOTGROUP_OFFSET, 0);
|
|
}
|
|
}
|
|
|
|
// if already created, just return success
|
|
dwError = FindEntry(CACHEGROUP_ID_BUILTIN_STICKY, &pGroupEntry, FALSE);
|
|
if( dwError == ERROR_SUCCESS )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// not found, need to create new default groups
|
|
//
|
|
// find the first available entry by using FindEntry()
|
|
// passing gid = 0 means looking for empty entry
|
|
// passing TRUE means create new page if no entry available
|
|
//
|
|
dwError = FindEntry(0, &pGroupEntry, TRUE );
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// set the sticky bit for non purgable group
|
|
pGroupEntry->gid = CACHEGROUP_ID_BUILTIN_STICKY;
|
|
pGroupEntry->dwGroupFlags = CACHEGROUP_FLAG_NONPURGEABLE;
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
exit:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::DeleteGroup(GROUPID gid, DWORD dwFlags)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
INET_ASSERT(gid);
|
|
|
|
BOOL fMustUnlock;
|
|
DWORD dwError;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
GROUP_DATA_ENTRY* pData = NULL;
|
|
DWORD hUrlFindHandle = 0;
|
|
URL_FILEMAP_ENTRY* pUrlEntry = 0;
|
|
DWORD dwFindFilter;
|
|
HASH_ITEM* pItem = NULL;
|
|
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// find the first available entry
|
|
dwError = FindEntry(gid, &pGroupEntry, FALSE);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
|
|
// Look for all the url associated with this group
|
|
// mark the groupid to 0
|
|
hUrlFindHandle = _pContainer->GetInitialFindHandle();
|
|
|
|
// set up find filter (do not care about cookie/history)
|
|
dwFindFilter = URLCACHE_FIND_DEFAULT_FILTER
|
|
& ~COOKIE_CACHE_ENTRY
|
|
& ~URLHISTORY_CACHE_ENTRY;
|
|
|
|
//
|
|
// loop find all url belongs to this group
|
|
// WARNING: this can be slow!
|
|
//
|
|
do
|
|
{
|
|
// next url in this group
|
|
pUrlEntry = (URL_FILEMAP_ENTRY*)
|
|
_pContainer->_UrlObjStorage->FindNextEntry(
|
|
&hUrlFindHandle, dwFindFilter, gid);
|
|
|
|
if( pUrlEntry )
|
|
{
|
|
INET_ASSERT(hUrlFindHandle);
|
|
pItem = (HASH_ITEM*)(
|
|
(LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart() +
|
|
hUrlFindHandle );
|
|
|
|
if( pItem->HasMultiGroup() )
|
|
{
|
|
//
|
|
// examing the group list and remove this group
|
|
// from the list
|
|
//
|
|
DWORD dwNewHeaderOffset = pUrlEntry->dwGroupOffset;
|
|
DWORD dwGroupEntryOffset = PtrDiff32(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
|
|
|
|
//
|
|
// find the to be deleted group entry in the list
|
|
// of groups associated with this url, we need to
|
|
// fix this by removing the to be dead group from
|
|
// the list
|
|
//
|
|
DWORD Error = RemoveFromGroupList(
|
|
pUrlEntry->dwGroupOffset,
|
|
dwGroupEntryOffset,
|
|
&dwNewHeaderOffset
|
|
);
|
|
|
|
|
|
//
|
|
// found the entry and head offset has been changed
|
|
//
|
|
if( Error == ERROR_SUCCESS &&
|
|
dwNewHeaderOffset != pUrlEntry->dwGroupOffset )
|
|
{
|
|
pUrlEntry->dwGroupOffset = dwNewHeaderOffset;
|
|
|
|
//
|
|
// no more group associated with this url
|
|
// let's update the hash flags
|
|
//
|
|
if( !dwNewHeaderOffset )
|
|
{
|
|
pItem->ClearMultGroup();
|
|
pItem->ClearGroup();
|
|
}
|
|
}
|
|
|
|
// sticky bit
|
|
if(!pUrlEntry->dwExemptDelta && IsStickyGroup(gid) )
|
|
{
|
|
//
|
|
// unset sticky bit for this url IFF
|
|
// 1) we are about to delete the last group of this url
|
|
// 2) there is no more sticky group associated with this
|
|
// url other than the to be deleted group
|
|
//
|
|
if( !pUrlEntry->dwGroupOffset ||
|
|
( pUrlEntry->dwGroupOffset &&
|
|
NoMoreStickyEntryOnList(pUrlEntry->dwGroupOffset)))
|
|
{
|
|
|
|
_pContainer->UpdateStickness(
|
|
pUrlEntry,
|
|
URLCACHE_OP_UNSET_STICKY,
|
|
hUrlFindHandle
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// do not move the url entry now, so we just
|
|
// need to reset the GroupOffset and re-exam the
|
|
// stick bit
|
|
//
|
|
pUrlEntry->dwGroupOffset = 0;
|
|
|
|
// sticky bit
|
|
if(!pUrlEntry->dwExemptDelta && IsStickyGroup(gid) )
|
|
{
|
|
|
|
_pContainer->UpdateStickness(
|
|
pUrlEntry,
|
|
URLCACHE_OP_UNSET_STICKY,
|
|
hUrlFindHandle
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
if( dwFlags & CACHEGROUP_FLAG_FLUSHURL_ONDELETE)
|
|
{
|
|
//
|
|
// Container's DeleteUrlEntry method takes two
|
|
// param, the url entry and hash item.
|
|
// The hUrlFindHandle actually contains the
|
|
// offset of the Hash Item, so we can get
|
|
// the hash item from there.
|
|
//
|
|
|
|
// if this url belongs to other groups,
|
|
// do not delete it
|
|
if( !pItem->HasMultiGroup() )
|
|
{
|
|
_pContainer->DeleteUrlEntry(pUrlEntry, pItem, SIG_DELETE);
|
|
}
|
|
}
|
|
|
|
} // find next url
|
|
} while( pUrlEntry);
|
|
|
|
// if data entry exists, we should free them as well
|
|
if( pGroupEntry->dwGroupNameOffset )
|
|
{
|
|
dwError = FindDataEntry(pGroupEntry, &pData, FALSE);
|
|
if( dwError == ERROR_SUCCESS )
|
|
{
|
|
FreeDataEntry(pData);
|
|
}
|
|
}
|
|
|
|
memset(pGroupEntry, 0, sizeof(GROUP_ENTRY) );
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
exit:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
GroupMgr::GetGroup(
|
|
GROUPID gid,
|
|
DWORD dwAttrib,
|
|
INTERNET_CACHE_GROUP_INFOA* pOutGroupInfo,
|
|
DWORD* pdwOutGroupInfoSize
|
|
)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
INET_ASSERT(gid && pOutGroupInfo && pdwOutGroupInfoSize);
|
|
|
|
BOOL fMustUnlock;
|
|
DWORD dwError;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
*pdwOutGroupInfoSize = 0;
|
|
|
|
// find the entry
|
|
dwError = FindEntry(gid, &pGroupEntry, FALSE);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// init out param
|
|
memset(pOutGroupInfo, 0, sizeof(INTERNET_CACHE_GROUP_INFOA) );
|
|
|
|
// copy over GROUP_ENTRY -> GROUP_INFO
|
|
Translate(
|
|
dwAttrib,
|
|
pOutGroupInfo,
|
|
pGroupEntry,
|
|
GROUP_ENTRY_TO_INFO,
|
|
pdwOutGroupInfoSize
|
|
);
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
exit:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::SetGroup(
|
|
GROUPID gid,
|
|
DWORD dwAttrib,
|
|
INTERNET_CACHE_GROUP_INFOA* pGroupInfo
|
|
)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
INET_ASSERT(pGroupInfo && gid);
|
|
|
|
BOOL fMustUnlock;
|
|
DWORD dwError;
|
|
GROUP_ENTRY* pGroupEntry;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
pGroupEntry = NULL;
|
|
|
|
INET_ASSERT(pGroupInfo);
|
|
|
|
if( dwAttrib & ~(CACHEGROUP_READWRITE_MASK) )
|
|
{
|
|
//
|
|
// read only fields are being requested
|
|
//
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( (dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME) &&
|
|
(strlen(pGroupInfo->szGroupName) >= GROUPNAME_MAX_LENGTH ) )
|
|
{
|
|
//
|
|
// name too long, exceed the buffer limit
|
|
//
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// find the entry
|
|
dwError = FindEntry(gid, &pGroupEntry, FALSE);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// copy over GROUP_INFO -> GROUP_ENTRY
|
|
Translate(
|
|
dwAttrib,
|
|
pGroupInfo,
|
|
pGroupEntry,
|
|
GROUP_INFO_TO_ENTRY,
|
|
0
|
|
);
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::GetNextGroup(
|
|
DWORD* pdwLastItemOffset,
|
|
GROUPID* pOutGroupId
|
|
)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
INET_ASSERT(pOutGroupId);
|
|
|
|
BOOL fMustUnlock;
|
|
BOOL fEndOfGroups;
|
|
GROUP_ENTRY* pGroupEntry;
|
|
DWORD dwNewOffset;
|
|
DWORD dwError;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
fEndOfGroups = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pGroupEntry = NULL;
|
|
dwNewOffset = 0;
|
|
fEndOfGroups = FALSE;
|
|
|
|
if( *pdwLastItemOffset == 0 )
|
|
{
|
|
// get root
|
|
dwError = FindRootEntry(&pGroupEntry, FALSE );
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
//
|
|
// new find and we can not get the root entry
|
|
// this means there are no group at all.
|
|
//
|
|
fEndOfGroups = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
} // IF: no previous offset, this is a new Find
|
|
|
|
else if( *pdwLastItemOffset == OFFSET_NO_MORE_GROUP )
|
|
{
|
|
// this group of search has completed already
|
|
fEndOfGroups = TRUE;
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
|
|
} // ELSE IF: previous FindNext has already reached the end of the groups
|
|
|
|
else
|
|
{
|
|
//
|
|
// use the offset to jump to the last returned item's entry
|
|
//
|
|
pGroupEntry = (GROUP_ENTRY*)
|
|
(*_pContainer->_UrlObjStorage->GetHeapStart() + *pdwLastItemOffset);
|
|
//
|
|
// one step forward
|
|
//
|
|
INET_ASSERT(pGroupEntry); // can't be null
|
|
INET_ASSERT( !IsIndexToNewPage(pGroupEntry) ); // can't be index item
|
|
pGroupEntry++;
|
|
|
|
} // ELSE: walk to the item which has been returned by previous FindNext()
|
|
|
|
|
|
// loop for next entry
|
|
while(pGroupEntry)
|
|
{
|
|
//
|
|
// if this entry is the last one of the page
|
|
// it contains offset pointing to the next page
|
|
//
|
|
if( IsIndexToNewPage(pGroupEntry) )
|
|
{
|
|
//
|
|
// BUGBUG
|
|
// we currently use dwFlags to indicating if
|
|
// this is pointing to the next offset
|
|
//
|
|
if( pGroupEntry->dwGroupFlags )
|
|
{
|
|
//
|
|
// walk to next page
|
|
//
|
|
pGroupEntry = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart()
|
|
+ pGroupEntry->dwGroupFlags );
|
|
} // IF: index entry point to next page
|
|
|
|
else
|
|
{
|
|
//
|
|
// we are done
|
|
//
|
|
fEndOfGroups = TRUE;
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
|
|
} // ELSE: index page contains nothing (this is the last page)
|
|
|
|
} // special case: current entry is the index(point to next page)
|
|
|
|
|
|
//
|
|
// using gid to test if the entry is empty, if not,
|
|
// walk to the next entry
|
|
//
|
|
if( !pGroupEntry->gid )
|
|
{
|
|
pGroupEntry++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
} // while(pGroupEntry)
|
|
|
|
|
|
Cleanup:
|
|
// update LastItemOffset
|
|
if (!fEndOfGroups
|
|
&& pGroupEntry)
|
|
{
|
|
LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
dwNewOffset = PtrDiff32(pGroupEntry, lpbBase);
|
|
*pdwLastItemOffset = dwNewOffset;
|
|
|
|
// copy over GROUP_ENTRY -> GROUP_INFO
|
|
*pOutGroupId = pGroupEntry->gid;
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
} // IF: find the item
|
|
|
|
else
|
|
{
|
|
*pdwLastItemOffset = OFFSET_NO_MORE_GROUP;
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
} // ELSE: not find
|
|
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::FindRootEntry(
|
|
GROUP_ENTRY** ppOut, // OUT: first empty entry
|
|
BOOL fCreate // allocate new page if needed
|
|
)
|
|
{
|
|
INET_ASSERT(ppOut);
|
|
*ppOut = NULL;
|
|
|
|
GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
|
|
DWORD dwError;
|
|
DWORD dwOffsetToRootEntry = 0;
|
|
|
|
// get base offset
|
|
if( GetHeaderData( CACHE_HEADER_DATA_ROOTGROUP_OFFSET, &dwOffsetToRootEntry))
|
|
{
|
|
if( !dwOffsetToRootEntry && fCreate )
|
|
{
|
|
dwError = CreateNewPage(&dwOffsetToRootEntry, TRUE);
|
|
|
|
if( dwError != ERROR_SUCCESS)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if( !dwOffsetToRootEntry && !fCreate )
|
|
{
|
|
//
|
|
// there is no offset infomation on the mem file
|
|
// however, the flag says do not create a new page
|
|
// failure is the only option here
|
|
//
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
} // IF: retrieve base offset
|
|
|
|
else
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
|
|
} // ELSE: failed to get base offset
|
|
|
|
|
|
//
|
|
// At this point, we should either:
|
|
// 1. retrieved valid dwOffsetToRootEntry or
|
|
// 2. get the new dwOffsetToRootEntry via CreateNewPage() call
|
|
//
|
|
INET_ASSERT( dwOffsetToRootEntry );
|
|
*ppOut = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart() + dwOffsetToRootEntry);
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
GroupMgr::FindEntry(
|
|
GROUPID gid, // gid, 0 means find first empty seat
|
|
GROUP_ENTRY** ppOut, // OUT: entry with gid specified
|
|
BOOL fCreate // allocate new page if needed
|
|
// (applied for searching empty seat only)
|
|
)
|
|
{
|
|
INET_ASSERT(ppOut);
|
|
|
|
// fCreate can only be associated with gid == 0
|
|
INET_ASSERT( (fCreate && !gid ) || (!fCreate && gid ) );
|
|
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
DWORD dwError;
|
|
|
|
// get Root Entry
|
|
dwError = FindRootEntry(&pGroupEntry, fCreate);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
} // failed to get the root entry
|
|
|
|
|
|
INET_ASSERT(pGroupEntry); // pGroupEntry should be available now
|
|
|
|
while(1)
|
|
{
|
|
// special case for end of this page
|
|
if( IsIndexToNewPage(pGroupEntry) )
|
|
{
|
|
//
|
|
// BUGBUG
|
|
// we currently use the dwFlags to indicating
|
|
// if this is pointing to the next offset
|
|
//
|
|
if( pGroupEntry->dwGroupFlags )
|
|
{
|
|
// walk to next page
|
|
pGroupEntry = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart()
|
|
+ pGroupEntry->dwGroupFlags );
|
|
|
|
} // IF: index entry points to next page
|
|
|
|
else if( fCreate)
|
|
{
|
|
//////////////////////////////////////////////////////////////////
|
|
// BEGIN WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
DWORD dwOffsetToFirstEntry = 0;
|
|
LPBYTE lpbBase = NULL;
|
|
|
|
// remember the old offset for pGroupEntry
|
|
DWORD_PTR dwpEntryOffset = PtrDifference(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
|
|
|
|
// create new page!
|
|
dwError = CreateNewPage(&dwOffsetToFirstEntry, FALSE);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// recalculate pGroupEntry using the offset remembered
|
|
lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwpEntryOffset);
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// END WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// pGroupEntry currently is the index item, insert
|
|
// the offset of the first item to the newly created page
|
|
//
|
|
pGroupEntry->dwGroupFlags = dwOffsetToFirstEntry;
|
|
|
|
// walk to the new page
|
|
pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwOffsetToFirstEntry);
|
|
|
|
|
|
} // ELSE IF: index entry not point to new page, fCreate is
|
|
// set, a new page is being created
|
|
|
|
else
|
|
{
|
|
// this is the end of all groups, item still not found,
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
|
|
} // ELSE: index entry not point to new page, fCreate not set
|
|
|
|
} // IF: this entry is an index entry
|
|
|
|
|
|
//
|
|
// now pGroupEntry must point to a normal group entry
|
|
//
|
|
INET_ASSERT( !IsIndexToNewPage(pGroupEntry) );
|
|
|
|
if( pGroupEntry->gid != gid )
|
|
{
|
|
// not found, walk to next entry
|
|
pGroupEntry++;
|
|
}
|
|
else
|
|
{
|
|
// found entry
|
|
dwError = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
} // WHILE: (loop over all page)
|
|
|
|
|
|
Cleanup:
|
|
if( dwError == ERROR_SUCCESS )
|
|
{
|
|
*ppOut = pGroupEntry;
|
|
}
|
|
else
|
|
{
|
|
*ppOut = NULL;
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
DWORD
|
|
GroupMgr::CreateNewPage(DWORD* dwOffsetToFirstEntry, BOOL fIsFirstPage)
|
|
{
|
|
DWORD dwError;
|
|
GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
|
|
DWORD cbSize = sizeof(GROUPS_ALLOC_FILEMAP_ENTRY);
|
|
|
|
pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*)
|
|
_pContainer->_UrlObjStorage->AllocateEntry(cbSize);
|
|
|
|
|
|
if( pPage )
|
|
{
|
|
// clean up allocated page
|
|
cbSize = PAGE_SIZE_FOR_GROUPS;
|
|
memset(pPage->pGroupBlock, 0, cbSize );
|
|
|
|
// calculate the group base offset
|
|
LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
|
|
*dwOffsetToFirstEntry = PtrDiff32(pPage->pGroupBlock, lpbBase);
|
|
|
|
//
|
|
// mark the last entry as index to next page
|
|
// (gid == GID_INDEX_TO_NEXT_PAGE) is the mark,
|
|
// the actual offset is stored at dwGroupFlags field
|
|
//
|
|
GROUP_ENTRY* pEnd = (GROUP_ENTRY*) pPage->pGroupBlock;
|
|
pEnd = pEnd + (GROUPS_PER_PAGE - 1);
|
|
pEnd->gid = GID_INDEX_TO_NEXT_PAGE;
|
|
|
|
if( fIsFirstPage )
|
|
{
|
|
//
|
|
// for first page, we would have to set the offset
|
|
// back to the CacheHeader
|
|
//
|
|
if( !SetHeaderData(
|
|
CACHE_HEADER_DATA_ROOTGROUP_OFFSET, *dwOffsetToFirstEntry))
|
|
{
|
|
// free allocated page
|
|
_pContainer->_UrlObjStorage->FreeEntry(pPage);
|
|
|
|
// set error and go
|
|
*dwOffsetToFirstEntry = 0;
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
|
|
} // IF: failed to set the offset
|
|
}
|
|
|
|
// return the offset to the first entry of the new page
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
} // IF: Allocate new page succeed
|
|
|
|
else
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
} // ELSE: failed to allocate new page
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
|
|
|
|
GROUPID
|
|
GroupMgr::ObtainNewGID()
|
|
{
|
|
SYSTEMTIME st;
|
|
DWORD dwC[2] = {0, 0};
|
|
GROUPID gid = 0;
|
|
|
|
// get counter from index file
|
|
if( GetHeaderData(CACHE_HEADER_DATA_GID_LOW, &dwC[0]) &&
|
|
GetHeaderData(CACHE_HEADER_DATA_GID_HIGH, &dwC[1]) )
|
|
{
|
|
if( !dwC[0] && !dwC[1] )
|
|
{
|
|
// need to get the current system time
|
|
GetSystemTime( &st );
|
|
SystemTimeToFileTime(&st, (FILETIME*)dwC);
|
|
|
|
} // IF: counter not initialized
|
|
|
|
else
|
|
{
|
|
// increment
|
|
if( dwC[0] != 0xffffffff )
|
|
{
|
|
dwC[0] ++;
|
|
}
|
|
else
|
|
{
|
|
dwC[0] = 0;
|
|
dwC[1] ++;
|
|
}
|
|
} // ELSE: counter initialized
|
|
|
|
// send data back to cache
|
|
if( SetHeaderData(CACHE_HEADER_DATA_GID_LOW, dwC[0] ) &&
|
|
SetHeaderData(CACHE_HEADER_DATA_GID_HIGH, dwC[1] ) )
|
|
{
|
|
//memcpy(&gid, dwC, sizeof(GROUPID) );
|
|
gid = *((GROUPID *)dwC);
|
|
}
|
|
}
|
|
|
|
// apply the mask to newly created gid
|
|
// the first 4 bits are reserved (one bit is used for stickness)
|
|
return (gid & GID_MASK);
|
|
}
|
|
|
|
|
|
BOOL
|
|
GroupMgr::Translate(
|
|
DWORD dwAttrib,
|
|
INTERNET_CACHE_GROUP_INFOA* pGroupInfo,
|
|
GROUP_ENTRY* pGroupEntry,
|
|
DWORD dwFlag,
|
|
DWORD* pdwSize
|
|
)
|
|
{
|
|
INET_ASSERT(pGroupInfo && pGroupEntry);
|
|
BOOL fRet = TRUE;
|
|
GROUP_DATA_ENTRY* pData = NULL;
|
|
DWORD dwError;
|
|
|
|
if( dwFlag == GROUP_ENTRY_TO_INFO )
|
|
{
|
|
INET_ASSERT(pdwSize);
|
|
|
|
// clear
|
|
memset(pGroupInfo, 0, sizeof(INTERNET_CACHE_GROUP_INFOA) );
|
|
*pdwSize = 0;
|
|
|
|
// basic entries
|
|
if( dwAttrib & CACHEGROUP_ATTRIBUTE_BASIC )
|
|
{
|
|
pGroupInfo->dwGroupSize = sizeof(INTERNET_CACHE_GROUP_INFOA);
|
|
pGroupInfo->dwGroupFlags = pGroupEntry->dwGroupFlags;
|
|
pGroupInfo->dwGroupType = pGroupEntry->dwGroupType;
|
|
pGroupInfo->dwDiskUsage = (DWORD)(pGroupEntry->llDiskUsage / 1024);
|
|
pGroupInfo->dwDiskQuota = pGroupEntry->dwDiskQuota;
|
|
}
|
|
|
|
// user friendly name
|
|
if( ( (dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME) |
|
|
(dwAttrib & CACHEGROUP_ATTRIBUTE_STORAGE ) ) &&
|
|
pGroupEntry->dwGroupNameOffset )
|
|
{
|
|
dwError = FindDataEntry(pGroupEntry, &pData, FALSE);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DWORD dwLen = strlen(pData->szName) + 1;
|
|
INET_ASSERT( dwLen > GROUPNAME_MAX_LENGTH );
|
|
|
|
memcpy( pGroupInfo->szGroupName,
|
|
pData->szName,
|
|
dwLen );
|
|
|
|
memcpy( pGroupInfo->dwOwnerStorage,
|
|
pData->dwOwnerStorage,
|
|
sizeof(DWORD) * GROUP_OWNER_STORAGE_SIZE );
|
|
}
|
|
}
|
|
|
|
// set size
|
|
*pdwSize = sizeof(INTERNET_CACHE_GROUP_INFOA);
|
|
}
|
|
|
|
else
|
|
if( dwFlag == GROUP_INFO_TO_ENTRY )
|
|
{
|
|
// copy
|
|
if( dwAttrib & CACHEGROUP_ATTRIBUTE_FLAG )
|
|
{
|
|
pGroupEntry->dwGroupFlags = pGroupInfo->dwGroupFlags;
|
|
}
|
|
|
|
if( dwAttrib & CACHEGROUP_ATTRIBUTE_TYPE )
|
|
{
|
|
pGroupEntry->dwGroupType = pGroupInfo->dwGroupType;
|
|
}
|
|
|
|
if( dwAttrib & CACHEGROUP_ATTRIBUTE_QUOTA )
|
|
{
|
|
pGroupEntry->dwDiskQuota = pGroupInfo->dwDiskQuota;
|
|
}
|
|
|
|
if( (dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME) |
|
|
(dwAttrib & CACHEGROUP_ATTRIBUTE_STORAGE ) )
|
|
{
|
|
|
|
dwError = FindDataEntry(pGroupEntry, &pData, TRUE);
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
else
|
|
{
|
|
|
|
if( dwAttrib & CACHEGROUP_ATTRIBUTE_GROUPNAME )
|
|
{
|
|
DWORD dwLen = strlen(pGroupInfo->szGroupName) + 1;
|
|
INET_ASSERT(dwLen > GROUPNAME_MAX_LENGTH);
|
|
|
|
memcpy( pData->szName,
|
|
pGroupInfo->szGroupName,
|
|
dwLen );
|
|
}
|
|
|
|
if( dwAttrib & CACHEGROUP_ATTRIBUTE_STORAGE )
|
|
{
|
|
memcpy( pData->dwOwnerStorage,
|
|
pGroupInfo->dwOwnerStorage,
|
|
sizeof(DWORD) * GROUP_OWNER_STORAGE_SIZE );
|
|
}
|
|
|
|
|
|
// BUGBUG
|
|
// if both fields are set to be empty, we should free
|
|
// the allocated data itam
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL
|
|
GroupMgr::IsPageEmpty(GROUP_ENTRY* pHead)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
GROUP_ENTRY* pGroupEntry = pHead;
|
|
for( int i = 0; i < (GROUPS_PER_PAGE - 1); i ++)
|
|
{
|
|
if( pGroupEntry->gid )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pGroupEntry++;
|
|
}
|
|
}
|
|
|
|
// there is no item found on this page
|
|
if( !pGroupEntry->gid && i == GROUPS_PER_PAGE - 1 )
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
|
|
|
|
return fRet;
|
|
}
|
|
|
|
BOOL
|
|
GroupMgr::IsLastPage(GROUP_ENTRY* pHead)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
GROUP_ENTRY* pEnd = NULL;
|
|
|
|
// jump to last item
|
|
pEnd = pHead + GROUPS_PER_PAGE;
|
|
|
|
//
|
|
// the gid has to be marked as GID_INDEX_TO_NEXT_PAGE
|
|
// for index entry, and if the dwGroupFlags is 0,
|
|
// that means we are not pointing to any
|
|
// other page, this is the last page indeed.
|
|
//
|
|
if( pEnd->gid == GID_INDEX_TO_NEXT_PAGE && !pEnd->dwGroupFlags )
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GroupMgr::FreeEmptyPages(DWORD dwFlags)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
BOOL fMustUnlock;
|
|
|
|
BOOL fRet = TRUE;
|
|
GROUP_ENTRY* pHead = NULL;
|
|
GROUP_ENTRY* pPrevHead = NULL;
|
|
GROUP_ENTRY* pEnd = NULL;
|
|
GROUP_ENTRY* pTobeDeleted = NULL;
|
|
BOOL fFirstPage = TRUE;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// BUGBUG FindRootEntry changed the return code, check for dwError
|
|
if( FindRootEntry(&pHead, FALSE ) )
|
|
{
|
|
pPrevHead = pHead;
|
|
while(pHead)
|
|
{
|
|
pTobeDeleted = NULL;
|
|
|
|
if( IsPageEmpty(pHead) )
|
|
{
|
|
pTobeDeleted = pHead;
|
|
|
|
//
|
|
// find the offset of the next page
|
|
// 0 which means the current page is the last one
|
|
//
|
|
DWORD dwOffsetNextPage = 0;
|
|
pEnd = pHead + GROUPS_PER_PAGE;
|
|
dwOffsetNextPage = pEnd->dwGroupFlags;
|
|
|
|
//
|
|
// if the first page is to be deleted, we have to
|
|
// update the offset which points to the next page
|
|
//
|
|
if( fFirstPage)
|
|
{
|
|
if( !SetHeaderData(
|
|
CACHE_HEADER_DATA_ROOTGROUP_OFFSET, dwOffsetNextPage))
|
|
{
|
|
fRet = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Link Prev page to Next page
|
|
//
|
|
GROUP_ENTRY* pPrevEnd = pPrevHead + GROUPS_PER_PAGE;
|
|
pPrevEnd->dwGroupFlags = dwOffsetNextPage;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// update pHead make it point to the next page
|
|
//
|
|
if( !IsLastPage(pHead) )
|
|
{
|
|
// remember pPrev
|
|
pPrevHead = pHead;
|
|
|
|
// walk to next page
|
|
pEnd = pHead + GROUPS_PER_PAGE;
|
|
pHead = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart()
|
|
+ pEnd->dwGroupFlags );
|
|
|
|
// not first page anymore
|
|
fFirstPage = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// this is the last page
|
|
pHead = NULL;
|
|
}
|
|
|
|
//
|
|
// free the tobe deleted page
|
|
//
|
|
if( pTobeDeleted )
|
|
{
|
|
GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
|
|
pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*) ((LPBYTE)pTobeDeleted - sizeof(FILEMAP_ENTRY));
|
|
|
|
_pContainer->_UrlObjStorage->FreeEntry(pPage);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::FindDataEntry(
|
|
GROUP_ENTRY* pGroupEntry,
|
|
GROUP_DATA_ENTRY** pOutData,
|
|
BOOL fCreate
|
|
)
|
|
{
|
|
INET_ASSERT(_pContainer);
|
|
INET_ASSERT(pGroupEntry && pOutData );
|
|
*pOutData = NULL;
|
|
|
|
BOOL fMustUnlock;
|
|
DWORD dwError;
|
|
LPBYTE lpbBase = NULL;
|
|
|
|
if( !_pContainer->LockContainer(&fMustUnlock) )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto exit;
|
|
}
|
|
|
|
if( pGroupEntry->dwGroupNameOffset )
|
|
{
|
|
lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
*pOutData = (GROUP_DATA_ENTRY*) (lpbBase + pGroupEntry->dwGroupNameOffset);
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
|
|
else if( fCreate)
|
|
{
|
|
//////////////////////////////////////////////////////////////////
|
|
// BEGIN WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
// remember the old offset for pGroupEntry
|
|
DWORD_PTR dwpEntryOffset = PtrDifference(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
|
|
|
|
// create new data entry
|
|
*pOutData = GetHeadDataEntry(TRUE);
|
|
if( *pOutData )
|
|
{
|
|
//
|
|
// re-calc pGroupEntry
|
|
//
|
|
lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwpEntryOffset);
|
|
|
|
//
|
|
// set entry's filename offset field
|
|
//
|
|
pGroupEntry->dwGroupNameOffset = PtrDiff32(*pOutData, lpbBase);
|
|
|
|
// succeed
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
}
|
|
//////////////////////////////////////////////////////////////////
|
|
// END WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
else
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
exit:
|
|
if( fMustUnlock )
|
|
{
|
|
_pContainer->UnlockContainer();
|
|
}
|
|
|
|
if( fCreate && (dwError == ERROR_SUCCESS) )
|
|
{
|
|
// for new item, it's nice to mark the next link to 0
|
|
(*pOutData)->dwOffsetNext = 0;
|
|
}
|
|
return dwError;
|
|
}
|
|
|
|
|
|
VOID
|
|
GroupMgr::FreeDataEntry(GROUP_DATA_ENTRY* pDataEntry)
|
|
{
|
|
// get the head entry
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
DWORD dwError = FindRootEntry(&pGroupEntry, FALSE );
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// walk to the index item whose dwGroupNameOffset
|
|
// contains offset the the head of free list
|
|
//
|
|
pGroupEntry += (GROUPS_PER_PAGE - 1);
|
|
INET_ASSERT( pGroupEntry->gid == GID_INDEX_TO_NEXT_PAGE);
|
|
|
|
// memset the freed data entry
|
|
memset(pDataEntry, 0, sizeof(GROUP_DATA_ENTRY) );
|
|
|
|
// make data item's next link points to current head
|
|
pDataEntry->dwOffsetNext = pGroupEntry->dwGroupNameOffset;
|
|
|
|
// make the current head to be the just freed item's offset
|
|
LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pGroupEntry->dwGroupNameOffset = PtrDiff32(pDataEntry, lpbBase);
|
|
}
|
|
|
|
|
|
LPGROUP_DATA_ENTRY
|
|
GroupMgr::GetHeadDataEntry(BOOL fCreate)
|
|
{
|
|
GROUP_DATA_ENTRY* pDataEntry = NULL;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
LPBYTE lpbBase = NULL;
|
|
|
|
// get the head entry
|
|
DWORD dwError = FindRootEntry(&pGroupEntry, FALSE );
|
|
if( dwError != ERROR_SUCCESS )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// walk to the index item
|
|
pGroupEntry += (GROUPS_PER_PAGE - 1);
|
|
INET_ASSERT( pGroupEntry->gid == GID_INDEX_TO_NEXT_PAGE);
|
|
|
|
// the dwGroupNameOffset contains offset the the head of free list
|
|
if( pGroupEntry->dwGroupNameOffset)
|
|
{
|
|
// get the head
|
|
lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pDataEntry = (GROUP_DATA_ENTRY*) (lpbBase + pGroupEntry->dwGroupNameOffset);
|
|
|
|
// reset head to next one
|
|
pGroupEntry->dwGroupNameOffset = pDataEntry->dwOffsetNext;
|
|
}
|
|
|
|
else if( fCreate )
|
|
{
|
|
//////////////////////////////////////////////////////////////////
|
|
// BEGIN WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// remember the old offset for pGroupEntry
|
|
DWORD_PTR dwpEntryOffset = PtrDifference(pGroupEntry, *_pContainer->_UrlObjStorage->GetHeapStart());
|
|
|
|
// create a new page
|
|
GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
|
|
DWORD cbSize = sizeof(GROUPS_ALLOC_FILEMAP_ENTRY);
|
|
|
|
pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*)
|
|
_pContainer->_UrlObjStorage->AllocateEntry(cbSize);
|
|
|
|
if( !pPage )
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
// memset
|
|
memset(pPage->pGroupBlock, 0, PAGE_SIZE_FOR_GROUPS);
|
|
|
|
lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
GROUP_DATA_ENTRY* pHead = (GROUP_DATA_ENTRY*)pPage->pGroupBlock;
|
|
pDataEntry = pHead;
|
|
|
|
// init list on the newly created page
|
|
for(int i = 0; i < GROUPS_DATA_PER_PAGE - 1; i++)
|
|
{
|
|
// point to next offset
|
|
GROUP_DATA_ENTRY* pNext = pHead + 1;
|
|
pHead->dwOffsetNext = PtrDiff32(pNext, lpbBase);
|
|
pHead = pNext;
|
|
}
|
|
|
|
//
|
|
// pGroupEntry needs to be re-calc!
|
|
//
|
|
pGroupEntry = (GROUP_ENTRY*)(lpbBase + dwpEntryOffset);
|
|
|
|
//
|
|
// pGroupEntry currently is the index entry of the first
|
|
// page, it's dwGroupNameOffset field points the head of
|
|
// the list of a free group data entry
|
|
//
|
|
pGroupEntry->dwGroupNameOffset = pDataEntry->dwOffsetNext;
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// END WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
}
|
|
|
|
else
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return pDataEntry;
|
|
}
|
|
|
|
DWORD
|
|
GroupMgr::GetOffsetFromList(DWORD dwHeadOffset, GROUPID gid, DWORD* pdwOffset)
|
|
{
|
|
DWORD dwError;
|
|
LIST_GROUP_ENTRY* pListGroup = NULL;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
|
|
*pdwOffset = 0;
|
|
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
|
|
if( !pListGroup )
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
while(1)
|
|
{
|
|
|
|
if(!_pContainer->_UrlObjStorage->IsBadGroupOffset(pListGroup->dwGroupOffset))
|
|
{
|
|
pGroupEntry = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart()
|
|
+ pListGroup->dwGroupOffset );
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( pGroupEntry && pGroupEntry->gid == gid )
|
|
{
|
|
*pdwOffset = pListGroup->dwGroupOffset;
|
|
break;
|
|
}
|
|
|
|
// end of list, not found
|
|
if( !pListGroup->dwNext )
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
}
|
|
|
|
// walk to next
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(
|
|
pListGroup->dwNext);
|
|
|
|
if( !pListGroup )
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if( *pdwOffset )
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::CreateNewGroupList(DWORD* pdwHeadOffset)
|
|
{
|
|
DWORD dwError;
|
|
|
|
// Find empty slot
|
|
*pdwHeadOffset = 0;
|
|
dwError = FindEmptySlotInListPage(pdwHeadOffset);
|
|
if( ERROR_SUCCESS != dwError )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
DWORD
|
|
GroupMgr::AddToGroupList(DWORD dwHeadOffset, DWORD dwOffset)
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwEmptySlot;
|
|
LIST_GROUP_ENTRY* pListGroup = NULL;
|
|
LIST_GROUP_ENTRY* pListGroupEmpty = NULL;
|
|
|
|
// if the item already on the list, return success
|
|
if( IsGroupOnList(dwHeadOffset, dwOffset) )
|
|
{
|
|
dwError = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
|
|
if( !pListGroup )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( !pListGroup->dwGroupOffset )
|
|
{
|
|
// list is empty, just need to fill up the Head
|
|
pListGroup->dwGroupOffset = dwOffset;
|
|
}
|
|
else
|
|
{
|
|
// List is not empty, we have to walk to end of the list
|
|
// also need to get another empty slot
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// BEGIN WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
// remember the old offset for pListGroup
|
|
DWORD_PTR dwpListGroupOffset = PtrDifference(pListGroup, *_pContainer->_UrlObjStorage->GetHeapStart());
|
|
|
|
// find empty slot
|
|
dwError = FindEmptySlotInListPage(&dwEmptySlot);
|
|
if( ERROR_SUCCESS != dwError )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// recalculate pListGroup using the offset remembered
|
|
LPBYTE lpbBase = *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pListGroup = (LIST_GROUP_ENTRY*)(lpbBase + dwpListGroupOffset);
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// END WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
// walk to end of list
|
|
while( pListGroup->dwNext )
|
|
{
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(
|
|
pListGroup->dwNext);
|
|
if( !pListGroup )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
// Get ListGroupEmpty Object from the empty slot
|
|
pListGroupEmpty =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwEmptySlot);
|
|
if( !pListGroupEmpty )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
// assign the new offset
|
|
pListGroupEmpty->dwGroupOffset = dwOffset;
|
|
|
|
// append empty slot at the end of the list
|
|
// this need to be done at last to prevent some invalid
|
|
// object get on the list
|
|
pListGroup->dwNext = dwEmptySlot;
|
|
}
|
|
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
DWORD
|
|
GroupMgr::RemoveFromGroupList(
|
|
DWORD dwHeadOffset,
|
|
DWORD dwOffset,
|
|
LPDWORD pdwNewHeadOffset
|
|
)
|
|
{
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
LIST_GROUP_ENTRY* pListGroup = NULL;
|
|
LIST_GROUP_ENTRY* pListGroupPrev = NULL;
|
|
LPBYTE lpbBase = NULL;
|
|
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
|
|
if( !pListGroup )
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
|
|
// header is the one we need, we will have to assign new header
|
|
if( pListGroup->dwGroupOffset == dwOffset )
|
|
{
|
|
// new head
|
|
*pdwNewHeadOffset = pListGroup->dwNext;
|
|
|
|
// empty removed head and added to free list
|
|
pListGroup->dwGroupOffset = 0;
|
|
pListGroup->dwNext= 0;
|
|
AddToFreeList(pListGroup);
|
|
|
|
// done
|
|
dwError = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( !pListGroup->dwNext )
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pListGroupPrev = pListGroup;
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(pListGroup->dwNext);
|
|
if( !pListGroup)
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
while( pListGroup )
|
|
{
|
|
INET_ASSERT(pListGroup->dwGroupOffset);
|
|
|
|
if( pListGroup->dwGroupOffset == dwOffset )
|
|
{
|
|
pListGroupPrev->dwNext = pListGroup->dwNext;
|
|
|
|
// empty removed item and added it to free list
|
|
pListGroup->dwGroupOffset = 0;
|
|
pListGroup->dwNext= 0;
|
|
AddToFreeList(pListGroup);
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
if( pListGroup->dwNext )
|
|
{
|
|
pListGroupPrev = pListGroup;
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(pListGroup->dwNext);
|
|
}
|
|
else
|
|
{
|
|
dwError = ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
DWORD
|
|
GroupMgr::FindEmptySlotInListPage(DWORD* pdwOffsetToSlot)
|
|
{
|
|
|
|
DWORD dwError;
|
|
DWORD dwOffsetRoot = 0;
|
|
LPBYTE lpbBase = NULL;
|
|
LIST_GROUP_ENTRY* pListGroupFreeHead = NULL;
|
|
LIST_GROUP_ENTRY* pListGroupEmpty = NULL;
|
|
|
|
if( !GetHeaderData( CACHE_HEADER_DATA_ROOT_GROUPLIST_OFFSET, &dwOffsetRoot))
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( !dwOffsetRoot)
|
|
{
|
|
// new page needs to be created
|
|
dwError = CreateNewListPage(&dwOffsetRoot, TRUE);
|
|
|
|
if( dwError != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// At this point, we've got the root entry
|
|
// 1. retrieved valid dwOffsetToRootEntry or
|
|
// 2. get the new dwOffsetToRootEntry via CreateNewPage() call
|
|
//
|
|
INET_ASSERT( dwOffsetRoot);
|
|
|
|
lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pListGroupFreeHead = (LIST_GROUP_ENTRY*) (lpbBase + dwOffsetRoot);
|
|
if( !pListGroupFreeHead )
|
|
{
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// BEGIN WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
// get the next free item from the list
|
|
if( !pListGroupFreeHead->dwNext )
|
|
{
|
|
// no free slot left!, let's create a new page!
|
|
|
|
// remember the old offset free list head entry
|
|
DWORD_PTR dwpFreeHeadOffset = PtrDifference(pListGroupFreeHead, lpbBase);
|
|
|
|
// create a new page
|
|
DWORD dwNewList;
|
|
dwError = CreateNewListPage(&dwNewList, FALSE);
|
|
|
|
if( dwError != ERROR_SUCCESS)
|
|
goto Cleanup;
|
|
|
|
// restore
|
|
lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
pListGroupFreeHead = (LIST_GROUP_ENTRY*) (lpbBase + dwpFreeHeadOffset);
|
|
//
|
|
// add the newly created page contains a list of empty
|
|
// slot (already chained together), now update the head
|
|
// of free list pointing to the head of the newly created
|
|
// list
|
|
//
|
|
pListGroupFreeHead->dwNext = dwNewList;
|
|
}
|
|
//////////////////////////////////////////////////////////////////
|
|
// END WARNING: The file might be grown and remapped, so all //
|
|
// pointers into the file before this point may be invalidated. //
|
|
//////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// get the empty slot offset
|
|
*pdwOffsetToSlot = pListGroupFreeHead->dwNext;
|
|
|
|
// update the free list to point to the next slot
|
|
pListGroupEmpty = (LIST_GROUP_ENTRY*)(lpbBase + pListGroupFreeHead->dwNext);
|
|
pListGroupFreeHead->dwNext = pListGroupEmpty->dwNext;
|
|
|
|
memset(pListGroupEmpty, 0, sizeof(LIST_GROUP_ENTRY) );
|
|
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GroupMgr::CreateNewListPage(DWORD* pdwOffsetToFirstEntry, BOOL fIsFirstPage)
|
|
{
|
|
DWORD dwError;
|
|
GROUPS_ALLOC_FILEMAP_ENTRY* pPage = NULL;
|
|
DWORD cbSize = sizeof(GROUPS_ALLOC_FILEMAP_ENTRY);
|
|
|
|
pPage = (GROUPS_ALLOC_FILEMAP_ENTRY*)
|
|
_pContainer->_UrlObjStorage->AllocateEntry(cbSize);
|
|
|
|
|
|
if( pPage )
|
|
{
|
|
// clean up allocated page
|
|
cbSize = PAGE_SIZE_FOR_GROUPS;
|
|
memset(pPage->pGroupBlock, 0, cbSize );
|
|
|
|
// calculate the group base offset
|
|
LPBYTE lpbBase = (LPBYTE) *_pContainer->_UrlObjStorage->GetHeapStart();
|
|
|
|
*pdwOffsetToFirstEntry = PtrDiff32(pPage->pGroupBlock, lpbBase);
|
|
|
|
//
|
|
// chain all items together
|
|
// (Last item will have dwNext == 0 since we have alredy memset
|
|
// the whole page )
|
|
//
|
|
LIST_GROUP_ENTRY* pList = (LIST_GROUP_ENTRY*) pPage->pGroupBlock;
|
|
|
|
for( DWORD dwi = 0; dwi < (LIST_GROUPS_PER_PAGE -1); dwi++)
|
|
{
|
|
pList->dwNext = PtrDiff32(pList+1, lpbBase);
|
|
pList++ ;
|
|
}
|
|
|
|
|
|
if( fIsFirstPage )
|
|
{
|
|
//
|
|
// for first page, we would have to set the offset
|
|
// back to the CacheHeader
|
|
//
|
|
if( !SetHeaderData(
|
|
CACHE_HEADER_DATA_ROOT_GROUPLIST_OFFSET,
|
|
*pdwOffsetToFirstEntry))
|
|
{
|
|
// free allocated page
|
|
_pContainer->_UrlObjStorage->FreeEntry(pPage);
|
|
|
|
// set error and go
|
|
*pdwOffsetToFirstEntry = 0;
|
|
dwError = ERROR_INTERNET_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
|
|
} // IF: failed to set the offset
|
|
}
|
|
|
|
// return the offset to the first entry of the new page
|
|
dwError = ERROR_SUCCESS;
|
|
|
|
} // IF: Allocate new page succeed
|
|
|
|
else
|
|
{
|
|
dwError = ERROR_NOT_ENOUGH_MEMORY;
|
|
} // ELSE: failed to allocate new page
|
|
|
|
Cleanup:
|
|
return dwError;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
GroupMgr::IsGroupOnList(DWORD dwHeadOffset, DWORD dwGrpOffset)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
LIST_GROUP_ENTRY* pListGroup = NULL;
|
|
|
|
LIST_GROUP_ENTRY *pMilestone = NULL; // used for detecting cycles
|
|
unsigned long dwNodeCount = 1;
|
|
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
|
|
if( !pListGroup )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
while( pListGroup )
|
|
{
|
|
//INET_ASSERT(pListGroup->dwGroupOffset);
|
|
|
|
if( pListGroup->dwGroupOffset == dwGrpOffset )
|
|
{
|
|
fRet = TRUE;
|
|
break;
|
|
}
|
|
|
|
if( pListGroup->dwNext )
|
|
{
|
|
LIST_GROUP_ENTRY* plgTemp =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(
|
|
pListGroup->dwNext);
|
|
|
|
// Sometimes the list is corrupted and contains a cycle
|
|
// This is detected by comparing against the saved pointer
|
|
// (Revisiting an earlier milestone indicates a cycle)
|
|
if (plgTemp==pMilestone)
|
|
break;
|
|
|
|
// Also check (and fix) simple self-loops
|
|
if (plgTemp==pListGroup)
|
|
{
|
|
pListGroup->dwNext = 0;
|
|
break;
|
|
}
|
|
|
|
// Advance to next node
|
|
pListGroup = plgTemp;
|
|
|
|
// Choose new milestone when node count is power of 2
|
|
dwNodeCount++;
|
|
|
|
if ((dwNodeCount & (dwNodeCount-1)) == 0)
|
|
pMilestone = pListGroup;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
}
|
|
|
|
|
|
BOOL
|
|
GroupMgr::NoMoreStickyEntryOnList(DWORD dwHeadOffset)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
LIST_GROUP_ENTRY* pListGroup = NULL;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
|
|
if( !pListGroup )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
while( pListGroup )
|
|
{
|
|
//INET_ASSERT(pListGroup->dwGroupOffset);
|
|
|
|
// get the GroupEntry structure
|
|
if( !_pContainer->_UrlObjStorage->IsBadGroupOffset(
|
|
pListGroup->dwGroupOffset) )
|
|
{
|
|
pGroupEntry = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart() +
|
|
pListGroup->dwGroupOffset );
|
|
|
|
// IsSticky?
|
|
if( IsStickyGroup(pGroupEntry->gid) )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
// end of list
|
|
if( !pListGroup->dwNext )
|
|
{
|
|
break;
|
|
}
|
|
|
|
// next item on list
|
|
pListGroup = _pContainer->_UrlObjStorage->ValidateListGroupOffset(
|
|
pListGroup->dwNext);
|
|
}
|
|
|
|
//
|
|
// reach here means we are at end of the list and can not find
|
|
// any sticky group, return TRUE
|
|
//
|
|
fRet = TRUE;
|
|
|
|
Cleanup:
|
|
return fRet;
|
|
|
|
|
|
}
|
|
|
|
|
|
void
|
|
GroupMgr::AdjustUsageOnList(DWORD dwHeadOffset, LONGLONG llDelta)
|
|
{
|
|
LIST_GROUP_ENTRY* pListGroup = NULL;
|
|
GROUP_ENTRY* pGroupEntry = NULL;
|
|
|
|
pListGroup =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwHeadOffset);
|
|
if( !pListGroup )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
while( pListGroup )
|
|
{
|
|
// INET_ASSERT(pListGroup->dwGroupOffset);
|
|
|
|
// get the GroupEntry structure
|
|
if( !_pContainer->_UrlObjStorage->IsBadGroupOffset(
|
|
pListGroup->dwGroupOffset) )
|
|
{
|
|
pGroupEntry = (GROUP_ENTRY*)
|
|
( *_pContainer->_UrlObjStorage->GetHeapStart() +
|
|
pListGroup->dwGroupOffset );
|
|
|
|
// AdjustUsage
|
|
_pContainer->AdjustGroupUsage(pGroupEntry, llDelta);
|
|
}
|
|
|
|
|
|
// end of list
|
|
if( !pListGroup->dwNext )
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// next item on list
|
|
pListGroup = _pContainer->_UrlObjStorage->ValidateListGroupOffset(
|
|
pListGroup->dwNext);
|
|
}
|
|
|
|
Cleanup:
|
|
return;
|
|
|
|
}
|
|
|
|
void
|
|
GroupMgr::AddToFreeList(LIST_GROUP_ENTRY* pFreeListGroup)
|
|
{
|
|
DWORD dwOffsetRoot = 0;
|
|
LIST_GROUP_ENTRY* pFreeListHead = NULL;
|
|
|
|
if( GetHeaderData( CACHE_HEADER_DATA_ROOT_GROUPLIST_OFFSET, &dwOffsetRoot))
|
|
{
|
|
|
|
pFreeListHead =
|
|
_pContainer->_UrlObjStorage->ValidateListGroupOffset(dwOffsetRoot);
|
|
|
|
if( pFreeListHead && pFreeListGroup )
|
|
{
|
|
pFreeListGroup->dwNext = pFreeListHead->dwNext;
|
|
|
|
pFreeListHead->dwNext = PtrDiff32(pFreeListGroup,
|
|
*_pContainer->_UrlObjStorage->GetHeapStart());
|
|
}
|
|
}
|
|
return;
|
|
}
|