[EVTLIB]: Don't spam the log with "add new record" lines.
[reactos.git] / reactos / sdk / lib / evtlib / evtlib.c
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 a library for reading and writing EventLog files
6 * 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
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
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
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
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);
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
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
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
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 DPRINT1("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 DPRINT1("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);
775 return STATUS_EVENTLOG_FILE_CORRUPT;
776 }
777 if (ReadLength != RecBuf.Length)
778 {
779 DPRINT1("Oh oh!!\n");
780 LogFile->Free(pRecBuf, 0);
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);
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);
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);
811 return STATUS_EVENTLOG_FILE_CORRUPT;
812 }
813
814 LogFile->Free(pRecBuf, 0);
815
816 if (NextOffset.QuadPart == LogFile->Header.EndOffset)
817 {
818 /* We have finished enumerating all the event records */
819 DPRINT1("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 DPRINT1("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 DPRINT1("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
876 ElfCreateFile(
877 IN 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);
956
957 if (LogFile->FileName.Buffer)
958 LogFile->Free(LogFile->FileName.Buffer, 0);
959 }
960
961 return Status;
962
963 }
964
965 NTSTATUS
966 NTAPI
967 ElfReCreateFile(
968 IN PEVTLOGFILE LogFile)
969 {
970 ASSERT(LogFile);
971
972 return ElfpInitNewFile(LogFile,
973 LogFile->CurrentSize,
974 LogFile->Header.MaxSize,
975 LogFile->Header.Retention);
976 }
977
978 NTSTATUS
979 NTAPI
980 ElfBackupFile(
981 IN PEVTLOGFILE LogFile,
982 IN PEVTLOGFILE BackupLogFile)
983 {
984 NTSTATUS Status;
985
986 LARGE_INTEGER FileOffset;
987 SIZE_T ReadLength, WrittenLength;
988 PEVENTLOGHEADER Header;
989 EVENTLOGRECORD RecBuf;
990 EVENTLOGEOF EofRec;
991 ULONG i;
992 ULONG RecOffset;
993 PVOID Buffer = NULL;
994
995 ASSERT(LogFile);
996
997 RtlZeroMemory(BackupLogFile, sizeof(*BackupLogFile));
998
999 BackupLogFile->FileSetSize = LogFile->FileSetSize;
1000 BackupLogFile->FileWrite = LogFile->FileWrite;
1001 BackupLogFile->FileFlush = LogFile->FileFlush;
1002
1003 // BackupLogFile->CurrentSize = LogFile->CurrentSize;
1004
1005 BackupLogFile->ReadOnly = FALSE;
1006
1007 /* Initialize the (dirty) log file header */
1008 Header = &BackupLogFile->Header;
1009 Header->HeaderSize = sizeof(EVENTLOGHEADER);
1010 Header->Signature = LOGFILE_SIGNATURE;
1011 Header->MajorVersion = MAJORVER;
1012 Header->MinorVersion = MINORVER;
1013 Header->StartOffset = sizeof(EVENTLOGHEADER);
1014 Header->EndOffset = sizeof(EVENTLOGHEADER);
1015 Header->CurrentRecordNumber = 1;
1016 Header->OldestRecordNumber = 0;
1017 Header->MaxSize = LogFile->Header.MaxSize;
1018 Header->Flags = ELF_LOGFILE_HEADER_DIRTY;
1019 Header->Retention = LogFile->Header.Retention;
1020 Header->EndHeaderSize = sizeof(EVENTLOGHEADER);
1021
1022 /* Write the (dirty) log file header */
1023 FileOffset.QuadPart = 0LL;
1024 Status = BackupLogFile->FileWrite(BackupLogFile,
1025 &FileOffset,
1026 Header,
1027 sizeof(EVENTLOGHEADER),
1028 &WrittenLength);
1029 if (!NT_SUCCESS(Status))
1030 {
1031 EVTLTRACE1("Failed to write the log file header (Status 0x%08lx)\n", Status);
1032 goto Quit;
1033 }
1034
1035 for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++)
1036 {
1037 RecOffset = ElfpOffsetByNumber(LogFile, i);
1038 if (RecOffset == 0)
1039 break;
1040
1041 /* Read the next EVENTLOGRECORD header at once (it cannot be split) */
1042 FileOffset.QuadPart = RecOffset;
1043 Status = LogFile->FileRead(LogFile,
1044 &FileOffset,
1045 &RecBuf,
1046 sizeof(RecBuf),
1047 &ReadLength);
1048 if (!NT_SUCCESS(Status))
1049 {
1050 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
1051 goto Quit;
1052 }
1053
1054 // if (ReadLength != sizeof(RecBuf))
1055 // break;
1056
1057 Buffer = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF);
1058 if (Buffer == NULL)
1059 {
1060 EVTLTRACE1("Allocate() failed!\n");
1061 goto Quit;
1062 }
1063
1064 /* Read the full EVENTLOGRECORD (header + data) with wrapping */
1065 Status = ReadLogBuffer(LogFile,
1066 Buffer,
1067 RecBuf.Length,
1068 &ReadLength,
1069 &FileOffset,
1070 NULL);
1071 if (!NT_SUCCESS(Status))
1072 {
1073 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
1074 LogFile->Free(Buffer, 0);
1075 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1076 goto Quit;
1077 }
1078
1079 /* Write the event record (no wrap for the backup log) */
1080 Status = BackupLogFile->FileWrite(BackupLogFile,
1081 NULL,
1082 Buffer,
1083 RecBuf.Length,
1084 &WrittenLength);
1085 if (!NT_SUCCESS(Status))
1086 {
1087 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1088 LogFile->Free(Buffer, 0);
1089 goto Quit;
1090 }
1091
1092 /* Update the header information */
1093 Header->EndOffset += RecBuf.Length;
1094
1095 /* Free the buffer */
1096 LogFile->Free(Buffer, 0);
1097 Buffer = NULL;
1098 }
1099
1100 // Quit:
1101
1102 /* Initialize the ELF_EOF_RECORD and write it (no wrap for the backup log) */
1103 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
1104 EofRec.BeginRecord = Header->StartOffset;
1105 EofRec.EndRecord = Header->EndOffset;
1106 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1107 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
1108
1109 Status = BackupLogFile->FileWrite(BackupLogFile,
1110 NULL,
1111 &EofRec,
1112 sizeof(EofRec),
1113 &WrittenLength);
1114 if (!NT_SUCCESS(Status))
1115 {
1116 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1117 goto Quit;
1118 }
1119
1120 /* Update the header information */
1121 Header->CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1122 Header->OldestRecordNumber = LogFile->Header.OldestRecordNumber;
1123 Header->MaxSize = ROUND_UP(Header->EndOffset + sizeof(EofRec), sizeof(ULONG));
1124 Header->Flags = 0; // FIXME?
1125
1126 /* Flush the log file - Write the (clean) log file header */
1127 Status = ElfFlushFile(BackupLogFile);
1128
1129 Quit:
1130 return Status;
1131 }
1132
1133 NTSTATUS
1134 NTAPI
1135 ElfFlushFile(
1136 IN PEVTLOGFILE LogFile)
1137 {
1138 NTSTATUS Status;
1139 LARGE_INTEGER FileOffset;
1140 SIZE_T WrittenLength;
1141
1142 ASSERT(LogFile);
1143
1144 if (LogFile->ReadOnly)
1145 return STATUS_SUCCESS; // STATUS_ACCESS_DENIED;
1146
1147 /*
1148 * NOTE that both the EOF record *AND* the log file header
1149 * are supposed to be already updated!
1150 * We just remove the dirty log bit.
1151 */
1152 LogFile->Header.Flags &= ~ELF_LOGFILE_HEADER_DIRTY;
1153
1154 /* Update the log file header */
1155 FileOffset.QuadPart = 0LL;
1156 Status = LogFile->FileWrite(LogFile,
1157 &FileOffset,
1158 &LogFile->Header,
1159 sizeof(EVENTLOGHEADER),
1160 &WrittenLength);
1161 if (!NT_SUCCESS(Status))
1162 {
1163 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1164 return Status;
1165 }
1166
1167 /* Flush the log file */
1168 Status = LogFile->FileFlush(LogFile, NULL, 0);
1169 if (!NT_SUCCESS(Status))
1170 {
1171 EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status);
1172 return Status;
1173 }
1174
1175 return STATUS_SUCCESS;
1176 }
1177
1178 VOID
1179 NTAPI
1180 ElfCloseFile( // ElfFree
1181 IN PEVTLOGFILE LogFile)
1182 {
1183 ASSERT(LogFile);
1184
1185 /* Flush the log file */
1186 ElfFlushFile(LogFile);
1187
1188 /* Free the data */
1189 LogFile->Free(LogFile->OffsetInfo, 0);
1190
1191 if (LogFile->FileName.Buffer)
1192 LogFile->Free(LogFile->FileName.Buffer, 0);
1193 RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0);
1194 }
1195
1196 NTSTATUS
1197 NTAPI
1198 ElfReadRecord(
1199 IN PEVTLOGFILE LogFile,
1200 IN ULONG RecordNumber,
1201 OUT PEVENTLOGRECORD Record,
1202 IN SIZE_T BufSize, // Length
1203 OUT PSIZE_T BytesRead OPTIONAL,
1204 OUT PSIZE_T BytesNeeded OPTIONAL)
1205 {
1206 NTSTATUS Status;
1207 LARGE_INTEGER FileOffset;
1208 ULONG RecOffset;
1209 SIZE_T RecSize;
1210 SIZE_T ReadLength;
1211
1212 ASSERT(LogFile);
1213
1214 if (BytesRead)
1215 *BytesRead = 0;
1216
1217 if (BytesNeeded)
1218 *BytesNeeded = 0;
1219
1220 /* Retrieve the offset of the event record */
1221 RecOffset = ElfpOffsetByNumber(LogFile, RecordNumber);
1222 if (RecOffset == 0)
1223 return STATUS_NOT_FOUND;
1224
1225 /* Retrieve its full size */
1226 FileOffset.QuadPart = RecOffset;
1227 Status = LogFile->FileRead(LogFile,
1228 &FileOffset,
1229 &RecSize,
1230 sizeof(RecSize),
1231 &ReadLength);
1232 if (!NT_SUCCESS(Status))
1233 {
1234 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
1235 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1236 return Status;
1237 }
1238
1239 /* Check whether the buffer is big enough to hold the event record */
1240 if (BufSize < RecSize)
1241 {
1242 if (BytesNeeded)
1243 *BytesNeeded = RecSize;
1244
1245 return STATUS_BUFFER_TOO_SMALL;
1246 }
1247
1248 /* Read the event record into the buffer */
1249 FileOffset.QuadPart = RecOffset;
1250 Status = ReadLogBuffer(LogFile,
1251 Record,
1252 RecSize,
1253 &ReadLength,
1254 &FileOffset,
1255 NULL);
1256 if (!NT_SUCCESS(Status))
1257 {
1258 EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status);
1259 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1260 }
1261
1262 if (BytesRead)
1263 *BytesRead = ReadLength;
1264
1265 return Status;
1266 }
1267
1268 NTSTATUS
1269 NTAPI
1270 ElfWriteRecord(
1271 IN PEVTLOGFILE LogFile,
1272 IN PEVENTLOGRECORD Record,
1273 IN SIZE_T BufSize)
1274 {
1275 NTSTATUS Status;
1276 LARGE_INTEGER FileOffset, NextOffset;
1277 SIZE_T ReadLength, WrittenLength;
1278 EVENTLOGEOF EofRec;
1279 EVENTLOGRECORD RecBuf;
1280 ULONG FreeSpace = 0;
1281 ULONG UpperBound;
1282 ULONG RecOffset, WriteOffset;
1283
1284 ASSERT(LogFile);
1285
1286 if (LogFile->ReadOnly)
1287 return STATUS_ACCESS_DENIED;
1288
1289 // ASSERT(sizeof(*Record) == sizeof(RecBuf));
1290
1291 if (!Record || BufSize < sizeof(*Record))
1292 return STATUS_INVALID_PARAMETER;
1293
1294 Record->RecordNumber = LogFile->Header.CurrentRecordNumber;
1295
1296 /* Compute the available log free space */
1297 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
1298 FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER);
1299 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1300 FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset;
1301
1302 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY;
1303
1304 /* If the event log was empty, it will now contain one record */
1305 if (LogFile->Header.OldestRecordNumber == 0)
1306 LogFile->Header.OldestRecordNumber = 1;
1307
1308 /* By default we append the new record at the old EOF record offset */
1309 WriteOffset = LogFile->Header.EndOffset;
1310
1311 /*
1312 * Check whether the log is going to wrap (the events being overwritten).
1313 */
1314
1315 if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset)
1316 UpperBound = LogFile->Header.MaxSize;
1317 else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset)
1318 UpperBound = LogFile->Header.StartOffset;
1319
1320 // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec))
1321 if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
1322 {
1323 EVTLTRACE("The event log file has reached maximum size (0x%x), wrapping...\n"
1324 "UpperBound = 0x%x, WriteOffset = 0x%x, BufSize = 0x%x\n",
1325 LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize);
1326 /* This will be done later */
1327 }
1328
1329 if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) &&
1330 (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf))
1331 {
1332 // ASSERT(UpperBound == LogFile->Header.MaxSize);
1333 // ASSERT(WriteOffset == LogFile->Header.EndOffset);
1334
1335 /*
1336 * We cannot fit the EVENTLOGRECORD header of the buffer before
1337 * the end of the file. We need to pad the end of the log with
1338 * 0x00000027, normally we will need to pad at most 0x37 bytes
1339 * (corresponding to sizeof(EVENTLOGRECORD) - 1).
1340 */
1341
1342 /* Rewind to the beginning of the log, just after the header */
1343 WriteOffset = sizeof(EVENTLOGHEADER);
1344 /**/UpperBound = LogFile->Header.StartOffset;/**/
1345
1346 FreeSpace = LogFile->Header.StartOffset - WriteOffset;
1347
1348 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
1349 }
1350 /*
1351 * Otherwise, we can fit the header and only part
1352 * of the data will overwrite the oldest records.
1353 *
1354 * It might be possible that all the event record can fit in one piece,
1355 * but that the EOF record needs to be split. This is not a problem,
1356 * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be.
1357 */
1358
1359 if (UpperBound - WriteOffset < BufSize + sizeof(EofRec))
1360 {
1361 ULONG OrgOldestRecordNumber, OldestRecordNumber;
1362
1363 // DPRINT("EventLogFile has reached maximum size, wrapping...\n");
1364
1365 OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber;
1366
1367 // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record???
1368 // NOTE: It should be, by construction (and this should have been checked when
1369 // initializing a new, or existing log).
1370
1371 /*
1372 * Determine how many old records need to be overwritten.
1373 * Check the size of the record as the record added may be larger.
1374 * Need to take into account that we append the EOF record.
1375 */
1376 while (FreeSpace < BufSize + sizeof(EofRec))
1377 {
1378 /* Get the oldest record data */
1379 RecOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber);
1380 if (RecOffset == 0)
1381 {
1382 EVTLTRACE1("Record number %d cannot be found, or log file is full and cannot wrap!\n", OldestRecordNumber);
1383 LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
1384 return STATUS_LOG_FILE_FULL;
1385 }
1386
1387 RtlZeroMemory(&RecBuf, sizeof(RecBuf));
1388
1389 FileOffset.QuadPart = RecOffset;
1390 Status = LogFile->FileRead(LogFile,
1391 &FileOffset,
1392 &RecBuf,
1393 sizeof(RecBuf),
1394 &ReadLength);
1395 if (!NT_SUCCESS(Status))
1396 {
1397 EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status);
1398 // Status = STATUS_EVENTLOG_FILE_CORRUPT;
1399 return Status;
1400 }
1401
1402 if (RecBuf.Reserved != LOGFILE_SIGNATURE)
1403 {
1404 EVTLTRACE1("The event log file is corrupted!\n");
1405 return STATUS_EVENTLOG_FILE_CORRUPT;
1406 }
1407
1408 /*
1409 * Check whether this event can be overwritten by comparing its
1410 * written timestamp with the log's retention value. This value
1411 * is the time interval, in seconds, that events records are
1412 * protected from being overwritten.
1413 *
1414 * If the retention value is zero the events are always overwritten.
1415 *
1416 * If the retention value is non-zero, when the age of an event,
1417 * in seconds, reaches or exceeds this value, it can be overwritten.
1418 * Also if the events are in the future, we do not overwrite them.
1419 */
1420 if (LogFile->Header.Retention != 0 &&
1421 (Record->TimeWritten < RecBuf.TimeWritten ||
1422 (Record->TimeWritten >= RecBuf.TimeWritten &&
1423 Record->TimeWritten - RecBuf.TimeWritten < LogFile->Header.Retention)))
1424 {
1425 EVTLTRACE1("The event log file is full and cannot wrap because of the retention policy.\n");
1426 LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN;
1427 return STATUS_LOG_FILE_FULL;
1428 }
1429
1430 /*
1431 * Advance the oldest record number, add the event record length
1432 * (as long as it is valid...) then take account for the possible
1433 * paddind after the record, in case this is the last one at the
1434 * end of the file.
1435 */
1436 OldestRecordNumber++;
1437 RecOffset += RecBuf.Length;
1438 FreeSpace += RecBuf.Length;
1439
1440 /*
1441 * If this was the last event record before the end of the log file,
1442 * the next one should start at the beginning of the log and the space
1443 * between the last event record and the end of the file is padded.
1444 */
1445 if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD))
1446 {
1447 /* Add the padding size */
1448 FreeSpace += LogFile->Header.MaxSize - RecOffset;
1449 }
1450 }
1451
1452 EVTLTRACE("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize);
1453
1454 /* The log records are wrapping */
1455 LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP;
1456
1457
1458 // FIXME: May lead to corruption if the other subsequent calls fail...
1459
1460 /*
1461 * We have validated all the region of events to be discarded,
1462 * now we can perform their deletion.
1463 */
1464 ElfpDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1);
1465 LogFile->Header.OldestRecordNumber = OldestRecordNumber;
1466 LogFile->Header.StartOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber);
1467 if (LogFile->Header.StartOffset == 0)
1468 {
1469 /*
1470 * We have deleted all the existing event records to make place
1471 * for the new one. We can put it at the start of the event log.
1472 */
1473 LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER);
1474 WriteOffset = LogFile->Header.StartOffset;
1475 LogFile->Header.EndOffset = WriteOffset;
1476 }
1477
1478 DPRINT1("MaxSize = 0x%x, StartOffset = 0x%x, WriteOffset = 0x%x, EndOffset = 0x%x, BufSize = 0x%x\n"
1479 "OldestRecordNumber = %d\n",
1480 LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize,
1481 OldestRecordNumber);
1482 }
1483
1484 /*
1485 * Expand the log file if needed.
1486 * NOTE: It may be needed to perform this task a bit sooner if we need
1487 * such a thing for performing read operations, in the future...
1488 * Or if this operation needs to modify 'FreeSpace'...
1489 */
1490 if (LogFile->CurrentSize < LogFile->Header.MaxSize)
1491 {
1492 EVTLTRACE1("Expanding the log file from %lu to %lu\n",
1493 LogFile->CurrentSize, LogFile->Header.MaxSize);
1494
1495 LogFile->CurrentSize = LogFile->Header.MaxSize;
1496 LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0);
1497 }
1498
1499 /* Since we can write events in the log, clear the log full flag */
1500 LogFile->Header.Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN;
1501
1502 /* Pad the end of the log */
1503 // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize)
1504 if (WriteOffset < LogFile->Header.EndOffset)
1505 {
1506 /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */
1507 WrittenLength = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG));
1508 RtlFillMemoryUlong(&RecBuf, WrittenLength, 0x00000027);
1509
1510 FileOffset.QuadPart = LogFile->Header.EndOffset;
1511 Status = LogFile->FileWrite(LogFile,
1512 &FileOffset,
1513 &RecBuf,
1514 WrittenLength,
1515 &WrittenLength);
1516 if (!NT_SUCCESS(Status))
1517 {
1518 EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status);
1519 // return Status;
1520 }
1521 }
1522
1523 /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */
1524 FileOffset.QuadPart = WriteOffset;
1525 Status = WriteLogBuffer(LogFile,
1526 Record,
1527 BufSize,
1528 &WrittenLength,
1529 &FileOffset,
1530 &NextOffset);
1531 if (!NT_SUCCESS(Status))
1532 {
1533 EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
1534 return Status;
1535 }
1536 /* FileOffset now contains the offset just after the end of the record buffer */
1537 FileOffset = NextOffset;
1538
1539 if (!ElfpAddOffsetInformation(LogFile,
1540 Record->RecordNumber,
1541 WriteOffset))
1542 {
1543 return STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT;
1544 }
1545
1546 LogFile->Header.CurrentRecordNumber++;
1547 if (LogFile->Header.CurrentRecordNumber == 0)
1548 LogFile->Header.CurrentRecordNumber = 1;
1549
1550 /*
1551 * Write the new EOF record offset just after the event record.
1552 * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF)
1553 * bytes remains between the end of the record and the end of the log file.
1554 */
1555 LogFile->Header.EndOffset = FileOffset.QuadPart;
1556
1557 RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord));
1558 EofRec.BeginRecord = LogFile->Header.StartOffset;
1559 EofRec.EndRecord = LogFile->Header.EndOffset;
1560 EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber;
1561 EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber;
1562
1563 // FileOffset.QuadPart = LogFile->Header.EndOffset;
1564 Status = WriteLogBuffer(LogFile,
1565 &EofRec,
1566 sizeof(EofRec),
1567 &WrittenLength,
1568 &FileOffset,
1569 &NextOffset);
1570 if (!NT_SUCCESS(Status))
1571 {
1572 EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status);
1573 return Status;
1574 }
1575 FileOffset = NextOffset;
1576
1577 /* Flush the log file */
1578 Status = ElfFlushFile(LogFile);
1579 if (!NT_SUCCESS(Status))
1580 {
1581 EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status);
1582 return STATUS_EVENTLOG_FILE_CORRUPT; // Status;
1583 }
1584
1585 return Status;
1586 }
1587
1588 ULONG
1589 NTAPI
1590 ElfGetOldestRecord(
1591 IN PEVTLOGFILE LogFile)
1592 {
1593 ASSERT(LogFile);
1594 return LogFile->Header.OldestRecordNumber;
1595 }
1596
1597 ULONG
1598 NTAPI
1599 ElfGetCurrentRecord(
1600 IN PEVTLOGFILE LogFile)
1601 {
1602 ASSERT(LogFile);
1603 return LogFile->Header.CurrentRecordNumber;
1604 }
1605
1606 ULONG
1607 NTAPI
1608 ElfGetFlags(
1609 IN PEVTLOGFILE LogFile)
1610 {
1611 ASSERT(LogFile);
1612 return LogFile->Header.Flags;
1613 }
1614
1615 #if DBG
1616 VOID PRINT_HEADER(PEVENTLOGHEADER Header)
1617 {
1618 ULONG Flags = Header->Flags;
1619
1620 EVTLTRACE1("PRINT_HEADER(0x%p)\n", Header);
1621
1622 DbgPrint("HeaderSize = %lu\n" , Header->HeaderSize);
1623 DbgPrint("Signature = 0x%x\n", Header->Signature);
1624 DbgPrint("MajorVersion = %lu\n" , Header->MajorVersion);
1625 DbgPrint("MinorVersion = %lu\n" , Header->MinorVersion);
1626 DbgPrint("StartOffset = 0x%x\n", Header->StartOffset);
1627 DbgPrint("EndOffset = 0x%x\n", Header->EndOffset);
1628 DbgPrint("CurrentRecordNumber = %lu\n", Header->CurrentRecordNumber);
1629 DbgPrint("OldestRecordNumber = %lu\n", Header->OldestRecordNumber);
1630 DbgPrint("MaxSize = 0x%x\n", Header->MaxSize);
1631 DbgPrint("Retention = 0x%x\n", Header->Retention);
1632 DbgPrint("EndHeaderSize = %lu\n" , Header->EndHeaderSize);
1633 DbgPrint("Flags: ");
1634 if (Flags & ELF_LOGFILE_HEADER_DIRTY)
1635 {
1636 DbgPrint("ELF_LOGFILE_HEADER_DIRTY");
1637 Flags &= ~ELF_LOGFILE_HEADER_DIRTY;
1638 }
1639 if (Flags) DbgPrint(" | ");
1640 if (Flags & ELF_LOGFILE_HEADER_WRAP)
1641 {
1642 DbgPrint("ELF_LOGFILE_HEADER_WRAP");
1643 Flags &= ~ELF_LOGFILE_HEADER_WRAP;
1644 }
1645 if (Flags) DbgPrint(" | ");
1646 if (Flags & ELF_LOGFILE_LOGFULL_WRITTEN)
1647 {
1648 DbgPrint("ELF_LOGFILE_LOGFULL_WRITTEN");
1649 Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN;
1650 }
1651 if (Flags) DbgPrint(" | ");
1652 if (Flags & ELF_LOGFILE_ARCHIVE_SET)
1653 {
1654 DbgPrint("ELF_LOGFILE_ARCHIVE_SET");
1655 Flags &= ~ELF_LOGFILE_ARCHIVE_SET;
1656 }
1657 if (Flags) DbgPrint(" | 0x%x", Flags);
1658 DbgPrint("\n");
1659 }
1660 #endif