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.
692 lines
14 KiB
692 lines
14 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 "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_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_hFile != INVALID_HANDLE_VALUE) {
|
|
::CloseHandle(m_hFile);
|
|
}
|
|
|
|
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,
|
|
PBY_HANDLE_FILE_INFORMATION pFileInfo,
|
|
PBYTE pFileBuff
|
|
)
|
|
{
|
|
m_FileAttributes = pFileInfo->dwFileAttributes;
|
|
m_nFileSizeLow = pFileInfo->nFileSizeLow;
|
|
m_nFileSizeHigh = pFileInfo->nFileSizeHigh;
|
|
|
|
|
|
m_ftLastWriteTime = pFileInfo->ftLastWriteTime;
|
|
|
|
//
|
|
// Save file buffer
|
|
//
|
|
|
|
m_pFileBuffer = pFileBuff;
|
|
|
|
//
|
|
// 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;
|
|
UINT64 Int64Value = (FILETIMEToUINT64(m_ftLastWriteTime) / 10000000) * 10000000;
|
|
|
|
m_CastratedLastWriteTime = UINT64ToFILETIME(Int64Value);
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
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;
|
|
FILETIME iNow, iFileTime;
|
|
|
|
if ( m_pFileBuffer ||
|
|
m_hFile != INVALID_HANDLE_VALUE ) {
|
|
::GetSystemTimeAsFileTime(&ftNow);
|
|
|
|
iNow = ftNow;
|
|
iFileTime = m_ftLastWriteTime;
|
|
|
|
if ((FILETIMEToUINT64(iNow) - FILETIMEToUINT64(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
|
|
//
|
|
|