WindowsXP-SP1/base/eventlog/server/operate.c
2020-09-30 16:53:49 +02:00

4573 lines
155 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1990-1994 Microsoft Corporation
Module Name:
operate.c
Abstract:
This file contains all the routines to perform operations on the
log files. They are called by the thread that performs requests.
Author:
Rajen Shah (rajens) 16-Jul-1991
Revision History:
04-Apr-1995 MarkBl
Resets the file archive attribute on log write. The backup caller
clears it.
29-Aug-1994 Danl
We no longer grow log files in place. Therefore, the ExtendSize
function will allocate a block that is as large as the old size plus
the size of the new block that must be added. If this allocation
succeeds, then it will free up the old block. If a failure occurs,
we continue to use the old block as if we have already grown as much
as we can.
22-Jul-1994 Danl
ValidFilePos: Changed test for pEndRecordLen > PhysicalEOF
so that it uses >=. In the case where pEndRecordLen == PhysicalEOF,
we want to wrap to find the last DWORD at the beginning of the File
(after the header).
08-Jul-1994 Danl
PerformWriteRequest: Fixed overwrite logic so that in the case where
a log is set up for always-overwrite, that we never go down the branch
that indicates the log was full. Previously, it would go down that
branch if the current time was less than the log time (ie. someone
set the clock back).
18-Apr-2001 a-jyotig
IsPositionWithinRange function changed to take 2 additional parameters
EOF and Base Address (BOF) and check if the position is between EOF
and BOF
--*/
/****
@doc EXTERNAL INTERFACES EVTLOG
****/
//
// INCLUDES
//
#include <eventp.h>
#include <alertmsg.h> // ALERT_ELF manifests
#include "elfmsg.h"
#include <strsafe.h>
#define OVERWRITE_AS_NEEDED 0x00000000
#define NEVER_OVERWRITE 0xffffffff
//
// Prototypes
//
VOID PerformClearRequest(PELF_REQUEST_RECORD Request);
BOOL
IsPositionWithinRange(
PVOID Position,
PVOID BeginningRecord,
PVOID EndingRecord,
PVOID PhysEOF,
PVOID BaseAddress
);
VOID
ElfExtendFile (
PLOGFILE pLogFile,
ULONG SpaceNeeded,
PULONG SpaceAvail
)
/*++
Routine Description:
This routine takes an open log file and extends the file and underlying
section and view if possible. If it can't be grown, it caps the file
at this size by setting the ConfigMaxFileSize to the Actual. It also
updates the SpaceAvail parm which is used in PerformWriteRequest (the
caller).
Arguments:
pLogFile - pointer to a LOGFILE structure for the open logfile
ExtendAmount - How much bigger to make the file/section/view
SpaceAvail - Update this with how much space was added to the section
Return Value:
None - If we can't extend the file, we just cap it at this size for the
duration of this boot. We'll try again the next time the eventlog
is closed and reopened.
Note:
ExtendAmount should always be granular to 64K.
--*/
{
LARGE_INTEGER NewSize;
NTSTATUS Status;
PVOID BaseAddress;
SIZE_T Size;
IO_STATUS_BLOCK IoStatusBlock;
//
// Calculate how much to grow the file then extend the section by
// that amount. Do this in 64K chunks.
//
SpaceNeeded = ((SpaceNeeded - *SpaceAvail) & 0xFFFF0000) + 0x10000;
if (SpaceNeeded > (pLogFile->ConfigMaxFileSize - pLogFile->ActualMaxFileSize))
{
//
// We can't grow it by the full amount we need. Grow
// it to the max size and let file wrap take over.
// If there isn't any room to grow, then return;
//
SpaceNeeded = pLogFile->ConfigMaxFileSize - pLogFile->ActualMaxFileSize;
if (SpaceNeeded == 0)
{
return;
}
}
NewSize = RtlConvertUlongToLargeInteger(pLogFile->ActualMaxFileSize + SpaceNeeded);
//
// Update the file size information, extend the section, and map the
// new section
//
Status = NtSetInformationFile(pLogFile->FileHandle,
&IoStatusBlock,
&NewSize,
sizeof(NewSize),
FileEndOfFileInformation);
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"ElfExtendFile: NtSetInformationFile for %ws log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
goto ErrorExit;
}
Status = NtExtendSection(pLogFile->SectionHandle, &NewSize);
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"ElfExtendFile: NtExtendSection for %ws log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
goto ErrorExit;
}
//
// Now that the section is extended, we need to map the new section.
//
//
// Map a view of the entire section (with the extension).
// Allow the allocator to tell us where it is located, and
// what the size is.
//
BaseAddress = NULL;
Size = 0;
Status = NtMapViewOfSection(pLogFile->SectionHandle,
NtCurrentProcess(),
&BaseAddress,
0,
0,
NULL,
&Size,
ViewUnmap,
0,
PAGE_READWRITE);
if (!NT_SUCCESS(Status))
{
//
// If this fails, just exit, and we will continue with the
// view that we have.
//
ELF_LOG2(ERROR,
"ElfExtendFile: NtMapViewOfSection for %ws log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
goto ErrorExit;
}
//
// Unmap the old section.
//
Status = NtUnmapViewOfSection(NtCurrentProcess(),
pLogFile->BaseAddress);
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"ElfExtendFile: NtUnmapeViewOfSection for %ws log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
}
pLogFile->BaseAddress = BaseAddress;
//
// We managed to extend the file, update the actual size
// and space available and press on.
//
if (pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP)
{
//
// Since we're wrapped, we want to move the "unwrapped" portion (i.e.,
// everything from the first record to the end of the old file) down to
// be at the bottom of the new file
//
// The call below moves memory as follows:
//
// 1. Destination -- PhysicalEOF - the size of the region
// 2. Source -- Address of the start of the first record
// 3. Size -- Num. bytes in the old file -
// offset of the first record
//
//
// Note that at this point, we have the following relevant variables
//
// BaseAddress -- The base address of the mapped section
// Size -- The size of the enlarged section
// pLogFile->ViewSize -- The size of the old section
// pLogfile->BeginRecord -- The offset of the first log record
//
//
// Calculate the number of bytes to move
//
DWORD dwWrapSize = (DWORD)(pLogFile->ViewSize - pLogFile->BeginRecord);
ELF_LOG1(FILES,
"ElfExtendFile: %ws is wrapped\n",
pLogFile->LogModuleName->Buffer);
RtlMoveMemory((LPBYTE)BaseAddress + Size - dwWrapSize,
(LPBYTE)BaseAddress + pLogFile->BeginRecord,
dwWrapSize);
//
// We've moved the BeginRecord -- update the offset
//
pLogFile->BeginRecord = (ULONG)(Size - dwWrapSize);
}
pLogFile->ViewSize = (ULONG)Size;
pLogFile->ActualMaxFileSize += SpaceNeeded;
*SpaceAvail += SpaceNeeded;
//
// Now flush this to disk to commit it
//
BaseAddress = pLogFile->BaseAddress;
Size = FILEHEADERBUFSIZE;
Status = NtFlushVirtualMemory(NtCurrentProcess(),
&BaseAddress,
&Size,
&IoStatusBlock);
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"ElfExtendFile: NtFlushVirtualMemory for %ws log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
}
return;
ErrorExit:
//
// Couldn't extend the section for some reason. Just wrap the file now.
// Cap the file at this size, so we don't try and extend the section on
// every write. The next time the eventlog service is started up it
// will revert to the configured max again.
//
//
// BUGBUG: Generate an Alert here
//
ELF_LOG1(ERROR,
"ElfExtendFile: Couldn't extend %ws log\n",
pLogFile->LogModuleName->Buffer);
pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
return;
}
NTSTATUS
CopyUnicodeToAnsiRecord (
OUT PVOID Dest OPTIONAL,
IN PVOID Src,
OUT PVOID *NewBufPos OPTIONAL,
OUT PULONG RecordSize
)
/*++
Routine Description:
This routine reads from the event log specified in the request packet.
This routine uses memory mapped I/O to access the log file. This makes
it much easier to move around the file.
Arguments:
Dest - Points to destination buffer. If NULL, calculate and return
the record length without copying the record.
Src - Points to the UNICODE record.
NewBufPos - Gets offset in Dest buffer after record just transferred.
If Dest is NULL, this is ignored.
RecordSize - Gets size of this (ANSI) record.
Return Value:
STATUS_SUCCESS if no errors occur. Specific NTSTATUS error otherwise.
Note:
--*/
{
ANSI_STRING StringA;
UNICODE_STRING StringU;
PEVENTLOGRECORD SrcRecord, DestRecord;
PWSTR pStringU;
PVOID TempPtr;
ULONG PadSize, i;
ULONG zero = 0;
WCHAR *SrcStrings, *DestStrings;
ULONG RecordLength, *pLength;
ULONG ulTempLength;
NTSTATUS Status = STATUS_SUCCESS;
DestRecord = (PEVENTLOGRECORD)Dest;
SrcRecord = (PEVENTLOGRECORD)Src;
if (DestRecord != NULL)
{
DestRecord->TimeGenerated = SrcRecord->TimeGenerated;
DestRecord->Reserved = SrcRecord->Reserved;
DestRecord->RecordNumber = SrcRecord->RecordNumber;
DestRecord->TimeWritten = SrcRecord->TimeWritten;
DestRecord->EventID = SrcRecord->EventID;
DestRecord->EventType = SrcRecord->EventType;
DestRecord->EventCategory = SrcRecord->EventCategory;
DestRecord->NumStrings = SrcRecord->NumStrings;
DestRecord->UserSidLength = SrcRecord->UserSidLength;
DestRecord->DataLength = SrcRecord->DataLength;
}
//
// Convert and copy over modulename
//
pStringU = (PWSTR)((ULONG_PTR)SrcRecord + sizeof(EVENTLOGRECORD));
RtlInitUnicodeString(&StringU, pStringU);
if (DestRecord != NULL)
{
Status = RtlUnicodeStringToAnsiString(&StringA,
&StringU,
TRUE);
ulTempLength = StringA.MaximumLength;
}
else
{
ulTempLength = RtlUnicodeStringToAnsiSize(&StringU);
}
if (NT_SUCCESS(Status))
{
TempPtr = (PVOID)((ULONG_PTR)DestRecord + sizeof(EVENTLOGRECORD));
if (DestRecord != NULL)
{
RtlMoveMemory ( TempPtr, StringA.Buffer, ulTempLength );
RtlFreeAnsiString(&StringA);
}
TempPtr = (PVOID)((ULONG_PTR) TempPtr + ulTempLength);
//
// Convert and copy over computername
//
// TempPtr points to location in the destination for the computername
//
pStringU = (PWSTR)((ULONG_PTR)pStringU + StringU.MaximumLength);
RtlInitUnicodeString ( &StringU, pStringU );
if (DestRecord != NULL)
{
Status = RtlUnicodeStringToAnsiString (
&StringA,
&StringU,
TRUE
);
ulTempLength = StringA.MaximumLength;
}
else
{
ulTempLength = RtlUnicodeStringToAnsiSize(&StringU);
}
if (NT_SUCCESS(Status))
{
if (DestRecord != NULL)
{
RtlMoveMemory ( TempPtr, StringA.Buffer, ulTempLength );
RtlFreeAnsiString(&StringA);
}
TempPtr = (PVOID)((ULONG_PTR) TempPtr + ulTempLength);
}
}
if (NT_SUCCESS(Status))
{
// TempPtr points to location after computername - i.e. UserSid.
// Before we write out the UserSid, we ensure that we pad the
// bytes so that the UserSid starts on a DWORD boundary.
//
PadSize = sizeof(ULONG)
- (ULONG)(((ULONG_PTR)TempPtr-(ULONG_PTR)DestRecord) % sizeof(ULONG));
if (DestRecord != NULL)
{
RtlMoveMemory (TempPtr, &zero, PadSize);
TempPtr = (PVOID)((ULONG_PTR)TempPtr + PadSize);
//
// Copy over the UserSid.
//
RtlMoveMemory(TempPtr,
(PVOID)((ULONG_PTR)SrcRecord + SrcRecord->UserSidOffset),
SrcRecord->UserSidLength);
DestRecord->UserSidOffset = (ULONG)((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord);
}
else
{
TempPtr = (PVOID)((ULONG_PTR)TempPtr + PadSize);
}
//
// Copy over the Strings
//
TempPtr = (PVOID)((ULONG_PTR)TempPtr + SrcRecord->UserSidLength);
SrcStrings = (WCHAR *)((ULONG_PTR)SrcRecord + (ULONG)SrcRecord->StringOffset);
DestStrings = (WCHAR *)TempPtr;
for (i=0; i < SrcRecord->NumStrings; i++)
{
RtlInitUnicodeString (&StringU, SrcStrings);
if (DestRecord != NULL)
{
Status = RtlUnicodeStringToAnsiString(&StringA,
&StringU,
TRUE);
ulTempLength = StringA.MaximumLength;
}
else
{
ulTempLength = RtlUnicodeStringToAnsiSize(&StringU);
}
if (!NT_SUCCESS(Status))
{
//
// Bail out
//
return Status;
}
if (DestRecord != NULL)
{
RtlMoveMemory(DestStrings,
StringA.Buffer,
ulTempLength);
RtlFreeAnsiString (&StringA);
}
DestStrings = (WCHAR*)((ULONG_PTR)DestStrings + (ULONG)ulTempLength);
SrcStrings = (WCHAR*)((ULONG_PTR)SrcStrings + (ULONG)StringU.MaximumLength);
}
//
// DestStrings points to the point after the last string copied.
//
if (DestRecord != NULL)
{
DestRecord->StringOffset = (ULONG)((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord);
TempPtr = (PVOID)DestStrings;
//
// Copy over the binary Data
//
DestRecord->DataOffset = (ULONG)((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord);
RtlMoveMemory(TempPtr,
(PVOID)((ULONG_PTR)SrcRecord + SrcRecord->DataOffset),
SrcRecord->DataLength);
}
else
{
TempPtr = (PVOID)DestStrings;
}
//
// Now do the pad bytes.
//
TempPtr = (PVOID) ((ULONG_PTR) TempPtr + SrcRecord->DataLength);
PadSize = sizeof(ULONG)
- (ULONG) (((ULONG_PTR) TempPtr - (ULONG_PTR) DestRecord) % sizeof(ULONG));
RecordLength = (ULONG) ((ULONG_PTR) TempPtr
+ PadSize
+ sizeof(ULONG)
- (ULONG_PTR)DestRecord);
if (DestRecord != NULL)
{
RtlMoveMemory (TempPtr, &zero, PadSize);
pLength = (PULONG)((ULONG_PTR)TempPtr + PadSize);
*pLength = RecordLength;
DestRecord->Length = RecordLength;
ASSERT(((ULONG_PTR) DestRecord + RecordLength) ==
((ULONG_PTR) pLength + sizeof(ULONG)));
*NewBufPos = (PVOID) ((ULONG_PTR) DestRecord + RecordLength);
}
*RecordSize = RecordLength;
}
return Status;
} // CopyUnicodeToAnsiRecord
BOOL
ValidFilePos (
PVOID Position,
PVOID BeginningRecord,
PVOID EndingRecord,
PVOID PhysicalEOF,
PVOID BaseAddress,
BOOL fCheckBeginEndRange
)
/*++
Routine Description:
This routine determines whether we are pointing to a valid beginning
of an event record in the event log. It does this by validating
the signature then comparing the length at the beginning of the record to
the length at the end, both of which have to be at least the size of the
fixed length portion of an eventlog record.
Arguments:
Position - Pointer to be verified.
BeginningRecord - Pointer to the beginning record in the file.
EndingRecord - Pointer to the byte after the ending record in the file.
PhysicalEOF - Pointer the physical end of the log.
BaseAddress - Pointer to the physical beginning of the log.
Return Value:
TRUE if this position is valid.
Note:
There is a possibility of error if a record just happens to have the
ULONG at the current position the same as the value that number of
bytes further on in the record. However, this is a very slim chance
as it must have a valid log signature as well.
--*/
{
PULONG pEndRecordLength;
BOOL fValid = TRUE;
PEVENTLOGRECORD pEventRecord;
try
{
pEventRecord = (PEVENTLOGRECORD)Position;
//
// Verify that the pointer is within the range of BEGINNING->END
//
if ( fCheckBeginEndRange )
{
fValid = IsPositionWithinRange(Position,
BeginningRecord,
EndingRecord,
PhysicalEOF,
BaseAddress);
}
//
// If the offset looks OK, then examine the lengths at the beginning
// and end of the current record. If they don't match, then the position
// is invalid.
//
if (fValid)
{
//
// Make sure the length is a multiple number of DWORDS
//
if (pEventRecord->Length & 3)
{
fValid = FALSE;
}
else
{
pEndRecordLength = (PULONG) ((PBYTE) Position + pEventRecord->Length) - 1;
//
// If the file is wrapped, adjust the pointer to reflect the
// portion of the record that is wrapped starting after the
// header
//
if ((PVOID) pEndRecordLength >= PhysicalEOF)
{
pEndRecordLength = (PULONG) ((PBYTE) BaseAddress +
((PBYTE) pEndRecordLength - (PBYTE) PhysicalEOF) +
FILEHEADERBUFSIZE);
}
// Do a sanity check on this pointer before dereferencing. DAVJ
if ((PVOID) pEndRecordLength > PhysicalEOF)
{
return FALSE;
}
if (pEventRecord->Length == *pEndRecordLength
&&
pEventRecord->Length == ELFEOFRECORDSIZE)
{
ULONG Size;
Size = min(ELFEOFUNIQUEPART,
(ULONG) ((PBYTE) PhysicalEOF - (PBYTE) pEventRecord));
if (RtlCompareMemory(pEventRecord,
&EOFRecord,
Size) == Size)
{
Size = ELFEOFUNIQUEPART - Size;
//
// If Size is non-zero, then the unique portion of
// the EOF record is wrapped across the end of file.
// Continue comparison at file beginning for the
// remainder of the record.
//
if ( Size )
{
PBYTE pRemainder = (PBYTE) &EOFRecord + ELFEOFUNIQUEPART - Size;
fValid = (RtlCompareMemory((PBYTE) BaseAddress + FILEHEADERBUFSIZE,
pRemainder,
Size) == Size);
}
}
else
{
fValid = FALSE;
}
}
else if ((pEventRecord->Length < sizeof(EVENTLOGRECORD))
||
(pEventRecord->Reserved != ELF_LOG_FILE_SIGNATURE)
||
(pEventRecord->Length != *pEndRecordLength))
{
fValid = FALSE;
}
}
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"ValidFilePos: Exception %#x caught validating file position %p\n",
GetExceptionCode(),
BeginningRecord);
fValid = FALSE;
}
return fValid;
}
BOOL
IsPositionWithinRange(
PVOID Position,
PVOID BeginningRecord,
PVOID EndingRecord,
PVOID PhysEOF,
PVOID BaseAddress
)
{
//
// Verify that the pointer is within the range of [Beginning, End]
//
//This check was introduced to make sure that the position does not
//cross the file boundaries. Refer to Bug #370063. If required, one
//can change this check to make sure that the position lies between
//PhyStart i.e. BaseAddress + FILEHEADERBUFSIZE
if((Position < BaseAddress) || (Position > PhysEOF))
return FALSE;
else if (EndingRecord > BeginningRecord)
{
return ((Position >= BeginningRecord) && (Position <= EndingRecord));
}
else if (EndingRecord < BeginningRecord)
{
return ((Position >= BeginningRecord) || (Position <= EndingRecord));
}
//
// If BeginningRecord and EndingRecord are equal, it means that the only
// record in the log file is the EOF record. In that case, return FALSE
// as Position isn't pointing to a valid (i.e., non-EOF) record.
//
return FALSE;
}
PVOID
FindStartOfNextRecord (
PVOID Position,
PVOID BeginningRecord,
PVOID EndingRecord,
PVOID PhysicalStart,
PVOID PhysicalEOF,
PVOID BaseAddress
)
/*++
Routine Description:
This routine starts at Position, and finds the beginning of the next
valid record, wrapping around the physical end of the file if necessary.
Arguments:
Position - Pointer at which to start the search.
BeginningRecord - Pointer to the beginning record in the file.
EndingRecord - Pointer to the byte after the ending record in the file.
PhysicalStart - Pointer to the start of log information (after header)
PhysicalEOF - Pointer to the physical end of the log.
BaseAddress - Pointer to the physical beginning of the log.
Return Value:
A pointer to the start of the next valid record, NULL if there is no
valid record.
Note:
There is a possibility of error if a record just happens to have the
ULONG at the current position the same as the value that number of
bytes further on in the record. However, this is a very slim chance
as it must have a valid log signature as well.
--*/
{
PULONG ptr;
PULONG EndOfBlock;
PULONG EndOfFile;
PVOID pRecord;
ULONG Size;
BOOL StillLooking = TRUE;
//
// Search for a ULONG which matches a record signature
//
ptr = (PULONG) Position;
EndOfBlock = EndOfFile = (PULONG) PhysicalEOF - 1;
while (StillLooking)
{
//
// Check to see if it is the EOF record
//
if (*ptr == ELFEOFRECORDSIZE)
{
//
// Only scan up to the end of the file. Just compare up the
// constant information
//
//
// BUGBUG: If (End - pEvent) is less than ELFEOFUNIQUEPART,
// we never validate what should be the remainder of
// the EOF record at the start of the logfile
//
Size = min(ELFEOFUNIQUEPART,
(ULONG) ((PBYTE) PhysicalEOF - (PBYTE) ptr));
pRecord = CONTAINING_RECORD(ptr,
ELF_EOF_RECORD,
RecordSizeBeginning);
if (RtlCompareMemory(pRecord,
&EOFRecord,
Size) == Size)
{
ELF_LOG1(FILES,
"FindStartOfNextRecord: Found EOF record at %p\n",
pRecord);
//
// This is the EOF record, back up to the last record
//
(PBYTE) pRecord -= *((PULONG) pRecord - 1);
if (pRecord < PhysicalStart)
{
pRecord = (PBYTE) PhysicalEOF -
((PBYTE)PhysicalStart - (PBYTE)pRecord);
}
}
if (ValidFilePos(pRecord,
BeginningRecord,
EndingRecord,
PhysicalEOF,
BaseAddress,
TRUE))
{
ELF_LOG1(FILES,
"FindStartOfNextRecord: Valid record at %p preceding EOF record\n",
pRecord);
return pRecord;
}
}
//
// Check to see if it is an event record
//
if (*ptr == ELF_LOG_FILE_SIGNATURE)
{
//
// This is a signature, see if the containing record is valid
//
pRecord = CONTAINING_RECORD(ptr,
EVENTLOGRECORD,
Reserved);
if (ValidFilePos(pRecord,
BeginningRecord,
EndingRecord,
PhysicalEOF,
BaseAddress,
TRUE))
{
ELF_LOG1(FILES,
"FindStartOfNextRecord: Valid record found at %p\n",
pRecord);
return pRecord;
}
}
//
// Bump to the next byte and see if we're done.
//
ptr++;
if (ptr >= EndOfBlock)
{
//
// Need the second test on this condition in case Position
// happens to equal PhysicalEOF - 1 (EndOfBlock initial value);
// without this, this loop would terminate prematurely.
//
if ((EndOfBlock == (PULONG) Position)
&&
((PULONG) Position != EndOfFile))
{
//
// This was the top half, so we're done
//
StillLooking = FALSE;
ELF_LOG0(FILES,
"FindStartOfNextRecord: Unsuccessfully searched "
"top half of file\n");
}
else
{
//
// This was the bottom half, let's look in the top half
//
EndOfBlock = (PULONG) Position;
ptr = (PULONG) PhysicalStart;
ELF_LOG0(FILES,
"FindStartOfNextRecord: Unsuccessfully searched "
"bottom half of file -- searching top half\n");
}
}
}
//
// Didn't find a valid record
//
return NULL;
}
PVOID
NextRecordPosition (
ULONG ReadFlags,
PVOID CurrPosition,
ULONG CurrRecordLength,
PVOID BeginRecord,
PVOID EndRecord,
PVOID PhysicalEOF,
PVOID PhysStart
)
/*++
Routine Description:
This routine seeks to the beginning of the next record to be read
depending on the flags in the request packet.
Arguments:
ReadFlags - Read forwards or backwards
CurrPosition - Pointer to the current position.
CurrRecordLength - Length of the record at the last position read.
BeginRecord - Logical first record
EndRecord - Logical last record (EOF record)
PhysEOF - End of file
PhysStart - Start of file pointer (following file header).
Return Value:
New position or NULL if invalid record.
Note:
--*/
{
PVOID NewPosition;
ULONG Length;
PDWORD FillDword;
if (ReadFlags & EVENTLOG_FORWARDS_READ)
{
//
// If we're pointing at the EOF record, just set the position to
// the first record
//
if (CurrRecordLength == ELFEOFRECORDSIZE)
{
ELF_LOG1(FILES,
"NextRecordPosition: Pointing to EOF record -- returning "
"address of first record (%p)\n",
BeginRecord);
return BeginRecord;
}
NewPosition = (PVOID) ((ULONG_PTR) CurrPosition + CurrRecordLength);
//
// Take care of wrapping.
//
if (NewPosition >= PhysicalEOF)
{
NewPosition = (PBYTE)PhysStart
+ ((PBYTE) NewPosition - (PBYTE) PhysicalEOF);
}
//
// If this is a ELF_SKIP_DWORD, skip to the top of the file
//
if (*(PDWORD) NewPosition == ELF_SKIP_DWORD)
{
NewPosition = PhysStart;
}
}
else
{
//
// Reading backwards.
//
ASSERT (ReadFlags & EVENTLOG_BACKWARDS_READ);
if (CurrPosition == BeginRecord)
{
//
// This is the "end of file" if we're reading backwards.
//
ELF_LOG1(FILES,
"NextRecordPosition: Reading backwards and pointing to first "
"record -- returning address of last record (%p)\n",
EndRecord);
return EndRecord;
}
else if (CurrPosition == PhysStart)
{
//
// Flip to the bottom of the file, but skip and ELF_SKIP_DWORDs
//
FillDword = (PDWORD) PhysicalEOF; // last dword
FillDword--;
while (*FillDword == ELF_SKIP_DWORD)
{
FillDword--;
}
CurrPosition = (PVOID) (FillDword + 1);
}
Length = *((PULONG) CurrPosition - 1);
if (Length < ELFEOFRECORDSIZE)
{
//
// Bogus length, must be invalid record
//
ELF_LOG1(FILES,
"NextRecordPosition: Invalid record length (%d) encountered\n",
Length);
return NULL;
}
NewPosition = (PBYTE) CurrPosition - Length;
//
// Take care of wrapping
//
if (NewPosition < PhysStart)
{
NewPosition = (PBYTE) PhysicalEOF
- ((PBYTE) PhysStart - (PBYTE) NewPosition);
}
}
return NewPosition;
}
NTSTATUS
SeekToStartingRecord (
PELF_REQUEST_RECORD Request,
PVOID *ReadPosition,
PVOID BeginRecord,
PVOID EndRecord,
PVOID PhysEOF,
PVOID PhysStart
)
/*++
Routine Description:
This routine seeks to the correct position as indicated in the
request packet.
Arguments:
Pointer to the request packet.
Pointer to a pointer where the final position after the seek is returned.
Return Value:
NTSTATUS and new position in file.
Note:
This routine ensures that it is possible to seek to the position
specified in the request packet. If not, then an error is returned
which indicates that the file probably changed between the two
READ operations, or else the record offset specified is beyond the
end of the file.
--*/
{
PVOID Position;
ULONG RecordLen;
ULONG NumRecordsToSeek;
ULONG BytesPerRecord;
ULONG NumberOfRecords;
ULONG NumberOfBytes;
ULONG ReadFlags;
//
// If the beginning and the end are the same, then there are no
// entries in this file.
//
if (BeginRecord == EndRecord)
{
ELF_LOG1(FILES,
"SeekToStartingRecord: %ws log is empty\n",
Request->Module->ModuleName);
return STATUS_END_OF_FILE;
}
//
// Find the last position (or the "beginning" if this is the first READ
// call for this handle).
//
if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEQUENTIAL_READ)
{
if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_FORWARDS_READ)
{
//
// If this is the first READ operation, LastSeekPosition will
// be zero. In that case, we set the position to the first
// record (in terms of time) in the file.
//
if (Request->Pkt.ReadPkt->LastSeekPos == 0)
{
ELF_LOG1(FILES,
"SeekToStartingRecord: First read (forwards) of %ws log\n",
Request->Module->ModuleName);
Position = BeginRecord;
}
else
{
Position = (PBYTE) Request->LogFile->BaseAddress
+ Request->Pkt.ReadPkt->LastSeekPos;
//
// If we're changing the direction we're reading, skip
// forward one record. This is because we're pointing at
// the "next" record based on the last read direction
//
if (!(Request->Pkt.ReadPkt->Flags & ELF_LAST_READ_FORWARD))
{
Position = NextRecordPosition(Request->Pkt.ReadPkt->ReadFlags,
Position,
((PEVENTLOGRECORD) Position)->Length,
BeginRecord,
EndRecord,
PhysEOF,
PhysStart);
}
else
{
//
// This *really* cheesy check exists to handle the case
// where Position could be on an ELF_SKIP_DWORD pad
// dword at end of the file.
//
// NB: Must be prepared to handle an exception since
// a somewhat unknown pointer is dereferenced.
//
NTSTATUS Status = STATUS_SUCCESS;
try
{
if (IsPositionWithinRange(Position,
BeginRecord,
EndRecord,
PhysEOF,
Request->LogFile->BaseAddress))
{
//
// If this is an ELF_SKIP_DWORD, skip to the
// top of the file.
//
if (*(PDWORD) Position == ELF_SKIP_DWORD)
{
ELF_LOG1(FILES,
"SeekToStartingRecord: Next forward read position "
"in %ws log is on an ELF_SKIP_DWORD\n",
Request->Module->ModuleName);
Position = PhysStart;
}
}
else
{
//
// More likely the caller's handle was invalid
// if the position was not within range.
//
ELF_LOG1(ERROR,
"SeekToStartingRecord: Next forward read position "
"in %ws log is out of range -- log is corrupt\n",
Request->Module->ModuleName);
Status = STATUS_INVALID_HANDLE;
}
}
except (EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"SeekToStartingRecord: Caught exception %#x looking for "
"next forward read position in %ws log\n",
GetExceptionCode(),
Request->Module->ModuleName);
Status = STATUS_EVENTLOG_FILE_CORRUPT;
}
if (!NT_SUCCESS(Status))
{
*ReadPosition = NULL;
return Status;
}
}
}
}
else
{
//
// READ backwards
//
// If this is the first READ operation, LastSeekPosition will
// be zero. In that case, we set the position to the last
// record (in terms of time) in the file.
//
if (Request->Pkt.ReadPkt->LastSeekPos == 0)
{
ELF_LOG1(FILES,
"SeekToStartingRecord: First read (backwards) of %ws log\n",
Request->Module->ModuleName);
Position = EndRecord;
//
// Subtract the length of the last record from the current
// position to get to the beginning of the record.
//
// If that moves beyond the physical beginning of the file,
// then we need to wrap around to the physical end of the file.
//
Position = (PBYTE) Position - *((PULONG) Position - 1);
if (Position < PhysStart)
{
Position = (PBYTE) PhysEOF - ((PBYTE) PhysStart - (PBYTE) Position);
}
}
else
{
Position = (PBYTE) Request->LogFile->BaseAddress
+ Request->Pkt.ReadPkt->LastSeekPos;
//
// If we're changing the direction we're reading, skip
// forward one record. This is because we're pointing at
// the "next" record based on the last read direction
//
if (Request->Pkt.ReadPkt->Flags & ELF_LAST_READ_FORWARD)
{
Position = NextRecordPosition(Request->Pkt.ReadPkt->ReadFlags,
Position,
0, // not used if reading backwards
BeginRecord,
EndRecord,
PhysEOF,
PhysStart);
}
}
}
}
else if (Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEEK_READ)
{
//
// Make sure the record number passed in is valid
//
if (Request->Pkt.ReadPkt->RecordNumber < Request->LogFile->OldestRecordNumber
||
Request->Pkt.ReadPkt->RecordNumber >= Request->LogFile->CurrentRecordNumber)
{
ELF_LOG1(ERROR,
"SeekToStartingRecord: Invalid record number %d\n",
Request->Pkt.ReadPkt->RecordNumber);
return STATUS_INVALID_PARAMETER;
}
//
// We're seeking to an absolute record number, so use the following
// algorithm:
//
// 1. Calculate the average number of bytes per record
//
// 2. Based on this number seek to where the record should start
//
// 3. Find the start of the next record in the file
//
// 4. Walk (forwards or backwards) from there to the right record
//
//
// 1. Calculate the average number of bytes per record
//
NumberOfRecords = Request->LogFile->CurrentRecordNumber
- Request->LogFile->OldestRecordNumber;
NumberOfBytes = Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP ?
Request->LogFile->ActualMaxFileSize :
Request->LogFile->EndRecord;
NumberOfBytes -= FILEHEADERBUFSIZE;
BytesPerRecord = NumberOfBytes / NumberOfRecords;
//
// 2. Calcuate the first guess as to what the offset of the desired
// record should be
//
Position = (PBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->BeginRecord
+ BytesPerRecord
* (Request->Pkt.ReadPkt->RecordNumber
- Request->LogFile->OldestRecordNumber);
//
// Align the position to a ULONG bountry.
//
Position = (PVOID) (((ULONG_PTR) Position + sizeof(ULONG) - 1) & ~(sizeof(ULONG) - 1));
//
// Take care of file wrap
//
if (Position >= PhysEOF)
{
Position = (PBYTE)PhysStart +
((PBYTE) Position - (PBYTE) PhysEOF);
if (Position >= PhysEOF)
{
//
// It's possible, in an obscure error case, that Position
// may still be beyond the EOF. Adjust, if so.
//
Position = BeginRecord;
}
}
//
// Bug fix:
//
// 57017 - Event Log Causes Services.Exe to Access Violate therefore
// Hanging the Server
//
// The calculation above can easily put Position out of range of
// the begin/end file markers. This is not good. Adjust Position,
// if necessary.
//
if (BeginRecord < EndRecord && Position >= EndRecord)
{
Position = BeginRecord;
}
else if (BeginRecord > EndRecord && Position >= EndRecord && Position < BeginRecord)
{
Position = BeginRecord;
}
else
{
// Do nothing.
}
//
// 3. Get to the start of the next record after Position
//
Position = FindStartOfNextRecord(Position,
BeginRecord,
EndRecord,
PhysStart,
PhysEOF,
Request->LogFile->BaseAddress);
//
// 4. Walk (forwards or backwards) from Position to the right record
//
if (Position)
{
if (Request->Pkt.ReadPkt->RecordNumber >
((PEVENTLOGRECORD) Position)->RecordNumber)
{
NumRecordsToSeek = Request->Pkt.ReadPkt->RecordNumber
- ((PEVENTLOGRECORD) Position)->RecordNumber;
ReadFlags = EVENTLOG_FORWARDS_READ;
ELF_LOG2(FILES,
"SeekToStartingRecord: Walking forward %d records from record %d\n",
NumRecordsToSeek,
((PEVENTLOGRECORD) Position)->RecordNumber);
}
else
{
NumRecordsToSeek = ((PEVENTLOGRECORD) Position)->RecordNumber
- Request->Pkt.ReadPkt->RecordNumber;
ReadFlags = EVENTLOG_BACKWARDS_READ;
ELF_LOG2(FILES,
"SeekToStartingRecord: Walking backward %d records from record %d\n",
NumRecordsToSeek,
((PEVENTLOGRECORD) Position)->RecordNumber);
}
}
while (Position != NULL && NumRecordsToSeek--)
{
RecordLen = ((PEVENTLOGRECORD) Position)->Length;
Position = NextRecordPosition(ReadFlags,
Position,
RecordLen,
BeginRecord,
EndRecord,
PhysEOF,
PhysStart);
}
}
else
{
//
// Flags didn't specify a sequential or seek read
//
return STATUS_INVALID_PARAMETER;
}
*ReadPosition = Position; // This is the new seek position
if (!Position)
{
//
// The record was invalid
//
ELF_LOG1(ERROR,
"SeekToStartingRecord: Position is NULL -- %ws log is corrupt\n",
Request->Module->ModuleName);
return STATUS_EVENTLOG_FILE_CORRUPT;
}
return STATUS_SUCCESS;
}
VOID
CopyRecordToBuffer(
IN PBYTE pReadPosition,
IN OUT PBYTE *ppBufferPosition,
IN ULONG ulRecordSize,
IN PBYTE pPhysicalEOF,
IN PBYTE pPhysStart
)
/*++
Routine Description:
This routine copies the EVENTLOGRECORD at pReadPosition into
*ppBufferPosition
Return Value:
None.
--*/
{
ULONG ulBytesToMove; // Number of bytes to copy
ASSERT(ppBufferPosition != NULL);
//
// If the number of bytes to the end of the file is less than the
// size of the record, then part of the record has wrapped to the
// beginning of the file - transfer the bytes piece-meal.
//
// Otherwise, transfer the whole record.
//
ulBytesToMove = min(ulRecordSize,
(ULONG) (pPhysicalEOF - pReadPosition));
if (ulBytesToMove < ulRecordSize)
{
//
// We need to copy the bytes up to the end of the file,
// and then wrap around and copy the remaining bytes of
// this record.
//
RtlMoveMemory(*ppBufferPosition, pReadPosition, ulBytesToMove);
//
// Advance user buffer pointer, move read position to the
// beginning of the file (past the file header), and
// update bytes remaining to be moved for this record.
//
*ppBufferPosition += ulBytesToMove;
pReadPosition = pPhysStart;
ulBytesToMove = ulRecordSize - ulBytesToMove; // Remaining bytes
}
//
// Move the remaining bytes of the record OR the full record.
//
RtlMoveMemory(*ppBufferPosition, pReadPosition, ulBytesToMove);
//
// Update to new read positions
//
*ppBufferPosition += ulBytesToMove;
}
NTSTATUS
ReadFromLog(
PELF_REQUEST_RECORD Request
)
/*++
Routine Description:
This routine reads from the event log specified in the request packet.
This routine uses memory mapped I/O to access the log file. This makes
it much easier to move around the file.
Arguments:
Pointer to the request packet.
Return Value:
NTSTATUS.
Note:
--*/
{
NTSTATUS Status;
PVOID ReadPosition; // Current read position in file
PVOID BufferPosition; // Current position in user's buffer
ULONG TotalBytesRead; // Total Bytes transferred
ULONG TotalRecordsRead; // Total records transferred
ULONG BytesInBuffer; // Bytes remaining in buffer
ULONG RecordSize; // Size of event record
PVOID PhysicalEOF; // Physical end of file
PVOID PhysStart; // Physical start of file (after file hdr)
PVOID BeginRecord; // Points to first record
PVOID EndRecord; // Points to byte after last record
PVOID TempBuf = NULL, TempBufferPosition;
ULONG RecordBytesTransferred;
PEVENTLOGRECORD pEvent;
//
// Initialize variables.
//
BytesInBuffer = Request->Pkt.ReadPkt->BufferSize;
BufferPosition = Request->Pkt.ReadPkt->Buffer;
TotalBytesRead = 0;
TotalRecordsRead = 0;
PhysicalEOF = (LPBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->ViewSize;
PhysStart = (LPBYTE) Request->LogFile->BaseAddress
+ FILEHEADERBUFSIZE;
BeginRecord = (LPBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->BeginRecord; // Start at first record
EndRecord = (LPBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->EndRecord; // Byte after end of last record
//
// "Seek" to the starting record depending on either the last seek
// position, or the starting record offset passed in.
//
Status = SeekToStartingRecord(Request,
&ReadPosition,
BeginRecord,
EndRecord,
PhysicalEOF,
PhysStart);
if (NT_SUCCESS(Status))
{
//
// Make sure the record is valid
//
if (!ValidFilePos(ReadPosition,
BeginRecord,
EndRecord,
PhysicalEOF,
Request->LogFile->BaseAddress,
TRUE))
{
ELF_LOG1(ERROR,
"ReadFromLog: Next record (%p) is not valid -- log is corrupt\n",
ReadPosition);
Request->Pkt.ReadPkt->BytesRead = 0;
Request->Pkt.ReadPkt->RecordsRead = 0;
return STATUS_INVALID_HANDLE;
}
// make sure that if we asked for a specific record, that we got it
if ((Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_SEEK_READ) &&
(Request->Pkt.ReadPkt->ReadFlags & EVENTLOG_BACKWARDS_READ))
{
pEvent = (PEVENTLOGRECORD)ReadPosition;
if(pEvent->Length == ELFEOFRECORDSIZE ||
pEvent->RecordNumber != Request->Pkt.ReadPkt->RecordNumber)
{
Request->Pkt.ReadPkt->BytesRead = 0;
Request->Pkt.ReadPkt->RecordsRead = 0;
return STATUS_EVENTLOG_FILE_CORRUPT;
}
}
RecordSize = RecordBytesTransferred = *(PULONG) ReadPosition;
if ((Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI)
&&
(RecordSize != ELFEOFRECORDSIZE))
{
//
//
// If we were called by an ANSI API, then we need to read the
// next record into a temporary buffer, process the data in
// that record and copy it over to the real buffer as ANSI
// strings (rather than UNICODE).
//
// We need to do this here since we won't be able to
// appropriately size a record that wraps for an ANSI
// call otherwise (we'll AV trying to read it past
// the end of the log).
//
TempBuf = ElfpAllocateBuffer(RecordSize);
if (TempBuf == NULL)
{
ELF_LOG0(ERROR,
"ReadFromLog: Unable to allocate memory for "
"Ansi record (1st call)\n");
return STATUS_NO_MEMORY;
}
TempBufferPosition = BufferPosition; // Save this away
BufferPosition = TempBuf; // Read into TempBuf
CopyRecordToBuffer((PBYTE) ReadPosition,
(PBYTE *) &BufferPosition,
RecordSize,
(PBYTE) PhysicalEOF,
(PBYTE) PhysStart);
//
// Call CopyUnicodeToAnsiRecord with a NULL destination
// location in order to get the size of the Ansi record
//
Status = CopyUnicodeToAnsiRecord(NULL,
TempBuf,
NULL,
&RecordBytesTransferred);
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"ReadFromLog: CopyUnicodeToAnsiRecord failed %#x (1st call)\n",
Status);
ElfpFreeBuffer(TempBuf);
return Status;
}
}
//
// While there are records to be read, and more space in the buffer,
// keep on reading records into the buffer.
//
while((RecordBytesTransferred <= BytesInBuffer)
&&
(RecordSize != ELFEOFRECORDSIZE))
{
//
// If we were called by an ANSI API, then we need to take the
// record read into TempBuf and transfer it over to the user's
// buffer while converting any UNICODE strings to ANSI.
//
if (Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI)
{
Status = CopyUnicodeToAnsiRecord(TempBufferPosition,
TempBuf,
&BufferPosition,
&RecordBytesTransferred);
//
// RecordBytesTransferred contains the bytes actually
// copied into the user's buffer.
//
// BufferPosition points to the point in the user's buffer
// just after this record.
//
ElfpFreeBuffer(TempBuf);
TempBuf = NULL;
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"ReadFromLog: CopyUnicodeToAnsiRecord failed %x "
"(2nd call)\n",
Status);
//
// Stop reading
//
break;
}
}
else
{
//
// Unicode call -- simply copy the record into the buffer
//
CopyRecordToBuffer((PBYTE) ReadPosition,
(PBYTE *) &BufferPosition,
RecordSize,
(PBYTE) PhysicalEOF,
(PBYTE) PhysStart);
}
//
// Update the byte and record counts
//
TotalRecordsRead++;
TotalBytesRead += RecordBytesTransferred;
BytesInBuffer -= RecordBytesTransferred;
ReadPosition = NextRecordPosition(Request->Pkt.ReadPkt->ReadFlags,
ReadPosition,
RecordSize,
BeginRecord,
EndRecord,
PhysicalEOF,
PhysStart);
//
// Make sure the record is valid
//
if (ReadPosition == NULL
||
!ValidFilePos(ReadPosition,
BeginRecord,
EndRecord,
PhysicalEOF,
Request->LogFile->BaseAddress,
TRUE))
{
ELF_LOG0(ERROR,
"ReadFromLog: Next record is invalid -- log is corrupt\n");
return STATUS_EVENTLOG_FILE_CORRUPT;
}
RecordSize = RecordBytesTransferred = *(PULONG) ReadPosition;
if ((Request->Pkt.ReadPkt->Flags & ELF_IREAD_ANSI)
&&
(RecordSize != ELFEOFRECORDSIZE))
{
TempBuf = ElfpAllocateBuffer(RecordSize);
if (TempBuf == NULL)
{
ELF_LOG0(ERROR,
"ReadFromLog: Unable to allocate memory for "
"Ansi record (2nd call)\n");
return STATUS_NO_MEMORY;
}
TempBufferPosition = BufferPosition; // Save this away
BufferPosition = TempBuf; // Read into TempBuf
CopyRecordToBuffer((PBYTE) ReadPosition,
(PBYTE *) &BufferPosition,
RecordSize,
(PBYTE) PhysicalEOF,
(PBYTE) PhysStart);
//
// Call CopyUnicodeToAnsiRecord with a NULL destination
// location in order to get the size of the Ansi record
//
Status = CopyUnicodeToAnsiRecord(NULL,
TempBuf,
NULL,
&RecordBytesTransferred);
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"ReadFromLog: CopyUnicodeToAnsiRecord failed %#x "
"(1st call)\n",
Status);
ElfpFreeBuffer(TempBuf);
return Status;
}
}
} // while
ElfpFreeBuffer(TempBuf);
TempBuf = NULL;
//
// If we got to the end and did not read in any records, return
// an error indicating that the user's buffer is too small if
// we're not at the EOF record, or end of file if we are.
//
if (TotalRecordsRead == 0)
{
if (RecordSize == ELFEOFRECORDSIZE)
{
ELF_LOG0(FILES,
"ReadFromLog: No records read -- pointing at EOF record\n");
Status = STATUS_END_OF_FILE;
}
else
{
//
// We didn't read any records, and we're not at EOF, so
// the buffer was too small
//
ELF_LOG1(ERROR,
"ReadFromLog: No records read -- buffer was too small "
"(%d bytes needed)\n",
RecordBytesTransferred);
Status = STATUS_BUFFER_TOO_SMALL;
Request->Pkt.ReadPkt->MinimumBytesNeeded = RecordBytesTransferred;
}
}
//
// Update the current file position.
//
Request->Pkt.ReadPkt->LastSeekPos =
(ULONG) ((ULONG_PTR) ReadPosition
- (ULONG_PTR) Request->LogFile->BaseAddress);
Request->Pkt.ReadPkt->LastSeekRecord += TotalRecordsRead;
ELF_LOG1(FILES,
"ReadFromLog: %d records successfully read\n",
TotalRecordsRead);
}
else
{
ELF_LOG1(ERROR,
"ReadFromLog: SeekToStartingRecord failed %#x\n",
Status);
}
//
// Set the bytes read in the request packet for return to client.
//
Request->Pkt.ReadPkt->BytesRead = TotalBytesRead;
Request->Pkt.ReadPkt->RecordsRead = TotalRecordsRead;
return Status;
}
VOID
PerformReadRequest(
PELF_REQUEST_RECORD Request
)
/*++
Routine Description:
This routine performs the READ request.
It first grabs the log file structure resource and then proceeds
to read from the file. If the resource is not available, it will
block until it is.
This routine impersonates the client in order to ensure that the correct
access control is uesd. If the client does not have permission to read
the file, the operation will fail.
Arguments:
Pointer to the request packet.
Return Value:
NONE
Note:
--*/
{
//
// Get shared access to the log file. This will allow multiple
// readers to get to the file together.
//
RtlAcquireResourceShared(&Request->Module->LogFile->Resource,
TRUE); // Wait until available
//
// Try to read from the log. Note that a corrupt log is the
// most likely cause of an exception (garbage pointers, etc).
// The eventlog corruption error is a bit all-inclusive, but
// necessary, since log state is pretty much indeterminable
// in this situation.
//
try
{
Request->Status = ReadFromLog(Request);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"PerformReadRequest: Caught exception %#x reading %ws log\n",
GetExceptionCode(),
Request->Module->ModuleName);
Request->Status = STATUS_EVENTLOG_FILE_CORRUPT;
}
//
// Release the resource
//
RtlReleaseResource(&Request->Module->LogFile->Resource);
}
//
// BUGBUG: These are only used in WriteToLog and they're not modified.
// Probably cleaner to make them #define'd constants.
//
WCHAR wszAltDosDevices[] = L"\\DosDevices\\";
WCHAR wszDosDevices[] = L"\\??\\";
#define DOSDEVICES_LEN ((sizeof(wszDosDevices) / sizeof(WCHAR)) - 1)
#define ALTDOSDEVICES_LEN ((sizeof(wszAltDosDevices) / sizeof(WCHAR)) - 1)
VOID
WriteToLog(
PLOGFILE pLogFile,
PVOID Buffer,
ULONG BufSize,
PULONG Destination,
ULONG PhysEOF,
ULONG PhysStart
)
/*++
Routine Description:
This routine writes the record into the log file, allowing for wrapping
around the end of the file.
It assumes that the caller has serialized access to the file, and has
ensured that there is enough space in the file for the record.
Arguments:
Buffer - Pointer to the buffer containing the event record.
BufSize - Size of the record to be written.
Destination - Pointer to the destination - which is in the log file.
PhysEOF - Physical end of file.
PhysStart - Physical beginning of file (past the file header).
Return Value:
NONE.
Note:
--*/
{
ULONG BytesToCopy;
SIZE_T FlushSize;
ULONG NewDestination;
NTSTATUS Status;
PVOID BaseAddress;
LPWSTR pwszLogFileName;
LARGE_INTEGER ByteOffset;
IO_STATUS_BLOCK IoStatusBlock;
BytesToCopy = min(PhysEOF - *Destination, BufSize);
ByteOffset = RtlConvertUlongToLargeInteger(*Destination);
//
// BUGBUG: If this call fails, we shouldn't set *Destination to
// NewDestination below. The same is true if the second
// NtWriteFile call fails.
//
Status = NtWriteFile(pLogFile->FileHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
Buffer, // Buffer
BytesToCopy, // Length
&ByteOffset, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"WriteToLog: NtWriteFile (1st call) failed %#x\n",
Status);
}
NewDestination = *Destination + BytesToCopy;
if (BytesToCopy != BufSize)
{
//
// Wrap around to the beginning of the file and copy the
// rest of the data.
//
Buffer = (PBYTE) Buffer + BytesToCopy;
BytesToCopy = BufSize - BytesToCopy;
ByteOffset = RtlConvertUlongToLargeInteger(PhysStart);
Status = NtWriteFile(pLogFile->FileHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
Buffer, // Buffer
BytesToCopy, // Length
&ByteOffset, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"WriteToLog: NtWriteFile (2nd call) failed %#x\n",
Status);
}
NewDestination = PhysStart + BytesToCopy;
//
// Set "wrap" bit in log file structure
//
pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
//
// Now flush this to disk to commit it
//
BaseAddress = pLogFile->BaseAddress;
FlushSize = FILEHEADERBUFSIZE;
Status = NtFlushVirtualMemory(NtCurrentProcess(),
&BaseAddress,
&FlushSize,
&IoStatusBlock);
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"WriteToLog: NtFlushVirtualMemory failed %#x\n",
Status);
}
}
*Destination = NewDestination; // Return new destination
//
// Providing all succeeded above, if not set, set the archive file
// attribute on this log.
//
if (NT_SUCCESS(Status)
&&
!(pLogFile->Flags & ELF_LOGFILE_ARCHIVE_SET))
{
//
// Advance past prefix string, '\??\' or '\DosDevices\'
//
if ((pLogFile->LogFileName->Length / 2) >= DOSDEVICES_LEN
&&
!_wcsnicmp(wszDosDevices, pLogFile->LogFileName->Buffer, DOSDEVICES_LEN))
{
pwszLogFileName = pLogFile->LogFileName->Buffer + DOSDEVICES_LEN;
}
else if ((pLogFile->LogFileName->Length / 2) >= ALTDOSDEVICES_LEN
&&
!_wcsnicmp(wszAltDosDevices, pLogFile->LogFileName->Buffer, ALTDOSDEVICES_LEN))
{
pwszLogFileName = pLogFile->LogFileName->Buffer + ALTDOSDEVICES_LEN;
}
else
{
pwszLogFileName = pLogFile->LogFileName->Buffer;
}
if (SetFileAttributes(pwszLogFileName, FILE_ATTRIBUTE_ARCHIVE))
{
pLogFile->Flags |= ELF_LOGFILE_ARCHIVE_SET;
}
else
{
ELF_LOG2(ERROR,
"WriteToLog: SetFileAttributes on file %ws failed %d\n",
pwszLogFileName,
GetLastError());
}
}
}
NTSTATUS
AutoBackupLogFile(PELF_REQUEST_RECORD Request, ULONG OverwrittenEOF)
/*++
Routine Description:
This routine makes a best attempt to backup the log file.
It will log an event in the SECURITY log file indicating
success or failure.
Arguments:
Pointer to the request packet.
Return Value:
NTSTATUS
Note:
--*/
{
UNICODE_STRING BackupFileNameU;
WCHAR BackupFileName[64]; // holds the "unique" part of the name (wsprintf)
PLOGFILE pLogFile; // For optimized access to structure
WCHAR BackupFileNamePrefix[] = L"Archive-";
WCHAR* EventMessage[3];
WCHAR Number[20]; // long enough to hold a DWORD represented as a string
SYSTEMTIME SystemTime;
ELF_REQUEST_RECORD ClearRequest;
CLEAR_PKT ClearPkt;
USHORT ClearStatus;
NTSTATUS Status;
ULONG WritePos; // Position to write record
pLogFile = Request->LogFile; // Set local variable
if (OverwrittenEOF)
{
//
// The EOF record was at the end of the physical file,
// and we overwrote it with ELF_SKIP_DWORDs, so we need
// to put it back since we're not going to be able to
// write a record. We also need to turn the wrap bit
// back off
//
pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_WRAP);
pLogFile->EndRecord = OverwrittenEOF;
WritePos = OverwrittenEOF;
//
// Write out the EOF record
//
WriteToLog(pLogFile,
&EOFRecord,
ELFEOFRECORDSIZE,
&WritePos,
pLogFile->ActualMaxFileSize,
FILEHEADERBUFSIZE);
}
Status = FlushLogFile(pLogFile);
if (!NT_SUCCESS(Status))
return Status;
//
// Make BackupFileNameU unique.
// Allocate enough space for the current LogModuleName, a NULL character,
// and BackupFileName bytes.
// If AutoBackupPath is non-NULL, add space for that too.
//
// Rename file in place.
//
BackupFileNameU.Length = 0;
BackupFileNameU.MaximumLength = ((wcslen(pLogFile->LogModuleName->Buffer) +
wcslen(BackupFileNamePrefix) + 1 ) * sizeof(WCHAR)) +
sizeof(BackupFileName);
BackupFileNameU.Buffer = ElfpAllocateBuffer(BackupFileNameU.MaximumLength);
if (BackupFileNameU.Buffer == NULL)
{
ELF_LOG0(ERROR,
"AutoBackupLogFile: failed due to lack of memory\n");
return STATUS_NO_MEMORY;
}
StringCbCopy(BackupFileNameU.Buffer, BackupFileNameU.MaximumLength,
BackupFileNamePrefix);
StringCbCat(BackupFileNameU.Buffer, BackupFileNameU.MaximumLength,
pLogFile->LogModuleName->Buffer);
GetSystemTime(&SystemTime);
StringCchPrintfW(BackupFileName, 64, L"-%u-%02u-%02u-%02u-%02u-%02u-%03u.evt",
SystemTime.wYear,
SystemTime.wMonth,
SystemTime.wDay,
SystemTime.wHour,
SystemTime.wMinute,
SystemTime.wSecond,
SystemTime.wMilliseconds);
StringCbCat(BackupFileNameU.Buffer, BackupFileNameU.MaximumLength,BackupFileName);
BackupFileNameU.Length = wcslen(BackupFileNameU.Buffer) * sizeof(WCHAR);
//
// Fill in the request packet
//
ClearRequest.Pkt.ClearPkt = &ClearPkt;
ClearRequest.Flags = 0;
ClearRequest.Module = Request->Module;
ClearRequest.LogFile = Request->LogFile;
ClearRequest.Command = ELF_COMMAND_CLEAR;
ClearRequest.Status = STATUS_SUCCESS;
ClearRequest.Pkt.ClearPkt->BackupFileName = &BackupFileNameU;
PerformClearRequest(&ClearRequest);
//
// Generate an audit in the Security log.
//
StringCchPrintfW(Number, 20, L"0x%x", ClearRequest.Status);
EventMessage[0] = pLogFile->LogModuleName->Buffer;
EventMessage[1] = BackupFileNameU.Buffer;
EventMessage[2] = Number;
if (NT_SUCCESS(ClearRequest.Status))
{
ELF_LOG0(TRACE,
"AutoBackupLogFile: auto backup and clear worked\n");
ClearStatus = EVENTLOG_AUDIT_SUCCESS;
}
else
{
ELF_LOG1(ERROR,
"AutoBackupLogFile: failed calling clear/backup, error 0x%x\n",
ClearRequest.Status);
ClearStatus = EVENTLOG_AUDIT_FAILURE;
}
ElfpCreateElfEvent(
0x20c, // todo, get the #def
ClearStatus,
1, // EventCategory (SE_CATEGID_SYSTEM)
3, // NumberOfStrings
EventMessage, // Strings
NULL, // Data
0, // Datalength
0, // Do not Overwrite if necc.
TRUE); // For the Security Log file
ElfpFreeBuffer(BackupFileNameU.Buffer);
return ClearRequest.Status;
}
VOID
PerformWriteRequest(
PELF_REQUEST_RECORD Request
)
/*++
Routine Description:
This routine writes the event log entry to the log file specified in
the request packet.
There is no need to impersonate the client since we want all clients
to have access to writing to the log file.
This routine does not use memory mapped I/O to access the log file. This
is so the changes can be immediately committed to disk if that was how
the log file was opened.
Arguments:
Pointer to the request packet.
Return Value:
NONE
Note:
--*/
{
NTSTATUS Status;
ULONG WritePos; // Position to write record
LARGE_INTEGER Time;
ULONG SpaceNeeded; // Record size + "buffer" size
ULONG CurrentTime = 0;
PEVENTLOGRECORD EventRecord;
ULONG RecordSize;
ULONG DeletedRecordOffset;
ULONG SpaceAvail;
ULONG EarliestTime;
PLOGFILE pLogFile; // For optimized access to structure
PELF_LOGFILE_HEADER pFileHeader;
PVOID BaseAddress;
IO_STATUS_BLOCK IoStatusBlock;
PEVENTLOGRECORD pEventLogRecord;
PDWORD FillDword;
ULONG OverwrittenEOF = 0;
BOOL bSecurity = FALSE;
BOOL fRetryWriteRequest = FALSE;
pLogFile = Request->LogFile; // Set local variable
// Set some basic BOOLs
if(!_wcsicmp(pLogFile->LogModuleName->Buffer, ELF_SECURITY_MODULE_NAME))
bSecurity = TRUE;
else
bSecurity = FALSE;
//This condition occurs under stress conditions
//and causes an AV further in this function
if(pLogFile->BaseAddress == NULL)
{
Request->Status = STATUS_INVALID_HANDLE;
return;
}
//
// Get exclusive access to the log file. This will ensure no one
// else is accessing the file.
//
RtlAcquireResourceExclusive(&pLogFile->Resource,
TRUE); // Wait until available
try
{
RetryWriteRequest:
//
// Put in the record number
//
pEventLogRecord = (PEVENTLOGRECORD) Request->Pkt.WritePkt->Buffer;
pEventLogRecord->RecordNumber = pLogFile->CurrentRecordNumber;
//
// Now, go to the end of the file and look for empty space.
//
// If there is enough space to write out the record, just
// write it out and update the pointers.
//
// If there isn't enough space, then we need to check if we can
// wrap around the file without overwriting any records that are
// within the time retention period.
// If we cannot find any room, then we have to return an error
// that the file is full (and alert the administrator).
//
RecordSize = Request->Pkt.WritePkt->Datasize;
SpaceNeeded = RecordSize + ELFEOFRECORDSIZE;
if (pLogFile->EndRecord > pLogFile->BeginRecord)
{
//
// The current write position is after the position of the first
// record, then we can write up to the end of the file without
// worrying about overwriting existing records.
//
SpaceAvail = pLogFile->ActualMaxFileSize
- (pLogFile->EndRecord - pLogFile->BeginRecord + FILEHEADERBUFSIZE);
}
else if (pLogFile->EndRecord == pLogFile->BeginRecord
&&
!(pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP))
{
//
// If the write position is equal to the position of the first
// record, and we have't wrapped yet, then the file is "empty"
// and so we have room to the physical end of the file.
//
SpaceAvail = pLogFile->ActualMaxFileSize - FILEHEADERBUFSIZE;
}
else
{
//
// If our write position is before the position of the first record, then
// the file has wrapped and we need to deal with overwriting existing
// records in the file.
//
SpaceAvail = pLogFile->BeginRecord - pLogFile->EndRecord;
}
//
// We now have the number of bytes available to write the record
// WITHOUT overwriting any existing records stored in SpaceAvail.
// If that amount is not sufficient, then we need to create more space
// by "deleting" existing records that are older than the retention
// time that was configured for this file.
//
// We check the retention time against the time when the log was
// written since that is consistent at the server. We cannot use the
// client's time since that may vary if the clients are in different
// time zones.
//
NtQuerySystemTime(&Time);
RtlTimeToSecondsSince1970(&Time, &CurrentTime);
EarliestTime = CurrentTime - pLogFile->Retention;
Status = STATUS_SUCCESS; // Initialize for return to caller
//
// Check to see if the file hasn't reached its maximum allowable
// size yet, and also hasn't wrapped. If not, grow it by as much as
// needed, in 64K chunks.
//
if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize
&&
SpaceNeeded > SpaceAvail)
{
//
// Extend it. This call cannot fail. If it can't extend it, it
// just caps it at the current size by changing
// pLogFile->ConfigMaxFileSize
//
ElfExtendFile(pLogFile,
SpaceNeeded,
&SpaceAvail);
}
//
// We don't want to split the fixed portion of a record across the
// physical end of the file, it makes it difficult when referencing
// these fields later (you have to check before you touch each one
// to make sure it's not after the physical EOF). So, if there's
// not enough room at the end of the file for the fixed portion,
// we fill it with a known byte pattern (ELF_SKIP_DWORD) that will
// be skipped if it's found at the start of a record (as long as
// it's less than the minimum record size, then we know it's not
// the start of a valid record).
//
if (pLogFile->ActualMaxFileSize - pLogFile->EndRecord < sizeof(EVENTLOGRECORD))
{
//
// Save the EndRecord pointer. In case we don't have the space
// to write another record, we'll need to rewrite the EOF where
// it was
//
OverwrittenEOF = pLogFile->EndRecord;
FillDword = (PDWORD) ((PBYTE) pLogFile->BaseAddress + pLogFile->EndRecord);
while (FillDword < (PDWORD) ((LPBYTE) pLogFile->BaseAddress +
pLogFile->ActualMaxFileSize))
{
*FillDword = ELF_SKIP_DWORD;
FillDword++;
}
pLogFile->EndRecord = FILEHEADERBUFSIZE;
SpaceAvail = pLogFile->BeginRecord - FILEHEADERBUFSIZE;
pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
}
EventRecord = (PEVENTLOGRECORD) ((PBYTE) pLogFile->BaseAddress
+ pLogFile->BeginRecord);
while (SpaceNeeded > SpaceAvail)
{
//
// If this logfile can be overwrite-as-needed, or if it has
// an overwrite time limit and the time hasn't expired, then
// allow the new event to overwrite an older event.
//
if (pLogFile->Retention == OVERWRITE_AS_NEEDED
||
(pLogFile->Retention != NEVER_OVERWRITE
&&
(EventRecord->TimeWritten < EarliestTime
||
Request->Flags & ELF_FORCE_OVERWRITE)))
{
//
// OK to overwrite
//
ULONG NextRecord;
ULONG SearchStartPos;
BOOL fBeginningRecordWrap = FALSE;
BOOL fInvalidRecordLength = FALSE;
DeletedRecordOffset = pLogFile->BeginRecord;
pLogFile->BeginRecord += EventRecord->Length;
//
// Ensure BeginRecord offset is DWORD-aligned.
//
pLogFile->BeginRecord = (pLogFile->BeginRecord + sizeof(ULONG) - 1)
& ~(sizeof(ULONG) - 1);
//
// Check specifically for a record length value of zero.
// Zero is considered invalid.
//
if (EventRecord->Length == 0)
{
ELF_LOG2(ERROR,
"PerformWriteRequest: Zero-length record at "
"offset %d in %ws log\n",
DeletedRecordOffset,
pLogFile->LogModuleName->Buffer);
fInvalidRecordLength = TRUE;
}
if (pLogFile->BeginRecord >= pLogFile->ActualMaxFileSize)
{
ULONG BeginRecord;
//
// We're about to wrap around the end of the file. Adjust
// BeginRecord accordingly.
//
fBeginningRecordWrap = TRUE;
BeginRecord = FILEHEADERBUFSIZE
+ (pLogFile->BeginRecord
- pLogFile->ActualMaxFileSize);
//
// If the record length was bogus (very large), it's possible
// the wrap-adjusted computed position is still beyond the
// end of file. In thise case, mark it as bogus.
//
if (BeginRecord >= pLogFile->ActualMaxFileSize)
{
ELF_LOG3(ERROR,
"PerformWriteRequest: Too-large record length (%#x) "
"at offset %d in %ws log\n",
EventRecord->Length,
DeletedRecordOffset,
pLogFile->LogModuleName->Buffer);
fInvalidRecordLength = TRUE;
}
else
{
pLogFile->BeginRecord = BeginRecord;
}
}
if (fInvalidRecordLength)
{
//
// If the record length is considered bogus, adjust
// BeginRecord to be just beyond the length and signature of
// the previous record to scan for the next valid record.
//
pLogFile->BeginRecord = DeletedRecordOffset
+ (sizeof(ULONG) * 2);
}
//
// Ensure the record referenced is indeed a valid record and that
// we're not reading into a partially overwritten record. With a
// circular log, it's possible to partially overwrite existing
// entries with the EOF record and/or ELF_SKIP_DWORD values.
//
// Skip the record size to the record signature since the loop
// below will search for the next valid signature. Note that
// the increment of SpaceAvail is undone when we find a valid
// record below.
//
NextRecord = pLogFile->BeginRecord + sizeof(ULONG);
if (NextRecord < pLogFile->ActualMaxFileSize)
{
SpaceAvail += min(sizeof(ULONG),
pLogFile->ActualMaxFileSize - NextRecord);
}
//
// Seek to find a record signature.
//
SearchStartPos = pLogFile->BeginRecord;
for ( ;; )
{
PVOID Position;
for ( ;
NextRecord != SearchStartPos;
SpaceAvail += sizeof(ULONG), NextRecord += sizeof(ULONG))
{
if (NextRecord >= pLogFile->ActualMaxFileSize)
{
NextRecord = pLogFile->BeginRecord = FILEHEADERBUFSIZE;
}
if (*(PULONG) ((PBYTE) pLogFile->BaseAddress + NextRecord)
== ELF_LOG_FILE_SIGNATURE)
{
//
// Found the next valid record signature
//
break;
}
}
Position = (PULONG) ((PBYTE) pLogFile->BaseAddress + NextRecord);
if (*(PULONG) Position == ELF_LOG_FILE_SIGNATURE)
{
//
// This record is valid so far, perform a final, more
// rigorous check for record validity.
//
if (ValidFilePos(CONTAINING_RECORD(Position,
EVENTLOGRECORD,
Reserved),
NULL,
NULL,
(PBYTE) pLogFile->BaseAddress
+ pLogFile->ViewSize,
pLogFile->BaseAddress,
FALSE))
{
//
// The record is valid. Adjust SpaceAvail to not
// include a sub-portion of this record in the
// available space computation.
//
SpaceAvail -= sizeof(ULONG);
pLogFile->BeginRecord = NextRecord - sizeof(ULONG);
break;
}
else
{
//
// Continue the search for the next valid record.
//
// NB : Not calling FixContextHandlesForRecord
// since we have not established a valid
// beginning record position yet. Not that
// it would do any good - this condition would
// be evaluated in cases of corrupt logs.
//
ELF_LOG2(FILES,
"PerformWriteRequest: Valid record signature with "
"invalid record found at offset %d of %ws log\n",
NextRecord,
pLogFile->LogModuleName->Buffer);
SpaceAvail += sizeof(ULONG);
NextRecord += sizeof(ULONG);
continue;
}
}
else
{
//
// Not a single valid record can be found. This is not
// good. Consider the log corrupted and bail the write.
//
ELF_LOG1(ERROR,
"PerformWriteRequest: No valid records found in %ws log\n",
pLogFile->LogModuleName->Buffer);
Status = STATUS_EVENTLOG_FILE_CORRUPT;
ASSERT(Status != STATUS_EVENTLOG_FILE_CORRUPT);
break;
}
}
if (Status == STATUS_EVENTLOG_FILE_CORRUPT)
{
break;
}
if (fBeginningRecordWrap)
{
//
// Check to see if the file has reached its maximum allowable
// size yet. If not, grow it by as much as needed in 64K
// chunks.
//
if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize)
{
//
// Extend it. This call cannot fail. If it can't
// extend it, it just caps it at the current size by
// changing pLogFile->ConfigMaxFileSize.
//
ElfExtendFile(pLogFile,
SpaceNeeded,
&SpaceAvail);
//
// Since extending the file will cause it to be moved, we
// need to re-establish the address for the EventRecord.
//
EventRecord = (PEVENTLOGRECORD) ((PBYTE) pLogFile->BaseAddress
+ DeletedRecordOffset);
}
}
//
// Make sure no handle points to the record that we're getting
// ready to overwrite, it one does, correct it to point to the
// new first record.
//
FixContextHandlesForRecord(DeletedRecordOffset,
pLogFile->BeginRecord);
if (!fInvalidRecordLength)
{
//
// Update SpaceAvail to include the deleted record's size.
// That is, if we have a high degree of confidence that
// it is valid.
//
SpaceAvail += EventRecord->Length;
}
//
// Bump to the next record, file wrap was handled above
//
//
// If these are ELF_SKIP_DWORDs, just move past them
//
FillDword = (PDWORD) ((PBYTE) pLogFile->BaseAddress
+ pLogFile->BeginRecord);
if (*FillDword == ELF_SKIP_DWORD)
{
SpaceAvail += pLogFile->ActualMaxFileSize - pLogFile->BeginRecord;
pLogFile->BeginRecord = FILEHEADERBUFSIZE;
}
EventRecord = (PEVENTLOGRECORD) ((PBYTE) pLogFile->BaseAddress
+ pLogFile->BeginRecord);
}
else
{
//
// CODEWORK: Split this out into a separate function
//
//
// All records within retention period
//
ELF_LOG1(ERROR,
"PerformWriteRequest: %ws log is full\n",
pLogFile->LogModuleName->Buffer);
//
// Do new behavior.
//
// Auto backup, clear the log, and log and event that says the file
// was backed up and try to log the current event one more time.
//
// If this cannot be done, we made our best attempt, so we will probably
// crash on audit fail, and not log the event (revert to old behavior).
//
if ((pLogFile->AutoBackupLogFiles != 0) && (fRetryWriteRequest == FALSE)) {
AutoBackupLogFile(Request, OverwrittenEOF);
fRetryWriteRequest = TRUE;
goto RetryWriteRequest;
}
//
// Hang an event on the queuedevent list for later writing
// if we haven't just written a log full event for this log.
// Don't put up the popup during setup as there's nothing
// the user can do about it until setup finishes.
//
if (pLogFile->logpLogPopup == LOGPOPUP_CLEARED
&&
!ElfGlobalData->fSetupInProgress && !bSecurity)
{
INT StringLen, id = -1;
LPTSTR lpModuleNameLoc = NULL;
HMODULE StringsResource;
//
// We should never be popping up or logging an event
// for the security log
//
ASSERT(_wcsicmp(pLogFile->LogModuleName->Buffer,
ELF_SECURITY_MODULE_NAME) != 0);
//
// Get the localized module name from message table
//
StringsResource = GetModuleHandle(L"EVENTLOG.DLL");
ASSERT(StringsResource != NULL);
if (_wcsicmp(pLogFile->LogModuleName->Buffer,
ELF_SYSTEM_MODULE_NAME) == 0)
{
id = ELF_MODULE_NAME_LOCALIZE_SYSTEM;
}
else if (_wcsicmp(pLogFile->LogModuleName->Buffer,
ELF_APPLICATION_MODULE_NAME) == 0)
{
id = ELF_MODULE_NAME_LOCALIZE_APPLICATION;
}
if (id != -1)
{
StringLen = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
StringsResource,
id,
0,
(LPTSTR) &lpModuleNameLoc,
0,
NULL);
if ((StringLen > 1) && (lpModuleNameLoc != NULL))
{
//
// Get rid of cr/lf control code at the end
//
*(lpModuleNameLoc + StringLen - 2) = 0;
}
}
//
// Create the "log full" event -- use the name stored in the
// log's default module if it's not a well-known log type
//
ElfpCreateElfEvent(EVENT_LOG_FULL,
EVENTLOG_ERROR_TYPE,
0, // EventCategory
1, // NumberOfStrings
(lpModuleNameLoc != NULL) ?
&lpModuleNameLoc :
&Request->LogFile->LogModuleName->Buffer,
NULL, // Data
0, // Datalength
ELF_FORCE_OVERWRITE,
FALSE); // Overwrite if necc.
ElfpCreateQueuedMessage(
ALERT_ELF_LogOverflow,
1,
(lpModuleNameLoc != NULL) ?
&lpModuleNameLoc :
&Request->Module->LogFile->LogModuleName->Buffer);
LocalFree(lpModuleNameLoc);
//
// Don't post the popup again until either the machine is
// rebooted or the log is cleared
//
pLogFile->logpLogPopup = LOGPOPUP_ALREADY_SHOWN;
}
else if(bSecurity)
{
if(pLogFile->bFullAlertDone == FALSE)
ElfpCreateQueuedAlert(
ALERT_ELF_LogOverflow,
1,
&Request->Module->LogFile->LogModuleName->Buffer);
pLogFile->bFullAlertDone = TRUE;
}
pLogFile->Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
if (OverwrittenEOF)
{
//
// The EOF record was at the end of the physical file,
// and we overwrote it with ELF_SKIP_DWORDs, so we need
// to put it back since we're not going to be able to
// write a record. We also need to turn the wrap bit
// back off
//
pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_WRAP);
pLogFile->EndRecord = OverwrittenEOF;
WritePos = OverwrittenEOF;
//
// Write out the EOF record
//
WriteToLog(pLogFile,
&EOFRecord,
ELFEOFRECORDSIZE,
&WritePos,
pLogFile->ActualMaxFileSize,
FILEHEADERBUFSIZE);
}
Status = STATUS_LOG_FILE_FULL;
break; // Get out of while loop
}
}
if (NT_SUCCESS(Status))
{
//
// We have enough room to write the record and the EOF record.
//
//
// Update OldestRecordNumber to reflect the records that were
// overwritten amd increment the CurrentRecordNumber
//
// Make sure that the log isn't empty, if it is, the oldest
// record is 1
//
if (pLogFile->BeginRecord == pLogFile->EndRecord)
{
pLogFile->OldestRecordNumber = 1;
}
else
{
pLogFile->OldestRecordNumber = EventRecord->RecordNumber;
}
pLogFile->CurrentRecordNumber++;
//
// If the dirty bit is not set, then this is the first time that
// we have written to the file since we started. In that case,
// set the dirty bit in the file header as well so that we will
// know that the contents have changed.
//
if (!(pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY))
{
SIZE_T HeaderSize;
pLogFile->Flags |= ELF_LOGFILE_HEADER_DIRTY;
pFileHeader = (PELF_LOGFILE_HEADER) pLogFile->BaseAddress;
pFileHeader->Flags |= ELF_LOGFILE_HEADER_DIRTY;
//
// Now flush this to disk to commit it
//
BaseAddress = pLogFile->BaseAddress;
HeaderSize = FILEHEADERBUFSIZE;
Status = NtFlushVirtualMemory(NtCurrentProcess(),
&BaseAddress,
&HeaderSize,
&IoStatusBlock);
if (!NT_SUCCESS(Status))
{
ELF_LOG1(ERROR,
"PerformWriteRequest: NtFlushVirtualMemory to add dirty "
"flag to header failed %#x\n",
Status);
}
}
//
// Write the event to the log
//
WriteToLog(pLogFile,
Request->Pkt.WritePkt->Buffer,
RecordSize,
&(pLogFile->EndRecord),
pLogFile->ActualMaxFileSize,
FILEHEADERBUFSIZE);
//
// Use a separate variable for the position since we don't want
// it updated.
//
WritePos = pLogFile->EndRecord;
if (WritePos > pLogFile->ActualMaxFileSize)
{
WritePos -= pLogFile->ActualMaxFileSize - FILEHEADERBUFSIZE;
}
//
// Update the EOF record fields
//
EOFRecord.BeginRecord = pLogFile->BeginRecord;
EOFRecord.EndRecord = WritePos;
EOFRecord.CurrentRecordNumber = pLogFile->CurrentRecordNumber;
EOFRecord.OldestRecordNumber = pLogFile->OldestRecordNumber;
//
// Write out the EOF record
//
WriteToLog(pLogFile,
&EOFRecord,
ELFEOFRECORDSIZE,
&WritePos,
pLogFile->ActualMaxFileSize,
FILEHEADERBUFSIZE);
//
// If we had just written a logfull record, turn the bit off.
// Since we just wrote a record, technically it's not full anymore
//
if (!(Request->Flags & ELF_FORCE_OVERWRITE))
{
pLogFile->Flags &= ~(ELF_LOGFILE_LOGFULL_WRITTEN);
}
//
// See if there are any ElfChangeNotify callers to notify, and if
// there are, pulse their event
//
NotifyChange(pLogFile);
}
//
// Set status field in the request packet.
//
Request->Status = Status;
}
except (EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"PerformWriteRequest: Caught exception %#x writing to %ws log\n",
GetExceptionCode(),
pLogFile->LogModuleName->Buffer);
Request->Status = STATUS_EVENTLOG_FILE_CORRUPT;
}
//
// Release the resource
//
RtlReleaseResource ( &pLogFile->Resource );
}
VOID
PerformClearRequest(
PELF_REQUEST_RECORD Request
)
/*++
Routine Description:
This routine will optionally back up the log file specified, and will
delete it.
Arguments:
Pointer to the request packet.
Return Value:
NONE
Note:
On the exit path, when we do some "cleanup" work, we discard the
status and instead return the status of the operation that is being
performed.
This is necessary since we wish to return any error condition that is
directly related to the clear operation. For other errors, we will
fail at a later stage.
--*/
{
NTSTATUS Status, IStatus;
PUNICODE_STRING FileName;
IO_STATUS_BLOCK IoStatusBlock;
PFILE_RENAME_INFORMATION NewName = NULL;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE ClearHandle = NULL;
FILE_DISPOSITION_INFORMATION DeleteInfo = {TRUE};
ULONG FileRefCount;
BOOLEAN FileRenamed = FALSE;
//
// Get exclusive access to the log file. This will ensure no one
// else is accessing the file.
//
RtlAcquireResourceExclusive (&Request->Module->LogFile->Resource,
TRUE); // Wait until available
//
// We have exclusive access to the file.
//
// We force the file to be closed, and store away the ref count
// so that we can set it back when we reopen the file.
// This is a little *sleazy* but we have exclusive access to the
// logfile structure so we can play these games.
//
FileRefCount = Request->LogFile->RefCount; // Store this away
ElfpCloseLogFile(Request->LogFile, ELF_LOG_CLOSE_FORCE);
Request->LogFile->FileHandle = NULL; // For use later
//
// Open the file with delete access in order to rename it.
//
InitializeObjectAttributes(&ObjectAttributes,
Request->LogFile->LogFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&ClearHandle,
GENERIC_READ | DELETE | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(Status))
{
//
// If the backup file name has been specified and is not NULL,
// then we move the current file to the new file. If that fails,
// then fail the whole operation.
//
if ((Request->Pkt.ClearPkt->BackupFileName != NULL)
&&
(Request->Pkt.ClearPkt->BackupFileName->Length != 0))
{
FileName = Request->Pkt.ClearPkt->BackupFileName;
//
// Set up the rename information structure with the new name
//
NewName = ElfpAllocateBuffer(FileName->Length
+ sizeof(WCHAR) + sizeof(*NewName));
if (NewName)
{
RtlCopyMemory(NewName->FileName,
FileName->Buffer,
FileName->Length);
//
// Guarantee that it's NULL terminated
//
NewName->FileName[FileName->Length / sizeof(WCHAR)] = L'\0';
NewName->ReplaceIfExists = FALSE;
NewName->RootDirectory = NULL;
NewName->FileNameLength = FileName->Length;
Status = NtSetInformationFile(ClearHandle,
&IoStatusBlock,
NewName,
FileName->Length + sizeof(*NewName),
FileRenameInformation);
if (Status == STATUS_NOT_SAME_DEVICE)
{
//
// They want the backup file to be on a different
// device. We need to copy this one, and then delete
// it.
//
ELF_LOG2(FILES,
"PerformClearRequest: Attempting to copy log file %ws "
"to different device (%ws)\n",
Request->LogFile->LogFileName->Buffer,
NewName->FileName);
Status = ElfpCopyFile(ClearHandle, FileName);
if (NT_SUCCESS(Status))
{
ELF_LOG1(FILES,
"PerformClearRequest: Copy succeeded -- deleting %ws\n",
Request->LogFile->LogFileName->Buffer);
Status = NtSetInformationFile(ClearHandle,
&IoStatusBlock,
&DeleteInfo,
sizeof(DeleteInfo),
FileDispositionInformation);
if (!NT_SUCCESS (Status))
{
ELF_LOG2(ERROR,
"PerformClearRequest: Delete of %ws after "
"successful copy failed %#x\n",
Request->LogFile->LogFileName->Buffer,
Status);
}
}
}
else if (NT_SUCCESS (Status))
{
FileRenamed = TRUE;
}
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformClearRequest: Rename of %ws failed %#x\n",
Request->LogFile->LogFileName->Buffer,
Status);
}
}
else
{
ELF_LOG0(ERROR,
"PerformClearRequest: Unable to allocate memory for "
"FILE_RENAME_INFORMATION structure\n");
Status = STATUS_NO_MEMORY;
}
}
else
{
//
// No backup name was specified. Just delete the log file
// (i.e. "clear it"). We can just delete it since we know
// that the first time anything is written to a log file,
// if that file does not exist, it is created and a header
// is written to it. By deleting it here, we make it cleaner
// to manage log files, and avoid having zero-length files all
// over the disk.
//
ELF_LOG1(FILES,
"PerformClearRequest: No backup name specified -- deleting %ws\n",
Request->LogFile->LogFileName->Buffer);
Status = NtSetInformationFile(ClearHandle,
&IoStatusBlock,
&DeleteInfo,
sizeof(DeleteInfo),
FileDispositionInformation);
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformClearRequest: Delete of %ws failed %#x\n",
Request->LogFile->LogFileName->Buffer,
Status);
}
}
IStatus = NtClose(ClearHandle); // Discard status
ASSERT(NT_SUCCESS(IStatus));
}
else
{
//
// The open-for-delete failed.
//
ELF_LOG2(ERROR,
"PerformClearRequest: NtOpenFile of %ws for delete failed %#x\n",
Request->LogFile->LogFileName->Buffer,
Status);
}
//
// If the user reduced the size of the log file, pick up the new
// size as it couldn't be used until the log was cleared
//
if (NT_SUCCESS (Status))
{
if (Request->LogFile->NextClearMaxFileSize)
{
Request->LogFile->ConfigMaxFileSize = Request->LogFile->NextClearMaxFileSize;
}
//
// We need to recreate the file or if the file was just closed,
// then we reopen it.
//
IStatus = ElfOpenLogFile(Request->LogFile, ElfNormalLog);
if (!NT_SUCCESS(IStatus))
{
ELF_LOG2(ERROR,
"PerformClearRequest: Open of %ws after successful delete "
"failed %#x\n",
Request->LogFile->LogFileName->Buffer,
IStatus);
Status = IStatus;
//
// The open failed -- try to restore the old log file. If
// NewName is NULL, it means there was no backup file specified.
//
if (NewName != NULL)
{
//
// Opening the new log file failed, reopen the old log and
// return this error from the Api
//
PFILE_RENAME_INFORMATION OldName;
UNICODE_STRING UnicodeString;
//
// There shouldn't be any way to fail unless we successfully
// renamed the file, and there's no recovery if that happens.
//
//
// BUGBUG: We can hit this if the user asked for the backup
// file to be put on a different device, in which
// case the delete succeeded but FileRenamed is
// still set to FALSE if ElfOpenLogFile fails.
//
ASSERT(FileRenamed == TRUE);
//
// Rename the file back to the original name. Reuse ClearHandle.
//
RtlInitUnicodeString(&UnicodeString, NewName->FileName);
InitializeObjectAttributes(&ObjectAttributes,
&UnicodeString,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
IStatus = NtOpenFile(&ClearHandle,
GENERIC_READ | DELETE | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_DELETE,
FILE_SYNCHRONOUS_IO_NONALERT);
if (NT_SUCCESS(IStatus))
{
//
// Set up the rename information structure with the old name
//
OldName = ElfpAllocateBuffer(Request->LogFile->LogFileName->Length
+ sizeof(WCHAR) + sizeof(*OldName));
if (OldName)
{
PUNICODE_STRING pFileName = Request->LogFile->LogFileName;
RtlCopyMemory(OldName->FileName,
pFileName->Buffer,
pFileName->Length);
//
// Guarantee that it's NULL terminated
//
OldName->FileName[pFileName->Length / sizeof(WCHAR)] = L'\0';
OldName->ReplaceIfExists = FALSE;
OldName->RootDirectory = NULL;
OldName->FileNameLength = pFileName->Length;
//
// Change the name of the backed-up (i.e., cleared) log
// file to its original name.
//
IStatus = NtSetInformationFile(ClearHandle,
&IoStatusBlock,
OldName,
pFileName->Length
+ sizeof(*OldName)
+ sizeof(WCHAR),
FileRenameInformation);
ASSERT(NT_SUCCESS(IStatus));
//
// Reopen the original file. This has to work.
//
IStatus = ElfOpenLogFile(Request->LogFile, ElfNormalLog);
ASSERT(NT_SUCCESS(IStatus));
ElfpFreeBuffer(OldName);
}
NtClose(ClearHandle);
}
else
{
ELF_LOG2(ERROR,
"PerformClearRequest: Open of backed-up log file %ws "
"failed %#x\n",
NewName->FileName,
IStatus);
}
}
}
}
else
{
//
// The delete failed for some reason -- reopen the original log file
//
ELF_LOG1(FILES,
"PerformClearRequest: Delete of %ws failed -- reopening original file\n",
Request->LogFile->LogFileName->Buffer);
IStatus = ElfOpenLogFile(Request->LogFile, ElfNormalLog);
ASSERT(NT_SUCCESS(IStatus));
}
Request->LogFile->RefCount = FileRefCount; // Restore old value.
if (Request->LogFile->logpLogPopup == LOGPOPUP_ALREADY_SHOWN)
{
//
// This log has a viewable popup (i.e., it's not LOGPOPUP_NEVER_SHOW),
// so we should show it again if the log fills up.
//
Request->LogFile->logpLogPopup = LOGPOPUP_CLEARED;
}
Request->LogFile->bFullAlertDone = FALSE;
//
// Mark any open context handles that point to this file as "invalid for
// read." This will fail any further READ operations and force the caller
// to close and reopen the handle.
//
InvalidateContextHandlesForLogFile(Request->LogFile);
//
// Set status field in the request packet.
//
Request->Status = Status;
//
// Release the resource
//
RtlReleaseResource(&Request->Module->LogFile->Resource);
ElfpFreeBuffer(NewName);
}
VOID
PerformBackupRequest(
PELF_REQUEST_RECORD Request
)
/*++
Routine Description:
This routine will back up the log file specified.
This routine impersonates the client in order to ensure that the correct
access control is used.
This routine is entered with the ElfGlobalResource held in a shared
state and the logfile lock is acquired shared to prevent writing, but
allow people to still read.
This copies the file in two chunks, from the first record to the end
of the file, and then from the top of the file (excluding the header)
to the end of the EOF record.
Arguments:
Pointer to the request packet.
Return Value:
NONE, status is placed in the packet for later use by the API wrapper
--*/
{
NTSTATUS Status, IStatus;
IO_STATUS_BLOCK IoStatusBlock;
OBJECT_ATTRIBUTES ObjectAttributes;
LARGE_INTEGER MaximumSizeOfSection;
LARGE_INTEGER Offset;
ULONG LastRecordNumber;
ULONG OldestRecordNumber;
HANDLE BackupHandle = INVALID_HANDLE_VALUE;
PBYTE StartOfCopy;
PBYTE EndOfCopy;
ULONG BytesToCopy;
ULONG EndRecord = FILEHEADERBUFSIZE;
BOOL ImpersonatingClient = FALSE;
ELF_LOGFILE_HEADER FileHeaderBuf = { FILEHEADERBUFSIZE, // Size
ELF_LOG_FILE_SIGNATURE,
ELF_VERSION_MAJOR,
ELF_VERSION_MINOR,
FILEHEADERBUFSIZE, // Start offset
FILEHEADERBUFSIZE, // End offset
1, // Next record #
1, // Oldest record #
0, // Maxsize
0, // Flags
0, // Retention
FILEHEADERBUFSIZE // Size
};
//
// Get shared access to the log file. This will ensure no one
// else clears the file.
//
RtlAcquireResourceShared(&Request->Module->LogFile->Resource,
TRUE); // Wait until available
//
// Save away the next record number. We'll stop copying when we get to
// the record before this one. Also save the first record number so we
// can update the header and EOF record.
//
LastRecordNumber = Request->LogFile->CurrentRecordNumber;
OldestRecordNumber = Request->LogFile->OldestRecordNumber;
//
// Impersonate the client
//
Status = I_RpcMapWin32Status(RpcImpersonateClient(NULL));
if (NT_SUCCESS(Status))
{
//
// Keep this info so I can only revert in 1 place
//
ImpersonatingClient = TRUE;
//
// Set up the object attributes structure for the backup file
//
InitializeObjectAttributes(&ObjectAttributes,
Request->Pkt.BackupPkt->BackupFileName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
//
// Open the backup file. Fail if a file by this name already exists.
//
MaximumSizeOfSection =
RtlConvertUlongToLargeInteger(Request->LogFile->ActualMaxFileSize);
Status = NtCreateFile(&BackupHandle,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
&MaximumSizeOfSection,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_CREATE,
FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformBackupRequest: Open of backup file %ws failed %#x\n",
Request->Pkt.BackupPkt->BackupFileName->Buffer,
Status);
goto errorexit;
}
//
// Write out the header, we'll update it later
//
FileHeaderBuf.CurrentRecordNumber = LastRecordNumber;
FileHeaderBuf.OldestRecordNumber = OldestRecordNumber;
FileHeaderBuf.Flags = 0;
FileHeaderBuf.Retention = Request->LogFile->Retention;
Status = NtWriteFile(BackupHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
&FileHeaderBuf, // Buffer
FILEHEADERBUFSIZE, // Length
NULL, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformBackupRequest: Write of header to backup file %ws "
"failed %#x\n",
Request->Pkt.BackupPkt->BackupFileName->Buffer,
Status);
goto errorexit;
}
//
// Scan from the end of the file skipping over ELF_SKIP_DWORDs
// to figure out how far to copy. If we haven't wrapped, we just
// copy to the EndRecord offset.
//
if (Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP)
{
EndOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->ActualMaxFileSize - sizeof(DWORD);
while (*((PDWORD) EndOfCopy) == ELF_SKIP_DWORD)
{
EndOfCopy -= sizeof(DWORD);
}
EndOfCopy += sizeof(DWORD);
}
else
{
EndOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->EndRecord;
}
//
// Now set the start position to be the first record and
// calculate the number of bytes to copy
//
StartOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->BeginRecord;
BytesToCopy = (ULONG) (EndOfCopy - StartOfCopy);
EndRecord += BytesToCopy;
Status = NtWriteFile(BackupHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
StartOfCopy, // Buffer
BytesToCopy, // Length
NULL, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformBackupRequest: Block write to backup file %ws (1st "
"call) failed %#x\n",
Request->Pkt.BackupPkt->BackupFileName->Buffer,
Status);
goto errorexit;
}
//
// If the file's not wrapped, we're done except for the EOF
// record. If the file is wrapped we have to copy the 2nd
// piece
//
if (Request->LogFile->Flags & ELF_LOGFILE_HEADER_WRAP)
{
StartOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ FILEHEADERBUFSIZE;
EndOfCopy = (PBYTE) Request->LogFile->BaseAddress
+ Request->LogFile->EndRecord;
BytesToCopy = (ULONG) (EndOfCopy - StartOfCopy);
EndRecord += BytesToCopy;
Status = NtWriteFile(BackupHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
StartOfCopy, // Buffer
BytesToCopy, // Length
NULL, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformBackupRequest: Block write to backup file %ws "
"(2nd call) failed %#x\n",
Request->Pkt.BackupPkt->BackupFileName->Buffer,
Status);
goto errorexit;
}
}
//
// Write out the EOF record after updating the fields needed for
// recovery.
//
EOFRecord.BeginRecord = FILEHEADERBUFSIZE;
EOFRecord.EndRecord = EndRecord;
EOFRecord.CurrentRecordNumber = LastRecordNumber;
EOFRecord.OldestRecordNumber = OldestRecordNumber;
Status = NtWriteFile(BackupHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
&EOFRecord, // Buffer
ELFEOFRECORDSIZE, // Length
NULL, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformBackupRequest: Write of EOF record to backup file "
"%ws failed %#x\n",
Request->Pkt.BackupPkt->BackupFileName->Buffer,
Status);
goto errorexit;
}
//
// Update the header with valid information
//
FileHeaderBuf.EndOffset = EndRecord;
FileHeaderBuf.MaxSize = EndRecord + ELFEOFRECORDSIZE;
Offset = RtlConvertUlongToLargeInteger(0);
Status = NtWriteFile(BackupHandle, // Filehandle
NULL, // Event
NULL, // APC routine
NULL, // APC context
&IoStatusBlock, // IO_STATUS_BLOCK
&FileHeaderBuf, // Buffer
FILEHEADERBUFSIZE, // Length
&Offset, // Byteoffset
NULL); // Key
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"PerformBackupRequest: Rewrite of header to backup file "
"%ws failed %#x\n",
Request->Pkt.BackupPkt->BackupFileName->Buffer,
Status);
goto errorexit;
}
//
// Clear the LogFile flag archive bit, assuming the caller will
// clear (or has cleared) this log's archive file attribute.
// Note: No big deal if the caller didn't clear the archive
// attribute.
//
// The next write to this log tests the LogFile flag archive bit.
// If the bit is clear, the archive file attribute is set on the
// log file.
//
Request->LogFile->Flags &= ~ELF_LOGFILE_ARCHIVE_SET;
}
else
{
ELF_LOG1(ERROR,
"PerformBackupRequest: RpcImpersonateClient failed %#x\n",
Status);
}
errorexit:
if (ImpersonatingClient)
{
IStatus = I_RpcMapWin32Status(RpcRevertToSelf());
if (!NT_SUCCESS(IStatus))
{
ELF_LOG1(ERROR,
"PerformBackupRequest: RpcRevertToSelf failed %#x\n",
IStatus);
}
}
//
// Close the output file
//
if (BackupHandle != INVALID_HANDLE_VALUE)
{
NtClose(BackupHandle);
}
//
// Set status field in the request packet.
//
Request->Status = Status;
//
// Release the resource
//
RtlReleaseResource(&Request->Module->LogFile->Resource);
}
VOID
ElfPerformRequest(
PELF_REQUEST_RECORD Request
)
/*++
Routine Description:
This routine takes the request packet and performs the operation
on the event log.
Before it does that, it takes the Global serialization resource
for a READ to prevent other threads from doing WRITE operations on
the resources of the service.
After it has performed the requested operation, it writes any records
generated by the eventlog service that have been put on the queued event
list.
Arguments:
Pointer to the request packet.
Return Value:
NONE
--*/
{
BOOL Acquired = FALSE;
//
// Acquire the global resource for shared access. If the resource is
// not immediately available (i.e., don't wait) then some other thread
// has it out for exclusive access.
//
// If we time out, one of two threads owns the global resource:
//
// 1) Thread monitoring the registry
// We can wait for this thread to finish so that the
// operation can continue.
//
// 2) Control thread
// In this case, it may turn out that the service is
// stopping. We examine the current service state to
// see if it is still running. If so, we loop around
// and try to get the resource again.
//
while ((GetElState() == RUNNING) && (!Acquired))
{
Acquired = RtlAcquireResourceShared(&GlobalElfResource,
FALSE); // Don't wait
if (!Acquired)
{
ELF_LOG1(TRACE,
"ElfPerformRequest: Sleep %d milliseconds waiting "
"for global resource\n",
ELF_GLOBAL_RESOURCE_WAIT);
Sleep(ELF_GLOBAL_RESOURCE_WAIT);
}
}
//
// If the resource was not available and the status of the service
// changed to one of the "non-working" states, then we just return
// unsuccesful. Rpc should not allow this to happen.
//
if (!Acquired)
{
ELF_LOG0(TRACE,
"ElfPerformRequest: Global resource not acquired\n");
Request->Status = STATUS_UNSUCCESSFUL;
}
else
{
switch (Request->Command)
{
case ELF_COMMAND_READ:
//
// The read/write code paths are high risk for exceptions.
// Ensure exceptions do not go beyond this point. Otherwise,
// services.exe will be taken out. Note that the try-except
// blocks are in PerformReadRequest and PerformWriteRequest
// since the risky calls are between calls to acquire and
// release a resource -- if the block were out here, a thrown
// exception would prevent the releasing of the resource
// (Bug #175768)
//
PerformReadRequest(Request);
break;
case ELF_COMMAND_WRITE:
PerformWriteRequest (Request);
break;
case ELF_COMMAND_CLEAR:
PerformClearRequest(Request);
break;
case ELF_COMMAND_BACKUP:
PerformBackupRequest(Request);
break;
case ELF_COMMAND_WRITE_QUEUED:
break;
}
//
// Now run the queued event list dequeueing elements and
// writing them
//
if (!IsListEmpty(&QueuedEventListHead))
{
//
// There are things queued up to write, do it
//
WriteQueuedEvents();
}
//
// Release the global resource.
//
ReleaseGlobalResource();
}
}
/****
@func NTSTATUS | FindSizeofEventsSinceStart| This routine walks
through all the logfile structures and returns the size of
events that were reported since the start of the eventlog service
and that need to be proapagated through the cluster-wide replicated
logs. For all logfiles that are returned in the list, the shared
lock for their log file is held and must be released by the caller.
@parm OUT PULONG | pulSize | Pointer to a LONG that contains the size on return.
@parm OUT PULONG | pulNumLogFiles | Pointer to a LONG that number of log files
configured for eventlogging.
@parm OUT PPROPLOGFILEINFO | *ppPropLogFileInfo | A pointer to a PROPLOGFILEINFO with
all the information about events that need to be propagated is returned via this.
@rdesc Returns a result code. ERROR_SUCCESS on success.
@comm This is called by ElfrRegisterClusterSvc
@xref <f ElfrRegisterClusterSvc>
****/
NTSTATUS
FindSizeofEventsSinceStart(
OUT PULONG pulTotalEventSize,
OUT PULONG pulNumLogFiles,
OUT PPROPLOGFILEINFO *ppPropLogFileInfo
)
{
PLOGFILE pLogFile;
PVOID pStartPropPosition;
PVOID pEndPropPosition;
ULONG ulSize;
ULONG ulNumLogFiles;
PPROPLOGFILEINFO pPropLogFileInfo = NULL;
UINT i;
PVOID PhysicalEOF; // Physical end of file
PVOID PhysStart; // Physical start of file (after file hdr)
PVOID BeginRecord; // Points to first record
PVOID EndRecord; // Points to byte after last record
ELF_REQUEST_RECORD Request; // points to the elf request
NTSTATUS Status = STATUS_SUCCESS;
READ_PKT ReadPkt;
//
// Lock the linked list
//
RtlEnterCriticalSection(&LogFileCritSec);
//
// Initialize the number of files
//
ulNumLogFiles = 0; // Count of files
//
// Initialize the number of files/total event size
//
*pulNumLogFiles = 0; // Count of files with events to be propagated
*pulTotalEventSize = 0;
//
// Count the number of files
// Initialize to the first logfile in the list
//
pLogFile = CONTAINING_RECORD(LogFilesHead.Flink,
LOGFILE,
FileList);
//
// While there are more
//
while(pLogFile->FileList.Flink != LogFilesHead.Flink)
{
ulNumLogFiles++;
//
// Advance to the next log file
//
pLogFile = CONTAINING_RECORD(pLogFile->FileList.Flink,
LOGFILE,
FileList);
}
ELF_LOG1(CLUSTER,
"FindSizeOfEventsSinceStart: %d log files\n",
ulNumLogFiles);
if (!ulNumLogFiles)
{
goto FnExit;
}
//
// Allocate a structure for log file info
//
pPropLogFileInfo =
(PPROPLOGFILEINFO) ElfpAllocateBuffer(ulNumLogFiles * sizeof(PROPLOGFILEINFO));
if (!pPropLogFileInfo)
{
ELF_LOG0(ERROR,
"FindSizeOfEventsSinceStart: Unable to allocate memory "
"for pPropLogFileInfo\n");
Status = STATUS_NO_MEMORY;
goto FnExit;
}
//
// Gather information about the files
// Initialize to the first logfile in the list
//
pLogFile = CONTAINING_RECORD(LogFilesHead.Flink,
LOGFILE,
FileList);
i = 0;
//
// While there are more
//
//
// BUGBUG: Based on the generation of ulNumLogFiles above, these
// two checks are actually identical
//
while ((pLogFile->FileList.Flink != LogFilesHead.Flink)
&&
(i < ulNumLogFiles))
{
ELF_LOG1(CLUSTER,
"FindSizeOfEventsSinceStart: Processing file %ws\n",
pLogFile->LogFileName->Buffer);
//
// Get shared access to the log file. This will allow multiple
// readers to get to the file together.
//
RtlAcquireResourceShared(&pLogFile->Resource,
TRUE); // Wait until available
//
// Check if any records need to be propagated
//
if (pLogFile->CurrentRecordNumber == pLogFile->SessionStartRecordNumber)
{
ELF_LOG1(CLUSTER,
"FindSizeOfEventsSinceStart: No records to propagate from %ws log\n",
pLogFile->LogModuleName->Buffer);
goto process_nextlogfile;
}
//
// Records need to be propagated, so find the positions in the
// file where they are logged
//
PhysicalEOF = (LPBYTE) pLogFile->BaseAddress
+ pLogFile->ViewSize;
PhysStart = (LPBYTE)pLogFile->BaseAddress
+ FILEHEADERBUFSIZE;
BeginRecord = (LPBYTE) pLogFile->BaseAddress
+ pLogFile->BeginRecord; // Start at first record
EndRecord = (LPBYTE)pLogFile->BaseAddress
+ pLogFile->EndRecord; // Byte after end of last record
//
// Set up the request structure
//
Request.Pkt.ReadPkt = &ReadPkt;
Request.LogFile = pLogFile;
//
// Set up the read packet structure for the first event logged in this session
//
Request.Pkt.ReadPkt->LastSeekPos = 0;
Request.Pkt.ReadPkt->ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
Request.Pkt.ReadPkt->RecordNumber = pLogFile->SessionStartRecordNumber;
//
// Chittur Subbaraman (chitturs) - 3/22/99
//
// Enclose the SeekToStartingRecord within a try-except block to
// account for the eventlog getting corrupted under certain
// circumstances (such as the system crashing). You don't want to
// read such corrupt records.
//
try
{
//
// Find the size of events in this log file
//
Status = SeekToStartingRecord(&Request,
&pStartPropPosition,
BeginRecord,
EndRecord,
PhysicalEOF,
PhysStart);
}
except(EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"FindSizeOfEventsSinceStart: Caught exception %#x while "
"seeking first record in %ws log\n",
GetExceptionCode(),
pLogFile->LogModuleName->Buffer);
Status = STATUS_EVENTLOG_FILE_CORRUPT;
}
//
// Skip this log file if error
//
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"FindSizeOfEventsSinceStart: SeekToStartingRecord (1st call) for %ws "
"log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
//
// Resetting status so that we skip only this file.
//
Status = STATUS_SUCCESS;
goto process_nextlogfile;
}
//
// SS: if this is not a valid position - the file could have wrapped since
// Should be try and find the last valid record after the session start record
// number then ? Since this is unlikely to happen-its not worth the trouble
// however valid position for session start record never succeeds even though
// it is valid, so we skip it
//
//
// Set up the read packet structure to seek till the start of the
// last record
//
//
// CODEWORK: We already have the position of the last record (via EndRecord)
// so just using ((PBYTE) EndRecord - *((PULONG) EndRecord - 1))
// should give the offset of the last record.
//
Request.Pkt.ReadPkt->LastSeekPos = 0;
Request.Pkt.ReadPkt->ReadFlags = EVENTLOG_SEEK_READ | EVENTLOG_FORWARDS_READ;
Request.Pkt.ReadPkt->RecordNumber = pLogFile->CurrentRecordNumber - 1;
//
// Chittur Subbaraman (chitturs) - 3/22/99
//
// Enclose the SeekToStartingRecord within a try-except block to
// account for the eventlog getting corrupted under certain
// circumstances (such as the system crashing). You don't want to
// read such corrupt records.
//
try
{
Status = SeekToStartingRecord(&Request,
&pEndPropPosition,
BeginRecord,
EndRecord,
PhysicalEOF,
PhysStart);
}
except(EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"FindSizeOfEventsSinceStart: Caught exception %#x while "
"seeking last record in %ws log\n",
GetExceptionCode(),
pLogFile->LogModuleName->Buffer);
Status = STATUS_EVENTLOG_FILE_CORRUPT;
}
//
// Skip this log file if error
//
if (!NT_SUCCESS(Status))
{
ELF_LOG2(ERROR,
"FindSizeOfEventsSinceStart: SeekToStartingRecord (2nd call) for %ws "
"log failed %#x\n",
pLogFile->LogModuleName->Buffer,
Status);
//
// Resetting status so that we skip only this file.
//
Status = STATUS_SUCCESS;
goto process_nextlogfile;
}
//
// SS: if this is not a valid position - the file could have wrapped since
//
if (!ValidFilePos(pEndPropPosition,
BeginRecord,
EndRecord,
PhysicalEOF,
pLogFile->BaseAddress,
TRUE))
{
ELF_LOG1(ERROR,
"FindSizeOfEventsSinceStart: ValidFilePos for pEndPropPosition "
"in %ws log failed\n",
pLogFile->LogModuleName->Buffer);
goto process_nextlogfile;
}
//
// The end prop position
//
pEndPropPosition = (PBYTE) pEndPropPosition
+ ((PEVENTLOGRECORD)pEndPropPosition)->Length;
ELF_LOG3(CLUSTER,
"FindSizeOfEventsSinceStart: Log %ws, pStartPosition %p, pEndPosition %p\n",
pLogFile->LogModuleName->Buffer,
pStartPropPosition,
pEndPropPosition);
//
// If no records to propagate - skip the file
//
if (pStartPropPosition == pEndPropPosition)
{
ELF_LOG1(CLUSTER,
"FindSizeOfEventsSinceStart: Start and end positions in %ws log "
"are equal -- no events to propagate\n",
pLogFile->LogModuleName->Buffer);
goto process_nextlogfile;
}
if (pEndPropPosition > pStartPropPosition)
{
ulSize = (ULONG) ((PBYTE) pEndPropPosition - (PBYTE) pStartPropPosition);
}
else
{
//
// BUGBUG: This ignores any ELF_SKIP_DWORDs at the end of the file
//
ulSize = (ULONG) ((PBYTE) PhysicalEOF - (PBYTE) pStartPropPosition)
+
(ULONG) ((PBYTE)pEndPropPosition - (PBYTE)PhysStart);
}
ELF_LOG2(CLUSTER,
"FindSizeOfEventsSinceStart: Need to propagate %d bytes from %ws log\n",
ulSize,
pLogFile->LogModuleName->Buffer);
pPropLogFileInfo[i].pLogFile = pLogFile;
pPropLogFileInfo[i].pStartPosition = pStartPropPosition;
pPropLogFileInfo[i].pEndPosition = pEndPropPosition;
pPropLogFileInfo[i].ulTotalEventSize = ulSize;
pPropLogFileInfo[i].ulNumRecords = pLogFile->CurrentRecordNumber
- pLogFile->SessionStartRecordNumber;
i++;
(*pulNumLogFiles)++;
*pulTotalEventSize += ulSize;
//
// Advance to the next log file
//
pLogFile = CONTAINING_RECORD(pLogFile->FileList.Flink,
LOGFILE,
FileList);
//
// NB: We were successful with this log file and we therefore hold on to
// the lock -- the caller will/must release it.
//
continue;
process_nextlogfile:
//
// We were not successful with the log file -- release the lock
//
RtlReleaseResource(&pLogFile->Resource);
//
// Advance to the next log file
//
pLogFile = CONTAINING_RECORD(pLogFile->FileList.Flink,
LOGFILE,
FileList);
}
//
// Free the memory if unsuccessful
//
if (!(*pulNumLogFiles))
{
ElfpFreeBuffer(pPropLogFileInfo);
pPropLogFileInfo = NULL;
}
FnExit:
*ppPropLogFileInfo = pPropLogFileInfo;
ELF_LOG3(CLUSTER,
"FindSizeOfEventsSinceStart: ulTotalEventSize = %d, ulNumLogFiles = %d, "
"pPropLogFileInfo = %p\n",
*pulTotalEventSize,
*pulNumLogFiles,
*ppPropLogFileInfo);
//
// Unlock the linked list
//
RtlLeaveCriticalSection(&LogFileCritSec);
return Status;
}
/****
@func NTSTATUS | GetEventsToProp| Given a propagate log file
info structure, this events prepares a block of eventlog
records to propagate. The shared lock to the logfile must
be held thru when the PROPLOGINFO structure is prepared
to when this routine is called.
@parm OUT PEVENTLOGRECORD | pEventLogRecords | Pointer to a EVENTLOGRECORD
structure where the events to be propagated are returned.
@parm IN PPROPLOGFILEINFO | pPropLogFileInfo | Pointer to a PROPLOGFILEINFO
structure that contains the information to retrieve events from the
corresponding eventlog file.
@rdesc Returns a result code. ERROR_SUCCESS on success.
@xref
****/
NTSTATUS
GetEventsToProp(
IN PEVENTLOGRECORD pEventLogRecords,
IN PPROPLOGFILEINFO pPropLogFileInfo
)
{
PVOID BufferPosition;
PVOID XferPosition;
PVOID PhysicalEOF;
PVOID PhysicalStart;
ULONG ulBytesToMove;
NTSTATUS Status = STATUS_SUCCESS;
ELF_LOG1(CLUSTER,
"GetEventsToProp: Getting events for %ws log\n",
pPropLogFileInfo->pLogFile->LogModuleName->Buffer);
BufferPosition = pEventLogRecords;
ulBytesToMove = pPropLogFileInfo->ulTotalEventSize;
//
// If the start and end positions are the same there are no bytes to copy
//
if (pPropLogFileInfo->pStartPosition == pPropLogFileInfo->pEndPosition)
{
ASSERT(FALSE);
//
// Shouldn't come here as FindSizeofEventsSinceStart checks
// for this explicitly
//
return STATUS_SUCCESS;
}
//
// Chittur Subbaraman (chitturs) - 3/15/99
//
// Enclose the memcpy within a try-except block to account for
// the eventlog getting corrupted under certain circumstances (such
// as the system crashing). You don't want to read such corrupt
// records.
//
try
{
XferPosition = pPropLogFileInfo->pStartPosition;
ulBytesToMove = pPropLogFileInfo->ulTotalEventSize;
if (pPropLogFileInfo->pStartPosition > pPropLogFileInfo->pEndPosition)
{
//
// The log is wrapped -- copy the bytes from the start position
// to the end of the file
//
PhysicalEOF = (PBYTE) pPropLogFileInfo->pLogFile->BaseAddress
+ pPropLogFileInfo->pLogFile->ViewSize;
PhysicalStart = (PBYTE) pPropLogFileInfo->pLogFile->BaseAddress
+ FILEHEADERBUFSIZE;
//
// BUGBUG: This copies any ELF_SKIP_DWORDs that are at the
// end of the file
//
ulBytesToMove = (ULONG) ((PBYTE) PhysicalEOF
- (PBYTE) pPropLogFileInfo->pStartPosition);
RtlCopyMemory(BufferPosition, XferPosition, ulBytesToMove);
//
// Set it up for the second half
//
BufferPosition = (PBYTE) BufferPosition + ulBytesToMove;
ulBytesToMove = pPropLogFileInfo->ulTotalEventSize - ulBytesToMove;
XferPosition = PhysicalStart;
}
RtlCopyMemory(BufferPosition, XferPosition, ulBytesToMove);
}
except (EXCEPTION_EXECUTE_HANDLER)
{
ELF_LOG2(ERROR,
"GetEventsToProp: Caught exception %#x copying records from %ws log\n",
GetExceptionCode(),
pPropLogFileInfo->pLogFile->LogModuleName->Buffer);
Status = STATUS_EVENTLOG_FILE_CORRUPT;
}
return Status;
}
//SS:end of changes made to enable cluster wide event logging