1810 lines
38 KiB
C++
1810 lines
38 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 1999 Microsoft Corporation
|
||
|
|
||
|
Module Name :
|
||
|
filecache.cxx
|
||
|
|
||
|
Abstract:
|
||
|
A file cache (filename->W3_FILE_INFO cache)
|
||
|
|
||
|
Author:
|
||
|
Bilal Alam (balam) 11-Nov-2000
|
||
|
|
||
|
Environment:
|
||
|
Win32 - User Mode
|
||
|
|
||
|
Project:
|
||
|
ULW3.DLL
|
||
|
--*/
|
||
|
|
||
|
#include "precomp.hxx"
|
||
|
|
||
|
#define STRONG_ETAG_DELTA 30000000
|
||
|
|
||
|
#define SIZE_PRIVILEGE_SET 128
|
||
|
|
||
|
ALLOC_CACHE_HANDLER * W3_FILE_INFO::sm_pachW3FileInfo;
|
||
|
|
||
|
GENERIC_MAPPING g_gmFile = {
|
||
|
FILE_GENERIC_READ,
|
||
|
FILE_GENERIC_WRITE,
|
||
|
FILE_GENERIC_EXECUTE,
|
||
|
FILE_ALL_ACCESS
|
||
|
};
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO_KEY::CreateCacheKey(
|
||
|
WCHAR * pszFileKey,
|
||
|
DWORD cchFileKey,
|
||
|
BOOL fCopy
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Initialize a file cache key
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pszFileKey - filename
|
||
|
cchFileKey - size of filename
|
||
|
fCopy - TRUE if we should copy into key buffer, otherwise just keep ref
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if ( fCopy )
|
||
|
{
|
||
|
hr = _strFileKey.Copy( pszFileKey );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
_pszFileKey = _strFileKey.QueryStr();
|
||
|
_cchFileKey = _strFileKey.QueryCCH();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_pszFileKey = pszFileKey;
|
||
|
_cchFileKey = cchFileKey;
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
W3_FILE_INFO::~W3_FILE_INFO(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
W3_FILE_INFO_CACHE* pFileCache;
|
||
|
|
||
|
DBG_ASSERT( CheckSignature() );
|
||
|
|
||
|
_dwSignature = W3_FILE_INFO_SIGNATURE_FREE;
|
||
|
|
||
|
//
|
||
|
// Clear any associated object
|
||
|
//
|
||
|
|
||
|
LockCacheEntry();
|
||
|
|
||
|
if ( _pAssociatedObject != NULL )
|
||
|
{
|
||
|
_pAssociatedObject->Cleanup();
|
||
|
_pAssociatedObject = NULL;
|
||
|
}
|
||
|
|
||
|
UnlockCacheEntry();
|
||
|
|
||
|
//
|
||
|
// Release the contents buffer if it exists
|
||
|
//
|
||
|
|
||
|
if ( _pFileBuffer != NULL )
|
||
|
{
|
||
|
pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
|
||
|
|
||
|
hr = pFileCache->ReleaseFromMemoryCache( _pFileBuffer,
|
||
|
_nFileSizeLow );
|
||
|
DBG_ASSERT( SUCCEEDED( hr ) );
|
||
|
|
||
|
_pFileBuffer = NULL;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Close the file handle if it still around
|
||
|
//
|
||
|
|
||
|
if ( _hFile != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
CloseHandle( _hFile );
|
||
|
_hFile = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
W3_FILE_INFO::SetAssociatedObject(
|
||
|
ASSOCIATED_FILE_OBJECT * pObject
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Associate object with this cache entry
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pObject - Object to associate
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
BOOL
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
LockCacheEntry();
|
||
|
|
||
|
if ( _pAssociatedObject == NULL )
|
||
|
{
|
||
|
_pAssociatedObject = pObject;
|
||
|
fRet = TRUE;
|
||
|
}
|
||
|
|
||
|
UnlockCacheEntry();
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
PSECURITY_DESCRIPTOR
|
||
|
W3_FILE_INFO::QuerySecDesc(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Return security descriptor
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
pointer to security descriptor
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if ( _pFileBuffer != NULL )
|
||
|
{
|
||
|
//
|
||
|
// The file is cached, therefore we must have security already
|
||
|
//
|
||
|
|
||
|
return _bufSecDesc.QueryPtr();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DBG_ASSERT( _hFile != NULL );
|
||
|
|
||
|
if ( FAILED( ReadSecurityDescriptor() ) )
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return _bufSecDesc.QueryPtr();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::GenerateETag(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Generate ETag string
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
CHAR * psz = _achETag;
|
||
|
PBYTE pbTime = (PBYTE) &_ftLastWriteTime;
|
||
|
DWORD dwChangeNumber;
|
||
|
const CHAR szHex[] = "0123456789abcdef";
|
||
|
FILETIME ftNow;
|
||
|
__int64 iNow;
|
||
|
__int64 iFileTime;
|
||
|
|
||
|
//
|
||
|
// Is this ETag weak? If so put the preceding W/
|
||
|
//
|
||
|
|
||
|
GetSystemTimeAsFileTime(&ftNow);
|
||
|
iNow = (__int64)*(__int64 *)&ftNow;
|
||
|
iFileTime = (__int64)*(__int64 *)&_ftLastWriteTime;
|
||
|
|
||
|
if ( ( iNow - iFileTime ) <= STRONG_ETAG_DELTA )
|
||
|
{
|
||
|
//
|
||
|
// This is a weak ETag
|
||
|
//
|
||
|
|
||
|
*psz++ = 'W';
|
||
|
*psz++ = '/';
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// System change number is from the metabase
|
||
|
//
|
||
|
|
||
|
dwChangeNumber = g_pW3Server->QuerySystemChangeNumber();
|
||
|
|
||
|
//
|
||
|
// Generate the meat of the ETag
|
||
|
//
|
||
|
|
||
|
*psz++ = '\"';
|
||
|
for (int i = 0; i < 8; i++)
|
||
|
{
|
||
|
BYTE b = *pbTime++;
|
||
|
BYTE bH = b >> 4;
|
||
|
if (bH != 0)
|
||
|
*psz++ = szHex[bH];
|
||
|
*psz++ = szHex[b & 0xF];
|
||
|
}
|
||
|
*psz++ = ':';
|
||
|
psz += strlen(_itoa((DWORD) dwChangeNumber, psz, 16));
|
||
|
*psz++ = '\"';
|
||
|
*psz = '\0';
|
||
|
|
||
|
_cchETag = DIFF(psz - _achETag);
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::GenerateLastModifiedTimeString(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Generate the Last-Modified-Time header string
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
SYSTEMTIME st;
|
||
|
|
||
|
FileTimeToSystemTime( &_ftLastWriteTime, &st );
|
||
|
|
||
|
if ( !SystemTimeToGMT( st,
|
||
|
_achLastModified,
|
||
|
sizeof(_achLastModified) ) )
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::DoAccessCheck(
|
||
|
FILE_CACHE_USER * pFileCacheUser
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Check whether given token has access to this file
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pFileCacheUser - User to access cache with
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BYTE psFile[SIZE_PRIVILEGE_SET];
|
||
|
DWORD dwPS;
|
||
|
DWORD dwGrantedAccess;
|
||
|
BOOL fAccess;
|
||
|
|
||
|
if ( pFileCacheUser == NULL )
|
||
|
{
|
||
|
DBG_ASSERT( FALSE );
|
||
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we don't have a security descriptor, then local system must have
|
||
|
// accessed the file originally. Just return success
|
||
|
//
|
||
|
|
||
|
if ( pFileCacheUser->_hToken == NULL )
|
||
|
{
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we have a last-user-sid, and the caller provided a sid, then do a
|
||
|
// quick check of sid equality
|
||
|
//
|
||
|
|
||
|
if ( QueryLastSid() != NULL &&
|
||
|
pFileCacheUser->_pSid != NULL )
|
||
|
{
|
||
|
if ( EqualSid( QueryLastSid(), pFileCacheUser->_pSid ) )
|
||
|
{
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Ok. Just use the token and cached security descriptor
|
||
|
//
|
||
|
|
||
|
dwPS = sizeof(psFile);
|
||
|
((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
|
||
|
|
||
|
//
|
||
|
// We must have a security descriptor if we've cached the file
|
||
|
//
|
||
|
|
||
|
DBG_ASSERT( QuerySecDesc() );
|
||
|
|
||
|
if ( !AccessCheck( QuerySecDesc(),
|
||
|
pFileCacheUser->_hToken,
|
||
|
FILE_GENERIC_READ,
|
||
|
&g_gmFile,
|
||
|
(PRIVILEGE_SET*)psFile,
|
||
|
&dwPS,
|
||
|
&dwGrantedAccess,
|
||
|
&fAccess ) || !fAccess )
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( ERROR_ACCESS_DENIED );
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::OpenFile(
|
||
|
STRU & strFileName,
|
||
|
FILE_CACHE_USER * pOpeningUser
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Open the given file (but don't read in the file contents). This method
|
||
|
does the minimum needed to allow the caller to make a reasonable
|
||
|
decision about whether this file should be cached here or in UL
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
strFileName - file name to open
|
||
|
pOpeningUser - User to open file under
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
||
|
STACK_STRU( strFilePath, MAX_PATH + 1 );
|
||
|
HRESULT hr = NO_ERROR;
|
||
|
BOOL fImpersonated = FALSE;
|
||
|
BY_HANDLE_FILE_INFORMATION FileInfo;
|
||
|
|
||
|
if ( pOpeningUser == NULL )
|
||
|
{
|
||
|
DBG_ASSERT( FALSE );
|
||
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// First make a cache key
|
||
|
//
|
||
|
|
||
|
hr = _cacheKey.CreateCacheKey( strFileName.QueryStr(),
|
||
|
strFileName.QueryCCH(),
|
||
|
TRUE );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Avoid the infamous ::$DATA bug
|
||
|
//
|
||
|
|
||
|
if ( wcschr( strFileName.QueryStr() + 2, L':' ) != NULL )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Turn off NT file canonicalization
|
||
|
//
|
||
|
|
||
|
hr = MakePathCanonicalizationProof( strFileName.QueryStr(),
|
||
|
&strFilePath );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We may need to impersonate some other user to open the file
|
||
|
//
|
||
|
|
||
|
if ( pOpeningUser->_hToken != NULL )
|
||
|
{
|
||
|
if ( !SetThreadToken( NULL, pOpeningUser->_hToken ) )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
goto Finished;
|
||
|
}
|
||
|
fImpersonated = TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Open the file
|
||
|
//
|
||
|
|
||
|
hFile = CreateFileW( strFilePath.QueryStr(),
|
||
|
GENERIC_READ,
|
||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
|
NULL,
|
||
|
OPEN_EXISTING,
|
||
|
FILE_DIRECTORY_FILE | FILE_FLAG_BACKUP_SEMANTICS,
|
||
|
NULL );
|
||
|
if ( hFile == INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Stop impersonating
|
||
|
//
|
||
|
|
||
|
if ( fImpersonated )
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
fImpersonated = FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We shouldn't be opening byte streams (like COM, LPT)
|
||
|
//
|
||
|
|
||
|
if ( GetFileType( hFile ) != FILE_TYPE_DISK )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get file attributes
|
||
|
//
|
||
|
|
||
|
if ( !GetFileInformationByHandle( hFile,
|
||
|
&FileInfo ) )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
|
||
|
CloseHandle( hFile );
|
||
|
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set the minimum properties now
|
||
|
//
|
||
|
|
||
|
_hFile = hFile;
|
||
|
_ftLastWriteTime = FileInfo.ftLastWriteTime;
|
||
|
_dwFileAttributes = FileInfo.dwFileAttributes;
|
||
|
_nFileSizeLow = FileInfo.nFileSizeLow;
|
||
|
_nFileSizeHigh = FileInfo.nFileSizeHigh;
|
||
|
|
||
|
*((__int64 *)&_CastratedLastWriteTime)
|
||
|
= (*((__int64 *)&_ftLastWriteTime) / 10000000) * 10000000;
|
||
|
|
||
|
//
|
||
|
// Create the ETag and LastModified strings
|
||
|
//
|
||
|
|
||
|
hr = GenerateETag();
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
hr = GenerateLastModifiedTimeString();
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Turn off the hidden attribute if this is a root directory listing
|
||
|
// (root some times has the bit set for no apparent reason)
|
||
|
//
|
||
|
|
||
|
if ( _dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )
|
||
|
{
|
||
|
if ( strFileName.QueryCCH() >= 2 )
|
||
|
{
|
||
|
if ( strFileName.QueryStr()[ 1 ] == L':' )
|
||
|
{
|
||
|
if ( ( strFileName.QueryStr()[ 2 ] == L'\0' ) ||
|
||
|
( strFileName.QueryStr()[ 2 ] == L'\\' &&
|
||
|
strFileName.QueryStr()[ 3 ] == L'\0' ) )
|
||
|
{
|
||
|
//
|
||
|
// This looks like a local root. Mask out the bit
|
||
|
//
|
||
|
|
||
|
_dwFileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Finished:
|
||
|
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
if ( fImpersonated )
|
||
|
{
|
||
|
RevertToSelf();
|
||
|
fImpersonated = FALSE;
|
||
|
}
|
||
|
|
||
|
if ( hFile != INVALID_HANDLE_VALUE )
|
||
|
{
|
||
|
CloseHandle( hFile );
|
||
|
hFile = INVALID_HANDLE_VALUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::ReadSecurityDescriptor(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Read security descriptor for current file
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD cbRequired;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Cache the security descriptor
|
||
|
//
|
||
|
|
||
|
if ( !GetKernelObjectSecurity( _hFile,
|
||
|
OWNER_SECURITY_INFORMATION
|
||
|
| GROUP_SECURITY_INFORMATION
|
||
|
| DACL_SECURITY_INFORMATION,
|
||
|
(PSECURITY_DESCRIPTOR) _bufSecDesc.QueryPtr(),
|
||
|
_bufSecDesc.QuerySize(),
|
||
|
&cbRequired ) )
|
||
|
{
|
||
|
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER )
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
|
||
|
DBG_ASSERT( cbRequired > _bufSecDesc.QuerySize() );
|
||
|
|
||
|
if ( !_bufSecDesc.Resize( cbRequired ) )
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Try again with the bigger buffer. No more excuses
|
||
|
//
|
||
|
|
||
|
if ( !GetKernelObjectSecurity( _hFile,
|
||
|
OWNER_SECURITY_INFORMATION
|
||
|
| GROUP_SECURITY_INFORMATION
|
||
|
| DACL_SECURITY_INFORMATION,
|
||
|
(PSECURITY_DESCRIPTOR) _bufSecDesc.QueryPtr(),
|
||
|
_bufSecDesc.QuerySize(),
|
||
|
&cbRequired ) )
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::MakeCacheable(
|
||
|
FILE_CACHE_USER * pFileUser
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Make the file cacheable by reading contents into memory, and caching
|
||
|
the security descriptor
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pFileUser - User trying to open file
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
HRESULT hr;
|
||
|
W3_FILE_INFO_CACHE* pFileCache;
|
||
|
|
||
|
if ( pFileUser == NULL )
|
||
|
{
|
||
|
DBG_ASSERT( FALSE );
|
||
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
||
|
}
|
||
|
|
||
|
DBG_ASSERT( IsCacheable() );
|
||
|
|
||
|
//
|
||
|
// We must have a file handle if we're here
|
||
|
//
|
||
|
|
||
|
DBG_ASSERT( _hFile != INVALID_HANDLE_VALUE );
|
||
|
|
||
|
//
|
||
|
// Get the security descriptor
|
||
|
//
|
||
|
|
||
|
hr = ReadSecurityDescriptor();
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// On top of reading the security descriptor, we will also store the
|
||
|
// last sid accessing the file if available
|
||
|
//
|
||
|
|
||
|
if ( pFileUser->_pSid != NULL )
|
||
|
{
|
||
|
if ( GetLengthSid( pFileUser->_pSid ) <= sizeof( _abLastSid ) )
|
||
|
{
|
||
|
memcpy( _abLastSid,
|
||
|
pFileUser->_pSid,
|
||
|
GetLengthSid( pFileUser->_pSid ) );
|
||
|
|
||
|
_pLastSid = (PSID) _abLastSid;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Now read the contents of the file into memory since we cannot cache
|
||
|
// the file handle itself
|
||
|
//
|
||
|
|
||
|
pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
|
||
|
|
||
|
dwError = pFileCache->ReadFileIntoMemoryCache( _hFile,
|
||
|
_nFileSizeLow,
|
||
|
(PVOID*) &_pFileBuffer );
|
||
|
if ( dwError == ERROR_SUCCESS )
|
||
|
{
|
||
|
//
|
||
|
// OK. The contents are now in memory. We can now close the file
|
||
|
//
|
||
|
|
||
|
CloseHandle( _hFile );
|
||
|
_hFile = INVALID_HANDLE_VALUE;
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( dwError );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
W3_FILE_INFO::IsCacheable(
|
||
|
VOID
|
||
|
) const
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Is this file cacheable? Specically, we should we even attempt to cache
|
||
|
this file?
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if cacheable
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
LARGE_INTEGER liFileSize;
|
||
|
W3_FILE_INFO_CACHE * pFileCache;
|
||
|
|
||
|
pFileCache = (W3_FILE_INFO_CACHE*) QueryCache();
|
||
|
DBG_ASSERT( pFileCache != NULL );
|
||
|
|
||
|
//
|
||
|
// Are we past the limit of file entries?
|
||
|
//
|
||
|
|
||
|
if ( pFileCache->QueryElementLimitExceeded() )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// No caching of directories
|
||
|
//
|
||
|
|
||
|
if ( _dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// No caching of file sizes greater than the configured threshold
|
||
|
//
|
||
|
|
||
|
liFileSize.LowPart = _nFileSizeLow;
|
||
|
liFileSize.HighPart = _nFileSizeHigh;
|
||
|
|
||
|
if ( liFileSize.QuadPart > pFileCache->QueryFileSizeThreshold() )
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
BOOL
|
||
|
W3_FILE_INFO::QueryIsOkToFlushDirmon(
|
||
|
WCHAR * pszPath,
|
||
|
DWORD cchPath
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Determine whether this file entry should be flushed, given the path
|
||
|
which has changed (dir monitor changed)
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pszPath - Path which changed
|
||
|
cchPath - Size of path changed
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
TRUE if we should flush, else FALSE
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DBG_ASSERT( _cacheKey._pszFileKey != NULL );
|
||
|
|
||
|
if ( _wcsnicmp( _cacheKey._pszFileKey,
|
||
|
pszPath,
|
||
|
cchPath ) == 0 )
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//static
|
||
|
HRESULT
|
||
|
W3_FILE_INFO::Initialize(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Initialize W3_FILE_INFO lookaside
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
ALLOC_CACHE_CONFIGURATION acConfig;
|
||
|
HRESULT hr;
|
||
|
|
||
|
//
|
||
|
// Initialize allocation lookaside
|
||
|
//
|
||
|
|
||
|
acConfig.nConcurrency = 1;
|
||
|
acConfig.nThreshold = 100;
|
||
|
acConfig.cbSize = sizeof( W3_FILE_INFO );
|
||
|
|
||
|
DBG_ASSERT( sm_pachW3FileInfo == NULL );
|
||
|
|
||
|
sm_pachW3FileInfo = new ALLOC_CACHE_HANDLER( "W3_FILE_INFO",
|
||
|
&acConfig );
|
||
|
|
||
|
if ( sm_pachW3FileInfo == NULL )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
||
|
|
||
|
DBGPRINTF(( DBG_CONTEXT,
|
||
|
"Error initializing sm_pachW3FileInfo. hr = 0x%x\n",
|
||
|
hr ));
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//static
|
||
|
VOID
|
||
|
W3_FILE_INFO::Terminate(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Cleanup W3_FILE_INFO lookaside
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if ( sm_pachW3FileInfo != NULL )
|
||
|
{
|
||
|
delete sm_pachW3FileInfo;
|
||
|
sm_pachW3FileInfo = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//static
|
||
|
VOID
|
||
|
W3_FILE_INFO_CACHE::MemoryCacheAdjustor(
|
||
|
PVOID pCache,
|
||
|
BOOLEAN TimerOrWaitFired
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Called to adjust our memory cache size if necessary
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pCache - Points to file cache
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
W3_FILE_INFO_CACHE * pFileCache;
|
||
|
MEMORYSTATUSEX MemoryStatus;
|
||
|
|
||
|
pFileCache = (W3_FILE_INFO_CACHE*) pCache;
|
||
|
|
||
|
MemoryStatus.dwLength = sizeof( MemoryStatus );
|
||
|
|
||
|
GlobalMemoryStatusEx( &MemoryStatus );
|
||
|
|
||
|
EnterCriticalSection( &( pFileCache->_csMemCache ) );
|
||
|
|
||
|
pFileCache->_cbMemCacheLimit = min(
|
||
|
MemoryStatus.ullAvailPhys + pFileCache->_cbMemCacheCurrentSize,
|
||
|
MemoryStatus.ullTotalVirtual ) / 2;
|
||
|
|
||
|
LeaveCriticalSection( &( pFileCache->_csMemCache ) );
|
||
|
}
|
||
|
|
||
|
W3_FILE_INFO_CACHE::W3_FILE_INFO_CACHE()
|
||
|
{
|
||
|
_cbFileSizeThreshold = DEFAULT_FILE_SIZE_THRESHOLD;
|
||
|
_cbMemoryCacheSize = 0;
|
||
|
_cMaxFileEntries = 0;
|
||
|
_cbMemCacheLimit = 0;
|
||
|
_cbMemCacheCurrentSize = 0;
|
||
|
_cbMaxMemCacheSize = 0;
|
||
|
_hMemCacheHeap = NULL;
|
||
|
_hTimer = NULL;
|
||
|
_fEnableCache = TRUE;
|
||
|
}
|
||
|
|
||
|
W3_FILE_INFO_CACHE::~W3_FILE_INFO_CACHE()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO_CACHE::InitializeMemoryCache(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Initialize the memory cache
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL fRet;
|
||
|
HRESULT hr = NO_ERROR;
|
||
|
|
||
|
InitializeCriticalSection( &_csMemCache );
|
||
|
|
||
|
//
|
||
|
// If the memory cache size was not explicitly set, then we occasionally
|
||
|
// check memory status when determining what to cache
|
||
|
//
|
||
|
|
||
|
if ( _cbMemoryCacheSize == 0 )
|
||
|
{
|
||
|
MEMORYSTATUSEX MemoryStatus;
|
||
|
|
||
|
MemoryStatus.dwLength = sizeof( MemoryStatus );
|
||
|
|
||
|
//
|
||
|
// Get our own estimate of size of cache
|
||
|
//
|
||
|
|
||
|
GlobalMemoryStatusEx( &MemoryStatus );
|
||
|
|
||
|
_cbMemCacheLimit = min( MemoryStatus.ullAvailPhys,
|
||
|
MemoryStatus.ullTotalVirtual ) / 2;
|
||
|
|
||
|
//
|
||
|
// Setup timer so we can update our memory status
|
||
|
//
|
||
|
|
||
|
fRet = CreateTimerQueueTimer( &_hTimer,
|
||
|
NULL,
|
||
|
W3_FILE_INFO_CACHE::MemoryCacheAdjustor,
|
||
|
this,
|
||
|
30000,
|
||
|
30000,
|
||
|
WT_EXECUTELONGFUNCTION );
|
||
|
if ( !fRet )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
_cbMemCacheLimit = _cbMemoryCacheSize;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Allocate a private heap
|
||
|
//
|
||
|
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
_hMemCacheHeap = HeapCreate( 0, 0, 0 );
|
||
|
if ( _hMemCacheHeap == NULL )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
if ( _hMemCacheHeap != NULL )
|
||
|
{
|
||
|
HeapDestroy( _hMemCacheHeap );
|
||
|
_hMemCacheHeap = NULL;
|
||
|
}
|
||
|
|
||
|
if ( _hTimer != NULL )
|
||
|
{
|
||
|
DeleteTimerQueueTimer( NULL,
|
||
|
_hTimer,
|
||
|
INVALID_HANDLE_VALUE );
|
||
|
_hTimer = NULL;
|
||
|
}
|
||
|
|
||
|
DeleteCriticalSection( &_csMemCache );
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO_CACHE::ReadFileIntoMemoryCache(
|
||
|
IN HANDLE hFile,
|
||
|
IN DWORD cbFile,
|
||
|
OUT VOID ** ppvBuffer
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Read contents of file into a buffer
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
hFile - Handle to valid file
|
||
|
cbFile - Size of file ( ==> size of buffer )
|
||
|
ppvBuffer - Filled in with pointer to buffer with file contents. Set
|
||
|
to NULL on failure
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
VOID * pvBuffer = NULL;
|
||
|
DWORD cbRead;
|
||
|
OVERLAPPED Overlapped;
|
||
|
HRESULT hr = NO_ERROR;
|
||
|
|
||
|
DBG_ASSERT( hFile && ( hFile != INVALID_HANDLE_VALUE ) );
|
||
|
DBG_ASSERT( ppvBuffer != NULL );
|
||
|
|
||
|
//
|
||
|
// First check whether there will be room in cache for the blob
|
||
|
//
|
||
|
|
||
|
EnterCriticalSection( &_csMemCache );
|
||
|
|
||
|
if ( ( _cbMemCacheCurrentSize + cbFile ) > _cbMemCacheLimit )
|
||
|
{
|
||
|
//
|
||
|
// Not enough room for cache
|
||
|
//
|
||
|
|
||
|
LeaveCriticalSection( &_csMemCache );
|
||
|
|
||
|
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
||
|
}
|
||
|
|
||
|
_cbMemCacheCurrentSize += cbFile;
|
||
|
|
||
|
_cbMaxMemCacheSize = max( _cbMaxMemCacheSize, _cbMemCacheCurrentSize );
|
||
|
|
||
|
LeaveCriticalSection( &_csMemCache );
|
||
|
|
||
|
//
|
||
|
// Allocate blob for file
|
||
|
//
|
||
|
|
||
|
DBG_ASSERT( _hMemCacheHeap != NULL );
|
||
|
pvBuffer = HeapAlloc( _hMemCacheHeap, 0, cbFile );
|
||
|
if ( pvBuffer == NULL )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
goto Finished;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read file into blob
|
||
|
//
|
||
|
|
||
|
Overlapped.Offset = 0;
|
||
|
Overlapped.OffsetHigh = 0;
|
||
|
Overlapped.hEvent = NULL;
|
||
|
|
||
|
bRet = ReadFile( hFile,
|
||
|
pvBuffer,
|
||
|
cbFile,
|
||
|
&cbRead,
|
||
|
&Overlapped );
|
||
|
|
||
|
if ( !bRet )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
|
||
|
if ( hr != HRESULT_FROM_WIN32( ERROR_IO_PENDING ) )
|
||
|
{
|
||
|
//
|
||
|
// Something bad happened
|
||
|
//
|
||
|
|
||
|
goto Finished;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Reset the error lest we confuse ourselves later on cleanup
|
||
|
//
|
||
|
|
||
|
hr = NO_ERROR;
|
||
|
|
||
|
//
|
||
|
// Wait for async read to complete
|
||
|
//
|
||
|
|
||
|
bRet = GetOverlappedResult( hFile,
|
||
|
&Overlapped,
|
||
|
&cbRead,
|
||
|
TRUE );
|
||
|
if ( !bRet )
|
||
|
{
|
||
|
//
|
||
|
// Something bad happened
|
||
|
//
|
||
|
|
||
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
||
|
|
||
|
goto Finished;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Ensure that we read the number of bytes we expected to
|
||
|
//
|
||
|
|
||
|
if ( cbRead != cbFile )
|
||
|
{
|
||
|
hr = HRESULT_FROM_WIN32( ERROR_INVALID_DATA );
|
||
|
}
|
||
|
|
||
|
Finished:
|
||
|
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
//
|
||
|
// Undo changes to memory cache statistics
|
||
|
//
|
||
|
|
||
|
EnterCriticalSection( &_csMemCache );
|
||
|
|
||
|
_cbMemCacheCurrentSize -= cbFile;
|
||
|
|
||
|
LeaveCriticalSection( &_csMemCache );
|
||
|
|
||
|
if ( pvBuffer != NULL )
|
||
|
{
|
||
|
HeapFree( _hMemCacheHeap, 0, pvBuffer );
|
||
|
pvBuffer = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ppvBuffer = pvBuffer;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO_CACHE::ReleaseFromMemoryCache(
|
||
|
IN VOID * pvBuffer,
|
||
|
IN DWORD cbBuffer
|
||
|
)
|
||
|
/*++
|
||
|
Routine Description:
|
||
|
|
||
|
Release file content blob from cache
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pvBuffer - Buffer to release
|
||
|
cbBuffer - Size of buffer
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DBG_ASSERT( pvBuffer );
|
||
|
|
||
|
DBG_ASSERT( _hMemCacheHeap != NULL);
|
||
|
|
||
|
HeapFree( _hMemCacheHeap, 0, pvBuffer );
|
||
|
|
||
|
EnterCriticalSection( &_csMemCache );
|
||
|
|
||
|
_cbMemCacheCurrentSize -= cbBuffer;
|
||
|
|
||
|
LeaveCriticalSection( &_csMemCache );
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
W3_FILE_INFO_CACHE::TerminateMemoryCache(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Terminate memory cache
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
if ( _hTimer != NULL )
|
||
|
{
|
||
|
DeleteTimerQueueTimer( NULL,
|
||
|
_hTimer,
|
||
|
INVALID_HANDLE_VALUE );
|
||
|
_hTimer = NULL;
|
||
|
}
|
||
|
|
||
|
if ( _hMemCacheHeap != NULL )
|
||
|
{
|
||
|
HeapDestroy( _hMemCacheHeap );
|
||
|
_hMemCacheHeap = NULL;
|
||
|
}
|
||
|
|
||
|
DeleteCriticalSection( &_csMemCache );
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO_CACHE::GetFileInfo(
|
||
|
STRU & strFileName,
|
||
|
DIRMON_CONFIG * pDirmonConfig,
|
||
|
FILE_CACHE_USER * pOpeningUser,
|
||
|
BOOL fDoCache,
|
||
|
W3_FILE_INFO ** ppFileInfo
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Returns a W3_FILE_INFO for the given file path. Depending on fDoCache,
|
||
|
this W3_FILE_INFO will be cached
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
strFileName - file name to find
|
||
|
pDirmonConfig - Dir monitor config
|
||
|
pOpeningUser - Token for user accessing the cache
|
||
|
fDoCache - Set to TRUE if we should attempt to cache if possible
|
||
|
ppFileInfo - Points to W3_FILE_INFO on success
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
W3_FILE_INFO_KEY fileKey;
|
||
|
DIRMON_CONFIG DefaultDirmonConfig;
|
||
|
STACK_STRU( strParentDir, MAX_PATH );
|
||
|
WCHAR * pszParentDir;
|
||
|
W3_FILE_INFO * pFileInfo;
|
||
|
HRESULT hr;
|
||
|
STACK_STRU( strFilePath, MAX_PATH );
|
||
|
BOOL fShouldCacheHint = FALSE;
|
||
|
|
||
|
if ( ppFileInfo == NULL ||
|
||
|
pOpeningUser == NULL )
|
||
|
{
|
||
|
DBG_ASSERT( FALSE );
|
||
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
||
|
}
|
||
|
*ppFileInfo = NULL;
|
||
|
|
||
|
//
|
||
|
// We need to upper case the path to avoid a bunch of insensitive
|
||
|
// compares in the hash table lookup
|
||
|
//
|
||
|
|
||
|
hr = strFilePath.Copy( strFileName );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
_wcsupr( strFilePath.QueryStr() );
|
||
|
|
||
|
//
|
||
|
// If the cache is enabled, lookup there first
|
||
|
//
|
||
|
|
||
|
if ( QueryCacheEnabled() )
|
||
|
{
|
||
|
//
|
||
|
// Make a key for the lookup
|
||
|
//
|
||
|
|
||
|
hr = fileKey.CreateCacheKey( strFilePath.QueryStr(),
|
||
|
strFilePath.QueryCCH(),
|
||
|
FALSE );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Look it up
|
||
|
//
|
||
|
|
||
|
hr = FindCacheEntry( &fileKey,
|
||
|
(CACHE_ENTRY**) &pFileInfo,
|
||
|
&fShouldCacheHint );
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
DBG_ASSERT( pFileInfo != NULL );
|
||
|
|
||
|
hr = pFileInfo->DoAccessCheck( pOpeningUser );
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
*ppFileInfo = pFileInfo;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pFileInfo->DereferenceCacheEntry();
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We will simply open the file and return the object
|
||
|
//
|
||
|
|
||
|
pFileInfo = new W3_FILE_INFO( this );
|
||
|
if ( pFileInfo == NULL )
|
||
|
{
|
||
|
return HRESULT_FROM_WIN32( GetLastError() );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Open the file
|
||
|
//
|
||
|
|
||
|
hr = pFileInfo->OpenFile( strFilePath,
|
||
|
pOpeningUser );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
pFileInfo->DereferenceCacheEntry();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we aren't asked to cache the file, OR the file is not cacheable
|
||
|
// then we can return it now
|
||
|
//
|
||
|
|
||
|
if ( !QueryCacheEnabled() ||
|
||
|
!fDoCache ||
|
||
|
!pFileInfo->IsCacheable() ||
|
||
|
!fShouldCacheHint )
|
||
|
{
|
||
|
*ppFileInfo = pFileInfo;
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we're supposed to cache but no dirmon was configured, then just
|
||
|
// assume the directory to monitor is the parent directory (and token
|
||
|
// to use is NULL)
|
||
|
//
|
||
|
|
||
|
if ( pDirmonConfig == NULL )
|
||
|
{
|
||
|
DefaultDirmonConfig.hToken = NULL;
|
||
|
|
||
|
pszParentDir = wcsrchr( strFilePath.QueryStr(), L'\\' );
|
||
|
if ( pszParentDir != NULL )
|
||
|
{
|
||
|
hr = strParentDir.Copy( strFilePath.QueryStr(),
|
||
|
DIFF( pszParentDir - strFilePath.QueryStr() ) );
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
DefaultDirmonConfig.pszDirPath = strParentDir.QueryStr();
|
||
|
pDirmonConfig = &DefaultDirmonConfig;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// If we still don't have a dir mon configuration, then just don't cache
|
||
|
//
|
||
|
|
||
|
if ( pDirmonConfig == NULL )
|
||
|
{
|
||
|
*ppFileInfo = pFileInfo;
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Start monitoring the appropriate directory for changes
|
||
|
//
|
||
|
|
||
|
hr = pFileInfo->AddDirmonInvalidator( pDirmonConfig );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
//
|
||
|
// If we can't monitor the directory, then just don't cache the item
|
||
|
//
|
||
|
|
||
|
*ppFileInfo = pFileInfo;
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Attempt to cache the file. Caching the file means reading the
|
||
|
// contents into memory, as well as caching the security descriptor
|
||
|
//
|
||
|
|
||
|
hr = pFileInfo->MakeCacheable( pOpeningUser );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
*ppFileInfo = pFileInfo;
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Insert into the hash table. AddCacheEntry() will only error if some
|
||
|
// thing fatal happened. If someone else already added the item, that
|
||
|
// is not fatal and we will simply return this item and it will cleanup
|
||
|
// on dereference
|
||
|
//
|
||
|
|
||
|
AddCacheEntry( pFileInfo );
|
||
|
|
||
|
*ppFileInfo = pFileInfo;
|
||
|
|
||
|
return NO_ERROR;
|
||
|
}
|
||
|
|
||
|
HRESULT
|
||
|
W3_FILE_INFO_CACHE::Initialize(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Initialize the file cache
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
DWORD dwError;
|
||
|
DWORD dwType;
|
||
|
DWORD dwValue;
|
||
|
DWORD cbData;
|
||
|
DWORD csecTTL = DEFAULT_W3_FILE_INFO_CACHE_TTL;
|
||
|
DWORD csecActivity = DEFAULT_W3_FILE_INFO_CACHE_ACTIVITY;
|
||
|
HKEY hKey = NULL;
|
||
|
HRESULT hr;
|
||
|
CACHE_HINT_CONFIG cacheHintConfig;
|
||
|
|
||
|
//
|
||
|
// Read the registry configuration of the file cache.
|
||
|
// For now, that is just the legacy confiugration from IIS 5.x
|
||
|
//
|
||
|
|
||
|
dwError = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
||
|
L"System\\CurrentControlSet\\Services\\inetinfo\\Parameters",
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hKey );
|
||
|
if ( dwError == ERROR_SUCCESS )
|
||
|
{
|
||
|
DBG_ASSERT( hKey != NULL );
|
||
|
|
||
|
//
|
||
|
// Should we be file caching at all?
|
||
|
//
|
||
|
|
||
|
cbData = sizeof( DWORD );
|
||
|
dwError = RegQueryValueEx( hKey,
|
||
|
L"DisableMemoryCache",
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE) &dwValue,
|
||
|
&cbData );
|
||
|
if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
|
{
|
||
|
_fEnableCache = dwValue ? FALSE : TRUE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What is the biggest file we should cache in user mode?
|
||
|
//
|
||
|
|
||
|
cbData = sizeof( DWORD );
|
||
|
dwError = RegQueryValueEx( hKey,
|
||
|
L"MaxCachedFileSize",
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE) &dwValue,
|
||
|
&cbData );
|
||
|
if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
|
{
|
||
|
_cbFileSizeThreshold = dwValue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What is the size of our memory cache? Size is in MB
|
||
|
//
|
||
|
|
||
|
cbData = sizeof( DWORD );
|
||
|
dwError = RegQueryValueEx( hKey,
|
||
|
L"MemCacheSize",
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE)&dwValue,
|
||
|
&cbData );
|
||
|
if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
|
{
|
||
|
_cbMemoryCacheSize = dwValue * (1024 * 1024);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Read the maximum # of files in cache
|
||
|
//
|
||
|
|
||
|
cbData = sizeof( DWORD );
|
||
|
dwError = RegQueryValueEx( hKey,
|
||
|
L"MaxOpenFiles",
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE) &dwValue,
|
||
|
&cbData );
|
||
|
if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
|
{
|
||
|
_cMaxFileEntries = dwValue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What is the TTL for the file cache?
|
||
|
//
|
||
|
|
||
|
cbData = sizeof( DWORD );
|
||
|
dwError = RegQueryValueEx( hKey,
|
||
|
L"ObjectCacheTTL",
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE) &dwValue,
|
||
|
&cbData );
|
||
|
if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
|
{
|
||
|
csecTTL = dwValue;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// What is the activity period before putting into cache
|
||
|
//
|
||
|
|
||
|
cbData = sizeof( DWORD );
|
||
|
dwError = RegQueryValueEx( hKey,
|
||
|
L"ActivityPeriod",
|
||
|
NULL,
|
||
|
&dwType,
|
||
|
(LPBYTE) &dwValue,
|
||
|
&cbData );
|
||
|
if ( dwError == ERROR_SUCCESS && dwType == REG_DWORD )
|
||
|
{
|
||
|
csecActivity = dwValue;
|
||
|
}
|
||
|
|
||
|
RegCloseKey( hKey );
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize memory cache
|
||
|
//
|
||
|
|
||
|
hr = InitializeMemoryCache();
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Setup cache hint config (for now hardcoded)
|
||
|
//
|
||
|
|
||
|
if ( csecActivity != 0 )
|
||
|
{
|
||
|
cacheHintConfig.cmsecActivityWindow = csecActivity * 1000;
|
||
|
cacheHintConfig.cmsecScavengeTime = cacheHintConfig.cmsecActivityWindow * 2;
|
||
|
cacheHintConfig.cmsecTTL = cacheHintConfig.cmsecActivityWindow * 2;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// We'll use TTL for scavenge period, and expect two inactive periods to
|
||
|
// flush
|
||
|
//
|
||
|
|
||
|
hr = SetCacheConfiguration( csecTTL * 1000,
|
||
|
csecTTL * 1000,
|
||
|
CACHE_INVALIDATION_DIRMON_FLUSH |
|
||
|
CACHE_INVALIDATION_DIRMON_SPECIFIC,
|
||
|
csecActivity ? &cacheHintConfig : NULL );
|
||
|
if ( FAILED( hr ) )
|
||
|
{
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Initialize file info lookaside
|
||
|
//
|
||
|
|
||
|
return W3_FILE_INFO::Initialize();
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
W3_FILE_INFO_CACHE::Terminate(
|
||
|
VOID
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Terminate the file cache
|
||
|
|
||
|
Argument:
|
||
|
|
||
|
None
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
None
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
TerminateMemoryCache();
|
||
|
|
||
|
W3_FILE_INFO::Terminate();
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
W3_FILE_INFO_CACHE::DoDirmonInvalidationSpecific(
|
||
|
WCHAR * pszPath
|
||
|
)
|
||
|
/*++
|
||
|
|
||
|
Routine Description:
|
||
|
|
||
|
Handle dirmon invalidation
|
||
|
|
||
|
Arguments:
|
||
|
|
||
|
pszPath - Path which changed
|
||
|
|
||
|
Return Value:
|
||
|
|
||
|
HRESULT
|
||
|
|
||
|
--*/
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
W3_FILE_INFO_KEY fileKey;
|
||
|
|
||
|
DBG_ASSERT( pszPath != NULL );
|
||
|
|
||
|
//
|
||
|
// We're not flushing all, then just lookup given file and flush it
|
||
|
//
|
||
|
|
||
|
hr = fileKey.CreateCacheKey( pszPath,
|
||
|
wcslen( pszPath ),
|
||
|
FALSE );
|
||
|
if ( SUCCEEDED( hr ) )
|
||
|
{
|
||
|
FlushCacheEntry( &fileKey );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//static
|
||
|
W3_FILE_INFO_CACHE *
|
||
|
W3_FILE_INFO_CACHE::GetFileCache(
|
||
|
VOID
|
||
|
)
|
||
|
{
|
||
|
DBG_ASSERT( g_pW3Server != NULL );
|
||
|
return g_pW3Server->QueryFileCache();
|
||
|
}
|