3820 lines
115 KiB
C
3820 lines
115 KiB
C
/*++
|
|
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).
|
|
|
|
--*/
|
|
/*
|
|
@doc EXTERNAL INTERFACES EVTLOG
|
|
*/
|
|
|
|
|
|
// INCLUDES
|
|
|
|
|
|
#include <eventp.h>
|
|
#include <alertmsg.h> // ALERT_ELF manifests
|
|
|
|
#include "elfmsg.h"
|
|
|
|
#define OVERWRITE_AS_NEEDED 0x00000000
|
|
#define NEVER_OVERWRITE 0xffffffff
|
|
|
|
#if DBG
|
|
#define ELF_ERR_OUT(txt,code,pLogFile) (ElfErrorOut(txt,code,pLogFile))
|
|
#else
|
|
#define ELF_ERR_OUT(txt,code,pLogFile)
|
|
#endif
|
|
|
|
|
|
// Prototypes
|
|
|
|
BOOL
|
|
IsPositionWithinRange(
|
|
PVOID Position,
|
|
PVOID BeginningRecord,
|
|
PVOID EndingRecord);
|
|
|
|
#if DBG
|
|
VOID
|
|
ElfErrorOut(
|
|
LPSTR ErrorText,
|
|
DWORD StatusCode,
|
|
PLOGFILE pLogFile);
|
|
#endif // DBG
|
|
|
|
|
|
|
|
VOID
|
|
ElfExtendFile(
|
|
PLOGFILE pLogFile,
|
|
ULONG SpaceNeeded,
|
|
PULONG SpaceAvail
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes an open log file and extends the file and underlying
|
|
section and view if possible. If it can't be grown, it caps the file
|
|
at this size by setting the ConfigMaxFileSize to the Actual. It also
|
|
updates the SpaceAvail parm which is used in PerformWriteRequest (the
|
|
caller).
|
|
|
|
Arguments:
|
|
|
|
pLogFile - pointer to a LOGFILE structure for the open logfile
|
|
ExtendAmount - How much bigger to make the file/section/view
|
|
SpaceAvail - Update this with how much space was added to the section
|
|
|
|
Return Value:
|
|
|
|
None - If we can't extend the file, we just cap it at this size for the
|
|
duration of this boot. We'll try again the next time the eventlog
|
|
is closed and reopened.
|
|
|
|
Note:
|
|
|
|
ExtendAmount should always be granular to 64K.
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER NewSize;
|
|
NTSTATUS Status;
|
|
PVOID BaseAddress;
|
|
SIZE_T Size;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
|
|
|
|
// Calculate how much to grow the file then extend the section by
|
|
// that amount. Do this in 64K chunks.
|
|
|
|
|
|
SpaceNeeded = ((SpaceNeeded - *SpaceAvail) & 0xFFFF0000) + 0x10000;
|
|
|
|
if (SpaceNeeded > (pLogFile->ConfigMaxFileSize - pLogFile->ActualMaxFileSize)) {
|
|
|
|
|
|
// We can't grow it by the full amount we need. Grow
|
|
// it to the max size and let file wrap take over.
|
|
// If there isn't any room to grow, then return;
|
|
|
|
|
|
SpaceNeeded = pLogFile->ConfigMaxFileSize -
|
|
pLogFile->ActualMaxFileSize;
|
|
|
|
if (SpaceNeeded == 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
NewSize = RtlConvertUlongToLargeInteger(pLogFile->ActualMaxFileSize
|
|
+ SpaceNeeded);
|
|
|
|
|
|
// Update the file size information, extend the section, and map the
|
|
// new section
|
|
|
|
|
|
Status = NtSetInformationFile(pLogFile->FileHandle,
|
|
&IoStatusBlock,
|
|
&NewSize,
|
|
sizeof(NewSize),
|
|
FileEndOfFileInformation);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrintNC(("[EVENTLOG]ElfExtendFile: NtSetInformationFile "
|
|
"failed 0x%lx\n", Status));
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Status = NtExtendSection(pLogFile->SectionHandle, &NewSize);
|
|
|
|
if (!NT_SUCCESS(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_ERR_OUT("ElfExtendFile:NtMapViewOfSection Failed", Status, pLogFile);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
|
|
// Unmap the old section.
|
|
|
|
|
|
Status = NtUnmapViewOfSection(NtCurrentProcess(),
|
|
pLogFile->BaseAddress);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ELF_ERR_OUT("ElfExtendFile:NtUnmapViewOfSection Failed",
|
|
Status,
|
|
pLogFile);
|
|
}
|
|
|
|
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)((LPBYTE)pLogFile->ViewSize -
|
|
(LPBYTE)pLogFile->BeginRecord);
|
|
|
|
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_ERR_OUT("ElfExtendFile:NtFlushVirtualMemory Failed",
|
|
Status,
|
|
pLogFile);
|
|
}
|
|
|
|
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.
|
|
|
|
// Generate an Alert here - BUGBUG
|
|
|
|
ELF_ERR_OUT("ElfExtendFile:Couldn't extend the File",
|
|
Status,
|
|
pLogFile);
|
|
|
|
pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
CopyUnicodeToAnsiRecord(
|
|
OUT PVOID Dest OPTIONAL,
|
|
IN PVOID Src,
|
|
OUT PVOID* NewBufPos OPTIONAL,
|
|
OUT PULONG RecordSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine reads from the event log specified in the request packet.
|
|
|
|
This routine uses memory mapped I/O to access the log file. This makes
|
|
it much easier to move around the file.
|
|
|
|
Arguments:
|
|
|
|
Dest - Points to destination buffer. If NULL, calculate and return
|
|
the record length without copying the record.
|
|
|
|
Src - Points to the UNICODE record.
|
|
|
|
NewBufPos - Gets offset in Dest buffer after record just transferred.
|
|
If Dest is NULL, this is ignored.
|
|
|
|
RecordSize - Gets size of this (ANSI) record.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if no errors occur. Specific NTSTATUS error otherwise.
|
|
|
|
Note:
|
|
|
|
--*/
|
|
{
|
|
ANSI_STRING StringA;
|
|
UNICODE_STRING StringU;
|
|
PEVENTLOGRECORD SrcRecord, DestRecord;
|
|
PWSTR pStringU;
|
|
PVOID TempPtr;
|
|
ULONG PadSize, i;
|
|
ULONG zero = 0;
|
|
WCHAR* SrcStrings, * DestStrings;
|
|
ULONG RecordLength, * pLength;
|
|
ULONG ulTempLength;
|
|
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
DestRecord = (PEVENTLOGRECORD)Dest;
|
|
SrcRecord = (PEVENTLOGRECORD)Src;
|
|
|
|
if (DestRecord != NULL) {
|
|
DestRecord->TimeGenerated = SrcRecord->TimeGenerated;
|
|
DestRecord->Reserved = SrcRecord->Reserved;
|
|
DestRecord->RecordNumber = SrcRecord->RecordNumber;
|
|
DestRecord->TimeWritten = SrcRecord->TimeWritten;
|
|
DestRecord->EventID = SrcRecord->EventID;
|
|
DestRecord->EventType = SrcRecord->EventType;
|
|
DestRecord->EventCategory = SrcRecord->EventCategory;
|
|
DestRecord->NumStrings = SrcRecord->NumStrings;
|
|
DestRecord->UserSidLength = SrcRecord->UserSidLength;
|
|
DestRecord->DataLength = SrcRecord->DataLength;
|
|
}
|
|
|
|
|
|
// Convert and copy over modulename
|
|
|
|
pStringU = (PWSTR)((ULONG_PTR)SrcRecord + sizeof(EVENTLOGRECORD));
|
|
|
|
RtlInitUnicodeString(&StringU, pStringU);
|
|
|
|
if (DestRecord != NULL) {
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&StringA,
|
|
&StringU,
|
|
TRUE
|
|
);
|
|
|
|
ulTempLength = StringA.MaximumLength;
|
|
} else {
|
|
ulTempLength = RtlUnicodeStringToAnsiSize(&StringU);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
TempPtr = (PVOID)((ULONG_PTR)DestRecord + sizeof(EVENTLOGRECORD));
|
|
|
|
if (DestRecord != NULL) {
|
|
RtlMoveMemory(TempPtr, StringA.Buffer, ulTempLength);
|
|
RtlFreeAnsiString(&StringA);
|
|
}
|
|
|
|
TempPtr = (PVOID)((ULONG_PTR)TempPtr + ulTempLength);
|
|
|
|
|
|
// Convert and copy over computername
|
|
|
|
// TempPtr points to location in the destination for the computername
|
|
|
|
|
|
pStringU = (PWSTR)((ULONG_PTR)pStringU + StringU.MaximumLength);
|
|
|
|
RtlInitUnicodeString(&StringU, pStringU);
|
|
|
|
if (DestRecord != NULL) {
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&StringA,
|
|
&StringU,
|
|
TRUE
|
|
);
|
|
|
|
ulTempLength = StringA.MaximumLength;
|
|
} else {
|
|
ulTempLength = RtlUnicodeStringToAnsiSize(&StringU);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
if (DestRecord != NULL) {
|
|
RtlMoveMemory(TempPtr, StringA.Buffer, ulTempLength);
|
|
RtlFreeAnsiString(&StringA);
|
|
}
|
|
|
|
TempPtr = (PVOID)((ULONG_PTR)TempPtr + ulTempLength);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
// TempPtr points to location after computername - i.e. UserSid.
|
|
// Before we write out the UserSid, we ensure that we pad the
|
|
// bytes so that the UserSid starts on a DWORD boundary.
|
|
|
|
PadSize = sizeof(ULONG) - (ULONG)(((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord) % sizeof(ULONG));
|
|
|
|
if (DestRecord != NULL) {
|
|
RtlMoveMemory(TempPtr, &zero, PadSize);
|
|
|
|
TempPtr = (PVOID)((ULONG_PTR)TempPtr + PadSize);
|
|
|
|
|
|
// Copy over the UserSid.
|
|
|
|
|
|
RtlMoveMemory(
|
|
TempPtr,
|
|
(PVOID)((ULONG_PTR)SrcRecord + SrcRecord->UserSidOffset),
|
|
SrcRecord->UserSidLength
|
|
);
|
|
|
|
DestRecord->UserSidOffset = (ULONG)((ULONG_PTR)TempPtr
|
|
- (ULONG_PTR)DestRecord);
|
|
} else {
|
|
TempPtr = (PVOID)((ULONG_PTR)TempPtr + PadSize);
|
|
}
|
|
|
|
|
|
// Copy over the Strings
|
|
|
|
TempPtr = (PVOID)((ULONG_PTR)TempPtr + SrcRecord->UserSidLength);
|
|
SrcStrings = (WCHAR*)((ULONG_PTR)SrcRecord + (ULONG)SrcRecord->StringOffset);
|
|
DestStrings = (WCHAR*)TempPtr;
|
|
|
|
for (i = 0; i < SrcRecord->NumStrings; i++) {
|
|
|
|
RtlInitUnicodeString(&StringU, SrcStrings);
|
|
|
|
if (DestRecord != NULL) {
|
|
Status = RtlUnicodeStringToAnsiString(
|
|
&StringA,
|
|
&StringU,
|
|
TRUE
|
|
);
|
|
|
|
ulTempLength = StringA.MaximumLength;
|
|
} else {
|
|
ulTempLength = RtlUnicodeStringToAnsiSize(&StringU);
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
|
|
// Bail out
|
|
|
|
return (Status);
|
|
}
|
|
|
|
if (DestRecord != NULL) {
|
|
RtlMoveMemory(
|
|
DestStrings,
|
|
StringA.Buffer,
|
|
ulTempLength
|
|
);
|
|
|
|
RtlFreeAnsiString(&StringA);
|
|
}
|
|
|
|
DestStrings = (WCHAR*)((ULONG_PTR)DestStrings
|
|
+ (ULONG)ulTempLength);
|
|
|
|
SrcStrings = (WCHAR*)((ULONG_PTR)SrcStrings
|
|
+ (ULONG)StringU.MaximumLength);
|
|
}
|
|
|
|
|
|
// DestStrings points to the point after the last string copied.
|
|
|
|
if (DestRecord != NULL) {
|
|
DestRecord->StringOffset = (ULONG)((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord);
|
|
|
|
TempPtr = (PVOID)DestStrings;
|
|
|
|
|
|
// Copy over the binary Data
|
|
|
|
DestRecord->DataOffset = (ULONG)((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord);
|
|
|
|
RtlMoveMemory(
|
|
TempPtr,
|
|
(PVOID)((ULONG_PTR)SrcRecord + SrcRecord->DataOffset),
|
|
SrcRecord->DataLength
|
|
);
|
|
} else {
|
|
TempPtr = (PVOID)DestStrings;
|
|
}
|
|
|
|
|
|
// Now do the pad bytes.
|
|
|
|
TempPtr = (PVOID)((ULONG_PTR)TempPtr + SrcRecord->DataLength);
|
|
PadSize = sizeof(ULONG) - (ULONG)(((ULONG_PTR)TempPtr - (ULONG_PTR)DestRecord) % sizeof(ULONG));
|
|
RecordLength = (ULONG)((ULONG_PTR)TempPtr + PadSize + sizeof(ULONG) - (ULONG_PTR)DestRecord);
|
|
|
|
if (DestRecord != NULL) {
|
|
RtlMoveMemory(TempPtr, &zero, PadSize);
|
|
pLength = (PULONG)((ULONG_PTR)TempPtr + PadSize);
|
|
*pLength = RecordLength;
|
|
DestRecord->Length = RecordLength;
|
|
ASSERT(((ULONG_PTR)DestRecord + RecordLength) == ((ULONG_PTR)pLength + sizeof(ULONG)));
|
|
|
|
*NewBufPos = (PVOID)((ULONG_PTR)DestRecord + RecordLength);
|
|
}
|
|
|
|
*RecordSize = RecordLength;
|
|
}
|
|
|
|
return (Status);
|
|
|
|
} // CopyUnicodeToAnsiRecord
|
|
|
|
BOOL
|
|
ValidFilePos(
|
|
PVOID Position,
|
|
PVOID BeginningRecord,
|
|
PVOID EndingRecord,
|
|
PVOID PhysicalEOF,
|
|
PVOID BaseAddress,
|
|
BOOL fCheckBeginEndRange
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether we are pointing to a valid beginning
|
|
of an event record in the event log. It does this by validating
|
|
the signature then comparing the length at the beginning of the record to
|
|
the length at the end, both of which have to be at least the size of the
|
|
fixed length portion of an eventlog record.
|
|
|
|
Arguments:
|
|
|
|
Position - Pointer to be verified.
|
|
BeginningRecord - Pointer to the beginning record in the file.
|
|
EndingRecord - Pointer to the byte after the ending record in the file.
|
|
PhysicalEOF - Pointer the physical end of the log.
|
|
BaseAddress - Pointer to the physical beginning of the log.
|
|
|
|
Return Value:
|
|
|
|
TRUE if this position is valid.
|
|
|
|
Note:
|
|
|
|
There is a probability 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.
|
|
|
|
|
|
--*/
|
|
{
|
|
PULONG pEndRecordLength;
|
|
BOOL fValid = TRUE;
|
|
PEVENTLOGRECORD pEventRecord;
|
|
|
|
|
|
try {
|
|
|
|
pEventRecord = (PEVENTLOGRECORD)Position;
|
|
|
|
|
|
// Verify that the pointer is within the range of BEGINNING->END
|
|
|
|
|
|
if (fCheckBeginEndRange) {
|
|
|
|
fValid = IsPositionWithinRange(Position,
|
|
BeginningRecord,
|
|
EndingRecord);
|
|
}
|
|
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (pEventRecord->Length == *pEndRecordLength &&
|
|
pEventRecord->Length == ELFEOFRECORDSIZE) {
|
|
|
|
ULONG Size;
|
|
|
|
Size = min(ELFEOFUNIQUEPART,
|
|
(ULONG)((PBYTE)PhysicalEOF - (PBYTE)pEventRecord));
|
|
|
|
if (RtlCompareMemory(
|
|
pEventRecord,
|
|
&EOFRecord,
|
|
Size) == Size) {
|
|
|
|
Size -= 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) {
|
|
|
|
fValid = (RtlCompareMemory(
|
|
(PBYTE)BaseAddress + FILEHEADERBUFSIZE,
|
|
&EOFRecord,
|
|
Size) == Size);
|
|
|
|
}
|
|
} else {
|
|
|
|
fValid = FALSE;
|
|
}
|
|
} else if (!(pEventRecord->Length >= sizeof(EVENTLOGRECORD)) ||
|
|
!(pEventRecord->Reserved == ELF_LOG_FILE_SIGNATURE)) {
|
|
|
|
fValid = FALSE;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
fValid = FALSE;
|
|
}
|
|
|
|
return(fValid);
|
|
}
|
|
|
|
|
|
BOOL
|
|
IsPositionWithinRange(
|
|
PVOID Position,
|
|
PVOID BeginningRecord,
|
|
PVOID EndingRecord)
|
|
{
|
|
|
|
// Verify that the pointer is within the range of BEGINNING->END
|
|
|
|
|
|
if (EndingRecord > BeginningRecord) {
|
|
if ((Position >= BeginningRecord) && (Position <= EndingRecord))
|
|
return(TRUE);
|
|
|
|
} else if (EndingRecord < BeginningRecord) {
|
|
if ((Position >= BeginningRecord) || (Position <= EndingRecord))
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
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 infor (after header)
|
|
PhysicalEOF - Pointer 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 probability 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.
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
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
|
|
|
|
|
|
Size = min(ELFEOFUNIQUEPART,
|
|
(ULONG)((PBYTE)PhysicalEOF - (PBYTE)ptr));
|
|
|
|
pRecord = (PVOID)CONTAINING_RECORD(ptr, ELF_EOF_RECORD,
|
|
RecordSizeBeginning);
|
|
|
|
if (RtlCompareMemory(
|
|
pRecord,
|
|
&EOFRecord,
|
|
Size) == Size) {
|
|
|
|
// This is the EOF record, back up to the last record
|
|
|
|
|
|
(PBYTE)pRecord -= *((PULONG)pRecord - 1);
|
|
if (pRecord < PhysicalStart) {
|
|
pRecord = (PVOID)((PBYTE)PhysicalEOF -
|
|
((PBYTE)PhysicalStart - (PBYTE)pRecord));
|
|
}
|
|
|
|
}
|
|
|
|
if (ValidFilePos(pRecord, BeginningRecord, EndingRecord,
|
|
PhysicalEOF, BaseAddress, TRUE)) {
|
|
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 = (PVOID)CONTAINING_RECORD(ptr, EVENTLOGRECORD,
|
|
Reserved);
|
|
if (ValidFilePos(pRecord, BeginningRecord, EndingRecord,
|
|
PhysicalEOF, BaseAddress, TRUE)) {
|
|
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;
|
|
} else {
|
|
|
|
|
|
// This was the bottom half, let's look in the top half
|
|
|
|
|
|
EndOfBlock = (PULONG)Position;
|
|
ptr = (PULONG)PhysicalStart;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
|
return(BeginRecord);
|
|
}
|
|
|
|
NewPosition = (PVOID)((ULONG_PTR)CurrPosition + CurrRecordLength);
|
|
|
|
|
|
// Take care of wrapping.
|
|
|
|
|
|
if (NewPosition >= PhysicalEOF) {
|
|
NewPosition = (PVOID)((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.
|
|
|
|
|
|
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
|
|
return(NULL);
|
|
}
|
|
|
|
NewPosition = (PVOID)((PBYTE)CurrPosition - Length);
|
|
|
|
|
|
// Take care of wrapping
|
|
|
|
|
|
if (NewPosition < PhysStart) {
|
|
NewPosition = (PVOID)((PBYTE)PhysicalEOF -
|
|
((PBYTE)PhysStart - (PBYTE)NewPosition));
|
|
}
|
|
}
|
|
return (NewPosition);
|
|
} // NextRecordPosition
|
|
|
|
|
|
|
|
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)
|
|
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) {
|
|
|
|
Position = BeginRecord;
|
|
|
|
} else {
|
|
|
|
Position = (PVOID)((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)) {
|
|
|
|
// If this is a ELF_SKIP_DWORD, skip to the
|
|
// top of the file.
|
|
|
|
|
|
if (*(PDWORD)Position == ELF_SKIP_DWORD) {
|
|
Position = PhysStart;
|
|
}
|
|
} else {
|
|
|
|
// More likely the caller's handle was invalid
|
|
// if the position was not within range.
|
|
|
|
|
|
Status = STATUS_INVALID_HANDLE;
|
|
}
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
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) {
|
|
|
|
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 = (PVOID)((PBYTE)Position - *((PULONG)Position - 1));
|
|
|
|
if (Position < PhysStart) {
|
|
Position = (PVOID)((PBYTE)PhysEOF
|
|
- ((PBYTE)PhysStart - (PBYTE)Position));
|
|
}
|
|
} else {
|
|
|
|
Position = (PVOID)((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) {
|
|
|
|
return(STATUS_INVALID_PARAMETER);
|
|
}
|
|
|
|
|
|
// We're seeking to an absolute record number, so use the following
|
|
// algorhythm:
|
|
|
|
// There are two defines that control the process:
|
|
|
|
// MAX_WALKING_DISTANCE - when we get this close to the record,
|
|
// we just sequentially read records till we find the right one
|
|
|
|
// MAX_TRIES - we'll only try this many times to get within
|
|
// walking distance by calculation using average record size,
|
|
// after this, we'll just brute force it from where we are
|
|
|
|
// Calculate the average number of bytes per record
|
|
|
|
// Based on this number seek to where the record should start
|
|
|
|
// Find the start of the next record in the file
|
|
|
|
// If it's within "walking distance" move forward sequentially
|
|
// to the right record
|
|
|
|
// If it's not, recalcuate average bytes per record for the records
|
|
// between the start and the current record, and repeat
|
|
|
|
// Have a max number of tries at this, then just walk from wherever
|
|
// we are to the right record
|
|
|
|
|
|
#define MAX_WALKING_DISTANCE 5
|
|
#define MAX_TRIES 5
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
// Calcuate the first guess as to what the offset of the desired
|
|
// record should be
|
|
|
|
|
|
Position = (PVOID)((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 = (PVOID)((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.
|
|
}
|
|
|
|
|
|
// Get to the start of the next record after position
|
|
|
|
|
|
Position = FindStartOfNextRecord(Position, BeginRecord, EndRecord,
|
|
PhysStart, PhysEOF, Request->LogFile->BaseAddress);
|
|
|
|
if (Position) {
|
|
if (Request->Pkt.ReadPkt->RecordNumber >
|
|
((PEVENTLOGRECORD)Position)->RecordNumber) {
|
|
|
|
NumRecordsToSeek = Request->Pkt.ReadPkt->RecordNumber -
|
|
((PEVENTLOGRECORD)Position)->RecordNumber;
|
|
ReadFlags = EVENTLOG_FORWARDS_READ;
|
|
} else {
|
|
|
|
NumRecordsToSeek = ((PEVENTLOGRECORD)Position)->RecordNumber -
|
|
Request->Pkt.ReadPkt->RecordNumber;
|
|
ReadFlags = EVENTLOG_BACKWARDS_READ;
|
|
}
|
|
|
|
ElfDbgPrint(("[ELF] Walking %d records\n", NumRecordsToSeek));
|
|
}
|
|
|
|
while (Position != NULL && NumRecordsToSeek--) {
|
|
|
|
RecordLen = ((PEVENTLOGRECORD)Position)->Length;
|
|
|
|
Position = NextRecordPosition(
|
|
ReadFlags,
|
|
Position,
|
|
RecordLen,
|
|
BeginRecord,
|
|
EndRecord,
|
|
PhysEOF,
|
|
PhysStart
|
|
);
|
|
}
|
|
|
|
} // if SEEK_READ
|
|
|
|
*ReadPosition = Position; // This is the new seek position
|
|
|
|
if (!Position) { // The record was invalid
|
|
return(STATUS_EVENTLOG_FILE_CORRUPT);
|
|
} else {
|
|
return (STATUS_SUCCESS);
|
|
}
|
|
|
|
} // SeekToStartingRecord
|
|
|
|
|
|
VOID
|
|
CopyRecordToBuffer(
|
|
IN PBYTE pReadPosition,
|
|
IN OUT PBYTE* ppBufferPosition,
|
|
ULONG ulRecordSize,
|
|
PBYTE pPhysicalEOF,
|
|
PBYTE pPhysStart
|
|
)
|
|
{
|
|
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.
|
|
// 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:
|
|
|
|
When we come here, we are impersonating the client. If we get a
|
|
fault accessing the user's buffer, the fault goes to the user,
|
|
not the service.
|
|
|
|
--*/
|
|
{
|
|
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;
|
|
|
|
|
|
// Initialize variables.
|
|
|
|
|
|
BytesInBuffer = Request->Pkt.ReadPkt->BufferSize;
|
|
BufferPosition = Request->Pkt.ReadPkt->Buffer;
|
|
TotalBytesRead = 0;
|
|
TotalRecordsRead = 0;
|
|
PhysicalEOF = (PVOID)((LPBYTE)Request->LogFile->BaseAddress
|
|
+ Request->LogFile->ViewSize);
|
|
|
|
PhysStart = (PVOID)((LPBYTE)Request->LogFile->BaseAddress
|
|
+ FILEHEADERBUFSIZE);
|
|
|
|
BeginRecord = (PVOID)((LPBYTE)Request->LogFile->BaseAddress
|
|
+ Request->LogFile->BeginRecord);// Start at first record
|
|
|
|
EndRecord = (PVOID)((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
|
|
)) {
|
|
|
|
Request->Pkt.ReadPkt->BytesRead = 0;
|
|
Request->Pkt.ReadPkt->RecordsRead = 0;
|
|
|
|
return (STATUS_INVALID_HANDLE);
|
|
|
|
}
|
|
|
|
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) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
TempBufferPosition = BufferPosition; // Save this away
|
|
BufferPosition = TempBuf; // Read into TempBuf
|
|
|
|
CopyRecordToBuffer((PBYTE)ReadPosition,
|
|
(PBYTE*)&BufferPosition,
|
|
RecordSize,
|
|
(PBYTE)PhysicalEOF,
|
|
(PBYTE)PhysStart);
|
|
|
|
|
|
// Call CopyUnicodeToAnsiRecord with a NULL destination
|
|
// location in order to get the size of the Ansi record
|
|
|
|
Status = CopyUnicodeToAnsiRecord(
|
|
NULL,
|
|
TempBuf,
|
|
NULL,
|
|
&RecordBytesTransferred
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
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); // Free the temp buffer
|
|
TempBuf = NULL;
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
break; // Exit this loop
|
|
}
|
|
} else {
|
|
|
|
// Unicode call -- simply copy the record into the buffer
|
|
|
|
CopyRecordToBuffer((PBYTE)ReadPosition,
|
|
(PBYTE*)&BufferPosition,
|
|
RecordSize,
|
|
(PBYTE)PhysicalEOF,
|
|
(PBYTE)PhysStart);
|
|
}
|
|
|
|
|
|
// Update the byte and record counts
|
|
|
|
|
|
TotalRecordsRead++;
|
|
TotalBytesRead += RecordBytesTransferred;
|
|
BytesInBuffer -= RecordBytesTransferred;
|
|
|
|
ReadPosition = NextRecordPosition(
|
|
Request->Pkt.ReadPkt->ReadFlags,
|
|
ReadPosition,
|
|
RecordSize,
|
|
BeginRecord,
|
|
EndRecord,
|
|
PhysicalEOF,
|
|
PhysStart
|
|
);
|
|
|
|
|
|
// Make sure the record is valid
|
|
|
|
|
|
if (ReadPosition == NULL
|
|
||
|
|
!ValidFilePos(ReadPosition,
|
|
BeginRecord,
|
|
EndRecord,
|
|
PhysicalEOF,
|
|
Request->LogFile->BaseAddress,
|
|
TRUE)) {
|
|
|
|
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) {
|
|
return(STATUS_NO_MEMORY);
|
|
}
|
|
|
|
TempBufferPosition = BufferPosition; // Save this away
|
|
BufferPosition = TempBuf; // Read into TempBuf
|
|
|
|
CopyRecordToBuffer((PBYTE)ReadPosition,
|
|
(PBYTE*)&BufferPosition,
|
|
RecordSize,
|
|
(PBYTE)PhysicalEOF,
|
|
(PBYTE)PhysStart);
|
|
|
|
|
|
// Call CopyUnicodeToAnsiRecord with a NULL destination
|
|
// location in order to get the size of the Ansi record
|
|
|
|
Status = CopyUnicodeToAnsiRecord(
|
|
NULL,
|
|
TempBuf,
|
|
NULL,
|
|
&RecordBytesTransferred
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
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) {
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
// Set the bytes read in the request packet for return to client.
|
|
|
|
|
|
Request->Pkt.ReadPkt->BytesRead = TotalBytesRead;
|
|
Request->Pkt.ReadPkt->RecordsRead = TotalRecordsRead;
|
|
|
|
return (Status);
|
|
|
|
} // ReadFromLog
|
|
|
|
|
|
|
|
|
|
VOID
|
|
PerformReadRequest(PELF_REQUEST_RECORD Request)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine performs the READ request.
|
|
It first grabs the log file structure resource and then proceeds
|
|
to read from the file. If the resource is not available, it will
|
|
block until it is.
|
|
|
|
This routine impersonates the client in order to ensure that the correct
|
|
access control is uesd. If the client does not have permission to read
|
|
the file, the operation will fail.
|
|
|
|
Arguments:
|
|
|
|
Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
// Get shared access to the log file. This will allow multiple
|
|
// readers to get to the file together.
|
|
|
|
|
|
RtlAcquireResourceShared(
|
|
&Request->Module->LogFile->Resource,
|
|
TRUE // Wait until available
|
|
);
|
|
|
|
|
|
|
|
// Try to read from the log. Note that a corrupt log is the
|
|
// most likely cause of an exception (garbage pointers, etc).
|
|
// The eventlog corruption error is a bit all-inclusive, but
|
|
// necessary, since log state is pretty much indeterminable
|
|
// in this situation.
|
|
|
|
|
|
try {
|
|
Request->Status = (NTSTATUS)ReadFromLog(Request);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Request->Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
|
|
|
|
// Free the resource
|
|
|
|
|
|
RtlReleaseResource(&Request->Module->LogFile->Resource);
|
|
|
|
|
|
} // PerformReadRequest
|
|
|
|
|
|
|
|
WCHAR wszAltDosDevices[] = L"\\DosDevices\\";
|
|
WCHAR wszDosDevices[] = L"\\??\\";
|
|
#define DOSDEVICES_LEN ((sizeof(wszDosDevices) / sizeof(WCHAR)) - 1)
|
|
#define ALTDOSDEVICES_LEN ((sizeof(wszAltDosDevices) / sizeof(WCHAR)) - 1)
|
|
|
|
|
|
VOID
|
|
WriteToLog(
|
|
PLOGFILE pLogFile,
|
|
PVOID Buffer,
|
|
ULONG BufSize,
|
|
PULONG Destination,
|
|
ULONG PhysEOF,
|
|
ULONG PhysStart
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes the record into the log file, allowing for wrapping
|
|
around the end of the file.
|
|
|
|
It assumes that the caller has serialized access to the file, and has
|
|
ensured that there is enough space in the file for the record.
|
|
|
|
Arguments:
|
|
|
|
Buffer - Pointer to the buffer containing the event record.
|
|
BufSize - Size of the record to be written.
|
|
Destination - Pointer to the destination - which is in the log file.
|
|
PhysEOF - Physical end of file.
|
|
PhysStart - Physical beginning of file (past the file header).
|
|
|
|
Return Value:
|
|
|
|
NONE.
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
ULONG BytesToCopy;
|
|
SIZE_T FlushSize;
|
|
ULONG NewDestination;
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER ByteOffset;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PVOID BaseAddress;
|
|
LPWSTR pwszLogFileName;
|
|
|
|
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
|
|
|
|
NewDestination = *Destination + BytesToCopy;
|
|
|
|
if (BytesToCopy != BufSize) {
|
|
|
|
|
|
// Wrap around to the beginning of the file and copy the
|
|
// rest of the data.
|
|
|
|
|
|
Buffer = (PVOID)((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
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
*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 {
|
|
ElfDbgPrintNC(("[ELF] SetFileAttributes on file (%ws) failed, "
|
|
"WIN32 error = 0x%lx\n",
|
|
pwszLogFileName,
|
|
GetLastError()));
|
|
}
|
|
}
|
|
|
|
} // WriteToLog
|
|
|
|
|
|
|
|
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:
|
|
|
|
--*/
|
|
{
|
|
static ULONG LastAlertWritten = 0; // Don't Generate alerts too often
|
|
NTSTATUS Status;
|
|
ULONG WritePos; // Position to write record
|
|
LARGE_INTEGER Time;
|
|
ULONG SpaceNeeded; // Record size + "buffer" size
|
|
ULONG CurrentTime;
|
|
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;
|
|
|
|
pLogFile = Request->LogFile; // Set local variable
|
|
|
|
|
|
// Get exclusive access to the log file. This will ensure no one
|
|
// else is accessing the file.
|
|
|
|
|
|
RtlAcquireResourceExclusive(
|
|
&pLogFile->Resource,
|
|
TRUE // Wait until available
|
|
);
|
|
|
|
try {
|
|
|
|
|
|
// Put in the record number
|
|
|
|
|
|
pEventLogRecord = (PEVENTLOGRECORD)Request->Pkt.WritePkt->Buffer;
|
|
pEventLogRecord->RecordNumber = pLogFile->CurrentRecordNumber;
|
|
|
|
|
|
// Now, go to the end of the file and look for empty space.
|
|
|
|
// If there is enough space to write out the record, just
|
|
// write it out and update the pointers.
|
|
|
|
// If there isn't enough space, then we need to check if we can
|
|
// wrap around the file without overwriting any records that are
|
|
// within the time retention period.
|
|
// If we cannot find any room, then we have to return an error
|
|
// that the file is full (and alert the administrator).
|
|
|
|
|
|
RecordSize = Request->Pkt.WritePkt->Datasize;
|
|
|
|
SpaceNeeded = RecordSize + ELFEOFRECORDSIZE;
|
|
|
|
if (pLogFile->EndRecord > pLogFile->BeginRecord) {
|
|
|
|
|
|
// The current write position is after the position of the first
|
|
// record, then we can write up to the end of the file without
|
|
// worrying about overwriting existing records.
|
|
|
|
|
|
SpaceAvail = pLogFile->ActualMaxFileSize - (pLogFile->EndRecord -
|
|
pLogFile->BeginRecord + FILEHEADERBUFSIZE);
|
|
|
|
|
|
} else if (pLogFile->EndRecord == pLogFile->BeginRecord
|
|
&& !(pLogFile->Flags & ELF_LOGFILE_HEADER_WRAP)) {
|
|
|
|
|
|
// If the write position is equal to the position of the first
|
|
// record, and we have't wrapped yet, then the file is "empty"
|
|
// and so we have room to the physical end of the file.
|
|
|
|
|
|
SpaceAvail = pLogFile->ActualMaxFileSize - FILEHEADERBUFSIZE;
|
|
|
|
} else {
|
|
|
|
|
|
// If our write position is before the position of the first record, then
|
|
// the file has wrapped and we need to deal with overwriting existing
|
|
// records in the file.
|
|
|
|
|
|
SpaceAvail = pLogFile->BeginRecord - pLogFile->EndRecord;
|
|
|
|
}
|
|
|
|
|
|
// We now have the number of bytes available to write the record
|
|
// WITHOUT overwriting any existing records - 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 it's maximum allowable
|
|
// size yet, and also hasn't wrapped. If not, grow it by as much as
|
|
// needed, in 64K chunks.
|
|
|
|
|
|
if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize &&
|
|
SpaceNeeded > SpaceAvail) {
|
|
|
|
|
|
// Extend it. This call cannot fail. If it can't extend it, it
|
|
// just caps it at the current size by changing
|
|
// pLogFile->ConfigMaxFileSize
|
|
|
|
|
|
ElfExtendFile(pLogFile,
|
|
SpaceNeeded,
|
|
&SpaceAvail);
|
|
}
|
|
|
|
|
|
// We don't want to split the fixed portion of a record across the
|
|
// physical end of the file, it makes it difficult when referencing
|
|
// these fields later (you have to check before you touch each one
|
|
// to make sure it's not after the physical EOF). So, if there's
|
|
// not enough room at the end of the file for the fixed portion,
|
|
// we fill it with a known byte pattern ELF_SKIP_DWORD that will
|
|
// be skipped if it's found at the start of a record (as long as
|
|
// it's less than the minimum record size, then we know it's not
|
|
// the start of a valid record).
|
|
|
|
|
|
if (pLogFile->ActualMaxFileSize - pLogFile->EndRecord <
|
|
sizeof(EVENTLOGRECORD)) {
|
|
|
|
|
|
// Save the EndRecord pointer in case we don't have the space
|
|
// to write another record, we'll need to rewrite the EOF where
|
|
// it was
|
|
|
|
|
|
OverwrittenEOF = pLogFile->EndRecord;
|
|
|
|
FillDword = (PDWORD)((PBYTE)pLogFile->BaseAddress +
|
|
pLogFile->EndRecord);
|
|
while (FillDword < (PDWORD)((LPBYTE)pLogFile->BaseAddress +
|
|
pLogFile->ActualMaxFileSize)) {
|
|
*FillDword = ELF_SKIP_DWORD;
|
|
FillDword++;
|
|
}
|
|
|
|
pLogFile->EndRecord = FILEHEADERBUFSIZE;
|
|
SpaceAvail = pLogFile->BeginRecord - FILEHEADERBUFSIZE;
|
|
pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
}
|
|
|
|
EventRecord = (PEVENTLOGRECORD)((PBYTE)pLogFile->BaseAddress +
|
|
pLogFile->BeginRecord);
|
|
|
|
while (SpaceNeeded > SpaceAvail) {
|
|
|
|
|
|
// If this logfile can be overwrite-as-needed, or if it has
|
|
// an overwrite time limit and the time hasn't expired, then
|
|
// allow the new event to overwrite an older event.
|
|
|
|
|
|
if ((pLogFile->Retention == OVERWRITE_AS_NEEDED) ||
|
|
((pLogFile->Retention != NEVER_OVERWRITE) &&
|
|
((EventRecord->TimeWritten < EarliestTime) ||
|
|
(Request->Flags & ELF_FORCE_OVERWRITE)))) { // OK to overwrite
|
|
|
|
ULONG NextRecord;
|
|
ULONG SearchStartPos;
|
|
BOOL fBeginningRecordWrap = FALSE;
|
|
BOOL fInvalidRecordLength = FALSE;
|
|
|
|
DeletedRecordOffset = pLogFile->BeginRecord;
|
|
|
|
pLogFile->BeginRecord += EventRecord->Length;
|
|
|
|
|
|
// Insure BeginRecord offset is DWORD-aligned.
|
|
|
|
|
|
pLogFile->BeginRecord = (((ULONG)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) {
|
|
|
|
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 this case, adjust BeginRecord to be just
|
|
// beyond the length/signature of the previous record to scan
|
|
// for the next valid record.
|
|
|
|
|
|
if (BeginRecord >= pLogFile->ActualMaxFileSize) {
|
|
|
|
fInvalidRecordLength = TRUE;
|
|
} else {
|
|
|
|
pLogFile->BeginRecord = BeginRecord;
|
|
}
|
|
}
|
|
|
|
if (fInvalidRecordLength) {
|
|
|
|
|
|
// If the record length is considered bogus, adjust
|
|
// BeginRecord to be just beyond the length,signature of
|
|
// the previous record to scan for the next valid record.
|
|
|
|
|
|
pLogFile->BeginRecord = DeletedRecordOffset +
|
|
(sizeof(ULONG) * 2);
|
|
}
|
|
|
|
|
|
// Insure 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.
|
|
|
|
|
|
NextRecord = pLogFile->BeginRecord + sizeof(ULONG);
|
|
|
|
if (NextRecord < pLogFile->ActualMaxFileSize) {
|
|
|
|
SpaceAvail += min(
|
|
sizeof(ULONG),
|
|
pLogFile->ActualMaxFileSize - NextRecord);
|
|
}
|
|
|
|
|
|
// Seek to find a record signature.
|
|
|
|
|
|
SearchStartPos = pLogFile->BeginRecord;
|
|
|
|
for (;; ) {
|
|
|
|
PVOID Position;
|
|
|
|
for (; NextRecord != SearchStartPos;
|
|
SpaceAvail += sizeof(ULONG),
|
|
NextRecord += sizeof(ULONG)) {
|
|
|
|
if (NextRecord >= pLogFile->ActualMaxFileSize) {
|
|
|
|
NextRecord = pLogFile->BeginRecord = FILEHEADERBUFSIZE;
|
|
}
|
|
|
|
if (*(PULONG)((PBYTE)pLogFile->BaseAddress +
|
|
NextRecord) == ELF_LOG_FILE_SIGNATURE) {
|
|
|
|
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, // Unused.
|
|
NULL, // Unused.
|
|
(PBYTE)pLogFile->BaseAddress +
|
|
pLogFile->ViewSize,
|
|
pLogFile->BaseAddress,
|
|
FALSE)) { // No range check.
|
|
|
|
|
|
// The record is valid. Adjust SpaceAvail to not
|
|
// include a sub-portion of this record in the
|
|
// available space size computation.
|
|
|
|
|
|
SpaceAvail -= sizeof(ULONG);
|
|
pLogFile->BeginRecord = NextRecord - sizeof(ULONG);
|
|
break;
|
|
} else {
|
|
|
|
|
|
// Continue the search for the next valid record.
|
|
|
|
// NB : Not calling FixContextHandlesForRecord
|
|
// since we have not established a valid
|
|
// beginning record position yet. Not that
|
|
// it would do any good - this condition would
|
|
// be evaluated in cases of corrupt logs.
|
|
|
|
|
|
SpaceAvail += sizeof(ULONG);
|
|
NextRecord += sizeof(ULONG);
|
|
continue;
|
|
}
|
|
} else {
|
|
|
|
|
|
// ** THIS SHOULD NEVER, EVER, OCCUR **
|
|
|
|
// In fact, I even considered not coding to handle it.
|
|
// All the more reason to handle it, though.
|
|
|
|
// Not a single valid record can be found. This is not
|
|
// good. Consider the log corrupted and bail the write.
|
|
|
|
|
|
Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
ASSERT(Status != STATUS_EVENTLOG_FILE_CORRUPT);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_EVENTLOG_FILE_CORRUPT) {
|
|
|
|
break;
|
|
}
|
|
|
|
if (fBeginningRecordWrap) {
|
|
|
|
|
|
// Check to see if the file has reached its maximum allowable
|
|
// size yet. If not, grow it by as much as needed, in 64K
|
|
// chunks.
|
|
|
|
|
|
if (pLogFile->ActualMaxFileSize < pLogFile->ConfigMaxFileSize) {
|
|
|
|
|
|
// Extend it. This call cannot fail. If it can't
|
|
// extend it, it just caps it at the current size by
|
|
// changing pLogFile->ConfigMaxFileSize.
|
|
|
|
|
|
ElfExtendFile(pLogFile,
|
|
SpaceNeeded,
|
|
&SpaceAvail);
|
|
|
|
|
|
// Since extending the file will cause it to be moved, we
|
|
// need to re-establish the address for the EventRecord.
|
|
|
|
EventRecord = (PEVENTLOGRECORD)((PBYTE)
|
|
pLogFile->BaseAddress +
|
|
DeletedRecordOffset);
|
|
}
|
|
}
|
|
|
|
|
|
// Make sure no handle points to the record that we're getting
|
|
// ready to overwrite, it one does, correct it to point to the
|
|
// new first record.
|
|
|
|
|
|
FixContextHandlesForRecord(DeletedRecordOffset,
|
|
pLogFile->BeginRecord);
|
|
|
|
if (!fInvalidRecordLength) {
|
|
|
|
|
|
// Update SpaceAvail to include the deleted record's size.
|
|
// That is, if we have a high degree of confidence that
|
|
// it is valid.
|
|
|
|
|
|
SpaceAvail += EventRecord->Length;
|
|
}
|
|
|
|
|
|
// Bump to the next record, file wrap was handled above
|
|
|
|
|
|
|
|
// If these are ELF_SKIP_DWORDs, just move past them
|
|
|
|
|
|
FillDword = (PDWORD)((PBYTE)pLogFile->BaseAddress +
|
|
pLogFile->BeginRecord);
|
|
|
|
if (*FillDword == ELF_SKIP_DWORD) {
|
|
SpaceAvail += pLogFile->ActualMaxFileSize -
|
|
pLogFile->BeginRecord;
|
|
pLogFile->BeginRecord = FILEHEADERBUFSIZE;
|
|
}
|
|
|
|
EventRecord = (PEVENTLOGRECORD)((PBYTE)pLogFile->BaseAddress +
|
|
pLogFile->BeginRecord);
|
|
|
|
} else { // All records within retention period
|
|
|
|
ElfDbgPrint(("[ELF] %s log file 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
|
|
|
|
|
|
if (pLogFile->logpLogPopup == LOGPOPUP_CLEARED
|
|
&&
|
|
!ElfGlobalData->fSetupInProgress) {
|
|
INT StringLen, id = -1;
|
|
LPTSTR lpModuleNameLoc = NULL;
|
|
HMODULE StringsResource;
|
|
|
|
|
|
// We should never be popping up or logging an event
|
|
// for the security log
|
|
|
|
|
|
ASSERT(_wcsicmp(pLogFile->LogModuleName->Buffer,
|
|
ELF_SECURITY_MODULE_NAME) != 0);
|
|
|
|
|
|
// Get the localized module name from message table
|
|
|
|
|
|
StringsResource = GetModuleHandle(L"EVENTLOG.DLL");
|
|
|
|
ASSERT(StringsResource != NULL);
|
|
|
|
if (_wcsicmp(pLogFile->LogModuleName->Buffer,
|
|
ELF_SYSTEM_MODULE_NAME) == 0) {
|
|
id = ELF_MODULE_NAME_LOCALIZE_SYSTEM;
|
|
} else if (_wcsicmp(pLogFile->LogModuleName->Buffer,
|
|
ELF_APPLICATION_MODULE_NAME) == 0) {
|
|
id = ELF_MODULE_NAME_LOCALIZE_APPLICATION;
|
|
}
|
|
|
|
if (id != -1) {
|
|
|
|
StringLen = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, StringsResource, id, 0, (LPTSTR)&lpModuleNameLoc, 0, NULL);
|
|
if ((StringLen > 1) && (lpModuleNameLoc != NULL)) {
|
|
|
|
// Get rid of cr/lf control code at the end
|
|
|
|
*(lpModuleNameLoc + StringLen - 2) = 0;
|
|
}
|
|
}
|
|
|
|
ElfpCreateElfEvent(
|
|
EVENT_LOG_FULL,
|
|
EVENTLOG_ERROR_TYPE,
|
|
0, // EventCategory
|
|
1, // NumberOfStrings
|
|
(lpModuleNameLoc != NULL) ?
|
|
&lpModuleNameLoc
|
|
: &Request->LogFile->LogModuleName->Buffer, // Strings
|
|
NULL, // Data
|
|
0, // Datalength
|
|
ELF_FORCE_OVERWRITE); // Overwrite if necc.
|
|
|
|
ElfpCreateQueuedMessage(ALERT_ELF_LogOverflow, 1,
|
|
(lpModuleNameLoc != NULL) ?
|
|
&lpModuleNameLoc
|
|
: &Request->Module->LogFile->LogModuleName->Buffer);
|
|
|
|
LocalFree(lpModuleNameLoc);
|
|
|
|
|
|
// Don't post the popup again until either the machine is
|
|
// rebooted or the log is cleared
|
|
|
|
|
|
pLogFile->logpLogPopup = LOGPOPUP_ALREADY_SHOWN;
|
|
}
|
|
|
|
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,
|
|
(PVOID)&EOFRecord,
|
|
ELFEOFRECORDSIZE,
|
|
&WritePos,
|
|
pLogFile->ActualMaxFileSize,
|
|
FILEHEADERBUFSIZE
|
|
);
|
|
}
|
|
|
|
Status = STATUS_LOG_FILE_FULL;
|
|
break; // Get out of while loop
|
|
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
// We have enough room to write the record and the EOF record.
|
|
|
|
|
|
|
|
// Update OldestRecordNumber to reflect the records that were
|
|
// overwritten amd increment the CurrentRecordNumber
|
|
|
|
// Make sure that the log isn't empty, if it is, the oldestrecord
|
|
// is 1
|
|
|
|
|
|
if (pLogFile->BeginRecord == pLogFile->EndRecord) {
|
|
pLogFile->OldestRecordNumber = 1;
|
|
} else {
|
|
pLogFile->OldestRecordNumber = EventRecord->RecordNumber;
|
|
}
|
|
pLogFile->CurrentRecordNumber++;
|
|
|
|
|
|
// If the dirty bit is not set, then this is the first time that
|
|
// we have written to the file since we started. In that case,
|
|
// set the dirty bit in the file header as well so that we will
|
|
// know that the contents have changed.
|
|
|
|
|
|
if (!(pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY)) {
|
|
SIZE_T HeaderSize;
|
|
|
|
pLogFile->Flags |= ELF_LOGFILE_HEADER_DIRTY;
|
|
|
|
pFileHeader = (PELF_LOGFILE_HEADER)(pLogFile->BaseAddress);
|
|
pFileHeader->Flags |= ELF_LOGFILE_HEADER_DIRTY;
|
|
|
|
|
|
// Now flush this to disk to commit it
|
|
|
|
|
|
BaseAddress = pLogFile->BaseAddress;
|
|
HeaderSize = FILEHEADERBUFSIZE;
|
|
|
|
Status = NtFlushVirtualMemory(
|
|
NtCurrentProcess(),
|
|
&BaseAddress,
|
|
&HeaderSize,
|
|
&IoStatusBlock
|
|
);
|
|
}
|
|
|
|
|
|
// Write the event to the log
|
|
|
|
|
|
WriteToLog(pLogFile,
|
|
Request->Pkt.WritePkt->Buffer,
|
|
RecordSize,
|
|
&(pLogFile->EndRecord),
|
|
pLogFile->ActualMaxFileSize,
|
|
FILEHEADERBUFSIZE);
|
|
|
|
|
|
// Use a separate variable for the position,since we don't want
|
|
// it updated.
|
|
|
|
|
|
WritePos = pLogFile->EndRecord;
|
|
|
|
if (WritePos > pLogFile->ActualMaxFileSize) {
|
|
WritePos -= pLogFile->ActualMaxFileSize - FILEHEADERBUFSIZE;
|
|
}
|
|
|
|
|
|
// Update the EOF record fields
|
|
|
|
|
|
EOFRecord.BeginRecord = pLogFile->BeginRecord;
|
|
EOFRecord.EndRecord = WritePos;
|
|
EOFRecord.CurrentRecordNumber = pLogFile->CurrentRecordNumber;
|
|
EOFRecord.OldestRecordNumber = pLogFile->OldestRecordNumber;
|
|
|
|
|
|
// Write out the EOF record
|
|
|
|
|
|
WriteToLog(pLogFile,
|
|
(PVOID)&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 = (NTSTATUS)Status;
|
|
}
|
|
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Request->Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
|
|
// Free the resource
|
|
|
|
|
|
RtlReleaseResource(&pLogFile->Resource);
|
|
|
|
} // PerformWriteRequest
|
|
|
|
|
|
VOID
|
|
PerformClearRequest(PELF_REQUEST_RECORD Request)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will optionally back up the log file specified, and will
|
|
delete it.
|
|
|
|
Arguments:
|
|
|
|
Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
On the exit path, when we do some "cleanup" work, we discard the
|
|
status and instead return the status of the operation that is being
|
|
performed.
|
|
This is necessary since we wish to return any error condition that is
|
|
directly related to the clear operation. For other errors, we will
|
|
fail at a later stage.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status, IStatus;
|
|
PUNICODE_STRING FileName;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PFILE_RENAME_INFORMATION NewName = NULL;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
HANDLE ClearHandle = NULL;
|
|
FILE_DISPOSITION_INFORMATION DeleteInfo = {TRUE};
|
|
ULONG FileRefCount;
|
|
BOOLEAN FileRenamed = FALSE;
|
|
|
|
|
|
// Get exclusive access to the log file. This will ensure no one
|
|
// else is accessing the file.
|
|
|
|
|
|
RtlAcquireResourceExclusive(
|
|
&Request->Module->LogFile->Resource,
|
|
TRUE // Wait until available
|
|
);
|
|
|
|
|
|
// We have exclusive access to the file.
|
|
|
|
// We force the file to be closed, and store away the ref count
|
|
// so that we can set it back when we reopen the file.
|
|
// This is a little *sleazy* but we have exclusive access to the
|
|
// logfile structure so we can play these games.
|
|
|
|
|
|
FileRefCount = Request->LogFile->RefCount; // Store this away
|
|
ElfpCloseLogFile(Request->LogFile, ELF_LOG_CLOSE_FORCE);
|
|
Request->LogFile->FileHandle = NULL; // For use later
|
|
|
|
|
|
// Open the file with delete access in order to rename it.
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
Request->LogFile->LogFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
Status = NtOpenFile(&ClearHandle,
|
|
GENERIC_READ | DELETE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
// If the backup file name has been specified and is not NULL,
|
|
// then we move the current file to the new file. If that fails,
|
|
// then fail the whole operation.
|
|
|
|
|
|
if ((Request->Pkt.ClearPkt->BackupFileName != NULL)
|
|
&& (Request->Pkt.ClearPkt->BackupFileName->Length != 0)) {
|
|
|
|
FileName = Request->Pkt.ClearPkt->BackupFileName;
|
|
|
|
|
|
// Set up the rename information structure with the new name
|
|
|
|
|
|
NewName = ElfpAllocateBuffer(
|
|
FileName->Length + sizeof(WCHAR) + sizeof(*NewName));
|
|
if (NewName) {
|
|
RtlMoveMemory(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.
|
|
|
|
|
|
ElfDbgPrint(("[ELF] Copy log file\n"));
|
|
|
|
Status = ElfpCopyFile(ClearHandle, FileName);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
ElfDbgPrint(("[ELF] Deleting log file\n"));
|
|
|
|
Status = NtSetInformationFile(
|
|
ClearHandle,
|
|
&IoStatusBlock,
|
|
&DeleteInfo,
|
|
sizeof(DeleteInfo),
|
|
FileDispositionInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrintNC(("[ELF] Delete failed 0x%lx\n",
|
|
Status));
|
|
}
|
|
}
|
|
} else if (NT_SUCCESS(Status)) {
|
|
FileRenamed = TRUE;
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrintNC(("[ELF] Rename/Copy failed 0x%lx\n", Status));
|
|
}
|
|
} else {
|
|
Status = STATUS_NO_MEMORY;
|
|
}
|
|
} else { // No backup to done
|
|
|
|
|
|
// 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.
|
|
|
|
|
|
ElfDbgPrint(("[ELF] Deleting log file\n"));
|
|
|
|
Status = NtSetInformationFile(
|
|
ClearHandle,
|
|
&IoStatusBlock,
|
|
&DeleteInfo,
|
|
sizeof(DeleteInfo),
|
|
FileDispositionInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrintNC(("[ELF] Delete failed 0x%lx\n", Status));
|
|
}
|
|
|
|
|
|
} // Backup and/or Delete
|
|
|
|
IStatus = NtClose(ClearHandle); // Discard status
|
|
ASSERT(NT_SUCCESS(IStatus));
|
|
|
|
} else { // The open-for-delete failed.
|
|
|
|
ElfDbgPrintNC(("[ELF] Open-for-delete failed 0x%lx\n", Status));
|
|
}
|
|
|
|
|
|
// Now pick up any new size value that was set before but
|
|
// couldn't be used until the log was cleared (they reduced the size of
|
|
// the log file.)
|
|
|
|
|
|
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)) {
|
|
|
|
Status = IStatus;
|
|
|
|
|
|
// Bug #62736 -- If somebody has a handle to the log file open,
|
|
// we can delete but can't write to it until that handle is
|
|
// released. Since there's no old log file, without this check
|
|
// we'd AV below.
|
|
|
|
if (IStatus != STATUS_DELETE_PENDING) {
|
|
|
|
|
|
// Opening the new log file failed, reopen the old log and
|
|
// return this error from the Api
|
|
|
|
|
|
PFILE_RENAME_INFORMATION OldName;
|
|
UNICODE_STRING UnicodeString;
|
|
|
|
|
|
// There shouldn't be any way to fail unless we successfully
|
|
// renamed the file, and there's no recovery if that happens.
|
|
|
|
|
|
ASSERT(FileRenamed == TRUE);
|
|
|
|
|
|
// Rename the file back to the original name. Reuse ClearHandle.
|
|
|
|
|
|
RtlInitUnicodeString(&UnicodeString, NewName->FileName);
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&UnicodeString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
IStatus = NtOpenFile(&ClearHandle,
|
|
GENERIC_READ | DELETE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT
|
|
);
|
|
|
|
|
|
// This can't fail, I just created it!
|
|
|
|
|
|
ASSERT(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) {
|
|
RtlMoveMemory(OldName->FileName,
|
|
Request->LogFile->LogFileName->Buffer,
|
|
Request->LogFile->LogFileName->Length
|
|
);
|
|
|
|
|
|
|
|
// Guarantee that it's NULL terminated
|
|
|
|
|
|
OldName->FileName[Request->LogFile->LogFileName->Length /
|
|
sizeof(WCHAR)] = L'\0';
|
|
|
|
OldName->ReplaceIfExists = FALSE;
|
|
OldName->RootDirectory = NULL;
|
|
OldName->FileNameLength = Request->LogFile->LogFileName->Length;
|
|
|
|
IStatus = NtSetInformationFile(
|
|
ClearHandle,
|
|
&IoStatusBlock,
|
|
OldName,
|
|
Request->LogFile->LogFileName->Length +
|
|
sizeof(*OldName) + sizeof(WCHAR),
|
|
FileRenameInformation
|
|
);
|
|
ASSERT(NT_SUCCESS(IStatus));
|
|
IStatus = NtClose(ClearHandle);
|
|
ASSERT(NT_SUCCESS(IStatus));
|
|
|
|
|
|
// Reopen the original file, this has to work
|
|
|
|
|
|
IStatus = ElfOpenLogFile(Request->LogFile, ElfNormalLog);
|
|
ASSERT(NT_SUCCESS(IStatus));
|
|
|
|
ElfpFreeBuffer(OldName);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
// The delete failed for some reason -- reopen the original log file
|
|
|
|
IStatus = ElfOpenLogFile(Request->LogFile, ElfNormalLog);
|
|
ASSERT(NT_SUCCESS(IStatus));
|
|
}
|
|
|
|
Request->LogFile->RefCount = FileRefCount; // Restore old value.
|
|
|
|
if (Request->LogFile->logpLogPopup == LOGPOPUP_ALREADY_SHOWN) {
|
|
|
|
// This log has a viewable popup (i.e., it's not LOGPOPUP_NEVER_SHOW),
|
|
// so we should show it again if the log fills up.
|
|
|
|
Request->LogFile->logpLogPopup = LOGPOPUP_CLEARED;
|
|
}
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
// Free the resource
|
|
|
|
RtlReleaseResource(&Request->Module->LogFile->Resource);
|
|
|
|
ElfpFreeBuffer(NewName);
|
|
|
|
} // PerformClearRequest
|
|
|
|
|
|
VOID
|
|
PerformBackupRequest(PELF_REQUEST_RECORD Request)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will back up the log file specified.
|
|
|
|
This routine impersonates the client in order to ensure that the correct
|
|
access control is used.
|
|
|
|
This routine is entered with the ElfGlobalResource held in a shared
|
|
state and the logfile lock is acquired shared to prevent writing, but
|
|
allow people to still read.
|
|
|
|
This copies the file in two chunks, from the first record to the end
|
|
of the file, and then from the top of the file (excluding the header)
|
|
to the end of the EOF record.
|
|
|
|
Arguments:
|
|
|
|
Pointer to the request packet.
|
|
|
|
Return Value:
|
|
|
|
NONE, status is placed in the packet for later use by the API wrapper
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status, IStatus;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
LARGE_INTEGER MaximumSizeOfSection;
|
|
LARGE_INTEGER Offset;
|
|
ULONG LastRecordNumber;
|
|
ULONG OldestRecordNumber;
|
|
HANDLE BackupHandle = INVALID_HANDLE_VALUE;
|
|
PBYTE StartOfCopy;
|
|
PBYTE EndOfCopy;
|
|
ULONG BytesToCopy;
|
|
ULONG EndRecord = FILEHEADERBUFSIZE;
|
|
BOOL ImpersonatingClient = FALSE;
|
|
ELF_LOGFILE_HEADER FileHeaderBuf = {FILEHEADERBUFSIZE, // Size
|
|
ELF_LOG_FILE_SIGNATURE,
|
|
ELF_VERSION_MAJOR,
|
|
ELF_VERSION_MINOR,
|
|
FILEHEADERBUFSIZE, // Start offset
|
|
FILEHEADERBUFSIZE, // End offset
|
|
1, // Next record #
|
|
1, // Oldest record #
|
|
0, // Maxsize
|
|
0, // Flags
|
|
0, // Retention
|
|
FILEHEADERBUFSIZE // Size
|
|
};
|
|
|
|
|
|
|
|
// Get shared access to the log file. This will ensure no one
|
|
// else clears the file.
|
|
|
|
|
|
RtlAcquireResourceShared(
|
|
&Request->Module->LogFile->Resource,
|
|
TRUE // Wait until available
|
|
);
|
|
|
|
|
|
// Save away the next record number. We'll stop copying when we get to
|
|
// the record before this one. Also save the first record number so we
|
|
// can update the header and EOF record.
|
|
|
|
|
|
LastRecordNumber = Request->LogFile->CurrentRecordNumber;
|
|
OldestRecordNumber = Request->LogFile->OldestRecordNumber;
|
|
|
|
|
|
// Impersonate the client
|
|
|
|
|
|
Status = I_RpcMapWin32Status(RpcImpersonateClient(NULL));
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
// Keep this info so I can only revert in 1 place
|
|
|
|
|
|
ImpersonatingClient = TRUE;
|
|
|
|
|
|
// Set up the object attributes structure for the backup file
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
Request->Pkt.BackupPkt->BackupFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
// Open the backup file. Fail if a file by this name already exists.
|
|
|
|
|
|
MaximumSizeOfSection =
|
|
RtlConvertUlongToLargeInteger(
|
|
Request->LogFile->ActualMaxFileSize);
|
|
|
|
Status = NtCreateFile(
|
|
&BackupHandle,
|
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
&MaximumSizeOfSection,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
FILE_CREATE,
|
|
FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
|
|
NULL,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrintNC(("[ELF] Open of Backup file failed - %X\n", 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)) {
|
|
|
|
ElfDbgPrintNC(("[ELF]: Backup file header write failed %X\n",
|
|
Status));
|
|
goto errorexit;
|
|
}
|
|
|
|
|
|
// Scan from the end of the file skipping over ELF_SKIP_DWORDs
|
|
// to figure out 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)) {
|
|
|
|
ElfDbgPrintNC(("[ELF]: Backup file 1st block write failed %X\n",
|
|
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)) {
|
|
|
|
ElfDbgPrintNC(("[ELF]: Backup file 2nd block write failed %X\n",
|
|
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)) {
|
|
|
|
ElfDbgPrintNC(("[ELF]: Backup file EOF record write failed %X\n",
|
|
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)) {
|
|
|
|
ElfDbgPrintNC(("[ELF]: Backup file header rewrite failed %X\n",
|
|
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;
|
|
|
|
|
|
// Undo the impersonation.
|
|
|
|
|
|
}
|
|
errorexit:
|
|
|
|
if (ImpersonatingClient) {
|
|
IStatus = I_RpcMapWin32Status(RpcRevertToSelf()); // Discard status
|
|
ASSERT(NT_SUCCESS(IStatus));
|
|
}
|
|
|
|
// Close the output file
|
|
if (BackupHandle != INVALID_HANDLE_VALUE) {
|
|
NtClose(BackupHandle);
|
|
}
|
|
|
|
// Set status field in the request packet.
|
|
Request->Status = Status;
|
|
|
|
// Free the resource
|
|
RtlReleaseResource(&Request->Module->LogFile->Resource);
|
|
} // PerformBackupRequest
|
|
|
|
|
|
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 queuedevent
|
|
list.
|
|
|
|
Arguments:
|
|
Pointer to the request packet.
|
|
--*/
|
|
{
|
|
|
|
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.
|
|
|
|
// In that case, we can do one of two things:
|
|
|
|
// 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 will
|
|
// be terminated or paused. We can examine the current
|
|
// status of the service and see if it is still "installed"
|
|
// (i.e. no "pending" state). If so, we loop around and try
|
|
// to get the resource again (after sleeping a bit?). We
|
|
// break out of the loop if the state of the service changes
|
|
// to PAUSED, PAUSE_PENDING, UNINSTALL_PENDING, etc. so as
|
|
// not to block the thread indefinitely.
|
|
|
|
|
|
while ((GetElState() == RUNNING) && (!Acquired)) {
|
|
|
|
Acquired = RtlAcquireResourceShared(
|
|
&GlobalElfResource,
|
|
FALSE // Don't wait
|
|
);
|
|
if (!Acquired) {
|
|
ElfDbgPrint(("[ELF] Sleep waiting for global resource\n"));
|
|
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) {
|
|
ElfDbgPrint(("[ELF] 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();
|
|
}
|
|
} // ElfPerformRequest
|
|
|
|
|
|
//SS:end of changes made to enable cluster wide event logging
|
|
/*
|
|
@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,
|
|
IN 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((PRTL_CRITICAL_SECTION)&LogFileCritSec);
|
|
|
|
//initialize the number of files
|
|
ulNumLogFiles = 0; //count of files
|
|
//initialize the number of files/total event size
|
|
*pulNumLogFiles = 0; //count of file 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
|
|
);
|
|
}
|
|
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: Numlogfiles %d \r\n",
|
|
ulNumLogFiles));
|
|
|
|
if (!ulNumLogFiles)
|
|
goto FnExit;
|
|
//allocate a structure for log file info
|
|
pPropLogFileInfo = (PPROPLOGFILEINFO)
|
|
ElfpAllocateBuffer((ulNumLogFiles) * sizeof(PROPLOGFILEINFO));
|
|
if (!pPropLogFileInfo) {
|
|
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
|
|
while ((pLogFile->FileList.Flink != LogFilesHead.Flink) &&
|
|
(i < (ulNumLogFiles))) {
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: processing file %ws\r\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)
|
|
goto process_nextlogfile;
|
|
|
|
//if they do, find the positions in the file where they are logged
|
|
PhysicalEOF = (PVOID)((LPBYTE)pLogFile->BaseAddress
|
|
+ pLogFile->ViewSize);
|
|
|
|
PhysStart = (PVOID)((LPBYTE)pLogFile->BaseAddress
|
|
+ FILEHEADERBUFSIZE);
|
|
|
|
BeginRecord = (PVOID)((LPBYTE)pLogFile->BaseAddress
|
|
+ pLogFile->BeginRecord);// Start at first record
|
|
|
|
EndRecord = (PVOID)((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)
|
|
{
|
|
Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
//skip this log file if error
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: SeekToStartingRecord(1) returned %d\r\n",
|
|
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
|
|
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)
|
|
{
|
|
Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
//skip this log file
|
|
//skip this log file if error
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: SeekToStartingRecord(2) returned 0x%08lx\r\n",
|
|
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
|
|
)) {
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: ValidFilePos(2) returned %d\r\n", Status));
|
|
goto process_nextlogfile;
|
|
}
|
|
|
|
//the end prop position
|
|
pEndPropPosition = (PBYTE)pEndPropPosition + (((PEVENTLOGRECORD)pEndPropPosition)->Length);
|
|
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: pStartPostion 0x%08lx pEndPosition 0x%08lx\r\n", pStartPropPosition, pEndPropPosition));
|
|
|
|
//if no records to propagate - skip the file
|
|
if (pStartPropPosition == pEndPropPosition)
|
|
goto process_nextlogfile;
|
|
|
|
if (pEndPropPosition > pStartPropPosition)
|
|
ulSize = (ULONG)((PBYTE)pEndPropPosition - (PBYTE)pStartPropPosition);
|
|
else {
|
|
ulSize = (ULONG)(((PBYTE)PhysicalEOF - (PBYTE)pStartPropPosition)) + (ULONG)(((PBYTE)pEndPropPosition - (PBYTE)PhysStart));
|
|
}
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: ulSize %d\r\n", ulSize));
|
|
|
|
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;
|
|
|
|
//SS: note that is the file is placed on this list, its lock is acquired
|
|
//and must be freed
|
|
//advance to the next log file
|
|
pLogFile = CONTAINING_RECORD(pLogFile->FileList.Flink, LOGFILE, FileList);
|
|
|
|
continue;
|
|
|
|
process_nextlogfile:
|
|
//release the lock on the old one
|
|
RtlReleaseResource(&pLogFile->Resource);
|
|
|
|
//advance to the next log file
|
|
pLogFile = CONTAINING_RECORD(pLogFile->FileList.Flink, LOGFILE, FileList);
|
|
}
|
|
|
|
//free the memory if unsuccessful or if numlogfiles is 0.
|
|
if (!(*pulNumLogFiles) && (pPropLogFileInfo)) {
|
|
ElfpFreeBuffer(pPropLogFileInfo);
|
|
pPropLogFileInfo = NULL;
|
|
}
|
|
FnExit:
|
|
*ppPropLogFileInfo = pPropLogFileInfo;
|
|
ElfDbgPrint(("[ELF] FindSizeofEventsSinceStart: ulTotalEventSize=%d ulNumLogfiles=%d pPropLogFileInfo=0x%08lx\r\n",
|
|
*pulTotalEventSize, *pulNumLogFiles, *ppPropLogFileInfo));
|
|
|
|
// Unlock the linked list
|
|
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)&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;
|
|
|
|
ElfDbgPrint(("[ELF] GetEventsToProp: Getting events for log file %ws\r\n", pPropLogFileInfo->pLogFile->LogFileName->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);
|
|
//shouldnt come here
|
|
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 = (PVOID)pPropLogFileInfo->pStartPosition;
|
|
ulBytesToMove = pPropLogFileInfo->ulTotalEventSize;
|
|
|
|
if (pPropLogFileInfo->pStartPosition > pPropLogFileInfo->pEndPosition) {
|
|
|
|
//find the end of this file
|
|
PhysicalEOF = (PVOID)((PBYTE)pPropLogFileInfo->pLogFile->BaseAddress + pPropLogFileInfo->pLogFile->ViewSize);
|
|
|
|
PhysicalStart = (PVOID)((PBYTE)pPropLogFileInfo->pLogFile->BaseAddress + FILEHEADERBUFSIZE);
|
|
//wraps- copy the first half
|
|
ulBytesToMove = (ULONG)((PBYTE)PhysicalEOF - (PBYTE)pPropLogFileInfo->pStartPosition);
|
|
RtlCopyMemory(BufferPosition, XferPosition, ulBytesToMove);
|
|
|
|
//set it up for the second half
|
|
BufferPosition = (PVOID)((PBYTE)BufferPosition + ulBytesToMove);
|
|
ulBytesToMove = pPropLogFileInfo->ulTotalEventSize - ulBytesToMove;
|
|
XferPosition = PhysicalStart;
|
|
}
|
|
|
|
RtlCopyMemory(BufferPosition, XferPosition, ulBytesToMove);
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|
|
//SS:end of changes made to enable cluster wide event logging
|
|
|
|
#if DBG
|
|
VOID
|
|
ElfErrorOut(
|
|
LPSTR ErrorText,
|
|
DWORD StatusCode,
|
|
PLOGFILE pLogFile)
|
|
{
|
|
DbgPrint("\n[EVENTLOG]: %s,0x%lx, \n\t%ws Log:\n"
|
|
"\tConfigMaxSize = 0x%lx, ActualMax = 0x%lx, BaseAddr = 0x%lx\n"
|
|
"\tViewSize = 0x%lx, EndRec = 0x%lx\n",
|
|
ErrorText,
|
|
StatusCode,
|
|
pLogFile->LogModuleName->Buffer,
|
|
pLogFile->ConfigMaxFileSize,
|
|
pLogFile->ActualMaxFileSize,
|
|
pLogFile->BaseAddress,
|
|
pLogFile->ViewSize,
|
|
pLogFile->EndRecord);
|
|
|
|
//DebugBreak();
|
|
}
|
|
#endif //DBG
|