1128 lines
30 KiB
C
1128 lines
30 KiB
C
/*
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
FILE.C
|
|
|
|
Abstract:
|
|
|
|
This file contains the routines that deal with file-related operations.
|
|
|
|
Author:
|
|
|
|
Rajen Shah (rajens) 07-Aug-1991
|
|
|
|
Revision History:
|
|
|
|
29-Aug-1994 Danl
|
|
We no longer grow log files in place. So there is no need to
|
|
reserve the MaxConfigSize block of memory.
|
|
|
|
--*/
|
|
|
|
|
|
// INCLUDES
|
|
|
|
|
|
#include <eventp.h>
|
|
#include <alertmsg.h> // ALERT_ELF manifests
|
|
|
|
|
|
// Macros
|
|
|
|
|
|
#define IS_EOF(Ptr, Size) \
|
|
(Ptr)->Length == ELFEOFRECORDSIZE && \
|
|
RtlCompareMemory (Ptr, &EOFRecord, Size) == Size
|
|
|
|
#ifdef CORRUPTED
|
|
|
|
|
|
BOOLEAN
|
|
VerifyLogIntegrity(
|
|
PLOGFILE pLogFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine walks the log file to verify that it isn't corrupt
|
|
|
|
|
|
Arguments:
|
|
|
|
A pointer to the LOGFILE structure for the log to validate.
|
|
|
|
Return Value:
|
|
|
|
TRUE if log OK
|
|
FALSE if it is corrupt
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
|
|
PEVENTLOGRECORD pEventLogRecord;
|
|
PVOID PhysicalStart;
|
|
PVOID PhysicalEOF;
|
|
PVOID BeginRecord;
|
|
PVOID EndRecord;
|
|
|
|
pEventLogRecord =
|
|
(PEVENTLOGRECORD)((PBYTE)pLogFile->BaseAddress + pLogFile->BeginRecord);
|
|
PhysicalStart =
|
|
(PVOID)((PBYTE)pLogFile->BaseAddress + FILEHEADERBUFSIZE);
|
|
PhysicalEOF =
|
|
(PVOID)((PBYTE)pLogFile->BaseAddress + pLogFile->ViewSize);
|
|
BeginRecord = (PVOID)((PBYTE)pLogFile->BaseAddress + pLogFile->BeginRecord);
|
|
EndRecord = (PVOID)((PBYTE)pLogFile->BaseAddress + pLogFile->EndRecord);
|
|
|
|
while (pEventLogRecord->Length != ELFEOFRECORDSIZE) {
|
|
|
|
pEventLogRecord = (PEVENTLOGRECORD)NextRecordPosition(
|
|
EVENTLOG_FORWARDS_READ,
|
|
(PVOID)pEventLogRecord,
|
|
pEventLogRecord->Length,
|
|
BeginRecord,
|
|
EndRecord,
|
|
PhysicalEOF,
|
|
PhysicalStart
|
|
);
|
|
|
|
if (!pEventLogRecord || pEventLogRecord->Length == 0) {
|
|
|
|
ElfDbgPrintNC(("[ELF] The %ws logfile is corrupt\n",
|
|
pLogFile->LogModuleName->Buffer));
|
|
return(FALSE);
|
|
}
|
|
}
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
#endif // CORRUPTED
|
|
|
|
|
|
NTSTATUS
|
|
FlushLogFile(
|
|
PLOGFILE pLogFile
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine flushes the file specified. It updates the file header,
|
|
and then flushes the virtual memory which causes the data to get
|
|
written to disk.
|
|
|
|
Arguments:
|
|
|
|
pLogFile points to the log file structure.
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
PVOID BaseAddress;
|
|
SIZE_T RegionSize;
|
|
PELF_LOGFILE_HEADER pLogFileHeader;
|
|
|
|
|
|
// If the dirty bit is set, update the file header before flushing it.
|
|
|
|
if (pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY) {
|
|
|
|
pLogFileHeader = (PELF_LOGFILE_HEADER)pLogFile->BaseAddress;
|
|
|
|
pLogFile->Flags &= ~ELF_LOGFILE_HEADER_DIRTY; // Remove dirty bit
|
|
pLogFileHeader->Flags = pLogFile->Flags;
|
|
|
|
pLogFileHeader->StartOffset = pLogFile->BeginRecord;
|
|
pLogFileHeader->EndOffset = pLogFile->EndRecord;
|
|
pLogFileHeader->CurrentRecordNumber = pLogFile->CurrentRecordNumber;
|
|
pLogFileHeader->OldestRecordNumber = pLogFile->OldestRecordNumber;
|
|
}
|
|
|
|
|
|
// Flush the memory in the section that is mapped to the file.
|
|
|
|
BaseAddress = pLogFile->BaseAddress;
|
|
RegionSize = pLogFile->ViewSize;
|
|
|
|
Status = NtFlushVirtualMemory(
|
|
NtCurrentProcess(),
|
|
&BaseAddress,
|
|
&RegionSize,
|
|
&IoStatusBlock
|
|
);
|
|
|
|
return (Status);
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ElfpFlushFiles(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine flushes all the log files and forces them on disk.
|
|
It is usually called in preparation for a shutdown or a pause.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PLOGFILE pLogFile;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
|
|
// Make sure that there's at least one file to flush
|
|
|
|
|
|
if (IsListEmpty(&LogFilesHead)) {
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
pLogFile
|
|
= (PLOGFILE)
|
|
CONTAINING_RECORD(LogFilesHead.Flink, LOGFILE, FileList);
|
|
|
|
|
|
// Go through this loop at least once. This ensures that the termination
|
|
// condition will work correctly.
|
|
|
|
do {
|
|
|
|
Status = FlushLogFile(pLogFile);
|
|
|
|
pLogFile = // Get next one
|
|
(PLOGFILE)
|
|
CONTAINING_RECORD(pLogFile->FileList.Flink, LOGFILE, FileList);
|
|
|
|
} while ((pLogFile->FileList.Flink != LogFilesHead.Flink)
|
|
&& (NT_SUCCESS(Status)));
|
|
|
|
return (Status);
|
|
}
|
|
|
|
|
|
|
|
|
|
NTSTATUS
|
|
ElfpCloseLogFile(
|
|
PLOGFILE pLogFile,
|
|
DWORD Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine undoes whatever is done in ElfOpenLogFile.
|
|
|
|
Arguments:
|
|
|
|
pLogFile points to the log file structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
Note:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PELF_LOGFILE_HEADER pLogFileHeader;
|
|
PVOID BaseAddress;
|
|
ULONG Size;
|
|
|
|
ElfDbgPrint(("[ELF] Closing and unmapping log file: %ws\n",
|
|
pLogFile->LogFileName->Buffer));
|
|
|
|
#ifdef CORRUPTED
|
|
|
|
|
|
// Just for debugging a log corruption problem
|
|
|
|
|
|
if (!VerifyLogIntegrity(pLogFile)) {
|
|
ElfDbgPrintNC(("[ELF] Integrity check failed in ElfpCloseLogFile\n"));
|
|
}
|
|
|
|
#endif // CORRUPTED
|
|
|
|
|
|
// If the dirty bit is set, update the file header before closing it.
|
|
// Check to be sure it's not a backup file that just had the dirty
|
|
// bit set when it was copied.
|
|
|
|
|
|
if (pLogFile->Flags & ELF_LOGFILE_HEADER_DIRTY &&
|
|
!(Flags & ELF_LOG_CLOSE_BACKUP)) {
|
|
pLogFileHeader = (PELF_LOGFILE_HEADER)pLogFile->BaseAddress;
|
|
pLogFileHeader->StartOffset = pLogFile->BeginRecord;
|
|
pLogFileHeader->EndOffset = pLogFile->EndRecord;
|
|
pLogFileHeader->CurrentRecordNumber = pLogFile->CurrentRecordNumber;
|
|
pLogFileHeader->OldestRecordNumber = pLogFile->OldestRecordNumber;
|
|
pLogFile->Flags &= ~(ELF_LOGFILE_HEADER_DIRTY |
|
|
ELF_LOGFILE_ARCHIVE_SET); // Remove dirty &
|
|
// archive bits
|
|
pLogFileHeader->Flags = pLogFile->Flags;
|
|
}
|
|
|
|
|
|
// Decrement the reference count, and if it goes to zero, unmap the file
|
|
// and close the handle. Also force the close if fForceClosed is TRUE.
|
|
|
|
|
|
if ((--pLogFile->RefCount == 0) || Flags & ELF_LOG_CLOSE_FORCE) {
|
|
|
|
|
|
// Last user has gone.
|
|
// Close all the views and the file and section handles. Free up
|
|
// any extra memory we had reserved, and unlink any structures
|
|
|
|
|
|
if (pLogFile->BaseAddress) // Unmap it if it was allocated
|
|
NtUnmapViewOfSection(
|
|
NtCurrentProcess(),
|
|
pLogFile->BaseAddress
|
|
);
|
|
|
|
if (pLogFile->SectionHandle)
|
|
NtClose(pLogFile->SectionHandle);
|
|
|
|
if (pLogFile->FileHandle)
|
|
NtClose(pLogFile->FileHandle);
|
|
}
|
|
|
|
return (Status);
|
|
|
|
} // ElfpCloseLogFile
|
|
|
|
|
|
NTSTATUS
|
|
RevalidateLogHeader(
|
|
PELF_LOGFILE_HEADER pLogFileHeader,
|
|
PLOGFILE pLogFile
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called if we encounter a "dirty" log file. The
|
|
routine walks through the file until it finds a signature for a valid log
|
|
record. It then walks forward thru the file until it finds the EOF
|
|
record, or an invalid record. Then it walks backwards from the first
|
|
record it found until it finds the EOF record from the other direction.
|
|
It then rebuilds the header and writes it back to the log. If it can't
|
|
find any valid records, it rebuilds the header to reflect an empty log
|
|
file. If it finds a trashed file, it writes 256 bytes of the log out in
|
|
an event to the system log.
|
|
|
|
Arguments:
|
|
|
|
pLogFileHeader points to the header of the log file.
|
|
pLogFile points to the log file structure.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
Note:
|
|
|
|
This is an expensive routine since it scans the entire file.
|
|
|
|
It assumes that the records are on DWORD boundaries.
|
|
|
|
--*/
|
|
{
|
|
PVOID Start, End;
|
|
PDWORD pSignature;
|
|
PEVENTLOGRECORD pEvent;
|
|
PEVENTLOGRECORD FirstRecord;
|
|
PEVENTLOGRECORD FirstPhysicalRecord;
|
|
PEVENTLOGRECORD pLastGoodRecord = NULL;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER ByteOffset;
|
|
SIZE_T Size;
|
|
|
|
ElfDbgPrint(("[ELF] Log file had dirty bit set, revalidating\n"));
|
|
|
|
try {
|
|
|
|
|
|
// Physical start and end of log file (skipping header)
|
|
|
|
|
|
Start = (PVOID)((PBYTE)pLogFile->BaseAddress + FILEHEADERBUFSIZE);
|
|
End = (PVOID)((PBYTE)pLogFile->BaseAddress +
|
|
pLogFile->ActualMaxFileSize);
|
|
|
|
|
|
// First see if the log has wrapped. The EOFRECORDSIZE is for the one
|
|
// case where the EOF record wraps so that it's final length just replaces
|
|
// the next records starting length
|
|
|
|
|
|
pEvent = (PEVENTLOGRECORD)Start;
|
|
|
|
if (pEvent->Reserved != ELF_LOG_FILE_SIGNATURE
|
|
||
|
|
pEvent->RecordNumber != 1
|
|
||
|
|
pEvent->Length == ELFEOFRECORDSIZE) {
|
|
|
|
|
|
// Log has already wrapped, go looking for the first valid record
|
|
|
|
|
|
for (pSignature = (PDWORD)Start;
|
|
(PVOID)pSignature < End;
|
|
pSignature++) {
|
|
if (*pSignature == ELF_LOG_FILE_SIGNATURE) {
|
|
|
|
|
|
// Make sure it's really a record
|
|
|
|
|
|
pEvent = CONTAINING_RECORD(pSignature, EVENTLOGRECORD, Reserved);
|
|
|
|
if (!ValidFilePos(pEvent, Start, End, End, pLogFileHeader, TRUE)) {
|
|
|
|
// Nope, not really, keep trying
|
|
|
|
|
|
continue;
|
|
}
|
|
|
|
|
|
// This is a valid record, Remember this so you can use
|
|
// it later
|
|
|
|
|
|
FirstPhysicalRecord = pEvent;
|
|
|
|
|
|
// Walk backwards from here (wrapping if necessary) until
|
|
// you hit the EOF record or an invalid record.
|
|
|
|
|
|
while (pEvent
|
|
&&
|
|
ValidFilePos(pEvent, Start, End, End, pLogFileHeader, TRUE)) {
|
|
|
|
// See if it's the EOF record
|
|
|
|
|
|
if (IS_EOF(pEvent,
|
|
min(ELFEOFUNIQUEPART,
|
|
(ULONG_PTR)((PBYTE)End - (PBYTE)pEvent)))) {
|
|
break;
|
|
}
|
|
|
|
pLastGoodRecord = pEvent;
|
|
pEvent = NextRecordPosition(
|
|
EVENTLOG_SEQUENTIAL_READ |
|
|
EVENTLOG_BACKWARDS_READ,
|
|
pEvent,
|
|
pEvent->Length,
|
|
0,
|
|
0,
|
|
End,
|
|
Start);
|
|
|
|
|
|
// Make sure we're not in an infinite loop
|
|
|
|
|
|
if (pEvent == FirstPhysicalRecord) {
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
}
|
|
|
|
|
|
// Found the first record, now go look for the last
|
|
|
|
|
|
FirstRecord = pLastGoodRecord;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pSignature == End || pLastGoodRecord == NULL) {
|
|
|
|
// Either there were no valid records in the file or
|
|
// the only valid record was the EOF record (which
|
|
// means the log is trashed anyhow). Give up
|
|
// and we'll set it to an empty log file.
|
|
|
|
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
} else {
|
|
|
|
|
|
// We haven't wrapped yet
|
|
|
|
|
|
FirstPhysicalRecord = FirstRecord = Start;
|
|
}
|
|
|
|
|
|
|
|
// Now read forward looking for the last good record
|
|
|
|
|
|
pEvent = FirstPhysicalRecord;
|
|
|
|
while (pEvent && ValidFilePos(pEvent, Start, End, End, pLogFileHeader, TRUE)) {
|
|
|
|
|
|
// See if it's the EOF record
|
|
|
|
|
|
if (IS_EOF(pEvent, min(ELFEOFUNIQUEPART,
|
|
(ULONG_PTR)((PBYTE)End - (PBYTE)pEvent)))) {
|
|
|
|
break;
|
|
}
|
|
|
|
pLastGoodRecord = pEvent;
|
|
pEvent = NextRecordPosition(
|
|
EVENTLOG_SEQUENTIAL_READ | EVENTLOG_FORWARDS_READ,
|
|
pEvent,
|
|
pEvent->Length,
|
|
0,
|
|
0,
|
|
End,
|
|
Start);
|
|
|
|
|
|
// Make sure we're not in an infinite loop
|
|
|
|
|
|
if (pEvent == FirstPhysicalRecord) {
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
}
|
|
|
|
|
|
// Now we know the first record (FirstRecord) and the last record
|
|
// (pLastGoodRecord) so we can create the header an EOF record and
|
|
// write them out (EOF record gets written at pEvent)
|
|
|
|
// First the EOF record
|
|
|
|
|
|
EOFRecord.BeginRecord = (ULONG)((PBYTE)FirstRecord - (PBYTE)pLogFileHeader);
|
|
EOFRecord.EndRecord = (ULONG)((PBYTE)pEvent - (PBYTE)pLogFileHeader);
|
|
EOFRecord.CurrentRecordNumber =
|
|
pLastGoodRecord->RecordNumber + 1;
|
|
EOFRecord.OldestRecordNumber = FirstRecord->RecordNumber;
|
|
|
|
ByteOffset = RtlConvertUlongToLargeInteger(
|
|
(ULONG)((PBYTE)pEvent - (PBYTE)pLogFileHeader));
|
|
|
|
|
|
// If the EOF record was wrapped, we can't write out the entire record at
|
|
// once. Instead, we'll write out as much as we can and then write the
|
|
// rest out at the beginning of the log
|
|
|
|
|
|
Size = min((PBYTE)End - (PBYTE)pEvent, ELFEOFRECORDSIZE);
|
|
|
|
Status = NtWriteFile(
|
|
pLogFile->FileHandle, // Filehandle
|
|
NULL, // Event
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
&IoStatusBlock, // IO_STATUS_BLOCK
|
|
&EOFRecord, // Buffer
|
|
(ULONG)Size, // Length
|
|
&ByteOffset, // Byteoffset
|
|
NULL); // Key
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
|
|
Status));
|
|
return (Status);
|
|
}
|
|
|
|
if (Size != ELFEOFRECORDSIZE) {
|
|
|
|
PBYTE pBuff;
|
|
|
|
pBuff = (PBYTE)&EOFRecord + Size;
|
|
Size = ELFEOFRECORDSIZE - Size;
|
|
ByteOffset = RtlConvertUlongToLargeInteger(FILEHEADERBUFSIZE);
|
|
|
|
|
|
// Make absolutely sure we have enough room to write the remainder of
|
|
// the EOF record. Note that this should always be the case since the
|
|
// record was wrapped around to begin with. To do this, make sure
|
|
// that the number of bytes we're writing after the header is <= the
|
|
// offset of the first record from the end of the header
|
|
|
|
|
|
ASSERT(Size <= (ULONG)((PBYTE)FirstRecord
|
|
- (PBYTE)pLogFileHeader
|
|
- FILEHEADERBUFSIZE));
|
|
|
|
Status = NtWriteFile(
|
|
pLogFile->FileHandle, // Filehandle
|
|
NULL, // Event
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
&IoStatusBlock, // IO_STATUS_BLOCK
|
|
pBuff, // Buffer
|
|
(ULONG)Size, // Length
|
|
&ByteOffset, // Byteoffset
|
|
NULL); // Key
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ElfDbgPrint(("[ELF]: Write of remainder of log file header failed 0x%lx\n",
|
|
Status));
|
|
|
|
return (Status);
|
|
}
|
|
}
|
|
|
|
|
|
// Now the header
|
|
|
|
|
|
pLogFileHeader->StartOffset = (ULONG)((PBYTE)FirstRecord - (PBYTE)pLogFileHeader);
|
|
pLogFileHeader->EndOffset = (ULONG)((PBYTE)pEvent - (PBYTE)pLogFileHeader);
|
|
pLogFileHeader->CurrentRecordNumber =
|
|
pLastGoodRecord->RecordNumber + 1;
|
|
pLogFileHeader->OldestRecordNumber = FirstRecord->RecordNumber;
|
|
pLogFileHeader->HeaderSize = pLogFileHeader->EndHeaderSize = FILEHEADERBUFSIZE;
|
|
pLogFileHeader->Signature = ELF_LOG_FILE_SIGNATURE;
|
|
pLogFileHeader->Flags = 0;
|
|
|
|
if (pLogFileHeader->StartOffset != FILEHEADERBUFSIZE)
|
|
pLogFileHeader->Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
|
|
pLogFileHeader->MaxSize = pLogFile->ActualMaxFileSize;
|
|
pLogFileHeader->Retention = pLogFile->Retention;
|
|
pLogFileHeader->MajorVersion = ELF_VERSION_MAJOR;
|
|
pLogFileHeader->MinorVersion = ELF_VERSION_MINOR;
|
|
|
|
|
|
// Now flush this to disk to commit it
|
|
|
|
|
|
Start = pLogFile->BaseAddress;
|
|
Size = FILEHEADERBUFSIZE;
|
|
|
|
Status = NtFlushVirtualMemory(
|
|
NtCurrentProcess(),
|
|
&Start,
|
|
&Size,
|
|
&IoStatusBlock
|
|
);
|
|
|
|
}
|
|
except(EXCEPTION_EXECUTE_HANDLER) {
|
|
return(STATUS_UNSUCCESSFUL);
|
|
}
|
|
|
|
return (Status);
|
|
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ElfOpenLogFile(
|
|
PLOGFILE pLogFile,
|
|
ELF_LOG_TYPE LogType
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open the log file, create it if it does not exist.
|
|
Create a section and map a view into the log file.
|
|
Write out the header to the file, if it is newly created.
|
|
If "dirty", update the "start" and "end" pointers by scanning
|
|
the file. Set AUTOWRAP if the "start" does not start right after
|
|
the file header.
|
|
|
|
Arguments:
|
|
|
|
pLogFile points to the log file structure, with the relevant data
|
|
filled in.
|
|
|
|
CreateOptions are the options to be passed to NtCreateFile which
|
|
indicate whether to open an existing file, or to create it
|
|
if it does not exist.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
Note:
|
|
|
|
It is up to the caller to set the RefCount in the Logfile structure.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER MaximumSizeOfSection;
|
|
LARGE_INTEGER ByteOffset;
|
|
PELF_LOGFILE_HEADER pLogFileHeader;
|
|
FILE_STANDARD_INFORMATION FileStandardInfo;
|
|
ULONG IoStatusInformation;
|
|
ULONG FileDesiredAccess;
|
|
ULONG SectionDesiredAccess;
|
|
ULONG SectionPageProtection;
|
|
ULONG CreateOptions;
|
|
ULONG CreateDisposition;
|
|
SIZE_T ViewSize;
|
|
|
|
|
|
// File header in a new file has the "Start" and "End" pointers the
|
|
// same since there are no records in the file.
|
|
|
|
|
|
static ELF_LOGFILE_HEADER FileHeaderBuf = {FILEHEADERBUFSIZE, // Size
|
|
ELF_LOG_FILE_SIGNATURE,
|
|
ELF_VERSION_MAJOR,
|
|
ELF_VERSION_MINOR,
|
|
FILEHEADERBUFSIZE, // Start offset
|
|
FILEHEADERBUFSIZE, // End offset
|
|
1, // Next record #
|
|
0, // Oldest record #
|
|
0, // Maxsize
|
|
0, // Flags
|
|
0, // Retention
|
|
FILEHEADERBUFSIZE // Size
|
|
};
|
|
|
|
|
|
// Set the file open and section create options based on the type of log
|
|
// file this is.
|
|
|
|
|
|
switch (LogType) {
|
|
|
|
case ElfNormalLog:
|
|
CreateDisposition = FILE_OPEN_IF;
|
|
FileDesiredAccess = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
|
|
SectionDesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE |
|
|
SECTION_QUERY | SECTION_EXTEND_SIZE;
|
|
SectionPageProtection = PAGE_READWRITE;
|
|
CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
|
|
break;
|
|
|
|
case ElfSecurityLog:
|
|
CreateDisposition = FILE_OPEN_IF;
|
|
FileDesiredAccess = GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE;
|
|
SectionDesiredAccess = SECTION_MAP_READ | SECTION_MAP_WRITE |
|
|
SECTION_QUERY | SECTION_EXTEND_SIZE;
|
|
SectionPageProtection = PAGE_READWRITE;
|
|
CreateOptions = FILE_WRITE_THROUGH | FILE_SYNCHRONOUS_IO_NONALERT;
|
|
break;
|
|
|
|
case ElfBackupLog:
|
|
CreateDisposition = FILE_OPEN;
|
|
FileDesiredAccess = GENERIC_READ | SYNCHRONIZE;
|
|
SectionDesiredAccess = SECTION_MAP_READ | SECTION_QUERY;
|
|
SectionPageProtection = PAGE_READONLY;
|
|
CreateOptions = FILE_SYNCHRONOUS_IO_NONALERT;
|
|
break;
|
|
|
|
}
|
|
|
|
ElfDbgPrint(("[ELF] Opening and mapping %ws\n",
|
|
pLogFile->LogFileName->Buffer));
|
|
|
|
if (pLogFile->FileHandle != NULL) {
|
|
|
|
|
|
// The log file is already in use. Do not reopen or remap it.
|
|
|
|
|
|
ElfDbgPrint(("[ELF] Log file already in use by another module\n"));
|
|
|
|
} else {
|
|
|
|
|
|
// Initialize the logfile structure so that it is easier to clean
|
|
// up.
|
|
|
|
|
|
pLogFile->ActualMaxFileSize = ELF_DEFAULT_LOG_SIZE;
|
|
pLogFile->Flags = 0;
|
|
pLogFile->BaseAddress = NULL;
|
|
pLogFile->SectionHandle = NULL;
|
|
|
|
|
|
|
|
// Set up the object attributes structure for the Log File
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
pLogFile->LogFileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
|
|
// Open the Log File. Create it if it does not exist and it's not
|
|
// being opened as a backup file. If creating, create a file of
|
|
// the maximum size configured.
|
|
|
|
|
|
MaximumSizeOfSection =
|
|
RtlConvertUlongToLargeInteger(ELF_DEFAULT_LOG_SIZE);
|
|
|
|
Status = NtCreateFile(
|
|
&pLogFile->FileHandle,
|
|
FileDesiredAccess,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
&MaximumSizeOfSection,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ,
|
|
CreateDisposition,
|
|
CreateOptions,
|
|
NULL,
|
|
0);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrint(("[ELF] Log File Open Failed 0x%lx\n", Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
// If the file already existed, get its size and use that as the
|
|
// actual size of the file.
|
|
|
|
|
|
IoStatusInformation = (ULONG)IoStatusBlock.Information; // Save it away
|
|
|
|
if (!(IoStatusInformation & FILE_CREATED)) {
|
|
ElfDbgPrint(("[Elf] Log file exists.\n"));
|
|
|
|
Status = NtQueryInformationFile(
|
|
pLogFile->FileHandle,
|
|
&IoStatusBlock,
|
|
&FileStandardInfo,
|
|
sizeof(FileStandardInfo),
|
|
FileStandardInformation
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrint(("[ELF] QueryInformation failed 0x%lx\n", Status));
|
|
goto cleanup;
|
|
} else {
|
|
|
|
|
|
ElfDbgPrint(("[ELF] Use existing log file size: 0x%lx:%lx\n",
|
|
FileStandardInfo.EndOfFile.HighPart,
|
|
FileStandardInfo.EndOfFile.LowPart
|
|
));
|
|
|
|
MaximumSizeOfSection.LowPart =
|
|
FileStandardInfo.EndOfFile.LowPart;
|
|
MaximumSizeOfSection.HighPart =
|
|
FileStandardInfo.EndOfFile.HighPart;
|
|
|
|
|
|
// Make sure that the high DWORD of the file size is ZERO.
|
|
|
|
|
|
ASSERT(MaximumSizeOfSection.HighPart == 0);
|
|
|
|
|
|
// If the filesize if 0, set it to the minimum size
|
|
|
|
|
|
if (MaximumSizeOfSection.LowPart == 0) {
|
|
MaximumSizeOfSection.LowPart = ELF_DEFAULT_LOG_SIZE;
|
|
}
|
|
|
|
|
|
// Set actual size of file
|
|
|
|
|
|
pLogFile->ActualMaxFileSize =
|
|
MaximumSizeOfSection.LowPart;
|
|
|
|
|
|
// If the size of the log file is reduced, a clear must
|
|
// happen for this to take effect
|
|
|
|
|
|
if (pLogFile->ActualMaxFileSize > pLogFile->ConfigMaxFileSize) {
|
|
pLogFile->ConfigMaxFileSize = pLogFile->ActualMaxFileSize;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Create a section mapped to the Log File just opened
|
|
|
|
|
|
Status = NtCreateSection(
|
|
&pLogFile->SectionHandle,
|
|
SectionDesiredAccess,
|
|
NULL,
|
|
&MaximumSizeOfSection,
|
|
SectionPageProtection,
|
|
SEC_COMMIT,
|
|
pLogFile->FileHandle
|
|
);
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ElfDbgPrintNC(("[ELF] Log Mem Section Create Failed 0x%lx\n",
|
|
Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
// Map a view of the Section into the eventlog address space
|
|
|
|
|
|
ViewSize = 0;
|
|
|
|
Status = NtMapViewOfSection(pLogFile->SectionHandle, NtCurrentProcess(), &pLogFile->BaseAddress, 0, 0, NULL, &ViewSize, ViewUnmap, 0, SectionPageProtection);
|
|
pLogFile->ViewSize = (ULONG)ViewSize;
|
|
if (!NT_SUCCESS(Status)) {
|
|
ElfDbgPrintNC(("[ELF] Log Mem Sect Map View failed 0x%lx\n", Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
// If the file was just created, write out the file header.
|
|
|
|
|
|
if (IoStatusInformation & FILE_CREATED) {
|
|
ElfDbgPrint(("[ELF] File was created\n"));
|
|
JustCreated:
|
|
FileHeaderBuf.MaxSize = pLogFile->ActualMaxFileSize;
|
|
FileHeaderBuf.Flags = 0;
|
|
FileHeaderBuf.Retention = pLogFile->Retention;
|
|
|
|
|
|
// Copy the header into the file
|
|
|
|
|
|
ByteOffset = RtlConvertUlongToLargeInteger(0);
|
|
Status = NtWriteFile(
|
|
pLogFile->FileHandle, // Filehandle
|
|
NULL, // Event
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
&IoStatusBlock, // IO_STATUS_BLOCK
|
|
&FileHeaderBuf, // Buffer
|
|
FILEHEADERBUFSIZE, // Length
|
|
&ByteOffset, // Byteoffset
|
|
NULL); // Key
|
|
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
|
|
Status));
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
// Copy the "EOF" record right after the header
|
|
|
|
|
|
ByteOffset = RtlConvertUlongToLargeInteger(FILEHEADERBUFSIZE);
|
|
Status = NtWriteFile(
|
|
pLogFile->FileHandle, // Filehandle
|
|
NULL, // Event
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
&IoStatusBlock, // IO_STATUS_BLOCK
|
|
&EOFRecord, // Buffer
|
|
ELFEOFRECORDSIZE, // Length
|
|
&ByteOffset, // Byteoffset
|
|
NULL); // Key
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
|
|
ElfDbgPrint(("[ELF]: Log file header write failed 0x%lx\n",
|
|
Status));
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
// Check to ensure that this is a valid log file. We look at the
|
|
// size of the header and the signature to see if they match, as
|
|
// well as checking the version number.
|
|
|
|
|
|
pLogFileHeader = (PELF_LOGFILE_HEADER)(pLogFile->BaseAddress);
|
|
|
|
if ((pLogFileHeader->HeaderSize != FILEHEADERBUFSIZE)
|
|
|| (pLogFileHeader->EndHeaderSize != FILEHEADERBUFSIZE)
|
|
|| (pLogFileHeader->Signature != ELF_LOG_FILE_SIGNATURE)
|
|
|| (pLogFileHeader->MajorVersion != ELF_VERSION_MAJOR)
|
|
|| (pLogFileHeader->MinorVersion != ELF_VERSION_MINOR)) {
|
|
|
|
|
|
// This file is corrupt, reset it to an empty log, unless
|
|
// it's being opened as a backup file, if it is, fail the
|
|
// open
|
|
|
|
|
|
ElfDbgPrint(("[ELF] Invalid file header found\n"));
|
|
|
|
if (LogType == ElfBackupLog) {
|
|
|
|
Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
goto cleanup;
|
|
} else {
|
|
ElfpCreateQueuedAlert(ALERT_ELF_LogFileCorrupt, 1,
|
|
&pLogFile->LogModuleName->Buffer);
|
|
|
|
// Treat it like it was just created
|
|
|
|
|
|
goto JustCreated;
|
|
}
|
|
} else {
|
|
|
|
|
|
// If the "dirty" bit is set in the file header, then we need to
|
|
// revalidate the BeginRecord and EndRecord fields since we did not
|
|
// get a chance to write them out before the system was rebooted.
|
|
// If the dirty bit is set and it's a backup file, just fail the
|
|
// open.
|
|
|
|
|
|
if (pLogFileHeader->Flags & ELF_LOGFILE_HEADER_DIRTY) {
|
|
|
|
if (LogType == ElfBackupLog) {
|
|
|
|
Status = STATUS_EVENTLOG_FILE_CORRUPT;
|
|
goto cleanup;
|
|
} else {
|
|
Status = RevalidateLogHeader(pLogFileHeader, pLogFile);
|
|
}
|
|
}
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
|
|
// Set the beginning and end record positions in our
|
|
// data structure as well as the wrap flag if appropriate.
|
|
|
|
|
|
pLogFile->EndRecord = pLogFileHeader->EndOffset;
|
|
pLogFile->BeginRecord = pLogFileHeader->StartOffset;
|
|
if (pLogFileHeader->Flags & ELF_LOGFILE_HEADER_WRAP) {
|
|
pLogFile->Flags |= ELF_LOGFILE_HEADER_WRAP;
|
|
}
|
|
|
|
ElfDbgPrint(("[ELF] BeginRecord: 0x%lx EndRecord: 0x%lx \n",
|
|
pLogFile->BeginRecord, pLogFile->EndRecord));
|
|
} else {
|
|
|
|
|
|
// Couldn't validate the file, treat it like it was just
|
|
// created (turn it into an empty file)
|
|
|
|
|
|
goto JustCreated;
|
|
}
|
|
|
|
#ifdef CORRUPTED
|
|
|
|
|
|
// Just for debugging a log corruption problem
|
|
|
|
|
|
if (!VerifyLogIntegrity(pLogFile)) {
|
|
ElfDbgPrintNC(("[ELF] Integrity check failed in ElfOpenLogFile\n"));
|
|
}
|
|
#endif // CORRUPTED
|
|
|
|
}
|
|
|
|
|
|
// Fill in the first and last record number values in the LogFile
|
|
// data structure.
|
|
|
|
//SS:save the record number of the first record in this session
|
|
//so that if the cluster service starts after the eventlog service
|
|
//it will be able to forward the pending records for replication
|
|
//when the cluster service registers
|
|
pLogFile->SessionStartRecordNumber = pLogFileHeader->CurrentRecordNumber;
|
|
pLogFile->CurrentRecordNumber = pLogFileHeader->CurrentRecordNumber;
|
|
pLogFile->OldestRecordNumber = pLogFileHeader->OldestRecordNumber;
|
|
|
|
}
|
|
|
|
return (Status);
|
|
|
|
cleanup:
|
|
|
|
// Clean up anything that got allocated
|
|
|
|
if (pLogFile->ViewSize) {
|
|
NtUnmapViewOfSection(NtCurrentProcess(), pLogFile->BaseAddress);
|
|
}
|
|
|
|
if (pLogFile->SectionHandle) {
|
|
NtClose(pLogFile->SectionHandle);
|
|
}
|
|
|
|
if (pLogFile->FileHandle) {
|
|
NtClose(pLogFile->FileHandle);
|
|
}
|
|
|
|
return (Status);
|
|
|
|
}
|