You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4764 lines
161 KiB
4764 lines
161 KiB
/*++
|
|
|
|
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 <msaudite.h>
|
|
#include <stdio.h>
|
|
#include "elfmsg.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;
|
|
BOOL bExpanded = FALSE;
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
bExpanded = TRUE;
|
|
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;
|
|
|
|
if(bExpanded)
|
|
pLogFile->bFailedExpansion = TRUE;
|
|
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;
|
|
|
|
// catch the 64 bit exception before it occurs!
|
|
|
|
#ifdef _WIN64
|
|
unsigned __int64 ullPosition = (unsigned __int64)Position;
|
|
if(ullPosition & 3)
|
|
{
|
|
ELF_LOG1(ERROR,
|
|
"ValidFilePos: Unaligned pointer %#x was passed in\n",
|
|
Position);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
PVOID LastReadPosition;
|
|
|
|
//
|
|
// 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
|
|
|
|
try
|
|
{
|
|
CopyRecordToBuffer((PBYTE) ReadPosition,
|
|
(PBYTE *) &BufferPosition,
|
|
RecordSize,
|
|
(PBYTE) PhysicalEOF,
|
|
(PBYTE) PhysStart);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
ELF_LOG0(ERROR,
|
|
"ReadFromLog: CopyRecordToBuffer failed");
|
|
ElfpFreeBuffer(TempBuf);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
LastReadPosition = ReadPosition;
|
|
|
|
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
|
|
|
|
try
|
|
{
|
|
CopyRecordToBuffer((PBYTE) ReadPosition,
|
|
(PBYTE *) &BufferPosition,
|
|
RecordSize,
|
|
(PBYTE) PhysicalEOF,
|
|
(PBYTE) PhysStart);
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
ELF_LOG0(ERROR,
|
|
"ReadFromLog: CopyRecordToBuffer failed");
|
|
ElfpFreeBuffer(TempBuf);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// 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
|
|
//
|
|
|
|
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:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// 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
|
|
Request->Pkt.ReadPkt->LastSeekPos = Request->Pkt.ReadPkt->ContextHandle->SeekBytePos;
|
|
Request->Pkt.ReadPkt->LastSeekRecord = Request->Pkt.ReadPkt->ContextHandle->SeekRecordPos;
|
|
|
|
if(Request->Module->LogFile->bHosedByClear)
|
|
{
|
|
Status = ElfOpenLogFile(Request->Module->LogFile, ElfNormalLog);
|
|
if(NT_SUCCESS(Status))
|
|
Request->Module->LogFile->bHosedByClear = FALSE;
|
|
}
|
|
if(Request->Module->LogFile->BaseAddress == NULL ||
|
|
Request->Module->LogFile->bHosedByClear)
|
|
{
|
|
Request->Status = STATUS_INVALID_HANDLE;
|
|
RtlReleaseResource ( &Request->Module->LogFile->Resource );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Update current seek positions
|
|
//
|
|
|
|
Request->Pkt.ReadPkt->ContextHandle->SeekRecordPos = Request->Pkt.ReadPkt->LastSeekRecord;
|
|
Request->Pkt.ReadPkt->ContextHandle->SeekBytePos = Request->Pkt.ReadPkt->LastSeekPos;
|
|
|
|
|
|
//
|
|
// 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)
|
|
|
|
|
|
NTSTATUS
|
|
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);
|
|
|
|
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);
|
|
return 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);
|
|
return 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);
|
|
return 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());
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
void WriteLogFullMessage(PELF_REQUEST_RECORD Request,
|
|
PLOGFILE pLogFile, BOOL bSecurity, ULONG OverwrittenEOF)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when an attempt to write to a full log is made and there is
|
|
no more room.
|
|
|
|
Arguments:
|
|
|
|
Pointer to the request packet.
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG WritePos; // Position to write record
|
|
|
|
//
|
|
// All records within retention period
|
|
//
|
|
ELF_LOG1(ERROR,
|
|
"PerformWriteRequest: %ws log is full\n",
|
|
pLogFile->LogModuleName->Buffer);
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// 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, // Overwrite if necc.
|
|
FALSE); // for security file
|
|
|
|
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);
|
|
}
|
|
|
|
}
|
|
|
|
NTSTATUS FindStartOfNextRecordInFile(PLOGFILE pLogFile, ULONG NextRecord,
|
|
ULONG * pSpaceAvail)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG SearchStartPos;
|
|
SearchStartPos = pLogFile->BeginRecord;
|
|
|
|
// if the record to search next is at eof, then we are done.
|
|
|
|
if(SearchStartPos == pLogFile->EndRecord)
|
|
{
|
|
pLogFile->BeginRecord = pLogFile->EndRecord;
|
|
return 0;
|
|
}
|
|
for(;;)
|
|
{
|
|
PVOID Position;
|
|
|
|
for ( ;
|
|
NextRecord != SearchStartPos;
|
|
*pSpaceAvail += sizeof(ULONG), NextRecord += sizeof(ULONG))
|
|
{
|
|
if (NextRecord >= pLogFile->ActualMaxFileSize)
|
|
{
|
|
NextRecord = pLogFile->BeginRecord = FILEHEADERBUFSIZE;
|
|
}
|
|
|
|
if ( NextRecord == pLogFile->EndRecord)
|
|
{
|
|
pLogFile->BeginRecord = pLogFile->EndRecord;
|
|
return 0;
|
|
}
|
|
|
|
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.
|
|
//
|
|
*pSpaceAvail -= 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);
|
|
|
|
*pSpaceAvail += 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);
|
|
return Status;
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
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,
|
|
SE_CATEGID_SYSTEM, // EventCategory
|
|
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;
|
|
ULONG TotalSpaceAvailable; // space available plus amount file can be extened
|
|
int iPercentUsedBefore; // percent of space used before record is added
|
|
int iPercentUsedAfter; // percent of space used after record is added
|
|
BOOL fRetryWriteRequest = FALSE;
|
|
BOOL bRecordAfterOverwrttenEOF;
|
|
|
|
pLogFile = Request->LogFile; // Set local variable
|
|
|
|
// Set some basic BOOLs
|
|
|
|
if(!_wcsicmp(pLogFile->LogModuleName->Buffer, ELF_SECURITY_MODULE_NAME))
|
|
bSecurity = TRUE;
|
|
else
|
|
bSecurity = FALSE;
|
|
|
|
//
|
|
// Get exclusive access to the log file. This will ensure no one
|
|
// else is accessing the file.
|
|
//
|
|
RtlAcquireResourceExclusive(&pLogFile->Resource,
|
|
TRUE); // Wait until available
|
|
|
|
//This condition occurs under stress conditions
|
|
//and causes an AV further in this function
|
|
|
|
if(pLogFile->bHosedByClear)
|
|
{
|
|
Status = ElfOpenLogFile(Request->LogFile, ElfNormalLog);
|
|
if(NT_SUCCESS(Status))
|
|
Request->LogFile->bHosedByClear = FALSE;
|
|
}
|
|
|
|
if(pLogFile->BaseAddress == NULL || pLogFile->bHosedByClear)
|
|
{
|
|
Request->Status = STATUS_INVALID_HANDLE;
|
|
RtlReleaseResource ( &pLogFile->Resource );
|
|
return;
|
|
}
|
|
|
|
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;
|
|
|
|
EventRecord = Request->Pkt.WritePkt->Buffer;
|
|
if(bSecurity && EventRecord->EventID != SE_AUDITID_UNABLE_TO_LOG_EVENTS)
|
|
{
|
|
SpaceNeeded += EstimateEventSize(
|
|
44, // assume two 10 wchar error codes
|
|
0,
|
|
L"Security");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// If this is a security log, and there is no overwrite allowed, determine
|
|
// if we have just exceeded the warning level
|
|
|
|
if(bSecurity && giWarningLevel > 0 && giWarningLevel < 100 &&
|
|
pLogFile->Retention != OVERWRITE_AS_NEEDED)
|
|
{
|
|
TotalSpaceAvailable = SpaceAvail +
|
|
(pLogFile->ConfigMaxFileSize -pLogFile->ActualMaxFileSize);
|
|
|
|
iPercentUsedBefore = 100 * (pLogFile->ConfigMaxFileSize - TotalSpaceAvailable) /
|
|
pLogFile->ConfigMaxFileSize;
|
|
iPercentUsedAfter = 100 * (pLogFile->ConfigMaxFileSize -
|
|
(TotalSpaceAvailable - SpaceNeeded)) /
|
|
pLogFile->ConfigMaxFileSize;
|
|
|
|
if(iPercentUsedBefore < giWarningLevel && iPercentUsedAfter >= giWarningLevel)
|
|
{
|
|
WCHAR wLevel[20];
|
|
LPWSTR pwStr[1];
|
|
pwStr[0] = wLevel;
|
|
StringCchPrintfW(wLevel, 20, L"%d", giWarningLevel);
|
|
ElfpCreateElfEvent(SE_AUDITID_SECURITY_LOG_EXCEEDS_WARNING_LEVEL,
|
|
EVENTLOG_AUDIT_SUCCESS,
|
|
SE_CATEGID_SYSTEM, // EventCategory
|
|
1, // NumberOfStrings
|
|
pwStr, // Strings
|
|
NULL, // Data
|
|
0, // Datalength
|
|
0, // flags
|
|
TRUE); // for security file
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
bRecordAfterOverwrttenEOF = TRUE;
|
|
}
|
|
else
|
|
bRecordAfterOverwrttenEOF = FALSE;
|
|
|
|
EventRecord = (PEVENTLOGRECORD) ((PBYTE) pLogFile->BaseAddress
|
|
+ pLogFile->BeginRecord);
|
|
|
|
while (SpaceNeeded > SpaceAvail &&
|
|
(pLogFile->BeginRecord != pLogFile->EndRecord ||
|
|
bRecordAfterOverwrttenEOF) )
|
|
{
|
|
|
|
bRecordAfterOverwrttenEOF = FALSE;
|
|
|
|
//
|
|
// 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;
|
|
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.
|
|
//
|
|
|
|
Status = FindStartOfNextRecordInFile(pLogFile, NextRecord, &SpaceAvail);
|
|
|
|
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, pLogFile);
|
|
|
|
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
|
|
{
|
|
break; // Get out of while loop
|
|
}
|
|
}
|
|
|
|
// if we still dont have enough space, pop up message box, write log full event etc
|
|
|
|
if(NT_SUCCESS(Status) && (SpaceNeeded > SpaceAvail))
|
|
{
|
|
// 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))
|
|
{
|
|
Status = AutoBackupLogFile(Request, OverwrittenEOF);
|
|
fRetryWriteRequest = TRUE;
|
|
if(NT_SUCCESS(Status))
|
|
goto RetryWriteRequest;
|
|
}
|
|
|
|
WriteLogFullMessage(Request, pLogFile, bSecurity, OverwrittenEOF);
|
|
Status = STATUS_LOG_FILE_FULL;
|
|
}
|
|
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;
|
|
pEventLogRecord->RecordNumber = 1;
|
|
pLogFile->CurrentRecordNumber = 2;
|
|
}
|
|
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
|
|
//
|
|
Status = WriteToLog(pLogFile,
|
|
Request->Pkt.WritePkt->Buffer,
|
|
RecordSize,
|
|
&(pLogFile->EndRecord),
|
|
pLogFile->ActualMaxFileSize,
|
|
FILEHEADERBUFSIZE);
|
|
|
|
if(NT_SUCCESS(Status)) // event write succeeded
|
|
{
|
|
//
|
|
// write the EOF record
|
|
// 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
|
|
//
|
|
Status = 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
|
|
|
|
try
|
|
{
|
|
//
|
|
// 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
|
|
|
|
Status = ElfpCloseLogFile(Request->LogFile, ELF_LOG_CLOSE_FORCE, FALSE);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DbgPrint("[ELF] %lx: PerformClearRequest: Failed calling ElfpCloseLogFile, Status = %#x\n",
|
|
GetCurrentThreadId(),Status);
|
|
RtlReleaseResource(&Request->Module->LogFile->Resource);
|
|
}
|
|
|
|
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 && FileRenamed == TRUE)
|
|
{
|
|
//
|
|
// Opening the new log file failed, reopen the old log and
|
|
// return this error from the Api
|
|
//
|
|
PFILE_RENAME_INFORMATION OldName;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
//
|
|
// 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);
|
|
if(!NT_SUCCESS(IStatus))
|
|
{
|
|
Request->LogFile->bHosedByClear = TRUE;
|
|
Request->LogFile->LastStatus = 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;
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ELF_LOG1(ERROR,
|
|
"PerformClearRequest: Caught exception %#x\n",
|
|
GetExceptionCode());
|
|
|
|
Request->Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// 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
|
|
|
|
try
|
|
{
|
|
//
|
|
// 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);
|
|
}
|
|
}
|
|
except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ELF_LOG1(ERROR,
|
|
"PerformBackupRequest: Caught exception %#x\n",
|
|
GetExceptionCode());
|
|
|
|
Request->Status = STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
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
|