mirror of
https://github.com/reactos/reactos.git
synced 2024-11-07 07:00:19 +00:00
1659 lines
55 KiB
C
1659 lines
55 KiB
C
/*
|
|
* PROJECT: ReactOS EventLog File Library
|
|
* LICENSE: GPL - See COPYING in the top level directory
|
|
* FILE: sdk/lib/evtlib/evtlib.c
|
|
* PURPOSE: Provides functionality for reading and writing
|
|
* EventLog files in the NT <= 5.2 (.evt) format.
|
|
* PROGRAMMERS: Copyright 2005 Saveliy Tretiakov
|
|
* Michael Martin
|
|
* Hermes Belusca-Maito
|
|
*/
|
|
|
|
/* INCLUDES ******************************************************************/
|
|
|
|
#include "evtlib.h"
|
|
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define EVTLTRACE(...) DPRINT("EvtLib: " __VA_ARGS__)
|
|
// Once things become stabilized enough, replace all the EVTLTRACE1 by EVTLTRACE
|
|
#define EVTLTRACE1(...) DPRINT1("EvtLib: " __VA_ARGS__)
|
|
|
|
|
|
/* GLOBALS *******************************************************************/
|
|
|
|
static const EVENTLOGEOF EOFRecord =
|
|
{
|
|
sizeof(EOFRecord),
|
|
0x11111111, 0x22222222, 0x33333333, 0x44444444,
|
|
0, 0, 0, 0,
|
|
sizeof(EOFRecord)
|
|
};
|
|
|
|
/* HELPER FUNCTIONS **********************************************************/
|
|
|
|
static NTSTATUS
|
|
ReadLogBuffer(
|
|
IN PEVTLOGFILE LogFile,
|
|
OUT PVOID Buffer,
|
|
IN SIZE_T Length,
|
|
OUT PSIZE_T ReadLength OPTIONAL,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
OUT PLARGE_INTEGER NextOffset OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset;
|
|
SIZE_T BufSize;
|
|
SIZE_T ReadBufLength = 0, OldReadBufLength;
|
|
|
|
ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize);
|
|
ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize);
|
|
|
|
if (ReadLength)
|
|
*ReadLength = 0;
|
|
|
|
if (NextOffset)
|
|
NextOffset->QuadPart = 0LL;
|
|
|
|
/* Read the first part of the buffer */
|
|
FileOffset = *ByteOffset;
|
|
BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart);
|
|
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
Buffer,
|
|
BufSize,
|
|
&ReadBufLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (Length > BufSize)
|
|
{
|
|
OldReadBufLength = ReadBufLength;
|
|
|
|
/*
|
|
* The buffer was splitted in two, its second part
|
|
* is to be read at the beginning of the log.
|
|
*/
|
|
Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize);
|
|
BufSize = Length - BufSize;
|
|
FileOffset.QuadPart = sizeof(EVENTLOGHEADER);
|
|
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
Buffer,
|
|
BufSize,
|
|
&ReadBufLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
/* Add the read number of bytes from the first read */
|
|
ReadBufLength += OldReadBufLength;
|
|
}
|
|
|
|
if (ReadLength)
|
|
*ReadLength = ReadBufLength;
|
|
|
|
/* We return the offset just after the end of the read buffer */
|
|
if (NextOffset)
|
|
NextOffset->QuadPart = FileOffset.QuadPart + BufSize;
|
|
|
|
return Status;
|
|
}
|
|
|
|
static NTSTATUS
|
|
WriteLogBuffer(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN PVOID Buffer,
|
|
IN SIZE_T Length,
|
|
OUT PSIZE_T WrittenLength OPTIONAL,
|
|
IN PLARGE_INTEGER ByteOffset,
|
|
OUT PLARGE_INTEGER NextOffset OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset;
|
|
SIZE_T BufSize;
|
|
SIZE_T WrittenBufLength = 0, OldWrittenBufLength;
|
|
|
|
/* We must have write access to the log file */
|
|
ASSERT(!LogFile->ReadOnly);
|
|
|
|
/*
|
|
* It is expected that the log file is already correctly expanded
|
|
* before we can write in it. Therefore the following assertions hold.
|
|
*/
|
|
ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize);
|
|
ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize);
|
|
|
|
if (WrittenLength)
|
|
*WrittenLength = 0;
|
|
|
|
if (NextOffset)
|
|
NextOffset->QuadPart = 0LL;
|
|
|
|
/* Write the first part of the buffer */
|
|
FileOffset = *ByteOffset;
|
|
BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart);
|
|
|
|
Status = LogFile->FileWrite(LogFile,
|
|
&FileOffset,
|
|
Buffer,
|
|
BufSize,
|
|
&WrittenBufLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
if (Length > BufSize)
|
|
{
|
|
OldWrittenBufLength = WrittenBufLength;
|
|
|
|
/*
|
|
* The buffer was splitted in two, its second part
|
|
* is written at the beginning of the log.
|
|
*/
|
|
Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize);
|
|
BufSize = Length - BufSize;
|
|
FileOffset.QuadPart = sizeof(EVENTLOGHEADER);
|
|
|
|
Status = LogFile->FileWrite(LogFile,
|
|
&FileOffset,
|
|
Buffer,
|
|
BufSize,
|
|
&WrittenBufLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
/* Add the written number of bytes from the first write */
|
|
WrittenBufLength += OldWrittenBufLength;
|
|
|
|
/* The log wraps */
|
|
LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
}
|
|
|
|
if (WrittenLength)
|
|
*WrittenLength = WrittenBufLength;
|
|
|
|
/* We return the offset just after the end of the written buffer */
|
|
if (NextOffset)
|
|
NextOffset->QuadPart = FileOffset.QuadPart + BufSize;
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* Returns 0 if nothing is found */
|
|
static ULONG
|
|
ElfpOffsetByNumber(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN ULONG RecordNumber)
|
|
{
|
|
UINT i;
|
|
|
|
for (i = 0; i < LogFile->OffsetInfoNext; i++)
|
|
{
|
|
if (LogFile->OffsetInfo[i].EventNumber == RecordNumber)
|
|
return LogFile->OffsetInfo[i].EventOffset;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#define OFFSET_INFO_INCREMENT 64
|
|
|
|
static BOOL
|
|
ElfpAddOffsetInformation(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN ULONG ulNumber,
|
|
IN ULONG ulOffset)
|
|
{
|
|
PVOID NewOffsetInfo;
|
|
|
|
if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize)
|
|
{
|
|
/* Allocate a new offset table */
|
|
NewOffsetInfo = LogFile->Allocate((LogFile->OffsetInfoSize + OFFSET_INFO_INCREMENT) *
|
|
sizeof(EVENT_OFFSET_INFO),
|
|
HEAP_ZERO_MEMORY,
|
|
TAG_ELF);
|
|
if (!NewOffsetInfo)
|
|
{
|
|
EVTLTRACE1("Cannot reallocate heap.\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Free the old offset table and use the new one */
|
|
if (LogFile->OffsetInfo)
|
|
{
|
|
/* Copy the handles from the old table to the new one */
|
|
RtlCopyMemory(NewOffsetInfo,
|
|
LogFile->OffsetInfo,
|
|
LogFile->OffsetInfoSize * sizeof(EVENT_OFFSET_INFO));
|
|
LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF);
|
|
}
|
|
LogFile->OffsetInfo = (PEVENT_OFFSET_INFO)NewOffsetInfo;
|
|
LogFile->OffsetInfoSize += OFFSET_INFO_INCREMENT;
|
|
}
|
|
|
|
LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventNumber = ulNumber;
|
|
LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventOffset = ulOffset;
|
|
LogFile->OffsetInfoNext++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static BOOL
|
|
ElfpDeleteOffsetInformation(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN ULONG ulNumberMin,
|
|
IN ULONG ulNumberMax)
|
|
{
|
|
UINT i;
|
|
|
|
if (ulNumberMin > ulNumberMax)
|
|
return FALSE;
|
|
|
|
/* Remove records ulNumberMin to ulNumberMax inclusive */
|
|
while (ulNumberMin <= ulNumberMax)
|
|
{
|
|
/*
|
|
* As the offset information is listed in increasing order, and we want
|
|
* to keep the list without holes, we demand that ulNumberMin is the first
|
|
* element in the list.
|
|
*/
|
|
if (ulNumberMin != LogFile->OffsetInfo[0].EventNumber)
|
|
return FALSE;
|
|
|
|
/*
|
|
* RtlMoveMemory(&LogFile->OffsetInfo[0],
|
|
* &LogFile->OffsetInfo[1],
|
|
* sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1));
|
|
*/
|
|
for (i = 0; i < LogFile->OffsetInfoNext - 1; i++)
|
|
{
|
|
LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber;
|
|
LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset;
|
|
}
|
|
LogFile->OffsetInfoNext--;
|
|
|
|
/* Go to the next offset information */
|
|
ulNumberMin++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static NTSTATUS
|
|
ElfpInitNewFile(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN ULONG FileSize,
|
|
IN ULONG MaxSize,
|
|
IN ULONG Retention)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset;
|
|
SIZE_T WrittenLength;
|
|
EVENTLOGEOF EofRec;
|
|
|
|
/* Initialize the event log header */
|
|
RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER));
|
|
|
|
LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER);
|
|
LogFile->Header.Signature = LOGFILE_SIGNATURE;
|
|
LogFile->Header.MajorVersion = MAJORVER;
|
|
LogFile->Header.MinorVersion = MINORVER;
|
|
|
|
/* Set the offset to the oldest record */
|
|
LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
|
|
/* Set the offset to the ELF_EOF_RECORD */
|
|
LogFile->Header.EndOffset = sizeof(EVENTLOGHEADER);
|
|
/* Set the number of the next record that will be added */
|
|
LogFile->Header.CurrentRecordNumber = 1;
|
|
/* The event log is empty, there is no record so far */
|
|
LogFile->Header.OldestRecordNumber = 0;
|
|
|
|
// FIXME: Windows' EventLog log file sizes are always multiple of 64kB
|
|
// but that does not mean the real log size is == file size.
|
|
|
|
/* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */
|
|
LogFile->Header.MaxSize = ROUND_UP(MaxSize, sizeof(ULONG));
|
|
LogFile->CurrentSize = LogFile->Header.MaxSize; // or: FileSize ??
|
|
LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
|
|
|
|
LogFile->Header.Flags = 0;
|
|
LogFile->Header.Retention = Retention;
|
|
LogFile->Header.EndHeaderSize = sizeof(EVENTLOGHEADER);
|
|
|
|
/* Write the header */
|
|
FileOffset.QuadPart = 0LL;
|
|
Status = LogFile->FileWrite(LogFile,
|
|
&FileOffset,
|
|
&LogFile->Header,
|
|
sizeof(EVENTLOGHEADER),
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Initialize the ELF_EOF_RECORD and write it */
|
|
RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
|
|
EofRec.BeginRecord = LogFile->Header.StartOffset;
|
|
EofRec.EndRecord = LogFile->Header.EndOffset;
|
|
EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
|
|
EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
|
|
|
|
Status = LogFile->FileWrite(LogFile,
|
|
NULL,
|
|
&EofRec,
|
|
sizeof(EofRec),
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
Status = LogFile->FileFlush(LogFile, NULL, 0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static NTSTATUS
|
|
ElfpInitExistingFile(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN ULONG FileSize,
|
|
// IN ULONG MaxSize,
|
|
IN ULONG Retention)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset, NextOffset;
|
|
SIZE_T ReadLength;
|
|
ULONG RecordNumber = 0;
|
|
ULONG RecOffset;
|
|
PULONG pRecSize2;
|
|
EVENTLOGEOF EofRec;
|
|
EVENTLOGRECORD RecBuf;
|
|
PEVENTLOGRECORD pRecBuf;
|
|
BOOLEAN Wrapping = FALSE;
|
|
BOOLEAN IsLogDirty = FALSE;
|
|
|
|
/* Read the log header */
|
|
FileOffset.QuadPart = 0LL;
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
&LogFile->Header,
|
|
sizeof(EVENTLOGHEADER),
|
|
&ReadLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT; // return Status;
|
|
}
|
|
if (ReadLength != sizeof(EVENTLOGHEADER))
|
|
{
|
|
EVTLTRACE("Invalid file `%wZ'.\n", &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
/* Header validity checks */
|
|
|
|
if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) ||
|
|
LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER))
|
|
{
|
|
EVTLTRACE("Invalid header size in `%wZ'.\n", &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
if (LogFile->Header.Signature != LOGFILE_SIGNATURE)
|
|
{
|
|
EVTLTRACE("Invalid signature %x in `%wZ'.\n",
|
|
LogFile->Header.Signature, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY);
|
|
|
|
/* If the log is read-only (e.g. a backup log) and is dirty, then it is corrupted */
|
|
if (LogFile->ReadOnly && IsLogDirty)
|
|
{
|
|
EVTLTRACE("Read-only log `%wZ' is dirty.\n", &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
LogFile->CurrentSize = FileSize;
|
|
// FIXME!! What to do? And what to do if the MaxSize from the registry
|
|
// is strictly less than the CurrentSize?? Should we "reduce" the log size
|
|
// by clearing it completely??
|
|
// --> ANSWER: Save the new MaxSize somewhere, and only when the log is
|
|
// being cleared, use the new MaxSize to resize (ie. shrink) it.
|
|
// LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
|
|
|
|
/* Adjust the log maximum size if needed */
|
|
if (LogFile->CurrentSize > LogFile->Header.MaxSize)
|
|
LogFile->Header.MaxSize = LogFile->CurrentSize;
|
|
|
|
/*
|
|
* Reset the log retention value. The value stored
|
|
* in the log file is just for information purposes.
|
|
*/
|
|
LogFile->Header.Retention = Retention;
|
|
|
|
/*
|
|
* For a non-read-only dirty log, the most up-to-date information about
|
|
* the Start/End offsets and the Oldest and Current event record numbers
|
|
* are found in the EOF record. We need to locate the EOF record without
|
|
* relying on the log header's EndOffset, then patch the log header with
|
|
* the values from the EOF record.
|
|
*/
|
|
if ((LogFile->Header.EndOffset >= sizeof(EVENTLOGHEADER)) &&
|
|
(LogFile->Header.EndOffset < LogFile->CurrentSize) &&
|
|
(LogFile->Header.EndOffset & 3) == 0) // EndOffset % sizeof(ULONG) == 0
|
|
{
|
|
/* The header EOF offset may be valid, try to start with it */
|
|
RecOffset = LogFile->Header.EndOffset;
|
|
}
|
|
else
|
|
{
|
|
/* The header EOF offset could not be valid, so start from the beginning */
|
|
RecOffset = sizeof(EVENTLOGHEADER);
|
|
}
|
|
|
|
FileOffset.QuadPart = RecOffset;
|
|
Wrapping = FALSE;
|
|
|
|
for (;;)
|
|
{
|
|
if (Wrapping && FileOffset.QuadPart >= RecOffset)
|
|
{
|
|
EVTLTRACE1("EOF record not found!\n");
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
/* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */
|
|
Status = ReadLogBuffer(LogFile,
|
|
&EofRec,
|
|
EVENTLOGEOF_SIZE_FIXED,
|
|
&ReadLength,
|
|
&FileOffset,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
if (ReadLength != EVENTLOGEOF_SIZE_FIXED)
|
|
{
|
|
EVTLTRACE1("Cannot read at most an EOF record!\n");
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
/* Is it an EVENTLOGEOF record? */
|
|
if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED)
|
|
{
|
|
DPRINT("Found EOF record at %llx\n", FileOffset.QuadPart);
|
|
|
|
/* Got it! Break the loop and continue */
|
|
break;
|
|
}
|
|
|
|
/* No, continue looping */
|
|
if (*(PULONG)((ULONG_PTR)&EofRec + sizeof(ULONG)) == *(PULONG)(&EOFRecord))
|
|
FileOffset.QuadPart += sizeof(ULONG);
|
|
else
|
|
if (*(PULONG)((ULONG_PTR)&EofRec + 2*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
|
|
FileOffset.QuadPart += 2*sizeof(ULONG);
|
|
else
|
|
if (*(PULONG)((ULONG_PTR)&EofRec + 3*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
|
|
FileOffset.QuadPart += 3*sizeof(ULONG);
|
|
else
|
|
if (*(PULONG)((ULONG_PTR)&EofRec + 4*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
|
|
FileOffset.QuadPart += 4*sizeof(ULONG);
|
|
else
|
|
FileOffset.QuadPart += 5*sizeof(ULONG); // EVENTLOGEOF_SIZE_FIXED
|
|
|
|
if (FileOffset.QuadPart >= LogFile->CurrentSize /* LogFile->Header.MaxSize */)
|
|
{
|
|
/* Wrap the offset */
|
|
FileOffset.QuadPart -= LogFile->CurrentSize /* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER);
|
|
Wrapping = TRUE;
|
|
}
|
|
}
|
|
/*
|
|
* The only way to be there is to have found a valid EOF record.
|
|
* Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT
|
|
* was returned.
|
|
*/
|
|
|
|
/* Read the full EVENTLOGEOF (may wrap) and validate it */
|
|
Status = ReadLogBuffer(LogFile,
|
|
&EofRec,
|
|
sizeof(EofRec),
|
|
&ReadLength,
|
|
&FileOffset,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
if (ReadLength != sizeof(EofRec))
|
|
{
|
|
EVTLTRACE1("Cannot read the full EOF record!\n");
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
/* Complete validity checks */
|
|
if ((EofRec.RecordSizeEnd != EofRec.RecordSizeBeginning) ||
|
|
(EofRec.EndRecord != FileOffset.QuadPart))
|
|
{
|
|
DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected 0x%x 0x%x!\n",
|
|
FileOffset.QuadPart,
|
|
EofRec.RecordSizeEnd, EofRec.RecordSizeBeginning,
|
|
EofRec.EndRecord, FileOffset.QuadPart,
|
|
EOFRecord.RecordSizeEnd, EOFRecord.RecordSizeBeginning);
|
|
DPRINT1("RecordSizeEnd = 0x%x\n", EofRec.RecordSizeEnd);
|
|
DPRINT1("RecordSizeBeginning = 0x%x\n", EofRec.RecordSizeBeginning);
|
|
DPRINT1("EndRecord = 0x%x\n", EofRec.EndRecord);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
/* The EOF record is valid, break the loop and continue */
|
|
|
|
/* If the log is not dirty, the header values should correspond to the EOF ones */
|
|
if (!IsLogDirty)
|
|
{
|
|
if ( (LogFile->Header.StartOffset != EofRec.BeginRecord) ||
|
|
(LogFile->Header.EndOffset != EofRec.EndRecord) ||
|
|
(LogFile->Header.CurrentRecordNumber != EofRec.CurrentRecordNumber) ||
|
|
(LogFile->Header.OldestRecordNumber != EofRec.OldestRecordNumber) )
|
|
{
|
|
DPRINT1("\n"
|
|
"Log header or EOF record is corrupted:\n"
|
|
" StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n"
|
|
" CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n",
|
|
LogFile->Header.StartOffset, EofRec.BeginRecord,
|
|
LogFile->Header.EndOffset , EofRec.EndRecord,
|
|
LogFile->Header.CurrentRecordNumber, EofRec.CurrentRecordNumber,
|
|
LogFile->Header.OldestRecordNumber , EofRec.OldestRecordNumber);
|
|
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
}
|
|
|
|
/* If the log is dirty, patch the log header with the values from the EOF record */
|
|
if (!LogFile->ReadOnly && IsLogDirty)
|
|
{
|
|
LogFile->Header.StartOffset = EofRec.BeginRecord;
|
|
LogFile->Header.EndOffset = EofRec.EndRecord;
|
|
LogFile->Header.CurrentRecordNumber = EofRec.CurrentRecordNumber;
|
|
LogFile->Header.OldestRecordNumber = EofRec.OldestRecordNumber;
|
|
}
|
|
|
|
/*
|
|
* FIXME! During operations the EOF record is the one that is the most
|
|
* updated (its Oldest & Current record numbers are always up-to
|
|
* date) while the ones from the header may be unsync. When closing
|
|
* (or flushing?) the event log, the header's record numbers get
|
|
* updated with the same values as the ones stored in the EOF record.
|
|
*/
|
|
|
|
/* Verify Start/End offsets boundaries */
|
|
|
|
if ((LogFile->Header.StartOffset >= LogFile->CurrentSize) ||
|
|
(LogFile->Header.StartOffset & 3) != 0) // StartOffset % sizeof(ULONG) != 0
|
|
{
|
|
EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n",
|
|
LogFile->Header.StartOffset, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
if ((LogFile->Header.EndOffset >= LogFile->CurrentSize) ||
|
|
(LogFile->Header.EndOffset & 3) != 0) // EndOffset % sizeof(ULONG) != 0
|
|
{
|
|
EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
|
|
LogFile->Header.EndOffset, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) &&
|
|
(LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD)))
|
|
{
|
|
/*
|
|
* If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF,
|
|
* it should point to a non-splitted EVENTLOGRECORD.
|
|
*/
|
|
EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n",
|
|
LogFile->Header.StartOffset, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
|
|
(LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD)))
|
|
{
|
|
/*
|
|
* In non-wrapping case, there must be enough space between StartOffset
|
|
* and EndOffset to contain at least a full EVENTLOGRECORD.
|
|
*/
|
|
EVTLTRACE("Invalid start offset 0x%x or end offset 0x%x in `%wZ'.\n",
|
|
LogFile->Header.StartOffset, LogFile->Header.EndOffset, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
|
|
{
|
|
/*
|
|
* Non-wrapping case: the (wrapping) free space starting at EndOffset
|
|
* must be able to contain an EVENTLOGEOF.
|
|
*/
|
|
if (LogFile->Header.MaxSize - LogFile->Header.EndOffset +
|
|
LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER) < sizeof(EVENTLOGEOF))
|
|
{
|
|
EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
|
|
LogFile->Header.EndOffset, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
}
|
|
else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
|
|
{
|
|
/*
|
|
* Wrapping case: the free space between EndOffset and StartOffset
|
|
* must be able to contain an EVENTLOGEOF.
|
|
*/
|
|
if (LogFile->Header.StartOffset - LogFile->Header.EndOffset < sizeof(EVENTLOGEOF))
|
|
{
|
|
EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
|
|
LogFile->Header.EndOffset, &LogFile->FileName);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
}
|
|
|
|
/* Start enumerating the event records from the beginning */
|
|
RecOffset = LogFile->Header.StartOffset;
|
|
FileOffset.QuadPart = RecOffset;
|
|
Wrapping = FALSE;
|
|
|
|
// // FIXME! FIXME!
|
|
// if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP))
|
|
// {
|
|
// DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n");
|
|
// LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
// }
|
|
|
|
DPRINT("StartOffset = 0x%x, EndOffset = 0x%x\n",
|
|
LogFile->Header.StartOffset, LogFile->Header.EndOffset);
|
|
|
|
/*
|
|
* For non-read-only logs of size < MaxSize, reorganize the events
|
|
* such that they do not wrap as soon as we write new ones.
|
|
*/
|
|
#if 0
|
|
if (!LogFile->ReadOnly)
|
|
{
|
|
pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
|
|
if (pRecBuf == NULL)
|
|
{
|
|
DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n");
|
|
goto Continue;
|
|
}
|
|
|
|
// TODO: Do the job!
|
|
}
|
|
|
|
Continue:
|
|
|
|
DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n",
|
|
LogFile->Header.StartOffset, LogFile->Header.EndOffset);
|
|
#endif
|
|
|
|
while (FileOffset.QuadPart != LogFile->Header.EndOffset)
|
|
{
|
|
if (Wrapping && FileOffset.QuadPart >= RecOffset)
|
|
{
|
|
/* We have finished enumerating all the event records */
|
|
break;
|
|
}
|
|
|
|
/* Read the next EVENTLOGRECORD header at once (it cannot be split) */
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
&RecBuf,
|
|
sizeof(RecBuf),
|
|
&ReadLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
if (ReadLength != sizeof(RecBuf))
|
|
{
|
|
DPRINT1("Length != sizeof(RecBuf)\n");
|
|
break;
|
|
}
|
|
|
|
if (RecBuf.Reserved != LOGFILE_SIGNATURE ||
|
|
RecBuf.Length < sizeof(EVENTLOGRECORD))
|
|
{
|
|
DPRINT1("RecBuf problem\n");
|
|
break;
|
|
}
|
|
|
|
/* Allocate a full EVENTLOGRECORD (header + data) */
|
|
pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
|
|
if (pRecBuf == NULL)
|
|
{
|
|
EVTLTRACE1("Cannot allocate heap!\n");
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
/* Attempt to read the full EVENTLOGRECORD (can wrap) */
|
|
Status = ReadLogBuffer(LogFile,
|
|
pRecBuf,
|
|
RecBuf.Length,
|
|
&ReadLength,
|
|
&FileOffset,
|
|
&NextOffset);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
if (ReadLength != RecBuf.Length)
|
|
{
|
|
DPRINT1("Oh oh!!\n");
|
|
LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
|
|
break;
|
|
}
|
|
|
|
// /* If OverWrittenRecords is TRUE and this record has already been read */
|
|
// if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber))
|
|
// {
|
|
// LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
|
|
// break;
|
|
// }
|
|
|
|
pRecSize2 = (PULONG)((ULONG_PTR)pRecBuf + RecBuf.Length - 4);
|
|
|
|
if (*pRecSize2 != RecBuf.Length)
|
|
{
|
|
EVTLTRACE1("Invalid RecordSizeEnd of record %d (0x%x) in `%wZ'\n",
|
|
RecordNumber, *pRecSize2, &LogFile->FileName);
|
|
LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
|
|
break;
|
|
}
|
|
|
|
EVTLTRACE("Add new record %d @ offset 0x%x\n", pRecBuf->RecordNumber, FileOffset.QuadPart);
|
|
|
|
RecordNumber++;
|
|
|
|
if (!ElfpAddOffsetInformation(LogFile,
|
|
pRecBuf->RecordNumber,
|
|
FileOffset.QuadPart))
|
|
{
|
|
EVTLTRACE1("ElfpAddOffsetInformation() failed!\n");
|
|
LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
|
|
|
|
if (NextOffset.QuadPart == LogFile->Header.EndOffset)
|
|
{
|
|
/* We have finished enumerating all the event records */
|
|
DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n");
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* If this was the last event record before the end of the log file,
|
|
* the next one should start at the beginning of the log and the space
|
|
* between the last event record and the end of the file is padded.
|
|
*/
|
|
if (LogFile->Header.MaxSize - NextOffset.QuadPart < sizeof(EVENTLOGRECORD))
|
|
{
|
|
/* Wrap to the beginning of the log */
|
|
DPRINT("Wrap!\n");
|
|
NextOffset.QuadPart = sizeof(EVENTLOGHEADER);
|
|
}
|
|
|
|
/*
|
|
* If the next offset to read is below the current offset,
|
|
* this means we are wrapping.
|
|
*/
|
|
if (FileOffset.QuadPart > NextOffset.QuadPart)
|
|
{
|
|
DPRINT("Wrapping = TRUE;\n");
|
|
Wrapping = TRUE;
|
|
}
|
|
|
|
/* Move the current offset */
|
|
FileOffset = NextOffset;
|
|
}
|
|
|
|
/* If the event log was empty, it will now contain one record */
|
|
if (RecordNumber != 0 && LogFile->Header.OldestRecordNumber == 0)
|
|
LogFile->Header.OldestRecordNumber = 1;
|
|
|
|
LogFile->Header.CurrentRecordNumber = RecordNumber + LogFile->Header.OldestRecordNumber;
|
|
if (LogFile->Header.CurrentRecordNumber == 0)
|
|
LogFile->Header.CurrentRecordNumber = 1;
|
|
|
|
/* Flush the log if it is not read-only */
|
|
if (!LogFile->ReadOnly)
|
|
{
|
|
Status = ElfFlushFile(LogFile);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT; // Status;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/* FUNCTIONS *****************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ElfCreateFile(
|
|
IN OUT PEVTLOGFILE LogFile,
|
|
IN PUNICODE_STRING FileName OPTIONAL,
|
|
IN ULONG FileSize,
|
|
IN ULONG MaxSize,
|
|
IN ULONG Retention,
|
|
IN BOOLEAN CreateNew,
|
|
IN BOOLEAN ReadOnly,
|
|
IN PELF_ALLOCATE_ROUTINE Allocate,
|
|
IN PELF_FREE_ROUTINE Free,
|
|
IN PELF_FILE_SET_SIZE_ROUTINE FileSetSize,
|
|
IN PELF_FILE_WRITE_ROUTINE FileWrite,
|
|
IN PELF_FILE_READ_ROUTINE FileRead,
|
|
IN PELF_FILE_FLUSH_ROUTINE FileFlush) // What about Seek ??
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ASSERT(LogFile);
|
|
|
|
/* Creating a new log file with the 'ReadOnly' flag set is incompatible */
|
|
if (CreateNew && ReadOnly)
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
RtlZeroMemory(LogFile, sizeof(*LogFile));
|
|
|
|
LogFile->Allocate = Allocate;
|
|
LogFile->Free = Free;
|
|
LogFile->FileSetSize = FileSetSize;
|
|
LogFile->FileWrite = FileWrite;
|
|
LogFile->FileRead = FileRead;
|
|
LogFile->FileFlush = FileFlush;
|
|
|
|
/* Copy the log file name if provided (optional) */
|
|
RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0);
|
|
if (FileName && FileName->Buffer && FileName->Length &&
|
|
(FileName->Length <= FileName->MaximumLength))
|
|
{
|
|
LogFile->FileName.Buffer = LogFile->Allocate(FileName->Length,
|
|
HEAP_ZERO_MEMORY,
|
|
TAG_ELF);
|
|
if (LogFile->FileName.Buffer)
|
|
{
|
|
LogFile->FileName.MaximumLength = FileName->Length;
|
|
RtlCopyUnicodeString(&LogFile->FileName, FileName);
|
|
}
|
|
}
|
|
|
|
LogFile->OffsetInfo = LogFile->Allocate(OFFSET_INFO_INCREMENT * sizeof(EVENT_OFFSET_INFO),
|
|
HEAP_ZERO_MEMORY,
|
|
TAG_ELF);
|
|
if (LogFile->OffsetInfo == NULL)
|
|
{
|
|
EVTLTRACE1("Cannot allocate heap\n");
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Quit;
|
|
}
|
|
LogFile->OffsetInfoSize = OFFSET_INFO_INCREMENT;
|
|
LogFile->OffsetInfoNext = 0;
|
|
|
|
// FIXME: Always use the regitry values for MaxSize,
|
|
// even for existing logs!
|
|
|
|
// FIXME: On Windows, EventLog uses the MaxSize setting
|
|
// from the registry itself; the MaxSize from the header
|
|
// is just for information purposes.
|
|
|
|
EVTLTRACE("Initializing log file `%wZ'\n", &LogFile->FileName);
|
|
|
|
LogFile->ReadOnly = ReadOnly; // !CreateNew && ReadOnly;
|
|
|
|
if (CreateNew)
|
|
Status = ElfpInitNewFile(LogFile, FileSize, MaxSize, Retention);
|
|
else
|
|
Status = ElfpInitExistingFile(LogFile, FileSize, /* MaxSize, */ Retention);
|
|
|
|
Quit:
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
if (LogFile->OffsetInfo)
|
|
LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF);
|
|
|
|
if (LogFile->FileName.Buffer)
|
|
LogFile->Free(LogFile->FileName.Buffer, 0, TAG_ELF);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ElfReCreateFile(
|
|
IN PEVTLOGFILE LogFile)
|
|
{
|
|
ASSERT(LogFile);
|
|
|
|
return ElfpInitNewFile(LogFile,
|
|
LogFile->CurrentSize,
|
|
LogFile->Header.MaxSize,
|
|
LogFile->Header.Retention);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ElfBackupFile(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN PEVTLOGFILE BackupLogFile)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
LARGE_INTEGER FileOffset;
|
|
SIZE_T ReadLength, WrittenLength;
|
|
PEVENTLOGHEADER Header;
|
|
EVENTLOGRECORD RecBuf;
|
|
EVENTLOGEOF EofRec;
|
|
ULONG i;
|
|
ULONG RecOffset;
|
|
PVOID Buffer = NULL;
|
|
|
|
ASSERT(LogFile);
|
|
|
|
RtlZeroMemory(BackupLogFile, sizeof(*BackupLogFile));
|
|
|
|
BackupLogFile->FileSetSize = LogFile->FileSetSize;
|
|
BackupLogFile->FileWrite = LogFile->FileWrite;
|
|
BackupLogFile->FileFlush = LogFile->FileFlush;
|
|
|
|
// BackupLogFile->CurrentSize = LogFile->CurrentSize;
|
|
|
|
BackupLogFile->ReadOnly = FALSE;
|
|
|
|
/* Initialize the (dirty) log file header */
|
|
Header = &BackupLogFile->Header;
|
|
Header->HeaderSize = sizeof(EVENTLOGHEADER);
|
|
Header->Signature = LOGFILE_SIGNATURE;
|
|
Header->MajorVersion = MAJORVER;
|
|
Header->MinorVersion = MINORVER;
|
|
Header->StartOffset = sizeof(EVENTLOGHEADER);
|
|
Header->EndOffset = sizeof(EVENTLOGHEADER);
|
|
Header->CurrentRecordNumber = 1;
|
|
Header->OldestRecordNumber = 0;
|
|
Header->MaxSize = LogFile->Header.MaxSize;
|
|
Header->Flags = ELF_LOGFILE_HEADER_DIRTY;
|
|
Header->Retention = LogFile->Header.Retention;
|
|
Header->EndHeaderSize = sizeof(EVENTLOGHEADER);
|
|
|
|
/* Write the (dirty) log file header */
|
|
FileOffset.QuadPart = 0LL;
|
|
Status = BackupLogFile->FileWrite(BackupLogFile,
|
|
&FileOffset,
|
|
Header,
|
|
sizeof(EVENTLOGHEADER),
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("Failed to write the log file header (Status 0x%08lx)\n", Status);
|
|
goto Quit;
|
|
}
|
|
|
|
for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++)
|
|
{
|
|
RecOffset = ElfpOffsetByNumber(LogFile, i);
|
|
if (RecOffset == 0)
|
|
break;
|
|
|
|
/* Read the next EVENTLOGRECORD header at once (it cannot be split) */
|
|
FileOffset.QuadPart = RecOffset;
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
&RecBuf,
|
|
sizeof(RecBuf),
|
|
&ReadLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
goto Quit;
|
|
}
|
|
|
|
// if (ReadLength != sizeof(RecBuf))
|
|
// break;
|
|
|
|
Buffer = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
|
|
if (Buffer == NULL)
|
|
{
|
|
EVTLTRACE1("Allocate() failed!\n");
|
|
goto Quit;
|
|
}
|
|
|
|
/* Read the full EVENTLOGRECORD (header + data) with wrapping */
|
|
Status = ReadLogBuffer(LogFile,
|
|
Buffer,
|
|
RecBuf.Length,
|
|
&ReadLength,
|
|
&FileOffset,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
LogFile->Free(Buffer, 0, TAG_ELF_BUF);
|
|
// Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
goto Quit;
|
|
}
|
|
|
|
/* Write the event record (no wrap for the backup log) */
|
|
Status = BackupLogFile->FileWrite(BackupLogFile,
|
|
NULL,
|
|
Buffer,
|
|
RecBuf.Length,
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
LogFile->Free(Buffer, 0, TAG_ELF_BUF);
|
|
goto Quit;
|
|
}
|
|
|
|
/* Update the header information */
|
|
Header->EndOffset += RecBuf.Length;
|
|
|
|
/* Free the buffer */
|
|
LogFile->Free(Buffer, 0, TAG_ELF_BUF);
|
|
Buffer = NULL;
|
|
}
|
|
|
|
// Quit:
|
|
|
|
/* Initialize the ELF_EOF_RECORD and write it (no wrap for the backup log) */
|
|
RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
|
|
EofRec.BeginRecord = Header->StartOffset;
|
|
EofRec.EndRecord = Header->EndOffset;
|
|
EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
|
|
EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
|
|
|
|
Status = BackupLogFile->FileWrite(BackupLogFile,
|
|
NULL,
|
|
&EofRec,
|
|
sizeof(EofRec),
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
goto Quit;
|
|
}
|
|
|
|
/* Update the header information */
|
|
Header->CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
|
|
Header->OldestRecordNumber = LogFile->Header.OldestRecordNumber;
|
|
Header->MaxSize = ROUND_UP(Header->EndOffset + sizeof(EofRec), sizeof(ULONG));
|
|
Header->Flags = 0; // FIXME?
|
|
|
|
/* Flush the log file - Write the (clean) log file header */
|
|
Status = ElfFlushFile(BackupLogFile);
|
|
|
|
Quit:
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ElfFlushFile(
|
|
IN PEVTLOGFILE LogFile)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset;
|
|
SIZE_T WrittenLength;
|
|
|
|
ASSERT(LogFile);
|
|
|
|
if (LogFile->ReadOnly)
|
|
return STATUS_SUCCESS; // STATUS_ACCESS_DENIED;
|
|
|
|
/*
|
|
* NOTE that both the EOF record *AND* the log file header
|
|
* are supposed to be already updated!
|
|
* We just remove the dirty log bit.
|
|
*/
|
|
LogFile->Header.Flags &= ~ELF_LOGFILE_HEADER_DIRTY;
|
|
|
|
/* Update the log file header */
|
|
FileOffset.QuadPart = 0LL;
|
|
Status = LogFile->FileWrite(LogFile,
|
|
&FileOffset,
|
|
&LogFile->Header,
|
|
sizeof(EVENTLOGHEADER),
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Flush the log file */
|
|
Status = LogFile->FileFlush(LogFile, NULL, 0);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
ElfCloseFile( // ElfFree
|
|
IN PEVTLOGFILE LogFile)
|
|
{
|
|
ASSERT(LogFile);
|
|
|
|
/* Flush the log file */
|
|
ElfFlushFile(LogFile);
|
|
|
|
/* Free the data */
|
|
LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF);
|
|
|
|
if (LogFile->FileName.Buffer)
|
|
LogFile->Free(LogFile->FileName.Buffer, 0, TAG_ELF);
|
|
RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0);
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ElfReadRecord(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN ULONG RecordNumber,
|
|
OUT PEVENTLOGRECORD Record,
|
|
IN SIZE_T BufSize, // Length
|
|
OUT PSIZE_T BytesRead OPTIONAL,
|
|
OUT PSIZE_T BytesNeeded OPTIONAL)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset;
|
|
ULONG RecOffset;
|
|
SIZE_T RecSize;
|
|
SIZE_T ReadLength;
|
|
|
|
ASSERT(LogFile);
|
|
|
|
if (BytesRead)
|
|
*BytesRead = 0;
|
|
|
|
if (BytesNeeded)
|
|
*BytesNeeded = 0;
|
|
|
|
/* Retrieve the offset of the event record */
|
|
RecOffset = ElfpOffsetByNumber(LogFile, RecordNumber);
|
|
if (RecOffset == 0)
|
|
return STATUS_NOT_FOUND;
|
|
|
|
/* Retrieve its full size */
|
|
FileOffset.QuadPart = RecOffset;
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
&RecSize,
|
|
sizeof(RecSize),
|
|
&ReadLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
// Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
return Status;
|
|
}
|
|
|
|
/* Check whether the buffer is big enough to hold the event record */
|
|
if (BufSize < RecSize)
|
|
{
|
|
if (BytesNeeded)
|
|
*BytesNeeded = RecSize;
|
|
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
/* Read the event record into the buffer */
|
|
FileOffset.QuadPart = RecOffset;
|
|
Status = ReadLogBuffer(LogFile,
|
|
Record,
|
|
RecSize,
|
|
&ReadLength,
|
|
&FileOffset,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
// Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
if (BytesRead)
|
|
*BytesRead = ReadLength;
|
|
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
ElfWriteRecord(
|
|
IN PEVTLOGFILE LogFile,
|
|
IN PEVENTLOGRECORD Record,
|
|
IN SIZE_T BufSize)
|
|
{
|
|
NTSTATUS Status;
|
|
LARGE_INTEGER FileOffset, NextOffset;
|
|
SIZE_T ReadLength, WrittenLength;
|
|
EVENTLOGEOF EofRec;
|
|
EVENTLOGRECORD RecBuf;
|
|
ULONG FreeSpace = 0;
|
|
ULONG UpperBound;
|
|
ULONG RecOffset, WriteOffset;
|
|
|
|
ASSERT(LogFile);
|
|
|
|
if (LogFile->ReadOnly)
|
|
return STATUS_ACCESS_DENIED;
|
|
|
|
// ASSERT(sizeof(*Record) == sizeof(RecBuf));
|
|
|
|
if (!Record || BufSize < sizeof(*Record))
|
|
return STATUS_INVALID_PARAMETER;
|
|
|
|
Record->RecordNumber = LogFile->Header.CurrentRecordNumber;
|
|
|
|
/* Compute the available log free space */
|
|
if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
|
|
FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER);
|
|
else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
|
|
FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset;
|
|
|
|
LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY;
|
|
|
|
/* If the event log was empty, it will now contain one record */
|
|
if (LogFile->Header.OldestRecordNumber == 0)
|
|
LogFile->Header.OldestRecordNumber = 1;
|
|
|
|
/* By default we append the new record at the old EOF record offset */
|
|
WriteOffset = LogFile->Header.EndOffset;
|
|
|
|
/*
|
|
* Check whether the log is going to wrap (the events being overwritten).
|
|
*/
|
|
|
|
if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
|
|
UpperBound = LogFile->Header.MaxSize;
|
|
else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
|
|
UpperBound = LogFile->Header.StartOffset;
|
|
|
|
// if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
|
|
if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
|
|
{
|
|
EVTLTRACE("The event log file has reached maximum size (0x%x), wrapping...\n"
|
|
"UpperBound = 0x%x, WriteOffset = 0x%x, BufSize = 0x%x\n",
|
|
LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize);
|
|
/* This will be done later */
|
|
}
|
|
|
|
if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
|
|
(LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
|
|
{
|
|
// ASSERT(UpperBound == LogFile->Header.MaxSize);
|
|
// ASSERT(WriteOffset == LogFile->Header.EndOffset);
|
|
|
|
/*
|
|
* We cannot fit the EVENTLOGRECORD header of the buffer before
|
|
* the end of the file. We need to pad the end of the log with
|
|
* 0x00000027, normally we will need to pad at most 0x37 bytes
|
|
* (corresponding to sizeof(EVENTLOGRECORD) - 1).
|
|
*/
|
|
|
|
/* Rewind to the beginning of the log, just after the header */
|
|
WriteOffset = sizeof(EVENTLOGHEADER);
|
|
/**/UpperBound = LogFile->Header.StartOffset;/**/
|
|
|
|
FreeSpace = LogFile->Header.StartOffset - WriteOffset;
|
|
|
|
LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
}
|
|
/*
|
|
* Otherwise, we can fit the header and only part
|
|
* of the data will overwrite the oldest records.
|
|
*
|
|
* It might be possible that all the event record can fit in one piece,
|
|
* but that the EOF record needs to be split. This is not a problem,
|
|
* EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be.
|
|
*/
|
|
|
|
if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
|
|
{
|
|
ULONG OrgOldestRecordNumber, OldestRecordNumber;
|
|
|
|
// DPRINT("EventLogFile has reached maximum size, wrapping...\n");
|
|
|
|
OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber;
|
|
|
|
// FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record???
|
|
// NOTE: It should be, by construction (and this should have been checked when
|
|
// initializing a new, or existing log).
|
|
|
|
/*
|
|
* Determine how many old records need to be overwritten.
|
|
* Check the size of the record as the record added may be larger.
|
|
* Need to take into account that we append the EOF record.
|
|
*/
|
|
while (FreeSpace < BufSize + sizeof(EofRec))
|
|
{
|
|
/* Get the oldest record data */
|
|
RecOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber);
|
|
if (RecOffset == 0)
|
|
{
|
|
EVTLTRACE1("Record number %d cannot be found, or log file is full and cannot wrap!\n", OldestRecordNumber);
|
|
LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
|
|
return STATUS_LOG_FILE_FULL;
|
|
}
|
|
|
|
RtlZeroMemory(&RecBuf, sizeof(RecBuf));
|
|
|
|
FileOffset.QuadPart = RecOffset;
|
|
Status = LogFile->FileRead(LogFile,
|
|
&FileOffset,
|
|
&RecBuf,
|
|
sizeof(RecBuf),
|
|
&ReadLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
|
|
// Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
return Status;
|
|
}
|
|
|
|
if (RecBuf.Reserved != LOGFILE_SIGNATURE)
|
|
{
|
|
EVTLTRACE1("The event log file is corrupted!\n");
|
|
return STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
/*
|
|
* Check whether this event can be overwritten by comparing its
|
|
* written timestamp with the log's retention value. This value
|
|
* is the time interval, in seconds, that events records are
|
|
* protected from being overwritten.
|
|
*
|
|
* If the retention value is zero the events are always overwritten.
|
|
*
|
|
* If the retention value is non-zero, when the age of an event,
|
|
* in seconds, reaches or exceeds this value, it can be overwritten.
|
|
* Also if the events are in the future, we do not overwrite them.
|
|
*/
|
|
if (LogFile->Header.Retention != 0 &&
|
|
(Record->TimeWritten < RecBuf.TimeWritten ||
|
|
(Record->TimeWritten >= RecBuf.TimeWritten &&
|
|
Record->TimeWritten - RecBuf.TimeWritten < LogFile->Header.Retention)))
|
|
{
|
|
EVTLTRACE1("The event log file is full and cannot wrap because of the retention policy.\n");
|
|
LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
|
|
return STATUS_LOG_FILE_FULL;
|
|
}
|
|
|
|
/*
|
|
* Advance the oldest record number, add the event record length
|
|
* (as long as it is valid...) then take account for the possible
|
|
* paddind after the record, in case this is the last one at the
|
|
* end of the file.
|
|
*/
|
|
OldestRecordNumber++;
|
|
RecOffset += RecBuf.Length;
|
|
FreeSpace += RecBuf.Length;
|
|
|
|
/*
|
|
* If this was the last event record before the end of the log file,
|
|
* the next one should start at the beginning of the log and the space
|
|
* between the last event record and the end of the file is padded.
|
|
*/
|
|
if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD))
|
|
{
|
|
/* Add the padding size */
|
|
FreeSpace += LogFile->Header.MaxSize - RecOffset;
|
|
}
|
|
}
|
|
|
|
EVTLTRACE("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize);
|
|
|
|
/* The log records are wrapping */
|
|
LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
|
|
|
|
// FIXME: May lead to corruption if the other subsequent calls fail...
|
|
|
|
/*
|
|
* We have validated all the region of events to be discarded,
|
|
* now we can perform their deletion.
|
|
*/
|
|
ElfpDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1);
|
|
LogFile->Header.OldestRecordNumber = OldestRecordNumber;
|
|
LogFile->Header.StartOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber);
|
|
if (LogFile->Header.StartOffset == 0)
|
|
{
|
|
/*
|
|
* We have deleted all the existing event records to make place
|
|
* for the new one. We can put it at the start of the event log.
|
|
*/
|
|
LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
|
|
WriteOffset = LogFile->Header.StartOffset;
|
|
LogFile->Header.EndOffset = WriteOffset;
|
|
}
|
|
|
|
EVTLTRACE("MaxSize = 0x%x, StartOffset = 0x%x, WriteOffset = 0x%x, EndOffset = 0x%x, BufSize = 0x%x\n"
|
|
"OldestRecordNumber = %d\n",
|
|
LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize,
|
|
OldestRecordNumber);
|
|
}
|
|
|
|
/*
|
|
* Expand the log file if needed.
|
|
* NOTE: It may be needed to perform this task a bit sooner if we need
|
|
* such a thing for performing read operations, in the future...
|
|
* Or if this operation needs to modify 'FreeSpace'...
|
|
*/
|
|
if (LogFile->CurrentSize < LogFile->Header.MaxSize)
|
|
{
|
|
EVTLTRACE1("Expanding the log file from %lu to %lu\n",
|
|
LogFile->CurrentSize, LogFile->Header.MaxSize);
|
|
|
|
LogFile->CurrentSize = LogFile->Header.MaxSize;
|
|
LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
|
|
}
|
|
|
|
/* Since we can write events in the log, clear the log full flag */
|
|
LogFile->Header.Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN;
|
|
|
|
/* Pad the end of the log */
|
|
// if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
|
|
if (WriteOffset < LogFile->Header.EndOffset)
|
|
{
|
|
/* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */
|
|
WrittenLength = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG));
|
|
RtlFillMemoryUlong(&RecBuf, WrittenLength, 0x00000027);
|
|
|
|
FileOffset.QuadPart = LogFile->Header.EndOffset;
|
|
Status = LogFile->FileWrite(LogFile,
|
|
&FileOffset,
|
|
&RecBuf,
|
|
WrittenLength,
|
|
&WrittenLength);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
|
|
// return Status;
|
|
}
|
|
}
|
|
|
|
/* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
|
|
FileOffset.QuadPart = WriteOffset;
|
|
Status = WriteLogBuffer(LogFile,
|
|
Record,
|
|
BufSize,
|
|
&WrittenLength,
|
|
&FileOffset,
|
|
&NextOffset);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
/* FileOffset now contains the offset just after the end of the record buffer */
|
|
FileOffset = NextOffset;
|
|
|
|
if (!ElfpAddOffsetInformation(LogFile,
|
|
Record->RecordNumber,
|
|
WriteOffset))
|
|
{
|
|
return STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT;
|
|
}
|
|
|
|
LogFile->Header.CurrentRecordNumber++;
|
|
if (LogFile->Header.CurrentRecordNumber == 0)
|
|
LogFile->Header.CurrentRecordNumber = 1;
|
|
|
|
/*
|
|
* Write the new EOF record offset just after the event record.
|
|
* The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF)
|
|
* bytes remains between the end of the record and the end of the log file.
|
|
*/
|
|
LogFile->Header.EndOffset = FileOffset.QuadPart;
|
|
|
|
RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
|
|
EofRec.BeginRecord = LogFile->Header.StartOffset;
|
|
EofRec.EndRecord = LogFile->Header.EndOffset;
|
|
EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
|
|
EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
|
|
|
|
// FileOffset.QuadPart = LogFile->Header.EndOffset;
|
|
Status = WriteLogBuffer(LogFile,
|
|
&EofRec,
|
|
sizeof(EofRec),
|
|
&WrittenLength,
|
|
&FileOffset,
|
|
&NextOffset);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
|
|
return Status;
|
|
}
|
|
FileOffset = NextOffset;
|
|
|
|
/* Flush the log file */
|
|
Status = ElfFlushFile(LogFile);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status);
|
|
return STATUS_EVENTLOG_FILE_CORRUPT; // Status;
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
ElfGetOldestRecord(
|
|
IN PEVTLOGFILE LogFile)
|
|
{
|
|
ASSERT(LogFile);
|
|
return LogFile->Header.OldestRecordNumber;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
ElfGetCurrentRecord(
|
|
IN PEVTLOGFILE LogFile)
|
|
{
|
|
ASSERT(LogFile);
|
|
return LogFile->Header.CurrentRecordNumber;
|
|
}
|
|
|
|
ULONG
|
|
NTAPI
|
|
ElfGetFlags(
|
|
IN PEVTLOGFILE LogFile)
|
|
{
|
|
ASSERT(LogFile);
|
|
return LogFile->Header.Flags;
|
|
}
|
|
|
|
#if DBG
|
|
VOID PRINT_HEADER(PEVENTLOGHEADER Header)
|
|
{
|
|
ULONG Flags = Header->Flags;
|
|
|
|
EVTLTRACE1("PRINT_HEADER(0x%p)\n", Header);
|
|
|
|
DbgPrint("HeaderSize = %lu\n" , Header->HeaderSize);
|
|
DbgPrint("Signature = 0x%x\n", Header->Signature);
|
|
DbgPrint("MajorVersion = %lu\n" , Header->MajorVersion);
|
|
DbgPrint("MinorVersion = %lu\n" , Header->MinorVersion);
|
|
DbgPrint("StartOffset = 0x%x\n", Header->StartOffset);
|
|
DbgPrint("EndOffset = 0x%x\n", Header->EndOffset);
|
|
DbgPrint("CurrentRecordNumber = %lu\n", Header->CurrentRecordNumber);
|
|
DbgPrint("OldestRecordNumber = %lu\n", Header->OldestRecordNumber);
|
|
DbgPrint("MaxSize = 0x%x\n", Header->MaxSize);
|
|
DbgPrint("Retention = 0x%x\n", Header->Retention);
|
|
DbgPrint("EndHeaderSize = %lu\n" , Header->EndHeaderSize);
|
|
DbgPrint("Flags: ");
|
|
if (Flags & ELF_LOGFILE_HEADER_DIRTY)
|
|
{
|
|
DbgPrint("ELF_LOGFILE_HEADER_DIRTY");
|
|
Flags &= ~ELF_LOGFILE_HEADER_DIRTY;
|
|
}
|
|
if (Flags) DbgPrint(" | ");
|
|
if (Flags & ELF_LOGFILE_HEADER_WRAP)
|
|
{
|
|
DbgPrint("ELF_LOGFILE_HEADER_WRAP");
|
|
Flags &= ~ELF_LOGFILE_HEADER_WRAP;
|
|
}
|
|
if (Flags) DbgPrint(" | ");
|
|
if (Flags & ELF_LOGFILE_LOGFULL_WRITTEN)
|
|
{
|
|
DbgPrint("ELF_LOGFILE_LOGFULL_WRITTEN");
|
|
Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN;
|
|
}
|
|
if (Flags) DbgPrint(" | ");
|
|
if (Flags & ELF_LOGFILE_ARCHIVE_SET)
|
|
{
|
|
DbgPrint("ELF_LOGFILE_ARCHIVE_SET");
|
|
Flags &= ~ELF_LOGFILE_ARCHIVE_SET;
|
|
}
|
|
if (Flags) DbgPrint(" | 0x%x", Flags);
|
|
DbgPrint("\n");
|
|
}
|
|
#endif
|