xref: /reactos/base/services/eventlog/file.c (revision 40462c92)
1 /*
2  * PROJECT:         ReactOS EventLog Service
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            base/services/eventlog/file.c
5  * PURPOSE:         Event log file support wrappers
6  * COPYRIGHT:       Copyright 2005 Saveliy Tretiakov
7  *                  Michael Martin
8  *                  Hermes Belusca-Maito
9  */
10 
11 /* INCLUDES ******************************************************************/
12 
13 #include "eventlog.h"
14 #include <ndk/iofuncs.h>
15 #include <ndk/kefuncs.h>
16 
17 #define NDEBUG
18 #include <debug.h>
19 
20 /* LOG FILE LIST - GLOBALS ***************************************************/
21 
22 static LIST_ENTRY LogFileListHead;
23 static CRITICAL_SECTION LogFileListCs;
24 
25 /* LOG FILE LIST - FUNCTIONS *************************************************/
26 
27 VOID LogfListInitialize(VOID)
28 {
29     InitializeCriticalSection(&LogFileListCs);
30     InitializeListHead(&LogFileListHead);
31 }
32 
33 PLOGFILE LogfListItemByName(LPCWSTR Name)
34 {
35     PLIST_ENTRY CurrentEntry;
36     PLOGFILE Item, Result = NULL;
37 
38     ASSERT(Name);
39 
40     EnterCriticalSection(&LogFileListCs);
41 
42     CurrentEntry = LogFileListHead.Flink;
43     while (CurrentEntry != &LogFileListHead)
44     {
45         Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
46 
47         if (Item->LogName && !_wcsicmp(Item->LogName, Name))
48         {
49             Result = Item;
50             break;
51         }
52 
53         CurrentEntry = CurrentEntry->Flink;
54     }
55 
56     LeaveCriticalSection(&LogFileListCs);
57     return Result;
58 }
59 
60 #if 0
61 /* Index starting from 1 */
62 DWORD LogfListItemIndexByName(LPCWSTR Name)
63 {
64     PLIST_ENTRY CurrentEntry;
65     DWORD Result = 0;
66     DWORD i = 1;
67 
68     ASSERT(Name);
69 
70     EnterCriticalSection(&LogFileListCs);
71 
72     CurrentEntry = LogFileListHead.Flink;
73     while (CurrentEntry != &LogFileListHead)
74     {
75         PLOGFILE Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
76 
77         if (Item->LogName && !_wcsicmp(Item->LogName, Name))
78         {
79             Result = i;
80             break;
81         }
82 
83         CurrentEntry = CurrentEntry->Flink;
84         i++;
85     }
86 
87     LeaveCriticalSection(&LogFileListCs);
88     return Result;
89 }
90 #endif
91 
92 /* Index starting from 1 */
93 PLOGFILE LogfListItemByIndex(DWORD Index)
94 {
95     PLIST_ENTRY CurrentEntry;
96     PLOGFILE Result = NULL;
97     DWORD i = 1;
98 
99     EnterCriticalSection(&LogFileListCs);
100 
101     CurrentEntry = LogFileListHead.Flink;
102     while (CurrentEntry != &LogFileListHead)
103     {
104         if (i == Index)
105         {
106             Result = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
107             break;
108         }
109 
110         CurrentEntry = CurrentEntry->Flink;
111         i++;
112     }
113 
114     LeaveCriticalSection(&LogFileListCs);
115     return Result;
116 }
117 
118 DWORD LogfListItemCount(VOID)
119 {
120     PLIST_ENTRY CurrentEntry;
121     DWORD i = 0;
122 
123     EnterCriticalSection(&LogFileListCs);
124 
125     CurrentEntry = LogFileListHead.Flink;
126     while (CurrentEntry != &LogFileListHead)
127     {
128         CurrentEntry = CurrentEntry->Flink;
129         i++;
130     }
131 
132     LeaveCriticalSection(&LogFileListCs);
133     return i;
134 }
135 
136 static VOID
137 LogfListAddItem(PLOGFILE Item)
138 {
139     EnterCriticalSection(&LogFileListCs);
140     InsertTailList(&LogFileListHead, &Item->ListEntry);
141     LeaveCriticalSection(&LogFileListCs);
142 }
143 
144 static VOID
145 LogfListRemoveItem(PLOGFILE Item)
146 {
147     EnterCriticalSection(&LogFileListCs);
148     RemoveEntryList(&Item->ListEntry);
149     LeaveCriticalSection(&LogFileListCs);
150 }
151 
152 
153 /* FUNCTIONS *****************************************************************/
154 
155 // PELF_ALLOCATE_ROUTINE
156 static
157 PVOID NTAPI
158 LogfpAlloc(IN SIZE_T Size,
159            IN ULONG Flags,
160            IN ULONG Tag)
161 {
162     UNREFERENCED_PARAMETER(Tag);
163     return RtlAllocateHeap(GetProcessHeap(), Flags, Size);
164 }
165 
166 // PELF_FREE_ROUTINE
167 static
168 VOID NTAPI
169 LogfpFree(IN PVOID Ptr,
170           IN ULONG Flags,
171           IN ULONG Tag)
172 {
173     UNREFERENCED_PARAMETER(Tag);
174     RtlFreeHeap(GetProcessHeap(), Flags, Ptr);
175 }
176 
177 // PELF_FILE_READ_ROUTINE
178 static
179 NTSTATUS NTAPI
180 LogfpReadFile(IN  PEVTLOGFILE LogFile,
181               IN  PLARGE_INTEGER FileOffset,
182               OUT PVOID   Buffer,
183               IN  SIZE_T  Length,
184               OUT PSIZE_T ReadLength OPTIONAL)
185 {
186     NTSTATUS Status;
187     PLOGFILE pLogFile = (PLOGFILE)LogFile;
188     IO_STATUS_BLOCK IoStatusBlock;
189 
190     if (ReadLength)
191         *ReadLength = 0;
192 
193     Status = NtReadFile(pLogFile->FileHandle,
194                         NULL,
195                         NULL,
196                         NULL,
197                         &IoStatusBlock,
198                         Buffer,
199                         Length,
200                         FileOffset,
201                         NULL);
202 
203     if (ReadLength)
204         *ReadLength = IoStatusBlock.Information;
205 
206     return Status;
207 }
208 
209 // PELF_FILE_WRITE_ROUTINE
210 static
211 NTSTATUS NTAPI
212 LogfpWriteFile(IN  PEVTLOGFILE LogFile,
213                IN  PLARGE_INTEGER FileOffset,
214                IN  PVOID   Buffer,
215                IN  SIZE_T  Length,
216                OUT PSIZE_T WrittenLength OPTIONAL)
217 {
218     NTSTATUS Status;
219     PLOGFILE pLogFile = (PLOGFILE)LogFile;
220     IO_STATUS_BLOCK IoStatusBlock;
221 
222     if (WrittenLength)
223         *WrittenLength = 0;
224 
225     Status = NtWriteFile(pLogFile->FileHandle,
226                          NULL,
227                          NULL,
228                          NULL,
229                          &IoStatusBlock,
230                          Buffer,
231                          Length,
232                          FileOffset,
233                          NULL);
234 
235     if (WrittenLength)
236         *WrittenLength = IoStatusBlock.Information;
237 
238     return Status;
239 }
240 
241 // PELF_FILE_SET_SIZE_ROUTINE
242 static
243 NTSTATUS NTAPI
244 LogfpSetFileSize(IN PEVTLOGFILE LogFile,
245                  IN ULONG FileSize,    // SIZE_T
246                  IN ULONG OldFileSize) // SIZE_T
247 {
248     NTSTATUS Status;
249     PLOGFILE pLogFile = (PLOGFILE)LogFile;
250     IO_STATUS_BLOCK IoStatusBlock;
251     FILE_END_OF_FILE_INFORMATION FileEofInfo;
252     FILE_ALLOCATION_INFORMATION FileAllocInfo;
253 
254     UNREFERENCED_PARAMETER(OldFileSize);
255 
256     // FIXME: Should we round up FileSize ??
257 
258     FileEofInfo.EndOfFile.QuadPart = FileSize;
259     Status = NtSetInformationFile(pLogFile->FileHandle,
260                                   &IoStatusBlock,
261                                   &FileEofInfo,
262                                   sizeof(FileEofInfo),
263                                   FileEndOfFileInformation);
264     if (!NT_SUCCESS(Status))
265         return Status;
266 
267     FileAllocInfo.AllocationSize.QuadPart = FileSize;
268     Status = NtSetInformationFile(pLogFile->FileHandle,
269                                   &IoStatusBlock,
270                                   &FileAllocInfo,
271                                   sizeof(FileAllocInfo),
272                                   FileAllocationInformation);
273 
274     return Status;
275 }
276 
277 // PELF_FILE_FLUSH_ROUTINE
278 static
279 NTSTATUS NTAPI
280 LogfpFlushFile(IN PEVTLOGFILE LogFile,
281                IN PLARGE_INTEGER FileOffset,
282                IN ULONG Length)
283 {
284     PLOGFILE pLogFile = (PLOGFILE)LogFile;
285     IO_STATUS_BLOCK IoStatusBlock;
286 
287     UNREFERENCED_PARAMETER(FileOffset);
288     UNREFERENCED_PARAMETER(Length);
289 
290     return NtFlushBuffersFile(pLogFile->FileHandle, &IoStatusBlock);
291 }
292 
293 NTSTATUS
294 LogfCreate(PLOGFILE* LogFile,
295            PCWSTR    LogName,
296            PUNICODE_STRING FileName,
297            ULONG     MaxSize,
298            ULONG     Retention,
299            BOOLEAN   Permanent,
300            BOOLEAN   Backup)
301 {
302     NTSTATUS Status;
303     OBJECT_ATTRIBUTES ObjectAttributes;
304     IO_STATUS_BLOCK IoStatusBlock;
305     FILE_STANDARD_INFORMATION FileStdInfo;
306     PLOGFILE pLogFile;
307     SIZE_T LogNameLen;
308     BOOLEAN CreateNew;
309 
310     pLogFile = LogfpAlloc(sizeof(*pLogFile), HEAP_ZERO_MEMORY, TAG_ELF);
311     if (!pLogFile)
312     {
313         DPRINT1("Cannot allocate heap!\n");
314         return STATUS_NO_MEMORY;
315     }
316 
317     LogNameLen = (LogName ? wcslen(LogName) : 0) + 1;
318     pLogFile->LogName = LogfpAlloc(LogNameLen * sizeof(WCHAR), HEAP_ZERO_MEMORY, 0);
319     if (pLogFile->LogName == NULL)
320     {
321         DPRINT1("Cannot allocate heap\n");
322         Status = STATUS_NO_MEMORY;
323         goto Quit;
324     }
325 
326     if (LogName)
327         StringCchCopyW(pLogFile->LogName, LogNameLen, LogName);
328 
329     InitializeObjectAttributes(&ObjectAttributes,
330                                FileName,
331                                OBJ_CASE_INSENSITIVE,
332                                NULL,
333                                NULL);
334 
335     DPRINT("Going to create or open %wZ\n", FileName);
336     Status = NtCreateFile(&pLogFile->FileHandle,
337                           Backup ? (GENERIC_READ | SYNCHRONIZE)
338                                  : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE),
339                           &ObjectAttributes,
340                           &IoStatusBlock,
341                           NULL,
342                           FILE_ATTRIBUTE_NORMAL,
343                           FILE_SHARE_READ,
344                           Backup ? FILE_OPEN : FILE_OPEN_IF,
345                           FILE_SYNCHRONOUS_IO_NONALERT,
346                           NULL,
347                           0);
348     if (!NT_SUCCESS(Status))
349     {
350         DPRINT1("Cannot create file `%wZ' (Status 0x%08lx)\n", FileName, Status);
351         goto Quit;
352     }
353 
354     CreateNew = (IoStatusBlock.Information == FILE_CREATED);
355     DPRINT("%wZ %s successfully\n", FileName, CreateNew ? "created" : "opened");
356 
357     /*
358      * Retrieve the log file size and check whether the file is not too large;
359      * this log format only supports files of theoretical size < 0xFFFFFFFF .
360      *
361      * As it happens that, on Windows (and ReactOS), retrieving the End-Of-File
362      * information using NtQueryInformationFile with the FileEndOfFileInformation
363      * class is invalid (who knows why...), use instead the FileStandardInformation
364      * class, and the EndOfFile member of the returned FILE_STANDARD_INFORMATION
365      * structure will give the desired information.
366      */
367     Status = NtQueryInformationFile(pLogFile->FileHandle,
368                                     &IoStatusBlock,
369                                     &FileStdInfo,
370                                     sizeof(FileStdInfo),
371                                     FileStandardInformation);
372     if (!NT_SUCCESS(Status))
373     {
374         DPRINT1("EventLog: NtQueryInformationFile failed (Status 0x%08lx)\n", Status);
375         goto Quit;
376     }
377     if (FileStdInfo.EndOfFile.HighPart != 0)
378     {
379         DPRINT1("EventLog: Log `%wZ' is too large.\n", FileName);
380         Status = STATUS_EVENTLOG_FILE_CORRUPT; // STATUS_FILE_TOO_LARGE;
381         goto Quit;
382     }
383 
384     DPRINT("Initializing LogFile `%S'\n", pLogFile->LogName);
385 
386     Status = ElfCreateFile(&pLogFile->LogFile,
387                            FileName,
388                            FileStdInfo.EndOfFile.LowPart,
389                            MaxSize,
390                            Retention,
391                            CreateNew,
392                            Backup,
393                            LogfpAlloc,
394                            LogfpFree,
395                            LogfpSetFileSize,
396                            LogfpWriteFile,
397                            LogfpReadFile,
398                            LogfpFlushFile);
399     if (!NT_SUCCESS(Status))
400         goto Quit;
401 
402     pLogFile->Permanent = Permanent;
403 
404     RtlInitializeResource(&pLogFile->Lock);
405 
406     LogfListAddItem(pLogFile);
407 
408 Quit:
409     if (!NT_SUCCESS(Status))
410     {
411         if (pLogFile->FileHandle != NULL)
412             NtClose(pLogFile->FileHandle);
413 
414         if (pLogFile->LogName)
415             LogfpFree(pLogFile->LogName, 0, 0);
416 
417         LogfpFree(pLogFile, 0, TAG_ELF);
418     }
419     else
420     {
421         *LogFile = pLogFile;
422     }
423 
424     return Status;
425 }
426 
427 VOID
428 LogfClose(PLOGFILE LogFile,
429           BOOLEAN  ForceClose)
430 {
431     if (LogFile == NULL)
432         return;
433 
434     if (!ForceClose && LogFile->Permanent)
435         return;
436 
437     RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
438 
439     LogfListRemoveItem(LogFile);
440 
441     ElfCloseFile(&LogFile->LogFile);
442     NtClose(LogFile->FileHandle);
443     LogfpFree(LogFile->LogName, 0, 0);
444 
445     RtlDeleteResource(&LogFile->Lock);
446 
447     LogfpFree(LogFile, 0, TAG_ELF);
448 
449     return;
450 }
451 
452 VOID LogfCloseAll(VOID)
453 {
454     EnterCriticalSection(&LogFileListCs);
455 
456     while (!IsListEmpty(&LogFileListHead))
457     {
458         LogfClose(CONTAINING_RECORD(LogFileListHead.Flink, LOGFILE, ListEntry), TRUE);
459     }
460 
461     LeaveCriticalSection(&LogFileListCs);
462 
463     DeleteCriticalSection(&LogFileListCs);
464 }
465 
466 NTSTATUS
467 LogfClearFile(PLOGFILE LogFile,
468               PUNICODE_STRING BackupFileName)
469 {
470     NTSTATUS Status;
471 
472     /* Lock the log file exclusive */
473     RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
474 
475     if (BackupFileName->Length > 0)
476     {
477         /* Write a backup file */
478         Status = LogfBackupFile(LogFile, BackupFileName);
479         if (!NT_SUCCESS(Status))
480         {
481             DPRINT1("LogfBackupFile failed (Status 0x%08lx)\n", Status);
482             goto Quit;
483         }
484     }
485 
486     Status = ElfReCreateFile(&LogFile->LogFile);
487     if (!NT_SUCCESS(Status))
488     {
489         DPRINT1("LogfInitializeNew failed (Status 0x%08lx)\n", Status);
490     }
491 
492 Quit:
493     /* Unlock the log file */
494     RtlReleaseResource(&LogFile->Lock);
495     return Status;
496 }
497 
498 NTSTATUS
499 LogfBackupFile(PLOGFILE LogFile,
500                PUNICODE_STRING BackupFileName)
501 {
502     NTSTATUS Status;
503     LOGFILE BackupLogFile;
504     OBJECT_ATTRIBUTES ObjectAttributes;
505     IO_STATUS_BLOCK IoStatusBlock;
506 
507     DPRINT("LogfBackupFile(%p, %wZ)\n", LogFile, BackupFileName);
508 
509     /* Lock the log file shared */
510     RtlAcquireResourceShared(&LogFile->Lock, TRUE);
511 
512     InitializeObjectAttributes(&ObjectAttributes,
513                                BackupFileName,
514                                OBJ_CASE_INSENSITIVE,
515                                NULL,
516                                NULL);
517 
518     Status = NtCreateFile(&BackupLogFile.FileHandle,
519                           GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
520                           &ObjectAttributes,
521                           &IoStatusBlock,
522                           NULL,
523                           FILE_ATTRIBUTE_NORMAL,
524                           FILE_SHARE_READ,
525                           FILE_CREATE,
526                           FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
527                           NULL,
528                           0);
529     if (!NT_SUCCESS(Status))
530     {
531         DPRINT("Cannot create backup file `%wZ' (Status 0x%08lx)\n", BackupFileName, Status);
532         goto Quit;
533     }
534 
535     Status = ElfBackupFile(&LogFile->LogFile,
536                            &BackupLogFile.LogFile);
537 
538 Quit:
539     /* Close the backup file */
540     if (BackupLogFile.FileHandle != NULL)
541         NtClose(BackupLogFile.FileHandle);
542 
543     /* Unlock the log file */
544     RtlReleaseResource(&LogFile->Lock);
545 
546     return Status;
547 }
548 
549 
550 static NTSTATUS
551 ReadRecord(IN  PEVTLOGFILE LogFile,
552            IN  ULONG RecordNumber,
553            OUT PEVENTLOGRECORD Record,
554            IN  SIZE_T  BufSize, // Length
555            OUT PSIZE_T BytesRead OPTIONAL,
556            OUT PSIZE_T BytesNeeded OPTIONAL,
557            IN  BOOLEAN Ansi)
558 {
559     NTSTATUS Status;
560     PEVENTLOGRECORD UnicodeBuffer = NULL;
561     PEVENTLOGRECORD Src, Dst;
562     ANSI_STRING StringA;
563     UNICODE_STRING StringW;
564     PVOID SrcPtr, DstPtr;
565     DWORD i;
566     DWORD dwPadding;
567     DWORD dwRecordLength;
568     PDWORD pLength;
569 
570     if (!Ansi)
571     {
572         return ElfReadRecord(LogFile,
573                              RecordNumber,
574                              Record,
575                              BufSize,
576                              BytesRead,
577                              BytesNeeded);
578     }
579 
580     if (BytesRead)
581         *BytesRead = 0;
582 
583     if (BytesNeeded)
584         *BytesNeeded = 0;
585 
586     UnicodeBuffer = LogfpAlloc(BufSize, HEAP_ZERO_MEMORY, TAG_ELF_BUF);
587     if (UnicodeBuffer == NULL)
588     {
589         DPRINT1("Alloc failed!\n");
590         return STATUS_NO_MEMORY;
591     }
592 
593     Status = ElfReadRecord(LogFile,
594                            RecordNumber,
595                            UnicodeBuffer,
596                            BufSize,
597                            BytesRead,
598                            BytesNeeded);
599     if (!NT_SUCCESS(Status))
600         goto Quit;
601 
602     Src = UnicodeBuffer;
603     Dst = Record;
604 
605     Dst->Reserved      = Src->Reserved;
606     Dst->RecordNumber  = Src->RecordNumber;
607     Dst->TimeGenerated = Src->TimeGenerated;
608     Dst->TimeWritten   = Src->TimeWritten;
609     Dst->EventID       = Src->EventID;
610     Dst->EventType     = Src->EventType;
611     Dst->EventCategory = Src->EventCategory;
612     Dst->NumStrings    = Src->NumStrings;
613     Dst->UserSidLength = Src->UserSidLength;
614     Dst->DataLength    = Src->DataLength;
615 
616     SrcPtr = (PVOID)((ULONG_PTR)Src + sizeof(EVENTLOGRECORD));
617     DstPtr = (PVOID)((ULONG_PTR)Dst + sizeof(EVENTLOGRECORD));
618 
619     /* Convert the module name */
620     RtlInitUnicodeString(&StringW, SrcPtr);
621     Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
622     if (NT_SUCCESS(Status))
623     {
624         RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
625         DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
626 
627         RtlFreeAnsiString(&StringA);
628     }
629     else
630     {
631         RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
632         DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
633     }
634     SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength);
635 
636     /* Convert the computer name */
637     RtlInitUnicodeString(&StringW, SrcPtr);
638     Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
639     if (NT_SUCCESS(Status))
640     {
641         RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
642         DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
643 
644         RtlFreeAnsiString(&StringA);
645     }
646     else
647     {
648         RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
649         DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
650     }
651 
652     /* Add the padding and the User SID */
653     dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG));
654     RtlZeroMemory(DstPtr, dwPadding);
655 
656     SrcPtr = (PVOID)((ULONG_PTR)Src + Src->UserSidOffset);
657     DstPtr = (PVOID)((ULONG_PTR)DstPtr + dwPadding);
658 
659     Dst->UserSidOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst);
660     RtlCopyMemory(DstPtr, SrcPtr, Src->UserSidLength);
661 
662     /* Convert the strings */
663     SrcPtr = (PVOID)((ULONG_PTR)Src + Src->StringOffset);
664     DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->UserSidLength);
665     Dst->StringOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst);
666 
667     for (i = 0; i < Dst->NumStrings; i++)
668     {
669         RtlInitUnicodeString(&StringW, SrcPtr);
670         Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
671         if (NT_SUCCESS(Status))
672         {
673             RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
674             DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
675 
676             RtlFreeAnsiString(&StringA);
677         }
678         else
679         {
680             RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
681             DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
682         }
683         SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength);
684     }
685 
686     /* Copy the binary data */
687     SrcPtr = (PVOID)((ULONG_PTR)Src + Src->DataOffset);
688     Dst->DataOffset = (ULONG_PTR)DstPtr - (ULONG_PTR)Dst;
689     RtlCopyMemory(DstPtr, SrcPtr, Src->DataLength);
690     DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->DataLength);
691 
692     /* Add the padding */
693     dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG));
694     RtlZeroMemory(DstPtr, dwPadding);
695 
696     /* Set the record length at the beginning and the end of the record */
697     dwRecordLength = (DWORD)((ULONG_PTR)DstPtr + dwPadding + sizeof(ULONG) - (ULONG_PTR)Dst);
698     Dst->Length = dwRecordLength;
699     pLength = (PDWORD)((ULONG_PTR)DstPtr + dwPadding);
700     *pLength = dwRecordLength;
701 
702     if (BytesRead)
703         *BytesRead = dwRecordLength;
704 
705     Status = STATUS_SUCCESS;
706 
707 Quit:
708     LogfpFree(UnicodeBuffer, 0, TAG_ELF_BUF);
709 
710     return Status;
711 }
712 
713 /*
714  * NOTE:
715  *   'RecordNumber' is a pointer to the record number at which the read operation
716  *   should start. If the record number is 0 and the flags given in the 'Flags'
717  *   parameter contain EVENTLOG_SEQUENTIAL_READ, an adequate record number is
718  *   computed.
719  */
720 NTSTATUS
721 LogfReadEvents(PLOGFILE LogFile,
722                ULONG    Flags,
723                PULONG   RecordNumber,
724                ULONG    BufSize,
725                PBYTE    Buffer,
726                PULONG   BytesRead,
727                PULONG   BytesNeeded,
728                BOOLEAN  Ansi)
729 {
730     NTSTATUS Status;
731     ULONG RecNum;
732     SIZE_T ReadLength, NeededSize;
733     ULONG BufferUsage;
734 
735     /* Parameters validation */
736 
737     /* EVENTLOG_SEQUENTIAL_READ and EVENTLOG_SEEK_READ are mutually exclusive */
738     if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (Flags & EVENTLOG_SEEK_READ))
739         return STATUS_INVALID_PARAMETER;
740 
741     if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && !(Flags & EVENTLOG_SEEK_READ))
742         return STATUS_INVALID_PARAMETER;
743 
744     /* EVENTLOG_FORWARDS_READ and EVENTLOG_BACKWARDS_READ are mutually exclusive */
745     if ((Flags & EVENTLOG_FORWARDS_READ) && (Flags & EVENTLOG_BACKWARDS_READ))
746         return STATUS_INVALID_PARAMETER;
747 
748     if (!(Flags & EVENTLOG_FORWARDS_READ) && !(Flags & EVENTLOG_BACKWARDS_READ))
749         return STATUS_INVALID_PARAMETER;
750 
751     if (!Buffer || !BytesRead || !BytesNeeded)
752         return STATUS_INVALID_PARAMETER;
753 
754     /* In seek read mode, a record number of 0 is invalid */
755     if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0))
756         return STATUS_INVALID_PARAMETER;
757 
758     /* Lock the log file shared */
759     RtlAcquireResourceShared(&LogFile->Lock, TRUE);
760 
761     /*
762      * In sequential read mode, a record number of 0 means we need
763      * to determine where to start the read operation. Otherwise
764      * we just use the provided record number.
765      */
766     if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0))
767     {
768         if (Flags & EVENTLOG_FORWARDS_READ)
769         {
770             *RecordNumber = ElfGetOldestRecord(&LogFile->LogFile);
771         }
772         else // if (Flags & EVENTLOG_BACKWARDS_READ)
773         {
774             *RecordNumber = ElfGetCurrentRecord(&LogFile->LogFile) - 1;
775         }
776     }
777 
778     RecNum = *RecordNumber;
779 
780     *BytesRead = 0;
781     *BytesNeeded = 0;
782 
783     BufferUsage = 0;
784     do
785     {
786         Status = ReadRecord(&LogFile->LogFile,
787                             RecNum,
788                             (PEVENTLOGRECORD)(Buffer + BufferUsage),
789                             BufSize - BufferUsage,
790                             &ReadLength,
791                             &NeededSize,
792                             Ansi);
793         if (Status == STATUS_NOT_FOUND)
794         {
795             if (BufferUsage == 0)
796             {
797                 Status = STATUS_END_OF_FILE;
798                 goto Quit;
799             }
800             else
801             {
802                 break;
803             }
804         }
805         else
806         if (Status == STATUS_BUFFER_TOO_SMALL)
807         {
808             if (BufferUsage == 0)
809             {
810                 *BytesNeeded = NeededSize;
811                 // Status = STATUS_BUFFER_TOO_SMALL;
812                 goto Quit;
813             }
814             else
815             {
816                 break;
817             }
818         }
819         else
820         if (!NT_SUCCESS(Status))
821         {
822             DPRINT1("ElfReadRecord failed (Status 0x%08lx)\n", Status);
823             goto Quit;
824         }
825 
826         /* Go to the next event record */
827         /*
828          * NOTE: This implicitly supposes that all the other record numbers
829          * are consecutive (and do not jump than more than one unit); but if
830          * it is not the case, then we would prefer here to call some
831          * "get_next_record_number" function.
832          */
833         if (Flags & EVENTLOG_FORWARDS_READ)
834             RecNum++;
835         else // if (Flags & EVENTLOG_BACKWARDS_READ)
836             RecNum--;
837 
838         BufferUsage += ReadLength;
839     }
840     while (BufferUsage <= BufSize);
841 
842     *BytesRead = BufferUsage;
843     *RecordNumber = RecNum;
844 
845     Status = STATUS_SUCCESS;
846 
847 Quit:
848     /* Unlock the log file */
849     RtlReleaseResource(&LogFile->Lock);
850 
851     if (!NT_SUCCESS(Status))
852         DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status);
853 
854     return Status;
855 }
856 
857 NTSTATUS
858 LogfWriteRecord(PLOGFILE LogFile,
859                 PEVENTLOGRECORD Record,
860                 SIZE_T BufSize)
861 {
862     NTSTATUS Status;
863     LARGE_INTEGER SystemTime;
864 
865     // ASSERT(sizeof(*Record) == sizeof(RecBuf));
866 
867     if (!Record || BufSize < sizeof(*Record))
868         return STATUS_INVALID_PARAMETER;
869 
870     /* Lock the log file exclusive */
871     RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
872 
873     /*
874      * Retrieve the record written time now, that will also be compared
875      * with the existing events timestamps in case the log is wrapping.
876      */
877     NtQuerySystemTime(&SystemTime);
878     RtlTimeToSecondsSince1970(&SystemTime, &Record->TimeWritten);
879 
880     Status = ElfWriteRecord(&LogFile->LogFile, Record, BufSize);
881     if (Status == STATUS_LOG_FILE_FULL)
882     {
883         /* The event log file is full, queue a message box for the user and exit */
884         // TODO!
885         DPRINT1("Log file `%S' is full!\n", LogFile->LogName);
886     }
887 
888     /* Unlock the log file */
889     RtlReleaseResource(&LogFile->Lock);
890 
891     return Status;
892 }
893 
894 
895 PEVENTLOGRECORD
896 LogfAllocAndBuildNewRecord(PSIZE_T pRecSize,
897                            ULONG   Time,
898                            USHORT  wType,
899                            USHORT  wCategory,
900                            ULONG   dwEventId,
901                            PUNICODE_STRING SourceName,
902                            PUNICODE_STRING ComputerName,
903                            ULONG   dwSidLength,
904                            PSID    pUserSid,
905                            USHORT  wNumStrings,
906                            PWSTR   pStrings,
907                            ULONG   dwDataSize,
908                            PVOID   pRawData)
909 {
910     SIZE_T RecSize;
911     SIZE_T SourceNameSize, ComputerNameSize, StringLen;
912     PBYTE Buffer;
913     PEVENTLOGRECORD pRec;
914     PWSTR str;
915     UINT i, pos;
916 
917     SourceNameSize   = (SourceName   && SourceName->Buffer)   ? SourceName->Length   : 0;
918     ComputerNameSize = (ComputerName && ComputerName->Buffer) ? ComputerName->Length : 0;
919 
920     RecSize = sizeof(EVENTLOGRECORD) + /* Add the sizes of the strings, NULL-terminated */
921         SourceNameSize + ComputerNameSize + 2*sizeof(UNICODE_NULL);
922 
923     /* Align on DWORD boundary for the SID */
924     RecSize = ROUND_UP(RecSize, sizeof(ULONG));
925 
926     RecSize += dwSidLength;
927 
928     /* Add the sizes for the strings array */
929     ASSERT((pStrings == NULL && wNumStrings == 0) ||
930            (pStrings != NULL && wNumStrings >= 0));
931     for (i = 0, str = pStrings; i < wNumStrings; i++)
932     {
933         StringLen = wcslen(str) + 1; // str must be != NULL
934         RecSize += StringLen * sizeof(WCHAR);
935         str += StringLen;
936     }
937 
938     /* Add the data size */
939     RecSize += dwDataSize;
940 
941     /* Align on DWORD boundary for the full structure */
942     RecSize = ROUND_UP(RecSize, sizeof(ULONG));
943 
944     /* Size of the trailing 'Length' member */
945     RecSize += sizeof(ULONG);
946 
947     Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, RecSize);
948     if (!Buffer)
949     {
950         DPRINT1("Cannot allocate heap!\n");
951         return NULL;
952     }
953 
954     pRec = (PEVENTLOGRECORD)Buffer;
955     pRec->Length = RecSize;
956     pRec->Reserved = LOGFILE_SIGNATURE;
957 
958     /*
959      * Do not assign here any precomputed record number to the event record.
960      * The true record number will be assigned atomically and sequentially in
961      * LogfWriteRecord, so that all the event records will have consistent and
962      * unique record numbers.
963      */
964     pRec->RecordNumber = 0;
965 
966     /*
967      * Set the generated time, and temporarily set the written time
968      * with the generated time.
969      */
970     pRec->TimeGenerated = Time;
971     pRec->TimeWritten   = Time;
972 
973     pRec->EventID = dwEventId;
974     pRec->EventType = wType;
975     pRec->EventCategory = wCategory;
976 
977     pos = sizeof(EVENTLOGRECORD);
978 
979     /* NOTE: Equivalents of RtlStringCbCopyUnicodeString calls */
980     if (SourceNameSize)
981     {
982         StringCbCopyNW((PWSTR)(Buffer + pos), SourceNameSize + sizeof(UNICODE_NULL),
983                        SourceName->Buffer, SourceNameSize);
984     }
985     pos += SourceNameSize + sizeof(UNICODE_NULL);
986     if (ComputerNameSize)
987     {
988         StringCbCopyNW((PWSTR)(Buffer + pos), ComputerNameSize + sizeof(UNICODE_NULL),
989                        ComputerName->Buffer, ComputerNameSize);
990     }
991     pos += ComputerNameSize + sizeof(UNICODE_NULL);
992 
993     /* Align on DWORD boundary for the SID */
994     pos = ROUND_UP(pos, sizeof(ULONG));
995 
996     pRec->UserSidLength = 0;
997     pRec->UserSidOffset = 0;
998     if (dwSidLength)
999     {
1000         RtlCopyMemory(Buffer + pos, pUserSid, dwSidLength);
1001         pRec->UserSidLength = dwSidLength;
1002         pRec->UserSidOffset = pos;
1003         pos += dwSidLength;
1004     }
1005 
1006     pRec->StringOffset = pos;
1007     for (i = 0, str = pStrings; i < wNumStrings; i++)
1008     {
1009         StringLen = wcslen(str) + 1; // str must be != NULL
1010         StringCchCopyW((PWSTR)(Buffer + pos), StringLen, str);
1011         str += StringLen;
1012         pos += StringLen * sizeof(WCHAR);
1013     }
1014     pRec->NumStrings = wNumStrings;
1015 
1016     pRec->DataLength = 0;
1017     pRec->DataOffset = 0;
1018     if (dwDataSize)
1019     {
1020         RtlCopyMemory(Buffer + pos, pRawData, dwDataSize);
1021         pRec->DataLength = dwDataSize;
1022         pRec->DataOffset = pos;
1023         pos += dwDataSize;
1024     }
1025 
1026     /* Align on DWORD boundary for the full structure */
1027     pos = ROUND_UP(pos, sizeof(ULONG));
1028 
1029     /* Initialize the trailing 'Length' member */
1030     *((PDWORD)(Buffer + pos)) = RecSize;
1031 
1032     *pRecSize = RecSize;
1033     return pRec;
1034 }
1035 
1036 VOID
1037 LogfReportEvent(USHORT wType,
1038                 USHORT wCategory,
1039                 ULONG  dwEventId,
1040                 USHORT wNumStrings,
1041                 PWSTR  pStrings,
1042                 ULONG  dwDataSize,
1043                 PVOID  pRawData)
1044 {
1045     NTSTATUS Status;
1046     UNICODE_STRING SourceName, ComputerName;
1047     PEVENTLOGRECORD LogBuffer;
1048     LARGE_INTEGER SystemTime;
1049     ULONG Time;
1050     SIZE_T RecSize;
1051     DWORD dwComputerNameLength;
1052     WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
1053 
1054     if (!EventLogSource)
1055         return;
1056 
1057     RtlInitUnicodeString(&SourceName, EventLogSource->szName);
1058 
1059     dwComputerNameLength = ARRAYSIZE(szComputerName);
1060     if (!GetComputerNameW(szComputerName, &dwComputerNameLength))
1061         szComputerName[0] = L'\0';
1062 
1063     RtlInitUnicodeString(&ComputerName, szComputerName);
1064 
1065     NtQuerySystemTime(&SystemTime);
1066     RtlTimeToSecondsSince1970(&SystemTime, &Time);
1067 
1068     LogBuffer = LogfAllocAndBuildNewRecord(&RecSize,
1069                                            Time,
1070                                            wType,
1071                                            wCategory,
1072                                            dwEventId,
1073                                            &SourceName,
1074                                            &ComputerName,
1075                                            0,
1076                                            NULL,
1077                                            wNumStrings,
1078                                            pStrings,
1079                                            dwDataSize,
1080                                            pRawData);
1081     if (LogBuffer == NULL)
1082     {
1083         DPRINT1("LogfAllocAndBuildNewRecord failed!\n");
1084         return;
1085     }
1086 
1087     Status = LogfWriteRecord(EventLogSource->LogFile, LogBuffer, RecSize);
1088     if (!NT_SUCCESS(Status))
1089     {
1090         DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n",
1091                 EventLogSource->LogFile->LogName, Status);
1092     }
1093 
1094     LogfFreeRecord(LogBuffer);
1095 }
1096