mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1088 lines
31 KiB
1088 lines
31 KiB
/*
|
|
|
|
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;
|
|
ULONG 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;
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
LARGE_INTEGER ByteOffset;
|
|
ULONG 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)) {
|
|
//
|
|
// 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)) {
|
|
|
|
//
|
|
// See if it's the EOF record
|
|
//
|
|
|
|
if (IS_EOF(pEvent,
|
|
min (ELFEOFUNIQUEPART,
|
|
(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) {
|
|
|
|
//
|
|
// We couldn't find one valid record in the whole log. 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)) {
|
|
|
|
//
|
|
// See if it's the EOF record
|
|
//
|
|
|
|
if (IS_EOF(pEvent, min (ELFEOFUNIQUEPART,
|
|
(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 = (PBYTE) FirstRecord - (PBYTE) pLogFileHeader;
|
|
EOFRecord.EndRecord = (PBYTE) pEvent - (PBYTE) pLogFileHeader;
|
|
EOFRecord.CurrentRecordNumber =
|
|
pLastGoodRecord->RecordNumber + 1;
|
|
EOFRecord.OldestRecordNumber = FirstRecord->RecordNumber;
|
|
|
|
ByteOffset = RtlConvertUlongToLargeInteger (
|
|
(PBYTE) pEvent - (PBYTE) pLogFileHeader);
|
|
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));
|
|
return (Status);
|
|
}
|
|
|
|
//
|
|
// Now the header
|
|
//
|
|
|
|
pLogFileHeader->StartOffset = (PBYTE) FirstRecord- (PBYTE) pLogFileHeader;
|
|
pLogFileHeader->EndOffset = (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;
|
|
|
|
//
|
|
// 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 = 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
|
|
//
|
|
|
|
pLogFile->ViewSize = 0; // Initialize value to zero
|
|
|
|
Status = NtMapViewOfSection(
|
|
pLogFile->SectionHandle,
|
|
NtCurrentProcess(),
|
|
&pLogFile->BaseAddress,
|
|
0,
|
|
0,
|
|
NULL,
|
|
&pLogFile->ViewSize,
|
|
ViewUnmap,
|
|
0,
|
|
SectionPageProtection);
|
|
|
|
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.
|
|
//
|
|
|
|
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);
|
|
|
|
}
|