From: Hermès Bélusca-Maïto Date: Tue, 25 Oct 2016 23:50:31 +0000 (+0000) Subject: [EVTLIB]: Create a EvtLib library for manipulating the event log file format from... X-Git-Tag: ReactOS-0.4.3~20 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=1f9c38b3b951d2b4becaa86f0e7f7b27e8e53569 [EVTLIB]: Create a EvtLib library for manipulating the event log file format from anywhere. [EVENTLOG]: Make usage of this library. CORE-11868 #resolve #comment Committed in r73035. [EVENTLOG]: Protect the global handle table with a lock as it seems to me that this needs serialization. Please notify me if you think this is not actually necessary (and I'll revert that in case). svn path=/trunk/; revision=73035 --- diff --git a/reactos/base/services/eventlog/CMakeLists.txt b/reactos/base/services/eventlog/CMakeLists.txt index 994dbe57717..b5c4909a782 100644 --- a/reactos/base/services/eventlog/CMakeLists.txt +++ b/reactos/base/services/eventlog/CMakeLists.txt @@ -1,5 +1,6 @@ include_directories( + ${REACTOS_SOURCE_DIR}/sdk/lib/evtlib ${REACTOS_SOURCE_DIR}/sdk/include/reactos/idl ${CMAKE_CURRENT_BINARY_DIR}) @@ -15,12 +16,13 @@ list(APPEND SOURCE ${CMAKE_CURRENT_BINARY_DIR}/eventlogrpc_s.c) add_executable(eventlog ${SOURCE} eventlog.rc) +add_pch(eventlog eventlog.h SOURCE) if(NOT MSVC) target_link_libraries(eventlog ${PSEH_LIB}) endif() set_module_type(eventlog win32cui UNICODE) +target_link_libraries(eventlog evtlib) add_importlibs(eventlog advapi32 rpcrt4 msvcrt kernel32 ntdll) -add_pch(eventlog eventlog.h SOURCE) add_cd_file(TARGET eventlog DESTINATION reactos/system32 FOR all) diff --git a/reactos/base/services/eventlog/eventlog.c b/reactos/base/services/eventlog/eventlog.c index d6b70a1331c..edb23bbfc2c 100644 --- a/reactos/base/services/eventlog/eventlog.c +++ b/reactos/base/services/eventlog/eventlog.c @@ -541,49 +541,6 @@ bye_bye: return RetCode; } -VOID PRINT_HEADER(PEVENTLOGHEADER header) -{ - ULONG Flags = header->Flags; - - DPRINT("HeaderSize = %lu\n", header->HeaderSize); - DPRINT("Signature = 0x%x\n", header->Signature); - DPRINT("MajorVersion = %lu\n", header->MajorVersion); - DPRINT("MinorVersion = %lu\n", header->MinorVersion); - DPRINT("StartOffset = %lu\n", header->StartOffset); - DPRINT("EndOffset = 0x%x\n", header->EndOffset); - DPRINT("CurrentRecordNumber = %lu\n", header->CurrentRecordNumber); - DPRINT("OldestRecordNumber = %lu\n", header->OldestRecordNumber); - DPRINT("MaxSize = 0x%x\n", header->MaxSize); - DPRINT("Retention = 0x%x\n", header->Retention); - DPRINT("EndHeaderSize = %lu\n", header->EndHeaderSize); - DPRINT("Flags: "); - if (Flags & ELF_LOGFILE_HEADER_DIRTY) - { - DPRINT("ELF_LOGFILE_HEADER_DIRTY"); - Flags &= ~ELF_LOGFILE_HEADER_DIRTY; - } - if (Flags) DPRINT(" | "); - if (Flags & ELF_LOGFILE_HEADER_WRAP) - { - DPRINT("ELF_LOGFILE_HEADER_WRAP"); - Flags &= ~ELF_LOGFILE_HEADER_WRAP; - } - if (Flags) DPRINT(" | "); - if (Flags & ELF_LOGFILE_LOGFULL_WRITTEN) - { - DPRINT("ELF_LOGFILE_LOGFULL_WRITTEN"); - Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN; - } - if (Flags) DPRINT(" | "); - if (Flags & ELF_LOGFILE_ARCHIVE_SET) - { - DPRINT("ELF_LOGFILE_ARCHIVE_SET"); - Flags &= ~ELF_LOGFILE_ARCHIVE_SET; - } - if (Flags) DPRINT(" | 0x%x", Flags); - DPRINT("\n"); -} - VOID PRINT_RECORD(PEVENTLOGRECORD pRec) { UINT i; diff --git a/reactos/base/services/eventlog/eventlog.h b/reactos/base/services/eventlog/eventlog.h index 455d677b005..fc64b5b5fe7 100644 --- a/reactos/base/services/eventlog/eventlog.h +++ b/reactos/base/services/eventlog/eventlog.h @@ -23,82 +23,29 @@ #define ROUND_DOWN(n, align) (((ULONG)n) & ~((align) - 1l)) #define ROUND_UP(n, align) ROUND_DOWN(((ULONG)n) + (align) - 1, (align)) +#include + #include #include +// FIXME: For that we may directly include NTOS header?? typedef struct _IO_ERROR_LPC { PORT_MESSAGE Header; IO_ERROR_LOG_MESSAGE Message; } IO_ERROR_LPC, *PIO_ERROR_LPC; +// C_ASSERT(sizeof(IO_ERROR_LPC) == 0x100); -/* - * Our file format will be compatible with NT's - */ -#define MAJORVER 1 -#define MINORVER 1 -#define LOGFILE_SIGNATURE 0x654c664c - -/* - * Flags used in logfile header - */ -#define ELF_LOGFILE_HEADER_DIRTY 1 -#define ELF_LOGFILE_HEADER_WRAP 2 -#define ELF_LOGFILE_LOGFULL_WRITTEN 4 -#define ELF_LOGFILE_ARCHIVE_SET 8 - -/* FIXME: MSDN reads that the following two structs are in winnt.h. Are they? */ -typedef struct _EVENTLOGHEADER -{ - ULONG HeaderSize; - ULONG Signature; - ULONG MajorVersion; - ULONG MinorVersion; - ULONG StartOffset; - ULONG EndOffset; - ULONG CurrentRecordNumber; - ULONG OldestRecordNumber; - ULONG MaxSize; - ULONG Flags; - ULONG Retention; - ULONG EndHeaderSize; -} EVENTLOGHEADER, *PEVENTLOGHEADER; - -typedef struct _EVENTLOGEOF -{ - ULONG RecordSizeBeginning; - ULONG Ones; - ULONG Twos; - ULONG Threes; - ULONG Fours; - ULONG BeginRecord; - ULONG EndRecord; - ULONG CurrentRecordNumber; - ULONG OldestRecordNumber; - ULONG RecordSizeEnd; -} EVENTLOGEOF, *PEVENTLOGEOF; - -#define EVENTLOGEOF_SIZE_FIXED (5 * sizeof(ULONG)) -C_ASSERT(EVENTLOGEOF_SIZE_FIXED == FIELD_OFFSET(EVENTLOGEOF, BeginRecord)); - -typedef struct _EVENT_OFFSET_INFO -{ - ULONG EventNumber; - ULONG EventOffset; -} EVENT_OFFSET_INFO, *PEVENT_OFFSET_INFO; +/* Defined in evtlib.h */ +// #define LOGFILE_SIGNATURE 0x654c664c // "LfLe" typedef struct _LOGFILE { - HANDLE hFile; - EVENTLOGHEADER Header; - ULONG CurrentSize; /* Equivalent to the file size, is <= MaxSize and can be extended to MaxSize if needed */ + EVTLOGFILE LogFile; + HANDLE FileHandle; WCHAR *LogName; - WCHAR *FileName; RTL_RESOURCE Lock; - PEVENT_OFFSET_INFO OffsetInfo; - ULONG OffsetInfoSize; - ULONG OffsetInfoNext; BOOL Permanent; LIST_ENTRY ListEntry; } LOGFILE, *PLOGFILE; @@ -128,7 +75,6 @@ typedef struct _LOGHANDLE /* eventlog.c */ extern PEVENTSOURCE EventLogSource; -VOID PRINT_HEADER(PEVENTLOGHEADER header); VOID PRINT_RECORD(PEVENTLOGRECORD pRec); @@ -150,7 +96,28 @@ PLOGFILE LogfListItemByIndex(DWORD Index); PLOGFILE LogfListItemByName(LPCWSTR Name); // DWORD LogfListItemIndexByName(WCHAR * Name); +NTSTATUS +LogfCreate(PLOGFILE* LogFile, + PCWSTR LogName, + PUNICODE_STRING FileName, + ULONG MaxSize, + ULONG Retention, + BOOLEAN Permanent, + BOOLEAN Backup); + +VOID +LogfClose(PLOGFILE LogFile, + BOOLEAN ForceClose); + +VOID LogfCloseAll(VOID); + +NTSTATUS +LogfClearFile(PLOGFILE LogFile, + PUNICODE_STRING BackupFileName); +NTSTATUS +LogfBackupFile(PLOGFILE LogFile, + PUNICODE_STRING BackupFileName); NTSTATUS LogfReadEvents(PLOGFILE LogFile, @@ -164,31 +131,8 @@ LogfReadEvents(PLOGFILE LogFile, NTSTATUS LogfWriteRecord(PLOGFILE LogFile, - ULONG BufSize, // SIZE_T - PEVENTLOGRECORD Record); - -NTSTATUS -LogfClearFile(PLOGFILE LogFile, - PUNICODE_STRING BackupFileName); - -NTSTATUS -LogfBackupFile(PLOGFILE LogFile, - PUNICODE_STRING BackupFileName); - -NTSTATUS -LogfCreate(PLOGFILE* LogFile, - PCWSTR LogName, - PUNICODE_STRING FileName, - ULONG ulMaxSize, - ULONG ulRetention, - BOOLEAN Permanent, - BOOLEAN Backup); - -VOID -LogfClose(PLOGFILE LogFile, - BOOLEAN ForceClose); - -VOID LogfCloseAll(VOID); + PEVENTLOGRECORD Record, + SIZE_T BufSize); PEVENTLOGRECORD LogfAllocAndBuildNewRecord(PSIZE_T pRecSize, diff --git a/reactos/base/services/eventlog/file.c b/reactos/base/services/eventlog/file.c index 3e22ed96be8..1b889074692 100644 --- a/reactos/base/services/eventlog/file.c +++ b/reactos/base/services/eventlog/file.c @@ -24,20 +24,6 @@ static CRITICAL_SECTION LogFileListCs; /* LOG FILE LIST - FUNCTIONS *************************************************/ -VOID LogfCloseAll(VOID) -{ - EnterCriticalSection(&LogFileListCs); - - while (!IsListEmpty(&LogFileListHead)) - { - LogfClose(CONTAINING_RECORD(LogFileListHead.Flink, LOGFILE, ListEntry), TRUE); - } - - LeaveCriticalSection(&LogFileListCs); - - DeleteCriticalSection(&LogFileListCs); -} - VOID LogfListInitialize(VOID) { InitializeCriticalSection(&LogFileListCs); @@ -164,1087 +150,468 @@ LogfListRemoveItem(PLOGFILE Item) } -/* GLOBALS *******************************************************************/ +/* FUNCTIONS *****************************************************************/ -static const EVENTLOGEOF EOFRecord = +// PELF_ALLOCATE_ROUTINE +static +PVOID NTAPI +LogfpAlloc(IN SIZE_T Size, + IN ULONG Flags, + IN ULONG Tag) { - sizeof(EOFRecord), - 0x11111111, 0x22222222, 0x33333333, 0x44444444, - 0, 0, 0, 0, - sizeof(EOFRecord) -}; + UNREFERENCED_PARAMETER(Tag); + return RtlAllocateHeap(GetProcessHeap(), Flags, Size); +} -/* FUNCTIONS *****************************************************************/ +// PELF_FREE_ROUTINE +static +VOID NTAPI +LogfpFree(IN PVOID Ptr, + IN ULONG Flags) +{ + RtlFreeHeap(GetProcessHeap(), Flags, Ptr); +} -static NTSTATUS -ReadLogBuffer(IN PLOGFILE LogFile, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset, - OUT PLARGE_INTEGER NextOffset OPTIONAL) +// 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; - ULONG BufSize; - LARGE_INTEGER FileOffset; - - ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize); - // ASSERT(ByteOffset->QuadPart <= LogFile->Header.MaxSize); - ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize); - - if (NextOffset) - NextOffset->QuadPart = 0LL; + PLOGFILE pLogFile = (PLOGFILE)LogFile; + IO_STATUS_BLOCK IoStatusBlock; - /* Read the first part of the buffer */ - FileOffset = *ByteOffset; - BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart); + if (ReadLength) + *ReadLength = 0; - Status = NtReadFile(LogFile->hFile, + Status = NtReadFile(pLogFile->FileHandle, NULL, NULL, NULL, - IoStatusBlock, + &IoStatusBlock, Buffer, - BufSize, - &FileOffset, + Length, + FileOffset, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); - return Status; - } - - if (Length > BufSize) - { - ULONG_PTR Information = IoStatusBlock->Information; - - /* - * The buffer was splitted in two, its second part - * is to be read at the beginning of the log. - */ - Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize); - BufSize = Length - BufSize; - FileOffset.QuadPart = sizeof(EVENTLOGHEADER); - - Status = NtReadFile(LogFile->hFile, - NULL, - NULL, - NULL, - IoStatusBlock, - Buffer, - BufSize, - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); - return Status; - } - /* Add the read number of bytes from the first read */ - IoStatusBlock->Information += Information; - } - /* We return the offset just after the end of the read buffer */ - if (NextOffset) - NextOffset->QuadPart = FileOffset.QuadPart + BufSize; + if (ReadLength) + *ReadLength = IoStatusBlock.Information; return Status; } -static NTSTATUS -WriteLogBuffer(IN PLOGFILE LogFile, - OUT PIO_STATUS_BLOCK IoStatusBlock, - IN PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset, - OUT PLARGE_INTEGER NextOffset OPTIONAL) +// 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; - ULONG BufSize; - LARGE_INTEGER FileOffset; - - ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize); - ASSERT(ByteOffset->QuadPart <= LogFile->Header.MaxSize); - ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize); - - if (NextOffset) - NextOffset->QuadPart = 0LL; + PLOGFILE pLogFile = (PLOGFILE)LogFile; + IO_STATUS_BLOCK IoStatusBlock; - /* Write the first part of the buffer */ - FileOffset = *ByteOffset; - BufSize = min(Length, LogFile->CurrentSize /* LogFile->Header.MaxSize */ - FileOffset.QuadPart); + if (WrittenLength) + *WrittenLength = 0; - Status = NtWriteFile(LogFile->hFile, + Status = NtWriteFile(pLogFile->FileHandle, NULL, NULL, NULL, - IoStatusBlock, + &IoStatusBlock, Buffer, - BufSize, - &FileOffset, + Length, + FileOffset, NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - return Status; - } - - if (Length > BufSize) - { - ULONG_PTR Information = IoStatusBlock->Information; - /* - * The buffer was splitted in two, its second part is written - * at the beginning of the log. - */ - Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize); - BufSize = Length - BufSize; - FileOffset.QuadPart = sizeof(EVENTLOGHEADER); - - Status = NtWriteFile(LogFile->hFile, - NULL, - NULL, - NULL, - IoStatusBlock, - Buffer, - BufSize, - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - return Status; - } - /* Add the written number of bytes from the first write */ - IoStatusBlock->Information += Information; - - /* The log wraps */ - LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; - } - - /* We return the offset just after the end of the written buffer */ - if (NextOffset) - NextOffset->QuadPart = FileOffset.QuadPart + BufSize; + if (WrittenLength) + *WrittenLength = IoStatusBlock.Information; return Status; } - -/* Returns 0 if nothing is found */ -static ULONG -LogfOffsetByNumber(PLOGFILE LogFile, - DWORD RecordNumber) -{ - DWORD i; - - for (i = 0; i < LogFile->OffsetInfoNext; i++) - { - if (LogFile->OffsetInfo[i].EventNumber == RecordNumber) - return LogFile->OffsetInfo[i].EventOffset; - } - return 0; -} - -static BOOL -LogfAddOffsetInformation(PLOGFILE LogFile, - ULONG ulNumber, - ULONG ulOffset) +// PELF_FILE_SET_SIZE_ROUTINE +static +NTSTATUS NTAPI +LogfpSetFileSize(IN PEVTLOGFILE LogFile, + IN ULONG FileSize, // SIZE_T + IN ULONG OldFileSize) // SIZE_T { - PVOID NewOffsetInfo; + NTSTATUS Status; + PLOGFILE pLogFile = (PLOGFILE)LogFile; + IO_STATUS_BLOCK IoStatusBlock; + FILE_END_OF_FILE_INFORMATION FileEofInfo; + FILE_ALLOCATION_INFORMATION FileAllocInfo; - if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize) - { - NewOffsetInfo = HeapReAlloc(GetProcessHeap(), - HEAP_ZERO_MEMORY, - LogFile->OffsetInfo, - (LogFile->OffsetInfoSize + 64) * - sizeof(EVENT_OFFSET_INFO)); + UNREFERENCED_PARAMETER(OldFileSize); - if (!NewOffsetInfo) - { - DPRINT1("Cannot reallocate heap.\n"); - return FALSE; - } + // FIXME: Should we round up FileSize ?? - LogFile->OffsetInfo = (PEVENT_OFFSET_INFO)NewOffsetInfo; - LogFile->OffsetInfoSize += 64; - } + FileEofInfo.EndOfFile.QuadPart = FileSize; + Status = NtSetInformationFile(pLogFile->FileHandle, + &IoStatusBlock, + &FileEofInfo, + sizeof(FileEofInfo), + FileEndOfFileInformation); + if (!NT_SUCCESS(Status)) + return Status; - LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventNumber = ulNumber; - LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventOffset = ulOffset; - LogFile->OffsetInfoNext++; + FileAllocInfo.AllocationSize.QuadPart = FileSize; + Status = NtSetInformationFile(pLogFile->FileHandle, + &IoStatusBlock, + &FileAllocInfo, + sizeof(FileAllocInfo), + FileAllocationInformation); - return TRUE; + return Status; } -static BOOL -LogfDeleteOffsetInformation(PLOGFILE LogFile, - ULONG ulNumberMin, - ULONG ulNumberMax) +// PELF_FILE_FLUSH_ROUTINE +static +NTSTATUS NTAPI +LogfpFlushFile(IN PEVTLOGFILE LogFile, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length) { - DWORD i; - - if (ulNumberMin > ulNumberMax) - return FALSE; - - /* Remove records ulNumberMin to ulNumberMax inclusive */ - while (ulNumberMin <= ulNumberMax) - { - /* - * As the offset information is listed in increasing order, and we want - * to keep the list without holes, we demand that ulNumberMin is the first - * element in the list. - */ - if (ulNumberMin != LogFile->OffsetInfo[0].EventNumber) - return FALSE; - - /* - * RtlMoveMemory(&LogFile->OffsetInfo[0], - * &LogFile->OffsetInfo[1], - * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1)); - */ - for (i = 0; i < LogFile->OffsetInfoNext - 1; i++) - { - LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber; - LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset; - } - LogFile->OffsetInfoNext--; + PLOGFILE pLogFile = (PLOGFILE)LogFile; + IO_STATUS_BLOCK IoStatusBlock; - /* Go to the next offset information */ - ulNumberMin++; - } + UNREFERENCED_PARAMETER(FileOffset); + UNREFERENCED_PARAMETER(Length); - return TRUE; + return NtFlushBuffersFile(pLogFile->FileHandle, &IoStatusBlock); } -static NTSTATUS -LogfInitializeNew(PLOGFILE LogFile, - ULONG ulMaxSize, - ULONG ulRetention) +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; - LARGE_INTEGER FileOffset; - EVENTLOGEOF EofRec; - - /* Initialize the event log header */ - RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER)); - - LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER); - LogFile->Header.Signature = LOGFILE_SIGNATURE; - LogFile->Header.MajorVersion = MAJORVER; - LogFile->Header.MinorVersion = MINORVER; - - /* Set the offset to the oldest record */ - LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER); - /* Set the offset to the ELF_EOF_RECORD */ - LogFile->Header.EndOffset = sizeof(EVENTLOGHEADER); - /* Set the number of the next record that will be added */ - LogFile->Header.CurrentRecordNumber = 1; - /* The event log is empty, there is no record so far */ - LogFile->Header.OldestRecordNumber = 0; - - // FIXME: Windows' EventLog log file sizes are always multiple of 64kB - // but that does not mean the real log size is == file size. - - /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */ - LogFile->Header.MaxSize = ROUND_UP(ulMaxSize, sizeof(ULONG)); - LogFile->CurrentSize = LogFile->Header.MaxSize; - - LogFile->Header.Flags = 0; - LogFile->Header.Retention = ulRetention; - LogFile->Header.EndHeaderSize = sizeof(EVENTLOGHEADER); - - /* Write the header */ - SetFilePointer(LogFile->hFile, 0, NULL, FILE_BEGIN); - SetEndOfFile(LogFile->hFile); - - FileOffset.QuadPart = 0LL; - Status = NtWriteFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) + FILE_STANDARD_INFORMATION FileStdInfo; + PLOGFILE pLogFile; + SIZE_T LogNameLen; + BOOLEAN CreateNew; + + pLogFile = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*pLogFile)); + if (!pLogFile) { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - return Status; + DPRINT1("Cannot allocate heap!\n"); + return STATUS_NO_MEMORY; } - /* Initialize the ELF_EOF_RECORD and write it */ - RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); - EofRec.BeginRecord = LogFile->Header.StartOffset; - EofRec.EndRecord = LogFile->Header.EndOffset; - EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - - Status = NtWriteFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &EofRec, - sizeof(EofRec), - NULL, - NULL); - if (!NT_SUCCESS(Status)) + LogNameLen = (LogName ? wcslen(LogName) : 0) + 1; + pLogFile->LogName = RtlAllocateHeap(GetProcessHeap(), + HEAP_ZERO_MEMORY, + LogNameLen * sizeof(WCHAR)); + if (pLogFile->LogName == NULL) { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - return Status; + DPRINT1("Cannot allocate heap\n"); + Status = STATUS_NO_MEMORY; + goto Quit; } - Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); + if (LogName) + StringCchCopyW(pLogFile->LogName, LogNameLen, LogName); + + InitializeObjectAttributes(&ObjectAttributes, + FileName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + DPRINT1("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("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status); - return Status; + DPRINT1("Cannot create file `%wZ' (Status 0x%08lx)\n", FileName, Status); + goto Quit; } - return STATUS_SUCCESS; -} + CreateNew = (IoStatusBlock.Information == FILE_CREATED); + DPRINT1("%wZ %s successfully\n", FileName, CreateNew ? "created" : "opened"); -static NTSTATUS -LogfInitializeExisting(PLOGFILE LogFile, - BOOLEAN Backup) -{ - NTSTATUS Status; - IO_STATUS_BLOCK IoStatusBlock; - LARGE_INTEGER FileOffset, NextOffset; - LARGE_INTEGER LogFileSize; - DWORD dwRecordsNumber = 0; - ULONG RecOffset; - PDWORD pdwRecSize2; - EVENTLOGEOF EofRec; - EVENTLOGRECORD RecBuf; - PEVENTLOGRECORD pRecBuf; - BOOLEAN Wrapping = FALSE; - BOOLEAN IsLogDirty = FALSE; - - DPRINT("Initializing LogFile %S\n", LogFile->LogName); - - /* Read the log header */ - FileOffset.QuadPart = 0LL; - Status = NtReadFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &FileOffset, - NULL); + /* + * 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("NtReadFile failed (Status 0x%08lx)\n", Status); - return STATUS_EVENTLOG_FILE_CORRUPT; + DPRINT1("EventLog: NtQueryInformationFile failed (Status 0x%08lx)\n", Status); + goto Quit; } - if (IoStatusBlock.Information != sizeof(EVENTLOGHEADER)) + if (FileStdInfo.EndOfFile.HighPart != 0) { - DPRINT("EventLog: Invalid file %S.\n", LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; + DPRINT1("EventLog: Log `%wZ' is too large.\n", FileName); + Status = STATUS_EVENTLOG_FILE_CORRUPT; // STATUS_FILE_TOO_LARGE; + goto Quit; } - /* Header validity checks */ + 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; - if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) || - LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER)) - { - DPRINT("EventLog: Invalid header size in %S.\n", LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + pLogFile->Permanent = Permanent; - if (LogFile->Header.Signature != LOGFILE_SIGNATURE) - { - DPRINT("EventLog: Invalid signature %x in %S.\n", - LogFile->Header.Signature, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + RtlInitializeResource(&pLogFile->Lock); - IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY); + LogfListAddItem(pLogFile); - /* If the log is a backup log that is dirty, then it is corrupted */ - if (Backup && IsLogDirty) +Quit: + if (!NT_SUCCESS(Status)) { - DPRINT("EventLog: Backup log %S is dirty.\n", LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + if (pLogFile->FileHandle != NULL) + NtClose(pLogFile->FileHandle); - /* - * Retrieve the log file size and check whether the file is not too large; - * this log format only supports files of theoretical size < 0xFFFFFFFF . - */ - if (!GetFileSizeEx(LogFile->hFile, &LogFileSize)) - return I_RpcMapWin32Status(GetLastError()); + if (pLogFile->LogName) + RtlFreeHeap(GetProcessHeap(), 0, pLogFile->LogName); - if (LogFileSize.HighPart != 0) + RtlFreeHeap(GetProcessHeap(), 0, pLogFile); + } + else { - DPRINT1("EventLog: Log %S is too large.\n", LogFile->FileName); - // return STATUS_FILE_TOO_LARGE; - return STATUS_EVENTLOG_FILE_CORRUPT; + *LogFile = pLogFile; } - LogFile->CurrentSize = LogFileSize.LowPart; // LogFileSize.QuadPart; + return Status; +} - /* Adjust the log maximum size if needed */ - if (LogFile->CurrentSize > LogFile->Header.MaxSize) - LogFile->Header.MaxSize = LogFile->CurrentSize; +VOID +LogfClose(PLOGFILE LogFile, + BOOLEAN ForceClose) +{ + if (LogFile == NULL) + return; - /* - * In a non-backup dirty log, the most up-to-date information about - * the Start/End offsets and the Oldest and Current event record numbers - * are found in the EOF record. We need to locate the EOF record without - * relying on the log header's EndOffset, then patch the log header with - * the values from the EOF record. - */ - if ((LogFile->Header.EndOffset >= sizeof(EVENTLOGHEADER)) && - (LogFile->Header.EndOffset < LogFile->CurrentSize) && - (LogFile->Header.EndOffset & 3) == 0) // EndOffset % sizeof(ULONG) == 0 - { - /* The header EOF offset may be valid, try to start with it */ - RecOffset = LogFile->Header.EndOffset; - } - else - { - /* The header EOF offset could not be valid, so start from the beginning */ - RecOffset = sizeof(EVENTLOGHEADER); - } + if (!ForceClose && LogFile->Permanent) + return; - FileOffset.QuadPart = RecOffset; - Wrapping = FALSE; + RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); - for (;;) - { - if (Wrapping && FileOffset.QuadPart >= RecOffset) - { - DPRINT1("EOF record not found!\n"); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + LogfListRemoveItem(LogFile); - /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */ - Status = ReadLogBuffer(LogFile, - &IoStatusBlock, - &EofRec, - EVENTLOGEOF_SIZE_FIXED, - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - if (IoStatusBlock.Information != EVENTLOGEOF_SIZE_FIXED) - { - DPRINT1("Cannot read at most an EOF record!\n"); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + ElfCloseFile(&LogFile->LogFile); + NtClose(LogFile->FileHandle); + RtlFreeHeap(GetProcessHeap(), 0, LogFile->LogName); - /* Is it an EVENTLOGEOF record? */ - if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED) - { - DPRINT("Found EOF record at %llx\n", FileOffset.QuadPart); + RtlDeleteResource(&LogFile->Lock); - /* Got it! Break the loop and continue */ - break; - } + RtlFreeHeap(GetProcessHeap(), 0, LogFile); - /* No, continue looping */ - if (*(PULONG)((ULONG_PTR)&EofRec + sizeof(ULONG)) == *(PULONG)(&EOFRecord)) - FileOffset.QuadPart += sizeof(ULONG); - else - if (*(PULONG)((ULONG_PTR)&EofRec + 2*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) - FileOffset.QuadPart += 2*sizeof(ULONG); - else - if (*(PULONG)((ULONG_PTR)&EofRec + 3*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) - FileOffset.QuadPart += 3*sizeof(ULONG); - else - if (*(PULONG)((ULONG_PTR)&EofRec + 4*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) - FileOffset.QuadPart += 4*sizeof(ULONG); - else - FileOffset.QuadPart += 5*sizeof(ULONG); // EVENTLOGEOF_SIZE_FIXED + return; +} - if (FileOffset.QuadPart >= LogFile->CurrentSize /* LogFile->Header.MaxSize */) - { - /* Wrap the offset */ - FileOffset.QuadPart -= LogFile->CurrentSize /* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER); - Wrapping = TRUE; - } - } - /* - * The only way to be there is to have found a valid EOF record. - * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT - * was returned. - */ +VOID LogfCloseAll(VOID) +{ + EnterCriticalSection(&LogFileListCs); - /* Read the full EVENTLOGEOF (may wrap) and validate it */ - Status = ReadLogBuffer(LogFile, - &IoStatusBlock, - &EofRec, - sizeof(EofRec), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - if (IoStatusBlock.Information != sizeof(EofRec)) + while (!IsListEmpty(&LogFileListHead)) { - DPRINT1("Cannot read the full EOF record!\n"); - return STATUS_EVENTLOG_FILE_CORRUPT; + LogfClose(CONTAINING_RECORD(LogFileListHead.Flink, LOGFILE, ListEntry), TRUE); } - /* Complete validity checks */ - if ((EofRec.RecordSizeEnd != EofRec.RecordSizeBeginning) || - (EofRec.EndRecord != FileOffset.QuadPart)) - { - DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected %x %x!\n", FileOffset.QuadPart, - EofRec.RecordSizeEnd, EofRec.RecordSizeBeginning, - EofRec.EndRecord, FileOffset.QuadPart, - EOFRecord.RecordSizeEnd, EOFRecord.RecordSizeBeginning); - DPRINT1("RecordSizeEnd = %x\n", EofRec.RecordSizeEnd); - DPRINT1("RecordSizeBeginning = %x\n", EofRec.RecordSizeBeginning); - DPRINT1("EndRecord = %x\n", EofRec.EndRecord); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + LeaveCriticalSection(&LogFileListCs); + + DeleteCriticalSection(&LogFileListCs); +} + +NTSTATUS +LogfClearFile(PLOGFILE LogFile, + PUNICODE_STRING BackupFileName) +{ + NTSTATUS Status; - /* The EOF record is valid, break the loop and continue */ + /* Lock the log file exclusive */ + RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); - /* If the log is not dirty, the header values should correspond to the EOF ones */ - if (!IsLogDirty) + if (BackupFileName->Length > 0) { - if ( (LogFile->Header.StartOffset != EofRec.BeginRecord) || - (LogFile->Header.EndOffset != EofRec.EndRecord) || - (LogFile->Header.CurrentRecordNumber != EofRec.CurrentRecordNumber) || - (LogFile->Header.OldestRecordNumber != EofRec.OldestRecordNumber) ) + /* Write a backup file */ + Status = LogfBackupFile(LogFile, BackupFileName); + if (!NT_SUCCESS(Status)) { - DPRINT1("\n" - "Log header or EOF record is corrupted:\n" - " StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n" - " CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n", - LogFile->Header.StartOffset, EofRec.BeginRecord, - LogFile->Header.EndOffset , EofRec.EndRecord, - LogFile->Header.CurrentRecordNumber, EofRec.CurrentRecordNumber, - LogFile->Header.OldestRecordNumber , EofRec.OldestRecordNumber); - - return STATUS_EVENTLOG_FILE_CORRUPT; + DPRINT1("LogfBackupFile failed (Status 0x%08lx)\n", Status); + goto Quit; } } - /* If the log is dirty, patch the log header with the values from the EOF record */ - if (!Backup && IsLogDirty) - { - LogFile->Header.StartOffset = EofRec.BeginRecord; - LogFile->Header.EndOffset = EofRec.EndRecord; - LogFile->Header.CurrentRecordNumber = EofRec.CurrentRecordNumber; - LogFile->Header.OldestRecordNumber = EofRec.OldestRecordNumber; - } - - /* - * FIXME! During operations the EOF record is the one that is the most - * updated (its Oldest & Current record numbers are always up-to - * date) while the ones from the header may be unsync. When closing - * (or flushing?) the event log, the header's record numbers get - * updated with the same values as the ones stored in the EOF record. - */ - - /* Verify Start/End offsets boundaries */ - - if ((LogFile->Header.StartOffset >= LogFile->CurrentSize) || - (LogFile->Header.StartOffset & 3) != 0) // StartOffset % sizeof(ULONG) != 0 - { - DPRINT("EventLog: Invalid start offset %x in %S.\n", - LogFile->Header.StartOffset, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - if ((LogFile->Header.EndOffset >= LogFile->CurrentSize) || - (LogFile->Header.EndOffset & 3) != 0) // EndOffset % sizeof(ULONG) != 0 - { - DPRINT("EventLog: Invalid EOF offset %x in %S.\n", - LogFile->Header.EndOffset, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) && - (LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) - { - /* - * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF, - * it should point to a non-splitted EVENTLOGRECORD. - */ - DPRINT("EventLog: Invalid start offset %x in %S.\n", - LogFile->Header.StartOffset, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) && - (LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) - { - /* - * In non-wrapping case, there must be enough space between StartOffset - * and EndOffset to contain at least a full EVENTLOGRECORD. - */ - DPRINT("EventLog: Invalid start offset %x or end offset %x in %S.\n", - LogFile->Header.StartOffset, LogFile->Header.EndOffset, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) - { - /* - * Non-wrapping case: the (wrapping) free space starting at EndOffset - * must be able to contain an EVENTLOGEOF. - */ - if (LogFile->Header.MaxSize - LogFile->Header.EndOffset + - LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER) < sizeof(EVENTLOGEOF)) - { - DPRINT("EventLog: Invalid EOF offset %x in %S.\n", - LogFile->Header.EndOffset, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - } - else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) - { - /* - * Wrapping case: the free space between EndOffset and StartOffset - * must be able to contain an EVENTLOGEOF. - */ - if (LogFile->Header.StartOffset - LogFile->Header.EndOffset < sizeof(EVENTLOGEOF)) - { - DPRINT("EventLog: Invalid EOF offset %x in %S.\n", - LogFile->Header.EndOffset, LogFile->FileName); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - } - - /* Start enumerating the event records from the beginning */ - RecOffset = LogFile->Header.StartOffset; - FileOffset.QuadPart = RecOffset; - Wrapping = FALSE; - - // // FIXME! FIXME! - // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP)) - // { - // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n"); - // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; - // } - - DPRINT("StartOffset = %x, EndOffset = %x\n", - LogFile->Header.StartOffset, LogFile->Header.EndOffset); - - /* - * For non-backup logs of size < MaxSize, reorganize the events such that - * they do not wrap as soon as we write new ones. - */ -#if 0 - if (!Backup) - { - pRecBuf = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length); - if (pRecBuf == NULL) - { - DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n"); - goto Continue; - } - - // TODO: Do the job! - } - -Continue: - - DPRINT("StartOffset = %x, EndOffset = %x\n", - LogFile->Header.StartOffset, LogFile->Header.EndOffset); -#endif - - while (FileOffset.QuadPart != LogFile->Header.EndOffset) - { - if (Wrapping && FileOffset.QuadPart >= RecOffset) - { - /* We have finished enumerating all the event records */ - break; - } - - /* Read the next EVENTLOGRECORD header at once (it cannot be split) */ - Status = NtReadFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &RecBuf, - sizeof(RecBuf), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - if (IoStatusBlock.Information != sizeof(RecBuf)) - { - DPRINT("Length != sizeof(RecBuf)\n"); - break; - } - - if (RecBuf.Reserved != LOGFILE_SIGNATURE || - RecBuf.Length < sizeof(EVENTLOGRECORD)) - { - DPRINT("RecBuf problem\n"); - break; - } - - /* Allocate a full EVENTLOGRECORD (header + data) */ - pRecBuf = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length); - if (pRecBuf == NULL) - { - DPRINT1("Cannot allocate heap!\n"); - return STATUS_NO_MEMORY; - } - - /* Attempt to read the full EVENTLOGRECORD (can wrap) */ - Status = ReadLogBuffer(LogFile, - &IoStatusBlock, - pRecBuf, - RecBuf.Length, - &FileOffset, - &NextOffset); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); - RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - if (IoStatusBlock.Information != RecBuf.Length) - { - DPRINT1("Oh oh!!\n"); - RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); - break; - } - - // /* If OverWrittenRecords is TRUE and this record has already been read */ - // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber)) - // { - // RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); - // break; - // } - - pdwRecSize2 = (PDWORD)((ULONG_PTR)pRecBuf + RecBuf.Length - 4); - - if (*pdwRecSize2 != RecBuf.Length) - { - DPRINT1("Invalid RecordSizeEnd of record %d (%x) in %S\n", - dwRecordsNumber, *pdwRecSize2, LogFile->LogName); - RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); - break; - } - - DPRINT("Add new record %d - %x\n", pRecBuf->RecordNumber, FileOffset.QuadPart); - - dwRecordsNumber++; - - if (!LogfAddOffsetInformation(LogFile, - pRecBuf->RecordNumber, - FileOffset.QuadPart)) - { - DPRINT1("LogfAddOffsetInformation() failed!\n"); - RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); - - if (NextOffset.QuadPart == LogFile->Header.EndOffset) - { - /* We have finished enumerating all the event records */ - DPRINT("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n"); - break; - } - - /* - * If this was the last event record before the end of the log file, - * the next one should start at the beginning of the log and the space - * between the last event record and the end of the file is padded. - */ - if (LogFile->Header.MaxSize - NextOffset.QuadPart < sizeof(EVENTLOGRECORD)) - { - /* Wrap to the beginning of the log */ - DPRINT("Wrap!\n"); - NextOffset.QuadPart = sizeof(EVENTLOGHEADER); - } - - /* - * If the next offset to read is below the current offset, - * this means we are wrapping. - */ - if (FileOffset.QuadPart > NextOffset.QuadPart) - { - DPRINT("Wrapping = TRUE;\n"); - Wrapping = TRUE; - } - - /* Move the current offset */ - FileOffset = NextOffset; - } - - /* If the event log was empty, it will now contain one record */ - if (dwRecordsNumber != 0 && LogFile->Header.OldestRecordNumber == 0) - LogFile->Header.OldestRecordNumber = 1; - - LogFile->Header.CurrentRecordNumber = dwRecordsNumber + LogFile->Header.OldestRecordNumber; - if (LogFile->Header.CurrentRecordNumber == 0) - LogFile->Header.CurrentRecordNumber = 1; - - if (!Backup) + Status = ElfReCreateFile(&LogFile->LogFile); + if (!NT_SUCCESS(Status)) { - FileOffset.QuadPart = 0LL; - Status = NtWriteFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status); - return STATUS_EVENTLOG_FILE_CORRUPT; - } + DPRINT1("LogfInitializeNew failed (Status 0x%08lx)\n", Status); } - return STATUS_SUCCESS; +Quit: + /* Unlock the log file */ + RtlReleaseResource(&LogFile->Lock); + return Status; } NTSTATUS -LogfCreate(PLOGFILE* LogFile, - PCWSTR LogName, - PUNICODE_STRING FileName, - ULONG ulMaxSize, - ULONG ulRetention, - BOOLEAN Permanent, - BOOLEAN Backup) +LogfBackupFile(PLOGFILE LogFile, + PUNICODE_STRING BackupFileName) { - NTSTATUS Status = STATUS_SUCCESS; + NTSTATUS Status; + LOGFILE BackupLogFile; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; - PLOGFILE pLogFile; - SIZE_T LogNameLen; - BOOLEAN CreateNew = FALSE; - pLogFile = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LOGFILE)); - if (!pLogFile) - { - DPRINT1("Cannot allocate heap!\n"); - return STATUS_NO_MEMORY; - } + DPRINT1("LogfBackupFile(%p, %wZ)\n", LogFile, BackupFileName); + + /* Lock the log file shared */ + RtlAcquireResourceShared(&LogFile->Lock, TRUE); InitializeObjectAttributes(&ObjectAttributes, - FileName, + BackupFileName, OBJ_CASE_INSENSITIVE, NULL, NULL); - Status = NtCreateFile(&pLogFile->hFile, - Backup ? (GENERIC_READ | SYNCHRONIZE) - : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE), + Status = NtCreateFile(&BackupLogFile.FileHandle, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, - Backup ? FILE_OPEN : FILE_OPEN_IF, - FILE_SYNCHRONOUS_IO_NONALERT, + FILE_CREATE, + FILE_WRITE_THROUGH | 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); - - LogNameLen = (LogName ? wcslen(LogName) : 0) + 1; - pLogFile->LogName = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - LogNameLen * sizeof(WCHAR)); - if (pLogFile->LogName == NULL) - { - DPRINT1("Cannot allocate heap\n"); - Status = STATUS_NO_MEMORY; - goto Quit; - } - - if (LogName) - StringCchCopyW(pLogFile->LogName, LogNameLen, LogName); - - pLogFile->FileName = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - /*(wcslen(FileName->Buffer) + 1) * sizeof(WCHAR)*/ - FileName->Length + sizeof(UNICODE_NULL)); - if (pLogFile->FileName == NULL) - { - DPRINT1("Cannot allocate heap\n"); - Status = STATUS_NO_MEMORY; + DPRINT("Cannot create backup file `%wZ' (Status 0x%08lx)\n", BackupFileName, Status); goto Quit; } - StringCchCopyW(pLogFile->FileName, - /*wcslen(FileName->Buffer) + 1*/ (FileName->Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR), - FileName->Buffer); - - pLogFile->OffsetInfo = RtlAllocateHeap(GetProcessHeap(), - HEAP_ZERO_MEMORY, - sizeof(EVENT_OFFSET_INFO) * 64); - if (pLogFile->OffsetInfo == NULL) - { - DPRINT1("Cannot allocate heap\n"); - Status = STATUS_NO_MEMORY; - goto Quit; - } - pLogFile->OffsetInfoSize = 64; - pLogFile->OffsetInfoNext = 0; - - pLogFile->Permanent = Permanent; - - // FIXME: Always use the regitry values for MaxSize & Retention, - // even for existing logs! - - // FIXME: On Windows, EventLog uses the MaxSize setting - // from the registry itself; the MaxSize from the header - // is just for information purposes. - - if (CreateNew) - Status = LogfInitializeNew(pLogFile, ulMaxSize, ulRetention); - else - Status = LogfInitializeExisting(pLogFile, Backup); - - if (!NT_SUCCESS(Status)) - goto Quit; - - RtlInitializeResource(&pLogFile->Lock); - - LogfListAddItem(pLogFile); + Status = ElfBackupFile(&LogFile->LogFile, + &BackupLogFile.LogFile); Quit: - if (!NT_SUCCESS(Status)) - { - if ((pLogFile->hFile != NULL) && (pLogFile->hFile != INVALID_HANDLE_VALUE)) - NtClose(pLogFile->hFile); - - if (pLogFile->OffsetInfo) - RtlFreeHeap(GetProcessHeap(), 0, pLogFile->OffsetInfo); - - if (pLogFile->FileName) - RtlFreeHeap(GetProcessHeap(), 0, pLogFile->FileName); - - if (pLogFile->LogName) - RtlFreeHeap(GetProcessHeap(), 0, pLogFile->LogName); + /* Close the backup file */ + if (BackupLogFile.FileHandle != NULL) + NtClose(BackupLogFile.FileHandle); - RtlFreeHeap(GetProcessHeap(), 0, pLogFile); - } - else - { - *LogFile = pLogFile; - } + /* Unlock the log file */ + RtlReleaseResource(&LogFile->Lock); return Status; } -VOID -LogfClose(PLOGFILE LogFile, - BOOLEAN ForceClose) -{ - IO_STATUS_BLOCK IoStatusBlock; - - if (LogFile == NULL) - return; - - if (!ForceClose && LogFile->Permanent) - return; - - RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); - - NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); - NtClose(LogFile->hFile); - LogfListRemoveItem(LogFile); - - RtlDeleteResource(&LogFile->Lock); - - RtlFreeHeap(GetProcessHeap(), 0, LogFile->LogName); - RtlFreeHeap(GetProcessHeap(), 0, LogFile->FileName); - RtlFreeHeap(GetProcessHeap(), 0, LogFile->OffsetInfo); - RtlFreeHeap(GetProcessHeap(), 0, LogFile); - - return; -} - static NTSTATUS -ReadAnsiLogEntry(IN PLOGFILE LogFile, - OUT PIO_STATUS_BLOCK IoStatusBlock, - OUT PVOID Buffer, - IN ULONG Length, - IN PLARGE_INTEGER ByteOffset, - OUT PLARGE_INTEGER NextOffset OPTIONAL) +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; - PVOID UnicodeBuffer = NULL; + PEVENTLOGRECORD UnicodeBuffer = NULL; PEVENTLOGRECORD Src, Dst; ANSI_STRING StringA; UNICODE_STRING StringW; PVOID SrcPtr, DstPtr; - // DWORD dwRead; DWORD i; DWORD dwPadding; - DWORD dwEntryLength; + DWORD dwRecordLength; PDWORD pLength; - IoStatusBlock->Information = 0; + if (!Ansi) + { + return ElfReadRecord(LogFile, + RecordNumber, + Record, + BufSize, + BytesRead, + BytesNeeded); + } + + if (BytesRead) + *BytesRead = 0; - UnicodeBuffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Length); + if (BytesNeeded) + *BytesNeeded = 0; + + UnicodeBuffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, BufSize); if (UnicodeBuffer == NULL) { DPRINT1("Alloc failed!\n"); return STATUS_NO_MEMORY; } - Status = ReadLogBuffer(LogFile, - IoStatusBlock, + Status = ElfReadRecord(LogFile, + RecordNumber, UnicodeBuffer, - Length, - ByteOffset, - NextOffset); + BufSize, + BytesRead, + BytesNeeded); if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); - // Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto done; - } - // dwRead = IoStatusBlock->Information; + goto Quit; - Src = (PEVENTLOGRECORD)UnicodeBuffer; - Dst = (PEVENTLOGRECORD)Buffer; + Src = UnicodeBuffer; + Dst = Record; - Dst->Reserved = Src->Reserved; - Dst->RecordNumber = Src->RecordNumber; + 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->TimeWritten = Src->TimeWritten; + Dst->EventID = Src->EventID; + Dst->EventType = Src->EventType; Dst->EventCategory = Src->EventCategory; - Dst->NumStrings = Src->NumStrings; + Dst->NumStrings = Src->NumStrings; Dst->UserSidLength = Src->UserSidLength; - Dst->DataLength = Src->DataLength; + Dst->DataLength = Src->DataLength; SrcPtr = (PVOID)((ULONG_PTR)Src + sizeof(EVENTLOGRECORD)); DstPtr = (PVOID)((ULONG_PTR)Dst + sizeof(EVENTLOGRECORD)); @@ -1323,23 +690,22 @@ ReadAnsiLogEntry(IN PLOGFILE LogFile, DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->DataLength); /* Add the padding */ - dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr-(ULONG_PTR)Dst) % sizeof(ULONG)); + dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG)); RtlZeroMemory(DstPtr, dwPadding); - dwEntryLength = (DWORD)((ULONG_PTR)DstPtr + dwPadding + sizeof(ULONG) - (ULONG_PTR)Dst); - - /* Set the entry length at the end of the entry */ + /* 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 = dwEntryLength; - Dst->Length = dwEntryLength; + *pLength = dwRecordLength; - IoStatusBlock->Information = dwEntryLength; + if (BytesRead) + *BytesRead = dwRecordLength; Status = STATUS_SUCCESS; -done: - if (UnicodeBuffer != NULL) - RtlFreeHeap(GetProcessHeap(), 0, UnicodeBuffer); +Quit: + RtlFreeHeap(GetProcessHeap(), 0, UnicodeBuffer); return Status; } @@ -1362,10 +728,9 @@ LogfReadEvents(PLOGFILE LogFile, BOOLEAN Ansi) { NTSTATUS Status; - IO_STATUS_BLOCK IoStatusBlock; - LARGE_INTEGER FileOffset; - DWORD dwOffset, dwRead, dwRecSize; - DWORD dwBufferUsage, dwRecNum; + ULONG RecNum; + SIZE_T ReadLength, NeededSize; + ULONG BufferUsage; /* Parameters validation */ @@ -1390,6 +755,9 @@ LogfReadEvents(PLOGFILE LogFile, 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 @@ -1399,102 +767,61 @@ LogfReadEvents(PLOGFILE LogFile, { if (Flags & EVENTLOG_FORWARDS_READ) { - *RecordNumber = LogFile->Header.OldestRecordNumber; + *RecordNumber = ElfGetOldestRecord(&LogFile->LogFile); } else // if (Flags & EVENTLOG_BACKWARDS_READ) { - *RecordNumber = LogFile->Header.CurrentRecordNumber - 1; + *RecordNumber = ElfGetCurrentRecord(&LogFile->LogFile) - 1; } } - dwRecNum = *RecordNumber; - - RtlAcquireResourceShared(&LogFile->Lock, TRUE); + RecNum = *RecordNumber; *BytesRead = 0; *BytesNeeded = 0; - dwBufferUsage = 0; + BufferUsage = 0; do { - dwOffset = LogfOffsetByNumber(LogFile, dwRecNum); - if (dwOffset == 0) + Status = ReadRecord(&LogFile->LogFile, + RecNum, + (PEVENTLOGRECORD)(Buffer + BufferUsage), + BufSize - BufferUsage, + &ReadLength, + &NeededSize, + Ansi); + if (Status == STATUS_NOT_FOUND) { - if (dwBufferUsage == 0) + if (BufferUsage == 0) { - RtlReleaseResource(&LogFile->Lock); - return STATUS_END_OF_FILE; + Status = STATUS_END_OF_FILE; + goto Quit; } else { break; } } - - FileOffset.QuadPart = dwOffset; - Status = NtReadFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &dwRecSize, - sizeof(dwRecSize), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); - // Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto Done; - } - // dwRead = IoStatusBlock.Information; - - if (dwBufferUsage + dwRecSize > BufSize) + else + if (Status == STATUS_BUFFER_TOO_SMALL) { - if (dwBufferUsage == 0) + if (BufferUsage == 0) { - *BytesNeeded = dwRecSize; - RtlReleaseResource(&LogFile->Lock); - return STATUS_BUFFER_TOO_SMALL; + *BytesNeeded = NeededSize; + // Status = STATUS_BUFFER_TOO_SMALL; + goto Quit; } else { break; } } - - FileOffset.QuadPart = dwOffset; - if (Ansi) - { - Status = ReadAnsiLogEntry(LogFile, - &IoStatusBlock, - Buffer + dwBufferUsage, - dwRecSize, - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadAnsiLogEntry failed (Status 0x%08lx)\n", Status); - // Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto Done; - } - } else + if (!NT_SUCCESS(Status)) { - Status = ReadLogBuffer(LogFile, - &IoStatusBlock, - Buffer + dwBufferUsage, - dwRecSize, - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); - // Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto Done; - } + DPRINT1("ElfReadRecord failed (Status 0x%08lx)\n", Status); + goto Quit; } - dwRead = IoStatusBlock.Information; /* Go to the next event record */ /* @@ -1504,47 +831,43 @@ LogfReadEvents(PLOGFILE LogFile, * "get_next_record_number" function. */ if (Flags & EVENTLOG_FORWARDS_READ) - dwRecNum++; + RecNum++; else // if (Flags & EVENTLOG_BACKWARDS_READ) - dwRecNum--; + RecNum--; - dwBufferUsage += dwRead; + BufferUsage += ReadLength; } - while (dwBufferUsage <= BufSize); + while (BufferUsage <= BufSize); - *BytesRead = dwBufferUsage; - *RecordNumber = dwRecNum; - RtlReleaseResource(&LogFile->Lock); - return STATUS_SUCCESS; + *BytesRead = BufferUsage; + *RecordNumber = RecNum; -Done: - DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status); + 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, - ULONG BufSize, // SIZE_T - PEVENTLOGRECORD Record) + PEVENTLOGRECORD Record, + SIZE_T BufSize) { NTSTATUS Status; - IO_STATUS_BLOCK IoStatusBlock; - LARGE_INTEGER FileOffset, NextOffset; - DWORD dwWritten; - // DWORD dwRead; LARGE_INTEGER SystemTime; - EVENTLOGEOF EofRec; - EVENTLOGRECORD RecBuf; - ULONG FreeSpace = 0; - ULONG UpperBound; - ULONG RecOffset, WriteOffset; // ASSERT(sizeof(*Record) == sizeof(RecBuf)); if (!Record || BufSize < sizeof(*Record)) return STATUS_INVALID_PARAMETER; + /* Lock the log file exclusive */ RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); /* @@ -1554,570 +877,13 @@ LogfWriteRecord(PLOGFILE LogFile, NtQuerySystemTime(&SystemTime); RtlTimeToSecondsSince1970(&SystemTime, &Record->TimeWritten); - Record->RecordNumber = LogFile->Header.CurrentRecordNumber; - - /* Compute the available log free space */ - if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) - FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER); - else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) - FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset; - - LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY; - - /* If the event log was empty, it will now contain one record */ - if (LogFile->Header.OldestRecordNumber == 0) - LogFile->Header.OldestRecordNumber = 1; - - /* By default we append the new record at the old EOF record offset */ - WriteOffset = LogFile->Header.EndOffset; - - /* - * Check whether the log is going to wrap (the events being overwritten). - */ - - if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) - UpperBound = LogFile->Header.MaxSize; - else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) - UpperBound = LogFile->Header.StartOffset; - - // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec)) - if (UpperBound - WriteOffset < BufSize + sizeof(EofRec)) - { - DPRINT("EventLogFile has reached maximum size (%x), wrapping...\n" - "UpperBound = %x, WriteOffset = %x, BufSize = %x\n", - LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize); - /* This will be done later */ - } - - if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) && - (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf)) - { - // ASSERT(UpperBound == LogFile->Header.MaxSize); - // ASSERT(WriteOffset == LogFile->Header.EndOffset); - - /* - * We cannot fit the EVENTLOGRECORD header of the buffer before - * the end of the file. We need to pad the end of the log with - * 0x00000027, normally we will need to pad at most 0x37 bytes - * (corresponding to sizeof(EVENTLOGRECORD) - 1). - */ - - /* Rewind to the beginning of the log, just after the header */ - WriteOffset = sizeof(EVENTLOGHEADER); - /**/UpperBound = LogFile->Header.StartOffset;/**/ - - FreeSpace = LogFile->Header.StartOffset - WriteOffset; - - LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; - } - /* - * Otherwise, we can fit the header and only part - * of the data will overwrite the oldest records. - * - * It might be possible that all the event record can fit in one piece, - * but that the EOF record needs to be split. This is not a problem, - * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be. - */ - - if (UpperBound - WriteOffset < BufSize + sizeof(EofRec)) - { - ULONG OrgOldestRecordNumber, OldestRecordNumber; - - // DPRINT("EventLogFile has reached maximum size, wrapping...\n"); - - OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber; - - // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record??? - // NOTE: It should be, by construction (and this should have been checked when - // initializing a new, or existing log). - - /* - * Determine how many old records need to be overwritten. - * Check the size of the record as the record added may be larger. - * Need to take into account that we append the EOF record. - */ - while (FreeSpace < BufSize + sizeof(EofRec)) - { - /* Get the oldest record data */ - RecOffset = LogfOffsetByNumber(LogFile, OldestRecordNumber); - if (RecOffset == 0) - { - // TODO: It cannot, queue a message box for the user and exit. - // See also below... - DPRINT1("Record number %d cannot be found, or LogFile is full and cannot wrap!\n", OldestRecordNumber); - Status = STATUS_LOG_FILE_FULL; // STATUS_LOG_FULL; - goto Quit; - } - - RtlZeroMemory(&RecBuf, sizeof(RecBuf)); - - FileOffset.QuadPart = RecOffset; - Status = NtReadFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &RecBuf, - sizeof(RecBuf), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); - // Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto Quit; - } - // dwRead = IoStatusBlock.Information; - - if (RecBuf.Reserved != LOGFILE_SIGNATURE) - { - DPRINT1("LogFile corrupt!\n"); - Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto Quit; - } - - /* - * Check whether this event can be overwritten by comparing its - * written timestamp with the log's retention value. This value - * is the time interval, in seconds, that events records are - * protected from being overwritten. - * - * If the retention value is zero the events are always overwritten. - * - * If the retention value is non-zero, when the age of an event, - * in seconds, reaches or exceeds this value, it can be overwritten. - * Also if the events are in the future, we do not overwrite them. - */ - if (LogFile->Header.Retention != 0 && - (Record->TimeWritten < RecBuf.TimeWritten || - (Record->TimeWritten >= RecBuf.TimeWritten && - Record->TimeWritten - RecBuf.TimeWritten < LogFile->Header.Retention))) - { - // TODO: It cannot, queue a message box for the user and exit. - DPRINT1("LogFile is full and cannot wrap!\n"); - Status = STATUS_LOG_FILE_FULL; // STATUS_LOG_FULL; - goto Quit; - } - - /* - * Advance the oldest record number, add the event record length - * (as long as it is valid...) then take account for the possible - * paddind after the record, in case this is the last one at the - * end of the file. - */ - OldestRecordNumber++; - RecOffset += RecBuf.Length; - FreeSpace += RecBuf.Length; - - /* - * If this was the last event record before the end of the log file, - * the next one should start at the beginning of the log and the space - * between the last event record and the end of the file is padded. - */ - if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD)) - { - /* Add the padding size */ - FreeSpace += LogFile->Header.MaxSize - RecOffset; - } - } - - DPRINT("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize); - - /* The log records are wrapping */ - LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; - - - // FIXME: May lead to corruption if the other subsequent calls fail... - - /* - * We have validated all the region of events to be discarded, - * now we can perform their deletion. - */ - LogfDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1); - LogFile->Header.OldestRecordNumber = OldestRecordNumber; - LogFile->Header.StartOffset = LogfOffsetByNumber(LogFile, OldestRecordNumber); - if (LogFile->Header.StartOffset == 0) - { - /* - * We have deleted all the existing event records to make place - * for the new one. We can put it at the start of the event log. - */ - LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER); - WriteOffset = LogFile->Header.StartOffset; - LogFile->Header.EndOffset = WriteOffset; - } - - DPRINT1("MaxSize = %x, StartOffset = %x, WriteOffset = %x, EndOffset = %x, BufSize = %x\n" - "OldestRecordNumber = %d\n", - LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize, - OldestRecordNumber); - } - - /* - * Expand the log file if needed. - * NOTE: It may be needed to perform this task a bit sooner if we need - * such a thing for performing read operations, in the future... - * Or if this operation needs to modify 'FreeSpace'... - */ - if (LogFile->CurrentSize < LogFile->Header.MaxSize) - { - DPRINT1("Expanding the log file from %lu to %lu\n", - LogFile->CurrentSize, LogFile->Header.MaxSize); - - /* For the moment this is a trivial operation */ - LogFile->CurrentSize = LogFile->Header.MaxSize; - } - - /* Pad the end of the log */ - // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize) - if (WriteOffset < LogFile->Header.EndOffset) - { - /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */ - dwWritten = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG)); - RtlFillMemoryUlong(&RecBuf, dwWritten, 0x00000027); - - FileOffset.QuadPart = LogFile->Header.EndOffset; - Status = NtWriteFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &RecBuf, - dwWritten, - &FileOffset, - NULL); - // dwWritten = IoStatusBlock.Information; - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - // goto Quit; - } - } - - /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */ - FileOffset.QuadPart = WriteOffset; - Status = WriteLogBuffer(LogFile, - &IoStatusBlock, - Record, - BufSize, - &FileOffset, - &NextOffset); - // dwWritten = IoStatusBlock.Information; - if (!NT_SUCCESS(Status)) - { - DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); - goto Quit; - } - /* FileOffset now contains the offset just after the end of the record buffer */ - FileOffset = NextOffset; - - if (!LogfAddOffsetInformation(LogFile, - Record->RecordNumber, - WriteOffset)) - { - Status = STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT; - goto Quit; - } - - LogFile->Header.CurrentRecordNumber++; - if (LogFile->Header.CurrentRecordNumber == 0) - LogFile->Header.CurrentRecordNumber = 1; - - /* - * Write the new EOF record offset just after the event record. - * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF) - * bytes remains between the end of the record and the end of the log file. - */ - LogFile->Header.EndOffset = FileOffset.QuadPart; - - RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); - EofRec.BeginRecord = LogFile->Header.StartOffset; - EofRec.EndRecord = LogFile->Header.EndOffset; - EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - - // FileOffset.QuadPart = LogFile->Header.EndOffset; - Status = WriteLogBuffer(LogFile, - &IoStatusBlock, - &EofRec, - sizeof(EofRec), - &FileOffset, - &NextOffset); - // dwWritten = IoStatusBlock.Information; - if (!NT_SUCCESS(Status)) + Status = ElfWriteRecord(&LogFile->LogFile, Record, BufSize); + if (Status == STATUS_LOG_FILE_FULL) { - DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); - goto Quit; + /* 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); } - FileOffset = NextOffset; - - LogFile->Header.Flags &= ELF_LOGFILE_HEADER_DIRTY; - - /* Update the event log header */ - FileOffset.QuadPart = 0LL; - Status = NtWriteFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &FileOffset, - NULL); - // dwWritten = IoStatusBlock.Information; - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); - goto Quit; - } - - Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status); - goto Quit; - } - - Status = STATUS_SUCCESS; - -Quit: - RtlReleaseResource(&LogFile->Lock); - return Status; -} - -NTSTATUS -LogfClearFile(PLOGFILE LogFile, - PUNICODE_STRING BackupFileName) -{ - NTSTATUS Status; - - 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 = LogfInitializeNew(LogFile, - LogFile->Header.MaxSize, - LogFile->Header.Retention); - if (!NT_SUCCESS(Status)) - { - DPRINT1("LogfInitializeNew failed (Status: 0x%08lx)\n", Status); - } - -Quit: - RtlReleaseResource(&LogFile->Lock); - return Status; -} - -NTSTATUS -LogfBackupFile(PLOGFILE LogFile, - PUNICODE_STRING BackupFileName) -{ - NTSTATUS Status; - OBJECT_ATTRIBUTES ObjectAttributes; - IO_STATUS_BLOCK IoStatusBlock; - LARGE_INTEGER FileOffset; - HANDLE FileHandle = NULL; - EVENTLOGHEADER Header; - EVENTLOGRECORD RecBuf; - EVENTLOGEOF EofRec; - ULONG i; - ULONG RecOffset; - PVOID Buffer = NULL; - - // DWORD dwRead; - - DPRINT1("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(&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 Done; - } - - /* Initialize the (dirty) log file header */ - Header.HeaderSize = sizeof(Header); - Header.Signature = LOGFILE_SIGNATURE; - Header.MajorVersion = MAJORVER; - Header.MinorVersion = MINORVER; - Header.StartOffset = sizeof(Header); - Header.EndOffset = sizeof(Header); - Header.CurrentRecordNumber = 1; - Header.OldestRecordNumber = 0; - Header.MaxSize = LogFile->Header.MaxSize; - Header.Flags = ELF_LOGFILE_HEADER_DIRTY; - Header.Retention = LogFile->Header.Retention; - Header.EndHeaderSize = sizeof(Header); - - /* Write the (dirty) log file header */ - FileOffset.QuadPart = 0LL; - Status = NtWriteFile(FileHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - &Header, - sizeof(Header), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("Failed to write the log file header (Status: 0x%08lx)\n", Status); - goto Done; - } - - for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++) - { - RecOffset = LogfOffsetByNumber(LogFile, i); - if (RecOffset == 0) - break; - - /* Read the next EVENTLOGRECORD header at once (it cannot be split) */ - FileOffset.QuadPart = RecOffset; - Status = NtReadFile(LogFile->hFile, - NULL, - NULL, - NULL, - &IoStatusBlock, - &RecBuf, - sizeof(RecBuf), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); - goto Done; - } - // dwRead = IoStatusBlock.Information; - - // if (dwRead != sizeof(RecBuf)) - // break; - - Buffer = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length); - if (Buffer == NULL) - { - DPRINT1("RtlAllocateHeap() failed!\n"); - goto Done; - } - - /* Read the full EVENTLOGRECORD (header + data) with wrapping */ - Status = ReadLogBuffer(LogFile, - &IoStatusBlock, - Buffer, - RecBuf.Length, - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); - RtlFreeHeap(GetProcessHeap(), 0, Buffer); - // Status = STATUS_EVENTLOG_FILE_CORRUPT; - goto Done; - } - // dwRead = IoStatusBlock.Information; - - /* Write the event record (no wrap for the backup log) */ - Status = NtWriteFile(FileHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - Buffer, - RecBuf.Length, - NULL, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status); - RtlFreeHeap(GetProcessHeap(), 0, Buffer); - goto Done; - } - - /* Update the header information */ - Header.EndOffset += RecBuf.Length; - - /* Free the buffer */ - RtlFreeHeap(GetProcessHeap(), 0, Buffer); - Buffer = NULL; - } - - /* Initialize the EOF record */ - RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); - EofRec.BeginRecord = Header.StartOffset; - EofRec.EndRecord = Header.EndOffset; - EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - - /* Write the EOF record (no wrap for the backup log) */ - Status = NtWriteFile(FileHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - &EofRec, - sizeof(EofRec), - NULL, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile() failed!\n"); - goto Done; - } - - /* Update the header information */ - Header.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - Header.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - Header.MaxSize = ROUND_UP(Header.EndOffset + sizeof(EofRec), sizeof(ULONG)); - Header.Flags = 0; - - /* Write the (clean) log file header */ - FileOffset.QuadPart = 0LL; - Status = NtWriteFile(FileHandle, - NULL, - NULL, - NULL, - &IoStatusBlock, - &Header, - sizeof(Header), - &FileOffset, - NULL); - if (!NT_SUCCESS(Status)) - { - DPRINT1("NtWriteFile() failed! (Status: 0x%08lx)\n", Status); - } - -Done: - /* Close the backup file */ - if (FileHandle != NULL) - NtClose(FileHandle); /* Unlock the log file */ RtlReleaseResource(&LogFile->Lock); @@ -2301,7 +1067,7 @@ LogfReportEvent(USHORT wType, dwDataSize, pRawData); - Status = LogfWriteRecord(EventLogSource->LogFile, RecSize, LogBuffer); + Status = LogfWriteRecord(EventLogSource->LogFile, LogBuffer, RecSize); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n", diff --git a/reactos/base/services/eventlog/logport.c b/reactos/base/services/eventlog/logport.c index 64779bc12b9..0af242a69ac 100644 --- a/reactos/base/services/eventlog/logport.c +++ b/reactos/base/services/eventlog/logport.c @@ -187,11 +187,11 @@ NTSTATUS ProcessPortMessage(VOID) if (!onLiveCD && SystemLog) { - Status = LogfWriteRecord(SystemLog, RecSize, pRec); + Status = LogfWriteRecord(SystemLog, pRec, RecSize); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n", - SystemLog->FileName, Status); + SystemLog->LogName, Status); } } diff --git a/reactos/base/services/eventlog/rpc.c b/reactos/base/services/eventlog/rpc.c index f5e72307565..40290fce3e2 100644 --- a/reactos/base/services/eventlog/rpc.c +++ b/reactos/base/services/eventlog/rpc.c @@ -16,27 +16,32 @@ #include static LIST_ENTRY LogHandleListHead; +static CRITICAL_SECTION LogHandleListCs; /* FUNCTIONS ****************************************************************/ +static NTSTATUS +ElfDeleteEventLogHandle(PIELF_HANDLE LogHandle); + DWORD WINAPI RpcThreadRoutine(LPVOID lpParameter) { RPC_STATUS Status; + InitializeCriticalSection(&LogHandleListCs); InitializeListHead(&LogHandleListHead); Status = RpcServerUseProtseqEpW(L"ncacn_np", 20, L"\\pipe\\EventLog", NULL); if (Status != RPC_S_OK) { DPRINT("RpcServerUseProtseqEpW() failed (Status %lx)\n", Status); - return 0; + goto Quit; } Status = RpcServerRegisterIf(eventlog_v0_0_s_ifspec, NULL, NULL); if (Status != RPC_S_OK) { DPRINT("RpcServerRegisterIf() failed (Status %lx)\n", Status); - return 0; + goto Quit; } Status = RpcServerListen(1, RPC_C_LISTEN_MAX_CALLS_DEFAULT, FALSE); @@ -45,6 +50,17 @@ DWORD WINAPI RpcThreadRoutine(LPVOID lpParameter) DPRINT("RpcServerListen() failed (Status %lx)\n", Status); } + EnterCriticalSection(&LogHandleListCs); + while (!IsListEmpty(&LogHandleListHead)) + { + IELF_HANDLE LogHandle = (IELF_HANDLE)CONTAINING_RECORD(LogHandleListHead.Flink, LOGHANDLE, LogHandleListEntry); + ElfDeleteEventLogHandle(&LogHandle); + } + LeaveCriticalSection(&LogHandleListCs); + +Quit: + DeleteCriticalSection(&LogHandleListCs); + return 0; } @@ -143,7 +159,9 @@ Done: if (NT_SUCCESS(Status)) { /* Append log handle */ + EnterCriticalSection(&LogHandleListCs); InsertTailList(&LogHandleListHead, &pLogHandle->LogHandleListEntry); + LeaveCriticalSection(&LogHandleListCs); *LogHandle = pLogHandle; } else @@ -199,7 +217,9 @@ Done: if (NT_SUCCESS(Status)) { /* Append log handle */ + EnterCriticalSection(&LogHandleListCs); InsertTailList(&LogHandleListHead, &pLogHandle->LogHandleListEntry); + LeaveCriticalSection(&LogHandleListCs); *LogHandle = pLogHandle; } else @@ -215,21 +235,28 @@ static PLOGHANDLE ElfGetLogHandleEntryByHandle(IELF_HANDLE EventLogHandle) { PLIST_ENTRY CurrentEntry; - PLOGHANDLE pLogHandle; + PLOGHANDLE Handle, pLogHandle = NULL; + + EnterCriticalSection(&LogHandleListCs); CurrentEntry = LogHandleListHead.Flink; while (CurrentEntry != &LogHandleListHead) { - pLogHandle = CONTAINING_RECORD(CurrentEntry, - LOGHANDLE, - LogHandleListEntry); + Handle = CONTAINING_RECORD(CurrentEntry, + LOGHANDLE, + LogHandleListEntry); CurrentEntry = CurrentEntry->Flink; - if (pLogHandle == EventLogHandle) - return pLogHandle; + if (Handle == EventLogHandle) + { + pLogHandle = Handle; + break; + } } - return NULL; + LeaveCriticalSection(&LogHandleListCs); + + return pLogHandle; } @@ -242,7 +269,10 @@ ElfDeleteEventLogHandle(PIELF_HANDLE LogHandle) if (!pLogHandle) return STATUS_INVALID_HANDLE; + EnterCriticalSection(&LogHandleListCs); RemoveEntryList(&pLogHandle->LogHandleListEntry); + LeaveCriticalSection(&LogHandleListCs); + LogfClose(pLogHandle->LogFile, FALSE); HeapFree(GetProcessHeap(), 0, pLogHandle); @@ -321,6 +351,7 @@ ElfrNumberOfRecords( { PLOGHANDLE pLogHandle; PLOGFILE pLogFile; + ULONG OldestRecordNumber, CurrentRecordNumber; DPRINT("ElfrNumberOfRecords()\n"); @@ -333,11 +364,19 @@ ElfrNumberOfRecords( pLogFile = pLogHandle->LogFile; + /* Lock the log file shared */ + RtlAcquireResourceShared(&pLogFile->Lock, TRUE); + + OldestRecordNumber = ElfGetOldestRecord(&pLogFile->LogFile); + CurrentRecordNumber = ElfGetCurrentRecord(&pLogFile->LogFile); + + /* Unlock the log file */ + RtlReleaseResource(&pLogFile->Lock); + DPRINT("Oldest: %lu Current: %lu\n", - pLogFile->Header.OldestRecordNumber, - pLogFile->Header.CurrentRecordNumber); + OldestRecordNumber, CurrentRecordNumber); - if (pLogFile->Header.OldestRecordNumber == 0) + if (OldestRecordNumber == 0) { /* OldestRecordNumber == 0 when the log is empty */ *NumberOfRecords = 0; @@ -345,8 +384,7 @@ ElfrNumberOfRecords( else { /* The log contains events */ - *NumberOfRecords = pLogFile->Header.CurrentRecordNumber - - pLogFile->Header.OldestRecordNumber; + *NumberOfRecords = CurrentRecordNumber - OldestRecordNumber; } return STATUS_SUCCESS; @@ -360,6 +398,7 @@ ElfrOldestRecord( PULONG OldestRecordNumber) { PLOGHANDLE pLogHandle; + PLOGFILE pLogFile; pLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); if (!pLogHandle) @@ -368,7 +407,15 @@ ElfrOldestRecord( if (!OldestRecordNumber) return STATUS_INVALID_PARAMETER; - *OldestRecordNumber = pLogHandle->LogFile->Header.OldestRecordNumber; + pLogFile = pLogHandle->LogFile; + + /* Lock the log file shared */ + RtlAcquireResourceShared(&pLogFile->Lock, TRUE); + + *OldestRecordNumber = ElfGetOldestRecord(&pLogFile->LogFile); + + /* Unlock the log file */ + RtlReleaseResource(&pLogFile->Lock); return STATUS_SUCCESS; } @@ -624,7 +671,7 @@ ElfrIntReportEventW( DataSize, Data); - Status = LogfWriteRecord(pLogHandle->LogFile, RecSize, LogBuffer); + Status = LogfWriteRecord(pLogHandle->LogFile, LogBuffer, RecSize); if (!NT_SUCCESS(Status)) { DPRINT1("ERROR writing to event log `%S' (Status 0x%08lx)\n", @@ -1068,11 +1115,17 @@ ElfrGetLogInformation( { NTSTATUS Status = STATUS_SUCCESS; PLOGHANDLE pLogHandle; + PLOGFILE pLogFile; pLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); if (!pLogHandle) return STATUS_INVALID_HANDLE; + pLogFile = pLogHandle->LogFile; + + /* Lock the log file shared */ + RtlAcquireResourceShared(&pLogFile->Lock, TRUE); + switch (InfoLevel) { case EVENTLOG_FULL_INFO: @@ -1086,12 +1139,7 @@ ElfrGetLogInformation( break; } - /* - * FIXME. To check whether an event log is "full" one needs - * to compare its current size with respect to the maximum - * size threshold "MaxSize" contained in its header. - */ - efi->dwFull = 0; + efi->dwFull = !!(ElfGetFlags(&pLogFile->LogFile) & ELF_LOGFILE_LOGFULL_WRITTEN); break; } @@ -1100,6 +1148,9 @@ ElfrGetLogInformation( break; } + /* Unlock the log file */ + RtlReleaseResource(&pLogFile->Lock); + return Status; } @@ -1109,14 +1160,25 @@ NTSTATUS ElfrFlushEL( IELF_HANDLE LogHandle) { + NTSTATUS Status; PLOGHANDLE pLogHandle; + PLOGFILE pLogFile; pLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); if (!pLogHandle) return STATUS_INVALID_HANDLE; - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + pLogFile = pLogHandle->LogFile; + + /* Lock the log file exclusive */ + RtlAcquireResourceExclusive(&pLogFile->Lock, TRUE); + + Status = ElfFlushFile(&pLogFile->LogFile); + + /* Unlock the log file */ + RtlReleaseResource(&pLogFile->Lock); + + return Status; } diff --git a/reactos/sdk/lib/evtlib/CMakeLists.txt b/reactos/sdk/lib/evtlib/CMakeLists.txt new file mode 100644 index 00000000000..0a863433f50 --- /dev/null +++ b/reactos/sdk/lib/evtlib/CMakeLists.txt @@ -0,0 +1,5 @@ + +## FIXME: Make the library cross-compiling aware (like cmlib or inflib) + +add_library(evtlib evtlib.c) +add_dependencies(evtlib xdk) diff --git a/reactos/sdk/lib/evtlib/evtlib.c b/reactos/sdk/lib/evtlib/evtlib.c new file mode 100644 index 00000000000..78c8554add5 --- /dev/null +++ b/reactos/sdk/lib/evtlib/evtlib.c @@ -0,0 +1,1660 @@ +/* + * PROJECT: ReactOS EventLog File Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: sdk/lib/evtlib/evtlib.c + * PURPOSE: Provides a library for reading and writing EventLog files + * in the NT <= 5.2 (.evt) format. + * PROGRAMMERS: Copyright 2005 Saveliy Tretiakov + * Michael Martin + * Hermes Belusca-Maito + */ + +/* INCLUDES ******************************************************************/ + +#include "evtlib.h" + +#define NDEBUG +#include + +#define EVTLTRACE(...) DPRINT("EvtLib: " __VA_ARGS__) +// Once things become stabilized enough, replace all the EVTLTRACE1 by EVTLTRACE +#define EVTLTRACE1(...) DPRINT1("EvtLib: " __VA_ARGS__) + + +/* GLOBALS *******************************************************************/ + +static const EVENTLOGEOF EOFRecord = +{ + sizeof(EOFRecord), + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0, 0, 0, 0, + sizeof(EOFRecord) +}; + +/* HELPER FUNCTIONS **********************************************************/ + +static NTSTATUS +ReadLogBuffer( + IN PEVTLOGFILE LogFile, + OUT PVOID Buffer, + IN SIZE_T Length, + OUT PSIZE_T ReadLength OPTIONAL, + IN PLARGE_INTEGER ByteOffset, + OUT PLARGE_INTEGER NextOffset OPTIONAL) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset; + SIZE_T BufSize; + SIZE_T ReadBufLength = 0, OldReadBufLength; + + ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize); + ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize); + + if (ReadLength) + *ReadLength = 0; + + if (NextOffset) + NextOffset->QuadPart = 0LL; + + /* Read the first part of the buffer */ + FileOffset = *ByteOffset; + BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart); + + Status = LogFile->FileRead(LogFile, + &FileOffset, + Buffer, + BufSize, + &ReadBufLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status); + return Status; + } + + if (Length > BufSize) + { + OldReadBufLength = ReadBufLength; + + /* + * The buffer was splitted in two, its second part + * is to be read at the beginning of the log. + */ + Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize); + BufSize = Length - BufSize; + FileOffset.QuadPart = sizeof(EVENTLOGHEADER); + + Status = LogFile->FileRead(LogFile, + &FileOffset, + Buffer, + BufSize, + &ReadBufLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE("FileRead() failed (Status 0x%08lx)\n", Status); + return Status; + } + /* Add the read number of bytes from the first read */ + ReadBufLength += OldReadBufLength; + } + + if (ReadLength) + *ReadLength = ReadBufLength; + + /* We return the offset just after the end of the read buffer */ + if (NextOffset) + NextOffset->QuadPart = FileOffset.QuadPart + BufSize; + + return Status; +} + +static NTSTATUS +WriteLogBuffer( + IN PEVTLOGFILE LogFile, + IN PVOID Buffer, + IN SIZE_T Length, + OUT PSIZE_T WrittenLength OPTIONAL, + IN PLARGE_INTEGER ByteOffset, + OUT PLARGE_INTEGER NextOffset OPTIONAL) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset; + SIZE_T BufSize; + SIZE_T WrittenBufLength = 0, OldWrittenBufLength; + + /* We must have write access to the log file */ + ASSERT(!LogFile->ReadOnly); + + /* + * It is expected that the log file is already correctly expanded + * before we can write in it. Therefore the following assertions hold. + */ + ASSERT(LogFile->CurrentSize <= LogFile->Header.MaxSize); + ASSERT(ByteOffset->QuadPart <= LogFile->CurrentSize); + + if (WrittenLength) + *WrittenLength = 0; + + if (NextOffset) + NextOffset->QuadPart = 0LL; + + /* Write the first part of the buffer */ + FileOffset = *ByteOffset; + BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart); + + Status = LogFile->FileWrite(LogFile, + &FileOffset, + Buffer, + BufSize, + &WrittenBufLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status); + return Status; + } + + if (Length > BufSize) + { + OldWrittenBufLength = WrittenBufLength; + + /* + * The buffer was splitted in two, its second part + * is written at the beginning of the log. + */ + Buffer = (PVOID)((ULONG_PTR)Buffer + BufSize); + BufSize = Length - BufSize; + FileOffset.QuadPart = sizeof(EVENTLOGHEADER); + + Status = LogFile->FileWrite(LogFile, + &FileOffset, + Buffer, + BufSize, + &WrittenBufLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE("FileWrite() failed (Status 0x%08lx)\n", Status); + return Status; + } + /* Add the written number of bytes from the first write */ + WrittenBufLength += OldWrittenBufLength; + + /* The log wraps */ + LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; + } + + if (WrittenLength) + *WrittenLength = WrittenBufLength; + + /* We return the offset just after the end of the written buffer */ + if (NextOffset) + NextOffset->QuadPart = FileOffset.QuadPart + BufSize; + + return Status; +} + + +/* Returns 0 if nothing is found */ +static ULONG +ElfpOffsetByNumber( + IN PEVTLOGFILE LogFile, + IN ULONG RecordNumber) +{ + UINT i; + + for (i = 0; i < LogFile->OffsetInfoNext; i++) + { + if (LogFile->OffsetInfo[i].EventNumber == RecordNumber) + return LogFile->OffsetInfo[i].EventOffset; + } + return 0; +} + +#define OFFSET_INFO_INCREMENT 64 + +static BOOL +ElfpAddOffsetInformation( + IN PEVTLOGFILE LogFile, + IN ULONG ulNumber, + IN ULONG ulOffset) +{ + PVOID NewOffsetInfo; + + if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize) + { + /* Allocate a new offset table */ + NewOffsetInfo = LogFile->Allocate((LogFile->OffsetInfoSize + OFFSET_INFO_INCREMENT) * + sizeof(EVENT_OFFSET_INFO), + HEAP_ZERO_MEMORY, + TAG_ELF); + if (!NewOffsetInfo) + { + EVTLTRACE1("Cannot reallocate heap.\n"); + return FALSE; + } + + /* Free the old offset table and use the new one */ + if (LogFile->OffsetInfo) + { + /* Copy the handles from the old table to the new one */ + RtlCopyMemory(NewOffsetInfo, + LogFile->OffsetInfo, + LogFile->OffsetInfoSize * sizeof(EVENT_OFFSET_INFO)); + LogFile->Free(LogFile->OffsetInfo, 0); + } + LogFile->OffsetInfo = (PEVENT_OFFSET_INFO)NewOffsetInfo; + LogFile->OffsetInfoSize += OFFSET_INFO_INCREMENT; + } + + LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventNumber = ulNumber; + LogFile->OffsetInfo[LogFile->OffsetInfoNext].EventOffset = ulOffset; + LogFile->OffsetInfoNext++; + + return TRUE; +} + +static BOOL +ElfpDeleteOffsetInformation( + IN PEVTLOGFILE LogFile, + IN ULONG ulNumberMin, + IN ULONG ulNumberMax) +{ + UINT i; + + if (ulNumberMin > ulNumberMax) + return FALSE; + + /* Remove records ulNumberMin to ulNumberMax inclusive */ + while (ulNumberMin <= ulNumberMax) + { + /* + * As the offset information is listed in increasing order, and we want + * to keep the list without holes, we demand that ulNumberMin is the first + * element in the list. + */ + if (ulNumberMin != LogFile->OffsetInfo[0].EventNumber) + return FALSE; + + /* + * RtlMoveMemory(&LogFile->OffsetInfo[0], + * &LogFile->OffsetInfo[1], + * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1)); + */ + for (i = 0; i < LogFile->OffsetInfoNext - 1; i++) + { + LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber; + LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset; + } + LogFile->OffsetInfoNext--; + + /* Go to the next offset information */ + ulNumberMin++; + } + + return TRUE; +} + + +static NTSTATUS +ElfpInitNewFile( + IN PEVTLOGFILE LogFile, + IN ULONG FileSize, + IN ULONG MaxSize, + IN ULONG Retention) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset; + SIZE_T WrittenLength; + EVENTLOGEOF EofRec; + + /* Initialize the event log header */ + RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER)); + + LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER); + LogFile->Header.Signature = LOGFILE_SIGNATURE; + LogFile->Header.MajorVersion = MAJORVER; + LogFile->Header.MinorVersion = MINORVER; + + /* Set the offset to the oldest record */ + LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER); + /* Set the offset to the ELF_EOF_RECORD */ + LogFile->Header.EndOffset = sizeof(EVENTLOGHEADER); + /* Set the number of the next record that will be added */ + LogFile->Header.CurrentRecordNumber = 1; + /* The event log is empty, there is no record so far */ + LogFile->Header.OldestRecordNumber = 0; + + // FIXME: Windows' EventLog log file sizes are always multiple of 64kB + // but that does not mean the real log size is == file size. + + /* Round MaxSize to be a multiple of ULONG (normally on Windows: multiple of 64 kB) */ + LogFile->Header.MaxSize = ROUND_UP(MaxSize, sizeof(ULONG)); + LogFile->CurrentSize = LogFile->Header.MaxSize; // or: FileSize ?? + LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0); + + LogFile->Header.Flags = 0; + LogFile->Header.Retention = Retention; + LogFile->Header.EndHeaderSize = sizeof(EVENTLOGHEADER); + + /* Write the header */ + FileOffset.QuadPart = 0LL; + Status = LogFile->FileWrite(LogFile, + &FileOffset, + &LogFile->Header, + sizeof(EVENTLOGHEADER), + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); + return Status; + } + + /* Initialize the ELF_EOF_RECORD and write it */ + RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); + EofRec.BeginRecord = LogFile->Header.StartOffset; + EofRec.EndRecord = LogFile->Header.EndOffset; + EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; + EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; + + Status = LogFile->FileWrite(LogFile, + NULL, + &EofRec, + sizeof(EofRec), + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); + return Status; + } + + Status = LogFile->FileFlush(LogFile, NULL, 0); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status); + return Status; + } + + return STATUS_SUCCESS; +} + +static NTSTATUS +ElfpInitExistingFile( + IN PEVTLOGFILE LogFile, + IN ULONG FileSize, + // IN ULONG MaxSize, + IN ULONG Retention) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset, NextOffset; + SIZE_T ReadLength; + ULONG RecordNumber = 0; + ULONG RecOffset; + PULONG pRecSize2; + EVENTLOGEOF EofRec; + EVENTLOGRECORD RecBuf; + PEVENTLOGRECORD pRecBuf; + BOOLEAN Wrapping = FALSE; + BOOLEAN IsLogDirty = FALSE; + + /* Read the log header */ + FileOffset.QuadPart = 0LL; + Status = LogFile->FileRead(LogFile, + &FileOffset, + &LogFile->Header, + sizeof(EVENTLOGHEADER), + &ReadLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); + return STATUS_EVENTLOG_FILE_CORRUPT; // return Status; + } + if (ReadLength != sizeof(EVENTLOGHEADER)) + { + EVTLTRACE("Invalid file `%wZ'.\n", &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* Header validity checks */ + + if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) || + LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER)) + { + EVTLTRACE("Invalid header size in `%wZ'.\n", &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + if (LogFile->Header.Signature != LOGFILE_SIGNATURE) + { + EVTLTRACE("Invalid signature %x in `%wZ'.\n", + LogFile->Header.Signature, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY); + + /* If the log is read-only (e.g. a backup log) and is dirty, then it is corrupted */ + if (LogFile->ReadOnly && IsLogDirty) + { + EVTLTRACE("Read-only log `%wZ' is dirty.\n", &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + LogFile->CurrentSize = FileSize; + // FIXME!! What to do? And what to do if the MaxSize from the registry + // is strictly less than the CurrentSize?? Should we "reduce" the log size + // by clearing it completely?? + // --> ANSWER: Save the new MaxSize somewhere, and only when the log is + // being cleared, use the new MaxSize to resize (ie. shrink) it. + // LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0); + + /* Adjust the log maximum size if needed */ + if (LogFile->CurrentSize > LogFile->Header.MaxSize) + LogFile->Header.MaxSize = LogFile->CurrentSize; + + /* + * Reset the log retention value. The value stored + * in the log file is just for information purposes. + */ + LogFile->Header.Retention = Retention; + + /* + * For a non-read-only dirty log, the most up-to-date information about + * the Start/End offsets and the Oldest and Current event record numbers + * are found in the EOF record. We need to locate the EOF record without + * relying on the log header's EndOffset, then patch the log header with + * the values from the EOF record. + */ + if ((LogFile->Header.EndOffset >= sizeof(EVENTLOGHEADER)) && + (LogFile->Header.EndOffset < LogFile->CurrentSize) && + (LogFile->Header.EndOffset & 3) == 0) // EndOffset % sizeof(ULONG) == 0 + { + /* The header EOF offset may be valid, try to start with it */ + RecOffset = LogFile->Header.EndOffset; + } + else + { + /* The header EOF offset could not be valid, so start from the beginning */ + RecOffset = sizeof(EVENTLOGHEADER); + } + + FileOffset.QuadPart = RecOffset; + Wrapping = FALSE; + + for (;;) + { + if (Wrapping && FileOffset.QuadPart >= RecOffset) + { + EVTLTRACE1("EOF record not found!\n"); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* Attempt to read the fixed part of an EVENTLOGEOF (may wrap) */ + Status = ReadLogBuffer(LogFile, + &EofRec, + EVENTLOGEOF_SIZE_FIXED, + &ReadLength, + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + if (ReadLength != EVENTLOGEOF_SIZE_FIXED) + { + EVTLTRACE1("Cannot read at most an EOF record!\n"); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* Is it an EVENTLOGEOF record? */ + if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED) + { + DPRINT1("Found EOF record at %llx\n", FileOffset.QuadPart); + + /* Got it! Break the loop and continue */ + break; + } + + /* No, continue looping */ + if (*(PULONG)((ULONG_PTR)&EofRec + sizeof(ULONG)) == *(PULONG)(&EOFRecord)) + FileOffset.QuadPart += sizeof(ULONG); + else + if (*(PULONG)((ULONG_PTR)&EofRec + 2*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) + FileOffset.QuadPart += 2*sizeof(ULONG); + else + if (*(PULONG)((ULONG_PTR)&EofRec + 3*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) + FileOffset.QuadPart += 3*sizeof(ULONG); + else + if (*(PULONG)((ULONG_PTR)&EofRec + 4*sizeof(ULONG)) == *(PULONG)(&EOFRecord)) + FileOffset.QuadPart += 4*sizeof(ULONG); + else + FileOffset.QuadPart += 5*sizeof(ULONG); // EVENTLOGEOF_SIZE_FIXED + + if (FileOffset.QuadPart >= LogFile->CurrentSize /* LogFile->Header.MaxSize */) + { + /* Wrap the offset */ + FileOffset.QuadPart -= LogFile->CurrentSize /* LogFile->Header.MaxSize */ - sizeof(EVENTLOGHEADER); + Wrapping = TRUE; + } + } + /* + * The only way to be there is to have found a valid EOF record. + * Otherwise the previous loop has failed and STATUS_EVENTLOG_FILE_CORRUPT + * was returned. + */ + + /* Read the full EVENTLOGEOF (may wrap) and validate it */ + Status = ReadLogBuffer(LogFile, + &EofRec, + sizeof(EofRec), + &ReadLength, + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + if (ReadLength != sizeof(EofRec)) + { + EVTLTRACE1("Cannot read the full EOF record!\n"); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* Complete validity checks */ + if ((EofRec.RecordSizeEnd != EofRec.RecordSizeBeginning) || + (EofRec.EndRecord != FileOffset.QuadPart)) + { + DPRINT1("EOF record %llx is corrupted (0x%x vs. 0x%x ; 0x%x vs. 0x%llx), expected 0x%x 0x%x!\n", + FileOffset.QuadPart, + EofRec.RecordSizeEnd, EofRec.RecordSizeBeginning, + EofRec.EndRecord, FileOffset.QuadPart, + EOFRecord.RecordSizeEnd, EOFRecord.RecordSizeBeginning); + DPRINT1("RecordSizeEnd = 0x%x\n", EofRec.RecordSizeEnd); + DPRINT1("RecordSizeBeginning = 0x%x\n", EofRec.RecordSizeBeginning); + DPRINT1("EndRecord = 0x%x\n", EofRec.EndRecord); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* The EOF record is valid, break the loop and continue */ + + /* If the log is not dirty, the header values should correspond to the EOF ones */ + if (!IsLogDirty) + { + if ( (LogFile->Header.StartOffset != EofRec.BeginRecord) || + (LogFile->Header.EndOffset != EofRec.EndRecord) || + (LogFile->Header.CurrentRecordNumber != EofRec.CurrentRecordNumber) || + (LogFile->Header.OldestRecordNumber != EofRec.OldestRecordNumber) ) + { + DPRINT1("\n" + "Log header or EOF record is corrupted:\n" + " StartOffset: 0x%x, expected 0x%x; EndOffset: 0x%x, expected 0x%x;\n" + " CurrentRecordNumber: %d, expected %d; OldestRecordNumber: %d, expected %d.\n", + LogFile->Header.StartOffset, EofRec.BeginRecord, + LogFile->Header.EndOffset , EofRec.EndRecord, + LogFile->Header.CurrentRecordNumber, EofRec.CurrentRecordNumber, + LogFile->Header.OldestRecordNumber , EofRec.OldestRecordNumber); + + return STATUS_EVENTLOG_FILE_CORRUPT; + } + } + + /* If the log is dirty, patch the log header with the values from the EOF record */ + if (!LogFile->ReadOnly && IsLogDirty) + { + LogFile->Header.StartOffset = EofRec.BeginRecord; + LogFile->Header.EndOffset = EofRec.EndRecord; + LogFile->Header.CurrentRecordNumber = EofRec.CurrentRecordNumber; + LogFile->Header.OldestRecordNumber = EofRec.OldestRecordNumber; + } + + /* + * FIXME! During operations the EOF record is the one that is the most + * updated (its Oldest & Current record numbers are always up-to + * date) while the ones from the header may be unsync. When closing + * (or flushing?) the event log, the header's record numbers get + * updated with the same values as the ones stored in the EOF record. + */ + + /* Verify Start/End offsets boundaries */ + + if ((LogFile->Header.StartOffset >= LogFile->CurrentSize) || + (LogFile->Header.StartOffset & 3) != 0) // StartOffset % sizeof(ULONG) != 0 + { + EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n", + LogFile->Header.StartOffset, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + if ((LogFile->Header.EndOffset >= LogFile->CurrentSize) || + (LogFile->Header.EndOffset & 3) != 0) // EndOffset % sizeof(ULONG) != 0 + { + EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n", + LogFile->Header.EndOffset, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) && + (LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) + { + /* + * If StartOffset does not point to EndOffset i.e. to an EVENTLOGEOF, + * it should point to a non-splitted EVENTLOGRECORD. + */ + EVTLTRACE("Invalid start offset 0x%x in `%wZ'.\n", + LogFile->Header.StartOffset, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) && + (LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) + { + /* + * In non-wrapping case, there must be enough space between StartOffset + * and EndOffset to contain at least a full EVENTLOGRECORD. + */ + EVTLTRACE("Invalid start offset 0x%x or end offset 0x%x in `%wZ'.\n", + LogFile->Header.StartOffset, LogFile->Header.EndOffset, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) + { + /* + * Non-wrapping case: the (wrapping) free space starting at EndOffset + * must be able to contain an EVENTLOGEOF. + */ + if (LogFile->Header.MaxSize - LogFile->Header.EndOffset + + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER) < sizeof(EVENTLOGEOF)) + { + EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n", + LogFile->Header.EndOffset, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + } + else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) + { + /* + * Wrapping case: the free space between EndOffset and StartOffset + * must be able to contain an EVENTLOGEOF. + */ + if (LogFile->Header.StartOffset - LogFile->Header.EndOffset < sizeof(EVENTLOGEOF)) + { + EVTLTRACE("Invalid EOF offset 0x%x in `%wZ'.\n", + LogFile->Header.EndOffset, &LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + } + + /* Start enumerating the event records from the beginning */ + RecOffset = LogFile->Header.StartOffset; + FileOffset.QuadPart = RecOffset; + Wrapping = FALSE; + + // // FIXME! FIXME! + // if (!(LogFile->Header.Flags & ELF_LOGFILE_HEADER_WRAP)) + // { + // DPRINT1("Log file was wrapping but the flag was not set! Fixing...\n"); + // LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; + // } + + DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n", + LogFile->Header.StartOffset, LogFile->Header.EndOffset); + + /* + * For non-read-only logs of size < MaxSize, reorganize the events + * such that they do not wrap as soon as we write new ones. + */ +#if 0 + if (!LogFile->ReadOnly) + { + pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF); + if (pRecBuf == NULL) + { + DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n"); + goto Continue; + } + + // TODO: Do the job! + } + +Continue: + + DPRINT1("StartOffset = 0x%x, EndOffset = 0x%x\n", + LogFile->Header.StartOffset, LogFile->Header.EndOffset); +#endif + + while (FileOffset.QuadPart != LogFile->Header.EndOffset) + { + if (Wrapping && FileOffset.QuadPart >= RecOffset) + { + /* We have finished enumerating all the event records */ + break; + } + + /* Read the next EVENTLOGRECORD header at once (it cannot be split) */ + Status = LogFile->FileRead(LogFile, + &FileOffset, + &RecBuf, + sizeof(RecBuf), + &ReadLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + if (ReadLength != sizeof(RecBuf)) + { + DPRINT1("Length != sizeof(RecBuf)\n"); + break; + } + + if (RecBuf.Reserved != LOGFILE_SIGNATURE || + RecBuf.Length < sizeof(EVENTLOGRECORD)) + { + DPRINT1("RecBuf problem\n"); + break; + } + + /* Allocate a full EVENTLOGRECORD (header + data) */ + pRecBuf = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF); + if (pRecBuf == NULL) + { + EVTLTRACE1("Cannot allocate heap!\n"); + return STATUS_NO_MEMORY; + } + + /* Attempt to read the full EVENTLOGRECORD (can wrap) */ + Status = ReadLogBuffer(LogFile, + pRecBuf, + RecBuf.Length, + &ReadLength, + &FileOffset, + &NextOffset); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + LogFile->Free(pRecBuf, 0); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + if (ReadLength != RecBuf.Length) + { + DPRINT1("Oh oh!!\n"); + LogFile->Free(pRecBuf, 0); + break; + } + + // /* If OverWrittenRecords is TRUE and this record has already been read */ + // if (OverWrittenRecords && (pRecBuf->RecordNumber == LogFile->Header.OldestRecordNumber)) + // { + // LogFile->Free(pRecBuf, 0); + // break; + // } + + pRecSize2 = (PULONG)((ULONG_PTR)pRecBuf + RecBuf.Length - 4); + + if (*pRecSize2 != RecBuf.Length) + { + EVTLTRACE1("Invalid RecordSizeEnd of record %d (0x%x) in `%wZ'\n", + RecordNumber, *pRecSize2, &LogFile->FileName); + LogFile->Free(pRecBuf, 0); + break; + } + + EVTLTRACE1("Add new record %d @ offset 0x%x\n", pRecBuf->RecordNumber, FileOffset.QuadPart); + + RecordNumber++; + + if (!ElfpAddOffsetInformation(LogFile, + pRecBuf->RecordNumber, + FileOffset.QuadPart)) + { + EVTLTRACE1("ElfpAddOffsetInformation() failed!\n"); + LogFile->Free(pRecBuf, 0); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + LogFile->Free(pRecBuf, 0); + + if (NextOffset.QuadPart == LogFile->Header.EndOffset) + { + /* We have finished enumerating all the event records */ + DPRINT1("NextOffset.QuadPart == LogFile->Header.EndOffset, break\n"); + break; + } + + /* + * If this was the last event record before the end of the log file, + * the next one should start at the beginning of the log and the space + * between the last event record and the end of the file is padded. + */ + if (LogFile->Header.MaxSize - NextOffset.QuadPart < sizeof(EVENTLOGRECORD)) + { + /* Wrap to the beginning of the log */ + DPRINT1("Wrap!\n"); + NextOffset.QuadPart = sizeof(EVENTLOGHEADER); + } + + /* + * If the next offset to read is below the current offset, + * this means we are wrapping. + */ + if (FileOffset.QuadPart > NextOffset.QuadPart) + { + DPRINT1("Wrapping = TRUE;\n"); + Wrapping = TRUE; + } + + /* Move the current offset */ + FileOffset = NextOffset; + } + + /* If the event log was empty, it will now contain one record */ + if (RecordNumber != 0 && LogFile->Header.OldestRecordNumber == 0) + LogFile->Header.OldestRecordNumber = 1; + + LogFile->Header.CurrentRecordNumber = RecordNumber + LogFile->Header.OldestRecordNumber; + if (LogFile->Header.CurrentRecordNumber == 0) + LogFile->Header.CurrentRecordNumber = 1; + + /* Flush the log if it is not read-only */ + if (!LogFile->ReadOnly) + { + Status = ElfFlushFile(LogFile); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status); + return STATUS_EVENTLOG_FILE_CORRUPT; // Status; + } + } + + return STATUS_SUCCESS; +} + + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS +NTAPI +ElfCreateFile( + IN PEVTLOGFILE LogFile, + IN PUNICODE_STRING FileName OPTIONAL, + IN ULONG FileSize, + IN ULONG MaxSize, + IN ULONG Retention, + IN BOOLEAN CreateNew, + IN BOOLEAN ReadOnly, + IN PELF_ALLOCATE_ROUTINE Allocate, + IN PELF_FREE_ROUTINE Free, + IN PELF_FILE_SET_SIZE_ROUTINE FileSetSize, + IN PELF_FILE_WRITE_ROUTINE FileWrite, + IN PELF_FILE_READ_ROUTINE FileRead, + IN PELF_FILE_FLUSH_ROUTINE FileFlush) // What about Seek ?? +{ + NTSTATUS Status = STATUS_SUCCESS; + + ASSERT(LogFile); + + /* Creating a new log file with the 'ReadOnly' flag set is incompatible */ + if (CreateNew && ReadOnly) + return STATUS_INVALID_PARAMETER; + + RtlZeroMemory(LogFile, sizeof(*LogFile)); + + LogFile->Allocate = Allocate; + LogFile->Free = Free; + LogFile->FileSetSize = FileSetSize; + LogFile->FileWrite = FileWrite; + LogFile->FileRead = FileRead; + LogFile->FileFlush = FileFlush; + + /* Copy the log file name if provided (optional) */ + RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0); + if (FileName && FileName->Buffer && FileName->Length && + (FileName->Length <= FileName->MaximumLength)) + { + LogFile->FileName.Buffer = LogFile->Allocate(FileName->Length, + HEAP_ZERO_MEMORY, + TAG_ELF); + if (LogFile->FileName.Buffer) + { + LogFile->FileName.MaximumLength = FileName->Length; + RtlCopyUnicodeString(&LogFile->FileName, FileName); + } + } + + LogFile->OffsetInfo = LogFile->Allocate(OFFSET_INFO_INCREMENT * sizeof(EVENT_OFFSET_INFO), + HEAP_ZERO_MEMORY, + TAG_ELF); + if (LogFile->OffsetInfo == NULL) + { + EVTLTRACE1("Cannot allocate heap\n"); + Status = STATUS_NO_MEMORY; + goto Quit; + } + LogFile->OffsetInfoSize = OFFSET_INFO_INCREMENT; + LogFile->OffsetInfoNext = 0; + + // FIXME: Always use the regitry values for MaxSize, + // even for existing logs! + + // FIXME: On Windows, EventLog uses the MaxSize setting + // from the registry itself; the MaxSize from the header + // is just for information purposes. + + EVTLTRACE("Initializing log file `%wZ'\n", &LogFile->FileName); + + LogFile->ReadOnly = ReadOnly; // !CreateNew && ReadOnly; + + if (CreateNew) + Status = ElfpInitNewFile(LogFile, FileSize, MaxSize, Retention); + else + Status = ElfpInitExistingFile(LogFile, FileSize, /* MaxSize, */ Retention); + +Quit: + if (!NT_SUCCESS(Status)) + { + if (LogFile->OffsetInfo) + LogFile->Free(LogFile->OffsetInfo, 0); + + if (LogFile->FileName.Buffer) + LogFile->Free(LogFile->FileName.Buffer, 0); + } + + return Status; + +} + +NTSTATUS +NTAPI +ElfReCreateFile( + IN PEVTLOGFILE LogFile) +{ + ASSERT(LogFile); + + return ElfpInitNewFile(LogFile, + LogFile->CurrentSize, + LogFile->Header.MaxSize, + LogFile->Header.Retention); +} + +NTSTATUS +NTAPI +ElfBackupFile( + IN PEVTLOGFILE LogFile, + IN PEVTLOGFILE BackupLogFile) +{ + NTSTATUS Status; + + LARGE_INTEGER FileOffset; + SIZE_T ReadLength, WrittenLength; + PEVENTLOGHEADER Header; + EVENTLOGRECORD RecBuf; + EVENTLOGEOF EofRec; + ULONG i; + ULONG RecOffset; + PVOID Buffer = NULL; + + ASSERT(LogFile); + + RtlZeroMemory(BackupLogFile, sizeof(*BackupLogFile)); + + BackupLogFile->FileSetSize = LogFile->FileSetSize; + BackupLogFile->FileWrite = LogFile->FileWrite; + BackupLogFile->FileFlush = LogFile->FileFlush; + + // BackupLogFile->CurrentSize = LogFile->CurrentSize; + + BackupLogFile->ReadOnly = FALSE; + + /* Initialize the (dirty) log file header */ + Header = &BackupLogFile->Header; + Header->HeaderSize = sizeof(EVENTLOGHEADER); + Header->Signature = LOGFILE_SIGNATURE; + Header->MajorVersion = MAJORVER; + Header->MinorVersion = MINORVER; + Header->StartOffset = sizeof(EVENTLOGHEADER); + Header->EndOffset = sizeof(EVENTLOGHEADER); + Header->CurrentRecordNumber = 1; + Header->OldestRecordNumber = 0; + Header->MaxSize = LogFile->Header.MaxSize; + Header->Flags = ELF_LOGFILE_HEADER_DIRTY; + Header->Retention = LogFile->Header.Retention; + Header->EndHeaderSize = sizeof(EVENTLOGHEADER); + + /* Write the (dirty) log file header */ + FileOffset.QuadPart = 0LL; + Status = BackupLogFile->FileWrite(BackupLogFile, + &FileOffset, + Header, + sizeof(EVENTLOGHEADER), + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("Failed to write the log file header (Status 0x%08lx)\n", Status); + goto Quit; + } + + for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++) + { + RecOffset = ElfpOffsetByNumber(LogFile, i); + if (RecOffset == 0) + break; + + /* Read the next EVENTLOGRECORD header at once (it cannot be split) */ + FileOffset.QuadPart = RecOffset; + Status = LogFile->FileRead(LogFile, + &FileOffset, + &RecBuf, + sizeof(RecBuf), + &ReadLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); + goto Quit; + } + + // if (ReadLength != sizeof(RecBuf)) + // break; + + Buffer = LogFile->Allocate(RecBuf.Length, 0, TAG_ELF_BUF); + if (Buffer == NULL) + { + EVTLTRACE1("Allocate() failed!\n"); + goto Quit; + } + + /* Read the full EVENTLOGRECORD (header + data) with wrapping */ + Status = ReadLogBuffer(LogFile, + Buffer, + RecBuf.Length, + &ReadLength, + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + LogFile->Free(Buffer, 0); + // Status = STATUS_EVENTLOG_FILE_CORRUPT; + goto Quit; + } + + /* Write the event record (no wrap for the backup log) */ + Status = BackupLogFile->FileWrite(BackupLogFile, + NULL, + Buffer, + RecBuf.Length, + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); + LogFile->Free(Buffer, 0); + goto Quit; + } + + /* Update the header information */ + Header->EndOffset += RecBuf.Length; + + /* Free the buffer */ + LogFile->Free(Buffer, 0); + Buffer = NULL; + } + +// Quit: + + /* Initialize the ELF_EOF_RECORD and write it (no wrap for the backup log) */ + RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); + EofRec.BeginRecord = Header->StartOffset; + EofRec.EndRecord = Header->EndOffset; + EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; + EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; + + Status = BackupLogFile->FileWrite(BackupLogFile, + NULL, + &EofRec, + sizeof(EofRec), + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); + goto Quit; + } + + /* Update the header information */ + Header->CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; + Header->OldestRecordNumber = LogFile->Header.OldestRecordNumber; + Header->MaxSize = ROUND_UP(Header->EndOffset + sizeof(EofRec), sizeof(ULONG)); + Header->Flags = 0; // FIXME? + + /* Flush the log file - Write the (clean) log file header */ + Status = ElfFlushFile(BackupLogFile); + +Quit: + return Status; +} + +NTSTATUS +NTAPI +ElfFlushFile( + IN PEVTLOGFILE LogFile) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset; + SIZE_T WrittenLength; + + ASSERT(LogFile); + + if (LogFile->ReadOnly) + return STATUS_SUCCESS; // STATUS_ACCESS_DENIED; + + /* + * NOTE that both the EOF record *AND* the log file header + * are supposed to be already updated! + * We just remove the dirty log bit. + */ + LogFile->Header.Flags &= ~ELF_LOGFILE_HEADER_DIRTY; + + /* Update the log file header */ + FileOffset.QuadPart = 0LL; + Status = LogFile->FileWrite(LogFile, + &FileOffset, + &LogFile->Header, + sizeof(EVENTLOGHEADER), + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); + return Status; + } + + /* Flush the log file */ + Status = LogFile->FileFlush(LogFile, NULL, 0); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileFlush() failed (Status 0x%08lx)\n", Status); + return Status; + } + + return STATUS_SUCCESS; +} + +VOID +NTAPI +ElfCloseFile( // ElfFree + IN PEVTLOGFILE LogFile) +{ + ASSERT(LogFile); + + /* Flush the log file */ + ElfFlushFile(LogFile); + + /* Free the data */ + LogFile->Free(LogFile->OffsetInfo, 0); + + if (LogFile->FileName.Buffer) + LogFile->Free(LogFile->FileName.Buffer, 0); + RtlInitEmptyUnicodeString(&LogFile->FileName, NULL, 0); +} + +NTSTATUS +NTAPI +ElfReadRecord( + IN PEVTLOGFILE LogFile, + IN ULONG RecordNumber, + OUT PEVENTLOGRECORD Record, + IN SIZE_T BufSize, // Length + OUT PSIZE_T BytesRead OPTIONAL, + OUT PSIZE_T BytesNeeded OPTIONAL) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset; + ULONG RecOffset; + SIZE_T RecSize; + SIZE_T ReadLength; + + ASSERT(LogFile); + + if (BytesRead) + *BytesRead = 0; + + if (BytesNeeded) + *BytesNeeded = 0; + + /* Retrieve the offset of the event record */ + RecOffset = ElfpOffsetByNumber(LogFile, RecordNumber); + if (RecOffset == 0) + return STATUS_NOT_FOUND; + + /* Retrieve its full size */ + FileOffset.QuadPart = RecOffset; + Status = LogFile->FileRead(LogFile, + &FileOffset, + &RecSize, + sizeof(RecSize), + &ReadLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); + // Status = STATUS_EVENTLOG_FILE_CORRUPT; + return Status; + } + + /* Check whether the buffer is big enough to hold the event record */ + if (BufSize < RecSize) + { + if (BytesNeeded) + *BytesNeeded = RecSize; + + return STATUS_BUFFER_TOO_SMALL; + } + + /* Read the event record into the buffer */ + FileOffset.QuadPart = RecOffset; + Status = ReadLogBuffer(LogFile, + Record, + RecSize, + &ReadLength, + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + // Status = STATUS_EVENTLOG_FILE_CORRUPT; + } + + if (BytesRead) + *BytesRead = ReadLength; + + return Status; +} + +NTSTATUS +NTAPI +ElfWriteRecord( + IN PEVTLOGFILE LogFile, + IN PEVENTLOGRECORD Record, + IN SIZE_T BufSize) +{ + NTSTATUS Status; + LARGE_INTEGER FileOffset, NextOffset; + SIZE_T ReadLength, WrittenLength; + EVENTLOGEOF EofRec; + EVENTLOGRECORD RecBuf; + ULONG FreeSpace = 0; + ULONG UpperBound; + ULONG RecOffset, WriteOffset; + + ASSERT(LogFile); + + if (LogFile->ReadOnly) + return STATUS_ACCESS_DENIED; + + // ASSERT(sizeof(*Record) == sizeof(RecBuf)); + + if (!Record || BufSize < sizeof(*Record)) + return STATUS_INVALID_PARAMETER; + + Record->RecordNumber = LogFile->Header.CurrentRecordNumber; + + /* Compute the available log free space */ + if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) + FreeSpace = LogFile->Header.MaxSize - LogFile->Header.EndOffset + LogFile->Header.StartOffset - sizeof(EVENTLOGHEADER); + else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) + FreeSpace = LogFile->Header.StartOffset - LogFile->Header.EndOffset; + + LogFile->Header.Flags |= ELF_LOGFILE_HEADER_DIRTY; + + /* If the event log was empty, it will now contain one record */ + if (LogFile->Header.OldestRecordNumber == 0) + LogFile->Header.OldestRecordNumber = 1; + + /* By default we append the new record at the old EOF record offset */ + WriteOffset = LogFile->Header.EndOffset; + + /* + * Check whether the log is going to wrap (the events being overwritten). + */ + + if (LogFile->Header.StartOffset <= LogFile->Header.EndOffset) + UpperBound = LogFile->Header.MaxSize; + else // if (LogFile->Header.StartOffset > LogFile->Header.EndOffset) + UpperBound = LogFile->Header.StartOffset; + + // if (LogFile->Header.MaxSize - WriteOffset < BufSize + sizeof(EofRec)) + if (UpperBound - WriteOffset < BufSize + sizeof(EofRec)) + { + EVTLTRACE("The event log file has reached maximum size (0x%x), wrapping...\n" + "UpperBound = 0x%x, WriteOffset = 0x%x, BufSize = 0x%x\n", + LogFile->Header.MaxSize, UpperBound, WriteOffset, BufSize); + /* This will be done later */ + } + + if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) && + (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf)) + { + // ASSERT(UpperBound == LogFile->Header.MaxSize); + // ASSERT(WriteOffset == LogFile->Header.EndOffset); + + /* + * We cannot fit the EVENTLOGRECORD header of the buffer before + * the end of the file. We need to pad the end of the log with + * 0x00000027, normally we will need to pad at most 0x37 bytes + * (corresponding to sizeof(EVENTLOGRECORD) - 1). + */ + + /* Rewind to the beginning of the log, just after the header */ + WriteOffset = sizeof(EVENTLOGHEADER); + /**/UpperBound = LogFile->Header.StartOffset;/**/ + + FreeSpace = LogFile->Header.StartOffset - WriteOffset; + + LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; + } + /* + * Otherwise, we can fit the header and only part + * of the data will overwrite the oldest records. + * + * It might be possible that all the event record can fit in one piece, + * but that the EOF record needs to be split. This is not a problem, + * EVENTLOGEOF can be splitted while EVENTLOGRECORD cannot be. + */ + + if (UpperBound - WriteOffset < BufSize + sizeof(EofRec)) + { + ULONG OrgOldestRecordNumber, OldestRecordNumber; + + // DPRINT("EventLogFile has reached maximum size, wrapping...\n"); + + OldestRecordNumber = OrgOldestRecordNumber = LogFile->Header.OldestRecordNumber; + + // FIXME: Assert whether LogFile->Header.StartOffset is the beginning of a record??? + // NOTE: It should be, by construction (and this should have been checked when + // initializing a new, or existing log). + + /* + * Determine how many old records need to be overwritten. + * Check the size of the record as the record added may be larger. + * Need to take into account that we append the EOF record. + */ + while (FreeSpace < BufSize + sizeof(EofRec)) + { + /* Get the oldest record data */ + RecOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber); + if (RecOffset == 0) + { + EVTLTRACE1("Record number %d cannot be found, or log file is full and cannot wrap!\n", OldestRecordNumber); + LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN; + return STATUS_LOG_FILE_FULL; + } + + RtlZeroMemory(&RecBuf, sizeof(RecBuf)); + + FileOffset.QuadPart = RecOffset; + Status = LogFile->FileRead(LogFile, + &FileOffset, + &RecBuf, + sizeof(RecBuf), + &ReadLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileRead() failed (Status 0x%08lx)\n", Status); + // Status = STATUS_EVENTLOG_FILE_CORRUPT; + return Status; + } + + if (RecBuf.Reserved != LOGFILE_SIGNATURE) + { + EVTLTRACE1("The event log file is corrupted!\n"); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* + * Check whether this event can be overwritten by comparing its + * written timestamp with the log's retention value. This value + * is the time interval, in seconds, that events records are + * protected from being overwritten. + * + * If the retention value is zero the events are always overwritten. + * + * If the retention value is non-zero, when the age of an event, + * in seconds, reaches or exceeds this value, it can be overwritten. + * Also if the events are in the future, we do not overwrite them. + */ + if (LogFile->Header.Retention != 0 && + (Record->TimeWritten < RecBuf.TimeWritten || + (Record->TimeWritten >= RecBuf.TimeWritten && + Record->TimeWritten - RecBuf.TimeWritten < LogFile->Header.Retention))) + { + EVTLTRACE1("The event log file is full and cannot wrap because of the retention policy.\n"); + LogFile->Header.Flags |= ELF_LOGFILE_LOGFULL_WRITTEN; + return STATUS_LOG_FILE_FULL; + } + + /* + * Advance the oldest record number, add the event record length + * (as long as it is valid...) then take account for the possible + * paddind after the record, in case this is the last one at the + * end of the file. + */ + OldestRecordNumber++; + RecOffset += RecBuf.Length; + FreeSpace += RecBuf.Length; + + /* + * If this was the last event record before the end of the log file, + * the next one should start at the beginning of the log and the space + * between the last event record and the end of the file is padded. + */ + if (LogFile->Header.MaxSize - RecOffset < sizeof(EVENTLOGRECORD)) + { + /* Add the padding size */ + FreeSpace += LogFile->Header.MaxSize - RecOffset; + } + } + + EVTLTRACE("Record will fit. FreeSpace %d, BufSize %d\n", FreeSpace, BufSize); + + /* The log records are wrapping */ + LogFile->Header.Flags |= ELF_LOGFILE_HEADER_WRAP; + + + // FIXME: May lead to corruption if the other subsequent calls fail... + + /* + * We have validated all the region of events to be discarded, + * now we can perform their deletion. + */ + ElfpDeleteOffsetInformation(LogFile, OrgOldestRecordNumber, OldestRecordNumber - 1); + LogFile->Header.OldestRecordNumber = OldestRecordNumber; + LogFile->Header.StartOffset = ElfpOffsetByNumber(LogFile, OldestRecordNumber); + if (LogFile->Header.StartOffset == 0) + { + /* + * We have deleted all the existing event records to make place + * for the new one. We can put it at the start of the event log. + */ + LogFile->Header.StartOffset = sizeof(EVENTLOGHEADER); + WriteOffset = LogFile->Header.StartOffset; + LogFile->Header.EndOffset = WriteOffset; + } + + DPRINT1("MaxSize = 0x%x, StartOffset = 0x%x, WriteOffset = 0x%x, EndOffset = 0x%x, BufSize = 0x%x\n" + "OldestRecordNumber = %d\n", + LogFile->Header.MaxSize, LogFile->Header.StartOffset, WriteOffset, LogFile->Header.EndOffset, BufSize, + OldestRecordNumber); + } + + /* + * Expand the log file if needed. + * NOTE: It may be needed to perform this task a bit sooner if we need + * such a thing for performing read operations, in the future... + * Or if this operation needs to modify 'FreeSpace'... + */ + if (LogFile->CurrentSize < LogFile->Header.MaxSize) + { + EVTLTRACE1("Expanding the log file from %lu to %lu\n", + LogFile->CurrentSize, LogFile->Header.MaxSize); + + LogFile->CurrentSize = LogFile->Header.MaxSize; + LogFile->FileSetSize(LogFile, LogFile->CurrentSize, 0); + } + + /* Since we can write events in the log, clear the log full flag */ + LogFile->Header.Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN; + + /* Pad the end of the log */ + // if (LogFile->Header.EndOffset + sizeof(RecBuf) > LogFile->Header.MaxSize) + if (WriteOffset < LogFile->Header.EndOffset) + { + /* Pad all the space from LogFile->Header.EndOffset to LogFile->Header.MaxSize */ + WrittenLength = ROUND_DOWN(LogFile->Header.MaxSize - LogFile->Header.EndOffset, sizeof(ULONG)); + RtlFillMemoryUlong(&RecBuf, WrittenLength, 0x00000027); + + FileOffset.QuadPart = LogFile->Header.EndOffset; + Status = LogFile->FileWrite(LogFile, + &FileOffset, + &RecBuf, + WrittenLength, + &WrittenLength); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("FileWrite() failed (Status 0x%08lx)\n", Status); + // return Status; + } + } + + /* Write the event record buffer with possible wrap at offset sizeof(EVENTLOGHEADER) */ + FileOffset.QuadPart = WriteOffset; + Status = WriteLogBuffer(LogFile, + Record, + BufSize, + &WrittenLength, + &FileOffset, + &NextOffset); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); + return Status; + } + /* FileOffset now contains the offset just after the end of the record buffer */ + FileOffset = NextOffset; + + if (!ElfpAddOffsetInformation(LogFile, + Record->RecordNumber, + WriteOffset)) + { + return STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT; + } + + LogFile->Header.CurrentRecordNumber++; + if (LogFile->Header.CurrentRecordNumber == 0) + LogFile->Header.CurrentRecordNumber = 1; + + /* + * Write the new EOF record offset just after the event record. + * The EOF record can wrap (be splitted) if less than sizeof(EVENTLOGEOF) + * bytes remains between the end of the record and the end of the log file. + */ + LogFile->Header.EndOffset = FileOffset.QuadPart; + + RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); + EofRec.BeginRecord = LogFile->Header.StartOffset; + EofRec.EndRecord = LogFile->Header.EndOffset; + EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; + EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; + + // FileOffset.QuadPart = LogFile->Header.EndOffset; + Status = WriteLogBuffer(LogFile, + &EofRec, + sizeof(EofRec), + &WrittenLength, + &FileOffset, + &NextOffset); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); + return Status; + } + FileOffset = NextOffset; + + /* Flush the log file */ + Status = ElfFlushFile(LogFile); + if (!NT_SUCCESS(Status)) + { + EVTLTRACE1("ElfFlushFile() failed (Status 0x%08lx)\n", Status); + return STATUS_EVENTLOG_FILE_CORRUPT; // Status; + } + + return Status; +} + +ULONG +NTAPI +ElfGetOldestRecord( + IN PEVTLOGFILE LogFile) +{ + ASSERT(LogFile); + return LogFile->Header.OldestRecordNumber; +} + +ULONG +NTAPI +ElfGetCurrentRecord( + IN PEVTLOGFILE LogFile) +{ + ASSERT(LogFile); + return LogFile->Header.CurrentRecordNumber; +} + +ULONG +NTAPI +ElfGetFlags( + IN PEVTLOGFILE LogFile) +{ + ASSERT(LogFile); + return LogFile->Header.Flags; +} + +#if DBG +VOID PRINT_HEADER(PEVENTLOGHEADER Header) +{ + ULONG Flags = Header->Flags; + + EVTLTRACE1("PRINT_HEADER(0x%p)\n", Header); + + DbgPrint("HeaderSize = %lu\n" , Header->HeaderSize); + DbgPrint("Signature = 0x%x\n", Header->Signature); + DbgPrint("MajorVersion = %lu\n" , Header->MajorVersion); + DbgPrint("MinorVersion = %lu\n" , Header->MinorVersion); + DbgPrint("StartOffset = 0x%x\n", Header->StartOffset); + DbgPrint("EndOffset = 0x%x\n", Header->EndOffset); + DbgPrint("CurrentRecordNumber = %lu\n", Header->CurrentRecordNumber); + DbgPrint("OldestRecordNumber = %lu\n", Header->OldestRecordNumber); + DbgPrint("MaxSize = 0x%x\n", Header->MaxSize); + DbgPrint("Retention = 0x%x\n", Header->Retention); + DbgPrint("EndHeaderSize = %lu\n" , Header->EndHeaderSize); + DbgPrint("Flags: "); + if (Flags & ELF_LOGFILE_HEADER_DIRTY) + { + DbgPrint("ELF_LOGFILE_HEADER_DIRTY"); + Flags &= ~ELF_LOGFILE_HEADER_DIRTY; + } + if (Flags) DbgPrint(" | "); + if (Flags & ELF_LOGFILE_HEADER_WRAP) + { + DbgPrint("ELF_LOGFILE_HEADER_WRAP"); + Flags &= ~ELF_LOGFILE_HEADER_WRAP; + } + if (Flags) DbgPrint(" | "); + if (Flags & ELF_LOGFILE_LOGFULL_WRITTEN) + { + DbgPrint("ELF_LOGFILE_LOGFULL_WRITTEN"); + Flags &= ~ELF_LOGFILE_LOGFULL_WRITTEN; + } + if (Flags) DbgPrint(" | "); + if (Flags & ELF_LOGFILE_ARCHIVE_SET) + { + DbgPrint("ELF_LOGFILE_ARCHIVE_SET"); + Flags &= ~ELF_LOGFILE_ARCHIVE_SET; + } + if (Flags) DbgPrint(" | 0x%x", Flags); + DbgPrint("\n"); +} +#endif diff --git a/reactos/sdk/lib/evtlib/evtlib.h b/reactos/sdk/lib/evtlib/evtlib.h new file mode 100644 index 00000000000..ce13fd0f7b2 --- /dev/null +++ b/reactos/sdk/lib/evtlib/evtlib.h @@ -0,0 +1,291 @@ +/* + * PROJECT: ReactOS EventLog File Library + * LICENSE: GPL - See COPYING in the top level directory + * FILE: sdk/lib/evtlib/evtlib.h + * PURPOSE: Provides a library for reading and writing EventLog files + * in the NT <= 5.2 (.evt) format. + * PROGRAMMERS: Copyright 2005 Saveliy Tretiakov + * Michael Martin + * Hermes Belusca-Maito + */ + +#ifndef __EVTLIB_H__ +#define __EVTLIB_H__ + +/* PSDK/NDK Headers */ +// #define WIN32_NO_STATUS +// #include +// #include +// #include + +#define NTOS_MODE_USER +#include + +#ifndef ROUND_DOWN +#define ROUND_DOWN(n, align) (((ULONG)n) & ~((align) - 1l)) +#endif + +#ifndef ROUND_UP +#define ROUND_UP(n, align) ROUND_DOWN(((ULONG)n) + (align) - 1, (align)) +#endif + +/* + * Our file format will be compatible with NT's + */ +#define MAJORVER 1 +#define MINORVER 1 +#define LOGFILE_SIGNATURE 0x654c664c // "LfLe" + +/* + * Flags used in the logfile header + */ +#define ELF_LOGFILE_HEADER_DIRTY 1 +#define ELF_LOGFILE_HEADER_WRAP 2 +#define ELF_LOGFILE_LOGFULL_WRITTEN 4 +#define ELF_LOGFILE_ARCHIVE_SET 8 + +/* + * On-disk event log structures (log file header, event record and EOF record). + * NOTE: Contrary to what MSDN claims, both the EVENTLOGHEADER and EVENTLOGEOF + * structures are absent from winnt.h . + */ + +#include // pshpack1 + +// ELF_LOGFILE_HEADER +typedef struct _EVENTLOGHEADER +{ + ULONG HeaderSize; + ULONG Signature; + ULONG MajorVersion; + ULONG MinorVersion; + ULONG StartOffset; + ULONG EndOffset; + ULONG CurrentRecordNumber; + ULONG OldestRecordNumber; + ULONG MaxSize; + ULONG Flags; + ULONG Retention; + ULONG EndHeaderSize; +} EVENTLOGHEADER, *PEVENTLOGHEADER; + + +/* Those flags and structure are defined in winnt.h */ +#ifndef _WINNT_ + +/* EventType flags */ +#define EVENTLOG_SUCCESS 0 +#define EVENTLOG_ERROR_TYPE 1 +#define EVENTLOG_WARNING_TYPE 2 +#define EVENTLOG_INFORMATION_TYPE 4 +#define EVENTLOG_AUDIT_SUCCESS 8 +#define EVENTLOG_AUDIT_FAILURE 16 + +typedef struct _EVENTLOGRECORD +{ + ULONG Length; /* Length of full record, including the data portion */ + ULONG Reserved; + ULONG RecordNumber; + ULONG TimeGenerated; + ULONG TimeWritten; + ULONG EventID; + USHORT EventType; + USHORT NumStrings; /* Number of strings in the 'Strings' array */ + USHORT EventCategory; + USHORT ReservedFlags; + ULONG ClosingRecordNumber; + ULONG StringOffset; + ULONG UserSidLength; + ULONG UserSidOffset; + ULONG DataLength; /* Length of the data portion */ + ULONG DataOffset; /* Offset from beginning of record */ +/* + * Length-varying data: + * + * WCHAR SourceName[]; + * WCHAR ComputerName[]; + * SID UserSid; // Must be aligned on a DWORD boundary + * WCHAR Strings[]; + * BYTE Data[]; + * CHAR Pad[]; // Padding for DWORD boundary + * ULONG Length; // Same as the first 'Length' member at the beginning + */ +} EVENTLOGRECORD, *PEVENTLOGRECORD; + +#endif // _WINNT_ + + +// ELF_EOF_RECORD +typedef struct _EVENTLOGEOF +{ + ULONG RecordSizeBeginning; + ULONG Ones; + ULONG Twos; + ULONG Threes; + ULONG Fours; + ULONG BeginRecord; + ULONG EndRecord; + ULONG CurrentRecordNumber; + ULONG OldestRecordNumber; + ULONG RecordSizeEnd; +} EVENTLOGEOF, *PEVENTLOGEOF; + +#define EVENTLOGEOF_SIZE_FIXED (5 * sizeof(ULONG)) +C_ASSERT(EVENTLOGEOF_SIZE_FIXED == FIELD_OFFSET(EVENTLOGEOF, BeginRecord)); + +#include + + +typedef struct _EVENT_OFFSET_INFO +{ + ULONG EventNumber; + ULONG EventOffset; +} EVENT_OFFSET_INFO, *PEVENT_OFFSET_INFO; + +#define TAG_ELF ' flE' +#define TAG_ELF_BUF 'BflE' + +struct _EVTLOGFILE; + +typedef PVOID +(NTAPI *PELF_ALLOCATE_ROUTINE)( + IN SIZE_T Size, + IN ULONG Flags, + IN ULONG Tag +); + +typedef VOID +(NTAPI *PELF_FREE_ROUTINE)( + IN PVOID Ptr, + IN ULONG Flags +); + +typedef NTSTATUS +(NTAPI *PELF_FILE_READ_ROUTINE)( + IN struct _EVTLOGFILE* LogFile, + IN PLARGE_INTEGER FileOffset, + OUT PVOID Buffer, + IN SIZE_T Length, + OUT PSIZE_T ReadLength OPTIONAL +); + +typedef NTSTATUS +(NTAPI *PELF_FILE_WRITE_ROUTINE)( + IN struct _EVTLOGFILE* LogFile, + IN PLARGE_INTEGER FileOffset, + IN PVOID Buffer, + IN SIZE_T Length, + OUT PSIZE_T WrittenLength OPTIONAL +); + +typedef NTSTATUS +(NTAPI *PELF_FILE_SET_SIZE_ROUTINE)( + IN struct _EVTLOGFILE* LogFile, + IN ULONG FileSize, + IN ULONG OldFileSize +); + +typedef NTSTATUS +(NTAPI *PELF_FILE_FLUSH_ROUTINE)( + IN struct _EVTLOGFILE* LogFile, + IN PLARGE_INTEGER FileOffset, + IN ULONG Length +); + +typedef struct _EVTLOGFILE +{ + PELF_ALLOCATE_ROUTINE Allocate; + PELF_FREE_ROUTINE Free; + PELF_FILE_SET_SIZE_ROUTINE FileSetSize; + PELF_FILE_WRITE_ROUTINE FileWrite; + PELF_FILE_READ_ROUTINE FileRead; + PELF_FILE_FLUSH_ROUTINE FileFlush; + + EVENTLOGHEADER Header; + ULONG CurrentSize; /* Equivalent to the file size, is <= MaxSize and can be extended to MaxSize if needed */ + UNICODE_STRING FileName; + PEVENT_OFFSET_INFO OffsetInfo; + ULONG OffsetInfoSize; + ULONG OffsetInfoNext; + BOOLEAN ReadOnly; +} EVTLOGFILE, *PEVTLOGFILE; + + +NTSTATUS +NTAPI +ElfCreateFile( + IN PEVTLOGFILE LogFile, + IN PUNICODE_STRING FileName OPTIONAL, + IN ULONG FileSize, + IN ULONG MaxSize, + IN ULONG Retention, + IN BOOLEAN CreateNew, + IN BOOLEAN ReadOnly, + IN PELF_ALLOCATE_ROUTINE Allocate, + IN PELF_FREE_ROUTINE Free, + IN PELF_FILE_SET_SIZE_ROUTINE FileSetSize, + IN PELF_FILE_WRITE_ROUTINE FileWrite, + IN PELF_FILE_READ_ROUTINE FileRead, + IN PELF_FILE_FLUSH_ROUTINE FileFlush); // What about Seek ?? + +NTSTATUS +NTAPI +ElfReCreateFile( + IN PEVTLOGFILE LogFile); + +// NTSTATUS +// ElfClearFile(PEVTLOGFILE LogFile); + +NTSTATUS +NTAPI +ElfBackupFile( + IN PEVTLOGFILE LogFile, + IN PEVTLOGFILE BackupLogFile); + +NTSTATUS +NTAPI +ElfFlushFile( + IN PEVTLOGFILE LogFile); + +VOID +NTAPI +ElfCloseFile( // ElfFree + IN PEVTLOGFILE LogFile); + +NTSTATUS +NTAPI +ElfReadRecord( + IN PEVTLOGFILE LogFile, + IN ULONG RecordNumber, + OUT PEVENTLOGRECORD Record, + IN SIZE_T BufSize, // Length + OUT PSIZE_T BytesRead OPTIONAL, + OUT PSIZE_T BytesNeeded OPTIONAL); + +NTSTATUS +NTAPI +ElfWriteRecord( + IN PEVTLOGFILE LogFile, + IN PEVENTLOGRECORD Record, + IN SIZE_T BufSize); + +ULONG +NTAPI +ElfGetOldestRecord( + IN PEVTLOGFILE LogFile); + +ULONG +NTAPI +ElfGetCurrentRecord( + IN PEVTLOGFILE LogFile); + +ULONG +NTAPI +ElfGetFlags( + IN PEVTLOGFILE LogFile); + +#if DBG +VOID PRINT_HEADER(PEVENTLOGHEADER Header); +#endif + +#endif /* __EVTLIB_H__ */