Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

960 lines
22 KiB

/*++
Copyright (c) 1995-1996 Microsoft Corporation
Module Name :
ilogfile.cxx
Abstract:
This module defines helper functions for File logging
Author:
Murali R. Krishnan ( MuraliK ) 21-FEb-1996
Environment:
Win32 - User Mode
Project:
Internet Server DLL
Functions Exported:
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
# include "tcpdllp.hxx"
# include "inetlog.h"
# include "tssched.hxx"
# include "ilogfile.hxx"
# include "ilogcls.hxx"
/************************************************************
* Functions
************************************************************/
static VOID
LogFileFlushHandler(VOID * pContext);
/************************************************************
* File Buffers
************************************************************/
# if 0
ILOG_FILE_BUFFER::ILOG_FILE_BUFFER( VOID)
: m_pbBuffer ( NULL),
m_cbUsed ( 0),
m_cbMax ( 0)
{
} // ILOG_FILE_BUFFER::ILOG_FILE_BUFFER()
ILOG_FILE_BUFFER::~ILOG_FILE_BUFFER( VOID)
{
if ( m_pbBuffer != NULL) {
TCP_FREE( m_pbBuffer);
m_pbBuffer = NULL;
}
m_cbMax = 0;
} // ILOG_FILE_BUFFER::~ILOG_FILE_BUFFER()
DWORD
ILOG_FILE_BUFFER::AllocBuffer( IN DWORD cbSize)
{
if ( m_cbMax != 0 || m_pbBuffer != NULL) {
return ( ERROR_INVALID_PARAMETER);
}
m_pbBuffer = TCP_ALLOC( cbSize);
if ( m_pbBuffer == NULL) {
return ( ERROR_NOT_ENOUGH_MEMORY);
}
m_cbMax = cbSize;
return ( NO_ERROR);
} // ILOG_FILE_BUFFER::AllocBuffer()
# if DBG
VOID
ILOG_FILE_BUFFER::Print(VOID) const
{
DBGPRINTF(( DBG_CONTEXT,
" ILOG_FILE_BUFFER[%08x]: buffer = %08x; %d/%d\n",
this, m_pbBuffer, m_cbUsed, m_cbMax));
return;
} // ILOG_FILE_BUFFER::Print()
# endif // DBG
# endif // 0
/************************************************************
* Log File
************************************************************/
ILOG_FILE::ILOG_FILE(VOID)
/*++
This function constructs a new File object used for handling
log files.
The reference count is used to count the number of owners
of this object. It starts off with 1.
the ILOG_FILE::OpenFileForAppend() function
inrements refcount when a new owner is given this object
the ILOG_FILE::CloseFile() function
derements refcount when an owner relinquishes the object
--*/
:
m_cReferences ( 0),
m_hFile ( INVALID_HANDLE_VALUE),
m_pvBuffer ( NULL),
m_cbBufferSize ( 0),
m_cbBufferUsed ( 0)
{
memset( m_rgchFileName, 0, sizeof(m_rgchFileName));
InitializeCriticalSection( &m_csLock);
InitializeListHead( &m_listEntry);
IF_DEBUG( INETLOG) {
DBGPRINTF((DBG_CONTEXT,
"ILOG_FILE(%08x) is created\n",
this));
}
} // ILOG_FILE::ILOG_FILE()
ILOG_FILE::~ILOG_FILE(VOID)
/*++
This function cleans up state maintained within the object -
freeing up the memory and closing file handle.
It then destroys all state information maintained.
The reference count should be zero, indicating this object is no more in use.
--*/
{
DBG_ASSERT( m_cReferences == 0);
Lock();
//
// 1. Close the file - close also flushes the file contents
//
DBG_REQUIRE( Close());
//
// 2. Free up buffer space
//
if ( m_pvBuffer != NULL) {
LocalFree( m_pvBuffer);
m_pvBuffer = NULL;
}
m_cbBufferSize = 0;
Unlock();
DBG_REQUIRE( m_hFile == INVALID_HANDLE_VALUE);
DBG_REQUIRE( m_pvBuffer == NULL);
DBG_REQUIRE( IsListEmpty( &m_listEntry));
DeleteCriticalSection( &m_csLock);
IF_DEBUG( INETLOG) {
DBGPRINTF((DBG_CONTEXT,
"ILOG_FILE(%08x) is destroyed\n",
this));
}
return;
} // ILOG_FILE::~ILOG_FILE()
BOOL
ILOG_FILE::Write(IN PVOID pvData, IN DWORD cbData)
/*++
This function writes the data present in the input buffer of specified
length to the file.
For performance reasons, the function actually buffers data in the
internal buffers of ILOG_FILE object. Such buffered data is flushed
to disk later on when buffer (chain) is full or when
a flush call is made from a scheduled flush.
Arguments:
pvData pointer to buffer containing data to be written
cbData count of characters of data to be written.
Returns:
TRUE on success and FALSE if there is any error.
--*/
{
DWORD cbWritten = 0;
BOOL fReturn = TRUE;
BOOL fWrite = FALSE;
if ( m_hFile == INVALID_HANDLE_VALUE) {
SetLastError( ERROR_INVALID_PARAMETER);
return ( FALSE);
}
DBG_REQUIRE( m_cReferences > 0);
//
// 1. Check to see if we can write into the current buffer
// 2. If yes, write and return back
// 3. Queue up the current buffer for flushing
// 4. If a free buffer is available from freeBufferChain,
// pick it up and write data into new buffer & return
// 5. If no free buffer,
// cleanup one of the old buffer and go back to step 4.
//
// TBD:
// For Now: Do a simplified buffering
//
Lock();
DBG_ASSERT( m_hFile != INVALID_HANDLE_VALUE);
DBG_ASSERT( m_pvBuffer != NULL);
if ( m_cbBufferUsed + cbData > m_cbBufferSize) {
fReturn = Flush();
}
if ( fReturn && (m_cbBufferUsed + cbData < m_cbBufferSize)) {
//
// copy the data into the local buffer for future write
// this ensures batched writes
//
memmove(m_pvBuffer + m_cbBufferUsed, pvData, cbData);
m_cbBufferUsed += cbData;
cbWritten = cbData;
fWrite = TRUE;
}
if ( fReturn && !fWrite) {
//
// The log data is not written. Write it directly to file now.
//
// TBD: If we make sure there is a buffer always,
// then we do not need this conditional branch.
//
fReturn = WriteFile(m_hFile, pvData,
cbData,
&cbWritten, NULL);
fWrite = TRUE;
}
Unlock();
DBG_ASSERT( !fReturn || fWrite); // either failure or write succeeded
IF_DEBUG( INETLOG) {
DWORD dwError = (fReturn) ? NO_ERROR : GetLastError();
DBGPRINTF( ( DBG_CONTEXT,
" Wrote %d bytes out of data( %d bytes)"
" to file( %08x). Error = %d.\n",
cbWritten, cbData,
m_hFile,
dwError));
SetLastError(dwError);
}
return ( fReturn);
} // ILOG_FILE::Write()
BOOL
ILOG_FILE::Open(IN LPCWSTR pszFileName)
/*++
This function opens up a new file for appending data.
This function automatically sets the file pointer to be
at the end of file to just enable append to file.
This function should be called after locking this object
Arguments:
pszFileName - name of the file to open
Returns:
TRUE on success and FALSE if there is any failure.
--*/
{
BOOL fReturn = TRUE;
DBG_ASSERT( m_hFile == INVALID_HANDLE_VALUE);
//
// 1. Create a new file -- open a file if it already exists
//
m_hFile = CreateFileW(pszFileName,
GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ,
NULL, // security attributes
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL); // template file handle
fReturn = (m_hFile != INVALID_HANDLE_VALUE);
//
// 2. Set the file pointer at the end of the file (append mode)
//
if ( fReturn) {
if ( SetFilePointer( m_hFile, 0, NULL, FILE_END)
== (DWORD) -1L) {
DBGPRINTF(( DBG_CONTEXT,
"SetFilePointer(%ws, End) failed. Error=%u\n",
pszFileName, GetLastError()));
fReturn = FALSE;
DBG_REQUIRE( Close());
m_hFile = INVALID_HANDLE_VALUE;
}
}
//
// 3. Allocate space for buffernig data
//
DBG_ASSERT( g_cbInetLogFileBatching > 0);
if ( fReturn && m_pvBuffer == NULL ) {
DWORD cbBuffer = g_cbInetLogFileBatching; // cache global value.
m_pvBuffer = (LPBYTE ) LocalAlloc( LPTR, cbBuffer);
if ( m_pvBuffer != NULL) {
m_cbBufferSize = cbBuffer;
m_cbBufferUsed = 0;
} else {
DBG_REQUIRE( Close());
m_hFile = INVALID_HANDLE_VALUE;
fReturn = FALSE;
SetLastError( ERROR_NOT_ENOUGH_MEMORY);
}
}
//
// 4. Remember the name of the file on success
//
if ( fReturn) {
lstrcpynW( m_rgchFileName, pszFileName, sizeof(m_rgchFileName));
}
return ( fReturn);
} // ILOG_FILE::Open()
BOOL
ILOG_FILE::Close(VOID)
/*++
This function closes the open file. It flushes out file data
before closing the file.
This function should be called after locking this object
Arguments:
None
Returns:
TRUE on success and FALSE if there is any failure.
--*/
{
BOOL fReturn = TRUE;
if ( m_hFile != INVALID_HANDLE_VALUE) {
fReturn = Flush();
FlushFileBuffers( m_hFile);
fReturn = fReturn && CloseHandle( m_hFile);
if ( fReturn) {
m_hFile = INVALID_HANDLE_VALUE; // store the invalid handle
}
}
IF_DEBUG( INETLOG) {
DBGPRINTF( ( DBG_CONTEXT,
" ILOG_FILE::Close( %ws. Handle = %08x) returns %d."
" Error = %d\n",
m_rgchFileName,
m_hFile, fReturn,
( fReturn) ? NO_ERROR: GetLastError()));
}
return ( fReturn);
} // ILOG_FILE::Close()
BOOL
ILOG_FILE::Flush( VOID)
/*++
Flushes the current file.
The ILOG_FILE object should be locked before calling this function.
There should be a valid open log file for flushing.
Otherwise this function silently returns a TRUE.
Arguments:
None
Returns:
TRUE on success and FALSE if there is a failure.
--*/
{
BOOL fReturn = TRUE;
if ( m_hFile != INVALID_HANDLE_VALUE) {
//
// Write out all the data that is pending to be written
//
if ( m_pvBuffer != NULL && m_cbBufferUsed > 0) {
DWORD cbWritten;
fReturn = WriteFile(m_hFile, m_pvBuffer,
m_cbBufferUsed,
&cbWritten, NULL);
m_cbBufferUsed = 0;
}
IF_DEBUG( INETLOG) {
DBGPRINTF( ( DBG_CONTEXT,
" FlushFile( %ws. Handle = %08x) returns %d."
" Error = %d\n",
m_rgchFileName,
m_hFile, fReturn,
( fReturn) ? NO_ERROR: GetLastError()));
}
}
return ( fReturn);
} // ILOG_FILE::Flush()
# if DBG
VOID
ILOG_FILE::Print(VOID) const
{
DBGPRINTF(( DBG_CONTEXT,
" ILOG_FILE(%08x) for %ws\n"
" File Handle = %08x; References = %d\n"
" Buffer = %08x; Size = %d; Bytes Used = %d\n"
,
this, m_rgchFileName,
m_hFile, m_cReferences,
m_pvBuffer, m_cbBufferSize,
m_cbBufferUsed
));
return;
} // ILOG_FILE::Print()
# endif // DBG
/************************************************************
* Static Functions
************************************************************/
//
// Class static data initialization
//
LIST_ENTRY ILOG_FILE::sm_listFiles;
CRITICAL_SECTION ILOG_FILE::sm_csFiles;
DWORD ILOG_FILE::sm_nFiles = 0;
DWORD ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
DWORD
ILOG_FILE::Initialize(VOID)
{
InitializeListHead( &ILOG_FILE::sm_listFiles);
InitializeCriticalSection( &ILOG_FILE::sm_csFiles);
DBG_ASSERT( ILOG_FILE::sm_nFiles == 0);
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie == 0);
return ( NO_ERROR);
} // ILOG_FILE::Initialize()
DWORD
ILOG_FILE::Cleanup(VOID)
{
//
// 1. Flush & Close all the files on the list and
// delete them all
//
// Ideally, by the time this function is called,
// there should be no outstanding open files.
//
DBG_ASSERT( ILOG_FILE::sm_nFiles == 0);
if ( ILOG_FILE::sm_nFiles != 0) {
PLIST_ENTRY pListEntry;
PLIST_ENTRY pListEntryNext;
PILOG_FILE piFileScan;
ILOG_FILE::LockFilesList();
for(
pListEntry = ILOG_FILE::sm_listFiles.Flink,
pListEntryNext = &ILOG_FILE::sm_listFiles;
pListEntry != &ILOG_FILE::sm_listFiles;
pListEntry = pListEntryNext
) {
piFileScan = CONTAINING_RECORD( pListEntry, ILOG_FILE,
m_listEntry);
DBG_REQUIRE( piFileScan->Close());
ILOG_FILE::sm_nFiles--;
DBG_REQUIRE(piFileScan->Dereference() == 0);
pListEntryNext = pListEntry->Flink;
delete piFileScan;
} // for
ILOG_FILE::UnlockFilesList();
}
//
// 2. Remove the scheduler if any pending
//
// Again, if everything went fine, the scheduler
// should have been removed when the last file object
// was removed by ILOG_FILE::CloseFile()
//
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie == 0);
if ( ILOG_FILE::sm_dwFlushSchedulerCookie != 0) {
ILOG_FILE::LockFilesList();
if (RemoveWorkItem( ILOG_FILE::sm_dwFlushSchedulerCookie)) {
ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
}
ILOG_FILE::UnlockFilesList();
}
//
// 3. Destroy the global state
//
DBG_ASSERT( ILOG_FILE::sm_nFiles == 0);
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie == 0);
DBG_ASSERT( IsListEmpty( &ILOG_FILE::sm_listFiles));
DeleteCriticalSection( &sm_csFiles);
return ( NO_ERROR);
} // ILOG_FILE::Cleanup()
PILOG_FILE
ILOG_FILE::OpenFileForAppend(IN LPCWSTR pszFileName)
{
PILOG_FILE piLogFile = NULL;
PLIST_ENTRY pListEntry;
PILOG_FILE piFileScan;
//
// 1. Search for this file in the list of files
// 2. If present, return the ILOG_FILE pointer
// 3. If absent
// 4. Create a new ILOG_FILE object for the file
// 5. Opne the file, set it in append mode and create buffers
// 6. If this is the first file in list, schedule a flusher
// 7. Return the new ILOG_FILE object
//
// 1. Search for this file in the list of files
ILOG_FILE::LockFilesList();
for( pListEntry = ILOG_FILE::sm_listFiles.Flink;
pListEntry != &ILOG_FILE::sm_listFiles;
pListEntry = pListEntry->Flink
) {
piFileScan = CONTAINING_RECORD( pListEntry, ILOG_FILE,
m_listEntry);
if ( !lstrcmpiW( piFileScan->QueryFileName(), pszFileName)) {
// a match is found.
piLogFile = piFileScan;
piLogFile->Reference(); // increment reference count for object
}
} // for
// 2. If present, return the ILOG_FILE pointer
if ( piLogFile != NULL) {
UnlockFilesList();
return ( piLogFile);
}
// 3. If absent
// 4. Create a new ILOG_FILE object for the file
// 5. Opne the file, set it in append mode and create buffers
piLogFile = new ILOG_FILE();
if ( piLogFile != NULL) {
piLogFile->Lock();
if ( !piLogFile->Open( pszFileName)) {
// unable to open the log file
// return appropriate error
piLogFile->Close(); // to cleanup any temporary state
piLogFile->Unlock();
delete piLogFile;
piLogFile = NULL;
} else {
piLogFile->Reference();
InsertTailList( &sm_listFiles, &piLogFile->m_listEntry);
piLogFile->Unlock();
ILOG_FILE::sm_nFiles++;
}
}
// 6. If this is the first file in list, schedule a flusher
if ( ILOG_FILE::sm_nFiles == 1 && piLogFile != NULL) {
//
// This is the first log file that is created.
// Schedule a flusher for the files
//
DBG_REQUIRE( ILOG_FILE::ScheduleFlush());
}
ILOG_FILE::UnlockFilesList();
return ( piLogFile);
} // ILOG_FILE::OpenFileForAppend()
DWORD
ILOG_FILE::CloseFile( IN ILOG_FILE ** ppLogFile)
/*++
This function closes the file object specified in *ppLogFile
Arguments:
ppLogFile pointer to pointer to log file.
if cleanup is successful, then it stores NULL
in *ppLogFile
Returns:
NO_ERROR on success and error on failure
--*/
{
DWORD dwError = NO_ERROR;
PILOG_FILE pLogFile;
DBG_ASSERT( ppLogFile != NULL);
// 1. If valid file object, do cleanup
pLogFile = *ppLogFile;
if ( pLogFile != NULL) {
// 2. Flush out the log buffers
pLogFile->Lock();
DBG_REQUIRE( pLogFile->Flush());
pLogFile->Unlock();
// 3. prepare for cleanup, deref the object.
// If ref drops to 0, then close and destroy file object
if ( pLogFile->Dereference() == 0) {
pLogFile->Lock();
DBG_REQUIRE( pLogFile->Close());
pLogFile->Unlock();
ILOG_FILE::LockFilesList();
RemoveEntryList( &pLogFile->m_listEntry);
DBG_CODE( InitializeListHead( &pLogFile->m_listEntry));
ILOG_FILE::sm_nFiles--;
// 4. Remove scheduler if no more files remain open
if ( ILOG_FILE::sm_nFiles == 0 &&
ILOG_FILE::sm_dwFlushSchedulerCookie != 0) {
if (RemoveWorkItem( ILOG_FILE::sm_dwFlushSchedulerCookie)) {
ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
}
}
ILOG_FILE::UnlockFilesList();
//
// 5. Destroy the object
//
delete pLogFile;
}
}
// 6. Set state of cleanup in the log object
*ppLogFile = NULL;
return ( dwError);
} // ILOG_FILE::CloseFile()
BOOL
ILOG_FILE::ScheduleFlush(VOID)
/*++
This function schedules a flusher for files objects.
This should be called after holding the ILOG_FILE::LockFilesList()
If a flush has already been scheduled,
then this function silently returns 0.
Arguments:
None
Returns:
TRUE on success and FALSE if there is a problem in starting the scheduler
--*/
{
BOOL fReturn = TRUE;
if (sm_dwFlushSchedulerCookie == 0 &&
g_cmsecIlogFileFlushInterval != INFINITE
) {
sm_dwFlushSchedulerCookie =
ScheduleWorkItem((PFN_SCHED_CALLBACK ) LogFileFlushHandler,
NULL,
g_cmsecIlogFileFlushInterval);
fReturn = (sm_dwFlushSchedulerCookie != 0);
if ( !fReturn) {
DBGPRINTF(( DBG_CONTEXT,
"[FileLog] ScheduleWorkItem failed, error %d, "
" File Flush disabled\n",
GetLastError() ));
}
}
return ( fReturn);
} // ILOG_FILE::ScheduleFlush()
BOOL
ILOG_FILE::FlushHandler(VOID)
/*++
This class static function handles flushing for file objects open.
This handles callbacks from the scheduler and flushes
the buffers.
After successfully handling the flushes,
if the number of open files is non-zero it schedules
another flush operation.
Arguments:
None
Returns:
TRUE on success
FALSE if there is any problem in scheduling a flush.
--*/
{
BOOL fReturn = TRUE;
PLIST_ENTRY pListEntry;
PILOG_FILE piFileScan;
ILOG_FILE::LockFilesList();
DBG_ASSERT( ILOG_FILE::sm_dwFlushSchedulerCookie != 0);
ILOG_FILE::sm_dwFlushSchedulerCookie = 0;
// 1. Flush all open files
for( pListEntry = sm_listFiles.Flink;
pListEntry != &sm_listFiles;
pListEntry = pListEntry->Flink
) {
piFileScan = CONTAINING_RECORD( pListEntry, ILOG_FILE,
m_listEntry);
piFileScan->Reference();
fReturn = (fReturn && piFileScan->Flush());
piFileScan->Dereference();
} // for
//
// 2. Reschedule for the next interval if need be
//
if ( fReturn && sm_nFiles >= 1) {
fReturn = ScheduleFlush();
}
ILOG_FILE::UnlockFilesList();
return (fReturn);
} // ILOG_FILE::FlushHandler()
/**************************************************
* Auxiliary Functions
**************************************************/
static VOID
LogFileFlushHandler(VOID * pContext)
/*++
C wrapper function for the C++ Flush Handler
--*/
{
DBG_REQUIRE( ILOG_FILE::FlushHandler());
return;
} // LogFileFlushHandler()
/************************ End of File ***********************/