diff --git a/reactos/base/services/eventlog/eventlog.c b/reactos/base/services/eventlog/eventlog.c index b0a6415c1b1..3399113c35b 100644 --- a/reactos/base/services/eventlog/eventlog.c +++ b/reactos/base/services/eventlog/eventlog.c @@ -5,6 +5,7 @@ * PURPOSE: Event logging service * COPYRIGHT: Copyright 2002 Eric Kohl * Copyright 2005 Saveliy Tretiakov + * Hermes Belusca-Maito */ /* INCLUDES *****************************************************************/ @@ -19,7 +20,7 @@ /* GLOBALS ******************************************************************/ -static VOID CALLBACK ServiceMain(DWORD, LPWSTR *); +static VOID CALLBACK ServiceMain(DWORD, LPWSTR*); static WCHAR ServiceName[] = L"EventLog"; static SERVICE_TABLE_ENTRYW ServiceTable[2] = { @@ -30,8 +31,7 @@ static SERVICE_TABLE_ENTRYW ServiceTable[2] = SERVICE_STATUS ServiceStatus; SERVICE_STATUS_HANDLE ServiceStatusHandle; -BOOL onLiveCD = FALSE; // On livecd events will go to debug output only -HANDLE MyHeap = NULL; +BOOL onLiveCD = FALSE; // On LiveCD events will go to debug output only PEVENTSOURCE EventLogSource = NULL; @@ -157,7 +157,8 @@ ReportProductInfoEvent(VOID) { OSVERSIONINFOW versionInfo; WCHAR szBuffer[512]; - DWORD dwLength; + PWSTR str; + size_t cchRemain; HKEY hKey; DWORD dwValueLength; DWORD dwType; @@ -171,21 +172,32 @@ ReportProductInfoEvent(VOID) return; ZeroMemory(szBuffer, sizeof(szBuffer)); + str = szBuffer; + cchRemain = ARRAYSIZE(szBuffer); - /* Write version into the buffer */ - dwLength = swprintf(szBuffer, - L"%lu.%lu", - versionInfo.dwMajorVersion, - versionInfo.dwMinorVersion) + 1; + /* Write the version number into the buffer */ + StringCchPrintfExW(str, cchRemain, + &str, &cchRemain, 0, + L"%lu.%lu", + versionInfo.dwMajorVersion, + versionInfo.dwMinorVersion); + str++; + cchRemain++; - /* Write build number into the buffer */ - dwLength += swprintf(&szBuffer[dwLength], - L"%lu", - versionInfo.dwBuildNumber) + 1; + /* Write the build number into the buffer */ + StringCchPrintfExW(str, cchRemain, + &str, &cchRemain, 0, + L"%lu", + versionInfo.dwBuildNumber); + str++; + cchRemain++; - /* Write service pack info into the buffer */ - wcscpy(&szBuffer[dwLength], versionInfo.szCSDVersion); - dwLength += wcslen(versionInfo.szCSDVersion) + 1; + /* Write the service pack info into the buffer */ + StringCchCopyExW(str, cchRemain, + versionInfo.szCSDVersion, + &str, &cchRemain, 0); + str++; + cchRemain++; /* Read 'CurrentType' from the registry and write it into the buffer */ lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, @@ -195,12 +207,12 @@ ReportProductInfoEvent(VOID) &hKey); if (lResult == ERROR_SUCCESS) { - dwValueLength = ARRAYSIZE(szBuffer) - dwLength; + dwValueLength = cchRemain; lResult = RegQueryValueEx(hKey, L"CurrentType", NULL, &dwType, - (LPBYTE)&szBuffer[dwLength], + (LPBYTE)str, &dwValueLength); RegCloseKey(hKey); @@ -219,7 +231,7 @@ ReportProductInfoEvent(VOID) static VOID CALLBACK ServiceMain(DWORD argc, - LPWSTR *argv) + LPWSTR* argv) { DWORD dwError; @@ -267,10 +279,10 @@ ServiceMain(DWORD argc, static PLOGFILE -LoadLogFile(HKEY hKey, WCHAR* LogName) +LoadLogFile(HKEY hKey, PWSTR LogName) { DWORD MaxValueLen, ValueLen, Type, ExpandedLen; - WCHAR *Buf = NULL, *Expanded = NULL; + PWSTR Buf = NULL, Expanded = NULL; LONG Result; PLOGFILE pLogf = NULL; UNICODE_STRING FileName; @@ -288,7 +300,7 @@ LoadLogFile(HKEY hKey, WCHAR* LogName) } MaxValueLen = ROUND_DOWN(MaxValueLen, sizeof(WCHAR)); - Buf = HeapAlloc(MyHeap, 0, MaxValueLen); + Buf = HeapAlloc(GetProcessHeap(), 0, MaxValueLen); if (!Buf) { DPRINT1("Cannot allocate heap!\n"); @@ -312,11 +324,11 @@ LoadLogFile(HKEY hKey, WCHAR* LogName) MaxValueLen = (wcslen(L"%SystemRoot%\\System32\\Config\\") + wcslen(LogName) + wcslen(L".evt") + 1) * sizeof(WCHAR); - Expanded = HeapReAlloc(MyHeap, 0, Buf, MaxValueLen); + Expanded = HeapReAlloc(GetProcessHeap(), 0, Buf, MaxValueLen); if (!Expanded) { DPRINT1("Cannot reallocate heap!\n"); - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Buf); return NULL; } Buf = Expanded; @@ -335,17 +347,17 @@ LoadLogFile(HKEY hKey, WCHAR* LogName) if (Result != ERROR_SUCCESS) { DPRINT1("RegSetValueEx failed: %lu\n", Result); - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Buf); return NULL; } } ExpandedLen = ExpandEnvironmentStringsW(Buf, NULL, 0); - Expanded = HeapAlloc(MyHeap, 0, ExpandedLen * sizeof(WCHAR)); + Expanded = HeapAlloc(GetProcessHeap(), 0, ExpandedLen * sizeof(WCHAR)); if (!Expanded) { DPRINT1("Cannot allocate heap!\n"); - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Buf); return NULL; } @@ -354,8 +366,8 @@ LoadLogFile(HKEY hKey, WCHAR* LogName) if (!RtlDosPathNameToNtPathName_U(Expanded, &FileName, NULL, NULL)) { DPRINT1("Cannot convert path!\n"); - HeapFree(MyHeap, 0, Expanded); - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Expanded); + HeapFree(GetProcessHeap(), 0, Buf); return NULL; } @@ -409,8 +421,8 @@ LoadLogFile(HKEY hKey, WCHAR* LogName) DPRINT1("Failed to create %S! (Status %08lx)\n", Expanded, Status); } - HeapFree(MyHeap, 0, Expanded); - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Expanded); + HeapFree(GetProcessHeap(), 0, Buf); return pLogf; } @@ -419,7 +431,7 @@ LoadLogFiles(HKEY eventlogKey) { LONG Result; DWORD MaxLognameLen, LognameLen; - WCHAR *Buf = NULL; + PWSTR Buf = NULL; DWORD dwIndex; PLOGFILE pLogFile; @@ -433,7 +445,7 @@ LoadLogFiles(HKEY eventlogKey) MaxLognameLen++; - Buf = HeapAlloc(MyHeap, 0, MaxLognameLen * sizeof(WCHAR)); + Buf = HeapAlloc(GetProcessHeap(), 0, MaxLognameLen * sizeof(WCHAR)); if (!Buf) { DPRINT1("Error: cannot allocate heap!\n"); @@ -456,7 +468,7 @@ LoadLogFiles(HKEY eventlogKey) if (Result != ERROR_SUCCESS) { DPRINT1("Failed to open %S key.\n", Buf); - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Buf); return FALSE; } @@ -477,7 +489,7 @@ LoadLogFiles(HKEY eventlogKey) dwIndex++; } - HeapFree(MyHeap, 0, Buf); + HeapFree(GetProcessHeap(), 0, Buf); return TRUE; } @@ -492,15 +504,6 @@ int wmain(int argc, WCHAR* argv[]) LogfListInitialize(); InitEventSourceList(); - MyHeap = HeapCreate(0, 1024 * 256, 0); - - if (!MyHeap) - { - DPRINT1("FATAL ERROR, can't create heap.\n"); - RetCode = 1; - goto bye_bye; - } - GetWindowsDirectoryW(LogPath, MAX_PATH); if (GetDriveTypeW(LogPath) == DRIVE_CDROM) @@ -537,9 +540,6 @@ int wmain(int argc, WCHAR* argv[]) bye_bye: LogfCloseAll(); - if (MyHeap) - HeapDestroy(MyHeap); - return RetCode; } @@ -589,7 +589,7 @@ VOID PRINT_HEADER(PEVENTLOGHEADER header) VOID PRINT_RECORD(PEVENTLOGRECORD pRec) { UINT i; - WCHAR *str; + PWSTR str; LARGE_INTEGER SystemTime; TIME_FIELDS Time; @@ -642,23 +642,22 @@ VOID PRINT_RECORD(PEVENTLOGRECORD pRec) DPRINT("DataLength = %lu\n", pRec->DataLength); DPRINT("DataOffset = %lu\n", pRec->DataOffset); - DPRINT("SourceName: %S\n", (WCHAR *) ((ULONG_PTR)pRec + sizeof(EVENTLOGRECORD))); + DPRINT("SourceName: %S\n", (PWSTR)((ULONG_PTR)pRec + sizeof(EVENTLOGRECORD))); - i = (lstrlenW((WCHAR *) ((ULONG_PTR)pRec + sizeof(EVENTLOGRECORD))) + 1) * - sizeof(WCHAR); + i = (wcslen((PWSTR)((ULONG_PTR)pRec + sizeof(EVENTLOGRECORD))) + 1) * sizeof(WCHAR); - DPRINT("ComputerName: %S\n", (WCHAR *) ((ULONG_PTR)pRec + sizeof(EVENTLOGRECORD) + i)); + DPRINT("ComputerName: %S\n", (PWSTR)((ULONG_PTR)pRec + sizeof(EVENTLOGRECORD) + i)); if (pRec->StringOffset < pRec->Length && pRec->NumStrings) { DPRINT("Strings:\n"); - str = (WCHAR *) ((ULONG_PTR)pRec + pRec->StringOffset); + str = (PWSTR)((ULONG_PTR)pRec + pRec->StringOffset); for (i = 0; i < pRec->NumStrings; i++) { DPRINT("[%u] %S\n", i, str); - str = str + lstrlenW(str) + 1; + str += wcslen(str) + 1; } } - DPRINT("Length2 = %lu\n", *(PDWORD) ((ULONG_PTR)pRec + pRec->Length - 4)); + DPRINT("Length2 = %lu\n", *(PULONG)((ULONG_PTR)pRec + pRec->Length - 4)); } diff --git a/reactos/base/services/eventlog/eventlog.h b/reactos/base/services/eventlog/eventlog.h index 732517ad61b..b39bcca06e8 100644 --- a/reactos/base/services/eventlog/eventlog.h +++ b/reactos/base/services/eventlog/eventlog.h @@ -79,6 +79,9 @@ typedef struct _EVENTLOGEOF 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; @@ -89,6 +92,7 @@ typedef struct _LOGFILE { HANDLE hFile; EVENTLOGHEADER Header; + ULONG CurrentSize; /* Equivalent to the file size, is <= MaxSize and can be extended to MaxSize if needed */ WCHAR *LogName; WCHAR *FileName; RTL_RESOURCE Lock; @@ -122,7 +126,6 @@ typedef struct _LOGHANDLE /* eventlog.c */ -extern HANDLE MyHeap; extern PEVENTSOURCE EventLogSource; VOID PRINT_HEADER(PEVENTLOGHEADER header); @@ -153,18 +156,20 @@ PLOGFILE LogfListItemByName(LPCWSTR Name); -DWORD LogfReadEvent(PLOGFILE LogFile, - DWORD Flags, - DWORD * RecordNumber, - DWORD BufSize, - PBYTE Buffer, - DWORD * BytesRead, - DWORD * BytesNeeded, - BOOL Ansi); +NTSTATUS +LogfReadEvents(PLOGFILE LogFile, + ULONG Flags, + PULONG RecordNumber, + ULONG BufSize, + PBYTE Buffer, + PULONG BytesRead, + PULONG BytesNeeded, + BOOLEAN Ansi); -BOOL LogfWriteData(PLOGFILE LogFile, - DWORD BufSize, - PBYTE Buffer); +NTSTATUS +LogfWriteRecord(PLOGFILE LogFile, + ULONG BufSize, // SIZE_T + PEVENTLOGRECORD Record); NTSTATUS LogfClearFile(PLOGFILE LogFile, @@ -175,27 +180,23 @@ LogfBackupFile(PLOGFILE LogFile, PUNICODE_STRING BackupFileName); NTSTATUS -LogfCreate(PLOGFILE *Logfile, - WCHAR * LogName, +LogfCreate(PLOGFILE* LogFile, + PCWSTR LogName, PUNICODE_STRING FileName, - ULONG ulMaxSize, - ULONG ulRetention, - BOOL Permanent, - BOOL Backup); + ULONG ulMaxSize, + ULONG ulRetention, + BOOLEAN Permanent, + BOOLEAN Backup); VOID LogfClose(PLOGFILE LogFile, - BOOL ForceClose); + BOOLEAN ForceClose); VOID LogfCloseAll(VOID); -DWORD LogfGetOldestRecord(PLOGFILE LogFile); - -DWORD LogfGetCurrentRecord(PLOGFILE LogFile); - -PBYTE +PEVENTLOGRECORD LogfAllocAndBuildNewRecord(PULONG lpRecSize, - ULONG dwRecordNumber, // FIXME! + ULONG Time, USHORT wType, USHORT wCategory, ULONG dwEventId, @@ -204,13 +205,13 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, ULONG dwSidLength, PSID lpUserSid, USHORT wNumStrings, - WCHAR* lpStrings, + PWSTR lpStrings, ULONG dwDataSize, PVOID lpRawData); -static __inline void LogfFreeRecord(LPVOID Rec) +static __inline void LogfFreeRecord(PEVENTLOGRECORD Record) { - HeapFree(MyHeap, 0, Rec); + HeapFree(GetProcessHeap(), 0, Record); } VOID @@ -218,7 +219,7 @@ LogfReportEvent(USHORT wType, USHORT wCategory, ULONG dwEventId, USHORT wNumStrings, - WCHAR* lpStrings, + PWSTR lpStrings, ULONG dwDataSize, PVOID lpRawData); diff --git a/reactos/base/services/eventlog/eventsource.c b/reactos/base/services/eventlog/eventsource.c index 83183414fd9..d2654a28e59 100644 --- a/reactos/base/services/eventlog/eventsource.c +++ b/reactos/base/services/eventlog/eventsource.c @@ -53,38 +53,86 @@ DumpEventSourceList(VOID) } +static BOOL +AddNewEventSource(PLOGFILE pLogFile, + PWSTR lpSourceName) +{ + PEVENTSOURCE lpEventSource; + + lpEventSource = HeapAlloc(GetProcessHeap(), 0, + FIELD_OFFSET(EVENTSOURCE, szName[wcslen(lpSourceName) + 1])); + if (lpEventSource != NULL) + { + wcscpy(lpEventSource->szName, lpSourceName); + lpEventSource->LogFile = pLogFile; + + DPRINT("Insert event source: %S\n", lpEventSource->szName); + + EnterCriticalSection(&EventSourceListCs); + InsertTailList(&EventSourceListHead, + &lpEventSource->EventSourceListEntry); + LeaveCriticalSection(&EventSourceListCs); + } + + return (lpEventSource != NULL); +} + + BOOL LoadEventSources(HKEY hKey, PLOGFILE pLogFile) { - PEVENTSOURCE lpEventSource; - DWORD dwMaxSubKeyLength; - DWORD dwEventSourceNameLength; + BOOL Success; + DWORD dwNumSubKeys, dwMaxSubKeyLength; + DWORD dwEventSourceNameLength, MaxValueLen; DWORD dwIndex; - WCHAR *Buf = NULL; + PWSTR Buf = NULL, SourceList = NULL, Source = NULL; + size_t cchRemaining = 0; LONG Result; DPRINT("LoadEventSources\n"); - Result = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, &dwMaxSubKeyLength, NULL, - NULL, NULL, NULL, NULL, NULL); + Result = RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwNumSubKeys, &dwMaxSubKeyLength, + NULL, NULL, NULL, NULL, NULL, NULL); if (Result != ERROR_SUCCESS) { DPRINT1("RegQueryInfoKeyW failed: %lu\n", Result); return FALSE; } - DPRINT("dwMaxSubKeyLength: %lu\n", dwMaxSubKeyLength); - dwMaxSubKeyLength++; - Buf = HeapAlloc(MyHeap, 0, dwMaxSubKeyLength * sizeof(WCHAR)); + Buf = HeapAlloc(GetProcessHeap(), 0, dwMaxSubKeyLength * sizeof(WCHAR)); if (!Buf) { DPRINT1("Error: cannot allocate heap!\n"); return FALSE; } + /* + * Allocate a buffer for storing the names of the sources as a REG_MULTI_SZ + * in the registry. Also add the event log as its own source. + * Add a final NULL-terminator. + */ + MaxValueLen = dwNumSubKeys * dwMaxSubKeyLength + wcslen(pLogFile->LogName) + 2; + SourceList = HeapAlloc(GetProcessHeap(), 0, MaxValueLen * sizeof(WCHAR)); + if (!SourceList) + { + DPRINT1("Error: cannot allocate heap!\n"); + /* It is not dramatic if we cannot create it */ + } + else + { + cchRemaining = MaxValueLen; + Source = SourceList; + } + + /* + * Enumerate all the subkeys of the event log key, that constitute + * all the possible event sources for this event log. At this point, + * skip the possible existing source having the same name as the + * event log, it will be added later on. + */ dwEventSourceNameLength = dwMaxSubKeyLength; dwIndex = 0; while (RegEnumKeyExW(hKey, @@ -93,28 +141,56 @@ LoadEventSources(HKEY hKey, &dwEventSourceNameLength, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { - DPRINT("Event Source: %S\n", Buf); - - lpEventSource = HeapAlloc(MyHeap, 0, sizeof(EVENTSOURCE) + wcslen(Buf) * sizeof(WCHAR)); - if (lpEventSource != NULL) + if (_wcsicmp(pLogFile->LogName, Buf) != 0) { - wcscpy(lpEventSource->szName, Buf); - lpEventSource->LogFile = pLogFile; - - DPRINT("Insert event source: %S\n", lpEventSource->szName); - - - EnterCriticalSection(&EventSourceListCs); - InsertTailList(&EventSourceListHead, - &lpEventSource->EventSourceListEntry); - LeaveCriticalSection(&EventSourceListCs); + DPRINT("Event Source: %S\n", Buf); + Success = AddNewEventSource(pLogFile, Buf); + if (Success && (Source != NULL)) + { + /* Append the event source name and an extra NULL-terminator */ + StringCchCopyExW(Source, cchRemaining, Buf, &Source, &cchRemaining, 0); + if (cchRemaining > 0) + { + *++Source = L'\0'; + cchRemaining--; + } + } } dwEventSourceNameLength = dwMaxSubKeyLength; dwIndex++; } - HeapFree(MyHeap, 0, Buf); + /* Finally, allow the event log itself to be its own source */ + DPRINT("Event Source: %S\n", pLogFile->LogName); + Success = AddNewEventSource(pLogFile, pLogFile->LogName); + if (Success && (Source != NULL)) + { + /* Append the event source name and an extra NULL-terminator */ + StringCchCopyExW(Source, cchRemaining, pLogFile->LogName, &Source, &cchRemaining, 0); + if (cchRemaining > 0) + { + *++Source = L'\0'; + cchRemaining--; + } + } + + /* Save the list of sources in the registry */ + Result = RegSetValueEx(hKey, + L"Sources", + 0, + REG_MULTI_SZ, + (LPBYTE)SourceList, + (MaxValueLen - cchRemaining + 1) * sizeof(WCHAR)); + if (Result != ERROR_SUCCESS) + { + DPRINT1("RegSetValueEx failed: %lu\n", Result); + } + + if (SourceList) + HeapFree(GetProcessHeap(), 0, SourceList); + + HeapFree(GetProcessHeap(), 0, Buf); DumpEventSourceList(); diff --git a/reactos/base/services/eventlog/file.c b/reactos/base/services/eventlog/file.c index f65e66e3b00..e4de3156375 100644 --- a/reactos/base/services/eventlog/file.c +++ b/reactos/base/services/eventlog/file.c @@ -5,6 +5,7 @@ * PURPOSE: Event logging service * COPYRIGHT: Copyright 2005 Saveliy Tretiakov * Michael Martin + * Hermes Belusca-Maito */ /* INCLUDES ******************************************************************/ @@ -49,16 +50,16 @@ PLOGFILE LogfListItemByName(LPCWSTR Name) PLIST_ENTRY CurrentEntry; PLOGFILE Item, Result = NULL; + ASSERT(Name); + EnterCriticalSection(&LogFileListCs); CurrentEntry = LogFileListHead.Flink; while (CurrentEntry != &LogFileListHead) { - Item = CONTAINING_RECORD(CurrentEntry, - LOGFILE, - ListEntry); + Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry); - if (Item->LogName && !lstrcmpi(Item->LogName, Name)) + if (Item->LogName && !_wcsicmp(Item->LogName, Name)) { Result = Item; break; @@ -73,22 +74,22 @@ PLOGFILE LogfListItemByName(LPCWSTR Name) #if 0 /* Index starting from 1 */ -DWORD LogfListItemIndexByName(WCHAR * Name) +DWORD LogfListItemIndexByName(LPCWSTR Name) { PLIST_ENTRY CurrentEntry; DWORD Result = 0; DWORD i = 1; + ASSERT(Name); + EnterCriticalSection(&LogFileListCs); CurrentEntry = LogFileListHead.Flink; while (CurrentEntry != &LogFileListHead) { - PLOGFILE Item = CONTAINING_RECORD(CurrentEntry, - LOGFILE, - ListEntry); + PLOGFILE Item = CONTAINING_RECORD(CurrentEntry, LOGFILE, ListEntry); - if (Item->LogName && !lstrcmpi(Item->LogName, Name)) + if (Item->LogName && !_wcsicmp(Item->LogName, Name)) { Result = i; break; @@ -164,8 +165,172 @@ LogfListRemoveItem(PLOGFILE Item) } +/* GLOBALS *******************************************************************/ + +static const EVENTLOGEOF EOFRecord = +{ + sizeof(EOFRecord), + 0x11111111, 0x22222222, 0x33333333, 0x44444444, + 0, 0, 0, 0, + sizeof(EOFRecord) +}; + /* FUNCTIONS *****************************************************************/ +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) +{ + 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; + + /* Read the first part of the buffer */ + FileOffset = *ByteOffset; + BufSize = min(Length, LogFile->CurrentSize - FileOffset.QuadPart); + + 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; + } + + 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; + + 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) +{ + 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; + + /* Write the first part of the buffer */ + FileOffset = *ByteOffset; + BufSize = min(Length, LogFile->CurrentSize /* LogFile->Header.MaxSize */ - FileOffset.QuadPart); + + 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; + } + + 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; + + return Status; +} + + /* Returns 0 if nothing is found */ static ULONG LogfOffsetByNumber(PLOGFILE LogFile, @@ -181,26 +346,16 @@ LogfOffsetByNumber(PLOGFILE LogFile, return 0; } -DWORD LogfGetOldestRecord(PLOGFILE LogFile) -{ - return LogFile->Header.OldestRecordNumber; -} - -DWORD LogfGetCurrentRecord(PLOGFILE LogFile) -{ - return LogFile->Header.CurrentRecordNumber; -} - static BOOL LogfAddOffsetInformation(PLOGFILE LogFile, ULONG ulNumber, ULONG ulOffset) { - LPVOID NewOffsetInfo; + PVOID NewOffsetInfo; if (LogFile->OffsetInfoNext == LogFile->OffsetInfoSize) { - NewOffsetInfo = HeapReAlloc(MyHeap, + NewOffsetInfo = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, LogFile->OffsetInfo, (LogFile->OffsetInfoSize + 64) * @@ -225,29 +380,40 @@ LogfAddOffsetInformation(PLOGFILE LogFile, static BOOL LogfDeleteOffsetInformation(PLOGFILE LogFile, - ULONG ulNumber) + ULONG ulNumberMin, + ULONG ulNumberMax) { DWORD i; - /* - * As the offset information is listed in increasing order, and we want - * to keep the list without holes, we demand that ulNumber is the first - * element in the list. - */ - if (ulNumber != LogFile->OffsetInfo[0].EventNumber) + if (ulNumberMin > ulNumberMax) return FALSE; - /* - * RtlMoveMemory(&LogFile->OffsetInfo[0], - * &LogFile->OffsetInfo[1], - * sizeof(EVENT_OFFSET_INFO) * (LogFile->OffsetInfoNext - 1)); - */ - for (i = 0; i < LogFile->OffsetInfoNext - 1; i++) + /* Remove records ulNumberMin to ulNumberMax inclusive */ + while (ulNumberMin <= ulNumberMax) { - LogFile->OffsetInfo[i].EventNumber = LogFile->OffsetInfo[i + 1].EventNumber; - LogFile->OffsetInfo[i].EventOffset = LogFile->OffsetInfo[i + 1].EventOffset; + /* + * 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++; } - LogFile->OffsetInfoNext--; return TRUE; } @@ -257,27 +423,44 @@ LogfInitializeNew(PLOGFILE LogFile, ULONG ulMaxSize, ULONG ulRetention) { - IO_STATUS_BLOCK IoStatusBlock; - EVENTLOGEOF EofRec; NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER FileOffset; + EVENTLOGEOF EofRec; - ZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER)); - SetFilePointer(LogFile->hFile, 0, NULL, FILE_BEGIN); - SetEndOfFile(LogFile->hFile); + /* Initialize the event log header */ + RtlZeroMemory(&LogFile->Header, sizeof(EVENTLOGHEADER)); LogFile->Header.HeaderSize = sizeof(EVENTLOGHEADER); - LogFile->Header.Signature = LOGFILE_SIGNATURE; + 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; - LogFile->Header.OldestRecordNumber = 1; - LogFile->Header.MaxSize = ulMaxSize; + /* 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, @@ -285,7 +468,7 @@ LogfInitializeNew(PLOGFILE LogFile, &IoStatusBlock, &LogFile->Header, sizeof(EVENTLOGHEADER), - NULL, + &FileOffset, NULL); if (!NT_SUCCESS(Status)) { @@ -293,16 +476,12 @@ LogfInitializeNew(PLOGFILE LogFile, return Status; } - EofRec.RecordSizeBeginning = sizeof(EVENTLOGEOF); - EofRec.Ones = 0x11111111; - EofRec.Twos = 0x22222222; - EofRec.Threes = 0x33333333; - EofRec.Fours = 0x44444444; + /* Initialize the ELF_EOF_RECORD and write it */ + RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); EofRec.BeginRecord = LogFile->Header.StartOffset; - EofRec.EndRecord = LogFile->Header.EndOffset; + EofRec.EndRecord = LogFile->Header.EndOffset; EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - EofRec.RecordSizeEnd = sizeof(EVENTLOGEOF); + EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; Status = NtWriteFile(LogFile->hFile, NULL, @@ -310,7 +489,7 @@ LogfInitializeNew(PLOGFILE LogFile, NULL, &IoStatusBlock, &EofRec, - sizeof(EVENTLOGEOF), + sizeof(EofRec), NULL, NULL); if (!NT_SUCCESS(Status)) @@ -319,8 +498,7 @@ LogfInitializeNew(PLOGFILE LogFile, return Status; } - Status = NtFlushBuffersFile(LogFile->hFile, - &IoStatusBlock); + Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); if (!NT_SUCCESS(Status)) { DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status); @@ -330,43 +508,49 @@ LogfInitializeNew(PLOGFILE LogFile, return STATUS_SUCCESS; } - static NTSTATUS LogfInitializeExisting(PLOGFILE LogFile, - BOOL Backup) + BOOLEAN Backup) { - DWORD dwRead; + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER FileOffset, NextOffset; + LARGE_INTEGER LogFileSize; DWORD dwRecordsNumber = 0; - DWORD dwRecSize, dwRecSign, dwFilePointer; + ULONG RecOffset; PDWORD pdwRecSize2; - PEVENTLOGRECORD RecBuf; - BOOL OvewrWrittenRecords = FALSE; + EVENTLOGEOF EofRec; + EVENTLOGRECORD RecBuf; + PEVENTLOGRECORD pRecBuf; + BOOLEAN Wrapping = FALSE; + BOOLEAN IsLogDirty = FALSE; - DPRINT("Initializing LogFile %S\n",LogFile->LogName); + DPRINT("Initializing LogFile %S\n", LogFile->LogName); - if (SetFilePointer(LogFile->hFile, 0, NULL, FILE_BEGIN) == - INVALID_SET_FILE_POINTER) + /* Read the log header */ + FileOffset.QuadPart = 0LL; + Status = NtReadFile(LogFile->hFile, + NULL, + NULL, + NULL, + &IoStatusBlock, + &LogFile->Header, + sizeof(EVENTLOGHEADER), + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) { - DPRINT1("SetFilePointer failed! %d\n", GetLastError()); + DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); return STATUS_EVENTLOG_FILE_CORRUPT; } - - if (!ReadFile(LogFile->hFile, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &dwRead, - NULL)) - { - DPRINT1("ReadFile failed! %d\n", GetLastError()); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - if (dwRead != sizeof(EVENTLOGHEADER)) + if (IoStatusBlock.Information != sizeof(EVENTLOGHEADER)) { DPRINT("EventLog: Invalid file %S.\n", LogFile->FileName); return STATUS_EVENTLOG_FILE_CORRUPT; } + /* Header validity checks */ + if (LogFile->Header.HeaderSize != sizeof(EVENTLOGHEADER) || LogFile->Header.EndHeaderSize != sizeof(EVENTLOGHEADER)) { @@ -381,158 +565,459 @@ LogfInitializeExisting(PLOGFILE LogFile, return STATUS_EVENTLOG_FILE_CORRUPT; } - if (LogFile->Header.EndOffset > GetFileSize(LogFile->hFile, NULL) + 1) + IsLogDirty = (LogFile->Header.Flags & ELF_LOGFILE_HEADER_DIRTY); + + /* If the log is a backup log that is dirty, then it is corrupted */ + if (Backup && IsLogDirty) { - DPRINT("EventLog: Invalid eof offset %x in %S.\n", + DPRINT("EventLog: Backup log %S is dirty.\n", LogFile->FileName); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* + * 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 (LogFileSize.HighPart != 0) + { + DPRINT1("EventLog: Log %S is too large.\n", LogFile->FileName); + // return STATUS_FILE_TOO_LARGE; + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + LogFile->CurrentSize = LogFileSize.LowPart; // LogFileSize.QuadPart; + + /* Adjust the log maximum size if needed */ + if (LogFile->CurrentSize > LogFile->Header.MaxSize) + LogFile->Header.MaxSize = LogFile->CurrentSize; + + /* + * 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); + } + + FileOffset.QuadPart = RecOffset; + Wrapping = FALSE; + + for (;;) + { + if (Wrapping && FileOffset.QuadPart >= RecOffset) + { + DPRINT1("EOF record not found!\n"); + return STATUS_EVENTLOG_FILE_CORRUPT; + } + + /* 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; + } + + /* Is it an EVENTLOGEOF record? */ + if (RtlCompareMemory(&EofRec, &EOFRecord, EVENTLOGEOF_SIZE_FIXED) == EVENTLOGEOF_SIZE_FIXED) + { + DPRINT("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, + &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)) + { + DPRINT1("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 %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; + } + + /* 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 (!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; } - /* Set the read location to the oldest record */ - dwFilePointer = SetFilePointer(LogFile->hFile, LogFile->Header.StartOffset, NULL, FILE_BEGIN); - if (dwFilePointer == INVALID_SET_FILE_POINTER) + if ((LogFile->Header.StartOffset != LogFile->Header.EndOffset) && + (LogFile->Header.MaxSize - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) { - DPRINT1("SetFilePointer failed! %d\n", GetLastError()); + /* + * 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; } - for (;;) + if ((LogFile->Header.StartOffset < LogFile->Header.EndOffset) && + (LogFile->Header.EndOffset - LogFile->Header.StartOffset < sizeof(EVENTLOGRECORD))) { - dwFilePointer = SetFilePointer(LogFile->hFile, 0, NULL, FILE_CURRENT); + /* + * 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 (dwFilePointer == INVALID_SET_FILE_POINTER) + 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)) { - DPRINT1("SetFilePointer failed! %d\n", GetLastError()); + DPRINT("EventLog: Invalid EOF offset %x in %S.\n", + LogFile->Header.EndOffset, LogFile->FileName); return STATUS_EVENTLOG_FILE_CORRUPT; } - - /* If the EVENTLOGEOF info has been reached and the oldest record was not immediately after the Header */ - if ((dwFilePointer == LogFile->Header.EndOffset) && (LogFile->Header.StartOffset != sizeof(EVENTLOGHEADER))) + } + 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)) { - OvewrWrittenRecords = TRUE; - /* The file has records that overwrote old ones so read them */ - dwFilePointer = SetFilePointer(LogFile->hFile, sizeof(EVENTLOGHEADER), NULL, FILE_BEGIN); - } - - if (!ReadFile(LogFile->hFile, - &dwRecSize, - sizeof(dwRecSize), - &dwRead, - NULL)) - { - DPRINT1("ReadFile failed! %d\n", GetLastError()); + DPRINT("EventLog: Invalid EOF offset %x in %S.\n", + LogFile->Header.EndOffset, LogFile->FileName); return STATUS_EVENTLOG_FILE_CORRUPT; } + } - if (dwRead != sizeof(dwRecSize)) - break; + /* Start enumerating the event records from the beginning */ + RecOffset = LogFile->Header.StartOffset; + FileOffset.QuadPart = RecOffset; + Wrapping = FALSE; - if (!ReadFile(LogFile->hFile, - &dwRecSign, - sizeof(dwRecSign), - &dwRead, - NULL)) + // // 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("ReadFile() failed! %d\n", GetLastError()); - return STATUS_EVENTLOG_FILE_CORRUPT; + DPRINT1("Cannot allocate temporary buffer, skip event reorganization.\n"); + goto Continue; } - if (dwRead != sizeof(dwRecSize)) - break; + // TODO: Do the job! + } - if (dwRecSign != LOGFILE_SIGNATURE || - dwRecSize + dwFilePointer > GetFileSize(LogFile->hFile, NULL) + 1 || - dwRecSize < sizeof(EVENTLOGRECORD)) +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; } - if (SetFilePointer(LogFile->hFile, - -((LONG) sizeof(DWORD) * 2), - NULL, - FILE_CURRENT) == INVALID_SET_FILE_POINTER) + /* 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("SetFilePointer() failed! %d", GetLastError()); + DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); return STATUS_EVENTLOG_FILE_CORRUPT; } - - RecBuf = (PEVENTLOGRECORD) HeapAlloc(MyHeap, 0, dwRecSize); - if (RecBuf == NULL) + if (IoStatusBlock.Information != sizeof(RecBuf)) { - DPRINT1("Can't allocate heap!\n"); + 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; } - if (!ReadFile(LogFile->hFile, RecBuf, dwRecSize, &dwRead, NULL)) + /* Attempt to read the full EVENTLOGRECORD (can wrap) */ + Status = ReadLogBuffer(LogFile, + &IoStatusBlock, + pRecBuf, + RecBuf.Length, + &FileOffset, + &NextOffset); + if (!NT_SUCCESS(Status)) { - DPRINT1("ReadFile() failed! %d\n", GetLastError()); - HeapFree(MyHeap, 0, RecBuf); + DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); return STATUS_EVENTLOG_FILE_CORRUPT; } - - if (dwRead != dwRecSize) + if (IoStatusBlock.Information != RecBuf.Length) { - HeapFree(MyHeap, 0, RecBuf); + DPRINT1("Oh oh!!\n"); + RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); break; } - /* if OvewrWrittenRecords is TRUE and this record has already been read */ - if ((OvewrWrittenRecords == TRUE) && (RecBuf->RecordNumber == LogFile->Header.OldestRecordNumber)) - { - HeapFree(MyHeap, 0, RecBuf); - 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) (((PBYTE) RecBuf) + dwRecSize - 4); + pdwRecSize2 = (PDWORD)((ULONG_PTR)pRecBuf + RecBuf.Length - 4); - if (*pdwRecSize2 != dwRecSize) + if (*pdwRecSize2 != RecBuf.Length) { DPRINT1("Invalid RecordSizeEnd of record %d (%x) in %S\n", dwRecordsNumber, *pdwRecSize2, LogFile->LogName); - HeapFree(MyHeap, 0, RecBuf); + RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); break; } + DPRINT("Add new record %d - %x\n", pRecBuf->RecordNumber, FileOffset.QuadPart); + dwRecordsNumber++; if (!LogfAddOffsetInformation(LogFile, - RecBuf->RecordNumber, - dwFilePointer)) + pRecBuf->RecordNumber, + FileOffset.QuadPart)) { DPRINT1("LogfAddOffsetInformation() failed!\n"); - HeapFree(MyHeap, 0, RecBuf); + RtlFreeHeap(GetProcessHeap(), 0, pRecBuf); return STATUS_EVENTLOG_FILE_CORRUPT; } - HeapFree(MyHeap, 0, RecBuf); + 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) { - if (SetFilePointer(LogFile->hFile, 0, NULL, FILE_BEGIN) == - INVALID_SET_FILE_POINTER) + FileOffset.QuadPart = 0LL; + Status = NtWriteFile(LogFile->hFile, + NULL, + NULL, + NULL, + &IoStatusBlock, + &LogFile->Header, + sizeof(EVENTLOGHEADER), + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) { - DPRINT1("SetFilePointer() failed! %d\n", GetLastError()); + DPRINT1("NtWriteFile failed (Status 0x%08lx)\n", Status); return STATUS_EVENTLOG_FILE_CORRUPT; } - if (!WriteFile(LogFile->hFile, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &dwRead, - NULL)) + Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); + if (!NT_SUCCESS(Status)) { - DPRINT1("WriteFile failed! %d\n", GetLastError()); - return STATUS_EVENTLOG_FILE_CORRUPT; - } - - if (!FlushFileBuffers(LogFile->hFile)) - { - DPRINT1("FlushFileBuffers failed! %d\n", GetLastError()); + DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status); return STATUS_EVENTLOG_FILE_CORRUPT; } } @@ -540,26 +1025,26 @@ LogfInitializeExisting(PLOGFILE LogFile, return STATUS_SUCCESS; } - NTSTATUS -LogfCreate(PLOGFILE *LogFile, - WCHAR *LogName, +LogfCreate(PLOGFILE* LogFile, + PCWSTR LogName, PUNICODE_STRING FileName, - ULONG ulMaxSize, - ULONG ulRetention, - BOOL Permanent, - BOOL Backup) + ULONG ulMaxSize, + ULONG ulRetention, + BOOLEAN Permanent, + BOOLEAN Backup) { + NTSTATUS Status = STATUS_SUCCESS; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; PLOGFILE pLogFile; - BOOL bCreateNew = FALSE; - NTSTATUS Status = STATUS_SUCCESS; + SIZE_T LogNameLen; + BOOLEAN CreateNew = FALSE; - pLogFile = (LOGFILE *) HeapAlloc(MyHeap, HEAP_ZERO_MEMORY, sizeof(LOGFILE)); + pLogFile = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LOGFILE)); if (!pLogFile) { - DPRINT1("Can't allocate heap!\n"); + DPRINT1("Cannot allocate heap!\n"); return STATUS_NO_MEMORY; } @@ -570,7 +1055,8 @@ LogfCreate(PLOGFILE *LogFile, NULL); Status = NtCreateFile(&pLogFile->hFile, - Backup ? (GENERIC_READ | SYNCHRONIZE) : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE), + Backup ? (GENERIC_READ | SYNCHRONIZE) + : (GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE), &ObjectAttributes, &IoStatusBlock, NULL, @@ -582,82 +1068,90 @@ LogfCreate(PLOGFILE *LogFile, 0); if (!NT_SUCCESS(Status)) { - DPRINT1("Can't create file %wZ (Status: 0x%08lx)\n", FileName, Status); - goto fail; + DPRINT1("Cannot create file %wZ (Status: 0x%08lx)\n", FileName, Status); + goto Quit; } - bCreateNew = (IoStatusBlock.Information == FILE_CREATED) ? TRUE: FALSE; + CreateNew = (IoStatusBlock.Information == FILE_CREATED); - pLogFile->LogName = - (WCHAR *) HeapAlloc(MyHeap, - HEAP_ZERO_MEMORY, - (lstrlenW(LogName) + 1) * sizeof(WCHAR)); + LogNameLen = (LogName ? wcslen(LogName) : 0) + 1; + pLogFile->LogName = RtlAllocateHeap(GetProcessHeap(), + HEAP_ZERO_MEMORY, + LogNameLen * sizeof(WCHAR)); if (pLogFile->LogName == NULL) { - DPRINT1("Can't allocate heap\n"); + DPRINT1("Cannot allocate heap\n"); Status = STATUS_NO_MEMORY; - goto fail; + goto Quit; } if (LogName) - StringCchCopy(pLogFile->LogName,lstrlenW(LogName) + 1, LogName); + StringCchCopy(pLogFile->LogName, LogNameLen, LogName); - pLogFile->FileName = - (WCHAR *) HeapAlloc(MyHeap, - HEAP_ZERO_MEMORY, - (lstrlenW(FileName->Buffer) + 1) * sizeof(WCHAR)); + pLogFile->FileName = RtlAllocateHeap(GetProcessHeap(), + HEAP_ZERO_MEMORY, + /*(wcslen(FileName->Buffer) + 1) * sizeof(WCHAR)*/ + FileName->Length + sizeof(UNICODE_NULL)); if (pLogFile->FileName == NULL) { - DPRINT1("Can't allocate heap\n"); + DPRINT1("Cannot allocate heap\n"); Status = STATUS_NO_MEMORY; - goto fail; + goto Quit; } - StringCchCopy(pLogFile->FileName, lstrlenW(FileName->Buffer) + 1, FileName->Buffer); + StringCchCopy(pLogFile->FileName, + /*wcslen(FileName->Buffer) + 1*/ (FileName->Length + sizeof(UNICODE_NULL)) / sizeof(WCHAR), + FileName->Buffer); - pLogFile->OffsetInfo = - (PEVENT_OFFSET_INFO) HeapAlloc(MyHeap, - HEAP_ZERO_MEMORY, - sizeof(EVENT_OFFSET_INFO) * 64); + pLogFile->OffsetInfo = RtlAllocateHeap(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(EVENT_OFFSET_INFO) * 64); if (pLogFile->OffsetInfo == NULL) { - DPRINT1("Can't allocate heap\n"); + DPRINT1("Cannot allocate heap\n"); Status = STATUS_NO_MEMORY; - goto fail; + goto Quit; } - pLogFile->OffsetInfoSize = 64; + pLogFile->OffsetInfoNext = 0; pLogFile->Permanent = Permanent; - if (bCreateNew) + // 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 fail; + goto Quit; RtlInitializeResource(&pLogFile->Lock); LogfListAddItem(pLogFile); - fail: +Quit: if (!NT_SUCCESS(Status)) { if ((pLogFile->hFile != NULL) && (pLogFile->hFile != INVALID_HANDLE_VALUE)) - CloseHandle(pLogFile->hFile); + NtClose(pLogFile->hFile); if (pLogFile->OffsetInfo) - HeapFree(MyHeap, 0, pLogFile->OffsetInfo); + RtlFreeHeap(GetProcessHeap(), 0, pLogFile->OffsetInfo); if (pLogFile->FileName) - HeapFree(MyHeap, 0, pLogFile->FileName); + RtlFreeHeap(GetProcessHeap(), 0, pLogFile->FileName); if (pLogFile->LogName) - HeapFree(MyHeap, 0, pLogFile->LogName); + RtlFreeHeap(GetProcessHeap(), 0, pLogFile->LogName); - HeapFree(MyHeap, 0, pLogFile); + RtlFreeHeap(GetProcessHeap(), 0, pLogFile); } else { @@ -667,80 +1161,84 @@ LogfCreate(PLOGFILE *LogFile, return Status; } - VOID LogfClose(PLOGFILE LogFile, - BOOL ForceClose) + BOOLEAN ForceClose) { + IO_STATUS_BLOCK IoStatusBlock; + if (LogFile == NULL) return; - if ((ForceClose == FALSE) && - (LogFile->Permanent == TRUE)) + if (!ForceClose && LogFile->Permanent) return; RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); - FlushFileBuffers(LogFile->hFile); - CloseHandle(LogFile->hFile); + NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); + NtClose(LogFile->hFile); LogfListRemoveItem(LogFile); RtlDeleteResource(&LogFile->Lock); - HeapFree(MyHeap, 0, LogFile->LogName); - HeapFree(MyHeap, 0, LogFile->FileName); - HeapFree(MyHeap, 0, LogFile->OffsetInfo); - HeapFree(MyHeap, 0, LogFile); + RtlFreeHeap(GetProcessHeap(), 0, LogFile->LogName); + RtlFreeHeap(GetProcessHeap(), 0, LogFile->FileName); + RtlFreeHeap(GetProcessHeap(), 0, LogFile->OffsetInfo); + RtlFreeHeap(GetProcessHeap(), 0, LogFile); return; } -static BOOL -ReadAnsiLogEntry(HANDLE hFile, - LPVOID lpBuffer, - DWORD nNumberOfBytesToRead, - LPDWORD lpNumberOfBytesRead) +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) { - PEVENTLOGRECORD Dst; - PEVENTLOGRECORD Src; + NTSTATUS Status; + PVOID UnicodeBuffer = NULL; + PEVENTLOGRECORD Src, Dst; ANSI_STRING StringA; UNICODE_STRING StringW; - LPWSTR SrcPtr; - LPSTR DstPtr; - LPWSTR SrcString; - LPSTR DstString; - LPVOID lpUnicodeBuffer = NULL; - DWORD dwRead = 0; + PVOID SrcPtr, DstPtr; + // DWORD dwRead; DWORD i; DWORD dwPadding; DWORD dwEntryLength; PDWORD pLength; - NTSTATUS Status; - BOOL ret = TRUE; - *lpNumberOfBytesRead = 0; + IoStatusBlock->Information = 0; - lpUnicodeBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, nNumberOfBytesToRead); - if (lpUnicodeBuffer == NULL) + UnicodeBuffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, Length); + if (UnicodeBuffer == NULL) { DPRINT1("Alloc failed!\n"); - return FALSE; + return STATUS_NO_MEMORY; } - if (!ReadFile(hFile, lpUnicodeBuffer, nNumberOfBytesToRead, &dwRead, NULL)) + Status = ReadLogBuffer(LogFile, + IoStatusBlock, + UnicodeBuffer, + Length, + ByteOffset, + NextOffset); + if (!NT_SUCCESS(Status)) { - DPRINT1("Read failed!\n"); - ret = FALSE; + DPRINT1("ReadLogBuffer failed (Status 0x%08lx)\n", Status); + // Status = STATUS_EVENTLOG_FILE_CORRUPT; goto done; } + // dwRead = IoStatusBlock->Information; - Dst = (PEVENTLOGRECORD)lpBuffer; - Src = (PEVENTLOGRECORD)lpUnicodeBuffer; + Src = (PEVENTLOGRECORD)UnicodeBuffer; + Dst = (PEVENTLOGRECORD)Buffer; - Dst->TimeGenerated = Src->TimeGenerated; Dst->Reserved = Src->Reserved; Dst->RecordNumber = Src->RecordNumber; + Dst->TimeGenerated = Src->TimeGenerated; Dst->TimeWritten = Src->TimeWritten; Dst->EventID = Src->EventID; Dst->EventType = Src->EventType; @@ -749,8 +1247,8 @@ ReadAnsiLogEntry(HANDLE hFile, Dst->UserSidLength = Src->UserSidLength; Dst->DataLength = Src->DataLength; - SrcPtr = (LPWSTR)((DWORD_PTR)Src + sizeof(EVENTLOGRECORD)); - DstPtr = (LPSTR)((DWORD_PTR)Dst + sizeof(EVENTLOGRECORD)); + SrcPtr = (PVOID)((ULONG_PTR)Src + sizeof(EVENTLOGRECORD)); + DstPtr = (PVOID)((ULONG_PTR)Dst + sizeof(EVENTLOGRECORD)); /* Convert the module name */ RtlInitUnicodeString(&StringW, SrcPtr); @@ -758,125 +1256,156 @@ ReadAnsiLogEntry(HANDLE hFile, if (NT_SUCCESS(Status)) { RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength); - - DstPtr = (PVOID)((DWORD_PTR)DstPtr + StringA.MaximumLength); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength); RtlFreeAnsiString(&StringA); } + else + { + RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR)); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR)); + } + SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength); /* Convert the computer name */ + RtlInitUnicodeString(&StringW, SrcPtr); + Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE); if (NT_SUCCESS(Status)) { - SrcPtr = (PWSTR)((DWORD_PTR)SrcPtr + StringW.MaximumLength); + RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength); + RtlFreeAnsiString(&StringA); + } + else + { + RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR)); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR)); + } + + /* Add the padding and the User SID */ + dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr - (ULONG_PTR)Dst) % sizeof(ULONG)); + RtlZeroMemory(DstPtr, dwPadding); + + SrcPtr = (PVOID)((ULONG_PTR)Src + Src->UserSidOffset); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + dwPadding); + + Dst->UserSidOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst); + RtlCopyMemory(DstPtr, SrcPtr, Src->UserSidLength); + + /* Convert the strings */ + SrcPtr = (PVOID)((ULONG_PTR)Src + Src->StringOffset); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->UserSidLength); + Dst->StringOffset = (DWORD)((ULONG_PTR)DstPtr - (ULONG_PTR)Dst); + + for (i = 0; i < Dst->NumStrings; i++) + { RtlInitUnicodeString(&StringW, SrcPtr); Status = RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE); if (NT_SUCCESS(Status)) { RtlCopyMemory(DstPtr, StringA.Buffer, StringA.MaximumLength); - - DstPtr = (PVOID)((DWORD_PTR)DstPtr + StringA.MaximumLength); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringA.MaximumLength); RtlFreeAnsiString(&StringA); } - } - - /* Add the padding and the User SID*/ - if (NT_SUCCESS(Status)) - { - dwPadding = sizeof(DWORD) - (((DWORD_PTR)DstPtr - (DWORD_PTR)Dst) % sizeof(DWORD)); - RtlZeroMemory(DstPtr, dwPadding); - - DstPtr = (LPSTR)((DWORD_PTR)DstPtr + dwPadding); - RtlCopyMemory(DstPtr, - (PVOID)((DWORD_PTR)Src + Src->UserSidOffset), - Src->UserSidLength); - - Dst->UserSidOffset = (DWORD)((DWORD_PTR)DstPtr - (DWORD_PTR)Dst); - } - - - /* Convert the strings */ - if (NT_SUCCESS(Status)) - { - DstPtr = (PVOID)((DWORD_PTR)DstPtr + Src->UserSidLength); - - SrcString = (LPWSTR)((DWORD_PTR)Src + (DWORD)Src->StringOffset); - DstString = (LPSTR)DstPtr; - - for (i = 0; i < Dst->NumStrings; i++) + else { - RtlInitUnicodeString(&StringW, SrcString); - - RtlUnicodeStringToAnsiString(&StringA, &StringW, TRUE); - - RtlCopyMemory(DstString, StringA.Buffer, StringA.MaximumLength); - - SrcString = (LPWSTR)((DWORD_PTR)SrcString + - (DWORD)StringW.MaximumLength); - - DstString = (LPSTR)((DWORD_PTR)DstString + - (DWORD)StringA.MaximumLength); - - RtlFreeAnsiString(&StringA); + RtlZeroMemory(DstPtr, StringW.MaximumLength / sizeof(WCHAR)); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + StringW.MaximumLength / sizeof(WCHAR)); } - - Dst->StringOffset = (DWORD)((DWORD_PTR)DstPtr - (DWORD_PTR)Dst); - - - /* Copy the binary data */ - DstPtr = (PVOID)DstString; - Dst->DataOffset = (DWORD_PTR)DstPtr - (DWORD_PTR)Dst; - - RtlCopyMemory(DstPtr, (PVOID)((DWORD_PTR)Src + Src->DataOffset), Src->DataLength); - - /* Add the padding */ - DstPtr = (PVOID)((DWORD_PTR)DstPtr + Src->DataLength); - dwPadding = sizeof(DWORD) - (((DWORD_PTR)DstPtr-(DWORD_PTR)Dst) % sizeof(DWORD)); - RtlZeroMemory(DstPtr, dwPadding); - - dwEntryLength = (DWORD)((DWORD_PTR)DstPtr + dwPadding + sizeof(DWORD) - (DWORD_PTR)Dst); - - /* Set the entry length at the end of the entry*/ - pLength = (PDWORD)((DWORD_PTR)DstPtr + dwPadding); - *pLength = dwEntryLength; - Dst->Length = dwEntryLength; - - *lpNumberOfBytesRead = dwEntryLength; + SrcPtr = (PVOID)((ULONG_PTR)SrcPtr + StringW.MaximumLength); } + /* Copy the binary data */ + SrcPtr = (PVOID)((ULONG_PTR)Src + Src->DataOffset); + Dst->DataOffset = (ULONG_PTR)DstPtr - (ULONG_PTR)Dst; + RtlCopyMemory(DstPtr, SrcPtr, Src->DataLength); + DstPtr = (PVOID)((ULONG_PTR)DstPtr + Src->DataLength); + + /* Add the padding */ + dwPadding = sizeof(ULONG) - (((ULONG_PTR)DstPtr-(ULONG_PTR)Dst) % sizeof(ULONG)); + RtlZeroMemory(DstPtr, dwPadding); + + dwEntryLength = (DWORD)((ULONG_PTR)DstPtr + dwPadding + sizeof(ULONG) - (ULONG_PTR)Dst); + + /* Set the entry length at the end of the entry */ + pLength = (PDWORD)((ULONG_PTR)DstPtr + dwPadding); + *pLength = dwEntryLength; + Dst->Length = dwEntryLength; + + IoStatusBlock->Information = dwEntryLength; + + Status = STATUS_SUCCESS; + done: - if (lpUnicodeBuffer != NULL) - HeapFree(GetProcessHeap(), 0, lpUnicodeBuffer); + if (UnicodeBuffer != NULL) + RtlFreeHeap(GetProcessHeap(), 0, UnicodeBuffer); - return ret; + return Status; } - -DWORD LogfReadEvent(PLOGFILE LogFile, - DWORD Flags, - DWORD * RecordNumber, - DWORD BufSize, - PBYTE Buffer, - DWORD * BytesRead, - DWORD * BytesNeeded, - BOOL Ansi) +/* + * NOTE: + * 'RecordNumber' is a pointer to the record number at which the read operation + * should start. If the record number is 0 and the flags given in the 'Flags' + * parameter contain EVENTLOG_SEQUENTIAL_READ, an adequate record number is + * computed. + */ +NTSTATUS +LogfReadEvents(PLOGFILE LogFile, + ULONG Flags, + PULONG RecordNumber, + ULONG BufSize, + PBYTE Buffer, + PULONG BytesRead, + PULONG BytesNeeded, + BOOLEAN Ansi) { + NTSTATUS Status; + IO_STATUS_BLOCK IoStatusBlock; + LARGE_INTEGER FileOffset; DWORD dwOffset, dwRead, dwRecSize; - DWORD dwBufferUsage = 0, dwRecNum; + DWORD dwBufferUsage, dwRecNum; - if (Flags & EVENTLOG_FORWARDS_READ && Flags & EVENTLOG_BACKWARDS_READ) - return ERROR_INVALID_PARAMETER; + /* Parameters validation */ + + /* EVENTLOG_SEQUENTIAL_READ and EVENTLOG_SEEK_READ are mutually exclusive */ + if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (Flags & EVENTLOG_SEEK_READ)) + return STATUS_INVALID_PARAMETER; + + if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && !(Flags & EVENTLOG_SEEK_READ)) + return STATUS_INVALID_PARAMETER; + + /* EVENTLOG_FORWARDS_READ and EVENTLOG_BACKWARDS_READ are mutually exclusive */ + if ((Flags & EVENTLOG_FORWARDS_READ) && (Flags & EVENTLOG_BACKWARDS_READ)) + return STATUS_INVALID_PARAMETER; if (!(Flags & EVENTLOG_FORWARDS_READ) && !(Flags & EVENTLOG_BACKWARDS_READ)) - return ERROR_INVALID_PARAMETER; + return STATUS_INVALID_PARAMETER; if (!Buffer || !BytesRead || !BytesNeeded) - return ERROR_INVALID_PARAMETER; + return STATUS_INVALID_PARAMETER; - if ((*RecordNumber==0) && !(EVENTLOG_SEQUENTIAL_READ)) + /* In seek read mode, a record number of 0 is invalid */ + if (!(Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0)) + return STATUS_INVALID_PARAMETER; + + /* + * In sequential read mode, a record number of 0 means we need + * to determine where to start the read operation. Otherwise + * we just use the provided record number. + */ + if ((Flags & EVENTLOG_SEQUENTIAL_READ) && (*RecordNumber == 0)) { - return ERROR_INVALID_PARAMETER; + if (Flags & EVENTLOG_FORWARDS_READ) + { + *RecordNumber = LogFile->Header.OldestRecordNumber; + } + else // if (Flags & EVENTLOG_BACKWARDS_READ) + { + *RecordNumber = LogFile->Header.CurrentRecordNumber - 1; + } } dwRecNum = *RecordNumber; @@ -886,323 +1415,475 @@ DWORD LogfReadEvent(PLOGFILE LogFile, *BytesRead = 0; *BytesNeeded = 0; - dwOffset = LogfOffsetByNumber(LogFile, dwRecNum); - - if (!dwOffset) + dwBufferUsage = 0; + do { - RtlReleaseResource(&LogFile->Lock); - return ERROR_HANDLE_EOF; - } - - if (SetFilePointer(LogFile->hFile, dwOffset, NULL, FILE_BEGIN) == - INVALID_SET_FILE_POINTER) - { - DPRINT1("SetFilePointer() failed!\n"); - goto Done; - } - - if (!ReadFile(LogFile->hFile, &dwRecSize, sizeof(DWORD), &dwRead, NULL)) - { - DPRINT1("ReadFile() failed!\n"); - goto Done; - } - - if (dwRecSize > BufSize) - { - *BytesNeeded = dwRecSize; - RtlReleaseResource(&LogFile->Lock); - return ERROR_INSUFFICIENT_BUFFER; - } - - if (SetFilePointer(LogFile->hFile, - -((LONG) sizeof(DWORD)), - NULL, - FILE_CURRENT) == INVALID_SET_FILE_POINTER) - { - DPRINT1("SetFilePointer() failed!\n"); - goto Done; - } - - if (Ansi == TRUE) - { - if (!ReadAnsiLogEntry(LogFile->hFile, Buffer, dwRecSize, &dwRead)) - { - DPRINT1("ReadAnsiLogEntry() failed!\n"); - goto Done; - } - } - else - { - if (!ReadFile(LogFile->hFile, Buffer, dwRecSize, &dwRead, NULL)) - { - DPRINT1("ReadFile() failed!\n"); - goto Done; - } - } - - dwBufferUsage += dwRead; - - while (dwBufferUsage <= BufSize) - { - if (Flags & EVENTLOG_FORWARDS_READ) - dwRecNum++; - else - dwRecNum--; - dwOffset = LogfOffsetByNumber(LogFile, dwRecNum); - if (!dwOffset) - break; - - if (SetFilePointer(LogFile->hFile, dwOffset, NULL, FILE_BEGIN) == - INVALID_SET_FILE_POINTER) + if (dwOffset == 0) { - DPRINT1("SetFilePointer() failed!\n"); - goto Done; - } - - if (!ReadFile(LogFile->hFile, - &dwRecSize, - sizeof(DWORD), - &dwRead, - NULL)) - { - DPRINT1("ReadFile() failed!\n"); - goto Done; - } - - if (dwBufferUsage + dwRecSize > BufSize) - break; - - if (SetFilePointer(LogFile->hFile, - -((LONG) sizeof(DWORD)), - NULL, - FILE_CURRENT) == INVALID_SET_FILE_POINTER) - { - DPRINT1("SetFilePointer() failed!\n"); - goto Done; - } - - if (Ansi == TRUE) - { - if (!ReadAnsiLogEntry(LogFile->hFile, - Buffer + dwBufferUsage, - dwRecSize, - &dwRead)) + if (dwBufferUsage == 0) { - DPRINT1("ReadAnsiLogEntry() failed!\n"); - goto Done; - } - } - else - { - if (!ReadFile(LogFile->hFile, - Buffer + dwBufferUsage, - dwRecSize, - &dwRead, - NULL)) - { - DPRINT1("ReadFile() failed!\n"); - goto Done; - } - } - - dwBufferUsage += dwRead; - } - - *BytesRead = dwBufferUsage; - * RecordNumber = dwRecNum; - RtlReleaseResource(&LogFile->Lock); - return ERROR_SUCCESS; - -Done: - DPRINT1("LogfReadEvent failed with %x\n",GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return GetLastError(); -} - -BOOL LogfWriteData(PLOGFILE LogFile, DWORD BufSize, PBYTE Buffer) -{ - DWORD dwWritten; - DWORD dwRead; - LARGE_INTEGER SystemTime; - EVENTLOGEOF EofRec; - PEVENTLOGRECORD RecBuf; - LARGE_INTEGER logFileSize; - ULONG RecOffSet; - ULONG WriteOffSet; - - if (!Buffer) - return FALSE; - - RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); - - NtQuerySystemTime(&SystemTime); - RtlTimeToSecondsSince1970(&SystemTime, &((PEVENTLOGRECORD)Buffer)->TimeWritten); - - if (!GetFileSizeEx(LogFile->hFile, &logFileSize)) - { - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } - - /* If the size of the file is over MaxSize */ - if ((logFileSize.QuadPart + BufSize)> LogFile->Header.MaxSize) - { - ULONG OverWriteLength = 0; - WriteOffSet = LogfOffsetByNumber(LogFile, LogFile->Header.OldestRecordNumber); - RecBuf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(EVENTLOGRECORD)); - /* Determine how many records need to be overwritten */ - while (TRUE) - { - DPRINT("EventLogFile has reached maximume size\n"); - - if (!RecBuf) - { - DPRINT1("Failed to allocate buffer for OldestRecord!\n"); - HeapFree(GetProcessHeap(), 0, RecBuf); RtlReleaseResource(&LogFile->Lock); - return FALSE; + return STATUS_END_OF_FILE; } - - /* Get the oldest record data */ - RecOffSet = LogfOffsetByNumber(LogFile, LogFile->Header.OldestRecordNumber); - - if (SetFilePointer(LogFile->hFile, - RecOffSet, - NULL, - FILE_BEGIN) == INVALID_SET_FILE_POINTER) + else { - DPRINT1("SetFilePointer() failed! %d\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, RecBuf); - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } - - if (!ReadFile(LogFile->hFile, RecBuf, sizeof(EVENTLOGRECORD), &dwRead, NULL)) - { - DPRINT1("ReadFile() failed!\n"); - HeapFree(GetProcessHeap(), 0, RecBuf); - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } - - if (RecBuf->Reserved != LOGFILE_SIGNATURE) - { - DPRINT1("LogFile corrupt!\n"); - HeapFree(GetProcessHeap(), 0, RecBuf); - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } - - LogfDeleteOffsetInformation(LogFile,LogFile->Header.OldestRecordNumber); - - LogFile->Header.OldestRecordNumber++; - - OverWriteLength += RecBuf->Length; - /* Check the size of the record as the record adding may be larger */ - if (OverWriteLength >= BufSize) - { - DPRINT("Record will fit. Length %d, BufSize %d\n", OverWriteLength, BufSize); - LogFile->Header.StartOffset = LogfOffsetByNumber(LogFile, LogFile->Header.OldestRecordNumber); break; } } - HeapFree(GetProcessHeap(), 0, RecBuf); - } - else - WriteOffSet = LogFile->Header.EndOffset; - if (SetFilePointer(LogFile->hFile, - WriteOffSet, - NULL, - FILE_BEGIN) == INVALID_SET_FILE_POINTER) + 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) + { + if (dwBufferUsage == 0) + { + *BytesNeeded = dwRecSize; + RtlReleaseResource(&LogFile->Lock); + return STATUS_BUFFER_TOO_SMALL; + } + 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 + { + 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; + } + } + dwRead = IoStatusBlock.Information; + + /* Go to the next event record */ + /* + * NOTE: This implicitely supposes that all the other record numbers + * are consecutive (and do not jump than more than one unit); but if + * it is not the case, then we would prefer here to call some + * "get_next_record_number" function. + */ + if (Flags & EVENTLOG_FORWARDS_READ) + dwRecNum++; + else // if (Flags & EVENTLOG_BACKWARDS_READ) + dwRecNum--; + + dwBufferUsage += dwRead; + } + while (dwBufferUsage <= BufSize); + + *BytesRead = dwBufferUsage; + *RecordNumber = dwRecNum; + RtlReleaseResource(&LogFile->Lock); + return STATUS_SUCCESS; + +Done: + DPRINT1("LogfReadEvents failed (Status 0x%08lx)\n", Status); + RtlReleaseResource(&LogFile->Lock); + return Status; +} + +NTSTATUS +LogfWriteRecord(PLOGFILE LogFile, + ULONG BufSize, // SIZE_T + PEVENTLOGRECORD Record) +{ + 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; + + RtlAcquireResourceExclusive(&LogFile->Lock, TRUE); + + /* + * Retrieve the record written time now, that will also be compared + * with the existing events timestamps in case the log is wrapping. + */ + NtQuerySystemTime(&SystemTime); + RtlTimeToSecondsSince1970(&SystemTime, &Record->TimeWritten); + + 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)) { - DPRINT1("SetFilePointer() failed! %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; + 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 (!WriteFile(LogFile->hFile, Buffer, BufSize, &dwWritten, NULL)) + if ( (LogFile->Header.StartOffset < LogFile->Header.EndOffset) && + (LogFile->Header.MaxSize - WriteOffset < sizeof(RecBuf)) ) // (UpperBound - WriteOffset < sizeof(RecBuf)) { - DPRINT1("WriteFile() failed! %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; + // 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, - LogFile->Header.CurrentRecordNumber, - WriteOffSet)) + Record->RecordNumber, + WriteOffset)) { - RtlReleaseResource(&LogFile->Lock); - return FALSE; + Status = STATUS_NO_MEMORY; // STATUS_EVENTLOG_FILE_CORRUPT; + goto Quit; } LogFile->Header.CurrentRecordNumber++; + if (LogFile->Header.CurrentRecordNumber == 0) + LogFile->Header.CurrentRecordNumber = 1; - if (WriteOffSet == LogFile->Header.EndOffset) - { - LogFile->Header.EndOffset += dwWritten; - } - if (SetFilePointer(LogFile->hFile, - LogFile->Header.EndOffset, - NULL, - FILE_BEGIN) == INVALID_SET_FILE_POINTER) - { - DPRINT1("SetFilePointer() failed! %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } + /* + * 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; - EofRec.Ones = 0x11111111; - EofRec.Twos = 0x22222222; - EofRec.Threes = 0x33333333; - EofRec.Fours = 0x44444444; - EofRec.RecordSizeBeginning = sizeof(EVENTLOGEOF); - EofRec.RecordSizeEnd = sizeof(EVENTLOGEOF); - EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; + RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); EofRec.BeginRecord = LogFile->Header.StartOffset; - EofRec.EndRecord = LogFile->Header.EndOffset; + EofRec.EndRecord = LogFile->Header.EndOffset; + EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; + EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - if (!WriteFile(LogFile->hFile, - &EofRec, - sizeof(EVENTLOGEOF), - &dwWritten, - NULL)) + // FileOffset.QuadPart = LogFile->Header.EndOffset; + Status = WriteLogBuffer(LogFile, + &IoStatusBlock, + &EofRec, + sizeof(EofRec), + &FileOffset, + &NextOffset); + // dwWritten = IoStatusBlock.Information; + if (!NT_SUCCESS(Status)) { - DPRINT1("WriteFile() failed! %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; + DPRINT1("WriteLogBuffer failed (Status 0x%08lx)\n", Status); + goto Quit; + } + 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; } - if (SetFilePointer(LogFile->hFile, 0, NULL, FILE_BEGIN) == - INVALID_SET_FILE_POINTER) + Status = NtFlushBuffersFile(LogFile->hFile, &IoStatusBlock); + if (!NT_SUCCESS(Status)) { - DPRINT1("SetFilePointer() failed! %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; + DPRINT1("NtFlushBuffersFile failed (Status 0x%08lx)\n", Status); + goto Quit; } - if (!WriteFile(LogFile->hFile, - &LogFile->Header, - sizeof(EVENTLOGHEADER), - &dwWritten, - NULL)) - { - DPRINT1("WriteFile failed! LastError = %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } - - if (!FlushFileBuffers(LogFile->hFile)) - { - DPRINT1("FlushFileBuffers() failed! %d\n", GetLastError()); - RtlReleaseResource(&LogFile->Lock); - return FALSE; - } + Status = STATUS_SUCCESS; +Quit: RtlReleaseResource(&LogFile->Lock); - return TRUE; + return Status; } NTSTATUS @@ -1241,17 +1922,19 @@ NTSTATUS LogfBackupFile(PLOGFILE LogFile, PUNICODE_STRING BackupFileName) { + NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; - EVENTLOGHEADER Header; - EVENTLOGEOF EofRec; - HANDLE FileHandle = NULL; - ULONG i; LARGE_INTEGER FileOffset; - NTSTATUS Status; - PUCHAR Buffer = NULL; + HANDLE FileHandle = NULL; + EVENTLOGHEADER Header; + EVENTLOGRECORD RecBuf; + EVENTLOGEOF EofRec; + ULONG i; + ULONG RecOffset; + PVOID Buffer = NULL; - DWORD dwOffset, dwRead, dwRecSize; + // DWORD dwRead; DPRINT1("LogfBackupFile(%p, %wZ)\n", LogFile, BackupFileName); @@ -1277,33 +1960,34 @@ LogfBackupFile(PLOGFILE LogFile, 0); if (!NT_SUCCESS(Status)) { - DPRINT("Can't create backup file %wZ (Status: 0x%08lx)\n", BackupFileName, Status); + DPRINT("Cannot create backup file %wZ (Status: 0x%08lx)\n", BackupFileName, Status); goto Done; } /* Initialize the (dirty) log file header */ - Header.HeaderSize = sizeof(EVENTLOGHEADER); + Header.HeaderSize = sizeof(Header); Header.Signature = LOGFILE_SIGNATURE; Header.MajorVersion = MAJORVER; Header.MinorVersion = MINORVER; - Header.StartOffset = sizeof(EVENTLOGHEADER); - Header.EndOffset = sizeof(EVENTLOGHEADER); + Header.StartOffset = sizeof(Header); + Header.EndOffset = sizeof(Header); Header.CurrentRecordNumber = 1; - Header.OldestRecordNumber = 1; + Header.OldestRecordNumber = 0; Header.MaxSize = LogFile->Header.MaxSize; Header.Flags = ELF_LOGFILE_HEADER_DIRTY; Header.Retention = LogFile->Header.Retention; - Header.EndHeaderSize = sizeof(EVENTLOGHEADER); + Header.EndHeaderSize = sizeof(Header); /* Write the (dirty) log file header */ + FileOffset.QuadPart = 0LL; Status = NtWriteFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, &Header, - sizeof(EVENTLOGHEADER), - NULL, + sizeof(Header), + &FileOffset, NULL); if (!NT_SUCCESS(Status)) { @@ -1313,85 +1997,94 @@ LogfBackupFile(PLOGFILE LogFile, for (i = LogFile->Header.OldestRecordNumber; i < LogFile->Header.CurrentRecordNumber; i++) { - dwOffset = LogfOffsetByNumber(LogFile, i); - if (dwOffset == 0) + RecOffset = LogfOffsetByNumber(LogFile, i); + if (RecOffset == 0) break; - if (SetFilePointer(LogFile->hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + /* 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("SetFilePointer() failed!\n"); + DPRINT1("NtReadFile failed (Status 0x%08lx)\n", Status); goto Done; } + // dwRead = IoStatusBlock.Information; - if (!ReadFile(LogFile->hFile, &dwRecSize, sizeof(DWORD), &dwRead, NULL)) - { - DPRINT1("ReadFile() failed!\n"); - goto Done; - } + // if (dwRead != sizeof(RecBuf)) + // break; - if (SetFilePointer(LogFile->hFile, dwOffset, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - { - DPRINT1("SetFilePointer() failed!\n"); - goto Done; - } - - Buffer = HeapAlloc(MyHeap, 0, dwRecSize); + Buffer = RtlAllocateHeap(GetProcessHeap(), 0, RecBuf.Length); if (Buffer == NULL) { - DPRINT1("HeapAlloc() failed!\n"); + DPRINT1("RtlAllocateHeap() failed!\n"); goto Done; } - if (!ReadFile(LogFile->hFile, Buffer, dwRecSize, &dwRead, NULL)) + /* Read the full EVENTLOGRECORD (header + data) with wrapping */ + Status = ReadLogBuffer(LogFile, + &IoStatusBlock, + Buffer, + RecBuf.Length, + &FileOffset, + NULL); + if (!NT_SUCCESS(Status)) { - DPRINT1("ReadFile() failed!\n"); + 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 */ + /* Write the event record (no wrap for the backup log) */ Status = NtWriteFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, Buffer, - dwRecSize, + 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 += dwRecSize; + Header.EndOffset += RecBuf.Length; /* Free the buffer */ - HeapFree(MyHeap, 0, Buffer); + RtlFreeHeap(GetProcessHeap(), 0, Buffer); Buffer = NULL; } /* Initialize the EOF record */ - EofRec.RecordSizeBeginning = sizeof(EVENTLOGEOF); - EofRec.Ones = 0x11111111; - EofRec.Twos = 0x22222222; - EofRec.Threes = 0x33333333; - EofRec.Fours = 0x44444444; - EofRec.BeginRecord = sizeof(EVENTLOGHEADER); - EofRec.EndRecord = Header.EndOffset; + RtlCopyMemory(&EofRec, &EOFRecord, sizeof(EOFRecord)); + EofRec.BeginRecord = Header.StartOffset; + EofRec.EndRecord = Header.EndOffset; EofRec.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - EofRec.RecordSizeEnd = sizeof(EVENTLOGEOF); + EofRec.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - /* Write the EOF record */ + /* Write the EOF record (no wrap for the backup log) */ Status = NtWriteFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, &EofRec, - sizeof(EVENTLOGEOF), + sizeof(EofRec), NULL, NULL); if (!NT_SUCCESS(Status)) @@ -1402,19 +2095,19 @@ LogfBackupFile(PLOGFILE LogFile, /* Update the header information */ Header.CurrentRecordNumber = LogFile->Header.CurrentRecordNumber; - Header.OldestRecordNumber = LogFile->Header.OldestRecordNumber; - Header.MaxSize = Header.EndOffset + sizeof(EVENTLOGEOF); + 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 = 0; + FileOffset.QuadPart = 0LL; Status = NtWriteFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, &Header, - sizeof(EVENTLOGHEADER), + sizeof(Header), &FileOffset, NULL); if (!NT_SUCCESS(Status)) @@ -1423,10 +2116,6 @@ LogfBackupFile(PLOGFILE LogFile, } Done: - /* Free the buffer */ - if (Buffer != NULL) - HeapFree(MyHeap, 0, Buffer); - /* Close the backup file */ if (FileHandle != NULL) NtClose(FileHandle); @@ -1438,9 +2127,9 @@ Done: } -PBYTE +PEVENTLOGRECORD LogfAllocAndBuildNewRecord(PULONG lpRecSize, - ULONG dwRecordNumber, // FIXME! + ULONG Time, USHORT wType, USHORT wCategory, ULONG dwEventId, @@ -1449,20 +2138,21 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, ULONG dwSidLength, PSID lpUserSid, USHORT wNumStrings, - WCHAR* lpStrings, + PWSTR lpStrings, ULONG dwDataSize, PVOID lpRawData) { DWORD dwRecSize; PEVENTLOGRECORD pRec; - LARGE_INTEGER SystemTime; - WCHAR *str; + PWSTR str; UINT i, pos; + SIZE_T SourceNameLen, ComputerNameLen, StringLen; PBYTE Buffer; - dwRecSize = - sizeof(EVENTLOGRECORD) + (lstrlenW(SourceName) + - lstrlenW(ComputerName) + 2) * sizeof(WCHAR); + SourceNameLen = (SourceName ? wcslen(SourceName) : 0) + 1; + ComputerNameLen = (ComputerName ? wcslen(ComputerName) : 0) + 1; + + dwRecSize = sizeof(EVENTLOGRECORD) + (SourceNameLen + ComputerNameLen) * sizeof(WCHAR); /* Align on DWORD boundary for the SID */ dwRecSize = ROUND_UP(dwRecSize, sizeof(ULONG)); @@ -1470,10 +2160,13 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, dwRecSize += dwSidLength; /* Add the sizes for the strings array */ + ASSERT((lpStrings == NULL && wNumStrings == 0) || + (lpStrings != NULL && wNumStrings >= 0)); for (i = 0, str = lpStrings; i < wNumStrings; i++) { - dwRecSize += (lstrlenW(str) + 1) * sizeof(WCHAR); - str += lstrlenW(str) + 1; + StringLen = wcslen(str) + 1; // str must be != NULL + dwRecSize += StringLen * sizeof(WCHAR); + str += StringLen; } /* Add the data size */ @@ -1485,7 +2178,7 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, /* Size of the trailing 'Length' member */ dwRecSize += sizeof(ULONG); - Buffer = HeapAlloc(MyHeap, HEAP_ZERO_MEMORY, dwRecSize); + Buffer = RtlAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRecSize); if (!Buffer) { DPRINT1("Cannot allocate heap!\n"); @@ -1496,26 +2189,20 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, pRec->Length = dwRecSize; pRec->Reserved = LOGFILE_SIGNATURE; - // FIXME: The problem with this technique is that it is possible - // to allocate different records with the same number. Suppose that - // two concurrent events writes happen for the same log, and the - // execution goes inside this function. The callers of this function - // have normally retrieved the 'CurrentRecordNumber' of this log, where - // the access to the log was non locked and no write operation happened - // in between. Then we have two different records for the same log with - // the very same record number, that will be written later. It is only in - // 'LogfWriteRecord' that the 'CurrentRecordNumber' is incremented!! - // - // Therefore we need to rewrite those functions. This function must - // not take any "precomputed" record number. It should attribute a new - // record number under lock, and then increment it atomically, so that - // all event records have unique record numbers. - pRec->RecordNumber = dwRecordNumber; + /* + * Do not assign here any precomputed record number to the event record. + * The true record number will be assigned atomically and sequentially in + * LogfWriteRecord, so that all the event records will have consistent and + * unique record numbers. + */ + pRec->RecordNumber = 0; - NtQuerySystemTime(&SystemTime); - RtlTimeToSecondsSince1970(&SystemTime, &pRec->TimeGenerated); - // FIXME: Already done in LogfWriteRecord. Is it needed to do that here first?? - RtlTimeToSecondsSince1970(&SystemTime, &pRec->TimeWritten); + /* + * Set the generated time, and temporarily set the written time + * with the generated time. + */ + pRec->TimeGenerated = Time; + pRec->TimeWritten = Time; pRec->EventID = dwEventId; pRec->EventType = wType; @@ -1523,18 +2210,21 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, pos = sizeof(EVENTLOGRECORD); - lstrcpyW((WCHAR *) (Buffer + pos), SourceName); - pos += (lstrlenW(SourceName) + 1) * sizeof(WCHAR); - lstrcpyW((WCHAR *) (Buffer + pos), ComputerName); - pos += (lstrlenW(ComputerName) + 1) * sizeof(WCHAR); + if (SourceName) + StringCchCopy((PWSTR)(Buffer + pos), SourceNameLen, SourceName); + pos += SourceNameLen * sizeof(WCHAR); + if (ComputerName) + StringCchCopy((PWSTR)(Buffer + pos), ComputerNameLen, ComputerName); + pos += ComputerNameLen * sizeof(WCHAR); /* Align on DWORD boundary for the SID */ pos = ROUND_UP(pos, sizeof(ULONG)); - pRec->UserSidOffset = pos; + pRec->UserSidLength = 0; + pRec->UserSidOffset = 0; if (dwSidLength) { - CopyMemory(Buffer + pos, lpUserSid, dwSidLength); + RtlCopyMemory(Buffer + pos, lpUserSid, dwSidLength); pRec->UserSidLength = dwSidLength; pRec->UserSidOffset = pos; pos += dwSidLength; @@ -1543,17 +2233,20 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, pRec->StringOffset = pos; for (i = 0, str = lpStrings; i < wNumStrings; i++) { - lstrcpyW((WCHAR *) (Buffer + pos), str); - pos += (lstrlenW(str) + 1) * sizeof(WCHAR); - str += lstrlenW(str) + 1; + StringLen = wcslen(str) + 1; // str must be != NULL + StringCchCopy((PWSTR)(Buffer + pos), StringLen, str); + str += StringLen; + pos += StringLen * sizeof(WCHAR); } pRec->NumStrings = wNumStrings; - pRec->DataOffset = pos; + pRec->DataLength = 0; + pRec->DataOffset = 0; if (dwDataSize) { + RtlCopyMemory(Buffer + pos, lpRawData, dwDataSize); pRec->DataLength = dwDataSize; - CopyMemory(Buffer + pos, lpRawData, dwDataSize); + pRec->DataOffset = pos; pos += dwDataSize; } @@ -1564,7 +2257,7 @@ LogfAllocAndBuildNewRecord(PULONG lpRecSize, *((PDWORD) (Buffer + pos)) = dwRecSize; *lpRecSize = dwRecSize; - return Buffer; + return pRec; } VOID @@ -1572,16 +2265,17 @@ LogfReportEvent(USHORT wType, USHORT wCategory, ULONG dwEventId, USHORT wNumStrings, - WCHAR* lpStrings, + PWSTR lpStrings, ULONG dwDataSize, PVOID lpRawData) { + NTSTATUS Status; WCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwComputerNameLength = MAX_COMPUTERNAME_LENGTH + 1; - PBYTE logBuffer; - DWORD lastRec; + PEVENTLOGRECORD logBuffer; + LARGE_INTEGER SystemTime; + ULONG Time; DWORD recSize; - DWORD dwError; if (!EventLogSource) return; @@ -1591,10 +2285,11 @@ LogfReportEvent(USHORT wType, szComputerName[0] = L'\0'; } - lastRec = LogfGetCurrentRecord(EventLogSource->LogFile); + NtQuerySystemTime(&SystemTime); + RtlTimeToSecondsSince1970(&SystemTime, &Time); logBuffer = LogfAllocAndBuildNewRecord(&recSize, - lastRec, // FIXME! + Time, wType, wCategory, dwEventId, @@ -1607,10 +2302,11 @@ LogfReportEvent(USHORT wType, dwDataSize, lpRawData); - dwError = LogfWriteData(EventLogSource->LogFile, recSize, logBuffer); - if (!dwError) + Status = LogfWriteRecord(EventLogSource->LogFile, recSize, logBuffer); + if (!NT_SUCCESS(Status)) { - DPRINT1("ERROR WRITING TO EventLog %S\n", EventLogSource->LogFile->FileName); + DPRINT1("ERROR WRITING TO EventLog %S (Status 0x%08lx)\n", + EventLogSource->LogFile->FileName, Status); } LogfFreeRecord(logBuffer); diff --git a/reactos/base/services/eventlog/logport.c b/reactos/base/services/eventlog/logport.c index 8a83cbe7d02..0ab18abdf0e 100644 --- a/reactos/base/services/eventlog/logport.c +++ b/reactos/base/services/eventlog/logport.c @@ -107,8 +107,8 @@ NTSTATUS ProcessPortMessage(VOID) { IO_ERROR_LPC Request; PIO_ERROR_LOG_MESSAGE Message; + ULONG Time; PEVENTLOGRECORD pRec; - ULONG ulRecNum; DWORD dwRecSize; NTSTATUS Status; PLOGFILE SystemLog = NULL; @@ -147,30 +147,32 @@ NTSTATUS ProcessPortMessage(VOID) else if (Request.Header.u2.s2.Type == LPC_DATAGRAM) { DPRINT("Received datagram\n"); - Message = (PIO_ERROR_LOG_MESSAGE) & Request.Message; - ulRecNum = SystemLog ? SystemLog->Header.CurrentRecordNumber : 0; + // Message = (PIO_ERROR_LOG_MESSAGE)&Request.Message; + Message = &Request.Message; if (!GetComputerNameW(szComputerName, &dwComputerNameLength)) { szComputerName[0] = L'\0'; } + RtlTimeToSecondsSince1970(&Message->TimeStamp, &Time); + // TODO: Log more information?? - pRec = (PEVENTLOGRECORD) LogfAllocAndBuildNewRecord( + pRec = LogfAllocAndBuildNewRecord( &dwRecSize, - ulRecNum, // FIXME! + Time, Message->Type, Message->EntryData.EventCategory, Message->EntryData.ErrorCode, - (WCHAR *) ((ULONG_PTR)Message + Message->DriverNameOffset), // FIXME: Use DriverNameLength too! + (PWSTR)((ULONG_PTR)Message + Message->DriverNameOffset), // FIXME: Use DriverNameLength too! szComputerName, 0, NULL, Message->EntryData.NumberOfStrings, - (WCHAR *) ((ULONG_PTR)Message + Message->EntryData.StringOffset), + (PWSTR)((ULONG_PTR)Message + Message->EntryData.StringOffset), Message->EntryData.DumpDataSize, - (LPVOID) ((ULONG_PTR)Message + FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData))); + (PVOID)((ULONG_PTR)Message + FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData))); if (pRec == NULL) { @@ -186,10 +188,12 @@ NTSTATUS ProcessPortMessage(VOID) if (!onLiveCD && SystemLog) { - if (!LogfWriteData(SystemLog, dwRecSize, (PBYTE) pRec)) - DPRINT("LogfWriteData failed!\n"); - else - DPRINT("Data written to Log!\n"); + Status = LogfWriteRecord(SystemLog, dwRecSize, pRec); + if (!NT_SUCCESS(Status)) + { + DPRINT1("ERROR WRITING TO EventLog %S (Status 0x%08lx)\n", + SystemLog->FileName, Status); + } } LogfFreeRecord(pRec); diff --git a/reactos/base/services/eventlog/rpc.c b/reactos/base/services/eventlog/rpc.c index 80312c4f4bd..e9db618fa7c 100644 --- a/reactos/base/services/eventlog/rpc.c +++ b/reactos/base/services/eventlog/rpc.c @@ -15,7 +15,7 @@ #define NDEBUG #include -LIST_ENTRY LogHandleListHead; +static LIST_ENTRY LogHandleListHead; /* FUNCTIONS ****************************************************************/ @@ -77,6 +77,8 @@ ElfCreateEventLogHandle(PLOGHANDLE* LogHandle, StringCchCopy(lpLogHandle->szName, i, LogName->Buffer); /* Get the number of Log Files the EventLog service found */ + // NOTE: We could just as well loop only once within the list of logs + // and retrieve what the code below that calls LogfListItemByIndex, does!! LogsActive = LogfListItemCount(); if (LogsActive == 0) { @@ -114,7 +116,6 @@ ElfCreateEventLogHandle(PLOGHANDLE* LogHandle, if (_wcsicmp(LogName->Buffer, currentLogFile->LogName) == 0) { lpLogHandle->LogFile = currentLogFile; - lpLogHandle->CurrentRecord = LogfGetOldestRecord(lpLogHandle->LogFile); break; } } @@ -123,16 +124,16 @@ ElfCreateEventLogHandle(PLOGHANDLE* LogHandle, if (lpLogHandle->LogFile == NULL) { lpLogHandle->LogFile = LogfListItemByName(L"Application"); - if (lpLogHandle->LogFile == NULL) { DPRINT1("Application log is missing!\n"); Status = STATUS_UNSUCCESSFUL; goto Done; } - - lpLogHandle->CurrentRecord = LogfGetOldestRecord(lpLogHandle->LogFile); } + + /* Reset the current record */ + lpLogHandle->CurrentRecord = 0; } if (!lpLogHandle->LogFile) @@ -189,8 +190,8 @@ ElfCreateBackupLogHandle(PLOGHANDLE* LogHandle, /* Set the backup flag */ lpLogHandle->Flags |= LOG_HANDLE_BACKUP_FILE; - /* Get the current record */ - lpLogHandle->CurrentRecord = LogfGetOldestRecord(lpLogHandle->LogFile); + /* Reset the current record */ + lpLogHandle->CurrentRecord = 0; Done: if (NT_SUCCESS(Status)) @@ -334,8 +335,17 @@ ElfrNumberOfRecords( lpLogFile->Header.OldestRecordNumber, lpLogFile->Header.CurrentRecordNumber); - *NumberOfRecords = lpLogFile->Header.CurrentRecordNumber - - lpLogFile->Header.OldestRecordNumber; + if (lpLogFile->Header.OldestRecordNumber == 0) + { + /* OldestRecordNumber == 0 when the log is empty */ + *NumberOfRecords = 0; + } + else + { + /* The log contains events */ + *NumberOfRecords = lpLogFile->Header.CurrentRecordNumber - + lpLogFile->Header.OldestRecordNumber; + } return STATUS_SUCCESS; } @@ -356,7 +366,7 @@ ElfrOldestRecord( if (!OldestRecordNumber) return STATUS_INVALID_PARAMETER; - *OldestRecordNumber = LogfGetOldestRecord(lpLogHandle->LogFile); + *OldestRecordNumber = lpLogHandle->LogFile->Header.OldestRecordNumber; return STATUS_SUCCESS; } @@ -466,9 +476,9 @@ ElfrReadELW( PULONG NumberOfBytesRead, PULONG MinNumberOfBytesNeeded) { + NTSTATUS Status; PLOGHANDLE lpLogHandle; - DWORD dwError; - DWORD RecordNumber; + ULONG RecordNumber; lpLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); if (!lpLogHandle) @@ -482,12 +492,12 @@ ElfrReadELW( { RecordNumber = lpLogHandle->CurrentRecord; } - else + else // (ReadFlags & EVENTLOG_SEEK_READ) { RecordNumber = RecordOffset; } - dwError = LogfReadEvent(lpLogHandle->LogFile, + Status = LogfReadEvents(lpLogHandle->LogFile, ReadFlags, &RecordNumber, NumberOfBytesToRead, @@ -497,27 +507,24 @@ ElfrReadELW( FALSE); /* Update the handle's CurrentRecord if success */ - if (dwError == ERROR_SUCCESS) + if (NT_SUCCESS(Status)) { lpLogHandle->CurrentRecord = RecordNumber; } - /* HACK!!! */ - if (dwError == ERROR_HANDLE_EOF) - return STATUS_END_OF_FILE; - - return I_RpcMapWin32Status(dwError); + return Status; } -/* Function 11 */ +/* Helper function for ElfrReportEventW/A and ElfrReportEventAndSourceW */ NTSTATUS -ElfrReportEventW( +ElfrIntReportEventW( IELF_HANDLE LogHandle, ULONG Time, USHORT EventType, USHORT EventCategory, ULONG EventID, + PRPC_UNICODE_STRING SourceName OPTIONAL, USHORT NumStrings, ULONG DataSize, PRPC_UNICODE_STRING ComputerName, @@ -528,16 +535,14 @@ ElfrReportEventW( PULONG RecordNumber, PULONG TimeWritten) { - USHORT i; - PBYTE LogBuffer; + NTSTATUS Status; PLOGHANDLE lpLogHandle; - DWORD lastRec; - DWORD recSize; - DWORD dwStringsSize = 0; - DWORD dwUserSidLength = 0; - DWORD dwError = ERROR_SUCCESS; - WCHAR *lpStrings; - int pos = 0; + PEVENTLOGRECORD LogBuffer; + USHORT i; + ULONG recSize; + ULONG dwStringsSize = 0; + ULONG dwUserSidLength = 0; + PWSTR lpStrings, str; lpLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); if (!lpLogHandle) @@ -547,8 +552,6 @@ ElfrReportEventW( if (Flags) return STATUS_INVALID_PARAMETER; - lastRec = LogfGetCurrentRecord(lpLogHandle->LogFile); - for (i = 0; i < NumStrings; i++) { switch (EventType) @@ -591,22 +594,26 @@ ElfrReportEventW( return STATUS_NO_MEMORY; } + str = lpStrings; for (i = 0; i < NumStrings; i++) { - RtlCopyMemory(lpStrings + pos, Strings[i]->Buffer, Strings[i]->Length); - pos += Strings[i]->Length / sizeof(WCHAR); - lpStrings[pos] = UNICODE_NULL; - pos += sizeof(UNICODE_NULL) / sizeof(WCHAR); + RtlCopyMemory(str, Strings[i]->Buffer, Strings[i]->Length); + str += Strings[i]->Length / sizeof(WCHAR); + *str = UNICODE_NULL; + str++; } if (UserSID) dwUserSidLength = FIELD_OFFSET(SID, SubAuthority[UserSID->SubAuthorityCount]); + LogBuffer = LogfAllocAndBuildNewRecord(&recSize, - lastRec, // FIXME! + Time, EventType, EventCategory, EventID, - lpLogHandle->szName, + (SourceName && SourceName->Buffer) + ? SourceName->Buffer + : lpLogHandle->szName, ComputerName->Buffer, dwUserSidLength, UserSID, @@ -615,17 +622,64 @@ ElfrReportEventW( DataSize, Data); - dwError = LogfWriteData(lpLogHandle->LogFile, recSize, LogBuffer); - if (!dwError) + Status = LogfWriteRecord(lpLogHandle->LogFile, recSize, LogBuffer); + if (!NT_SUCCESS(Status)) { - DPRINT1("ERROR WRITING TO EventLog %S\n", lpLogHandle->LogFile->FileName); + DPRINT1("ERROR WRITING TO EventLog %S (Status 0x%08lx)\n", + lpLogHandle->LogFile->FileName, Status); + } + + if (NT_SUCCESS(Status)) + { + /* Retrieve the two fields that were set by LogfWriteRecord into the record */ + if (RecordNumber) + *RecordNumber = LogBuffer->RecordNumber; + if (TimeWritten) + *TimeWritten = LogBuffer->TimeWritten; } LogfFreeRecord(LogBuffer); HeapFree(GetProcessHeap(), 0, lpStrings); - return I_RpcMapWin32Status(dwError); + return Status; +} + + +/* Function 11 */ +NTSTATUS +ElfrReportEventW( + IELF_HANDLE LogHandle, + ULONG Time, + USHORT EventType, + USHORT EventCategory, + ULONG EventID, + USHORT NumStrings, + ULONG DataSize, + PRPC_UNICODE_STRING ComputerName, + PRPC_SID UserSID, + PRPC_UNICODE_STRING Strings[], + PBYTE Data, + USHORT Flags, + PULONG RecordNumber, + PULONG TimeWritten) +{ + /* Call the helper function. The event source is provided via the log handle. */ + return ElfrIntReportEventW(LogHandle, + Time, + EventType, + EventCategory, + EventID, + NULL, + NumStrings, + DataSize, + ComputerName, + UserSID, + Strings, + Data, + Flags, + RecordNumber, + TimeWritten); } @@ -813,9 +867,9 @@ ElfrReadELA( PULONG NumberOfBytesRead, PULONG MinNumberOfBytesNeeded) { + NTSTATUS Status; PLOGHANDLE lpLogHandle; - DWORD dwError; - DWORD RecordNumber; + ULONG RecordNumber; lpLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); if (!lpLogHandle) @@ -829,12 +883,12 @@ ElfrReadELA( { RecordNumber = lpLogHandle->CurrentRecord; } - else + else // (ReadFlags & EVENTLOG_SEEK_READ) { RecordNumber = RecordOffset; } - dwError = LogfReadEvent(lpLogHandle->LogFile, + Status = LogfReadEvents(lpLogHandle->LogFile, ReadFlags, &RecordNumber, NumberOfBytesToRead, @@ -844,16 +898,12 @@ ElfrReadELA( TRUE); /* Update the handle's CurrentRecord if success */ - if (dwError == ERROR_SUCCESS) + if (NT_SUCCESS(Status)) { lpLogHandle->CurrentRecord = RecordNumber; } - /* HACK!!! */ - if (dwError == ERROR_HANDLE_EOF) - return STATUS_END_OF_FILE; - - return I_RpcMapWin32Status(dwError); + return Status; } @@ -904,7 +954,7 @@ ElfrReportEventA( if (NumStrings != 0) { - StringsArrayW = HeapAlloc(MyHeap, + StringsArrayW = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, NumStrings * sizeof(PUNICODE_STRING)); if (StringsArrayW == NULL) @@ -917,7 +967,7 @@ ElfrReportEventA( { if (Strings[i] != NULL) { - StringsArrayW[i] = HeapAlloc(MyHeap, + StringsArrayW[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(UNICODE_STRING)); if (StringsArrayW[i] == NULL) @@ -964,12 +1014,12 @@ Done: if (StringsArrayW[i]->Buffer) { RtlFreeUnicodeString(StringsArrayW[i]); - HeapFree(MyHeap, 0, StringsArrayW[i]); + HeapFree(GetProcessHeap(), 0, StringsArrayW[i]); } } } - HeapFree(MyHeap, 0, StringsArrayW); + HeapFree(GetProcessHeap(), 0, StringsArrayW); } RtlFreeUnicodeString(&ComputerNameW); @@ -1027,24 +1077,24 @@ ElfrGetLogInformation( switch (InfoLevel) { case EVENTLOG_FULL_INFO: + { + LPEVENTLOG_FULL_INFORMATION efi = (LPEVENTLOG_FULL_INFORMATION)Buffer; + + *pcbBytesNeeded = sizeof(EVENTLOG_FULL_INFORMATION); + if (cbBufSize < sizeof(EVENTLOG_FULL_INFORMATION)) { - LPEVENTLOG_FULL_INFORMATION efi = (LPEVENTLOG_FULL_INFORMATION)Buffer; - - *pcbBytesNeeded = sizeof(EVENTLOG_FULL_INFORMATION); - if (cbBufSize < sizeof(EVENTLOG_FULL_INFORMATION)) - { - Status = STATUS_BUFFER_TOO_SMALL; - 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; + Status = STATUS_BUFFER_TOO_SMALL; + 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; break; + } default: Status = STATUS_INVALID_LEVEL; @@ -1060,6 +1110,12 @@ NTSTATUS ElfrFlushEL( IELF_HANDLE LogHandle) { + PLOGHANDLE lpLogHandle; + + lpLogHandle = ElfGetLogHandleEntryByHandle(LogHandle); + if (!lpLogHandle) + return STATUS_INVALID_HANDLE; + UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } @@ -1084,8 +1140,22 @@ ElfrReportEventAndSourceW( PULONG RecordNumber, PULONG TimeWritten) { - UNIMPLEMENTED; - return STATUS_NOT_IMPLEMENTED; + /* Call the helper function. The event source is specified by the caller. */ + return ElfrIntReportEventW(LogHandle, + Time, + EventType, + EventCategory, + EventID, + SourceName, + NumStrings, + DataSize, + ComputerName, + UserSID, + Strings, + Data, + Flags, + RecordNumber, + TimeWritten); }