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