/* * 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 #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