--- /dev/null
+/*
+ * PROJECT: ReactOS EventLog Service
+ * LICENSE: GPL - See COPYING in the top level directory
+ * FILE: base/services/eventlog/file.c
+ * PURPOSE: Event log file support wrappers
+ * COPYRIGHT: Copyright 2005 Saveliy Tretiakov
+ * Michael Martin
+ * Hermes Belusca-Maito
+ */
+
+/* INCLUDES ******************************************************************/
+
+#include "eventlog.h"
+#include <ndk/iofuncs.h>
+#include <ndk/kefuncs.h>
+
+#define NDEBUG
+#include <debug.h>
+
+/* LOG FILE LIST - GLOBALS ***************************************************/
+
+static LIST_ENTRY LogFileListHead;
+static CRITICAL_SECTION LogFileListCs;
+
+/* LOG FILE LIST - FUNCTIONS *************************************************/
+
+VOID LogfListInitialize(VOID)
+{
+ InitializeCriticalSection(&LogFileListCs);
+ InitializeListHead(&LogFileListHead);
+}
+
+PLOGFILE LogfListItemByName(LPCWSTR Name)
+{
+ PLIST_ENTRY CurrentEntry;
+ PLOGFILE Item, Result = NULL;
+
+ ASSERT(Name);
+
+ EnterCriticalSection(&LogFileListCs);
+
+ CurrentEntry = LogFileListHead.Flink;
+ while (CurrentEntry != &LogFileListHead)
+ {
+ Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
+
+ if (Item->LogName && !_wcsicmp(Item->LogName, Name))
+ {
+ Result = Item;
+ break;
+ }
+
+ CurrentEntry = CurrentEntry->Flink;
+ }
+
+ LeaveCriticalSection(&LogFileListCs);
+ return Result;
+}
+
+#if 0
+/* Index starting from 1 */
+DWORD LogfListItemIndexByName(LPCWSTR Name)
+{
+ PLIST_ENTRY CurrentEntry;
+ DWORD Result = 0;
+ DWORD i = 1;
+
+ ASSERT(Name);
+
+ EnterCriticalSection(&LogFileListCs);
+
+ CurrentEntry = LogFileListHead.Flink;
+ while (CurrentEntry != &LogFileListHead)
+ {
+ PLOGFILE Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
+
+ if (Item->LogName && !_wcsicmp(Item->LogName, Name))
+ {
+ Result = i;
+ break;
+ }
+
+ CurrentEntry = CurrentEntry->Flink;
+ i++;
+ }
+
+ LeaveCriticalSection(&LogFileListCs);
+ return Result;
+}
+#endif
+
+/* Index starting from 1 */
+PLOGFILE LogfListItemByIndex(DWORD Index)
+{
+ PLIST_ENTRY CurrentEntry;
+ PLOGFILE Result = NULL;
+ DWORD i = 1;
+
+ EnterCriticalSection(&LogFileListCs);
+
+ CurrentEntry = LogFileListHead.Flink;
+ while (CurrentEntry != &LogFileListHead)
+ {
+ if (i == Index)
+ {
+ Result = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry);
+ break;
+ }
+
+ CurrentEntry = CurrentEntry->Flink;
+ i++;
+ }
+
+ LeaveCriticalSection(&LogFileListCs);
+ return Result;
+}
+
+DWORD LogfListItemCount(VOID)
+{
+ PLIST_ENTRY CurrentEntry;
+ DWORD i = 0;
+
+ EnterCriticalSection(&LogFileListCs);
+
+ CurrentEntry = LogFileListHead.Flink;
+ while (CurrentEntry != &LogFileListHead)
+ {
+ CurrentEntry = CurrentEntry->Flink;
+ i++;
+ }
+
+ LeaveCriticalSection(&LogFileListCs);
+ return i;
+}
+
+static VOID
+LogfListAddItem(PLOGFILE Item)
+{
+ EnterCriticalSection(&LogFileListCs);
+ InsertTailList(&LogFileListHead, &Item->ListEntry);
+ LeaveCriticalSection(&LogFileListCs);
+}
+
+static VOID
+LogfListRemoveItem(PLOGFILE Item)
+{
+ EnterCriticalSection(&LogFileListCs);
+ RemoveEntryList(&Item->ListEntry);
+ LeaveCriticalSection(&LogFileListCs);
+}
+
+
+/* FUNCTIONS *****************************************************************/
+
+// PELF_ALLOCATE_ROUTINE
+static
+PVOID NTAPI
+LogfpAlloc(IN SIZE_T Size,
+ IN ULONG Flags,
+ IN ULONG Tag)
+{
+ UNREFERENCED_PARAMETER(Tag);
+ return RtlAllocateHeap(GetProcessHeap(), Flags, Size);
+}
+
+// PELF_FREE_ROUTINE
+static
+VOID NTAPI
+LogfpFree(IN PVOID Ptr,
+ IN ULONG Flags,
+ IN ULONG Tag)
+{
+ UNREFERENCED_PARAMETER(Tag);
+ RtlFreeHeap(GetProcessHeap(), Flags, Ptr);
+}
+
+// PELF_FILE_READ_ROUTINE
+static
+NTSTATUS NTAPI
+LogfpReadFile(IN PEVTLOGFILE LogFile,
+ IN PLARGE_INTEGER FileOffset,
+ OUT PVOID Buffer,
+ IN SIZE_T Length,
+ OUT PSIZE_T ReadLength OPTIONAL)
+{
+ NTSTATUS Status;
+ PLOGFILE pLogFile = (PLOGFILE)LogFile;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ if (ReadLength)
+ *ReadLength = 0;
+
+ Status = NtReadFile(pLogFile->FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ Buffer,
+ Length,
+ FileOffset,
+ NULL);
+
+ if (ReadLength)
+ *ReadLength = IoStatusBlock.Information;
+
+ return Status;
+}
+
+// PELF_FILE_WRITE_ROUTINE
+static
+NTSTATUS NTAPI
+LogfpWriteFile(IN PEVTLOGFILE LogFile,
+ IN PLARGE_INTEGER FileOffset,
+ IN PVOID Buffer,
+ IN SIZE_T Length,
+ OUT PSIZE_T WrittenLength OPTIONAL)
+{
+ NTSTATUS Status;
+ PLOGFILE pLogFile = (PLOGFILE)LogFile;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ if (WrittenLength)
+ *WrittenLength = 0;
+
+ Status = NtWriteFile(pLogFile->FileHandle,
+ NULL,
+ NULL,
+ NULL,
+ &IoStatusBlock,
+ Buffer,
+ Length,
+ FileOffset,
+ NULL);
+
+ if (WrittenLength)
+ *WrittenLength = IoStatusBlock.Information;
+
+ return Status;
+}
+
+// PELF_FILE_SET_SIZE_ROUTINE
+static
+NTSTATUS NTAPI
+LogfpSetFileSize(IN PEVTLOGFILE LogFile,
+ IN ULONG FileSize, // SIZE_T
+ IN ULONG OldFileSize) // SIZE_T
+{
+ NTSTATUS Status;
+ PLOGFILE pLogFile = (PLOGFILE)LogFile;
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_END_OF_FILE_INFORMATION FileEofInfo;
+ FILE_ALLOCATION_INFORMATION FileAllocInfo;
+
+ UNREFERENCED_PARAMETER(OldFileSize);
+
+ // FIXME: Should we round up FileSize ??
+
+ FileEofInfo.EndOfFile.QuadPart = FileSize;
+ Status = NtSetInformationFile(pLogFile->FileHandle,
+ &IoStatusBlock,
+ &FileEofInfo,
+ sizeof(FileEofInfo),
+ FileEndOfFileInformation);
+ if (!NT_SUCCESS(Status))
+ return Status;
+
+ FileAllocInfo.AllocationSize.QuadPart = FileSize;
+ Status = NtSetInformationFile(pLogFile->FileHandle,
+ &IoStatusBlock,
+ &FileAllocInfo,
+ sizeof(FileAllocInfo),
+ FileAllocationInformation);
+
+ return Status;
+}
+
+// PELF_FILE_FLUSH_ROUTINE
+static
+NTSTATUS NTAPI
+LogfpFlushFile(IN PEVTLOGFILE LogFile,
+ IN PLARGE_INTEGER FileOffset,
+ IN ULONG Length)
+{
+ PLOGFILE pLogFile = (PLOGFILE)LogFile;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ UNREFERENCED_PARAMETER(FileOffset);
+ UNREFERENCED_PARAMETER(Length);
+
+ return NtFlushBuffersFile(pLogFile->FileHandle, &IoStatusBlock);
+}
+
+NTSTATUS
+LogfCreate(PLOGFILE* LogFile,
+ PCWSTR LogName,
+ PUNICODE_STRING FileName,
+ ULONG MaxSize,
+ ULONG Retention,
+ BOOLEAN Permanent,
+ BOOLEAN Backup)
+{
+ NTSTATUS Status;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+ FILE_STANDARD_INFORMATION FileStdInfo;
+ PLOGFILE pLogFile;
+ SIZE_T LogNameLen;
+ BOOLEAN CreateNew;
+
+ pLogFile = LogfpAlloc(sizeof(*pLogFile), HEAP_ZERO_MEMORY, TAG_ELF);
+ if (!pLogFile)
+ {
+ DPRINT1("Cannot allocate heap!\n");
+ return STATUS_NO_MEMORY;
+ }
+
+ LogNameLen = (LogName ? wcslen(LogName) : 0) + 1;
+ pLogFile->LogName = LogfpAlloc(LogNameLen * sizeof(WCHAR), HEAP_ZERO_MEMORY, 0);
+ if (pLogFile->LogName == NULL)
+ {
+ DPRINT1("Cannot allocate heap\n");
+ Status = STATUS_NO_MEMORY;
+ goto Quit;
+ }
+
+ if (LogName)
+ StringCchCopyW(pLogFile->LogName, LogNameLen, LogName);
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ FileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ DPRINT("Going to create or open %wZ\n", FileName);
+ Status = NtCreateFile(&pLogFile->FileHandle,
+ Backup ? (GENERIC_READ | SYNCHRONIZE)
+ : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE),
+ &ObjectAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ Backup ? FILE_OPEN : FILE_OPEN_IF,
+ FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("Cannot create file `%wZ' (Status 0x%08lx)\n", FileName, Status);
+ goto Quit;
+ }
+
+ CreateNew = (IoStatusBlock.Information == FILE_CREATED);
+ DPRINT("%wZ %s successfully\n", FileName, CreateNew ? "created" : "opened");
+
+ /*
+ * Retrieve the log file size and check whether the file is not too large;
+ * this log format only supports files of theoretical size < 0xFFFFFFFF .
+ *
+ * As it happens that, on Windows (and ReactOS), retrieving the End-Of-File
+ * information using NtQueryInformationFile with the FileEndOfFileInformation
+ * class is invalid (who knows why...), use instead the FileStandardInformation
+ * class, and the EndOfFile member of the returned FILE_STANDARD_INFORMATION
+ * structure will give the desired information.
+ */
+ Status = NtQueryInformationFile(pLogFile->FileHandle,
+ &IoStatusBlock,
+ &FileStdInfo,
+ sizeof(FileStdInfo),
+ FileStandardInformation);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("EventLog: NtQueryInformationFile failed (Status 0x%08lx)\n", Status);
+ goto Quit;
+ }
+ if (FileStdInfo.EndOfFile.HighPart != 0)
+ {
+ DPRINT1("EventLog: Log `%wZ' is too large.\n", FileName);
+ Status = STATUS_EVENTLOG_FILE_CORRUPT; // STATUS_FILE_TOO_LARGE;
+ goto Quit;
+ }
+
+ DPRINT("Initializing LogFile `%S'\n", pLogFile->LogName);
+
+ Status = ElfCreateFile(&pLogFile->LogFile,
+ FileName,
+ FileStdInfo.EndOfFile.LowPart,
+ MaxSize,
+ Retention,
+ CreateNew,
+ Backup,
+ LogfpAlloc,
+ LogfpFree,
+ LogfpSetFileSize,
+ LogfpWriteFile,
+ LogfpReadFile,
+ LogfpFlushFile);
+ if (!NT_SUCCESS(Status))
+ goto Quit;
+
+ pLogFile->Permanent = Permanent;
+
+ RtlInitializeResource(&pLogFile->Lock);
+
+ LogfListAddItem(pLogFile);
+
+Quit:
+ if (!NT_SUCCESS(Status))
+ {
+ if (pLogFile->FileHandle != NULL)
+ NtClose(pLogFile->FileHandle);
+
+ if (pLogFile->LogName)
+ LogfpFree(pLogFile->LogName, 0, 0);
+
+ LogfpFree(pLogFile, 0, TAG_ELF);
+ }
+ else
+ {
+ *LogFile = pLogFile;
+ }
+
+ return Status;
+}
+
+VOID
+LogfClose(PLOGFILE LogFile,
+ BOOLEAN ForceClose)
+{
+ if (LogFile == NULL)
+ return;
+
+ if (!ForceClose && LogFile->Permanent)
+ return;
+
+ RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
+
+ LogfListRemoveItem(LogFile);
+
+ ElfCloseFile(&LogFile->LogFile);
+ NtClose(LogFile->FileHandle);
+ LogfpFree(LogFile->LogName, 0, 0);
+
+ RtlDeleteResource(&LogFile->Lock);
+
+ LogfpFree(LogFile, 0, TAG_ELF);
+
+ return;
+}
+
+VOID LogfCloseAll(VOID)
+{
+ EnterCriticalSection(&LogFileListCs);
+
+ while (!IsListEmpty(&LogFileListHead))
+ {
+ LogfClose(CONTAINING_RECORD(LogFileListHead.Flink, LOGFILE, ListEntry), TRUE);
+ }
+
+ LeaveCriticalSection(&LogFileListCs);
+
+ DeleteCriticalSection(&LogFileListCs);
+}
+
+NTSTATUS
+LogfClearFile(PLOGFILE LogFile,
+ PUNICODE_STRING BackupFileName)
+{
+ NTSTATUS Status;
+
+ /* Lock the log file exclusive */
+ RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
+
+ if (BackupFileName->Length > 0)
+ {
+ /* Write a backup file */
+ Status = LogfBackupFile(LogFile, BackupFileName);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("LogfBackupFile failed (Status 0x%08lx)\n", Status);
+ goto Quit;
+ }
+ }
+
+ Status = ElfReCreateFile(&LogFile->LogFile);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("LogfInitializeNew failed (Status 0x%08lx)\n", Status);
+ }
+
+Quit:
+ /* Unlock the log file */
+ RtlReleaseResource(&LogFile->Lock);
+ return Status;
+}
+
+NTSTATUS
+LogfBackupFile(PLOGFILE LogFile,
+ PUNICODE_STRING BackupFileName)
+{
+ NTSTATUS Status;
+ LOGFILE BackupLogFile;
+ OBJECT_ATTRIBUTES ObjectAttributes;
+ IO_STATUS_BLOCK IoStatusBlock;
+
+ DPRINT("LogfBackupFile(%p, %wZ)\n", LogFile, BackupFileName);
+
+ /* Lock the log file shared */
+ RtlAcquireResourceShared(&LogFile->Lock, TRUE);
+
+ InitializeObjectAttributes(&ObjectAttributes,
+ BackupFileName,
+ OBJ_CASE_INSENSITIVE,
+ NULL,
+ NULL);
+
+ Status = NtCreateFile(&BackupLogFile.FileHandle,
+ GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+ &ObjectAttributes,
+ &IoStatusBlock,
+ NULL,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ,
+ FILE_CREATE,
+ FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT,
+ NULL,
+ 0);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT("Cannot create backup file `%wZ' (Status 0x%08lx)\n", BackupFileName, Status);
+ goto Quit;
+ }
+
+ Status = ElfBackupFile(&LogFile->LogFile,
+ &BackupLogFile.LogFile);
+
+Quit:
+ /* Close the backup file */
+ if (BackupLogFile.FileHandle != NULL)
+ NtClose(BackupLogFile.FileHandle);
+
+ /* Unlock the log file */
+ RtlReleaseResource(&LogFile->Lock);
+
+ return Status;
+}
+
+
+static NTSTATUS
+ReadRecord(IN PEVTLOGFILE LogFile,
+ IN ULONG RecordNumber,
+ OUT PEVENTLOGRECORD Record,
+ IN SIZE_T BufSize, // Length
+ OUT PSIZE_T BytesRead OPTIONAL,
+ OUT PSIZE_T BytesNeeded OPTIONAL,
+ IN BOOLEAN Ansi)
+{
+ NTSTATUS Status;
+ PEVENTLOGRECORD UnicodeBuffer = NULL;
+ PEVENTLOGRECORD Src, Dst;
+ ANSI_STRING StringA;
+ UNICODE_STRING StringW;
+ PVOID SrcPtr, DstPtr;
+ DWORD i;
+ DWORD dwPadding;
+ DWORD dwRecordLength;
+ PDWORD pLength;
+
+ if (!Ansi)
+ {
+ return ElfReadRecord(LogFile,
+ RecordNumber,
+ Record,
+ BufSize,
+ BytesRead,
+ BytesNeeded);
+ }
+
+ if (BytesRead)
+ *BytesRead = 0;
+
+ if (BytesNeeded)
+ *BytesNeeded = 0;
+
+ UnicodeBuffer = LogfpAlloc(BufSize, HEAP_ZERO_MEMORY, TAG_ELF_BUF);
+ if (UnicodeBuffer == NULL)
+ {
+ DPRINT1("Alloc failed!\n");
+ return STATUS_NO_MEMORY;
+ }
+
+ Status = ElfReadRecord(LogFile,
+ RecordNumber,
+ UnicodeBuffer,
+ BufSize,
+ BytesRead,
+ BytesNeeded);
+ if (!NT_SUCCESS(Status))
+ goto Quit;
+
+ Src = UnicodeBuffer;
+ Dst = Record;
+
+ Dst->Reserved = Src->Reserved;
+ Dst->RecordNumber = Src->RecordNumber;
+ Dst->TimeGenerated = Src->TimeGenerated;
+ Dst->TimeWritten = Src->TimeWritten;
+ Dst->EventID = Src->EventID;
+ Dst->EventType = Src->EventType;
+ Dst->EventCategory = Src->EventCategory;
+ Dst->NumStrings = Src->NumStrings;
+ Dst->UserSidLength = Src->UserSidLength;
+ Dst->DataLength = Src->DataLength;
+
+ SrcPtr = (PVOID)((ULONG_PTR)Src + sizeof(EVENTLOGRECORD));
+ DstPtr = (PVOID)((ULONG_PTR)Dst + sizeof(EVENTLOGRECORD));
+
+ /* Convert the module name */
+ RtlInitUnicodeString(&StringW, SrcPtr);
+ Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+ }
+ else
+ {
+ RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
+ }
+ SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength);
+
+ /* Convert the computer name */
+ RtlInitUnicodeString(&StringW, SrcPtr);
+ Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+ }
+ else
+ {
+ RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
+ }
+
+ /* Add the padding and the User SID */
+ dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG));
+ RtlZeroMemory(DstPtr, dwPadding);
+
+ SrcPtr = (PVOID)((ULONG_PTR)Src + Src->UserSidOffset);
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + dwPadding);
+
+ Dst->UserSidOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst);
+ RtlCopyMemory(DstPtr, SrcPtr, Src->UserSidLength);
+
+ /* Convert the strings */
+ SrcPtr = (PVOID)((ULONG_PTR)Src + Src->StringOffset);
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->UserSidLength);
+ Dst->StringOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst);
+
+ for (i = 0; i < Dst->NumStrings; i++)
+ {
+ RtlInitUnicodeString(&StringW, SrcPtr);
+ Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE);
+ if (NT_SUCCESS(Status))
+ {
+ RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength);
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength);
+
+ RtlFreeAnsiString(&StringA);
+ }
+ else
+ {
+ RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR));
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR));
+ }
+ SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength);
+ }
+
+ /* Copy the binary data */
+ SrcPtr = (PVOID)((ULONG_PTR)Src + Src->DataOffset);
+ Dst->DataOffset = (ULONG_PTR)DstPtr - (ULONG_PTR)Dst;
+ RtlCopyMemory(DstPtr, SrcPtr, Src->DataLength);
+ DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->DataLength);
+
+ /* Add the padding */
+ dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG));
+ RtlZeroMemory(DstPtr, dwPadding);
+
+ /* Set the record length at the beginning and the end of the record */
+ dwRecordLength = (DWORD)((ULONG_PTR)DstPtr + dwPadding + sizeof(ULONG) - (ULONG_PTR)Dst);
+ Dst->Length = dwRecordLength;
+ pLength = (PDWORD)((ULONG_PTR)DstPtr + dwPadding);
+ *pLength = dwRecordLength;
+
+ if (BytesRead)
+ *BytesRead = dwRecordLength;
+
+ Status = STATUS_SUCCESS;
+
+Quit:
+ LogfpFree(UnicodeBuffer, 0, TAG_ELF_BUF);
+
+ return Status;
+}
+
+/*
+ * NOTE:
+ * 'RecordNumber' is a pointer to the record number at which the read operation
+ * should start. If the record number is 0 and the flags given in the 'Flags'
+ * parameter contain EVENTLOG_SEQUENTIAL_READ, an adequate record number is
+ * computed.
+ */
+NTSTATUS
+LogfReadEvents(PLOGFILE LogFile,
+ ULONG Flags,
+ PULONG RecordNumber,
+ ULONG BufSize,
+ PBYTE Buffer,
+ PULONG BytesRead,
+ PULONG BytesNeeded,
+ BOOLEAN Ansi)
+{
+ NTSTATUS Status;
+ ULONG RecNum;
+ SIZE_T ReadLength, NeededSize;
+ ULONG BufferUsage;
+
+ /* Parameters validation */
+
+ /* EVENTLOG_SEQUENTIAL_READ and EVENTLOG_SEEK_READ are mutually exclusive */
+ if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (Flags & EVENTLOG_SEEK_READ))
+ return STATUS_INVALID_PARAMETER;
+
+ if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && !(Flags & EVENTLOG_SEEK_READ))
+ return STATUS_INVALID_PARAMETER;
+
+ /* EVENTLOG_FORWARDS_READ and EVENTLOG_BACKWARDS_READ are mutually exclusive */
+ if ((Flags & EVENTLOG_FORWARDS_READ) && (Flags & EVENTLOG_BACKWARDS_READ))
+ return STATUS_INVALID_PARAMETER;
+
+ if (!(Flags & EVENTLOG_FORWARDS_READ) && !(Flags & EVENTLOG_BACKWARDS_READ))
+ return STATUS_INVALID_PARAMETER;
+
+ if (!Buffer || !BytesRead || !BytesNeeded)
+ return STATUS_INVALID_PARAMETER;
+
+ /* In seek read mode, a record number of 0 is invalid */
+ if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0))
+ return STATUS_INVALID_PARAMETER;
+
+ /* Lock the log file shared */
+ RtlAcquireResourceShared(&LogFile->Lock, TRUE);
+
+ /*
+ * In sequential read mode, a record number of 0 means we need
+ * to determine where to start the read operation. Otherwise
+ * we just use the provided record number.
+ */
+ if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0))
+ {
+ if (Flags & EVENTLOG_FORWARDS_READ)
+ {
+ *RecordNumber = ElfGetOldestRecord(&LogFile->LogFile);
+ }
+ else // if (Flags & EVENTLOG_BACKWARDS_READ)
+ {
+ *RecordNumber = ElfGetCurrentRecord(&LogFile->LogFile) - 1;
+ }
+ }
+
+ RecNum = *RecordNumber;
+
+ *BytesRead = 0;
+ *BytesNeeded = 0;
+
+ BufferUsage = 0;
+ do
+ {
+ Status = ReadRecord(&LogFile->LogFile,
+ RecNum,
+ (PEVENTLOGRECORD)(Buffer + BufferUsage),
+ BufSize - BufferUsage,
+ &ReadLength,
+ &NeededSize,
+ Ansi);
+ if (Status == STATUS_NOT_FOUND)
+ {
+ if (BufferUsage == 0)
+ {
+ Status = STATUS_END_OF_FILE;
+ goto Quit;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ if (Status == STATUS_BUFFER_TOO_SMALL)
+ {
+ if (BufferUsage == 0)
+ {
+ *BytesNeeded = NeededSize;
+ // Status = STATUS_BUFFER_TOO_SMALL;
+ goto Quit;
+ }
+ else
+ {
+ break;
+ }
+ }
+ else
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ElfReadRecord failed (Status 0x%08lx)\n", Status);
+ goto Quit;
+ }
+
+ /* Go to the next event record */
+ /*
+ * NOTE: This implicitly supposes that all the other record numbers
+ * are consecutive (and do not jump than more than one unit); but if
+ * it is not the case, then we would prefer here to call some
+ * "get_next_record_number" function.
+ */
+ if (Flags & EVENTLOG_FORWARDS_READ)
+ RecNum++;
+ else // if (Flags & EVENTLOG_BACKWARDS_READ)
+ RecNum--;
+
+ BufferUsage += ReadLength;
+ }
+ while (BufferUsage <= BufSize);
+
+ *BytesRead = BufferUsage;
+ *RecordNumber = RecNum;
+
+ Status = STATUS_SUCCESS;
+
+Quit:
+ /* Unlock the log file */
+ RtlReleaseResource(&LogFile->Lock);
+
+ if (!NT_SUCCESS(Status))
+ DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status);
+
+ return Status;
+}
+
+NTSTATUS
+LogfWriteRecord(PLOGFILE LogFile,
+ PEVENTLOGRECORD Record,
+ SIZE_T BufSize)
+{
+ NTSTATUS Status;
+ LARGE_INTEGER SystemTime;
+
+ // ASSERT(sizeof(*Record) == sizeof(RecBuf));
+
+ if (!Record || BufSize < sizeof(*Record))
+ return STATUS_INVALID_PARAMETER;
+
+ /* Lock the log file exclusive */
+ RtlAcquireResourceExclusive(&LogFile->Lock, TRUE);
+
+ /*
+ * Retrieve the record written time now, that will also be compared
+ * with the existing events timestamps in case the log is wrapping.
+ */
+ NtQuerySystemTime(&SystemTime);
+ RtlTimeToSecondsSince1970(&SystemTime, &Record->TimeWritten);
+
+ Status = ElfWriteRecord(&LogFile->LogFile, Record, BufSize);
+ if (Status == STATUS_LOG_FILE_FULL)
+ {
+ /* The event log file is full, queue a message box for the user and exit */
+ // TODO!
+ DPRINT1("Log file `%S' is full!\n", LogFile->LogName);
+ }
+
+ /* Unlock the log file */
+ RtlReleaseResource(&LogFile->Lock);
+
+ return Status;
+}
+
+
+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)
+{
+ SIZE_T RecSize;
+ SIZE_T SourceNameSize, ComputerNameSize, StringLen;
+ PBYTE Buffer;
+ PEVENTLOGRECORD pRec;
+ PWSTR str;
+ UINT i, pos;
+
+ SourceNameSize = (SourceName && SourceName->Buffer) ? SourceName->Length : 0;
+ ComputerNameSize = (ComputerName && ComputerName->Buffer) ? ComputerName->Length : 0;
+
+ RecSize = sizeof(EVENTLOGRECORD) + /* Add the sizes of the strings, NULL-terminated */
+ SourceNameSize + ComputerNameSize + 2*sizeof(UNICODE_NULL);
+
+ /* Align on DWORD boundary for the SID */
+ RecSize = ROUND_UP(RecSize, sizeof(ULONG));
+
+ RecSize += dwSidLength;
+
+ /* Add the sizes for the strings array */
+ ASSERT((pStrings == NULL && wNumStrings == 0) ||
+ (pStrings != NULL && wNumStrings >= 0));
+ for (i = 0, str = pStrings; i < wNumStrings; i++)
+ {
+ StringLen = wcslen(str) + 1; // str must be != NULL
+ RecSize += StringLen * sizeof(WCHAR);
+ str += StringLen;
+ }
+
+ /* Add the data size */
+ RecSize += dwDataSize;
+
+ /* Align on DWORD boundary for the full structure */
+ RecSize = ROUND_UP(RecSize, sizeof(ULONG));
+
+ /* Size of the trailing 'Length' member */
+ RecSize += sizeof(ULONG);
+
+ Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, RecSize);
+ if (!Buffer)
+ {
+ DPRINT1("Cannot allocate heap!\n");
+ return NULL;
+ }
+
+ pRec = (PEVENTLOGRECORD)Buffer;
+ pRec->Length = RecSize;
+ pRec->Reserved = LOGFILE_SIGNATURE;
+
+ /*
+ * Do not assign here any precomputed record number to the event record.
+ * The true record number will be assigned atomically and sequentially in
+ * LogfWriteRecord, so that all the event records will have consistent and
+ * unique record numbers.
+ */
+ pRec->RecordNumber = 0;
+
+ /*
+ * Set the generated time, and temporarily set the written time
+ * with the generated time.
+ */
+ pRec->TimeGenerated = Time;
+ pRec->TimeWritten = Time;
+
+ pRec->EventID = dwEventId;
+ pRec->EventType = wType;
+ pRec->EventCategory = wCategory;
+
+ pos = sizeof(EVENTLOGRECORD);
+
+ /* NOTE: Equivalents of RtlStringCbCopyUnicodeString calls */
+ if (SourceNameSize)
+ {
+ StringCbCopyNW((PWSTR)(Buffer + pos), SourceNameSize + sizeof(UNICODE_NULL),
+ SourceName->Buffer, SourceNameSize);
+ }
+ pos += SourceNameSize + sizeof(UNICODE_NULL);
+ if (ComputerNameSize)
+ {
+ StringCbCopyNW((PWSTR)(Buffer + pos), ComputerNameSize + sizeof(UNICODE_NULL),
+ ComputerName->Buffer, ComputerNameSize);
+ }
+ pos += ComputerNameSize + sizeof(UNICODE_NULL);
+
+ /* Align on DWORD boundary for the SID */
+ pos = ROUND_UP(pos, sizeof(ULONG));
+
+ pRec->UserSidLength = 0;
+ pRec->UserSidOffset = 0;
+ if (dwSidLength)
+ {
+ RtlCopyMemory(Buffer + pos, pUserSid, dwSidLength);
+ pRec->UserSidLength = dwSidLength;
+ pRec->UserSidOffset = pos;
+ pos += dwSidLength;
+ }
+
+ pRec->StringOffset = pos;
+ for (i = 0, str = pStrings; i < wNumStrings; i++)
+ {
+ StringLen = wcslen(str) + 1; // str must be != NULL
+ StringCchCopyW((PWSTR)(Buffer + pos), StringLen, str);
+ str += StringLen;
+ pos += StringLen * sizeof(WCHAR);
+ }
+ pRec->NumStrings = wNumStrings;
+
+ pRec->DataLength = 0;
+ pRec->DataOffset = 0;
+ if (dwDataSize)
+ {
+ RtlCopyMemory(Buffer + pos, pRawData, dwDataSize);
+ pRec->DataLength = dwDataSize;
+ pRec->DataOffset = pos;
+ pos += dwDataSize;
+ }
+
+ /* Align on DWORD boundary for the full structure */
+ pos = ROUND_UP(pos, sizeof(ULONG));
+
+ /* Initialize the trailing 'Length' member */
+ *((PDWORD)(Buffer + pos)) = RecSize;
+
+ *pRecSize = RecSize;
+ return pRec;
+}
+
+VOID
+LogfReportEvent(USHORT wType,
+ USHORT wCategory,
+ ULONG dwEventId,
+ USHORT wNumStrings,
+ PWSTR pStrings,
+ ULONG dwDataSize,
+ PVOID pRawData)
+{
+ NTSTATUS Status;
+ UNICODE_STRING SourceName, ComputerName;
+ PEVENTLOGRECORD LogBuffer;
+ LARGE_INTEGER SystemTime;
+ ULONG Time;
+ SIZE_T RecSize;
+ DWORD dwComputerNameLength;
+ WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
+
+ if (!EventLogSource)
+ return;
+
+ RtlInitUnicodeString(&SourceName, EventLogSource->szName);
+
+ dwComputerNameLength = ARRAYSIZE(szComputerName);
+ if (!GetComputerNameW(szComputerName, &dwComputerNameLength))
+ szComputerName[0] = L'\0';
+
+ RtlInitUnicodeString(&ComputerName, szComputerName);
+
+ NtQuerySystemTime(&SystemTime);
+ RtlTimeToSecondsSince1970(&SystemTime, &Time);
+
+ LogBuffer = LogfAllocAndBuildNewRecord(&RecSize,
+ Time,
+ wType,
+ wCategory,
+ dwEventId,
+ &SourceName,
+ &ComputerName,
+ 0,
+ NULL,
+ wNumStrings,
+ pStrings,
+ dwDataSize,
+ pRawData);
+ if (LogBuffer == NULL)
+ {
+ DPRINT1("LogfAllocAndBuildNewRecord failed!\n");
+ return;
+ }
+
+ Status = LogfWriteRecord(EventLogSource->LogFile, LogBuffer, RecSize);
+ if (!NT_SUCCESS(Status))
+ {
+ DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n",
+ EventLogSource->LogFile->LogName, Status);
+ }
+
+ LogfFreeRecord(LogBuffer);
+}