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.
1406 lines
27 KiB
1406 lines
27 KiB
/*++
|
|
|
|
Copyright (c) 1994 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
filemap.cxx
|
|
|
|
Abstract:
|
|
|
|
contains implementation of MEMMAP_FILE class.
|
|
|
|
Author:
|
|
|
|
Madan Appiah (madana) 28-April-1995
|
|
|
|
Environment:
|
|
|
|
User Mode - Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include <cache.hxx>
|
|
|
|
BOOL
|
|
ValidateUrlEntry(
|
|
LPWSTR PathName,
|
|
LPURL_FILEMAP_ENTRY UrlEntry
|
|
)
|
|
{
|
|
DWORD Error;
|
|
WCHAR UrlFileName[MAX_PATH];
|
|
LPSTR UrlName;
|
|
|
|
//
|
|
// validate url name
|
|
//
|
|
|
|
if( strlen(UrlName = UrlEntry->UrlName) == 0 ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE, "Url Name is emty.\n" ));
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// check corresponding disk file is present.
|
|
//
|
|
|
|
wcscpy( UrlFileName, PathName );
|
|
wcscat( UrlFileName, PATH_CONNECT_STRING );
|
|
wcscat( UrlFileName, UrlEntry->InternalFileName );
|
|
|
|
LONGLONG UrlFileSize;
|
|
|
|
Error = GetFileSizeByName(
|
|
UrlFileName,
|
|
&UrlFileSize );
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"GetFileSizeByName (%s) call failed, %ld.\n", UrlName, Error ));
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
if( UrlFileSize != UrlEntry->FileSize ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"UrlFile (%s) size does not match.\n", UrlName ));
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// validate time fields.
|
|
//
|
|
|
|
//
|
|
// last accessed time can't less than last modified time.
|
|
//
|
|
|
|
if( UrlEntry->LastAccessedTime < UrlEntry->LastModifiedTime ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"Invalid UrlFile (%s) entry, LastAccessedTime is invalid.\n", UrlName ));
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Expire Time can't be less than last modified time.
|
|
//
|
|
|
|
if( UrlEntry->ExpireTime < UrlEntry->LastModifiedTime ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"Invalid UrlFile (%s) entry, ExpireTime is invalid.\n", UrlName ));
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
|
|
if( UrlEntry->cbHeaders == 0 ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"Invalid UrlFile (%s) entry, Header Size is invalid.\n", UrlName ));
|
|
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// Valid entry.
|
|
//
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
DWORD
|
|
MEMMAP_FILE::DeleteFiles(
|
|
LPWSTR Files
|
|
)
|
|
/*++
|
|
|
|
This private member function validates the cache file content.
|
|
|
|
Arguments:
|
|
|
|
Files : pointer to file name string (contains wildcard chars, such as
|
|
* and ?).
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
WCHAR PathFiles[MAX_PATH];
|
|
WCHAR FullFileName[MAX_PATH];
|
|
LPWSTR FullFileNamePtr;
|
|
WIN32_FIND_DATAW FindData;
|
|
HANDLE FindHandle = NULL;
|
|
DWORD Error;
|
|
BOOL BoolError;
|
|
|
|
wcscpy( PathFiles, _FullPathName );
|
|
wcscat( PathFiles, Files );
|
|
|
|
wcscpy( FullFileName, _FullPathName );
|
|
FullFileNamePtr = FullFileName + wcslen( FullFileName );
|
|
|
|
FindHandle = FindFirstFileW( (LPWSTR)PathFiles, &FindData );
|
|
|
|
if( FindHandle == INVALID_HANDLE_VALUE ) {
|
|
FindHandle = NULL;
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
for(;;) {
|
|
|
|
wcscpy( FullFileNamePtr, FindData.cFileName );
|
|
BoolError = DeleteFileW( (LPWSTR)FullFileName );
|
|
|
|
if( !BoolError ) {
|
|
|
|
Error = GetLastError();
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"DeleteFile (%ws) call failed, %ld.\n",
|
|
FindData.cFileName , Error ));
|
|
}
|
|
|
|
//
|
|
// find next file.
|
|
//
|
|
|
|
BoolError = FindNextFileW( FindHandle, &FindData );
|
|
|
|
if( !BoolError ) {
|
|
|
|
Error = GetLastError();
|
|
|
|
if( Error == ERROR_NO_MORE_FILES ) {
|
|
Error = ERROR_SUCCESS;
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if( FindHandle != NULL ) {
|
|
CloseHandle( FindHandle );
|
|
}
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS,
|
|
"MEMMAP_FILE::DeleteFiles failed, %ld.\n",
|
|
Error ));
|
|
}
|
|
|
|
return( Error );
|
|
}
|
|
|
|
BOOL
|
|
MEMMAP_FILE::ValidateCache(
|
|
LPWSTR PathName
|
|
)
|
|
/*++
|
|
|
|
This private member function validates the cache file content.
|
|
|
|
Arguments:
|
|
|
|
NONE.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the cache is valid.
|
|
FALSE - otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOL ReturnCode = FALSE;
|
|
|
|
//
|
|
// validate signatue.
|
|
//
|
|
|
|
if( memcmp(
|
|
_HeaderInfo->FileSignature,
|
|
CACHE_SIGNATURE,
|
|
MAX_SIG_SIZE ) != 0 ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"File signature does not match.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// check file size.
|
|
//
|
|
|
|
DWORD ExpectedFileSize;
|
|
|
|
ExpectedFileSize =
|
|
sizeof(MEMMAP_HEADER) +
|
|
_HeaderInfo->NumUrlInternalEntries *
|
|
sizeof( URL_FILEMAP_ENTRY );
|
|
|
|
//
|
|
// cell the size to GlobalMapFileGrowSize.
|
|
//
|
|
|
|
if( ExpectedFileSize % GlobalMapFileGrowSize ) {
|
|
ExpectedFileSize =
|
|
((ExpectedFileSize / GlobalMapFileGrowSize) + 1) *
|
|
GlobalMapFileGrowSize;
|
|
}
|
|
|
|
if( _FileSize != ExpectedFileSize ) {
|
|
|
|
//
|
|
// it is ok if the file size is one block bigger.
|
|
//
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"File size does not match.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// check each url entry log in the file.
|
|
//
|
|
|
|
DWORD i;
|
|
LPURL_FILEMAP_ENTRY NextEntry;
|
|
DWORD BitMap;
|
|
DWORD BitMask;
|
|
LPDWORD NextBitMap;
|
|
|
|
NextBitMap = _HeaderInfo->AllocationBitMap;
|
|
|
|
for( i = 0, NextEntry = _EntryArray;
|
|
i < _HeaderInfo->NumUrlInternalEntries;
|
|
i++, NextEntry++ ) {
|
|
|
|
//
|
|
// setup bitmap and bitmask.
|
|
//
|
|
|
|
if( (i % NUM_BITS_IN_DWORD) == 0 ) {
|
|
|
|
//
|
|
// move to next bitmap dword.
|
|
//
|
|
|
|
BitMask = 0x1;
|
|
BitMap = *NextBitMap++;
|
|
|
|
TcpsvcsDbgAssert( NextBitMap <
|
|
_HeaderInfo->AllocationBitMap + ALLOC_BIT_MAP_SIZE );
|
|
}
|
|
else {
|
|
|
|
//
|
|
// move to next bit.
|
|
//
|
|
|
|
BitMask <<= 1;
|
|
}
|
|
|
|
if( BitMap & BitMask ) {
|
|
|
|
//
|
|
// used entry, check other fields.
|
|
//
|
|
|
|
if( ValidateUrlEntry( PathName, NextEntry ) == FALSE ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"Invalid Url Entry : %s\n", NextEntry->UrlName ));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// marked as unused entry, check to see url name is empty.
|
|
//
|
|
|
|
if( *NextEntry->UrlName != '\0' ) {
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE,
|
|
"Invalid free entry (%s)\n", NextEntry->UrlName ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// ?? enum url files and check with entries.
|
|
//
|
|
|
|
//
|
|
// every thing is fine.
|
|
//
|
|
|
|
ReturnCode = TRUE;
|
|
|
|
Cleanup:
|
|
|
|
if( ReturnCode == FALSE ) {
|
|
TcpsvcsDbgPrint(( DEBUG_FILE_VALIDATE, "Invalid Cache\n" ));
|
|
}
|
|
|
|
return( ReturnCode );
|
|
}
|
|
|
|
DWORD
|
|
MEMMAP_FILE::RemapAddress(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
This private member function remaps the memory mapped file just after
|
|
the file size has been modified.
|
|
|
|
Container must be locked when this function is called.
|
|
|
|
Arguments:
|
|
|
|
NONE.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error = ERROR_SUCCESS;
|
|
PVOID OldBaseAddr;
|
|
DWORD OldViewSize;
|
|
PVOID VirtualBase;
|
|
BOOL BoolError;
|
|
|
|
TcpsvcsDbgAssert( _BaseAddr != NULL );
|
|
|
|
if( _BaseAddr == NULL ) {
|
|
return( ERROR_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// close existing mapping.
|
|
//
|
|
|
|
if( _FileMappingHandle != NULL ) {
|
|
UnmapViewOfFile( _BaseAddr );
|
|
CloseHandle( _FileMappingHandle );
|
|
}
|
|
|
|
//
|
|
// remember old values.
|
|
//
|
|
|
|
OldBaseAddr = _BaseAddr;
|
|
|
|
TcpsvcsDbgAssert( _FileSize >= GlobalMapFileGrowSize );
|
|
OldViewSize = _FileSize - GlobalMapFileGrowSize;
|
|
|
|
//
|
|
// re-create memory mapping.
|
|
//
|
|
|
|
_FileMappingHandle =
|
|
CreateFileMapping(
|
|
_FileHandle,
|
|
NULL,
|
|
PAGE_READWRITE,
|
|
0, // high dword of max memory mapped file size.
|
|
0, // map entire file.
|
|
NULL );
|
|
|
|
if( _FileMappingHandle == NULL ) {
|
|
_Status = GetLastError();
|
|
return( Error );
|
|
}
|
|
|
|
//
|
|
// decommit the memory that reserved for expansion.
|
|
//
|
|
|
|
BoolError = VirtualFree( _VirtualBaseAddr, 0, MEM_RELEASE );
|
|
|
|
if( !BoolError ) {
|
|
Error = GetLastError();
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS, "VirtualFree failed, %ld.\n", Error ));
|
|
|
|
//
|
|
// continue.
|
|
//
|
|
}
|
|
|
|
//
|
|
// remap view region.
|
|
//
|
|
|
|
_BaseAddr =
|
|
MapViewOfFileEx(
|
|
_FileMappingHandle,
|
|
FILE_MAP_WRITE,
|
|
0,
|
|
0,
|
|
0, // MAP entire file.
|
|
OldBaseAddr );
|
|
|
|
if( _BaseAddr == NULL ) {
|
|
|
|
Error = GetLastError();
|
|
TcpsvcsDbgAssert( FALSE );
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS,
|
|
"MapViewOfFile failed to extend address space, %ld.\n",
|
|
Error ));
|
|
|
|
//
|
|
// now try to restore at least the old mapping.
|
|
//
|
|
|
|
if( OldViewSize > 0 ) {
|
|
|
|
_BaseAddr =
|
|
MapViewOfFileEx(
|
|
_FileMappingHandle,
|
|
FILE_MAP_WRITE,
|
|
0,
|
|
0,
|
|
OldViewSize,
|
|
OldBaseAddr );
|
|
|
|
if( _BaseAddr == NULL ) {
|
|
|
|
Error = GetLastError();
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS,
|
|
"MapViewOfFile failed again to extend address space, %ld.\n",
|
|
Error ));
|
|
}
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// adjust virtual address space for future expansion.
|
|
//
|
|
|
|
_VirtualBaseAddr = (LPBYTE)_BaseAddr + _FileSize;
|
|
_VirtualCommitedSize = MAX_MAPFILE_SIZE - _FileSize;
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
//
|
|
// re-commit the virtual address space.
|
|
//
|
|
|
|
VirtualBase =
|
|
VirtualAlloc(
|
|
_VirtualBaseAddr,
|
|
_VirtualCommitedSize,
|
|
MEM_RESERVE,
|
|
PAGE_READWRITE );
|
|
|
|
if( VirtualBase == NULL ) {
|
|
|
|
DWORD LocalError;
|
|
|
|
LocalError = GetLastError();
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS, "VirtualAlloc failed, %ld.\n", LocalError ));
|
|
}
|
|
|
|
return( Error );
|
|
}
|
|
|
|
DWORD
|
|
MEMMAP_FILE::GrowMapFile(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
This private member function extends the memory mapped file and
|
|
creates more free url store entries.
|
|
|
|
Arguments:
|
|
|
|
NONE.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Error;
|
|
BOOL BoolError;
|
|
DWORD FilePointer;
|
|
DWORD OldNumUrlInternalEntries;
|
|
|
|
LockFileMap();
|
|
|
|
//
|
|
// check to see that we have reached the limit.
|
|
// we can hold only MAX_URL_ENTRIES url entries.
|
|
// so the file size can grow more than
|
|
//
|
|
// MAX_URL_ENTRIES * sizeof(URL_FILEMAP_ENTRY) +
|
|
// sizeof( MEMMAP_HEADER );
|
|
//
|
|
|
|
if( ( _FileSize + GlobalMapFileGrowSize) >=
|
|
(MAX_URL_ENTRIES * sizeof(URL_FILEMAP_ENTRY) +
|
|
sizeof( MEMMAP_HEADER ) ) ) {
|
|
|
|
//
|
|
// best matching error code.
|
|
//
|
|
|
|
Error = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
FilePointer = SetFilePointer(
|
|
_FileHandle,
|
|
GlobalMapFileGrowSize,
|
|
NULL,
|
|
FILE_END );
|
|
|
|
|
|
if( FilePointer == 0xFFFFFFFF ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = SetEndOfFile( _FileHandle );
|
|
|
|
if( BoolError != TRUE ) {
|
|
Error = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// adjust internal size parameters.
|
|
//
|
|
|
|
_FileSize += GlobalMapFileGrowSize;
|
|
|
|
OldNumUrlInternalEntries = _HeaderInfo->NumUrlInternalEntries;
|
|
_HeaderInfo->NumUrlInternalEntries +=
|
|
GlobalMapFileGrowSize / sizeof(URL_FILEMAP_ENTRY);
|
|
|
|
_NumBitMapDWords =
|
|
(_HeaderInfo->NumUrlInternalEntries + (NUM_BITS_IN_DWORD - 1)) /
|
|
NUM_BITS_IN_DWORD; // cell
|
|
|
|
//
|
|
// remap ??
|
|
//
|
|
|
|
Error = RemapAddress();
|
|
|
|
if( Error != ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// ?? if(Error == ??) {
|
|
// CleanupCache();
|
|
// MEMMAP_FILE();
|
|
// }
|
|
//
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// init the extended file map memory.
|
|
//
|
|
|
|
memset(
|
|
&_EntryArray[OldNumUrlInternalEntries],
|
|
0,
|
|
GlobalMapFileGrowSize );
|
|
|
|
Error = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
UnlockFileMap();
|
|
return( Error );
|
|
}
|
|
|
|
DWORD
|
|
MEMMAP_FILE::GetAndSetNextFreeEntry(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
This private member function computes the first available free entry
|
|
index.
|
|
|
|
Arguments:
|
|
|
|
NONE.
|
|
|
|
Return Value:
|
|
|
|
Next available free entry Index.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
LPDWORD BitMap;
|
|
DWORD BitMask;
|
|
DWORD Index;
|
|
DWORD i;
|
|
|
|
//
|
|
// process all DWORDs but the last.
|
|
//
|
|
|
|
for ( i = 0; i < _NumBitMapDWords - 1; i++) {
|
|
|
|
//
|
|
// process this dword only if it has free bit.
|
|
//
|
|
|
|
if( _HeaderInfo->AllocationBitMap[i] != 0xFFFFFFFF ) {
|
|
|
|
BitMap = &_HeaderInfo->AllocationBitMap[i];
|
|
BitMask = 0x1;
|
|
|
|
for( Index = 0; Index < NUM_BITS_IN_DWORD; Index++, BitMask <<= 1) {
|
|
|
|
if( (*BitMap & BitMask) == 0 ) {
|
|
|
|
//
|
|
// found the first free bit; set it before we return.
|
|
//
|
|
|
|
*BitMap |= BitMask;
|
|
return( NUM_BITS_IN_DWORD * i + Index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TcpsvcsDbgAssert( i == _NumBitMapDWords - 1);
|
|
|
|
//
|
|
// process last bitmap (partial) DWORD.
|
|
//
|
|
|
|
BitMap = &_HeaderInfo->AllocationBitMap[i];
|
|
BitMask = 0x1;
|
|
|
|
//
|
|
// compute number of ramaining bits.
|
|
//
|
|
|
|
DWORD NumBits =
|
|
_HeaderInfo->NumUrlInternalEntries -
|
|
((_NumBitMapDWords - 1) * NUM_BITS_IN_DWORD);
|
|
|
|
TcpsvcsDbgAssert( NumBits <= NUM_BITS_IN_DWORD );
|
|
|
|
for( Index = 0; Index < NumBits; Index++, BitMask << 1) {
|
|
|
|
if( (*BitMap & BitMask) == 0 ) {
|
|
|
|
//
|
|
// found the first free bit; set it before we return.
|
|
//
|
|
|
|
*BitMap |= BitMask;
|
|
return( NUM_BITS_IN_DWORD * i + Index);
|
|
}
|
|
}
|
|
|
|
return( 0xFFFFFFFF);
|
|
}
|
|
|
|
MEMMAP_FILE::MEMMAP_FILE(
|
|
LPWSTR PathName
|
|
)
|
|
/*++
|
|
|
|
MEMMAP_FILE object constructor.
|
|
|
|
Arguments:
|
|
|
|
PathName : full path name of the memory mapped file.
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// init all variable.
|
|
//
|
|
|
|
_FullPathName = NULL;
|
|
_FileName = NULL;
|
|
|
|
_FileSize = 0;
|
|
_FileHandle = NULL;
|
|
_FileMappingHandle = NULL;
|
|
_BaseAddr = NULL;
|
|
_HeaderInfo = NULL;
|
|
_EntryArray = NULL;
|
|
_NumBitMapDWords = 0;
|
|
|
|
_VirtualCommitedSize = 0;
|
|
_VirtualBaseAddr = NULL;
|
|
|
|
//
|
|
// initialize crit sect first.
|
|
//
|
|
|
|
InitializeCriticalSection( &_CritSect );
|
|
|
|
LockFileMap();
|
|
|
|
//
|
|
// make path name.
|
|
//
|
|
|
|
_FullPathName = (LPWSTR)CacheHeap->Alloc(
|
|
wcslen(PathName) * sizeof( WCHAR ) +
|
|
sizeof(PATH_CONNECT_STRING) );
|
|
|
|
if( _FullPathName == NULL ) {
|
|
_Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
wcscpy( _FullPathName, PathName );
|
|
wcscat( _FullPathName, PATH_CONNECT_STRING );
|
|
|
|
//
|
|
// make file name.
|
|
//
|
|
|
|
_FileName = (LPWSTR)CacheHeap->Alloc(
|
|
wcslen(_FullPathName) * sizeof( WCHAR ) +
|
|
sizeof(MEMMAP_FILE_NAME) );
|
|
|
|
if( _FileName == NULL ) {
|
|
_Status = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
wcscpy( _FileName, _FullPathName );
|
|
wcscat( _FileName, (LPWSTR)MEMMAP_FILE_NAME );
|
|
|
|
//
|
|
// Create/Open memory mapped file.
|
|
//
|
|
|
|
_FileHandle =
|
|
CreateFileW(
|
|
_FileName,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, // don't share this file while it is being used.
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
// FILE_FLAG_WRITE_THROUGH | // ??
|
|
FILE_FLAG_RANDOM_ACCESS,
|
|
NULL );
|
|
|
|
_Status = GetLastError();
|
|
|
|
if( _FileHandle == INVALID_HANDLE_VALUE ) {
|
|
_FileHandle = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// check to this file is new.
|
|
//
|
|
|
|
if ( _Status == ERROR_ALREADY_EXISTS ) {
|
|
|
|
//
|
|
// old file.
|
|
//
|
|
|
|
_Status = ERROR_SUCCESS;
|
|
_NewFile = FALSE;
|
|
_FileSize = GetFileSize( _FileHandle, NULL );
|
|
|
|
if( _FileSize == 0xFFFFFFFF ) {
|
|
_Status = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
TcpsvcsDbgAssert( _FileSize >= GlobalMapFileGrowSize );
|
|
TcpsvcsDbgAssert( (_FileSize % GlobalMapFileGrowSize) == 0 );
|
|
}
|
|
else if( _Status == ERROR_SUCCESS ) {
|
|
|
|
BOOL BoolError;
|
|
DWORD FilePointer;
|
|
|
|
//
|
|
// new file
|
|
//
|
|
|
|
_NewFile = TRUE;
|
|
|
|
//
|
|
// set initial file size.
|
|
//
|
|
|
|
_FileSize = GlobalMapFileGrowSize;
|
|
|
|
FilePointer = SetFilePointer( _FileHandle, _FileSize, NULL, FILE_BEGIN );
|
|
|
|
if( FilePointer == 0xFFFFFFFF ) {
|
|
_Status = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
BoolError = SetEndOfFile( _FileHandle );
|
|
|
|
if( BoolError != TRUE ) {
|
|
_Status = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else {
|
|
|
|
//
|
|
// we should reach here.
|
|
//
|
|
|
|
TcpsvcsDbgAssert( FALSE );
|
|
}
|
|
|
|
//
|
|
// Create virtual address space.
|
|
//
|
|
|
|
_VirtualBaseAddr =
|
|
VirtualAlloc(
|
|
NULL, // get a address space that can fit MAX_MAPFILE_SIZE
|
|
MAX_MAPFILE_SIZE, // maximum size.
|
|
MEM_RESERVE, // reserve the address range.
|
|
PAGE_READWRITE ); // for read and write.
|
|
|
|
if( _VirtualBaseAddr == NULL ) {
|
|
|
|
_Status = GetLastError();
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS, "VirtualAlloc failed, %ld.\n", _Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
_VirtualCommitedSize = MAX_MAPFILE_SIZE;
|
|
|
|
//
|
|
// this is our base address for memory mapped file.
|
|
//
|
|
|
|
_BaseAddr = _VirtualBaseAddr;
|
|
|
|
_Status = RemapAddress();
|
|
|
|
if( _Status != ERROR_SUCCESS ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// initialize pointers.
|
|
//
|
|
|
|
_HeaderInfo = (LPMEMMAP_HEADER)_BaseAddr;
|
|
_EntryArray = (LPURL_FILEMAP_ENTRY)
|
|
((LPBYTE)_BaseAddr + sizeof(MEMMAP_HEADER));
|
|
|
|
//
|
|
// validate the file content if the file is not new.
|
|
//
|
|
|
|
if( _NewFile != TRUE ) {
|
|
|
|
if( ValidateCache( PathName ) == FALSE) {
|
|
|
|
BOOL BoolError;
|
|
DWORD FilePointer;
|
|
|
|
//
|
|
// cache file content is invalid, so dump it and restart
|
|
// fresh.
|
|
//
|
|
|
|
_NewFile = TRUE;
|
|
|
|
//
|
|
// set initial file size.
|
|
//
|
|
|
|
_FileSize = GlobalMapFileGrowSize;
|
|
|
|
FilePointer = SetFilePointer( _FileHandle, _FileSize, NULL, FILE_BEGIN );
|
|
|
|
if( FilePointer == 0xFFFFFFFF ) {
|
|
_Status = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// close mapping.
|
|
//
|
|
|
|
UnmapViewOfFile( _BaseAddr );
|
|
CloseHandle( _FileMappingHandle );
|
|
_FileMappingHandle = NULL;
|
|
|
|
BoolError = SetEndOfFile( _FileHandle );
|
|
|
|
if( BoolError != TRUE ) {
|
|
_Status = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// remap address space.
|
|
//
|
|
|
|
_Status = RemapAddress();
|
|
|
|
if( _Status != ERROR_SUCCESS ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// delete all url files from this directory.
|
|
//
|
|
|
|
BoolError = DeleteFiles( CACHE_FILES );
|
|
|
|
}
|
|
}
|
|
|
|
if( _NewFile == TRUE ) {
|
|
|
|
//
|
|
// it is a brand new file, initialize file header.
|
|
//
|
|
|
|
wcscpy( _HeaderInfo->FileSignature, CACHE_SIGNATURE );
|
|
|
|
_HeaderInfo->NumUrlInternalEntries =
|
|
(GlobalMapFileGrowSize - sizeof(MEMMAP_HEADER)) /
|
|
sizeof(URL_FILEMAP_ENTRY);
|
|
|
|
memset( _HeaderInfo->AllocationBitMap, 0, sizeof(_HeaderInfo->AllocationBitMap) );
|
|
memset( _EntryArray, 0, (GlobalMapFileGrowSize - sizeof(MEMMAP_HEADER)) );
|
|
}
|
|
|
|
//
|
|
// compute number of bimap DWORDs used.
|
|
//
|
|
|
|
_NumBitMapDWords =
|
|
(_HeaderInfo->NumUrlInternalEntries + (NUM_BITS_IN_DWORD - 1)) /
|
|
NUM_BITS_IN_DWORD; //cell
|
|
|
|
//
|
|
// we are done.
|
|
//
|
|
|
|
_Status = ERROR_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if( _Status != ERROR_SUCCESS ) {
|
|
|
|
TcpsvcsDbgPrint(( DEBUG_ERRORS,
|
|
"MEMMAP_FILE::MEMMAP_FILE failed, %ld\n", _Status ));
|
|
}
|
|
|
|
UnlockFileMap();
|
|
return;
|
|
}
|
|
|
|
MEMMAP_FILE::~MEMMAP_FILE(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
MEMMAP_FILE object destructor.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Unmap view.
|
|
//
|
|
|
|
LockFileMap();
|
|
|
|
if( _BaseAddr != NULL ) {
|
|
UnmapViewOfFile( _BaseAddr );
|
|
_BaseAddr = NULL;
|
|
_HeaderInfo = NULL;
|
|
_EntryArray = NULL;
|
|
}
|
|
|
|
//
|
|
// close mapping handle object.
|
|
//
|
|
|
|
if( _FileMappingHandle != NULL ) {
|
|
CloseHandle( _FileMappingHandle );
|
|
_FileMappingHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// release any virtual memory reserved.
|
|
//
|
|
|
|
if( _VirtualBaseAddr != NULL ) {
|
|
|
|
VirtualFree( _VirtualBaseAddr, 0, MEM_RELEASE );
|
|
|
|
_VirtualBaseAddr = NULL;
|
|
_VirtualCommitedSize = 0;
|
|
}
|
|
|
|
//
|
|
// close file handle.
|
|
//
|
|
|
|
if( _FileHandle != NULL ) {
|
|
CloseHandle( _FileHandle );
|
|
_FileHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// at last free up the file name space.
|
|
//
|
|
|
|
CacheHeap->Free( _FileName );
|
|
CacheHeap->Free( _FullPathName );
|
|
|
|
UnlockFileMap();
|
|
|
|
//
|
|
// delete crit sect.
|
|
//
|
|
|
|
DeleteCriticalSection( &_CritSect );
|
|
|
|
return;
|
|
}
|
|
|
|
LPURL_FILEMAP_ENTRY
|
|
MEMMAP_FILE::AllocateUrlEntry(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Member function that returns an free entry from the cache list. If
|
|
none is available free, it grows the map file, makes more free entry.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Pointer to a Url Entry.
|
|
If NULL, GetStatus() will return actual error code.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// find next free entry.
|
|
//
|
|
|
|
LockFileMap();
|
|
DWORD FreeEntryIndex = GetAndSetNextFreeEntry();
|
|
|
|
if( FreeEntryIndex == 0xFFFFFFFF ) {
|
|
|
|
//
|
|
// the map file is full, grow it now.
|
|
//
|
|
|
|
_Status = GrowMapFile( );
|
|
|
|
if( _Status != ERROR_SUCCESS ) {
|
|
|
|
//
|
|
// can't grow file.
|
|
//
|
|
|
|
UnlockFileMap();
|
|
return( NULL );
|
|
}
|
|
|
|
FreeEntryIndex = GetAndSetNextFreeEntry();
|
|
TcpsvcsDbgAssert( FreeEntryIndex != 0xFFFFFFFF );
|
|
}
|
|
|
|
TcpsvcsDbgAssert( FreeEntryIndex < _HeaderInfo->NumUrlInternalEntries );
|
|
|
|
LPURL_FILEMAP_ENTRY NewEntry = &_EntryArray[FreeEntryIndex];
|
|
|
|
//
|
|
// make sure it is an unused entry.
|
|
//
|
|
|
|
TcpsvcsDbgAssert( NewEntry->UrlName[0] == '\0' );
|
|
|
|
UnlockFileMap();
|
|
return( NewEntry );
|
|
}
|
|
|
|
BOOL
|
|
MEMMAP_FILE::FreeUrlEntry(
|
|
LPURL_FILEMAP_ENTRY UrlEntry
|
|
)
|
|
/*++
|
|
|
|
This public member function frees up a file cache entry.
|
|
|
|
Arguments:
|
|
|
|
UrlEntry : pointer to the entry that being freed.
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the entry is successfully removed from the cache.
|
|
FALSE - otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD Index;
|
|
|
|
LockFileMap();
|
|
|
|
//
|
|
// validate the pointer passed in.
|
|
//
|
|
|
|
if( (UrlEntry < _EntryArray) ||
|
|
(UrlEntry >= &_EntryArray[_HeaderInfo->NumUrlInternalEntries] ) ) {
|
|
|
|
UnlockFileMap();
|
|
return( FALSE );
|
|
}
|
|
|
|
DWORD Offset = ((DWORD)UrlEntry - (DWORD)_EntryArray);
|
|
|
|
if( Offset % sizeof( URL_FILEMAP_ENTRY ) ) {
|
|
|
|
//
|
|
// pointer does not point to a valid entry.
|
|
//
|
|
|
|
UnlockFileMap();
|
|
return( FALSE );
|
|
}
|
|
|
|
//
|
|
// compute the index.
|
|
//
|
|
|
|
Index = Offset / sizeof( URL_FILEMAP_ENTRY );
|
|
|
|
//
|
|
// cleanup url name
|
|
//
|
|
|
|
TcpsvcsDbgAssert( strlen(UrlEntry->UrlName) > 0 );
|
|
|
|
UrlEntry->UrlName[0] = '\0';
|
|
|
|
//
|
|
// unmark the index bit in the map.
|
|
//
|
|
|
|
LPDWORD BitMapDWord =
|
|
&_HeaderInfo->AllocationBitMap[Index / NUM_BITS_IN_DWORD];
|
|
|
|
DWORD BitMask = 0x1 << (Index % NUM_BITS_IN_DWORD);
|
|
|
|
*BitMapDWord &= ~BitMask;
|
|
|
|
UnlockFileMap();
|
|
return( TRUE );
|
|
}
|
|
|
|
LPURL_FILEMAP_ENTRY
|
|
MEMMAP_FILE::FindFirstEntry(
|
|
LPDWORD Handle
|
|
)
|
|
/*++
|
|
|
|
This public member function finds the first used cache entry. When
|
|
enumurating the entries in the cache, the user should calls this
|
|
member function first and continue to call FindNextEntry member
|
|
function until it returns NULL (reaches end of list).
|
|
|
|
Arguments:
|
|
|
|
Handle - pointer to a dword location where the enum handle is
|
|
returned.
|
|
|
|
Return Value:
|
|
|
|
pointer to the first URL entry.
|
|
NULL if the cache is empty.
|
|
|
|
--*/
|
|
{
|
|
|
|
*Handle = 0;
|
|
|
|
return( FindNextEntry( Handle ) );
|
|
}
|
|
|
|
LPURL_FILEMAP_ENTRY
|
|
MEMMAP_FILE::FindNextEntry(
|
|
LPDWORD Handle
|
|
)
|
|
/*++
|
|
|
|
This public member function finds the next used cache entry. When
|
|
enumurating the entries in the cache, the user should calls this
|
|
member function first and continue to call FindNextEntry member
|
|
function until it returns NULL (reaches end of list).
|
|
|
|
Arguments:
|
|
|
|
Handle - pointer to a dword location where the enum handle is
|
|
returned.
|
|
|
|
|
|
Return Value:
|
|
|
|
pointer to the next URL entry.
|
|
NULL if there is no more cache entry.
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
DWORD NextIndex = *Handle;
|
|
|
|
//
|
|
// Handle contain the index of the next bit map bit to test.
|
|
//
|
|
|
|
DWORD StartBit = (NextIndex % (NUM_BITS_IN_DWORD));
|
|
DWORD BitMapIndex = (NextIndex / (NUM_BITS_IN_DWORD));
|
|
|
|
DWORD BitMask = (0x1 << StartBit);
|
|
|
|
|
|
LockFileMap();
|
|
while( BitMapIndex < _NumBitMapDWords ) {
|
|
|
|
DWORD BitMap = _HeaderInfo->AllocationBitMap[BitMapIndex];
|
|
|
|
for( i = StartBit; i < NUM_BITS_IN_DWORD; i++, BitMask <<= 1 ) {
|
|
|
|
if( BitMap & BitMask ) {
|
|
|
|
//
|
|
// found the next used bit.
|
|
//
|
|
|
|
//
|
|
// compute the index to this used entry.
|
|
//
|
|
|
|
DWORD Index = BitMapIndex * NUM_BITS_IN_DWORD + i;
|
|
|
|
//
|
|
// set the next co-ordinate in the handle for next lookup.
|
|
//
|
|
|
|
*Handle = Index + 1;
|
|
|
|
UnlockFileMap();
|
|
return( &_EntryArray[Index] );
|
|
}
|
|
}
|
|
|
|
//
|
|
// reset the StartBit and BitMask for lookup in the next DWORD
|
|
// BitMap.
|
|
//
|
|
|
|
StartBit = 0;
|
|
BitMask = 0x1;
|
|
|
|
//
|
|
// move to next DWORD BitMap.
|
|
//
|
|
|
|
BitMapIndex++;
|
|
}
|
|
|
|
//
|
|
// we reached end of enum list.
|
|
//
|
|
|
|
UnlockFileMap();
|
|
return( NULL );
|
|
}
|
|
|
|
|
|
DWORD
|
|
MEMMAP_FILE::Sync(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
This public member function forces to commit all changes to the disk.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Windows Error Code.
|
|
|
|
--*/
|
|
{
|
|
if( !FlushViewOfFile( _BaseAddr, 0 ) ) {
|
|
return( GetLastError() );
|
|
}
|
|
|
|
return( ERROR_SUCCESS );
|
|
}
|