Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

747 lines
15 KiB

/**********************************************************************/
/** Microsoft Windows NT **/
/** Copyright(c) Microsoft Corp., 1998 **/
/**********************************************************************/
/*
fileinfo.cxx
This module contains the methods for TS_OPEN_FILE_INFO
FILE HISTORY:
MCourage 09-Dec-1997 Created
*/
#include <tsunami.hxx>
#include "tsunamip.hxx"
#include <iistypes.hxx>
#include <acache.hxx>
#include <imd.h>
#include <mb.hxx>
#include "string.h"
#include <uspud.h>
#include "filecach.hxx"
#include "filehash.hxx"
#include "tlcach.h"
#include "etagmb.h"
GENERIC_MAPPING g_gmFile = {
FILE_GENERIC_READ,
FILE_GENERIC_WRITE,
FILE_GENERIC_EXECUTE,
FILE_ALL_ACCESS
};
ALLOC_CACHE_HANDLER * TS_OPEN_FILE_INFO::sm_pachFileInfos;
CRITICAL_SECTION TS_OPEN_FILE_INFO::sm_cs;
TS_OPEN_FILE_INFO::TS_OPEN_FILE_INFO()
: m_Signature(TS_FILE_INFO_SIGNATURE),
m_hFile(INVALID_HANDLE_VALUE),
m_hCopyFile(INVALID_HANDLE_VALUE),
m_pFileBuffer(0),
m_hUser(INVALID_HANDLE_VALUE),
m_cbSecDescMaxSize(0),
m_pSecurityDescriptor(m_abSecDesc),
m_fSecurityDescriptor(FALSE),
m_cchHttpInfo(0),
m_cchETag(0),
m_ETagIsWeak(TRUE),
m_bIsCached(FALSE),
m_state(FI_UNINITIALIZED),
m_lRefCount(0),
m_dwIORefCount(0),
m_TTL(1),
m_FileAttributes(0xFFFFFFFF),
m_pvContext( NULL ),
m_pfnFreeRoutine( NULL )
{
m_FileKey.m_cbFileName = 0;
m_FileKey.m_pszFileName = NULL;
}
BOOL
TS_OPEN_FILE_INFO::SetContext(
PVOID pvContext,
PCONTEXT_FREE_ROUTINE pfnFreeRoutine
)
/*++
Routine Description
Used by external components (SSI) to set an opaque context and a
free routine to be called when freeing the context. This allows SSI
to associate the SSI parsed template with actual TS_OPEN_FILE_INFO
Arguments
None.
Return
TRUE if the context was set. FALSE if there was already a context.
--*/
{
BOOL fRet;
Lock();
if ( m_pvContext != NULL )
{
fRet = FALSE;
}
else
{
m_pvContext = pvContext;
m_pfnFreeRoutine = pfnFreeRoutine;
fRet = TRUE;
}
Unlock();
return fRet;
}
PVOID
TS_OPEN_FILE_INFO::QueryContext(
VOID
) const
/*++
Routine Description
Returns the context associated with the TS_OPEN_FILE_INFO
Arguments
None.
Return
Pointer to context
--*/
{
PVOID pvContext;
Lock();
pvContext = m_pvContext;
Unlock();
return pvContext;
}
VOID
TS_OPEN_FILE_INFO::FreeContext(
VOID
)
/*++
Routine Description
Frees the opaque context by calling the free routine
Arguments
None.
Return
None
--*/
{
Lock();
if ( m_pvContext )
{
if ( m_pfnFreeRoutine )
{
__try
{
m_pfnFreeRoutine( m_pvContext );
}
__finally
{
m_pvContext = NULL;
}
}
else
{
DBG_ASSERT( FALSE );
}
}
Unlock();
}
BOOL
FileFlushFilterContext(
TS_OPEN_FILE_INFO *pOpenFile,
PVOID pv
)
/*++
Routine Description
Filter used by FilteredFlushFileCache to select those TS_OPEN_FILE_INFO
objects which have a context. This routine will actually do the freeing
and will return FALSE. This is done to prevent premature flushing of
the cache when SSI shuts down.
Arguments
pOpenFile - TS_OPEN_FILE_INFO object
pv - Unused context
Return
Always returns FALSE
--*/
{
DBG_ASSERT( pOpenFile );
pOpenFile->FreeContext();
DBG_ASSERT( !pOpenFile->QueryContext() );
return FALSE;
}
VOID
TsFlushFilesWithContext(
VOID
)
/*++
Routine Description
Exported routine used by SSI to free all opaque contexts. This is called
before SSINC.DLL is unloaded to prevent AVs in context free
Arguments
None
Return
None
--*/
{
FilteredFlushFileCache( FileFlushFilterContext, NULL );
}
BOOL
TS_OPEN_FILE_INFO::SetFileName(const CHAR * pszFileName)
{
DBG_ASSERT( pszFileName );
DBG_ASSERT( pszFileName[0] );
m_FileKey.m_cbFileName = strlen(pszFileName);
if (m_FileKey.m_cbFileName < TS_DEFAULT_PATH_SIZE) {
//
// It fits in our fixed size buffer
//
m_FileKey.m_pszFileName = m_FileKey.m_achFileNameBuf;
} else {
//
// we need a bigger buffer
//
m_FileKey.m_pszFileName = new CHAR[m_FileKey.m_cbFileName + 1];
}
if (NULL != m_FileKey.m_pszFileName) {
memcpy(m_FileKey.m_pszFileName, pszFileName, m_FileKey.m_cbFileName + 1);
} else {
m_FileKey.m_cbFileName = 0;
}
return (0 != m_FileKey.m_cbFileName);
}
TS_OPEN_FILE_INFO::~TS_OPEN_FILE_INFO( VOID)
{
DBG_ASSERT( 0 == m_lRefCount );
DBG_ASSERT( 0 == m_dwIORefCount );
DBG_ASSERT( CheckSignature() );
m_Signature = TS_FREE_FILE_INFO_SIGNATURE;
if (m_FileKey.m_pszFileName
&& (m_FileKey.m_achFileNameBuf != m_FileKey.m_pszFileName)) {
delete [] m_FileKey.m_pszFileName;
}
if (m_pFileBuffer) {
DWORD dwError;
dwError = ReleaseFromMemoryCache(
m_pFileBuffer,
m_nFileSizeLow
);
DBG_ASSERT(dwError == ERROR_SUCCESS);
}
if (m_pSecurityDescriptor
&& (m_pSecurityDescriptor != m_abSecDesc)) {
FREE(m_pSecurityDescriptor);
}
if (m_pvContext)
{
FreeContext();
}
}
BOOL
TS_OPEN_FILE_INFO::AccessCheck(
IN HANDLE hUser,
IN BOOL bCache
)
{
DBG_ASSERT(hUser != INVALID_HANDLE_VALUE);
//
// If it's the same user that last opened the file
// then we know we have access.
//
if (hUser == m_hUser) {
TraceCheckpointEx(TS_MAGIC_ACCESS_CHECK, (PVOID)hUser, (PVOID)(LONG_PTR)0xffffffff);
return TRUE;
}
//
// If we've got a security descriptor we can check
// against that, otherwise fail the check.
//
BYTE psFile[SIZE_PRIVILEGE_SET];
DWORD dwPS;
DWORD dwGrantedAccess;
BOOL fAccess;
dwPS = sizeof(psFile);
((PRIVILEGE_SET*)&psFile)->PrivilegeCount = 0;
if (m_fSecurityDescriptor
&& ::AccessCheck(m_pSecurityDescriptor,
hUser,
FILE_GENERIC_READ,
&g_gmFile,
(PRIVILEGE_SET*)psFile,
&dwPS,
&dwGrantedAccess,
&fAccess) ) {
if (fAccess && bCache) {
m_hUser = hUser;
}
TraceCheckpointEx(TS_MAGIC_ACCESS_CHECK, (PVOID)hUser, (PVOID) (ULONG_PTR) fAccess);
return fAccess;
} else {
return FALSE;
}
}
VOID
TS_OPEN_FILE_INFO::SetFileInfo(
HANDLE hFile,
HANDLE hUser,
PSECURITY_DESCRIPTOR pSecDesc,
DWORD dwSecDescSize
)
{
BY_HANDLE_FILE_INFORMATION FileInfo;
//
// Fetch file information
//
BOOL fReturn;
fReturn = GetFileInformationByHandle(
hFile,
&FileInfo
);
m_FileAttributes = FileInfo.dwFileAttributes;
m_nFileSizeLow = FileInfo.nFileSizeLow;
m_nFileSizeHigh = FileInfo.nFileSizeHigh;
m_ftLastWriteTime = (__int64)*(__int64 *) &FileInfo.ftLastWriteTime;
//
// Handle common info
//
SetFileInfoHelper(hFile, hUser, pSecDesc, dwSecDescSize);
}
VOID
TS_OPEN_FILE_INFO::SetFileInfo(
PBYTE pFileBuff,
HANDLE hCopyFile,
HANDLE hFile,
HANDLE hUser,
PSECURITY_DESCRIPTOR pSecDesc,
DWORD dwSecDescSize,
PSPUD_FILE_INFORMATION pFileInfo
)
{
//
// Save SPUD specific parameters
//
m_FileAttributes = pFileInfo->BasicInformation.FileAttributes;
m_nFileSizeLow = pFileInfo->StandardInformation.EndOfFile.LowPart;
m_nFileSizeHigh = pFileInfo->StandardInformation.EndOfFile.HighPart;
m_ftLastWriteTime = (__int64)*(__int64 *) &pFileInfo->BasicInformation.LastWriteTime;
//
// Save file buffer
//
m_pFileBuffer = pFileBuff;
m_hCopyFile = hCopyFile;
//
// Handle common info
//
SetFileInfoHelper(hFile, hUser, pSecDesc, dwSecDescSize);
}
VOID
TS_OPEN_FILE_INFO::SetFileInfoHelper(
HANDLE hFile,
HANDLE hUser,
PSECURITY_DESCRIPTOR pSecDesc,
DWORD dwSecDescSize
)
{
//
// Save away the given parameters
//
m_hFile = hFile;
m_hUser = hUser;
m_pSecurityDescriptor = pSecDesc;
m_cbSecDescMaxSize = dwSecDescSize;
if (dwSecDescSize)
m_fSecurityDescriptor = TRUE;
//
// Generate some other file attributes
//
DWORD dwChangeNumber = ETagChangeNumber::GetChangeNumber();
BOOL fReturn = TRUE;
*((__int64 *)&m_CastratedLastWriteTime)
= (*((__int64 *)&m_ftLastWriteTime) / 10000000)
* 10000000;
//
// Make the ETag
//
m_ETagIsWeak = TRUE;
m_cchETag = FORMAT_ETAG(m_achETag, *(FILETIME*) &m_ftLastWriteTime,
dwChangeNumber);
//
// Make the ETag strong if possible
//
MakeStrongETag();
//
// Turn off the hidden attribute if this is a root directory listing
// (root some times has the bit set for no apparent reason)
//
if ( m_FileAttributes & FILE_ATTRIBUTE_HIDDEN )
{
CHAR * pszFileName = m_FileKey.m_pszFileName;
if ( m_FileKey.m_cbFileName >= 2 )
{
if ( pszFileName[ 1 ] == ':' )
{
if ( ( pszFileName[ 2 ] == '\0' ) ||
( pszFileName[ 2 ] == '\\' && pszFileName[ 3 ] == '\0' ) )
{
//
// This looks like a local root. Mask out the bit
//
m_FileAttributes &= ~FILE_ATTRIBUTE_HIDDEN;
}
}
}
}
}
VOID
TS_OPEN_FILE_INFO::CloseHandle(
void
)
{
HANDLE hFile;
PBYTE pFileBuffer;
BOOL bClose = FALSE;
BOOL bRelease = FALSE;
DWORD dwError;
Lock();
ASSERT( DisableTsunamiCaching
|| (m_state == FI_FLUSHED)
|| (m_bIsCached == FALSE) );
ASSERT( m_dwIORefCount == 0 );
m_state = FI_CLOSED;
if (m_pFileBuffer) {
pFileBuffer = m_pFileBuffer;
m_pFileBuffer = NULL;
bRelease = TRUE;
}
if (m_hFile != INVALID_HANDLE_VALUE) {
hFile = m_hFile;
m_hFile = INVALID_HANDLE_VALUE;
bClose = TRUE;
}
DBG_ASSERT(m_hCopyFile == INVALID_HANDLE_VALUE);
Unlock();
if (bRelease) {
TraceCheckpointEx(TS_MAGIC_CLOSE, pFileBuffer, (PVOID) 1);
dwError = ReleaseFromMemoryCache(
pFileBuffer,
m_nFileSizeLow
);
DBG_ASSERT(dwError == ERROR_SUCCESS);
}
if (bClose) {
TraceCheckpointEx(TS_MAGIC_CLOSE, hFile, 0);
::CloseHandle(hFile);
}
}
INT
FormatETag(
PCHAR pszBuffer,
const FILETIME& rft,
DWORD mdchange)
{
PCHAR psz = pszBuffer;
PBYTE pbTime = (PBYTE) &rft;
const char szHex[] = "0123456789abcdef";
*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) mdchange, psz, 16));
*psz++ = '\"';
*psz = '\0';
return (INT)(psz - pszBuffer);
}
VOID
TS_OPEN_FILE_INFO::MakeStrongETag(
VOID
)
/*++
Routine Description
Try and make an ETag 'strong'. To do this we see if the difference
between now and the last modified date is greater than our strong ETag
delta - if so, we mark the ETag strong.
Arguments
None.
Returns
Nothing.
--*/
{
FILETIME ftNow;
SYSTEMTIME stNow;
__int64 iNow, iFileTime;
if ( m_pFileBuffer ||
m_hFile != INVALID_HANDLE_VALUE ) {
::GetSystemTimeAsFileTime(&ftNow);
iNow = (__int64)*(__int64 *)&ftNow;
iFileTime = (__int64)*(__int64 *)&m_ftLastWriteTime;
if ((iNow - iFileTime) > STRONG_ETAG_DELTA )
{
m_ETagIsWeak = FALSE;
}
}
}
BOOL
TS_OPEN_FILE_INFO::SetHttpInfo(
IN PSTR pszInfo,
IN INT InfoLength
)
/*++
Routine Description
Set the "Last-Modified:" header field in the file structure.
Arguments
pszDate - pointer to the header value to save
InfoLength - length of the header value to save
Returns
TRUE if information was cached,
FALSE if not cached
--*/
{
if ( !m_ETagIsWeak && InfoLength < sizeof(m_achHttpInfo)-1 ) {
CopyMemory( m_achHttpInfo, pszInfo, InfoLength+1 );
//
// this MUST be set after updating the array,
// as this is checked to know if the array content is valid.
//
m_cchHttpInfo = InfoLength;
return TRUE;
}
return FALSE;
} // TS_OPEN_FILE_INFO::SetHttpInfo
/*
* Static members
*/
BOOL
TS_OPEN_FILE_INFO::Initialize(
DWORD dwMaxFiles
)
{
ALLOC_CACHE_CONFIGURATION acConfig = { 1, dwMaxFiles, sizeof(TS_OPEN_FILE_INFO)};
if ( NULL != sm_pachFileInfos) {
// already initialized
return ( TRUE);
}
sm_pachFileInfos = new ALLOC_CACHE_HANDLER( "FileInfos",
&acConfig);
if ( sm_pachFileInfos ) {
INITIALIZE_CRITICAL_SECTION(&sm_cs);
}
return ( NULL != sm_pachFileInfos);
}
VOID
TS_OPEN_FILE_INFO::Cleanup(
VOID
)
{
if ( NULL != sm_pachFileInfos) {
delete sm_pachFileInfos;
sm_pachFileInfos = NULL;
DeleteCriticalSection(&sm_cs);
}
}
VOID
TS_OPEN_FILE_INFO::Lock(
VOID
)
{
EnterCriticalSection(&sm_cs);
}
VOID
TS_OPEN_FILE_INFO::Unlock(
VOID
)
{
LeaveCriticalSection(&sm_cs);
}
VOID *
TS_OPEN_FILE_INFO::operator new( size_t s)
{
DBG_ASSERT( s == sizeof( TS_OPEN_FILE_INFO));
// allocate from allocation cache.
DBG_ASSERT( NULL != sm_pachFileInfos);
return (sm_pachFileInfos->Alloc());
}
VOID
TS_OPEN_FILE_INFO::operator delete( void * pOpenFile)
{
DBG_ASSERT( NULL != pOpenFile);
// free to the allocation pool
DBG_ASSERT( NULL != sm_pachFileInfos);
DBG_REQUIRE( sm_pachFileInfos->Free(pOpenFile));
return;
}
//
// fileopen.cxx
//