[EVTLIB]: Allow specifying a memory allocation tag when freeing the allocated buffers...
[reactos.git] / reactos / base / services / eventlog / file.c
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 }