/**********************************************************************/ /** 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 #include "tsunamip.hxx" #include #include #include #include #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 //