Something went wrong. Try again.
Reactos
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660/* * 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 NTSTATUSReadLogBuffer( 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 NTSTATUSWriteLogBuffer( 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 ULONGElfpOffsetByNumber( 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 BOOLElfpAddOffsetInformation( 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 BOOLElfpDeleteOffsetInformation( 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 NTSTATUSElfpInitNewFile( 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 NTSTATUSElfpInitExistingFile( 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 *****************************************************************/
NTSTATUSNTAPIElfCreateFile( 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;}
NTSTATUSNTAPIElfReCreateFile( IN PEVTLOGFILE LogFile){ ASSERT(LogFile);
return ElfpInitNewFile(LogFile, LogFile->CurrentSize, LogFile->Header.MaxSize, LogFile->Header.Retention);}
NTSTATUSNTAPIElfBackupFile( 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;}
NTSTATUSNTAPIElfFlushFile( 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;}
VOIDNTAPIElfCloseFile( // 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);}
NTSTATUSNTAPIElfReadRecord( 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; ULONG 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;}
NTSTATUSNTAPIElfWriteRecord( 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;}
ULONGNTAPIElfGetOldestRecord( IN PEVTLOGFILE LogFile){ ASSERT(LogFile); return LogFile->Header.OldestRecordNumber;}
ULONGNTAPIElfGetCurrentRecord( IN PEVTLOGFILE LogFile){ ASSERT(LogFile); return LogFile->Header.CurrentRecordNumber;}
ULONGNTAPIElfGetFlags( IN PEVTLOGFILE LogFile){ ASSERT(LogFile); return LogFile->Header.Flags;}
#if DBGVOID 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