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