xref: /reactos/sdk/lib/evtlib/evtlib.c (revision a8ece7e8)
1 /*
2  * PROJECT:         ReactOS EventLog File Library
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            sdk/lib/evtlib/evtlib.c
5  * PURPOSE:         Provides functionality for reading and writing
6  *                  EventLog files in the NT <= 5.2 (.evt) format.
7  * PROGRAMMERS:     Copyright 2005 Saveliy Tretiakov
8  *                  Michael Martin
9  *                  Hermes Belusca-Maito
10  */
11 
12 /* INCLUDES ******************************************************************/
13 
14 #include "evtlib.h"
15 
16 #define NDEBUG
17 #include <debug.h>
18 
19 #define EVTLTRACE(...)  DPRINT("EvtLib: " __VA_ARGS__)
20 // Once things become stabilized enough, replace all the EVTLTRACE1 by EVTLTRACE
21 #define EVTLTRACE1(...)  DPRINT1("EvtLib: " __VA_ARGS__)
22 
23 
24 /* GLOBALS *******************************************************************/
25 
26 static const EVENTLOGEOF EOFRecord =
27 {
28     sizeof(EOFRecord),
29     0x11111111, 0x22222222, 0x33333333, 0x44444444,
30     0, 0, 0, 0,
31     sizeof(EOFRecord)
32 };
33 
34 /* HELPER FUNCTIONS **********************************************************/
35 
36 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)37 ReadLogBuffer(
38     IN  PEVTLOGFILE LogFile,
39     OUT PVOID   Buffer,
40     IN  SIZE_T  Length,
41     OUT PSIZE_T ReadLength OPTIONAL,
42     IN  PLARGE_INTEGER ByteOffset,
43     OUT PLARGE_INTEGER NextOffset OPTIONAL)
44 {
45     NTSTATUS Status;
46     LARGE_INTEGER FileOffset;
47     SIZE_T BufSize;
48     SIZE_T ReadBufLength = 0, OldReadBufLength;
49 
50     ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize);
51     ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize);
52 
53     if (ReadLength)
54         *ReadLength = 0;
55 
56     if (NextOffset)
57         NextOffset->QuadPart = 0LL;
58 
59     /* Read the first part of the buffer */
60     FileOffset = *ByteOffset;
61     BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart);
62 
63     Status = LogFile->FileRead(LogFile,
64                                &FileOffset,
65                                Buffer,
66                                BufSize,
67                                &ReadBufLength);
68     if (!NT_SUCCESS(Status))
69     {
70         EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status);
71         return Status;
72     }
73 
74     if (Length > BufSize)
75     {
76         OldReadBufLength = ReadBufLength;
77 
78         /*
79          * The buffer was splitted in two, its second part
80          * is to be read at the beginning of the log.
81          */
82         Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize);
83         BufSize = Length - BufSize;
84         FileOffset.QuadPart = sizeof(EVENTLOGHEADER);
85 
86         Status = LogFile->FileRead(LogFile,
87                                    &FileOffset,
88                                    Buffer,
89                                    BufSize,
90                                    &ReadBufLength);
91         if (!NT_SUCCESS(Status))
92         {
93             EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status);
94             return Status;
95         }
96         /* Add the read number of bytes from the first read */
97         ReadBufLength += OldReadBufLength;
98     }
99 
100     if (ReadLength)
101         *ReadLength = ReadBufLength;
102 
103     /* We return the offset just after the end of the read buffer */
104     if (NextOffset)
105         NextOffset->QuadPart = FileOffset.QuadPart + BufSize;
106 
107     return Status;
108 }
109 
110 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)111 WriteLogBuffer(
112     IN  PEVTLOGFILE LogFile,
113     IN  PVOID   Buffer,
114     IN  SIZE_T  Length,
115     OUT PSIZE_T WrittenLength OPTIONAL,
116     IN  PLARGE_INTEGER ByteOffset,
117     OUT PLARGE_INTEGER NextOffset OPTIONAL)
118 {
119     NTSTATUS Status;
120     LARGE_INTEGER FileOffset;
121     SIZE_T BufSize;
122     SIZE_T WrittenBufLength = 0, OldWrittenBufLength;
123 
124     /* We must have write access to the log file */
125     ASSERT(!LogFile->ReadOnly);
126 
127     /*
128      * It is expected that the log file is already correctly expanded
129      * before we can write in it. Therefore the following assertions hold.
130      */
131     ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize);
132     ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize);
133 
134     if (WrittenLength)
135         *WrittenLength = 0;
136 
137     if (NextOffset)
138         NextOffset->QuadPart = 0LL;
139 
140     /* Write the first part of the buffer */
141     FileOffset = *ByteOffset;
142     BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart);
143 
144     Status = LogFile->FileWrite(LogFile,
145                                 &FileOffset,
146                                 Buffer,
147                                 BufSize,
148                                 &WrittenBufLength);
149     if (!NT_SUCCESS(Status))
150     {
151         EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status);
152         return Status;
153     }
154 
155     if (Length > BufSize)
156     {
157         OldWrittenBufLength = WrittenBufLength;
158 
159         /*
160          * The buffer was splitted in two, its second part
161          * is written at the beginning of the log.
162          */
163         Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize);
164         BufSize = Length - BufSize;
165         FileOffset.QuadPart = sizeof(EVENTLOGHEADER);
166 
167         Status = LogFile->FileWrite(LogFile,
168                                     &FileOffset,
169                                     Buffer,
170                                     BufSize,
171                                     &WrittenBufLength);
172         if (!NT_SUCCESS(Status))
173         {
174             EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status);
175             return Status;
176         }
177         /* Add the written number of bytes from the first write */
178         WrittenBufLength += OldWrittenBufLength;
179 
180         /* The log wraps */
181         LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
182     }
183 
184     if (WrittenLength)
185         *WrittenLength = WrittenBufLength;
186 
187     /* We return the offset just after the end of the written buffer */
188     if (NextOffset)
189         NextOffset->QuadPart = FileOffset.QuadPart + BufSize;
190 
191     return Status;
192 }
193 
194 
195 /* Returns 0 if nothing is found */
196 static ULONG
ElfpOffsetByNumber(IN PEVTLOGFILE LogFile,IN ULONG RecordNumber)197 ElfpOffsetByNumber(
198     IN PEVTLOGFILE LogFile,
199     IN ULONG RecordNumber)
200 {
201     UINT i;
202 
203     for (i = 0; i < LogFile->OffsetInfoNext; i++)
204     {
205         if (LogFile->OffsetInfo[i].EventNumber == RecordNumber)
206             return LogFile->OffsetInfo[i].EventOffset;
207     }
208     return 0;
209 }
210 
211 #define OFFSET_INFO_INCREMENT   64
212 
213 static BOOL
ElfpAddOffsetInformation(IN PEVTLOGFILE LogFile,IN ULONG ulNumber,IN ULONG ulOffset)214 ElfpAddOffsetInformation(
215     IN PEVTLOGFILE LogFile,
216     IN ULONG ulNumber,
217     IN ULONG ulOffset)
218 {
219     PVOID NewOffsetInfo;
220 
221     if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize)
222     {
223         /* Allocate a new offset table */
224         NewOffsetInfo = LogFile->Allocate((LogFile->OffsetInfoSize + OFFSET_INFO_INCREMENT) *
225                                               sizeof(EVENT_OFFSET_INFO),
226                                           HEAP_ZERO_MEMORY,
227                                           TAG_ELF);
228         if (!NewOffsetInfo)
229         {
230             EVTLTRACE1("Cannot reallocate heap.\n");
231             return FALSE;
232         }
233 
234         /* Free the old offset table and use the new one */
235         if (LogFile->OffsetInfo)
236         {
237             /* Copy the handles from the old table to the new one */
238             RtlCopyMemory(NewOffsetInfo,
239                           LogFile->OffsetInfo,
240                           LogFile->OffsetInfoSize * sizeof(EVENT_OFFSET_INFO));
241             LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF);
242         }
243         LogFile->OffsetInfo = (PEVENT_OFFSET_INFO)NewOffsetInfo;
244         LogFile->OffsetInfoSize += OFFSET_INFO_INCREMENT;
245     }
246 
247     LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventNumber = ulNumber;
248     LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventOffset = ulOffset;
249     LogFile->OffsetInfoNext++;
250 
251     return TRUE;
252 }
253 
254 static BOOL
ElfpDeleteOffsetInformation(IN PEVTLOGFILE LogFile,IN ULONG ulNumberMin,IN ULONG ulNumberMax)255 ElfpDeleteOffsetInformation(
256     IN PEVTLOGFILE LogFile,
257     IN ULONG ulNumberMin,
258     IN ULONG ulNumberMax)
259 {
260     UINT i;
261 
262     if (ulNumberMin > ulNumberMax)
263         return FALSE;
264 
265     /* Remove records ulNumberMin to ulNumberMax inclusive */
266     while (ulNumberMin <= ulNumberMax)
267     {
268         /*
269          * As the offset information is listed in increasing order, and we want
270          * to keep the list without holes, we demand that ulNumberMin is the first
271          * element in the list.
272          */
273         if (ulNumberMin != LogFile->OffsetInfo[0].EventNumber)
274             return FALSE;
275 
276         /*
277          * RtlMoveMemory(&LogFile->OffsetInfo[0],
278          *               &LogFile->OffsetInfo[1],
279          *               sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1));
280          */
281         for (i = 0; i < LogFile->OffsetInfoNext - 1; i++)
282         {
283             LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber;
284             LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset;
285         }
286         LogFile->OffsetInfoNext--;
287 
288         /* Go to the next offset information */
289         ulNumberMin++;
290     }
291 
292     return TRUE;
293 }
294 
295 
296 static NTSTATUS
ElfpInitNewFile(IN PEVTLOGFILE LogFile,IN ULONG FileSize,IN ULONG MaxSize,IN ULONG Retention)297 ElfpInitNewFile(
298     IN PEVTLOGFILE LogFile,
299     IN ULONG FileSize,
300     IN ULONG MaxSize,
301     IN ULONG Retention)
302 {
303     NTSTATUS Status;
304     LARGE_INTEGER FileOffset;
305     SIZE_T WrittenLength;
306     EVENTLOGEOF EofRec;
307 
308     /* Initialize the event log header */
309     RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER));
310 
311     LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER);
312     LogFile->Header.Signature  = LOGFILE_SIGNATURE;
313     LogFile->Header.MajorVersion = MAJORVER;
314     LogFile->Header.MinorVersion = MINORVER;
315 
316     /* Set the offset to the oldest record */
317     LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
318     /* Set the offset to the ELF_EOF_RECORD */
319     LogFile->Header.EndOffset = sizeof(EVENTLOGHEADER);
320     /* Set the number of the next record that will be added */
321     LogFile->Header.CurrentRecordNumber = 1;
322     /* The event log is empty, there is no record so far */
323     LogFile->Header.OldestRecordNumber = 0;
324 
325     // FIXME: Windows' EventLog log file sizes are always multiple of 64kB
326     // but that does not mean the real log size is == file size.
327 
328     /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */
329     LogFile->Header.MaxSize = ROUND_UP(MaxSize, sizeof(ULONG));
330     LogFile->CurrentSize = LogFile->Header.MaxSize; // or: FileSize ??
331     LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
332 
333     LogFile->Header.Flags = 0;
334     LogFile->Header.Retention = Retention;
335     LogFile->Header.EndHeaderSize = sizeof(EVENTLOGHEADER);
336 
337     /* Write the header */
338     FileOffset.QuadPart = 0LL;
339     Status = LogFile->FileWrite(LogFile,
340                                 &FileOffset,
341                                 &LogFile->Header,
342                                 sizeof(EVENTLOGHEADER),
343                                 &WrittenLength);
344     if (!NT_SUCCESS(Status))
345     {
346         EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
347         return Status;
348     }
349 
350     /* Initialize the ELF_EOF_RECORD and write it */
351     RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
352     EofRec.BeginRecord = LogFile->Header.StartOffset;
353     EofRec.EndRecord   = LogFile->Header.EndOffset;
354     EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
355     EofRec.OldestRecordNumber  = LogFile->Header.OldestRecordNumber;
356 
357     Status = LogFile->FileWrite(LogFile,
358                                 NULL,
359                                 &EofRec,
360                                 sizeof(EofRec),
361                                 &WrittenLength);
362     if (!NT_SUCCESS(Status))
363     {
364         EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
365         return Status;
366     }
367 
368     Status = LogFile->FileFlush(LogFile, NULL, 0);
369     if (!NT_SUCCESS(Status))
370     {
371         EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status);
372         return Status;
373     }
374 
375     return STATUS_SUCCESS;
376 }
377 
378 static NTSTATUS
ElfpInitExistingFile(IN PEVTLOGFILE LogFile,IN ULONG FileSize,IN ULONG Retention)379 ElfpInitExistingFile(
380     IN PEVTLOGFILE LogFile,
381     IN ULONG FileSize,
382     // IN ULONG MaxSize,
383     IN ULONG Retention)
384 {
385     NTSTATUS Status;
386     LARGE_INTEGER FileOffset, NextOffset;
387     SIZE_T ReadLength;
388     ULONG RecordNumber = 0;
389     ULONG RecOffset;
390     PULONG pRecSize2;
391     EVENTLOGEOF EofRec;
392     EVENTLOGRECORD RecBuf;
393     PEVENTLOGRECORD pRecBuf;
394     BOOLEAN Wrapping = FALSE;
395     BOOLEAN IsLogDirty = FALSE;
396 
397     /* Read the log header */
398     FileOffset.QuadPart = 0LL;
399     Status = LogFile->FileRead(LogFile,
400                                &FileOffset,
401                                &LogFile->Header,
402                                sizeof(EVENTLOGHEADER),
403                                &ReadLength);
404     if (!NT_SUCCESS(Status))
405     {
406         EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
407         return STATUS_EVENTLOG_FILE_CORRUPT; // return Status;
408     }
409     if (ReadLength != sizeof(EVENTLOGHEADER))
410     {
411         EVTLTRACE("Invalid file `%wZ'.\n", &LogFile->FileName);
412         return STATUS_EVENTLOG_FILE_CORRUPT;
413     }
414 
415     /* Header validity checks */
416 
417     if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) ||
418         LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER))
419     {
420         EVTLTRACE("Invalid header size in `%wZ'.\n", &LogFile->FileName);
421         return STATUS_EVENTLOG_FILE_CORRUPT;
422     }
423 
424     if (LogFile->Header.Signature != LOGFILE_SIGNATURE)
425     {
426         EVTLTRACE("Invalid signature %x in `%wZ'.\n",
427                LogFile->Header.Signature, &LogFile->FileName);
428         return STATUS_EVENTLOG_FILE_CORRUPT;
429     }
430 
431     IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY);
432 
433     /* If the log is read-only (e.g. a backup log) and is dirty, then it is corrupted */
434     if (LogFile->ReadOnly && IsLogDirty)
435     {
436         EVTLTRACE("Read-only log `%wZ' is dirty.\n", &LogFile->FileName);
437         return STATUS_EVENTLOG_FILE_CORRUPT;
438     }
439 
440     LogFile->CurrentSize = FileSize;
441     // FIXME!! What to do? And what to do if the MaxSize from the registry
442     // is strictly less than the CurrentSize?? Should we "reduce" the log size
443     // by clearing it completely??
444     // --> ANSWER: Save the new MaxSize somewhere, and only when the log is
445     //     being cleared, use the new MaxSize to resize (ie. shrink) it.
446     // LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
447 
448     /* Adjust the log maximum size if needed */
449     if (LogFile->CurrentSize > LogFile->Header.MaxSize)
450         LogFile->Header.MaxSize = LogFile->CurrentSize;
451 
452     /*
453      * Reset the log retention value. The value stored
454      * in the log file is just for information purposes.
455      */
456     LogFile->Header.Retention = Retention;
457 
458     /*
459      * For a non-read-only dirty log, the most up-to-date information about
460      * the Start/End offsets and the Oldest and Current event record numbers
461      * are found in the EOF record. We need to locate the EOF record without
462      * relying on the log header's EndOffset, then patch the log header with
463      * the values from the EOF record.
464      */
465     if ((LogFile->Header.EndOffset >= sizeof(EVENTLOGHEADER)) &&
466         (LogFile->Header.EndOffset <  LogFile->CurrentSize) &&
467         (LogFile->Header.EndOffset & 3) == 0) // EndOffset % sizeof(ULONG) == 0
468     {
469         /* The header EOF offset may be valid, try to start with it */
470         RecOffset = LogFile->Header.EndOffset;
471     }
472     else
473     {
474         /* The header EOF offset could not be valid, so start from the beginning */
475         RecOffset = sizeof(EVENTLOGHEADER);
476     }
477 
478     FileOffset.QuadPart = RecOffset;
479     Wrapping = FALSE;
480 
481     for (;;)
482     {
483         if (Wrapping && FileOffset.QuadPart >= RecOffset)
484         {
485             EVTLTRACE1("EOF record not found!\n");
486             return STATUS_EVENTLOG_FILE_CORRUPT;
487         }
488 
489         /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */
490         Status = ReadLogBuffer(LogFile,
491                                &EofRec,
492                                EVENTLOGEOF_SIZE_FIXED,
493                                &ReadLength,
494                                &FileOffset,
495                                NULL);
496         if (!NT_SUCCESS(Status))
497         {
498             EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
499             return STATUS_EVENTLOG_FILE_CORRUPT;
500         }
501         if (ReadLength != EVENTLOGEOF_SIZE_FIXED)
502         {
503             EVTLTRACE1("Cannot read at most an EOF record!\n");
504             return STATUS_EVENTLOG_FILE_CORRUPT;
505         }
506 
507         /* Is it an EVENTLOGEOF record? */
508         if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED)
509         {
510             DPRINT("Found EOF record at %llx\n", FileOffset.QuadPart);
511 
512             /* Got it! Break the loop and continue */
513             break;
514         }
515 
516         /* No, continue looping */
517         if (*(PULONG)((ULONG_PTR)&EofRec + sizeof(ULONG)) == *(PULONG)(&EOFRecord))
518             FileOffset.QuadPart += sizeof(ULONG);
519         else
520         if (*(PULONG)((ULONG_PTR)&EofRec + 2*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
521             FileOffset.QuadPart += 2*sizeof(ULONG);
522         else
523         if (*(PULONG)((ULONG_PTR)&EofRec + 3*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
524             FileOffset.QuadPart += 3*sizeof(ULONG);
525         else
526         if (*(PULONG)((ULONG_PTR)&EofRec + 4*sizeof(ULONG)) == *(PULONG)(&EOFRecord))
527             FileOffset.QuadPart += 4*sizeof(ULONG);
528         else
529             FileOffset.QuadPart += 5*sizeof(ULONG); // EVENTLOGEOF_SIZE_FIXED
530 
531         if (FileOffset.QuadPart >= LogFile->CurrentSize /* LogFile->Header.MaxSize */)
532         {
533             /* Wrap the offset */
534             FileOffset.QuadPart -= LogFile->CurrentSize /* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER);
535             Wrapping = TRUE;
536         }
537     }
538     /*
539      * The only way to be there is to have found a valid EOF record.
540      * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT
541      * was returned.
542      */
543 
544     /* Read the full EVENTLOGEOF (may wrap) and validate it */
545     Status = ReadLogBuffer(LogFile,
546                            &EofRec,
547                            sizeof(EofRec),
548                            &ReadLength,
549                            &FileOffset,
550                            NULL);
551     if (!NT_SUCCESS(Status))
552     {
553         EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
554         return STATUS_EVENTLOG_FILE_CORRUPT;
555     }
556     if (ReadLength != sizeof(EofRec))
557     {
558         EVTLTRACE1("Cannot read the full EOF record!\n");
559         return STATUS_EVENTLOG_FILE_CORRUPT;
560     }
561 
562     /* Complete validity checks */
563     if ((EofRec.RecordSizeEnd != EofRec.RecordSizeBeginning) ||
564         (EofRec.EndRecord != FileOffset.QuadPart))
565     {
566         DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected 0x%x 0x%x!\n",
567                 FileOffset.QuadPart,
568                 EofRec.RecordSizeEnd, EofRec.RecordSizeBeginning,
569                 EofRec.EndRecord, FileOffset.QuadPart,
570                 EOFRecord.RecordSizeEnd, EOFRecord.RecordSizeBeginning);
571         DPRINT1("RecordSizeEnd = 0x%x\n", EofRec.RecordSizeEnd);
572         DPRINT1("RecordSizeBeginning = 0x%x\n", EofRec.RecordSizeBeginning);
573         DPRINT1("EndRecord = 0x%x\n", EofRec.EndRecord);
574         return STATUS_EVENTLOG_FILE_CORRUPT;
575     }
576 
577     /* The EOF record is valid, break the loop and continue */
578 
579     /* If the log is not dirty, the header values should correspond to the EOF ones */
580     if (!IsLogDirty)
581     {
582         if ( (LogFile->Header.StartOffset != EofRec.BeginRecord) ||
583              (LogFile->Header.EndOffset   != EofRec.EndRecord)   ||
584              (LogFile->Header.CurrentRecordNumber != EofRec.CurrentRecordNumber) ||
585              (LogFile->Header.OldestRecordNumber  != EofRec.OldestRecordNumber) )
586         {
587             DPRINT1("\n"
588                     "Log header or EOF record is corrupted:\n"
589                     "    StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n"
590                     "    CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n",
591                     LogFile->Header.StartOffset, EofRec.BeginRecord,
592                     LogFile->Header.EndOffset  , EofRec.EndRecord,
593                     LogFile->Header.CurrentRecordNumber, EofRec.CurrentRecordNumber,
594                     LogFile->Header.OldestRecordNumber , EofRec.OldestRecordNumber);
595 
596             return STATUS_EVENTLOG_FILE_CORRUPT;
597         }
598     }
599 
600     /* If the log is dirty, patch the log header with the values from the EOF record */
601     if (!LogFile->ReadOnly && IsLogDirty)
602     {
603         LogFile->Header.StartOffset = EofRec.BeginRecord;
604         LogFile->Header.EndOffset   = EofRec.EndRecord;
605         LogFile->Header.CurrentRecordNumber = EofRec.CurrentRecordNumber;
606         LogFile->Header.OldestRecordNumber  = EofRec.OldestRecordNumber;
607     }
608 
609     /*
610      * FIXME! During operations the EOF record is the one that is the most
611      * updated (its Oldest & Current record numbers are always up-to
612      * date) while the ones from the header may be unsync. When closing
613      * (or flushing?) the event log, the header's record numbers get
614      * updated with the same values as the ones stored in the EOF record.
615      */
616 
617     /* Verify Start/End offsets boundaries */
618 
619     if ((LogFile->Header.StartOffset >= LogFile->CurrentSize) ||
620         (LogFile->Header.StartOffset & 3) != 0) // StartOffset % sizeof(ULONG) != 0
621     {
622         EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n",
623                LogFile->Header.StartOffset, &LogFile->FileName);
624         return STATUS_EVENTLOG_FILE_CORRUPT;
625     }
626     if ((LogFile->Header.EndOffset >= LogFile->CurrentSize) ||
627         (LogFile->Header.EndOffset & 3) != 0) // EndOffset % sizeof(ULONG) != 0
628     {
629         EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
630                LogFile->Header.EndOffset, &LogFile->FileName);
631         return STATUS_EVENTLOG_FILE_CORRUPT;
632     }
633 
634     if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) &&
635         (LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD)))
636     {
637         /*
638          * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF,
639          * it should point to a non-splitted EVENTLOGRECORD.
640          */
641         EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n",
642                LogFile->Header.StartOffset, &LogFile->FileName);
643         return STATUS_EVENTLOG_FILE_CORRUPT;
644     }
645 
646     if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
647         (LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD)))
648     {
649         /*
650          * In non-wrapping case, there must be enough space between StartOffset
651          * and EndOffset to contain at least a full EVENTLOGRECORD.
652          */
653         EVTLTRACE("Invalid start offset 0x%x or end offset 0x%x in `%wZ'.\n",
654                LogFile->Header.StartOffset, LogFile->Header.EndOffset, &LogFile->FileName);
655         return STATUS_EVENTLOG_FILE_CORRUPT;
656     }
657 
658     if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
659     {
660         /*
661          * Non-wrapping case: the (wrapping) free space starting at EndOffset
662          * must be able to contain an EVENTLOGEOF.
663          */
664         if (LogFile->Header.MaxSize - LogFile->Header.EndOffset +
665             LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER) < sizeof(EVENTLOGEOF))
666         {
667             EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
668                    LogFile->Header.EndOffset, &LogFile->FileName);
669             return STATUS_EVENTLOG_FILE_CORRUPT;
670         }
671     }
672     else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
673     {
674         /*
675          * Wrapping case: the free space between EndOffset and StartOffset
676          * must be able to contain an EVENTLOGEOF.
677          */
678         if (LogFile->Header.StartOffset - LogFile->Header.EndOffset < sizeof(EVENTLOGEOF))
679         {
680             EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n",
681                    LogFile->Header.EndOffset, &LogFile->FileName);
682             return STATUS_EVENTLOG_FILE_CORRUPT;
683         }
684     }
685 
686     /* Start enumerating the event records from the beginning */
687     RecOffset = LogFile->Header.StartOffset;
688     FileOffset.QuadPart = RecOffset;
689     Wrapping = FALSE;
690 
691     // // FIXME! FIXME!
692     // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP))
693     // {
694         // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n");
695         // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
696     // }
697 
698     DPRINT("StartOffset = 0x%x, EndOffset = 0x%x\n",
699            LogFile->Header.StartOffset, LogFile->Header.EndOffset);
700 
701     /*
702      * For non-read-only logs of size < MaxSize, reorganize the events
703      * such that they do not wrap as soon as we write new ones.
704      */
705 #if 0
706     if (!LogFile->ReadOnly)
707     {
708         pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
709         if (pRecBuf == NULL)
710         {
711             DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n");
712             goto Continue;
713         }
714 
715         // TODO: Do the job!
716     }
717 
718 Continue:
719 
720     DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n",
721             LogFile->Header.StartOffset, LogFile->Header.EndOffset);
722 #endif
723 
724     while (FileOffset.QuadPart != LogFile->Header.EndOffset)
725     {
726         if (Wrapping && FileOffset.QuadPart >= RecOffset)
727         {
728             /* We have finished enumerating all the event records */
729             break;
730         }
731 
732         /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
733         Status = LogFile->FileRead(LogFile,
734                                    &FileOffset,
735                                    &RecBuf,
736                                    sizeof(RecBuf),
737                                    &ReadLength);
738         if (!NT_SUCCESS(Status))
739         {
740             EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
741             return STATUS_EVENTLOG_FILE_CORRUPT;
742         }
743         if (ReadLength != sizeof(RecBuf))
744         {
745             DPRINT1("Length != sizeof(RecBuf)\n");
746             break;
747         }
748 
749         if (RecBuf.Reserved != LOGFILE_SIGNATURE ||
750             RecBuf.Length < sizeof(EVENTLOGRECORD))
751         {
752             DPRINT1("RecBuf problem\n");
753             break;
754         }
755 
756         /* Allocate a full EVENTLOGRECORD (header + data) */
757         pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
758         if (pRecBuf == NULL)
759         {
760             EVTLTRACE1("Cannot allocate heap!\n");
761             return STATUS_NO_MEMORY;
762         }
763 
764         /* Attempt to read the full EVENTLOGRECORD (can wrap) */
765         Status = ReadLogBuffer(LogFile,
766                                pRecBuf,
767                                RecBuf.Length,
768                                &ReadLength,
769                                &FileOffset,
770                                &NextOffset);
771         if (!NT_SUCCESS(Status))
772         {
773             EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
774             LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
775             return STATUS_EVENTLOG_FILE_CORRUPT;
776         }
777         if (ReadLength != RecBuf.Length)
778         {
779             DPRINT1("Oh oh!!\n");
780             LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
781             break;
782         }
783 
784         // /* If OverWrittenRecords is TRUE and this record has already been read */
785         // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber))
786         // {
787             // LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
788             // break;
789         // }
790 
791         pRecSize2 = (PULONG)((ULONG_PTR)pRecBuf + RecBuf.Length - 4);
792 
793         if (*pRecSize2 != RecBuf.Length)
794         {
795             EVTLTRACE1("Invalid RecordSizeEnd of record %d (0x%x) in `%wZ'\n",
796                     RecordNumber, *pRecSize2, &LogFile->FileName);
797             LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
798             break;
799         }
800 
801         EVTLTRACE("Add new record %d @ offset 0x%x\n", pRecBuf->RecordNumber, FileOffset.QuadPart);
802 
803         RecordNumber++;
804 
805         if (!ElfpAddOffsetInformation(LogFile,
806                                       pRecBuf->RecordNumber,
807                                       FileOffset.QuadPart))
808         {
809             EVTLTRACE1("ElfpAddOffsetInformation() failed!\n");
810             LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
811             return STATUS_EVENTLOG_FILE_CORRUPT;
812         }
813 
814         LogFile->Free(pRecBuf, 0, TAG_ELF_BUF);
815 
816         if (NextOffset.QuadPart == LogFile->Header.EndOffset)
817         {
818             /* We have finished enumerating all the event records */
819             DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n");
820             break;
821         }
822 
823         /*
824          * If this was the last event record before the end of the log file,
825          * the next one should start at the beginning of the log and the space
826          * between the last event record and the end of the file is padded.
827          */
828         if (LogFile->Header.MaxSize - NextOffset.QuadPart < sizeof(EVENTLOGRECORD))
829         {
830             /* Wrap to the beginning of the log */
831             DPRINT("Wrap!\n");
832             NextOffset.QuadPart = sizeof(EVENTLOGHEADER);
833         }
834 
835         /*
836          * If the next offset to read is below the current offset,
837          * this means we are wrapping.
838          */
839         if (FileOffset.QuadPart > NextOffset.QuadPart)
840         {
841             DPRINT("Wrapping = TRUE;\n");
842             Wrapping = TRUE;
843         }
844 
845         /* Move the current offset */
846         FileOffset = NextOffset;
847     }
848 
849     /* If the event log was empty, it will now contain one record */
850     if (RecordNumber != 0 && LogFile->Header.OldestRecordNumber == 0)
851         LogFile->Header.OldestRecordNumber = 1;
852 
853     LogFile->Header.CurrentRecordNumber = RecordNumber + LogFile->Header.OldestRecordNumber;
854     if (LogFile->Header.CurrentRecordNumber == 0)
855         LogFile->Header.CurrentRecordNumber = 1;
856 
857     /* Flush the log if it is not read-only */
858     if (!LogFile->ReadOnly)
859     {
860         Status = ElfFlushFile(LogFile);
861         if (!NT_SUCCESS(Status))
862         {
863             EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status);
864             return STATUS_EVENTLOG_FILE_CORRUPT; // Status;
865         }
866     }
867 
868     return STATUS_SUCCESS;
869 }
870 
871 
872 /* FUNCTIONS *****************************************************************/
873 
874 NTSTATUS
875 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)876 ElfCreateFile(
877     IN OUT PEVTLOGFILE LogFile,
878     IN PUNICODE_STRING FileName OPTIONAL,
879     IN ULONG    FileSize,
880     IN ULONG    MaxSize,
881     IN ULONG    Retention,
882     IN BOOLEAN  CreateNew,
883     IN BOOLEAN  ReadOnly,
884     IN PELF_ALLOCATE_ROUTINE   Allocate,
885     IN PELF_FREE_ROUTINE       Free,
886     IN PELF_FILE_SET_SIZE_ROUTINE FileSetSize,
887     IN PELF_FILE_WRITE_ROUTINE FileWrite,
888     IN PELF_FILE_READ_ROUTINE  FileRead,
889     IN PELF_FILE_FLUSH_ROUTINE FileFlush) // What about Seek ??
890 {
891     NTSTATUS Status = STATUS_SUCCESS;
892 
893     ASSERT(LogFile);
894 
895     /* Creating a new log file with the 'ReadOnly' flag set is incompatible */
896     if (CreateNew && ReadOnly)
897         return STATUS_INVALID_PARAMETER;
898 
899     RtlZeroMemory(LogFile, sizeof(*LogFile));
900 
901     LogFile->Allocate  = Allocate;
902     LogFile->Free      = Free;
903     LogFile->FileSetSize = FileSetSize;
904     LogFile->FileWrite = FileWrite;
905     LogFile->FileRead  = FileRead;
906     LogFile->FileFlush = FileFlush;
907 
908     /* Copy the log file name if provided (optional) */
909     RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0);
910     if (FileName && FileName->Buffer && FileName->Length &&
911         (FileName->Length <= FileName->MaximumLength))
912     {
913         LogFile->FileName.Buffer = LogFile->Allocate(FileName->Length,
914                                                      HEAP_ZERO_MEMORY,
915                                                      TAG_ELF);
916         if (LogFile->FileName.Buffer)
917         {
918             LogFile->FileName.MaximumLength = FileName->Length;
919             RtlCopyUnicodeString(&LogFile->FileName, FileName);
920         }
921     }
922 
923     LogFile->OffsetInfo = LogFile->Allocate(OFFSET_INFO_INCREMENT * sizeof(EVENT_OFFSET_INFO),
924                                             HEAP_ZERO_MEMORY,
925                                             TAG_ELF);
926     if (LogFile->OffsetInfo == NULL)
927     {
928         EVTLTRACE1("Cannot allocate heap\n");
929         Status = STATUS_NO_MEMORY;
930         goto Quit;
931     }
932     LogFile->OffsetInfoSize = OFFSET_INFO_INCREMENT;
933     LogFile->OffsetInfoNext = 0;
934 
935     // FIXME: Always use the regitry values for MaxSize,
936     // even for existing logs!
937 
938     // FIXME: On Windows, EventLog uses the MaxSize setting
939     // from the registry itself; the MaxSize from the header
940     // is just for information purposes.
941 
942     EVTLTRACE("Initializing log file `%wZ'\n", &LogFile->FileName);
943 
944     LogFile->ReadOnly = ReadOnly; // !CreateNew && ReadOnly;
945 
946     if (CreateNew)
947         Status = ElfpInitNewFile(LogFile, FileSize, MaxSize, Retention);
948     else
949         Status = ElfpInitExistingFile(LogFile, FileSize, /* MaxSize, */ Retention);
950 
951 Quit:
952     if (!NT_SUCCESS(Status))
953     {
954         if (LogFile->OffsetInfo)
955             LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF);
956 
957         if (LogFile->FileName.Buffer)
958             LogFile->Free(LogFile->FileName.Buffer, 0, TAG_ELF);
959     }
960 
961     return Status;
962 }
963 
964 NTSTATUS
965 NTAPI
ElfReCreateFile(IN PEVTLOGFILE LogFile)966 ElfReCreateFile(
967     IN PEVTLOGFILE LogFile)
968 {
969     ASSERT(LogFile);
970 
971     return ElfpInitNewFile(LogFile,
972                            LogFile->CurrentSize,
973                            LogFile->Header.MaxSize,
974                            LogFile->Header.Retention);
975 }
976 
977 NTSTATUS
978 NTAPI
ElfBackupFile(IN PEVTLOGFILE LogFile,IN PEVTLOGFILE BackupLogFile)979 ElfBackupFile(
980     IN PEVTLOGFILE LogFile,
981     IN PEVTLOGFILE BackupLogFile)
982 {
983     NTSTATUS Status;
984 
985     LARGE_INTEGER FileOffset;
986     SIZE_T ReadLength, WrittenLength;
987     PEVENTLOGHEADER Header;
988     EVENTLOGRECORD RecBuf;
989     EVENTLOGEOF EofRec;
990     ULONG i;
991     ULONG RecOffset;
992     PVOID Buffer = NULL;
993 
994     ASSERT(LogFile);
995 
996     RtlZeroMemory(BackupLogFile, sizeof(*BackupLogFile));
997 
998     BackupLogFile->FileSetSize = LogFile->FileSetSize;
999     BackupLogFile->FileWrite = LogFile->FileWrite;
1000     BackupLogFile->FileFlush = LogFile->FileFlush;
1001 
1002     // BackupLogFile->CurrentSize = LogFile->CurrentSize;
1003 
1004     BackupLogFile->ReadOnly = FALSE;
1005 
1006     /* Initialize the (dirty) log file header */
1007     Header = &BackupLogFile->Header;
1008     Header->HeaderSize = sizeof(EVENTLOGHEADER);
1009     Header->Signature = LOGFILE_SIGNATURE;
1010     Header->MajorVersion = MAJORVER;
1011     Header->MinorVersion = MINORVER;
1012     Header->StartOffset = sizeof(EVENTLOGHEADER);
1013     Header->EndOffset = sizeof(EVENTLOGHEADER);
1014     Header->CurrentRecordNumber = 1;
1015     Header->OldestRecordNumber = 0;
1016     Header->MaxSize = LogFile->Header.MaxSize;
1017     Header->Flags = ELF_LOGFILE_HEADER_DIRTY;
1018     Header->Retention = LogFile->Header.Retention;
1019     Header->EndHeaderSize = sizeof(EVENTLOGHEADER);
1020 
1021     /* Write the (dirty) log file header */
1022     FileOffset.QuadPart = 0LL;
1023     Status = BackupLogFile->FileWrite(BackupLogFile,
1024                                       &FileOffset,
1025                                       Header,
1026                                       sizeof(EVENTLOGHEADER),
1027                                       &WrittenLength);
1028     if (!NT_SUCCESS(Status))
1029     {
1030         EVTLTRACE1("Failed to write the log file header (Status 0x%08lx)\n", Status);
1031         goto Quit;
1032     }
1033 
1034     for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++)
1035     {
1036         RecOffset = ElfpOffsetByNumber(LogFile, i);
1037         if (RecOffset == 0)
1038             break;
1039 
1040         /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
1041         FileOffset.QuadPart = RecOffset;
1042         Status = LogFile->FileRead(LogFile,
1043                                    &FileOffset,
1044                                    &RecBuf,
1045                                    sizeof(RecBuf),
1046                                    &ReadLength);
1047         if (!NT_SUCCESS(Status))
1048         {
1049             EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
1050             goto Quit;
1051         }
1052 
1053         // if (ReadLength != sizeof(RecBuf))
1054             // break;
1055 
1056         Buffer = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
1057         if (Buffer == NULL)
1058         {
1059             EVTLTRACE1("Allocate() failed!\n");
1060             goto Quit;
1061         }
1062 
1063         /* Read the full EVENTLOGRECORD (header + data) with wrapping */
1064         Status = ReadLogBuffer(LogFile,
1065                                Buffer,
1066                                RecBuf.Length,
1067                                &ReadLength,
1068                                &FileOffset,
1069                                NULL);
1070         if (!NT_SUCCESS(Status))
1071         {
1072             EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
1073             LogFile->Free(Buffer, 0, TAG_ELF_BUF);
1074             // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1075             goto Quit;
1076         }
1077 
1078         /* Write the event record (no wrap for the backup log) */
1079         Status = BackupLogFile->FileWrite(BackupLogFile,
1080                                           NULL,
1081                                           Buffer,
1082                                           RecBuf.Length,
1083                                           &WrittenLength);
1084         if (!NT_SUCCESS(Status))
1085         {
1086             EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1087             LogFile->Free(Buffer, 0, TAG_ELF_BUF);
1088             goto Quit;
1089         }
1090 
1091         /* Update the header information */
1092         Header->EndOffset += RecBuf.Length;
1093 
1094         /* Free the buffer */
1095         LogFile->Free(Buffer, 0, TAG_ELF_BUF);
1096         Buffer = NULL;
1097     }
1098 
1099 // Quit:
1100 
1101     /* Initialize the ELF_EOF_RECORD and write it (no wrap for the backup log) */
1102     RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
1103     EofRec.BeginRecord = Header->StartOffset;
1104     EofRec.EndRecord   = Header->EndOffset;
1105     EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1106     EofRec.OldestRecordNumber  = LogFile->Header.OldestRecordNumber;
1107 
1108     Status = BackupLogFile->FileWrite(BackupLogFile,
1109                                       NULL,
1110                                       &EofRec,
1111                                       sizeof(EofRec),
1112                                       &WrittenLength);
1113     if (!NT_SUCCESS(Status))
1114     {
1115         EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1116         goto Quit;
1117     }
1118 
1119     /* Update the header information */
1120     Header->CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1121     Header->OldestRecordNumber  = LogFile->Header.OldestRecordNumber;
1122     Header->MaxSize = ROUND_UP(Header->EndOffset + sizeof(EofRec), sizeof(ULONG));
1123     Header->Flags = 0; // FIXME?
1124 
1125     /* Flush the log file - Write the (clean) log file header */
1126     Status = ElfFlushFile(BackupLogFile);
1127 
1128 Quit:
1129     return Status;
1130 }
1131 
1132 NTSTATUS
1133 NTAPI
ElfFlushFile(IN PEVTLOGFILE LogFile)1134 ElfFlushFile(
1135     IN PEVTLOGFILE LogFile)
1136 {
1137     NTSTATUS Status;
1138     LARGE_INTEGER FileOffset;
1139     SIZE_T WrittenLength;
1140 
1141     ASSERT(LogFile);
1142 
1143     if (LogFile->ReadOnly)
1144         return STATUS_SUCCESS; // STATUS_ACCESS_DENIED;
1145 
1146     /*
1147      * NOTE that both the EOF record *AND* the log file header
1148      * are supposed to be already updated!
1149      * We just remove the dirty log bit.
1150      */
1151     LogFile->Header.Flags &= ~ELF_LOGFILE_HEADER_DIRTY;
1152 
1153     /* Update the log file header */
1154     FileOffset.QuadPart = 0LL;
1155     Status = LogFile->FileWrite(LogFile,
1156                                 &FileOffset,
1157                                 &LogFile->Header,
1158                                 sizeof(EVENTLOGHEADER),
1159                                 &WrittenLength);
1160     if (!NT_SUCCESS(Status))
1161     {
1162         EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1163         return Status;
1164     }
1165 
1166     /* Flush the log file */
1167     Status = LogFile->FileFlush(LogFile, NULL, 0);
1168     if (!NT_SUCCESS(Status))
1169     {
1170         EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status);
1171         return Status;
1172     }
1173 
1174     return STATUS_SUCCESS;
1175 }
1176 
1177 VOID
1178 NTAPI
ElfCloseFile(IN PEVTLOGFILE LogFile)1179 ElfCloseFile(  // ElfFree
1180     IN PEVTLOGFILE LogFile)
1181 {
1182     ASSERT(LogFile);
1183 
1184     /* Flush the log file */
1185     ElfFlushFile(LogFile);
1186 
1187     /* Free the data */
1188     LogFile->Free(LogFile->OffsetInfo, 0, TAG_ELF);
1189 
1190     if (LogFile->FileName.Buffer)
1191         LogFile->Free(LogFile->FileName.Buffer, 0, TAG_ELF);
1192     RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0);
1193 }
1194 
1195 NTSTATUS
1196 NTAPI
ElfReadRecord(IN PEVTLOGFILE LogFile,IN ULONG RecordNumber,OUT PEVENTLOGRECORD Record,IN SIZE_T BufSize,OUT PSIZE_T BytesRead OPTIONAL,OUT PSIZE_T BytesNeeded OPTIONAL)1197 ElfReadRecord(
1198     IN  PEVTLOGFILE LogFile,
1199     IN  ULONG RecordNumber,
1200     OUT PEVENTLOGRECORD Record,
1201     IN  SIZE_T  BufSize, // Length
1202     OUT PSIZE_T BytesRead OPTIONAL,
1203     OUT PSIZE_T BytesNeeded OPTIONAL)
1204 {
1205     NTSTATUS Status;
1206     LARGE_INTEGER FileOffset;
1207     ULONG RecOffset;
1208     ULONG RecSize;
1209     SIZE_T ReadLength;
1210 
1211     ASSERT(LogFile);
1212 
1213     if (BytesRead)
1214         *BytesRead = 0;
1215 
1216     if (BytesNeeded)
1217         *BytesNeeded = 0;
1218 
1219     /* Retrieve the offset of the event record */
1220     RecOffset = ElfpOffsetByNumber(LogFile, RecordNumber);
1221     if (RecOffset == 0)
1222         return STATUS_NOT_FOUND;
1223 
1224     /* Retrieve its full size */
1225     FileOffset.QuadPart = RecOffset;
1226     Status = LogFile->FileRead(LogFile,
1227                                &FileOffset,
1228                                &RecSize,
1229                                sizeof(RecSize),
1230                                &ReadLength);
1231     if (!NT_SUCCESS(Status))
1232     {
1233         EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
1234         // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1235         return Status;
1236     }
1237 
1238     /* Check whether the buffer is big enough to hold the event record */
1239     if (BufSize < RecSize)
1240     {
1241         if (BytesNeeded)
1242             *BytesNeeded = RecSize;
1243 
1244         return STATUS_BUFFER_TOO_SMALL;
1245     }
1246 
1247     /* Read the event record into the buffer */
1248     FileOffset.QuadPart = RecOffset;
1249     Status = ReadLogBuffer(LogFile,
1250                            Record,
1251                            RecSize,
1252                            &ReadLength,
1253                            &FileOffset,
1254                            NULL);
1255     if (!NT_SUCCESS(Status))
1256     {
1257         EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
1258         // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1259     }
1260 
1261     if (BytesRead)
1262         *BytesRead = ReadLength;
1263 
1264     return Status;
1265 }
1266 
1267 NTSTATUS
1268 NTAPI
ElfWriteRecord(IN PEVTLOGFILE LogFile,IN PEVENTLOGRECORD Record,IN SIZE_T BufSize)1269 ElfWriteRecord(
1270     IN PEVTLOGFILE LogFile,
1271     IN PEVENTLOGRECORD Record,
1272     IN SIZE_T BufSize)
1273 {
1274     NTSTATUS Status;
1275     LARGE_INTEGER FileOffset, NextOffset;
1276     SIZE_T ReadLength, WrittenLength;
1277     EVENTLOGEOF EofRec;
1278     EVENTLOGRECORD RecBuf;
1279     ULONG FreeSpace = 0;
1280     ULONG UpperBound;
1281     ULONG RecOffset, WriteOffset;
1282 
1283     ASSERT(LogFile);
1284 
1285     if (LogFile->ReadOnly)
1286         return STATUS_ACCESS_DENIED;
1287 
1288     // ASSERT(sizeof(*Record) == sizeof(RecBuf));
1289 
1290     if (!Record || BufSize < sizeof(*Record))
1291         return STATUS_INVALID_PARAMETER;
1292 
1293     Record->RecordNumber = LogFile->Header.CurrentRecordNumber;
1294 
1295     /* Compute the available log free space */
1296     if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
1297         FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER);
1298     else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1299         FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset;
1300 
1301     LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY;
1302 
1303     /* If the event log was empty, it will now contain one record */
1304     if (LogFile->Header.OldestRecordNumber == 0)
1305         LogFile->Header.OldestRecordNumber = 1;
1306 
1307     /* By default we append the new record at the old EOF record offset */
1308     WriteOffset = LogFile->Header.EndOffset;
1309 
1310     /*
1311      * Check whether the log is going to wrap (the events being overwritten).
1312      */
1313 
1314     if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
1315         UpperBound = LogFile->Header.MaxSize;
1316     else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1317         UpperBound = LogFile->Header.StartOffset;
1318 
1319     // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
1320     if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
1321     {
1322         EVTLTRACE("The event log file has reached maximum size (0x%x), wrapping...\n"
1323                "UpperBound = 0x%x, WriteOffset = 0x%x, BufSize = 0x%x\n",
1324                LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize);
1325         /* This will be done later */
1326     }
1327 
1328     if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
1329          (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
1330     {
1331         // ASSERT(UpperBound  == LogFile->Header.MaxSize);
1332         // ASSERT(WriteOffset == LogFile->Header.EndOffset);
1333 
1334         /*
1335          * We cannot fit the EVENTLOGRECORD header of the buffer before
1336          * the end of the file. We need to pad the end of the log with
1337          * 0x00000027, normally we will need to pad at most 0x37 bytes
1338          * (corresponding to sizeof(EVENTLOGRECORD) - 1).
1339          */
1340 
1341         /* Rewind to the beginning of the log, just after the header */
1342         WriteOffset = sizeof(EVENTLOGHEADER);
1343         /**/UpperBound = LogFile->Header.StartOffset;/**/
1344 
1345         FreeSpace = LogFile->Header.StartOffset - WriteOffset;
1346 
1347         LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
1348     }
1349     /*
1350      * Otherwise, we can fit the header and only part
1351      * of the data will overwrite the oldest records.
1352      *
1353      * It might be possible that all the event record can fit in one piece,
1354      * but that the EOF record needs to be split. This is not a problem,
1355      * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be.
1356      */
1357 
1358     if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
1359     {
1360         ULONG OrgOldestRecordNumber, OldestRecordNumber;
1361 
1362         // DPRINT("EventLogFile has reached maximum size, wrapping...\n");
1363 
1364         OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber;
1365 
1366         // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record???
1367         // NOTE: It should be, by construction (and this should have been checked when
1368         // initializing a new, or existing log).
1369 
1370         /*
1371          * Determine how many old records need to be overwritten.
1372          * Check the size of the record as the record added may be larger.
1373          * Need to take into account that we append the EOF record.
1374          */
1375         while (FreeSpace < BufSize + sizeof(EofRec))
1376         {
1377             /* Get the oldest record data */
1378             RecOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber);
1379             if (RecOffset == 0)
1380             {
1381                 EVTLTRACE1("Record number %d cannot be found, or log file is full and cannot wrap!\n", OldestRecordNumber);
1382                 LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
1383                 return STATUS_LOG_FILE_FULL;
1384             }
1385 
1386             RtlZeroMemory(&RecBuf, sizeof(RecBuf));
1387 
1388             FileOffset.QuadPart = RecOffset;
1389             Status = LogFile->FileRead(LogFile,
1390                                        &FileOffset,
1391                                        &RecBuf,
1392                                        sizeof(RecBuf),
1393                                        &ReadLength);
1394             if (!NT_SUCCESS(Status))
1395             {
1396                 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
1397                 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1398                 return Status;
1399             }
1400 
1401             if (RecBuf.Reserved != LOGFILE_SIGNATURE)
1402             {
1403                 EVTLTRACE1("The event log file is corrupted!\n");
1404                 return STATUS_EVENTLOG_FILE_CORRUPT;
1405             }
1406 
1407             /*
1408              * Check whether this event can be overwritten by comparing its
1409              * written timestamp with the log's retention value. This value
1410              * is the time interval, in seconds, that events records are
1411              * protected from being overwritten.
1412              *
1413              * If the retention value is zero the events are always overwritten.
1414              *
1415              * If the retention value is non-zero, when the age of an event,
1416              * in seconds, reaches or exceeds this value, it can be overwritten.
1417              * Also if the events are in the future, we do not overwrite them.
1418              */
1419             if (LogFile->Header.Retention != 0 &&
1420                 (Record->TimeWritten <  RecBuf.TimeWritten ||
1421                 (Record->TimeWritten >= RecBuf.TimeWritten &&
1422                  Record->TimeWritten -  RecBuf.TimeWritten < LogFile->Header.Retention)))
1423             {
1424                 EVTLTRACE1("The event log file is full and cannot wrap because of the retention policy.\n");
1425                 LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
1426                 return STATUS_LOG_FILE_FULL;
1427             }
1428 
1429             /*
1430              * Advance the oldest record number, add the event record length
1431              * (as long as it is valid...) then take account for the possible
1432              * paddind after the record, in case this is the last one at the
1433              * end of the file.
1434              */
1435             OldestRecordNumber++;
1436             RecOffset += RecBuf.Length;
1437             FreeSpace += RecBuf.Length;
1438 
1439             /*
1440              * If this was the last event record before the end of the log file,
1441              * the next one should start at the beginning of the log and the space
1442              * between the last event record and the end of the file is padded.
1443              */
1444             if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD))
1445             {
1446                 /* Add the padding size */
1447                 FreeSpace += LogFile->Header.MaxSize - RecOffset;
1448             }
1449         }
1450 
1451         EVTLTRACE("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize);
1452 
1453         /* The log records are wrapping */
1454         LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
1455 
1456 
1457         // FIXME: May lead to corruption if the other subsequent calls fail...
1458 
1459         /*
1460          * We have validated all the region of events to be discarded,
1461          * now we can perform their deletion.
1462          */
1463         ElfpDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1);
1464         LogFile->Header.OldestRecordNumber = OldestRecordNumber;
1465         LogFile->Header.StartOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber);
1466         if (LogFile->Header.StartOffset == 0)
1467         {
1468             /*
1469              * We have deleted all the existing event records to make place
1470              * for the new one. We can put it at the start of the event log.
1471              */
1472             LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
1473             WriteOffset = LogFile->Header.StartOffset;
1474             LogFile->Header.EndOffset = WriteOffset;
1475         }
1476 
1477         EVTLTRACE("MaxSize = 0x%x, StartOffset = 0x%x, WriteOffset = 0x%x, EndOffset = 0x%x, BufSize = 0x%x\n"
1478                   "OldestRecordNumber = %d\n",
1479                   LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize,
1480                   OldestRecordNumber);
1481     }
1482 
1483     /*
1484      * Expand the log file if needed.
1485      * NOTE: It may be needed to perform this task a bit sooner if we need
1486      * such a thing for performing read operations, in the future...
1487      * Or if this operation needs to modify 'FreeSpace'...
1488      */
1489     if (LogFile->CurrentSize < LogFile->Header.MaxSize)
1490     {
1491         EVTLTRACE1("Expanding the log file from %lu to %lu\n",
1492                 LogFile->CurrentSize, LogFile->Header.MaxSize);
1493 
1494         LogFile->CurrentSize = LogFile->Header.MaxSize;
1495         LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
1496     }
1497 
1498     /* Since we can write events in the log, clear the log full flag */
1499     LogFile->Header.Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN;
1500 
1501     /* Pad the end of the log */
1502     // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
1503     if (WriteOffset < LogFile->Header.EndOffset)
1504     {
1505         /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */
1506         WrittenLength = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG));
1507         RtlFillMemoryUlong(&RecBuf, WrittenLength, 0x00000027);
1508 
1509         FileOffset.QuadPart = LogFile->Header.EndOffset;
1510         Status = LogFile->FileWrite(LogFile,
1511                                     &FileOffset,
1512                                     &RecBuf,
1513                                     WrittenLength,
1514                                     &WrittenLength);
1515         if (!NT_SUCCESS(Status))
1516         {
1517             EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1518             // return Status;
1519         }
1520     }
1521 
1522     /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
1523     FileOffset.QuadPart = WriteOffset;
1524     Status = WriteLogBuffer(LogFile,
1525                             Record,
1526                             BufSize,
1527                             &WrittenLength,
1528                             &FileOffset,
1529                             &NextOffset);
1530     if (!NT_SUCCESS(Status))
1531     {
1532         EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
1533         return Status;
1534     }
1535     /* FileOffset now contains the offset just after the end of the record buffer */
1536     FileOffset = NextOffset;
1537 
1538     if (!ElfpAddOffsetInformation(LogFile,
1539                                   Record->RecordNumber,
1540                                   WriteOffset))
1541     {
1542         return STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT;
1543     }
1544 
1545     LogFile->Header.CurrentRecordNumber++;
1546     if (LogFile->Header.CurrentRecordNumber == 0)
1547         LogFile->Header.CurrentRecordNumber = 1;
1548 
1549     /*
1550      * Write the new EOF record offset just after the event record.
1551      * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF)
1552      * bytes remains between the end of the record and the end of the log file.
1553      */
1554     LogFile->Header.EndOffset = FileOffset.QuadPart;
1555 
1556     RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
1557     EofRec.BeginRecord = LogFile->Header.StartOffset;
1558     EofRec.EndRecord   = LogFile->Header.EndOffset;
1559     EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1560     EofRec.OldestRecordNumber  = LogFile->Header.OldestRecordNumber;
1561 
1562     // FileOffset.QuadPart = LogFile->Header.EndOffset;
1563     Status = WriteLogBuffer(LogFile,
1564                             &EofRec,
1565                             sizeof(EofRec),
1566                             &WrittenLength,
1567                             &FileOffset,
1568                             &NextOffset);
1569     if (!NT_SUCCESS(Status))
1570     {
1571         EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
1572         return Status;
1573     }
1574     FileOffset = NextOffset;
1575 
1576     /* Flush the log file */
1577     Status = ElfFlushFile(LogFile);
1578     if (!NT_SUCCESS(Status))
1579     {
1580         EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status);
1581         return STATUS_EVENTLOG_FILE_CORRUPT; // Status;
1582     }
1583 
1584     return Status;
1585 }
1586 
1587 ULONG
1588 NTAPI
ElfGetOldestRecord(IN PEVTLOGFILE LogFile)1589 ElfGetOldestRecord(
1590     IN PEVTLOGFILE LogFile)
1591 {
1592     ASSERT(LogFile);
1593     return LogFile->Header.OldestRecordNumber;
1594 }
1595 
1596 ULONG
1597 NTAPI
ElfGetCurrentRecord(IN PEVTLOGFILE LogFile)1598 ElfGetCurrentRecord(
1599     IN PEVTLOGFILE LogFile)
1600 {
1601     ASSERT(LogFile);
1602     return LogFile->Header.CurrentRecordNumber;
1603 }
1604 
1605 ULONG
1606 NTAPI
ElfGetFlags(IN PEVTLOGFILE LogFile)1607 ElfGetFlags(
1608     IN PEVTLOGFILE LogFile)
1609 {
1610     ASSERT(LogFile);
1611     return LogFile->Header.Flags;
1612 }
1613 
1614 #if DBG
PRINT_HEADER(PEVENTLOGHEADER Header)1615 VOID PRINT_HEADER(PEVENTLOGHEADER Header)
1616 {
1617     ULONG Flags = Header->Flags;
1618 
1619     EVTLTRACE1("PRINT_HEADER(0x%p)\n", Header);
1620 
1621     DbgPrint("HeaderSize    = %lu\n" , Header->HeaderSize);
1622     DbgPrint("Signature     = 0x%x\n", Header->Signature);
1623     DbgPrint("MajorVersion  = %lu\n" , Header->MajorVersion);
1624     DbgPrint("MinorVersion  = %lu\n" , Header->MinorVersion);
1625     DbgPrint("StartOffset   = 0x%x\n", Header->StartOffset);
1626     DbgPrint("EndOffset     = 0x%x\n", Header->EndOffset);
1627     DbgPrint("CurrentRecordNumber = %lu\n", Header->CurrentRecordNumber);
1628     DbgPrint("OldestRecordNumber  = %lu\n", Header->OldestRecordNumber);
1629     DbgPrint("MaxSize       = 0x%x\n", Header->MaxSize);
1630     DbgPrint("Retention     = 0x%x\n", Header->Retention);
1631     DbgPrint("EndHeaderSize = %lu\n" , Header->EndHeaderSize);
1632     DbgPrint("Flags: ");
1633     if (Flags & ELF_LOGFILE_HEADER_DIRTY)
1634     {
1635         DbgPrint("ELF_LOGFILE_HEADER_DIRTY");
1636         Flags &= ~ELF_LOGFILE_HEADER_DIRTY;
1637     }
1638     if (Flags) DbgPrint(" | ");
1639     if (Flags & ELF_LOGFILE_HEADER_WRAP)
1640     {
1641         DbgPrint("ELF_LOGFILE_HEADER_WRAP");
1642         Flags &= ~ELF_LOGFILE_HEADER_WRAP;
1643     }
1644     if (Flags) DbgPrint(" | ");
1645     if (Flags & ELF_LOGFILE_LOGFULL_WRITTEN)
1646     {
1647         DbgPrint("ELF_LOGFILE_LOGFULL_WRITTEN");
1648         Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN;
1649     }
1650     if (Flags) DbgPrint(" | ");
1651     if (Flags & ELF_LOGFILE_ARCHIVE_SET)
1652     {
1653         DbgPrint("ELF_LOGFILE_ARCHIVE_SET");
1654         Flags &= ~ELF_LOGFILE_ARCHIVE_SET;
1655     }
1656     if (Flags) DbgPrint(" | 0x%x", Flags);
1657     DbgPrint("\n");
1658 }
1659 #endif
1660