mirror of
https://github.com/reactos/reactos.git
synced 2024-08-30 23:28:58 +00:00
1096 lines
31 KiB
C
1096 lines
31 KiB
C
/*
|
|
* 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);
|
|
}
|