[EVTLIB]: Allow specifying a memory allocation tag when freeing the allocated buffers...
[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 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
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, 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
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, 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 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 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
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
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
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
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
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 SIZE_T 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
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 DPRINT1("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
1589 ElfGetOldestRecord(
1590 IN PEVTLOGFILE LogFile)
1591 {
1592 ASSERT(LogFile);
1593 return LogFile->Header.OldestRecordNumber;
1594 }
1595
1596 ULONG
1597 NTAPI
1598 ElfGetCurrentRecord(
1599 IN PEVTLOGFILE LogFile)
1600 {
1601 ASSERT(LogFile);
1602 return LogFile->Header.CurrentRecordNumber;
1603 }
1604
1605 ULONG
1606 NTAPI
1607 ElfGetFlags(
1608 IN PEVTLOGFILE LogFile)
1609 {
1610 ASSERT(LogFile);
1611 return LogFile->Header.Flags;
1612 }
1613
1614 #if DBG
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