xref: /reactos/base/services/eventlog/file.c (revision c2c66aff)
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 
LogfListInitialize(VOID)27 VOID LogfListInitialize(VOID)
28 {
29     InitializeCriticalSection(&LogFileListCs);
30     InitializeListHead(&LogFileListHead);
31 }
32 
LogfListItemByName(LPCWSTR Name)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 */
LogfListItemByIndex(DWORD Index)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 
LogfListItemCount(VOID)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
LogfListAddItem(PLOGFILE Item)137 LogfListAddItem(PLOGFILE Item)
138 {
139     EnterCriticalSection(&LogFileListCs);
140     InsertTailList(&LogFileListHead, &Item->ListEntry);
141     LeaveCriticalSection(&LogFileListCs);
142 }
143 
144 static VOID
LogfListRemoveItem(PLOGFILE Item)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
LogfpAlloc(IN SIZE_T Size,IN ULONG Flags,IN ULONG Tag)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
LogfpFree(IN PVOID Ptr,IN ULONG Flags,IN ULONG Tag)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
LogfpReadFile(IN PEVTLOGFILE LogFile,IN PLARGE_INTEGER FileOffset,OUT PVOID Buffer,IN SIZE_T Length,OUT PSIZE_T ReadLength OPTIONAL)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
LogfpWriteFile(IN PEVTLOGFILE LogFile,IN PLARGE_INTEGER FileOffset,IN PVOID Buffer,IN SIZE_T Length,OUT PSIZE_T WrittenLength OPTIONAL)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
LogfpSetFileSize(IN PEVTLOGFILE LogFile,IN ULONG FileSize,IN ULONG OldFileSize)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
LogfpFlushFile(IN PEVTLOGFILE LogFile,IN PLARGE_INTEGER FileOffset,IN ULONG Length)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
LogfCreate(PLOGFILE * LogFile,PCWSTR LogName,PUNICODE_STRING FileName,ULONG MaxSize,ULONG Retention,BOOLEAN Permanent,BOOLEAN Backup)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
LogfClose(PLOGFILE LogFile,BOOLEAN ForceClose)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 
LogfCloseAll(VOID)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
LogfClearFile(PLOGFILE LogFile,PUNICODE_STRING BackupFileName)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
LogfBackupFile(PLOGFILE LogFile,PUNICODE_STRING BackupFileName)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
ReadRecord(IN PEVTLOGFILE LogFile,IN ULONG RecordNumber,OUT PEVENTLOGRECORD Record,IN SIZE_T BufSize,OUT PSIZE_T BytesRead OPTIONAL,OUT PSIZE_T BytesNeeded OPTIONAL,IN BOOLEAN Ansi)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
LogfReadEvents(PLOGFILE LogFile,ULONG Flags,PULONG RecordNumber,ULONG BufSize,PBYTE Buffer,PULONG BytesRead,PULONG BytesNeeded,BOOLEAN Ansi)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
LogfWriteRecord(PLOGFILE LogFile,PEVENTLOGRECORD Record,SIZE_T BufSize)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
LogfAllocAndBuildNewRecord(PSIZE_T pRecSize,ULONG Time,USHORT wType,USHORT wCategory,ULONG dwEventId,PUNICODE_STRING SourceName,PUNICODE_STRING ComputerName,ULONG dwSidLength,PSID pUserSid,USHORT wNumStrings,PWSTR pStrings,ULONG dwDataSize,PVOID pRawData)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
LogfReportEvent(USHORT wType,USHORT wCategory,ULONG dwEventId,USHORT wNumStrings,PWSTR pStrings,ULONG dwDataSize,PVOID pRawData)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