mirror of https://github.com/lianthony/NT4.0
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.
2536 lines
47 KiB
2536 lines
47 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
contain.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains code that implements CONTAINER classes defined in
|
|
contain.hxx.
|
|
|
|
Author:
|
|
|
|
Madan Appiah (madana) 28-Dec-1994
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <cache.hxx>
|
|
|
|
DWORD
|
|
GetFileSizeByName(
|
|
LPCWSTR FileName,
|
|
LONGLONG *FileSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the size of the specified file.
|
|
|
|
Arguments:
|
|
|
|
FileName : full path name of the file whose size is asked for.
|
|
|
|
FileSize : pointer to a longlong location where the size value is
|
|
returned.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
HANDLE FileHandle;
|
|
ULARGE_INTEGER LocalFileSize;
|
|
|
|
//
|
|
// get the size of the file being cached.
|
|
//
|
|
|
|
FileHandle = CreateFileW(
|
|
FileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if( FileHandle == INVALID_HANDLE_VALUE ) {
|
|
|
|
return( GetLastError() );
|
|
}
|
|
|
|
LocalFileSize.LowPart = GetFileSize( FileHandle, &LocalFileSize.HighPart );
|
|
|
|
CloseHandle( FileHandle );
|
|
|
|
if( LocalFileSize.LowPart == 0xFFFFFFFF ) {
|
|
|
|
DWORD Error = GetLastError();
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
return( Error );;
|
|
}
|
|
}
|
|
|
|
*FileSize = LocalFileSize.QuadPart;
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
VOID
|
|
MakeRandomFileName(
|
|
LPCSTR UrlName,
|
|
LPWSTR FileName,
|
|
LPWSTR Extension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a random 8.3 file name. The format of the name will be as
|
|
below:
|
|
|
|
ca(0-99999).(0-999)
|
|
|
|
Ex ca19200.340
|
|
Ex ca19354.tmp - if an extension (tmp) is specified.
|
|
|
|
Arguments:
|
|
|
|
UrlName : pointer to an URL string (unused for now)
|
|
|
|
FileName : pointer to a string buffer where the random file name is
|
|
returned. The buffer length must be atleast 8+3+1+1= 13 wchars.
|
|
|
|
Extension : pointer to an extension string. if this is non-NULL, then
|
|
the specified extension is used otherwise random extension as
|
|
explained above is used.
|
|
|
|
Return Value:
|
|
|
|
none.
|
|
|
|
--*/
|
|
{
|
|
DWORD RandomNum;
|
|
LPWSTR FileNamePtr = FileName;
|
|
DWORD i;
|
|
|
|
*FileNamePtr++ = L'C';
|
|
*FileNamePtr++ = L'A';
|
|
|
|
//
|
|
// generate a 5 digit random number.
|
|
//
|
|
|
|
RandomNum = (DWORD)(rand() % 100000);
|
|
|
|
for ( i = 0; i < 5; i++) {
|
|
*FileNamePtr++ = (WCHAR)(L'0' + RandomNum % 10);
|
|
RandomNum /= 10;
|
|
}
|
|
|
|
*FileNamePtr++ = L'.';
|
|
|
|
//
|
|
// if an extension is specified, use it.
|
|
//
|
|
|
|
if( Extension != NULL ) {
|
|
|
|
*FileNamePtr++ = *Extension++;
|
|
*FileNamePtr++ = *Extension++;
|
|
*FileNamePtr++ = *Extension++;
|
|
*FileNamePtr = L'\0';
|
|
return;
|
|
}
|
|
|
|
//
|
|
// generate a three digit random number;
|
|
//
|
|
|
|
RandomNum = (DWORD)(rand() % 1000);
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
*FileNamePtr++ = (WCHAR)(L'0' + RandomNum % 10);
|
|
RandomNum /= 10;
|
|
}
|
|
|
|
*FileNamePtr = L'\0';
|
|
return;
|
|
}
|
|
|
|
DWORD
|
|
CreateUniqueFile(
|
|
LPCSTR UrlName,
|
|
LPWSTR Path,
|
|
LPWSTR FileName,
|
|
LPWSTR Extension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
UrlName : pointer to url name.
|
|
|
|
Path : pointer to cache path.
|
|
|
|
FileName : pointer to a buffer that receives the full path name of the
|
|
newly created file.
|
|
|
|
Extension : if specified the extension is used to make random file.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
WCHAR RandomFileName[8 + 1 + 3 + 1];
|
|
WCHAR FullFileName[MAX_PATH];
|
|
HANDLE FileHandle;
|
|
|
|
for(;;) {
|
|
|
|
//
|
|
// make a random file name.
|
|
//
|
|
|
|
MakeRandomFileName( UrlName, RandomFileName, Extension );
|
|
|
|
//
|
|
// make full path name.
|
|
//
|
|
|
|
wcscpy( FullFileName, Path );
|
|
wcscat( FullFileName, RandomFileName );
|
|
|
|
//
|
|
// check this file exists.
|
|
//
|
|
|
|
FileHandle = CreateFileW(
|
|
FullFileName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
CREATE_NEW,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL );
|
|
|
|
if( FileHandle != INVALID_HANDLE_VALUE ) {
|
|
|
|
//
|
|
// successfully create a new file close it and return.
|
|
//
|
|
|
|
CloseHandle( FileHandle );
|
|
break;
|
|
}
|
|
|
|
Error = GetLastError();
|
|
|
|
if( Error != ERROR_FILE_EXISTS ) {
|
|
return( Error );
|
|
}
|
|
|
|
//
|
|
// try another random file.
|
|
//
|
|
}
|
|
|
|
wcscpy( FileName, FullFileName );
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
LONGLONG
|
|
GetGmtTime(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the current system time as LONGLONG data.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
LONGLONG system time..
|
|
|
|
--*/
|
|
{
|
|
SYSTEMTIME SystemTime;
|
|
FILETIME Time;
|
|
|
|
GetSystemTime( &SystemTime );
|
|
SystemTimeToFileTime( &SystemTime, &Time );
|
|
|
|
return( *((LONGLONG *)(&Time)) );
|
|
}
|
|
|
|
INLINE
|
|
SORTED_CONTAINER::SORTED_CONTAINER(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function constructs an SORTED_CONTAINER object.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
_RootEntry = NULL;
|
|
return;
|
|
}
|
|
|
|
INT
|
|
SORTED_CONTAINER::CompareElements(
|
|
DWORD Element1,
|
|
DWORD Element2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member virtual function implements the default compare
|
|
function, which compares two DWORD data.
|
|
|
|
Arguments:
|
|
|
|
Element1 : First DWORD data.
|
|
|
|
Element2 : Second DWORD data.
|
|
|
|
Return Value:
|
|
|
|
0 - if Element1 and Element2 are equal.
|
|
1 - if Element1 is greater than Element2.
|
|
-1 - if Element1 is less than Element2.
|
|
|
|
--*/
|
|
{
|
|
if( Element1 == Element2 ) {
|
|
return( 0 );
|
|
}
|
|
|
|
if( Element1 < Element2 ) {
|
|
return( -1 );
|
|
}
|
|
|
|
return( 1 );
|
|
}
|
|
|
|
BOOL
|
|
SORTED_CONTAINER::AddEntry(
|
|
DWORD NewElement
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function adds an object to the sorted container.
|
|
|
|
Arguments:
|
|
|
|
NewElement : new object.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the new object is successfully added to the container.
|
|
|
|
FALSE : if the new object can't be added to the container when the
|
|
system runs out of memory.
|
|
|
|
--*/
|
|
{
|
|
LPTREE_ENTRY NewEntry;
|
|
LPTREE_ENTRY *CurrentNode;
|
|
|
|
//
|
|
// start off from root.
|
|
//
|
|
|
|
CurrentNode = &_RootEntry;
|
|
|
|
while( *CurrentNode != NULL ) {
|
|
|
|
INT CompareResult;
|
|
|
|
CompareResult = CompareElements(
|
|
(*CurrentNode)->Element,
|
|
NewElement );
|
|
|
|
if( CompareResult == 0 ) {
|
|
return( TRUE );
|
|
}
|
|
|
|
if( CompareResult < 0 ) {
|
|
|
|
CurrentNode = &((*CurrentNode)->Left);
|
|
}
|
|
else {
|
|
CurrentNode = &((*CurrentNode)->Right);
|
|
}
|
|
}
|
|
|
|
//
|
|
// allocate memory for the new entry.
|
|
//
|
|
|
|
NewEntry = (LPTREE_ENTRY)CacheHeap->Alloc( sizeof(TREE_ENTRY) );
|
|
|
|
if( NewEntry == NULL ) {
|
|
return( FALSE );
|
|
}
|
|
|
|
NewEntry->Left = NULL;
|
|
NewEntry->Right = NULL;
|
|
NewEntry->Element = NewElement;
|
|
|
|
//
|
|
// now hook up the new entry.
|
|
//
|
|
|
|
*CurrentNode = NewEntry;
|
|
return( TRUE );
|
|
}
|
|
|
|
BOOL
|
|
SORTED_CONTAINER::FindEntry(
|
|
DWORD Element,
|
|
LPDWORD FoundElement
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function searches for the given object and returns
|
|
the object pointer.
|
|
|
|
Arguments:
|
|
|
|
Element : object to be searched in the container.
|
|
|
|
FoundElement : pointer to a location where the found object pointer
|
|
is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the object is found.
|
|
|
|
FALSE : otherwise
|
|
|
|
--*/
|
|
{
|
|
LPTREE_ENTRY CurrentNode;
|
|
|
|
CurrentNode = _RootEntry;
|
|
|
|
while( CurrentNode != NULL ) {
|
|
|
|
INT CompareResult;
|
|
|
|
CompareResult = CompareElements( CurrentNode->Element, Element );
|
|
|
|
if( CompareResult == 0 ) {
|
|
*FoundElement = CurrentNode->Element;
|
|
return( TRUE );
|
|
}
|
|
|
|
if( CompareResult < 0 ) {
|
|
CurrentNode = CurrentNode->Left;
|
|
}
|
|
else {
|
|
CurrentNode = CurrentNode->Right;
|
|
}
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
VOID
|
|
SORTED_CONTAINER::DeleteEntry(
|
|
DWORD Element
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function deletes an object from the sorted container.
|
|
|
|
Arguments:
|
|
|
|
Element : object to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LPTREE_ENTRY *PreviousNode;
|
|
LPTREE_ENTRY CurrentNode;
|
|
|
|
PreviousNode = &_RootEntry;
|
|
CurrentNode = _RootEntry;
|
|
|
|
while( CurrentNode != NULL ) {
|
|
|
|
INT CompareResult;
|
|
|
|
CompareResult = CompareElements( CurrentNode->Element, Element );
|
|
|
|
if( CompareResult == 0 ) {
|
|
|
|
//
|
|
// found the entry we need to delete.
|
|
//
|
|
|
|
LPTREE_ENTRY Left = CurrentNode->Left;
|
|
LPTREE_ENTRY Right = CurrentNode->Right;
|
|
|
|
//
|
|
// free this entry.
|
|
//
|
|
|
|
CacheHeap->Free( (PVOID)CurrentNode );
|
|
|
|
//
|
|
// we don't have anything on right, move the left tree up.
|
|
//
|
|
|
|
if( Right == NULL ) {
|
|
|
|
*PreviousNode = Left;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// if the left tree is not empty, hook the left tree to the
|
|
// left most bottom of right tree.
|
|
//
|
|
|
|
if( Left != NULL ) {
|
|
|
|
LPTREE_ENTRY Temp = Right;
|
|
|
|
while( Temp->Left != NULL ) {
|
|
Temp = Temp->Left;
|
|
}
|
|
|
|
Temp->Left = Left;
|
|
}
|
|
|
|
*PreviousNode = Right;
|
|
return;
|
|
}
|
|
|
|
if( CompareResult < 0 ) {
|
|
|
|
PreviousNode = &(CurrentNode->Left);
|
|
CurrentNode = CurrentNode->Left;
|
|
}
|
|
else {
|
|
PreviousNode = &(CurrentNode->Right);
|
|
CurrentNode = CurrentNode->Right;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BOOL
|
|
SORTED_CONTAINER::FindFirstFromTree(
|
|
LPTREE_ENTRY Root,
|
|
LPDWORD FirstElement
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function returns the first object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
Root : pointer to the root node of the binary tree.
|
|
|
|
FirstElement : pointer to a location where the pointer to the first
|
|
object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the first element is successfully found.
|
|
|
|
FALSE : if the list is empty.
|
|
|
|
--*/
|
|
{
|
|
LPTREE_ENTRY Temp = Root;
|
|
|
|
if( Temp == NULL ) {
|
|
return( FALSE );
|
|
}
|
|
|
|
while( Temp->Left != NULL ) {
|
|
Temp = Temp->Left;
|
|
}
|
|
|
|
*FirstElement = Temp->Element;
|
|
return( TRUE );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_CONTAINER::FindFirst(
|
|
LPDWORD FirstElement
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function returns the first object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
FirstElement : pointer to a location where the pointer to the first
|
|
object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the first element is successfully found.
|
|
|
|
FALSE : if the list is empty.
|
|
|
|
--*/
|
|
{
|
|
return( FindFirstFromTree( _RootEntry, FirstElement ) );
|
|
}
|
|
|
|
BOOL
|
|
SORTED_CONTAINER::FindNextFromTree(
|
|
LPTREE_ENTRY Root,
|
|
DWORD LastElement,
|
|
LPDWORD NextElement,
|
|
LPBOOL NoMoreElement
|
|
)
|
|
/*++
|
|
|
|
This member function returns the next object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
Root : pointer to the root node of the binary tree.
|
|
|
|
LastElement : Last object that was returuned either by the FindFirst
|
|
or FindNext calls.
|
|
|
|
NextElement : pointer to a location where the pointer to the next
|
|
object is returned.
|
|
|
|
NoMoreElement : pointer to a location where TRUE is returned if
|
|
there is no more object in the container otherwise FALSE is
|
|
returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the next element is successfully found.
|
|
|
|
FALSE : Otherwise.
|
|
|
|
--*/
|
|
{
|
|
INT CompareResult;
|
|
BOOL Result;
|
|
|
|
//
|
|
// empty tree.
|
|
//
|
|
|
|
if( Root == NULL ) {
|
|
return( FALSE );
|
|
}
|
|
|
|
CompareResult = CompareElements( Root->Element, LastElement );
|
|
|
|
if( CompareResult == 0 ) {
|
|
|
|
//
|
|
// now traverse right tree.
|
|
//
|
|
|
|
if( Root->Right == NULL ) {
|
|
|
|
//
|
|
// no more entry.
|
|
//
|
|
|
|
*NoMoreElement = TRUE;
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// find first element from right tree.
|
|
//
|
|
|
|
Result = FindFirstFromTree( Root->Right, NextElement );
|
|
|
|
TcpsvcsDbgAssert( Result == TRUE );
|
|
return( Result );
|
|
}
|
|
|
|
if( CompareResult < 0 ) {
|
|
|
|
//
|
|
// find next element from left tree.
|
|
//
|
|
|
|
Result = FindNextFromTree(
|
|
Root->Left,
|
|
LastElement,
|
|
NextElement,
|
|
NoMoreElement );
|
|
|
|
if( Result == TRUE ) {
|
|
return( TRUE );
|
|
}
|
|
|
|
if( *NoMoreElement == TRUE ) {
|
|
|
|
//
|
|
// no more entry on left tree.
|
|
// return this entry element as next element.
|
|
//
|
|
|
|
*NextElement = Root->Element;
|
|
*NoMoreElement = FALSE;
|
|
return( TRUE );
|
|
}
|
|
|
|
//
|
|
// something must went wrong.
|
|
//
|
|
|
|
TcpsvcsDbgAssert( FALSE );
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
//
|
|
// get next entry from right tree.
|
|
//
|
|
|
|
return( FindNextFromTree(
|
|
Root->Right,
|
|
LastElement,
|
|
NextElement,
|
|
NoMoreElement ) );
|
|
}
|
|
|
|
BOOL
|
|
SORTED_CONTAINER::FindNext(
|
|
DWORD LastElement,
|
|
LPDWORD NextElement
|
|
)
|
|
/*++
|
|
|
|
This member function returns the next object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
LastElement : Last object that was returuned either by the FindFirst
|
|
or FindNext calls.
|
|
|
|
NextElement : pointer to a location where the pointer to the next
|
|
object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the next element is successfully found.
|
|
|
|
FALSE : Otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOL NoMoreElement = FALSE;
|
|
BOOL Result;
|
|
|
|
Result = FindNextFromTree(
|
|
_RootEntry,
|
|
LastElement,
|
|
NextElement,
|
|
&NoMoreElement );
|
|
|
|
if( Result == TRUE ) {
|
|
return( TRUE );
|
|
}
|
|
|
|
if( NoMoreElement == TRUE ) {
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// something must went wrong.
|
|
//
|
|
|
|
TcpsvcsDbgAssert( FALSE );
|
|
return( FALSE );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_CONTAINER::IsEmpty(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function determines the container is empty or not.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the container is empty.
|
|
|
|
FALSE : Otherwise.
|
|
|
|
--*/
|
|
{
|
|
if( _RootEntry == NULL ) {
|
|
return( TRUE );
|
|
}
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
|
|
INT
|
|
SORTED_BY_NAME_URL_CONTAINER::CompareElements(
|
|
DWORD Element1,
|
|
DWORD Element2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member virtual function implements the URL name compare
|
|
function, which compares two URL names.
|
|
|
|
Arguments:
|
|
|
|
Element1 : pointer to first URL data.
|
|
|
|
Element2 : pointer to second URL data.
|
|
|
|
Return Value:
|
|
|
|
0 - if Element1 and Element2 are equal.
|
|
1 - if Element1 is greater than Element2.
|
|
-1 - if Element1 is less than Element2.
|
|
|
|
--*/
|
|
{
|
|
LPURL_FILEMAP_ENTRY UrlEntry1 = (LPURL_FILEMAP_ENTRY)Element1;
|
|
LPURL_FILEMAP_ENTRY UrlEntry2 = (LPURL_FILEMAP_ENTRY)Element2;
|
|
|
|
return( strcmp( UrlEntry1->UrlName, UrlEntry2->UrlName ) );
|
|
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_NAME_URL_CONTAINER::AddEntry(
|
|
LPURL_FILEMAP_ENTRY NewUrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function adds an URL object to the sorted container.
|
|
|
|
Arguments:
|
|
|
|
NewElement : pointer to a new URL object.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the new URL object is successfully added to the container.
|
|
|
|
FALSE : if the new URL object can't be added to the container when
|
|
the system runs out of memory.
|
|
|
|
--*/
|
|
{
|
|
return( SORTED_CONTAINER::AddEntry( (DWORD)NewUrlEntry ) );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_NAME_URL_CONTAINER::FindEntry(
|
|
LPURL_FILEMAP_ENTRY UrlEntry,
|
|
LPURL_FILEMAP_ENTRY *FoundUrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function searches for the given URL named object and
|
|
returns the URL object pointer.
|
|
|
|
Arguments:
|
|
|
|
Element : object to be searched in the container.
|
|
|
|
FoundElement : pointer to a location where the found object pointer
|
|
is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the object is found.
|
|
|
|
FALSE : otherwise
|
|
|
|
--*/
|
|
{
|
|
return( SORTED_CONTAINER::
|
|
FindEntry( (DWORD)UrlEntry, (LPDWORD)FoundUrlEntry ) );
|
|
}
|
|
|
|
INLINE
|
|
VOID
|
|
SORTED_BY_NAME_URL_CONTAINER::DeleteEntry(
|
|
LPURL_FILEMAP_ENTRY UrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function deletes an URL object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
Element : pointer to an URL object to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SORTED_CONTAINER::DeleteEntry( (DWORD)UrlEntry );
|
|
return;
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_NAME_URL_CONTAINER::FindFirst(
|
|
LPURL_FILEMAP_ENTRY *FirstEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function returns the first URL object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
FirstElement : pointer to a location where the pointer to the first
|
|
URL object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the first element is successfully found.
|
|
|
|
FALSE : if the list is empty.
|
|
|
|
--*/
|
|
{
|
|
|
|
return( SORTED_CONTAINER::FindFirst( (DWORD *)FirstEntry ) );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_NAME_URL_CONTAINER::FindNext(
|
|
LPURL_FILEMAP_ENTRY LastEntry,
|
|
LPURL_FILEMAP_ENTRY *NextEntry
|
|
)
|
|
/*++
|
|
|
|
This member function returns the next URL object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
LastElement : Last object that was returuned either by the FindFirst
|
|
or FindNext calls.
|
|
|
|
NextElement : pointer to a location where the pointer to the next
|
|
object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the next element is successfully found.
|
|
|
|
FALSE : Otherwise.
|
|
|
|
--*/
|
|
{
|
|
return( SORTED_CONTAINER::
|
|
FindNext( (DWORD)LastEntry, (DWORD *)NextEntry ) );
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
|
|
INT
|
|
SORTED_BY_SCORE_URL_CONTAINER::CompareElements(
|
|
DWORD Element1,
|
|
DWORD Element2
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member virtual function implements the URL update time compare
|
|
function, which compares two URL update time data.
|
|
|
|
Arguments:
|
|
|
|
Element1 : pointer to first URL data.
|
|
|
|
Element2 : pointer to second URL data.
|
|
|
|
Return Value:
|
|
|
|
0 - if Element1 and Element2 are equal.
|
|
1 - if Element1 is greater than Element2.
|
|
-1 - if Element1 is less than Element2.
|
|
|
|
--*/
|
|
{
|
|
LPURL_FILEMAP_ENTRY UrlEntry1 = (LPURL_FILEMAP_ENTRY)Element1;
|
|
LPURL_FILEMAP_ENTRY UrlEntry2 = (LPURL_FILEMAP_ENTRY)Element2;
|
|
|
|
if( UrlEntry1->Score == UrlEntry2->Score ) {
|
|
return( 0 );
|
|
}
|
|
|
|
//
|
|
// the b-tree is made in the decending order. So that
|
|
// the first element in the b-tree will highest score.
|
|
//
|
|
|
|
if( UrlEntry1->Score > UrlEntry2->Score ) {
|
|
return( -1 );
|
|
}
|
|
|
|
return( 1 );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_SCORE_URL_CONTAINER::AddEntry(
|
|
LPURL_FILEMAP_ENTRY NewUrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function adds an URL object to the sorted (by
|
|
update time order) container.
|
|
|
|
Arguments:
|
|
|
|
NewElement : pointer to a new URL object.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the new URL object is successfully added to the container.
|
|
|
|
FALSE : if the new URL object can't be added to the container when
|
|
the system runs out of memory.
|
|
|
|
--*/
|
|
{
|
|
return( SORTED_CONTAINER::AddEntry( (DWORD)NewUrlEntry ) );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_SCORE_URL_CONTAINER::FindEntry(
|
|
LPURL_FILEMAP_ENTRY UrlEntry,
|
|
LPURL_FILEMAP_ENTRY *FoundUrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function searches for the given URL object and
|
|
returns the URL object pointer.
|
|
|
|
Arguments:
|
|
|
|
Element : object to be searched in the container.
|
|
|
|
FoundElement : pointer to a location where the found object pointer
|
|
is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the object is found.
|
|
|
|
FALSE : otherwise
|
|
|
|
--*/
|
|
{
|
|
return( SORTED_CONTAINER::
|
|
FindEntry( (DWORD)UrlEntry, (LPDWORD)FoundUrlEntry ) );
|
|
}
|
|
|
|
INLINE
|
|
VOID
|
|
SORTED_BY_SCORE_URL_CONTAINER::DeleteEntry(
|
|
LPURL_FILEMAP_ENTRY UrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function deletes an URL object from the sorted
|
|
container.
|
|
|
|
Arguments:
|
|
|
|
Element : pointer to an URL object to be deleted.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SORTED_CONTAINER::DeleteEntry( (DWORD)UrlEntry );
|
|
return;
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_SCORE_URL_CONTAINER::FindFirst(
|
|
LPURL_FILEMAP_ENTRY *FirstEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function returns the first URL object from the sorted
|
|
container (by update time order).
|
|
|
|
Arguments:
|
|
|
|
FirstElement : pointer to a location where the pointer to the first
|
|
URL object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the first element is successfully found.
|
|
|
|
FALSE : if the list is empty.
|
|
|
|
--*/
|
|
{
|
|
|
|
return( SORTED_CONTAINER::FindFirst( (DWORD *)FirstEntry ) );
|
|
}
|
|
|
|
INLINE
|
|
BOOL
|
|
SORTED_BY_SCORE_URL_CONTAINER::FindNext(
|
|
LPURL_FILEMAP_ENTRY LastEntry,
|
|
LPURL_FILEMAP_ENTRY *NextEntry
|
|
)
|
|
/*++
|
|
|
|
This member function returns the next URL object from the sorted
|
|
container (by update time order).
|
|
|
|
Arguments:
|
|
|
|
LastElement : Last object that was returuned either by the FindFirst
|
|
or FindNext calls.
|
|
|
|
NextElement : pointer to a location where the pointer to the next
|
|
object is returned.
|
|
|
|
Return Value:
|
|
|
|
TRUE : if the next element is successfully found.
|
|
|
|
FALSE : Otherwise.
|
|
|
|
--*/
|
|
{
|
|
return( SORTED_CONTAINER::
|
|
FindNext( (DWORD)LastEntry, (DWORD *)NextEntry ) );
|
|
}
|
|
|
|
// ------------------------------------------------------------------ //
|
|
|
|
URL_CONTAINER::URL_CONTAINER(
|
|
LPWSTR CachePath,
|
|
LONGLONG CacheLimit
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function constructs a URL cache container object.
|
|
|
|
Arguments:
|
|
|
|
CachePath : pointer to a path string where the cache files are
|
|
stored.
|
|
|
|
CacheLimit : total size of the cache allowed.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
LPURL_FILEMAP_ENTRY UrlObjEntry;
|
|
DWORD EnumHandle;
|
|
|
|
_UrlsByName = NULL;
|
|
_UrlObjStorage = NULL;
|
|
_CachePath = NULL;
|
|
_CacheSize = 0;
|
|
_CacheLimit = 0;
|
|
_Status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// initialize crit sect first.
|
|
//
|
|
|
|
InitializeCriticalSection( &_ListAccessCritSect );
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// Store Path name for future use. append "\\" also, so
|
|
// that just appending file name later to create full path
|
|
// name.
|
|
//
|
|
|
|
_CachePath = (LPWSTR) CacheHeap->Alloc(
|
|
(wcslen(CachePath) + 1) * sizeof(WCHAR) +
|
|
sizeof(PATH_CONNECT_STRING) );
|
|
|
|
if( _CachePath == NULL ) {
|
|
_Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
wcscpy( _CachePath, CachePath );
|
|
wcscat( _CachePath, PATH_CONNECT_STRING );
|
|
|
|
_CacheLimit = CacheLimit;
|
|
|
|
//
|
|
// Create storage object.
|
|
//
|
|
|
|
_UrlObjStorage = new MEMMAP_FILE( CachePath );
|
|
|
|
if( _UrlObjStorage == NULL ) {
|
|
_Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( (_Status = _UrlObjStorage->GetStatus()) != ERROR_SUCCESS ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// create empty b-tree for sorted by name Url objects.
|
|
//
|
|
|
|
_UrlsByName = new SORTED_BY_NAME_URL_CONTAINER;
|
|
|
|
if( _UrlsByName == NULL ) {
|
|
_Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// enumerate Url Objects from from storage and add them to the above
|
|
// b-tree.
|
|
//
|
|
|
|
UrlObjEntry = _UrlObjStorage->FindFirstEntry( &EnumHandle );
|
|
|
|
while( UrlObjEntry != NULL ) {
|
|
|
|
//
|
|
// compute current cache size.
|
|
//
|
|
|
|
_CacheSize += UrlObjEntry->FileSize;
|
|
|
|
//
|
|
// reset reference counts.
|
|
//
|
|
|
|
UrlObjEntry->NumReferences = 0;
|
|
UrlObjEntry->DeletePending = FALSE;
|
|
UrlObjEntry->Score = 0;
|
|
|
|
//
|
|
// add this entry to b-tree.
|
|
//
|
|
|
|
if( _UrlsByName->AddEntry( UrlObjEntry ) == FALSE ) {
|
|
|
|
//
|
|
// AddEntry fails only when there is no memory left in the
|
|
// system.
|
|
//
|
|
|
|
_Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
UrlObjEntry = _UrlObjStorage->FindNextEntry( &EnumHandle );
|
|
}
|
|
|
|
//
|
|
// We are done with object initialization successfully.
|
|
//
|
|
|
|
_Status = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if( _Status != ERROR_SUCCESS ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS,
|
|
"URL_CONTAINER::URL_CONTAINER failed, %ld\n", _Status ));
|
|
}
|
|
|
|
UnlockContainer();
|
|
return;
|
|
}
|
|
|
|
URL_CONTAINER::~URL_CONTAINER(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function destruct an URL cache container object.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// delete UrlsByUpdateTime List.
|
|
//
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// delete _UrlsByName List.
|
|
//
|
|
|
|
if( _UrlsByName != NULL ) {
|
|
|
|
//
|
|
// remove objects from the containter.
|
|
//
|
|
|
|
while( !_UrlsByName->IsEmpty() ) {
|
|
|
|
LPURL_FILEMAP_ENTRY FirstEntry;
|
|
BOOL Result;
|
|
|
|
Result = _UrlsByName->FindFirst( &FirstEntry );
|
|
|
|
TcpsvcsDbgAssert( Result == TRUE );
|
|
|
|
if( Result ) {
|
|
|
|
_UrlsByName->DeleteEntry( FirstEntry );
|
|
}
|
|
}
|
|
|
|
//
|
|
// delete container Object.
|
|
//
|
|
|
|
delete _UrlsByName;
|
|
}
|
|
|
|
//
|
|
// delete storage object.
|
|
//
|
|
|
|
if( _UrlObjStorage != NULL ) {
|
|
delete _UrlObjStorage;
|
|
}
|
|
|
|
if( _CachePath != NULL ) {
|
|
CacheHeap->Free( _CachePath );
|
|
}
|
|
|
|
UnlockContainer();
|
|
|
|
//
|
|
// now delete crit sect.
|
|
//
|
|
|
|
DeleteCriticalSection( &_ListAccessCritSect );
|
|
|
|
return;
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::DeleteUrl(
|
|
LPURL_FILEMAP_ENTRY UrlEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member functions deletes an URL from the container and also
|
|
deletes the cache file from cache path.
|
|
|
|
The caller should lock the container before calling this function.
|
|
|
|
Arguments:
|
|
|
|
UrlName : pointer to an URL string.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
WCHAR FullFileName[MAX_PATH];
|
|
|
|
if( UrlEntry->NumReferences != 0 ) {
|
|
|
|
//
|
|
// can't delete this entry, some one is using it.
|
|
//
|
|
|
|
//
|
|
// mark this entry to be deleted. When everyone is finished
|
|
// using this entry, it will be deleted automatically.
|
|
//
|
|
|
|
UrlEntry->DeletePending = TRUE;
|
|
|
|
return( ERROR_SHARING_VIOLATION );
|
|
}
|
|
|
|
//
|
|
// delete the entry from _UrlsByName container.
|
|
//
|
|
|
|
_UrlsByName->DeleteEntry( UrlEntry );
|
|
|
|
//
|
|
// remember file size before deleting the entry from memory map.
|
|
//
|
|
|
|
LONGLONG FileSize = UrlEntry->FileSize;
|
|
|
|
//
|
|
// delete this entry from memory mapped array.
|
|
//
|
|
|
|
BOOL BoolError = _UrlObjStorage->FreeUrlEntry( UrlEntry );
|
|
TcpsvcsDbgAssert( BoolError == TRUE );
|
|
|
|
//
|
|
// delete the file from storage.
|
|
//
|
|
|
|
wcscpy( FullFileName, _CachePath );
|
|
wcscat( FullFileName, UrlEntry->InternalFileName );
|
|
|
|
//
|
|
// delete file.
|
|
//
|
|
|
|
if( !DeleteFileW( FullFileName ) ) {
|
|
|
|
DWORD Error = GetLastError();
|
|
return( Error );
|
|
}
|
|
|
|
//
|
|
// adjust cache size.
|
|
//
|
|
|
|
_CacheSize -= FileSize;
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
VOID
|
|
URL_CONTAINER::ComputeUrlScore(
|
|
LPURL_FILEMAP_ENTRY UrlEntry,
|
|
LONGLONG CurrentGmtTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function computes the score for the given url entry.
|
|
|
|
Arguments:
|
|
|
|
UrlEntry : pointer to the Url entry.
|
|
|
|
CurrentGmtTime : Current GMT time.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
DWORD SizeScore;
|
|
DWORD LastAccessScore;
|
|
DWORD AccessFreScore;
|
|
DWORD ExpireScore;
|
|
LONGLONG ElapseTime;
|
|
|
|
//
|
|
// compute file size score.
|
|
//
|
|
|
|
SizeScore = (DWORD)
|
|
((UrlEntry->FileSize * SCORE_FACTOR * 1000000) / _CacheLimit);
|
|
|
|
//
|
|
// compute phase out score.
|
|
//
|
|
|
|
ElapseTime = CurrentGmtTime - UrlEntry->LastAccessedTime;
|
|
|
|
if( ElapseTime > PHASEOUT_TIME ) {
|
|
LastAccessScore = PHASEOUT_FACTOR * 1000000;
|
|
}
|
|
else {
|
|
LastAccessScore = (DWORD)
|
|
((ElapseTime * PHASEOUT_FACTOR) / PHASEOUT_TIME) * 1000000;
|
|
}
|
|
|
|
//
|
|
// compute access frequency score.
|
|
//
|
|
|
|
if( UrlEntry->NumAccessed > ACCESS_FRE_CUTOFF ) {
|
|
AccessFreScore = ACCESS_FRE_FACTOR * 1000000;
|
|
}
|
|
else {
|
|
AccessFreScore = (DWORD)
|
|
((UrlEntry->NumAccessed * ACCESS_FRE_FACTOR * 1000000) /
|
|
ACCESS_FRE_CUTOFF);
|
|
}
|
|
|
|
//
|
|
// compute freshness score.
|
|
//
|
|
|
|
if( CurrentGmtTime > UrlEntry->ExpireTime ) {
|
|
ExpireScore = EXPIRE_FACTOR * 1000000;
|
|
}
|
|
else {
|
|
|
|
TcpsvcsDbgAssert( CurrentGmtTime > UrlEntry->LastModifiedTime);
|
|
|
|
ExpireScore = (DWORD)
|
|
(((CurrentGmtTime - UrlEntry->LastModifiedTime) * EXPIRE_FACTOR) /
|
|
(UrlEntry->ExpireTime - UrlEntry->LastModifiedTime)) * 1000000;
|
|
}
|
|
|
|
|
|
TcpsvcsDbgAssert( SizeScore <= SCORE_FACTOR * 1000000 );
|
|
TcpsvcsDbgAssert( LastAccessScore <= PHASEOUT_FACTOR * 1000000 );
|
|
TcpsvcsDbgAssert( AccessFreScore <= ACCESS_FRE_CUTOFF * 1000000 );
|
|
TcpsvcsDbgAssert( ExpireScore <= EXPIRE_FACTOR * 1000000 );
|
|
|
|
//
|
|
// add all scores to make total score.
|
|
//
|
|
|
|
UrlEntry->Score = SizeScore + LastAccessScore + AccessFreScore + ExpireScore;
|
|
|
|
TcpsvcsDbgAssert( UrlEntry->Score <= 100 * 1000000);
|
|
return;
|
|
}
|
|
|
|
LPURL_FILEMAP_ENTRY
|
|
URL_CONTAINER::LookupUrl(
|
|
LPCSTR UrlName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function returns a copy URL entry record. The caller
|
|
should free up the memory after use.
|
|
|
|
Arguments:
|
|
|
|
UrlName : pointer to an URL string whose URL record is searched.
|
|
|
|
Note : The container should be locked when this routine is called.
|
|
|
|
Return Value:
|
|
|
|
NULL : if the record is not found.
|
|
|
|
non-NULL : if the record is found, the caller should free up the
|
|
memory after use.
|
|
|
|
--*/
|
|
{
|
|
URL_FILEMAP_ENTRY DummyEntry;
|
|
LPURL_FILEMAP_ENTRY FoundEntry;
|
|
|
|
memset( (PVOID)&DummyEntry, 0x0, sizeof(URL_FILEMAP_ENTRY) );
|
|
|
|
if( strlen(UrlName) >= MAX_URL_LENGTH ) {
|
|
TcpsvcsDbgAssert( FALSE );
|
|
return( NULL );
|
|
}
|
|
|
|
strcpy( DummyEntry.UrlName, UrlName );
|
|
|
|
if( _UrlsByName->FindEntry( &DummyEntry, &FoundEntry ) == FALSE ) {
|
|
|
|
//
|
|
// entry not found.
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
return( FoundEntry );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::CleanupAllUrls(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function deletes all urls from cache. This function is
|
|
used when persistent flag is set to FALSE.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
LPURL_FILEMAP_ENTRY UrlObjEntry;
|
|
DWORD EnumHandle;
|
|
|
|
//
|
|
// delete all entries from object storage.
|
|
//
|
|
|
|
LockContainer();
|
|
|
|
UrlObjEntry = _UrlObjStorage->FindFirstEntry( &EnumHandle );
|
|
|
|
while( UrlObjEntry != NULL ) {
|
|
|
|
//
|
|
// delete this entry from cache.
|
|
//
|
|
|
|
Error = DeleteUrl( UrlObjEntry );
|
|
|
|
//
|
|
// if the file is in use, skip it.
|
|
//
|
|
|
|
if( (Error != ERROR_SUCCESS) &&
|
|
(Error != ERROR_SHARING_VIOLATION) ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
if( Error == ERROR_SHARING_VIOLATION ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_MISC,
|
|
"Persistent Cleanup can not delete url (%s), "
|
|
"some one is using it.\n",
|
|
UrlObjEntry->UrlName ));
|
|
}
|
|
|
|
UrlObjEntry = _UrlObjStorage->FindNextEntry( &EnumHandle );
|
|
}
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
UnlockContainer();
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS,
|
|
"CleanupAllUrls call failed, %ld.\n", Error ));
|
|
}
|
|
|
|
return( Error );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::CleanupUrls(
|
|
DWORD Factor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function computes the score for each of the entry in the
|
|
cache and deletes entries with high score until it recovers specified free
|
|
space.
|
|
|
|
Arguments:
|
|
|
|
Factor : amount of free space to make. Foctor of 25 means, delete
|
|
files to make CacheSize <= .75 * CacheLimit.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
LONGLONG FreeLimit;
|
|
SORTED_BY_SCORE_URL_CONTAINER *UrlsByScore = NULL;
|
|
LPURL_FILEMAP_ENTRY UrlObjEntry;
|
|
DWORD EnumHandle;
|
|
DWORD Error;
|
|
LONGLONG CurrentGmtTime;
|
|
|
|
FreeLimit = ((100 - Factor) * _CacheLimit) / 100;
|
|
|
|
//
|
|
// check to see the cache space is free by the specified factor.
|
|
//
|
|
|
|
if( _CacheSize <= FreeLimit ) {
|
|
return( ERROR_SUCCESS);
|
|
}
|
|
|
|
|
|
//
|
|
// make score b-tree.
|
|
//
|
|
|
|
UrlsByScore = new SORTED_BY_SCORE_URL_CONTAINER;
|
|
|
|
if( UrlsByScore == NULL ) {
|
|
return( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
LockContainer();
|
|
|
|
UrlObjEntry = _UrlObjStorage->FindFirstEntry( &EnumHandle );
|
|
CurrentGmtTime = GetGmtTime();
|
|
|
|
while( UrlObjEntry != NULL ) {
|
|
|
|
//
|
|
// compute score for this entry.
|
|
//
|
|
|
|
ComputeUrlScore( UrlObjEntry, CurrentGmtTime );
|
|
|
|
//
|
|
// add this entry to b-tree.
|
|
//
|
|
|
|
if( UrlsByScore->AddEntry( UrlObjEntry ) == FALSE ) {
|
|
|
|
//
|
|
// AddEntry fails only when there is no memory left in the
|
|
// system.
|
|
//
|
|
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
UrlObjEntry = _UrlObjStorage->FindNextEntry( &EnumHandle );
|
|
}
|
|
|
|
//
|
|
// walk through the tree and free files with high score until
|
|
// CacheSize is equal or less than FreeLimit.
|
|
//
|
|
|
|
//
|
|
// remove objects from the containter.
|
|
//
|
|
|
|
while( (_CacheSize > FreeLimit) && !UrlsByScore->IsEmpty() ) {
|
|
|
|
LPURL_FILEMAP_ENTRY FirstEntry;
|
|
BOOL Result;
|
|
|
|
Result = UrlsByScore->FindFirst( &FirstEntry );
|
|
|
|
TcpsvcsDbgAssert( Result == TRUE );
|
|
|
|
if( Result ) {
|
|
|
|
//
|
|
// delete this entry from cache.
|
|
//
|
|
|
|
//
|
|
// delete the entry from score b-tree first.
|
|
//
|
|
|
|
UrlsByScore->DeleteEntry( FirstEntry );
|
|
|
|
Error = DeleteUrl( FirstEntry );
|
|
|
|
//
|
|
// if the file is in use, skip it.
|
|
//
|
|
|
|
if( (Error != ERROR_SUCCESS) &&
|
|
(Error != ERROR_SHARING_VIOLATION) ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
TcpsvcsDbgAssert( _CacheSize <= FreeLimit );
|
|
Error = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if( UrlsByScore != NULL ) {
|
|
|
|
//
|
|
// empty the container.
|
|
//
|
|
|
|
while( !UrlsByScore->IsEmpty() ) {
|
|
|
|
LPURL_FILEMAP_ENTRY FirstEntry;
|
|
BOOL Result;
|
|
|
|
Result = UrlsByScore->FindFirst( &FirstEntry );
|
|
|
|
TcpsvcsDbgAssert( Result == TRUE );
|
|
|
|
if( Result ) {
|
|
|
|
//
|
|
// delete this entry from cache.
|
|
//
|
|
|
|
UrlsByScore->DeleteEntry( FirstEntry );
|
|
|
|
}
|
|
}
|
|
|
|
delete UrlsByScore;
|
|
}
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS, "CleanupUrls call failed, %ld.\n",
|
|
Error ));
|
|
}
|
|
|
|
UnlockContainer();
|
|
return( Error );
|
|
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::AddUrl(
|
|
LPCSTR UrlName,
|
|
LPCWSTR CacheFileName,
|
|
LONGLONG ExpireTime,
|
|
DWORD cbHeaders
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member functions adds an URL to the container and moves the
|
|
cache file to cache path.
|
|
|
|
Arguments:
|
|
|
|
UrlName : pointer to an URL string.
|
|
|
|
CacheFileName : pointer to a cache file (full) name.
|
|
|
|
ExpireTime : expire time of the file.
|
|
|
|
cbHeaders : The count of bytes of header data the prefixes the file
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
WCHAR FullFileName[MAX_PATH];
|
|
LPURL_FILEMAP_ENTRY UrlEntry;
|
|
LONGLONG NewFileSize;
|
|
|
|
//
|
|
// if the file name is too long.
|
|
//
|
|
|
|
if( strlen(UrlName) > MAX_URL_LENGTH ) {
|
|
return( ERROR_INVALID_NAME );
|
|
}
|
|
|
|
//
|
|
// get the new file size.
|
|
//
|
|
|
|
Error = GetFileSizeByName( CacheFileName, &NewFileSize );
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
return( Error );
|
|
}
|
|
|
|
//
|
|
// don't cache big files.
|
|
//
|
|
|
|
if( NewFileSize > ((_CacheLimit * GlobalCleanupFactor) / 100) ) {
|
|
return( ERROR_DISK_FULL );
|
|
}
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// search to see the Url exists already.
|
|
//
|
|
|
|
UrlEntry = LookupUrl( UrlName );
|
|
|
|
if( UrlEntry != NULL ) {
|
|
|
|
//
|
|
// existing url.
|
|
|
|
//
|
|
// we can't update the url if someone is using it.
|
|
//
|
|
|
|
if( UrlEntry->NumReferences != 0 ) {
|
|
Error = ERROR_SHARING_VIOLATION;
|
|
goto Cleanup;
|
|
}
|
|
|
|
wcscpy( FullFileName, _CachePath );
|
|
wcscat( FullFileName, UrlEntry->InternalFileName );
|
|
|
|
LONGLONG OldFileSize = UrlEntry->FileSize;
|
|
|
|
LONGLONG NewCacheSize = _CacheSize - OldFileSize + NewFileSize;
|
|
if( NewCacheSize >= _CacheLimit ) {
|
|
|
|
//
|
|
// Cleanup some unwanted Urls from storage.
|
|
//
|
|
|
|
CleanupUrls( GlobalCleanupFactor );
|
|
}
|
|
|
|
TcpsvcsDbgAssert( NewCacheSize < _CacheLimit );
|
|
|
|
//
|
|
// move file.
|
|
//
|
|
|
|
if( !MoveFileExW(
|
|
CacheFileName,
|
|
FullFileName,
|
|
MOVEFILE_REPLACE_EXISTING ) ) {
|
|
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Update Url Info.
|
|
//
|
|
|
|
_CacheSize = NewCacheSize;
|
|
|
|
UrlEntry->FileSize = NewFileSize;
|
|
UrlEntry->LastModifiedTime =
|
|
UrlEntry->LastAccessedTime =
|
|
GetGmtTime();
|
|
|
|
if( ExpireTime == 0 ) {
|
|
UrlEntry->ExpireTime =
|
|
UrlEntry->LastModifiedTime +
|
|
(LONGLONG)GlobalFreshnessInterval * 10000000 ;
|
|
UrlEntry->IsDefaultExpire = TRUE;
|
|
}
|
|
else {
|
|
|
|
if( ExpireTime > UrlEntry->LastModifiedTime ) {
|
|
UrlEntry->ExpireTime = ExpireTime;
|
|
}
|
|
else {
|
|
|
|
TcpsvcsDbgAssert( FALSE );
|
|
UrlEntry->ExpireTime = UrlEntry->LastModifiedTime;
|
|
}
|
|
|
|
UrlEntry->IsDefaultExpire = FALSE;
|
|
}
|
|
|
|
//
|
|
// reset num accessed, since the file is updated.
|
|
//
|
|
|
|
UrlEntry->NumAccessed = 1;
|
|
|
|
UrlEntry->cbHeaders = cbHeaders;
|
|
|
|
Error = ERROR_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// new url.
|
|
|
|
if( _CacheSize + NewFileSize >= _CacheLimit ) {
|
|
|
|
//
|
|
// Cleanup some unwanted Urls from storage.
|
|
//
|
|
|
|
CleanupUrls( GlobalCleanupFactor );
|
|
}
|
|
|
|
TcpsvcsDbgAssert( _CacheSize + NewFileSize < _CacheLimit );
|
|
|
|
//
|
|
// make a unique internal file name.
|
|
//
|
|
|
|
Error = CreateUniqueFile(
|
|
UrlName,
|
|
_CachePath,
|
|
FullFileName,
|
|
NULL ); // random extension.
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// move file.
|
|
//
|
|
|
|
if( !MoveFileExW( CacheFileName, FullFileName, MOVEFILE_REPLACE_EXISTING ) ) {
|
|
|
|
//
|
|
// can't move file.
|
|
//
|
|
|
|
Error = GetLastError();
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// get a free URL_FILEMAP_ENTRY entry from memory mapped
|
|
// array.
|
|
//
|
|
|
|
LPURL_FILEMAP_ENTRY NewEntry;
|
|
|
|
NewEntry = _UrlObjStorage->AllocateUrlEntry();
|
|
|
|
if( NewEntry == NULL ) {
|
|
|
|
//
|
|
// move back the file.
|
|
//
|
|
|
|
MoveFileW( FullFileName, CacheFileName ) ;
|
|
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
strcpy( NewEntry->UrlName, UrlName );
|
|
|
|
//
|
|
// FileName = FullFileName - CachePath;
|
|
//
|
|
|
|
LPWSTR FileName;
|
|
FileName = FullFileName + wcslen(_CachePath);
|
|
TcpsvcsDbgAssert( wcslen(FileName) <= INTERNAL_FILENAME_LENGTH );
|
|
wcscpy( NewEntry->InternalFileName, FileName );
|
|
|
|
NewEntry->FileSize = NewFileSize;
|
|
NewEntry->LastModifiedTime = NewEntry->LastAccessedTime = GetGmtTime();
|
|
|
|
if( ExpireTime == 0 ) {
|
|
NewEntry->ExpireTime =
|
|
NewEntry->LastModifiedTime +
|
|
(LONGLONG)GlobalFreshnessInterval * 10000000;
|
|
NewEntry->IsDefaultExpire = TRUE;
|
|
}
|
|
else {
|
|
|
|
if( ExpireTime >= NewEntry->LastModifiedTime ) {
|
|
NewEntry->ExpireTime = ExpireTime;
|
|
}
|
|
else {
|
|
NewEntry->ExpireTime = NewEntry->LastModifiedTime;
|
|
}
|
|
|
|
NewEntry->IsDefaultExpire = FALSE;
|
|
}
|
|
|
|
NewEntry->NumAccessed = 1;
|
|
|
|
NewEntry->NumReferences = 0;
|
|
NewEntry->DeletePending = FALSE;
|
|
NewEntry->Score = 0;
|
|
NewEntry->cbHeaders = cbHeaders;
|
|
|
|
//
|
|
// add this entry to name list.
|
|
//
|
|
|
|
if( !_UrlsByName->AddEntry( NewEntry ) ) {
|
|
|
|
//
|
|
// could add to name list.
|
|
//
|
|
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
//
|
|
// move back the file.
|
|
//
|
|
|
|
MoveFileW( FullFileName, CacheFileName ) ;
|
|
|
|
//
|
|
// free memory map entry.
|
|
//
|
|
|
|
_UrlObjStorage->FreeUrlEntry( NewEntry );
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// adjust CacheSize.
|
|
//
|
|
|
|
_CacheSize += NewFileSize;
|
|
|
|
Cleanup:
|
|
|
|
UnlockContainer();
|
|
return( Error );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::RetrieveUrl(
|
|
LPCSTR UrlName,
|
|
LPWSTR FileName,
|
|
BOOL * IsExpired,
|
|
DWORD * pcbHeaders
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function retrives an url from the cache. The url is marked
|
|
as referenced, so that caller should call UnlockUrl when he is done
|
|
using it.
|
|
|
|
Arguments:
|
|
|
|
UrlName : pointer to the url name.
|
|
|
|
FileName : pointer to a buffer that receives the local file name of
|
|
the url.
|
|
|
|
IsExpired : pointer to a BOOL location that receives the expire state
|
|
of the url.
|
|
|
|
pcbHeaders : pointer to DWORD that receives the number of bytes of header
|
|
data that prefixes the file. This parameter may be NULL (optional).
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
LPURL_FILEMAP_ENTRY UrlEntry;
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// serach the url.
|
|
//
|
|
|
|
UrlEntry = LookupUrl( UrlName );
|
|
|
|
if( UrlEntry == NULL ) {
|
|
UnlockContainer();
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
//
|
|
// found the entry, increment the reference count.
|
|
//
|
|
|
|
UrlEntry->NumReferences++;
|
|
|
|
//
|
|
// copy file name.
|
|
//
|
|
|
|
wcscpy( FileName, _CachePath );
|
|
wcscat( FileName, UrlEntry->InternalFileName );
|
|
|
|
//
|
|
// check to see the file expired.
|
|
//
|
|
|
|
LONGLONG CurrentTime = GetGmtTime();
|
|
|
|
if( CurrentTime > UrlEntry->ExpireTime ) {
|
|
*IsExpired = TRUE;
|
|
}
|
|
else {
|
|
*IsExpired = FALSE;
|
|
}
|
|
|
|
if ( pcbHeaders ) {
|
|
*pcbHeaders = UrlEntry->cbHeaders;
|
|
}
|
|
|
|
UnlockContainer();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::UnlockUrl(
|
|
LPCSTR UrlName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function unreferences the url entry, so that it can be
|
|
freed up when used no one.
|
|
|
|
Arguments:
|
|
|
|
Url : pointer to an URL name.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
LPURL_FILEMAP_ENTRY UrlEntry;
|
|
|
|
//
|
|
// lock the container.
|
|
//
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// look up the entry.
|
|
//
|
|
|
|
UrlEntry = LookupUrl( UrlName );
|
|
|
|
if( UrlEntry == NULL ) {
|
|
UnlockContainer();
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
if( UrlEntry->NumReferences != 0 ) {
|
|
UrlEntry->NumReferences--;
|
|
|
|
if( (UrlEntry->NumReferences == 0) && (UrlEntry->DeletePending) ) {
|
|
|
|
//
|
|
// if this Url is mark to delete, do so now.
|
|
//
|
|
|
|
DWORD Error = DeleteUrl( UrlEntry );
|
|
|
|
TcpsvcsDbgAssert( Error == ERROR_SUCCESS );
|
|
}
|
|
}
|
|
else {
|
|
TcpsvcsDbgAssert( FALSE );
|
|
}
|
|
|
|
UnlockContainer();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::GetUrlInfo(
|
|
LPCSTR UrlName,
|
|
LPURL_INFO UrlInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This member function retrieves the url info.
|
|
|
|
Arguments:
|
|
|
|
UrlName : name of the url file (unused now).
|
|
|
|
UrlInfo : pointer to the url info structure that receives the url
|
|
info.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
LPURL_FILEMAP_ENTRY UrlEntry;
|
|
|
|
//
|
|
// lock the container.
|
|
//
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// look up the entry.
|
|
//
|
|
|
|
UrlEntry = LookupUrl( UrlName );
|
|
|
|
if( UrlEntry == NULL ) {
|
|
UnlockContainer();
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
UrlInfo->LastModifiedTime = UrlEntry->LastModifiedTime;
|
|
|
|
//
|
|
// return expire time if it is not default expire time.
|
|
//
|
|
|
|
if( UrlEntry->IsDefaultExpire ) {
|
|
UrlInfo->ExpireTime = 0;
|
|
}
|
|
else {
|
|
UrlInfo->ExpireTime = UrlEntry->ExpireTime;
|
|
}
|
|
|
|
UnlockContainer();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::SetUrlInfo(
|
|
LPCSTR UrlName,
|
|
LPURL_INFO UrlInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
UrlName : name of the url file (unused now).
|
|
|
|
UrlInfo : pointer to the url info structure that has the url info to
|
|
be set.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
LPURL_FILEMAP_ENTRY UrlEntry;
|
|
|
|
//
|
|
// lock the container.
|
|
//
|
|
|
|
LockContainer();
|
|
|
|
//
|
|
// look up the entry.
|
|
//
|
|
|
|
UrlEntry = LookupUrl( UrlName );
|
|
|
|
if( UrlEntry == NULL ) {
|
|
UnlockContainer();
|
|
return( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
//
|
|
// set last modified time if we are asked so.
|
|
//
|
|
|
|
if( UrlInfo->LastModifiedTime != 0 ) {
|
|
UrlEntry->LastModifiedTime = UrlInfo->LastModifiedTime;
|
|
}
|
|
|
|
//
|
|
// set expire time if we are asked so.
|
|
//
|
|
|
|
if( UrlInfo->ExpireTime != 0 ) {
|
|
|
|
if( UrlInfo->ExpireTime > UrlEntry->LastModifiedTime ) {
|
|
UrlEntry->ExpireTime = UrlInfo->ExpireTime;
|
|
}
|
|
else {
|
|
|
|
TcpsvcsDbgAssert( FALSE );
|
|
UrlEntry->ExpireTime = UrlEntry->LastModifiedTime;
|
|
}
|
|
}
|
|
|
|
UnlockContainer();
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
DWORD
|
|
URL_CONTAINER::CreateUrlFile(
|
|
LPCSTR UrlName,
|
|
DWORD ExpectedSize,
|
|
LPWSTR FileName )
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates a temperary file in the cache storage. This call
|
|
is called by the application when it receives a url file from a
|
|
server. When the receive is completed it caches this file to url cache
|
|
management, which will move the file to permanent cache file. The idea
|
|
is the cache file is written only once directly into the cache store.
|
|
|
|
Arguments:
|
|
|
|
UrlName : name of the url file (unused now).
|
|
|
|
ExpectedSize : expected size of the incoming file. If it is unknown
|
|
this value is set to null.
|
|
|
|
FileName : pointer to a buffer that receives the full path name of the
|
|
the temp file.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
|
|
#if 0
|
|
|
|
if( ExpectedSize > (_CacheLimit - _CacheSize) ){
|
|
return( ERROR_DISK_FULL );
|
|
}
|
|
#endif // 0
|
|
|
|
Error = CreateUniqueFile(
|
|
UrlName,
|
|
_CachePath,
|
|
FileName,
|
|
TEMP_FILE_EXTENSION );
|
|
|
|
return( Error );
|
|
}
|
|
|
|
|