reactos/base/services/eventlog/file.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);
}