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.
1035 lines
25 KiB
1035 lines
25 KiB
/*++
|
|
|
|
Copyright (c) 1997, 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
silog.c
|
|
|
|
Abstract:
|
|
|
|
Logging support for the single instance store
|
|
|
|
Authors:
|
|
|
|
Bill Bolosky, Summer, 1997
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "sip.h"
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, SipComputeChecksum)
|
|
#pragma alloc_text(PAGE, SipDrainLogFile)
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
|
|
#if DBG
|
|
VOID
|
|
SipDBGDumpLogRecord(
|
|
PSIS_LOG_HEADER header)
|
|
{
|
|
DbgPrint("log record: type %d, size %d, index 0x%x.0x%x\n",
|
|
header->Type,
|
|
header->Size,
|
|
header->Index.HighPart,
|
|
header->Index.LowPart);
|
|
|
|
switch (header->Type) {
|
|
}
|
|
}
|
|
#endif // DBG
|
|
|
|
NTSTATUS
|
|
SipMakeLogEntry(
|
|
IN OUT PDEVICE_EXTENSION deviceExtension,
|
|
IN USHORT type,
|
|
IN USHORT size,
|
|
IN PVOID record)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Make an entry in the SIS log. Creates the header, computes the
|
|
checksum and then writes the log entry to the log file for this
|
|
volume. A successful return guarantees that the log record is
|
|
flushed to disk. This routine blocks.
|
|
|
|
Arguments:
|
|
|
|
deviceExtension - the device extension for the volume onto which we're
|
|
logging.
|
|
|
|
type - the type of the record we're writing.
|
|
|
|
size - the size of the record we're writing (not counting the header)
|
|
|
|
record - the log record data to write to the file.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCCESS or an error returned from the actual disk write.
|
|
--*/
|
|
{
|
|
#if ENABLE_LOGGING
|
|
PSIS_LOG_HEADER header = NULL;
|
|
NTSTATUS status;
|
|
PIRP irp;
|
|
KEVENT event[1];
|
|
PIO_STACK_LOCATION irpSp;
|
|
BOOLEAN mutantAcquired = FALSE;
|
|
IO_STATUS_BLOCK Iosb[1];
|
|
|
|
|
|
if (deviceExtension->LogFileHandle == NULL) {
|
|
SIS_MARK_POINT();
|
|
return STATUS_DRIVER_INTERNAL_ERROR;
|
|
}
|
|
|
|
header = ExAllocatePoolWithTag(PagedPool, size + sizeof(SIS_LOG_HEADER), ' siS');
|
|
|
|
if (!header) {
|
|
SIS_MARK_POINT();
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
ASSERT(size % 4 == 0); // The log drain code relies on this
|
|
|
|
header->Magic = SIS_LOG_HEADER_MAGIC;
|
|
header->Type = type;
|
|
header->Size = size + sizeof(SIS_LOG_HEADER);
|
|
|
|
status = SipAllocateIndex(deviceExtension, &header->Index);
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Copy the log record into the newly alloated header+record area.
|
|
//
|
|
RtlMoveMemory(header + 1, record, size);
|
|
|
|
//
|
|
// Compute the checksum. We need to set the checksum field in the header
|
|
// to 0 before we do the computation so that whatever's there isn't
|
|
// part of the checksum (and then overwritten with the real checksum).
|
|
//
|
|
header->Checksum.QuadPart = 0;
|
|
SipComputeChecksum(header, header->Size, &header->Checksum.QuadPart);
|
|
|
|
//
|
|
// Acquire the log mutant to serialize writing to the log file.
|
|
//
|
|
|
|
status = KeWaitForSingleObject(deviceExtension->LogFileMutant, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
mutantAcquired = TRUE;
|
|
|
|
ASSERT(deviceExtension->LogFileHandle != NULL && deviceExtension->LogFileObject != NULL); // Should have happened in phase 2 initialization
|
|
|
|
//
|
|
// Create an irp to do the write. We don't want to just use ZwWriteFile because we want to
|
|
// avoid the context switch to the process where we hold the log handle.
|
|
//
|
|
|
|
irp = IoBuildAsynchronousFsdRequest(
|
|
IRP_MJ_WRITE,
|
|
deviceExtension->FileSystemDeviceObject,
|
|
header,
|
|
header->Size,
|
|
&deviceExtension->LogWriteOffset,
|
|
Iosb);
|
|
|
|
if (!irp) {
|
|
SIS_MARK_POINT();
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->FileObject = deviceExtension->LogFileObject;
|
|
|
|
//
|
|
// Initialize the event on which we'll wait for the write to complete.
|
|
//
|
|
KeInitializeEvent(event,NotificationEvent,FALSE);
|
|
|
|
IoSetCompletionRoutine(
|
|
irp,
|
|
SiDeleteAndSetCompletion,
|
|
event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
//
|
|
// Make sure that this request is really write through all the way to the disk
|
|
// medium.
|
|
//
|
|
irpSp->Flags |= SL_WRITE_THROUGH;
|
|
|
|
|
|
status = IoCallDriver(deviceExtension->FileSystemDeviceObject, irp);
|
|
|
|
// At this point, we have released the mutant in the complete
|
|
// routine.
|
|
|
|
#if DBG
|
|
irp = NULL; irpSp = NULL; // The completion routine may have already deallocated the irp.
|
|
#endif // DBG
|
|
|
|
if (STATUS_PENDING == status) {
|
|
status = KeWaitForSingleObject(event,Executive,KernelMode,FALSE,NULL);
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
status = Iosb->Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
#if DBG
|
|
DbgPrint("SiMakeLogEntry: Log entry failed after write wait, 0x%x\n",status);
|
|
#endif // DBG
|
|
SIS_MARK_POINT_ULONG(status);
|
|
goto done;
|
|
} else {
|
|
ASSERT(Iosb->Information == header->Size);
|
|
deviceExtension->LogWriteOffset.QuadPart += header->Size;
|
|
}
|
|
|
|
done:
|
|
if (header != NULL) {
|
|
ExFreePool(header);
|
|
}
|
|
|
|
if (mutantAcquired) {
|
|
KeReleaseMutant(
|
|
deviceExtension->LogFileMutant,
|
|
IO_NO_INCREMENT,
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
|
|
return status;
|
|
#else // ENABLE_LOGGING
|
|
UNREFERENCED_PARAMETER( deviceExtension );
|
|
UNREFERENCED_PARAMETER( type );
|
|
UNREFERENCED_PARAMETER( size );
|
|
UNREFERENCED_PARAMETER( record );
|
|
|
|
return STATUS_SUCCESS;
|
|
#endif // ENABLE_LOGGING
|
|
}
|
|
|
|
VOID
|
|
SipComputeChecksum(
|
|
IN PVOID buffer,
|
|
IN ULONG size,
|
|
IN OUT PLONGLONG checksum)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Compute a checksum for a buffer. We use the "131 hash," which
|
|
work by keeping a 64 bit running total, and for each 32 bits of
|
|
data multiplying the 64 bits by 131 and adding in the next 32
|
|
bits. Must be called at PASSIVE_LEVEL, and all aruments
|
|
may be pagable.
|
|
|
|
Arguments:
|
|
|
|
buffer - pointer to the data to be checksummed
|
|
|
|
size - size of the data to be checksummed
|
|
|
|
checksum - pointer to large integer to receive the checksum. This
|
|
may be within the buffer, and SipComputeChecksum guarantees that
|
|
the initial value will be used in computing the checksum.
|
|
|
|
Return Value:
|
|
|
|
void
|
|
--*/
|
|
{
|
|
LONGLONG runningTotal;
|
|
ULONG *ptr = (unsigned *)buffer;
|
|
ULONG bytesRemaining = size;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// NB: code in volume check assumes that the checksum of the empty bit string is
|
|
// 0. If this is ceases to be true, be sure to fix the code there.
|
|
//
|
|
|
|
runningTotal = *checksum;
|
|
|
|
while (bytesRemaining >= sizeof(*ptr)) {
|
|
runningTotal = runningTotal * 131 + *ptr;
|
|
bytesRemaining -= sizeof(*ptr);
|
|
ptr++;
|
|
}
|
|
|
|
if (bytesRemaining > 0) {
|
|
ULONG extra;
|
|
|
|
ASSERT(bytesRemaining < sizeof (ULONG));
|
|
extra = 0;
|
|
RtlMoveMemory(&extra, ptr, bytesRemaining);
|
|
|
|
runningTotal = runningTotal * 131 + extra;
|
|
}
|
|
|
|
*checksum = runningTotal;
|
|
}
|
|
|
|
NTSTATUS
|
|
SipOpenLogFile(
|
|
IN OUT PDEVICE_EXTENSION deviceExtension)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Open the log file for this volume. Must not already be opened. Must be called
|
|
exactly once per volume, and must be called on a worker thread.
|
|
|
|
Arguments:
|
|
|
|
deviceExtension - the device extension for the volume for which we're
|
|
to open the log file.
|
|
|
|
Return Value:
|
|
|
|
Returns status of the open.
|
|
--*/
|
|
{
|
|
#if ENABLE_LOGGING
|
|
NTSTATUS status;
|
|
OBJECT_ATTRIBUTES Obja[1];
|
|
UNICODE_STRING fileName;
|
|
IO_STATUS_BLOCK Iosb[1];
|
|
|
|
SIS_MARK_POINT();
|
|
|
|
ASSERT(deviceExtension->LogFileHandle == NULL);
|
|
ASSERT(deviceExtension->LogFileObject == NULL);
|
|
|
|
fileName.Length = 0;
|
|
fileName.MaximumLength = deviceExtension->CommonStorePathname.Length + LOG_FILE_NAME_LEN;
|
|
fileName.Buffer = ExAllocatePoolWithTag(PagedPool, fileName.MaximumLength, ' siS');
|
|
|
|
if (!fileName.Buffer) {
|
|
#if DBG
|
|
DbgPrint("SIS: SipOpenLogFile: unable to allocate filename buffer. We're toast.\n");
|
|
#endif // DBG
|
|
|
|
SIS_MARK_POINT();
|
|
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto done;
|
|
}
|
|
|
|
RtlCopyUnicodeString(
|
|
&fileName,
|
|
&deviceExtension->CommonStorePathname);
|
|
|
|
ASSERT(fileName.Length == deviceExtension->CommonStorePathname.Length);
|
|
|
|
status = RtlAppendUnicodeToString(
|
|
&fileName,
|
|
LOG_FILE_NAME);
|
|
|
|
ASSERT(status == STATUS_SUCCESS);
|
|
ASSERT(fileName.Length == deviceExtension->CommonStorePathname.Length + LOG_FILE_NAME_LEN); // or else you changed LOG_FILE_NAME without changing LOG_FILE_NAME_LEN
|
|
|
|
InitializeObjectAttributes(
|
|
Obja,
|
|
&fileName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = NtCreateFile(
|
|
&deviceExtension->LogFileHandle,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
Obja,
|
|
Iosb,
|
|
NULL, // allocation size
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ, // share access
|
|
FILE_OPEN_IF,
|
|
FILE_WRITE_THROUGH,
|
|
NULL, // EA buffer
|
|
0); // EA length
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
#if DBG
|
|
DbgPrint("SipOpenLogFile: ZwCreate failed, 0x%x\n",status);
|
|
#endif // DBG
|
|
SIS_MARK_POINT_ULONG(status);
|
|
goto done;
|
|
} else {
|
|
status = ObReferenceObjectByHandle(
|
|
deviceExtension->LogFileHandle,
|
|
FILE_READ_DATA | FILE_WRITE_DATA,
|
|
*IoFileObjectType,
|
|
KernelMode,
|
|
&deviceExtension->LogFileObject,
|
|
NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
#if DBG
|
|
DbgPrint("SipOpenLogFile: ObReferenceObjectByHandle failed, 0x%x\n",status);
|
|
#endif // DBG
|
|
|
|
NtClose(deviceExtension->LogFileHandle);
|
|
deviceExtension->LogFileHandle = NULL;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
|
|
SipDrainLogFile(deviceExtension);
|
|
|
|
SIS_MARK_POINT();
|
|
|
|
done:
|
|
|
|
if (fileName.Buffer) {
|
|
ExFreePool(fileName.Buffer);
|
|
#if DBG
|
|
fileName.Buffer = NULL;
|
|
#endif // DBG
|
|
}
|
|
|
|
return status;
|
|
|
|
#undef LOG_FILE_NAME
|
|
#undef LOG_FILE_NAME_LEN
|
|
#else // ENABLE_LOGGING
|
|
|
|
UNREFERENCED_PARAMETER( deviceExtension );
|
|
return STATUS_SUCCESS;
|
|
#endif // ENABLE_LOGGING
|
|
}
|
|
|
|
VOID
|
|
SipDrainLogFile(
|
|
PDEVICE_EXTENSION deviceExtension)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Drain the log file for this volume and assure that all of the operations in it
|
|
have happened or not happened atomically.
|
|
|
|
Arguments:
|
|
|
|
deviceExtension - the device extension for the volume for which we're
|
|
to drain the log file.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
--*/
|
|
{
|
|
#if ENABLE_LOGGING
|
|
FILE_ALLOCATED_RANGE_BUFFER inArb[1];
|
|
FILE_ALLOCATED_RANGE_BUFFER outArb[1];
|
|
NTSTATUS status;
|
|
HANDLE eventHandle = NULL;
|
|
PKEVENT event = NULL;
|
|
IO_STATUS_BLOCK Iosb[1];
|
|
LARGE_INTEGER fileOffset;
|
|
PCHAR buffer = NULL;
|
|
#define BUFFER_SIZE 16384
|
|
PULONG bufferPointer;
|
|
PSIS_LOG_HEADER logHeader;
|
|
LARGE_INTEGER stashedChecksum, computedChecksum;
|
|
BOOLEAN clearLog = FALSE;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
buffer = ExAllocatePoolWithTag(PagedPool, BUFFER_SIZE, ' siS');
|
|
|
|
if (NULL == buffer) {
|
|
SIS_MARK_POINT();
|
|
goto done;
|
|
}
|
|
|
|
status = SipCreateEvent(
|
|
NotificationEvent,
|
|
&eventHandle,
|
|
&event);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
|
|
goto done;
|
|
}
|
|
|
|
#if DBG
|
|
deviceExtension->LogWriteOffset.QuadPart = -1;
|
|
#endif // DBG
|
|
|
|
//
|
|
// Figure out where the log file starts.
|
|
//
|
|
inArb->FileOffset.QuadPart = 0;
|
|
inArb->Length.QuadPart = MAXLONGLONG;
|
|
|
|
status = NtFsControlFile(
|
|
deviceExtension->LogFileHandle,
|
|
eventHandle,
|
|
NULL, // APC routine
|
|
NULL, // ApcContext
|
|
Iosb,
|
|
FSCTL_QUERY_ALLOCATED_RANGES,
|
|
inArb,
|
|
sizeof(FILE_ALLOCATED_RANGE_BUFFER),
|
|
outArb,
|
|
sizeof(FILE_ALLOCATED_RANGE_BUFFER));
|
|
|
|
if (STATUS_PENDING == status) {
|
|
status = KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(STATUS_SUCCESS == status); // must succeed because Iosb is on the stack
|
|
status = Iosb->Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
clearLog = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (0 == Iosb->Information) {
|
|
//
|
|
// The file is empty. We're done.
|
|
//
|
|
SIS_MARK_POINT_ULONG(deviceExtension);
|
|
clearLog = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Skip over any leading unallocated range, starting at the beginning of the first allocated range.
|
|
//
|
|
fileOffset = outArb->FileOffset;
|
|
|
|
//
|
|
// Find the first log entry by searching for the first occurance of the magic number.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
KeClearEvent(event);
|
|
|
|
status = ZwReadFile(
|
|
deviceExtension->LogFileHandle,
|
|
eventHandle,
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
Iosb,
|
|
buffer,
|
|
BUFFER_SIZE,
|
|
&fileOffset,
|
|
NULL); // key
|
|
|
|
if (STATUS_PENDING == status) {
|
|
status = KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(STATUS_SUCCESS == status); // must succeed because Iosb is on the stack
|
|
status = Iosb->Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
clearLog = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
if (0 == Iosb->Information) {
|
|
SIS_MARK_POINT();
|
|
clearLog = TRUE;
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// Cruise through the buffer looking for the magic number
|
|
//
|
|
for (bufferPointer = (PULONG)buffer; bufferPointer < ((PULONG)buffer) + BUFFER_SIZE/sizeof(ULONG); bufferPointer++) {
|
|
if (SIS_LOG_HEADER_MAGIC == *bufferPointer) {
|
|
fileOffset.QuadPart += (bufferPointer - ((PULONG)buffer)) * sizeof(ULONG);
|
|
goto startLogReading;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We didn't find it, read in the next chunk.
|
|
//
|
|
|
|
fileOffset.QuadPart += BUFFER_SIZE;
|
|
}
|
|
|
|
startLogReading:
|
|
|
|
for (;;) {
|
|
KeClearEvent(event);
|
|
|
|
status = ZwReadFile(
|
|
deviceExtension->LogFileHandle,
|
|
eventHandle,
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
Iosb,
|
|
buffer,
|
|
BUFFER_SIZE,
|
|
&fileOffset,
|
|
NULL); // key
|
|
|
|
if (STATUS_PENDING == status) {
|
|
status = KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(STATUS_SUCCESS == status); // must succeed because Iosb is on the stack
|
|
status = Iosb->Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
deviceExtension->LogWriteOffset = fileOffset;
|
|
goto done;
|
|
}
|
|
|
|
if (0 == Iosb->Information) {
|
|
SIS_MARK_POINT();
|
|
deviceExtension->LogWriteOffset = fileOffset;
|
|
goto done;
|
|
}
|
|
|
|
ASSERT(Iosb->Information <= BUFFER_SIZE);
|
|
|
|
logHeader = (PSIS_LOG_HEADER)buffer;
|
|
|
|
while ((((PCHAR)logHeader) - buffer) + sizeof(SIS_LOG_HEADER) <= Iosb->Information) {
|
|
//
|
|
// We know that we've got enough space for the log header.
|
|
//
|
|
|
|
//
|
|
// Check the header to see if it looks valid (ie., if it's got a good magic
|
|
// number).
|
|
//
|
|
if (SIS_LOG_HEADER_MAGIC != logHeader->Magic) {
|
|
//
|
|
// This log record is corrupt. Start writing the new log records here, and
|
|
// punt the readback.
|
|
//
|
|
SIS_MARK_POINT();
|
|
deviceExtension->LogWriteOffset.QuadPart = fileOffset.QuadPart + (((PCHAR)logHeader) - buffer);
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// See if we have enough space for the whole record.
|
|
//
|
|
if (((ULONG)(((PCHAR)logHeader - buffer) + logHeader->Size)) > Iosb->Information) {
|
|
if (logHeader->Size > BUFFER_SIZE) {
|
|
//
|
|
// The log is corrupt. Punt reading it.
|
|
//
|
|
SIS_MARK_POINT();
|
|
deviceExtension->LogWriteOffset.QuadPart = fileOffset.QuadPart + (((PCHAR)logHeader) - buffer);
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// The log record isn't contained entirely within the buffer we've read. Advance the buffer.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We've got a whole log record. Process it.
|
|
//
|
|
|
|
//
|
|
// Make sure that the log record checkum matches. First, we have to stash the checksum out of
|
|
// the header and then set the header space to 0, because that's what it was when the checksum
|
|
// was computed in the first place.
|
|
//
|
|
stashedChecksum = logHeader->Checksum;
|
|
logHeader->Checksum.QuadPart = 0;
|
|
computedChecksum.QuadPart = 0;
|
|
|
|
SipComputeChecksum(logHeader, logHeader->Size, &computedChecksum.QuadPart);
|
|
|
|
if (computedChecksum.QuadPart != stashedChecksum.QuadPart) {
|
|
//
|
|
// eventlog an error.
|
|
//
|
|
#if DBG
|
|
DbgPrint("SIS: SipDrainLogFile: log record checksum doesn't match, 0x%x.0x%x != 0x%x.0x%x\n",
|
|
computedChecksum.HighPart,computedChecksum.LowPart,
|
|
stashedChecksum.HighPart,stashedChecksum.LowPart);
|
|
#endif // DBG
|
|
deviceExtension->LogWriteOffset.QuadPart = fileOffset.QuadPart + (((PCHAR)logHeader) - buffer);
|
|
goto done;
|
|
}
|
|
|
|
//
|
|
// The log record looks good. Process it.
|
|
//
|
|
switch (logHeader->Type) {
|
|
case SIS_LOG_TYPE_REFCOUNT_UPDATE: {
|
|
PSIS_LOG_REFCOUNT_UPDATE refcountLogRecord = (PSIS_LOG_REFCOUNT_UPDATE)(logHeader + 1);
|
|
|
|
SipProcessRefcountUpdateLogRecord(deviceExtension,refcountLogRecord);
|
|
|
|
/*BJB*/ DbgPrint("SIS: SipDrainLog: RC update UT %d, LF NTFS id 0x%x.0x%x, LI 0x%x.0x%x, CSid <whatever>\n",
|
|
refcountLogRecord->UpdateType,refcountLogRecord->LinkFileNtfsId.HighPart,
|
|
refcountLogRecord->LinkFileNtfsId.LowPart,refcountLogRecord->LinkIndex.HighPart,
|
|
refcountLogRecord->LinkIndex.LowPart);
|
|
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
#if DBG
|
|
DbgPrint("SIS: SipDrainLog: Unknown log record type %d, ignoring.\n",logHeader->Type);
|
|
#endif // DBG
|
|
break;
|
|
}
|
|
}
|
|
|
|
logHeader = (PSIS_LOG_HEADER)(((PCHAR)logHeader) + logHeader->Size);
|
|
}
|
|
|
|
//
|
|
// Advance within the file to the beginning of the next record, loop around and reread the buffer.
|
|
//
|
|
fileOffset.QuadPart += ((PCHAR)logHeader) - buffer;
|
|
}
|
|
|
|
done:
|
|
|
|
if (clearLog) {
|
|
SipClearLogFile(deviceExtension);
|
|
}
|
|
|
|
if (NULL != event) {
|
|
ObDereferenceObject(event);
|
|
event = NULL;
|
|
}
|
|
|
|
if (NULL != eventHandle) {
|
|
NtClose(eventHandle);
|
|
eventHandle = NULL;
|
|
}
|
|
|
|
if (NULL != buffer) {
|
|
ExFreePool(buffer);
|
|
}
|
|
|
|
ASSERT(-1 != deviceExtension->LogWriteOffset.QuadPart); // This should have been reset somewhere here.
|
|
|
|
#undef BUFFER_SIZE
|
|
#else
|
|
UNREFERENCED_PARAMETER( deviceExtension );
|
|
#endif // ENABLE_LOGGING
|
|
}
|
|
|
|
VOID
|
|
SipClearLogFile(
|
|
PDEVICE_EXTENSION deviceExtension)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Clear out the contents of the log file. Must be called during initialization
|
|
when we're guaranteed to be serialized. Also sets the log file sparse.
|
|
|
|
Arguments:
|
|
|
|
deviceExtension - the device extension for the volume for which we're
|
|
to clear the log file.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
--*/
|
|
{
|
|
#if ENABLE_LOGGING
|
|
FILE_END_OF_FILE_INFORMATION eofInfo[1];
|
|
LARGE_INTEGER byteOffset;
|
|
NTSTATUS status;
|
|
IO_STATUS_BLOCK Iosb[1];
|
|
|
|
ASSERT(NULL != deviceExtension->LogFileObject);
|
|
|
|
eofInfo->EndOfFile.QuadPart = 0;
|
|
|
|
status = SipSetInformationFile(
|
|
deviceExtension->LogFileObject,
|
|
deviceExtension->DeviceObject,
|
|
FileEndOfFileInformation,
|
|
sizeof(FILE_END_OF_FILE_INFORMATION),
|
|
eofInfo);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
#if DBG
|
|
DbgPrint("SipClearLogFile: unable to set EOF to 0, status 0x%x\n",status);
|
|
#endif // DBG
|
|
return;
|
|
}
|
|
|
|
deviceExtension->LogWriteOffset.QuadPart = 0;
|
|
|
|
status = SipFsControlFile(
|
|
deviceExtension->LogFileObject,
|
|
deviceExtension->DeviceObject,
|
|
FSCTL_SET_SPARSE,
|
|
NULL, // input buffer
|
|
0, // input buffer length
|
|
NULL, // output buffer
|
|
0, // output buffer length
|
|
NULL); // returned output buffer length
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
|
|
DbgPrint("SIS: SipClearLogFile: set sparse failed 0x%x\n",status);
|
|
}
|
|
#endif // DBG
|
|
|
|
#else
|
|
UNREFERENCED_PARAMETER( deviceExtension );
|
|
#endif // ENABLE_LOGGING
|
|
}
|
|
|
|
#if ENABLE_LOGGING
|
|
VOID
|
|
SipAcquireLog(
|
|
IN OUT PDEVICE_EXTENSION deviceExtension)
|
|
{
|
|
NTSTATUS status;
|
|
status = KeWaitForSingleObject(deviceExtension->LogFileMutant, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(status == STATUS_SUCCESS || status == STATUS_ABANDONED);
|
|
}
|
|
|
|
VOID
|
|
SipReleaseLog(
|
|
IN OUT PDEVICE_EXTENSION deviceExtension)
|
|
{
|
|
KeReleaseMutant(deviceExtension->LogFileMutant, IO_NO_INCREMENT, TRUE, FALSE);
|
|
}
|
|
|
|
typedef struct _TRIM_ENTRY {
|
|
HANDLE logHandle;
|
|
LARGE_INTEGER firstValidAddress;
|
|
|
|
struct _TRIM_ENTRY *next;
|
|
} TRIM_ENTRY, *PTRIM_ENTRY;
|
|
|
|
HANDLE trimEventHandle = NULL;
|
|
PKEVENT trimEvent = NULL;
|
|
#endif // ENABLE_LOGGING
|
|
|
|
VOID
|
|
SiTrimLogs(
|
|
IN PVOID parameter)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Run through the list of SIS volumes on this system, and trim the log files
|
|
for each of them. This function should be called with a period greater than
|
|
the longest time we expect log entries to be meaningful.
|
|
|
|
Note: this routine is NOT thread safe; it can only be called once at a time.
|
|
Since it reschedules itself, this should not be an issue.
|
|
|
|
Arguments:
|
|
|
|
the parameter is ignored
|
|
|
|
Return Value:
|
|
|
|
none
|
|
--*/
|
|
{
|
|
#if ENABLE_LOGGING
|
|
KIRQL OldIrql;
|
|
PTRIM_ENTRY trimEntries = NULL;
|
|
PDEVICE_EXTENSION deviceExtension;
|
|
NTSTATUS status;
|
|
FILE_ZERO_DATA_INFORMATION zeroDataInfo[1];
|
|
IO_STATUS_BLOCK Iosb[1];
|
|
LARGE_INTEGER dueTime;
|
|
|
|
UNREFERENCED_PARAMETER(parameter);
|
|
|
|
ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL);
|
|
|
|
SIS_MARK_POINT();
|
|
|
|
if (NULL == trimEventHandle) {
|
|
status = SipCreateEvent(
|
|
SynchronizationEvent,
|
|
&trimEventHandle,
|
|
&trimEvent);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
#if DBG
|
|
DbgPrint("SIS: SipTrimLogs: can't allocate event, 0x%x\n",status);
|
|
#endif // DBG
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
//
|
|
// First cruise the device extensions and build up a list of trim entries for them.
|
|
// We need to do it this way (rather than running the list of device extensions directly)
|
|
// because we need to handle the case where a volume is dismounted while we're in progress.
|
|
// If it happens, then we'll have an invalid LogFileHandle, which will cause an error return
|
|
// from the fsctl, which we'll ignore.
|
|
//
|
|
|
|
KeAcquireSpinLock(deviceExtensionListLock, &OldIrql);
|
|
|
|
for (deviceExtension = deviceExtensionListHead->Next;
|
|
deviceExtension != deviceExtensionListHead;
|
|
deviceExtension = deviceExtension->Next) {
|
|
|
|
if (deviceExtension->Phase2InitializationComplete && (NULL != deviceExtension->LogFileHandle)) {
|
|
//
|
|
// This is a device with a log file. Make a new trim entry for it.
|
|
//
|
|
PTRIM_ENTRY newEntry = ExAllocatePoolWithTag(NonPagedPool, sizeof(TRIM_ENTRY), ' siS');
|
|
if (NULL == newEntry) {
|
|
//
|
|
// Just punt the rest of the volumes.
|
|
//
|
|
break;
|
|
}
|
|
|
|
newEntry->next = trimEntries;
|
|
trimEntries = newEntry;
|
|
|
|
newEntry->firstValidAddress = deviceExtension->PreviousLogWriteOffset;
|
|
newEntry->logHandle = deviceExtension->LogFileHandle;
|
|
|
|
//
|
|
// Now update the device extension so that we'll trim to the current pointer on the
|
|
// next pass.
|
|
//
|
|
deviceExtension->PreviousLogWriteOffset = deviceExtension->LogWriteOffset;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(deviceExtensionListLock, OldIrql);
|
|
|
|
//
|
|
// Now we're back at PASSIVE_LEVEL. Cruise the trim entries and truncate each log file as appropriate.
|
|
//
|
|
zeroDataInfo->FileOffset.QuadPart = 0;
|
|
|
|
while (NULL != trimEntries) {
|
|
PTRIM_ENTRY thisEntry;
|
|
|
|
#if DBG
|
|
if (BJBDebug & 0x20000) {
|
|
DbgPrint("SIS: SipTrimLogs: trimming log with LFH 0x%x.\n",trimEntries->logHandle);
|
|
}
|
|
#endif // DBG
|
|
|
|
zeroDataInfo->BeyondFinalZero = trimEntries->firstValidAddress;
|
|
|
|
status = ZwFsControlFile(
|
|
trimEntries->logHandle,
|
|
trimEventHandle,
|
|
NULL, // APC routine
|
|
NULL, // APC context
|
|
Iosb,
|
|
FSCTL_SET_ZERO_DATA,
|
|
zeroDataInfo,
|
|
sizeof(FILE_ZERO_DATA_INFORMATION),
|
|
NULL,
|
|
0);
|
|
|
|
if (STATUS_PENDING == status) {
|
|
status = KeWaitForSingleObject(trimEvent, Executive, KernelMode, FALSE, NULL);
|
|
ASSERT(STATUS_SUCCESS == status); // Iosb is on the stack, so we can't let this fail
|
|
status = Iosb->Status;
|
|
}
|
|
|
|
#if DBG
|
|
if (!NT_SUCCESS(status)) {
|
|
SIS_MARK_POINT_ULONG(status);
|
|
DbgPrint("SIS: SipTrimLogs: FSCTL_ZERO_DATA failed, 0x%x\n",status);
|
|
}
|
|
#endif // DBG
|
|
|
|
thisEntry = trimEntries;
|
|
trimEntries = thisEntry->next;
|
|
|
|
ExFreePool(thisEntry);
|
|
}
|
|
|
|
//
|
|
// We've trimmed every log file in the system. Rechedule ourselves.
|
|
//
|
|
|
|
done:
|
|
|
|
dueTime.QuadPart = LOG_TRIM_TIMER_INTERVAL;
|
|
|
|
KeSetTimerEx(
|
|
LogTrimTimer,
|
|
dueTime,
|
|
0,
|
|
LogTrimDpc);
|
|
|
|
return;
|
|
|
|
#else // ENABLE_LOGGING
|
|
|
|
UNREFERENCED_PARAMETER( parameter );
|
|
#endif // ENABLE_LOGGING
|
|
}
|
|
|
|
VOID
|
|
SiLogTrimDpcRoutine(
|
|
IN PKDPC dpc,
|
|
IN PVOID context,
|
|
IN PVOID systemArg1,
|
|
IN PVOID systemArg2)
|
|
{
|
|
#if ENABLE_LOGGING
|
|
ExQueueWorkItem(LogTrimWorkItem,DelayedWorkQueue);
|
|
|
|
#if DBG
|
|
if (BJBDebug & 0x20000) {
|
|
DbgPrint("SIS: LogTrimDpcRoutine: queued up log trim.\n");
|
|
}
|
|
#endif // DBG
|
|
|
|
#else // ENABLE_LOGGING
|
|
UNREFERENCED_PARAMETER( dpc );
|
|
UNREFERENCED_PARAMETER( context );
|
|
UNREFERENCED_PARAMETER( systemArg1 );
|
|
UNREFERENCED_PARAMETER( systemArg2 );
|
|
#endif // ENABLE_LOGGING
|
|
}
|