1140 lines
35 KiB
C
1140 lines
35 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1992 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
ritebhnd.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements support for WRITE_BEHIND buffers
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Larry Osterman (larryo) 19-Dec-1992
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
19-Dec-1992 larryo
|
|||
|
|
|||
|
Created
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#define INCLUDE_SMB_READ_WRITE
|
|||
|
#include "precomp.h"
|
|||
|
#pragma hdrstop
|
|||
|
|
|||
|
typedef struct _WRITE_FLUSH_CONTEXT {
|
|||
|
WORK_QUEUE_ITEM WorkItem;
|
|||
|
PWRITE_BUFFER_HEAD BufferHead;
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
NTSTATUS Status;
|
|||
|
ULONG AmountActuallyWritten;
|
|||
|
BOOLEAN WaitForCompletion;
|
|||
|
BOOLEAN AllDataWritten;
|
|||
|
} WRITE_FLUSH_CONTEXT, *PWRITE_FLUSH_CONTEXT;
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FlushWriteBufferHead(
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PWRITE_BUFFER_HEAD BufferHead,
|
|||
|
IN BOOLEAN WaitForCompletion
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FlushBufferCompletion(
|
|||
|
IN NTSTATUS Status,
|
|||
|
IN PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
CompleteBufferFlushOperation(
|
|||
|
IN PVOID Ctx
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DereferenceWriteBufferLocked(
|
|||
|
IN PIRP Irp,
|
|||
|
IN PWRITE_BUFFER WriteBuffer,
|
|||
|
IN BOOLEAN WaitForCompletion,
|
|||
|
IN ULONG EventNumber,
|
|||
|
IN BOOLEAN IncrementWriteBuffersAvailable
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, FlushWriteBufferHead)
|
|||
|
#pragma alloc_text(PAGE, CompleteBufferFlushOperation)
|
|||
|
#pragma alloc_text(PAGE, RdrFlushWriteBufferForFile)
|
|||
|
#pragma alloc_text(PAGE, RdrTruncateWriteBufferForFcb)
|
|||
|
#pragma alloc_text(PAGE, RdrTruncateWriteBufferForIcb)
|
|||
|
#pragma alloc_text(PAGE, RdrFindOrAllocateWriteBuffer)
|
|||
|
#pragma alloc_text(PAGE, RdrDereferenceWriteBuffer)
|
|||
|
#pragma alloc_text(PAGE, DereferenceWriteBufferLocked)
|
|||
|
#pragma alloc_text(PAGE, RdrInitializeWriteBufferHead)
|
|||
|
#pragma alloc_text(PAGE, RdrUninitializeWriteBufferHead)
|
|||
|
#pragma alloc_text(PAGE3FILE, FlushBufferCompletion)
|
|||
|
|
|||
|
#endif
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FlushWriteBufferHead(
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PWRITE_BUFFER_HEAD BufferHead,
|
|||
|
IN BOOLEAN WaitForCompletion
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
PWRITE_FLUSH_CONTEXT Context = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushWriteBufferHead: %lx\n", BufferHead));
|
|||
|
|
|||
|
//
|
|||
|
// NOTE: This routine is called with the write buffer head locked.
|
|||
|
// It unlocks it before returning.
|
|||
|
//
|
|||
|
|
|||
|
if (BufferHead->FlushInProgress) {
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushWriteBufferHead: already flushing %lx\n", BufferHead));
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
BufferHead->FlushInProgress = TRUE;
|
|||
|
|
|||
|
while (!IsListEmpty(&BufferHead->FlushList)) {
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
PLIST_ENTRY BufferEntry = RemoveHeadList(&BufferHead->FlushList);
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(BufferEntry, WRITE_BUFFER, NextWbBuffer);
|
|||
|
|
|||
|
DEBUG Buffer->NextWbBuffer.Flink = NULL;
|
|||
|
DEBUG Buffer->NextWbBuffer.Blink = NULL;
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushWriteBufferHead: %lx Offset: %lx%lx, Length: %lx\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
|
|||
|
//
|
|||
|
// Reference the file object so this file does not get cleaned up
|
|||
|
// while flush is in progress.
|
|||
|
//
|
|||
|
|
|||
|
#if 0 && RDRDBG_LOG
|
|||
|
{
|
|||
|
//LARGE_INTEGER tick;
|
|||
|
//KeQueryTickCount(&tick);
|
|||
|
//RdrLog(( "ritebhnd", &((PFCB)BufferHead->FileObject->FsContext)->FileName, 2, tick.LowPart, tick.HighPart ));
|
|||
|
//RdrLog(( "ritebhnd", &((PFCB)BufferHead->FileObject->FsContext)->FileName, 2, Buffer->ByteOffset.LowPart, Buffer->Length ));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
ObReferenceObject(BufferHead->FileObject);
|
|||
|
|
|||
|
RdrStartAndXBehindOperation(&BufferHead->AndXBehind);
|
|||
|
|
|||
|
Context = ALLOCATE_POOL(NonPagedPool, sizeof(WRITE_FLUSH_CONTEXT), POOL_WRITEFLUSHCTX);
|
|||
|
|
|||
|
if (Context == NULL) {
|
|||
|
LOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushWriteBufferHead: alloc failed; requeueing %lx\n", Buffer));
|
|||
|
InsertHeadList(&BufferHead->FlushList, &Buffer->NextWbBuffer);
|
|||
|
|
|||
|
BufferHead->FlushInProgress = FALSE;
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
ObDereferenceObject( BufferHead->FileObject );
|
|||
|
|
|||
|
RdrEndAndXBehindOperation(&BufferHead->AndXBehind);
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
Context->BufferHead = BufferHead;
|
|||
|
Context->Buffer = Buffer;
|
|||
|
Context->WaitForCompletion = (BOOLEAN)((ARGUMENT_PRESENT(Irp) ? TRUE : WaitForCompletion)),
|
|||
|
|
|||
|
//
|
|||
|
// We need to ignore the IRP provided when we call RdrWriteRange. If
|
|||
|
// this IRP is a user mode IRP, we will probe the buffer for UserMode
|
|||
|
// inside RdrWriteRange, and this will cause us to access violate
|
|||
|
// when we do this write.
|
|||
|
//
|
|||
|
|
|||
|
Status = RdrWriteRange(NULL,
|
|||
|
BufferHead->FileObject,
|
|||
|
NULL,
|
|||
|
Buffer->Buffer,
|
|||
|
Buffer->Length,
|
|||
|
Buffer->ByteOffset,
|
|||
|
Context->WaitForCompletion,
|
|||
|
FlushBufferCompletion,
|
|||
|
Context,
|
|||
|
&Context->AllDataWritten,
|
|||
|
&Context->AmountActuallyWritten
|
|||
|
);
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
if ((Status == STATUS_INSUFFICIENT_RESOURCES) ||
|
|||
|
(Status == STATUS_FILE_LOCK_CONFLICT)) {
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushWriteBufferHead: Write range failed; requeueing %lx\n", Buffer));
|
|||
|
|
|||
|
//
|
|||
|
// Stick this buffer back on the head of the queue.
|
|||
|
//
|
|||
|
|
|||
|
InsertHeadList(&BufferHead->FlushList, &Buffer->NextWbBuffer);
|
|||
|
|
|||
|
BufferHead->FlushInProgress = FALSE;
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
ObDereferenceObject( BufferHead->FileObject );
|
|||
|
|
|||
|
RdrEndAndXBehindOperation(&BufferHead->AndXBehind);
|
|||
|
|
|||
|
FREE_POOL( Context );
|
|||
|
return Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
BufferHead->FlushInProgress = FALSE;
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushWriteBufferHead: done with %lx\n", BufferHead));
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FlushBufferCompletion(
|
|||
|
IN NTSTATUS Status,
|
|||
|
IN PVOID Ctx
|
|||
|
)
|
|||
|
{
|
|||
|
PWRITE_FLUSH_CONTEXT Context = Ctx;
|
|||
|
|
|||
|
DISCARDABLE_CODE(RdrFileDiscardableSection);
|
|||
|
dprintf(DPRT_RITEBHND, ("FlushBufferCompletion: Status:%lx, Context:%lx, Buffer:%lx\n", Status, Context, Context->Buffer));
|
|||
|
|
|||
|
Context->Status = Status;
|
|||
|
|
|||
|
ExInitializeWorkItem(&Context->WorkItem, CompleteBufferFlushOperation, Context);
|
|||
|
|
|||
|
//
|
|||
|
// Queue directly to the EX queue, instead of the redir queue, in order to avoid
|
|||
|
// deadlock where the redir's single thread for the queue is blocked waiting for
|
|||
|
// this write operation to complete.
|
|||
|
//
|
|||
|
|
|||
|
ExQueueWorkItem(&Context->WorkItem, DelayedWorkQueue);
|
|||
|
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
CompleteBufferFlushOperation(
|
|||
|
IN PVOID Ctx
|
|||
|
)
|
|||
|
{
|
|||
|
PWRITE_FLUSH_CONTEXT Context = Ctx;
|
|||
|
PWRITE_BUFFER Buffer = Context->Buffer;
|
|||
|
PWRITE_BUFFER_HEAD WriteHeader = Buffer->BufferHead;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("CompleteBufferFlushOperation: Status:%lx, Context:%lx, Buffer: %lx\n", Context->Status, Context, Context->Buffer));
|
|||
|
|
|||
|
if (!Context->WaitForCompletion &&
|
|||
|
(!NT_SUCCESS(Context->Status) ||
|
|||
|
!Context->AllDataWritten)) {
|
|||
|
ULONG DataBuffer[2];
|
|||
|
PICB Icb = Buffer->BufferHead->FileObject->FsContext2;
|
|||
|
|
|||
|
DataBuffer[0] = Buffer->Length;
|
|||
|
DataBuffer[1] = Context->AmountActuallyWritten;
|
|||
|
|
|||
|
RdrWriteErrorLogEntry(Icb->Fcb->Connection->Server,
|
|||
|
IO_ERR_LAYERED_FAILURE,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
Context->Status,
|
|||
|
&DataBuffer,
|
|||
|
sizeof(DataBuffer)
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the count so it can allow another write behind to
|
|||
|
// proceed.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD( WriteHeader );
|
|||
|
WriteHeader->WriteBuffersActive--;
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( WriteHeader );
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the file object so it can be cleaned up.
|
|||
|
//
|
|||
|
|
|||
|
#if 0 && RDRDBG_LOG
|
|||
|
{
|
|||
|
//LARGE_INTEGER tick;
|
|||
|
//KeQueryTickCount(&tick);
|
|||
|
//RdrLog(( "ritebCMP", &((PFCB)WriteHeader->FileObject->FsContext)->FileName, 2, tick.LowPart, tick.HighPart ));
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// End the AndXBehind operation before dereferencing the file object.
|
|||
|
// The dereference may cause the file object to be closed, and the
|
|||
|
// processing of the close will need to acquire the FCB lock. But
|
|||
|
// RdrFlushCacheFile might be holding the FCB lock while waiting for
|
|||
|
// AndXBehind operations to complete.
|
|||
|
//
|
|||
|
|
|||
|
RdrEndAndXBehindOperation(&WriteHeader->AndXBehind);
|
|||
|
|
|||
|
ObDereferenceObject( WriteHeader->FileObject );
|
|||
|
|
|||
|
FREE_POOL(Context);
|
|||
|
FREE_POOL(Buffer);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
RdrFlushWriteBufferForFile(
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PICB Icb,
|
|||
|
IN BOOLEAN WaitForCompletion
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
NTSTATUS Status1;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFlushWriteBufferForFile: %lx.\n", Icb));
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD( &Icb->u.f.WriteBufferHead );
|
|||
|
|
|||
|
while (!IsListEmpty(&Icb->u.f.WriteBufferHead.BufferList)) {
|
|||
|
PLIST_ENTRY List;
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
|
|||
|
//
|
|||
|
// Remove the buffers in an LRU order.
|
|||
|
//
|
|||
|
|
|||
|
List = RemoveTailList(&Icb->u.f.WriteBufferHead.BufferList);
|
|||
|
|
|||
|
DEBUG List->Flink = NULL;
|
|||
|
DEBUG List->Blink = NULL;
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(List, WRITE_BUFFER, NextWbBuffer);
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFlushWriteBufferForFile: buffer %lx\n", Buffer));
|
|||
|
|
|||
|
//
|
|||
|
// DereferenceWriteBufferLocked expects to own the write buffer
|
|||
|
// head's lock on entry. It releases the lock before returning.
|
|||
|
//
|
|||
|
|
|||
|
Status1 = DereferenceWriteBufferLocked(Irp,
|
|||
|
Buffer,
|
|||
|
WaitForCompletion,
|
|||
|
0,
|
|||
|
TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// If the buffer was flushed, and the flush failed, and this was
|
|||
|
// the first such failure, save the failure status.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS(Status)) {
|
|||
|
Status = Status1;
|
|||
|
}
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD( &Icb->u.f.WriteBufferHead );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ASSERT (Icb->u.f.WriteBufferHead.WriteBuffersAvailable == WRITE_BUFFERS_PER_FILE);
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( &Icb->u.f.WriteBufferHead );
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFlushWriteBufferForFile %lx done, status %lx.\n", Icb, Status));
|
|||
|
|
|||
|
return(Status);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RdrTruncateWriteBufferForFcb (
|
|||
|
IN PFCB Fcb
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY IcbEntry;
|
|||
|
PICB Icb;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForFcb %lx\n", Fcb));
|
|||
|
|
|||
|
if ( Fcb->NonPagedFcb->Type == DiskFile ) {
|
|||
|
for (IcbEntry = Fcb->InstanceChain.Flink ;
|
|||
|
IcbEntry != &Fcb->InstanceChain ;
|
|||
|
IcbEntry = IcbEntry->Flink) {
|
|||
|
|
|||
|
Icb = CONTAINING_RECORD(IcbEntry, ICB, InstanceNext);
|
|||
|
if ( (Icb->Type == DiskFile) &&
|
|||
|
(Icb->Flags & ICB_OPENED) ) {
|
|||
|
RdrTruncateWriteBufferForIcb( Icb );
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForFcb %lx done\n", Fcb));
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RdrTruncateWriteBufferForIcb (
|
|||
|
IN PICB Icb
|
|||
|
)
|
|||
|
{
|
|||
|
PWRITE_BUFFER_HEAD BufferHead;
|
|||
|
PLIST_ENTRY BufferEntry;
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
LARGE_INTEGER BufferEnd;
|
|||
|
LARGE_INTEGER FileSize;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForIcb: %lx.\n", Icb));
|
|||
|
ASSERT (Icb->Type == DiskFile);
|
|||
|
|
|||
|
FileSize = Icb->Fcb->Header.FileSize;
|
|||
|
|
|||
|
BufferHead = &Icb->u.f.WriteBufferHead;
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
//
|
|||
|
// Walk the active buffer list, discarding buffers that lie entirely
|
|||
|
// beyond the new EOF, and truncating those that cross the new EOF.
|
|||
|
//
|
|||
|
|
|||
|
BufferEntry = BufferHead->BufferList.Flink;
|
|||
|
|
|||
|
while (BufferEntry != &BufferHead->BufferList) {
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(BufferEntry, WRITE_BUFFER, NextWbBuffer);
|
|||
|
|
|||
|
BufferEntry = BufferEntry->Flink;
|
|||
|
|
|||
|
//
|
|||
|
// If this buffer starts beyond the new end of file, remove it
|
|||
|
// from the list and discard it.
|
|||
|
//
|
|||
|
|
|||
|
if ( Buffer->ByteOffset.QuadPart > FileSize.QuadPart ) {
|
|||
|
|
|||
|
RemoveEntryList( &Buffer->NextWbBuffer );
|
|||
|
BufferHead->WriteBuffersAvailable += 1;
|
|||
|
BufferHead->WriteBuffersActive--;
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForIcb: buffer %lx discarded\n", Buffer));
|
|||
|
FREE_POOL(Buffer);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If this buffer ends beyond the new end of file, truncate it.
|
|||
|
//
|
|||
|
|
|||
|
BufferEnd.QuadPart = Buffer->ByteOffset.QuadPart + Buffer->Length;
|
|||
|
|
|||
|
if ( BufferEnd.QuadPart > FileSize.QuadPart ) {
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForIcb: buffer %lx truncated\n", Buffer));
|
|||
|
Buffer->Length = (ULONG)(FileSize.QuadPart - Buffer->ByteOffset.QuadPart);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Do the same thing with the flush list.
|
|||
|
//
|
|||
|
|
|||
|
BufferEntry = BufferHead->FlushList.Flink;
|
|||
|
|
|||
|
while (BufferEntry != &BufferHead->FlushList) {
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(BufferEntry, WRITE_BUFFER, NextWbBuffer);
|
|||
|
|
|||
|
BufferEntry = BufferEntry->Flink;
|
|||
|
|
|||
|
//
|
|||
|
// If this buffer starts beyond the new end of file, remove it
|
|||
|
// from the list and discard it.
|
|||
|
//
|
|||
|
|
|||
|
if ( Buffer->ByteOffset.QuadPart > FileSize.QuadPart ) {
|
|||
|
|
|||
|
RemoveEntryList( &Buffer->NextWbBuffer );
|
|||
|
BufferHead->WriteBuffersActive--;
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForIcb: buffer %lx discarded\n", Buffer));
|
|||
|
FREE_POOL(Buffer);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// If this buffer ends beyond the new end of file, truncate it.
|
|||
|
//
|
|||
|
|
|||
|
BufferEnd.QuadPart = Buffer->ByteOffset.QuadPart + Buffer->Length;
|
|||
|
|
|||
|
if ( BufferEnd.QuadPart > FileSize.QuadPart ) {
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForIcb: buffer %lx truncated\n", Buffer));
|
|||
|
Buffer->Length = (ULONG)(FileSize.QuadPart - Buffer->ByteOffset.QuadPart);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD( BufferHead );
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrTruncateWriteBufferForIcb %lx done.\n", Icb));
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
PWRITE_BUFFER
|
|||
|
RdrFindOrAllocateWriteBuffer(
|
|||
|
IN PWRITE_BUFFER_HEAD WriteHeader,
|
|||
|
IN LARGE_INTEGER WriteOffset,
|
|||
|
IN ULONG Length,
|
|||
|
IN LARGE_INTEGER FileValidDataEnd
|
|||
|
)
|
|||
|
{
|
|||
|
PLIST_ENTRY List;
|
|||
|
LARGE_INTEGER TransferEnd;
|
|||
|
LARGE_INTEGER TransferBufferEnd;
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
PWRITE_BUFFER NewBuffer;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
try {
|
|||
|
if (!RdrUseWriteBehind) {
|
|||
|
return (Buffer = NULL);
|
|||
|
}
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: Header: %lx Offset: %lx%lx, Length: %lx\n", WriteHeader, WriteOffset.HighPart, WriteOffset.LowPart, Length));
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Calculate the end of the write. Note that this is actually the
|
|||
|
// first byte NOT written.
|
|||
|
//
|
|||
|
|
|||
|
TransferEnd.QuadPart = WriteOffset.QuadPart + Length;
|
|||
|
TransferBufferEnd.QuadPart = WriteOffset.QuadPart + WriteHeader->MaxDataSize;
|
|||
|
|
|||
|
if ( TransferEnd.QuadPart > TransferBufferEnd.QuadPart) {
|
|||
|
TransferBufferEnd = TransferEnd;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Walk the write behind buffer list. At the end of this loop, if
|
|||
|
// Buffer is not NULL, then it is the buffer to be returned to the
|
|||
|
// caller. If it is NULL, then no matching buffer was found, and a
|
|||
|
// new one must be created.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD(WriteHeader);
|
|||
|
|
|||
|
for (Buffer = NULL, List = WriteHeader->BufferList.Flink ;
|
|||
|
List != &WriteHeader->BufferList ;
|
|||
|
Buffer = NULL, List = List->Flink) {
|
|||
|
LARGE_INTEGER BufferEnd;
|
|||
|
LARGE_INTEGER BufferValidDataEnd;
|
|||
|
|
|||
|
//
|
|||
|
// If the new write overlaps the write buffer, but is not
|
|||
|
// completely contained in the buffer, then we need to flush
|
|||
|
// the buffer. We also need to flush the buffer if the write
|
|||
|
// fits in the buffer but starts beyond the valid data in the
|
|||
|
// buffer AND the end of the buffer's valid data is NOT the end
|
|||
|
// of the file's valid data. This is necessary because we
|
|||
|
// can't read from the file in order to fill in the gap. If
|
|||
|
// the write is beyond the file's valid data, then we can fill
|
|||
|
// the gap with zeroes.
|
|||
|
//
|
|||
|
// Calculate the ending offset of the buffer and the ending
|
|||
|
// offset of the valid data in the buffer. Note that these
|
|||
|
// are actually the first byte AFTER the ending offset, to
|
|||
|
// simplify the calculations.
|
|||
|
//
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(List, WRITE_BUFFER, NextWbBuffer);
|
|||
|
|
|||
|
BufferEnd.QuadPart = Buffer->ByteOffset.QuadPart + WriteHeader->MaxDataSize;
|
|||
|
BufferValidDataEnd.QuadPart = Buffer->ByteOffset.QuadPart + Buffer->Length;
|
|||
|
|
|||
|
//
|
|||
|
// Does the write fall entirely within the buffer?
|
|||
|
//
|
|||
|
|
|||
|
if ((WriteOffset.QuadPart >= Buffer->ByteOffset.QuadPart) &&
|
|||
|
(TransferEnd.QuadPart <= BufferEnd.QuadPart)) {
|
|||
|
|
|||
|
//
|
|||
|
// The write fits in the buffer. If the write starts at
|
|||
|
// or before the end of the valid data in the buffer, then
|
|||
|
// we can just use this buffer. Break out of the loop with
|
|||
|
// Buffer != NULL to indicate that this buffer is to be
|
|||
|
// returned.
|
|||
|
//
|
|||
|
|
|||
|
if (WriteOffset.QuadPart <= BufferValidDataEnd.QuadPart) {
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: using existing buffer %lx Offset: %lx%lx, Length: %lx\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The write starts after the end of the valid data in the
|
|||
|
// buffer. If the end of the buffer's valid data is also
|
|||
|
// the end of the file's valid data, then materialize
|
|||
|
// zeroes in front of the write and break out of the loop.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( BufferValidDataEnd.QuadPart <= FileValidDataEnd.QuadPart);
|
|||
|
|
|||
|
if (BufferValidDataEnd.QuadPart == FileValidDataEnd.QuadPart) {
|
|||
|
|
|||
|
// ASSERT(((LARGE_INTEGER)(WriteOffset.QuadPart - BufferValidDataEnd.QuadPart)).HighPart == 0);
|
|||
|
|
|||
|
RtlZeroMemory(Buffer->Buffer+Buffer->Length,
|
|||
|
(ULONG)(WriteOffset.QuadPart - BufferValidDataEnd.QuadPart));
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: zeroing then using existing buffer %lx Offset: %lx%lx, Length: %lx\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, the write lies completely within the buffer,
|
|||
|
// but starts beyond the valid data in the buffer, which is
|
|||
|
// not at the end of the file's valid data. This means we are
|
|||
|
// missing some data in the write buffer. Since we can't read
|
|||
|
// from the file, we have to flush this write buffer and start
|
|||
|
// a new one.
|
|||
|
//
|
|||
|
// As a simplification, we drop into the next conditional to
|
|||
|
// do the flush. This means a longer code path for this case,
|
|||
|
// but less code overall. We shouldn't hit this code path
|
|||
|
// very often.
|
|||
|
//
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The write doesn't fit within the buffer. Does it overlap?
|
|||
|
// Note that overlap must be calculated using the size of the
|
|||
|
// new buffer that would be allocated for this write, not just
|
|||
|
// the length of this write. Otherwise we could get two buffers
|
|||
|
// covering the same area.
|
|||
|
//
|
|||
|
|
|||
|
if ((WriteOffset.QuadPart < BufferEnd.QuadPart) &&
|
|||
|
(TransferBufferEnd.QuadPart > Buffer->ByteOffset.QuadPart)) {
|
|||
|
|
|||
|
//
|
|||
|
// The write overlaps the buffer. We need to flush the buffer.
|
|||
|
//
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: flushing overlapped buffer %lx Offset: %lx%lx, Length: %lx.\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
RemoveEntryList(List);
|
|||
|
|
|||
|
DEBUG List->Flink = NULL;
|
|||
|
DEBUG List->Blink = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the buffer. The contents will be flushed when
|
|||
|
// all threads writing to the buffer have finished.
|
|||
|
//
|
|||
|
// It doesn't matter if the dereferenced write completes
|
|||
|
// after this one, since the I/O system doesn't guarantee
|
|||
|
// any ordering of the writes if two threads are writing to
|
|||
|
// the same location in the file.
|
|||
|
//
|
|||
|
// Note that this call to DereferenceWriteBufferLocked is
|
|||
|
// made with the write buffer head locked, and returns with
|
|||
|
// it unlocked.
|
|||
|
//
|
|||
|
|
|||
|
(VOID)DereferenceWriteBufferLocked(NULL,
|
|||
|
Buffer,
|
|||
|
(BOOLEAN)!RdrUseAsyncWriteBehind,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
TRUE);
|
|||
|
|
|||
|
//
|
|||
|
// Reacquire the write buffer head lock and restart the scan.
|
|||
|
// We have to restart at the beginning of the list because
|
|||
|
// the list may have changed while we were flushing the
|
|||
|
// buffer.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD(WriteHeader);
|
|||
|
|
|||
|
List = &WriteHeader->BufferList;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The write does not overlap this buffer. Move on.
|
|||
|
//
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: no overlap on buffer %lx Offset: %lx%lx, Length: %lx\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// At this point, we've either found the buffer we want or exhausted
|
|||
|
// the buffer list.
|
|||
|
//
|
|||
|
|
|||
|
if ( Buffer != NULL ) {
|
|||
|
|
|||
|
//
|
|||
|
// We have found the buffer for this write. Reference it, move
|
|||
|
// it to the front of the list (MRU), and return a pointer to it.
|
|||
|
//
|
|||
|
|
|||
|
Buffer->ReferenceCount += 1;
|
|||
|
|
|||
|
RemoveEntryList(List);
|
|||
|
InsertHeadList(&WriteHeader->BufferList, List);
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD(WriteHeader);
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: Found existing %lx: Offset: %lx%lx, Length: %lx\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
|
|||
|
try_return(Buffer);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// There is no matching buffer in the list. Is this write is too big
|
|||
|
// to be cached?
|
|||
|
//
|
|||
|
|
|||
|
|
|||
|
if ( Length > WriteHeader->MaxDataSize ) {
|
|||
|
try_return(Buffer = NULL);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Don't allow more than ACTIVE_WRITE_BUFFERS_PER_FILE write behind buffers
|
|||
|
// per file to be outstanding.
|
|||
|
//
|
|||
|
|
|||
|
if ( WriteHeader->WriteBuffersActive == ACTIVE_WRITE_BUFFERS_PER_FILE ) {
|
|||
|
|
|||
|
try_return(Buffer = NULL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Allocate a new buffer.
|
|||
|
//
|
|||
|
|
|||
|
NewBuffer = ALLOCATE_POOL(PagedPool,
|
|||
|
sizeof(WRITE_BUFFER) - 1 + WriteHeader->MaxDataSize, POOL_WRITEBUFFERBUFFER);
|
|||
|
|
|||
|
if (NewBuffer == NULL) {
|
|||
|
try_return(Buffer = NULL);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the new write buffer.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(NewBuffer, sizeof(WRITE_BUFFER) - 1);
|
|||
|
|
|||
|
NewBuffer->Signature = STRUCTURE_SIGNATURE_WRITE_BUFFER;
|
|||
|
NewBuffer->Size = sizeof(WRITE_BUFFER);
|
|||
|
NewBuffer->BufferHead = WriteHeader;
|
|||
|
NewBuffer->ByteOffset = WriteOffset;
|
|||
|
|
|||
|
//RtlZeroMemory(NewBuffer->Buffer, WriteHeader->MaxDataSize);
|
|||
|
|
|||
|
//
|
|||
|
// Insert the buffer on the list.
|
|||
|
//
|
|||
|
|
|||
|
InsertHeadList(&WriteHeader->BufferList, &NewBuffer->NextWbBuffer);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the reference count to 2 - one for being inserted to the
|
|||
|
// list, the other because we are about to return this buffer.
|
|||
|
//
|
|||
|
|
|||
|
NewBuffer->ReferenceCount = 2;
|
|||
|
|
|||
|
//
|
|||
|
// If there are no write buffers available, flush the oldest one.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (WriteHeader->WriteBuffersAvailable >= 0);
|
|||
|
|
|||
|
//
|
|||
|
// Increment the number of active write buffers
|
|||
|
//
|
|||
|
|
|||
|
WriteHeader->WriteBuffersActive++;
|
|||
|
|
|||
|
//
|
|||
|
// The file's write buffer list is not full. Decrement the count
|
|||
|
// of available buffers to account for the one we just added.
|
|||
|
//
|
|||
|
|
|||
|
if (WriteHeader->WriteBuffersAvailable == 0) {
|
|||
|
|
|||
|
//
|
|||
|
// The file's write buffer list is full. Remove the oldest one.
|
|||
|
//
|
|||
|
|
|||
|
List = RemoveTailList(&WriteHeader->BufferList);
|
|||
|
ASSERT (List != &WriteHeader->BufferList);
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(List, WRITE_BUFFER, NextWbBuffer);
|
|||
|
|
|||
|
DEBUG List->Flink = NULL;
|
|||
|
DEBUG List->Blink = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Dereference the buffer. The contents will be flushed when
|
|||
|
// all threads writing to the buffer have finished.
|
|||
|
//
|
|||
|
// It doesn't matter if the dereferenced write completes
|
|||
|
// after this one, since the I/O system doesn't guarantee
|
|||
|
// any ordering of the writes if two threads are writing to
|
|||
|
// the same location in the file.
|
|||
|
//
|
|||
|
// Note that this call to DereferenceWriteBufferLocked has
|
|||
|
// two special characteristics: 1) it is called with the write
|
|||
|
// buffer head locked, and returns with it unlocked; and 2) it
|
|||
|
// does not increment WriteBuffersAvailable, because we just
|
|||
|
// added a new write buffer.
|
|||
|
//
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: write buffer list full; flushing buffer %lx Offset: %lx%lx, Length: %lx\n", Buffer, Buffer->ByteOffset.HighPart, Buffer->ByteOffset.LowPart, Buffer->Length));
|
|||
|
(VOID)DereferenceWriteBufferLocked(NULL,
|
|||
|
Buffer,
|
|||
|
(BOOLEAN)!RdrUseAsyncWriteBehind,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// The file's write buffer list is not full. Decrement the count
|
|||
|
// of available buffers to account for the one we just added.
|
|||
|
//
|
|||
|
|
|||
|
WriteHeader->WriteBuffersAvailable -= 1;
|
|||
|
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD(WriteHeader);
|
|||
|
}
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: New buffer %lx: Offset: %lx%lx, Length: %lx\n", NewBuffer, NewBuffer->ByteOffset.HighPart, NewBuffer->ByteOffset.LowPart, NewBuffer->Length));
|
|||
|
|
|||
|
try_return(Buffer = NewBuffer);
|
|||
|
|
|||
|
try_exit:NOTHING;
|
|||
|
} finally {
|
|||
|
|
|||
|
if (Buffer == NULL) {
|
|||
|
NTSTATUS Status;
|
|||
|
PICB Icb = WriteHeader->FileObject->FsContext2;
|
|||
|
|
|||
|
ASSERT (Icb->Signature == STRUCTURE_SIGNATURE_ICB);
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrFindOrAllocateWriteBuffer: returning NULL\n"));
|
|||
|
|
|||
|
//
|
|||
|
// There is a possible write ordering problem that can occur if
|
|||
|
// we return before all the flushed writes have completed, so
|
|||
|
// we want to wait for any active flushes to complete before returning
|
|||
|
// NULL.
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// FlushWriteBufferHead will unlock the write header.
|
|||
|
//
|
|||
|
|
|||
|
Status = FlushWriteBufferHead(NULL, WriteHeader, TRUE);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
#if MAGIC_BULLET
|
|||
|
if ( RdrEnableMagic ) {
|
|||
|
RdrSendMagicBullet(NULL);
|
|||
|
DbgPrint( "RDR: About to raise write behind hard error for ICB %x\n", Icb );
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
#endif
|
|||
|
IoRaiseInformationalHardError(Status,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
RdrWriteErrorLogEntry(
|
|||
|
NULL,
|
|||
|
IO_ERR_LAYERED_FAILURE,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
Status,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now wait for any previously initiated write behind operations to
|
|||
|
// complete.
|
|||
|
//
|
|||
|
|
|||
|
RdrWaitForWriteBehindOperation(Icb);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return(Buffer);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RdrDereferenceWriteBuffer(
|
|||
|
IN PWRITE_BUFFER WriteBuffer,
|
|||
|
IN BOOLEAN WaitForCompletion
|
|||
|
)
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Lock the write buffer head, then called DereferenceWriteBufferLocked.
|
|||
|
// This function returns with the write buffer head unlocked.
|
|||
|
//
|
|||
|
|
|||
|
LOCK_WRITE_BUFFER_HEAD(WriteBuffer->BufferHead);
|
|||
|
|
|||
|
(VOID)DereferenceWriteBufferLocked(NULL,
|
|||
|
WriteBuffer,
|
|||
|
WaitForCompletion,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
TRUE);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DereferenceWriteBufferLocked(
|
|||
|
IN PIRP Irp OPTIONAL,
|
|||
|
IN PWRITE_BUFFER WriteBuffer,
|
|||
|
IN BOOLEAN WaitForCompletion,
|
|||
|
IN ULONG EventNumber,
|
|||
|
IN BOOLEAN IncrementWriteBuffersAvailable
|
|||
|
)
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
WriteBuffer->ReferenceCount -= 1;
|
|||
|
|
|||
|
if (WriteBuffer->ReferenceCount > 0) {
|
|||
|
UNLOCK_WRITE_BUFFER_HEAD(WriteBuffer->BufferHead);
|
|||
|
dprintf(DPRT_RITEBHND, ("DereferenceWriteBufferLocked: buffer %lx, new refcnt %d\n", WriteBuffer, WriteBuffer->ReferenceCount));
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT (WriteBuffer->ReferenceCount == 0);
|
|||
|
|
|||
|
//
|
|||
|
// If the ref count goes to 0, it had better not be linked into
|
|||
|
// the list.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT (WriteBuffer->NextWbBuffer.Flink == NULL);
|
|||
|
ASSERT (WriteBuffer->NextWbBuffer.Blink == NULL);
|
|||
|
|
|||
|
//
|
|||
|
// There is one more write buffer available to be put on the list.
|
|||
|
//
|
|||
|
|
|||
|
if (IncrementWriteBuffersAvailable) {
|
|||
|
WriteBuffer->BufferHead->WriteBuffersAvailable += 1;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Stick this buffer to the tail of the "pending write flush list"
|
|||
|
//
|
|||
|
|
|||
|
InsertTailList(&WriteBuffer->BufferHead->FlushList, &WriteBuffer->NextWbBuffer);
|
|||
|
|
|||
|
//
|
|||
|
// Start a flush operation. Note that FlushWriteBufferHead unlocks
|
|||
|
// the write buffer head before returning.
|
|||
|
//
|
|||
|
|
|||
|
dprintf(DPRT_RITEBHND, ("DereferenceWriteBufferLocked: flushing buffer %lx\n", WriteBuffer));
|
|||
|
Status = FlushWriteBufferHead(Irp,
|
|||
|
WriteBuffer->BufferHead,
|
|||
|
WaitForCompletion);
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status) && (EventNumber != 0)) {
|
|||
|
#if MAGIC_BULLET
|
|||
|
if ( RdrEnableMagic ) {
|
|||
|
RdrSendMagicBullet(NULL);
|
|||
|
DbgPrint( "RDR: About to raise write behind hard error for write buffer %x\n", WriteBuffer );
|
|||
|
DbgBreakPoint();
|
|||
|
}
|
|||
|
#endif
|
|||
|
IoRaiseInformationalHardError(Status,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
RdrWriteErrorLogEntry(
|
|||
|
NULL,
|
|||
|
IO_ERR_LAYERED_FAILURE,
|
|||
|
EventNumber,
|
|||
|
Status,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RdrInitializeWriteBufferHead(
|
|||
|
IN PWRITE_BUFFER_HEAD WriteHeader,
|
|||
|
IN PFILE_OBJECT FileObject
|
|||
|
)
|
|||
|
{
|
|||
|
PICB Icb = FileObject->FsContext2;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
WriteHeader->Signature = STRUCTURE_SIGNATURE_WRITE_BUFFER_HEAD;
|
|||
|
WriteHeader->Size = sizeof(WRITE_BUFFER_HEAD);
|
|||
|
|
|||
|
WriteHeader->FileObject = FileObject;
|
|||
|
|
|||
|
InitializeListHead(&WriteHeader->BufferList);
|
|||
|
|
|||
|
InitializeListHead(&WriteHeader->FlushList);
|
|||
|
|
|||
|
WriteHeader->FlushInProgress = FALSE;
|
|||
|
|
|||
|
INITIALIZE_WRITE_BUFFER_HEAD_LOCK(WriteHeader);
|
|||
|
|
|||
|
WriteHeader->MaxDataSize = Icb->Fcb->Connection->Server->BufferSize - (sizeof(SMB_HEADER) + sizeof(REQ_NT_WRITE_ANDX));
|
|||
|
|
|||
|
//
|
|||
|
// We assume WRITE_BUFFERS_PER_FILE buffers for each file.
|
|||
|
//
|
|||
|
|
|||
|
WriteHeader->WriteBuffersAvailable = WRITE_BUFFERS_PER_FILE;
|
|||
|
|
|||
|
//
|
|||
|
// We initialize the count that restricts the number of
|
|||
|
// active write behind buffers.
|
|||
|
//
|
|||
|
|
|||
|
WriteHeader->WriteBuffersActive = 0;
|
|||
|
|
|||
|
RdrInitializeAndXBehind(&WriteHeader->AndXBehind);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
RdrUninitializeWriteBufferHead(
|
|||
|
IN PWRITE_BUFFER_HEAD WriteHeader
|
|||
|
)
|
|||
|
{
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
while (!IsListEmpty(&WriteHeader->BufferList)) {
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
PLIST_ENTRY Entry = RemoveHeadList(&WriteHeader->BufferList);
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(Entry, WRITE_BUFFER, NextWbBuffer);
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrUninitializeWriteBufferHead: buffer %lx still on buffer list\n", Buffer));
|
|||
|
|
|||
|
WriteHeader->WriteBuffersActive--;
|
|||
|
FREE_POOL(Buffer);
|
|||
|
|
|||
|
//
|
|||
|
// If this is not a temporary file, write an error log entry
|
|||
|
// indicating that data has been lost.
|
|||
|
//
|
|||
|
|
|||
|
if (!FlagOn(WriteHeader->FileObject->Flags, FO_TEMPORARY_FILE)) {
|
|||
|
RdrWriteErrorLogEntry(NULL,
|
|||
|
IO_ERR_LAYERED_FAILURE,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
STATUS_SUCCESS,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Discard any buffers on the flush list as well.
|
|||
|
//
|
|||
|
while (!IsListEmpty(&WriteHeader->FlushList)) {
|
|||
|
PWRITE_BUFFER Buffer;
|
|||
|
PLIST_ENTRY Entry = RemoveHeadList(&WriteHeader->FlushList);
|
|||
|
|
|||
|
Buffer = CONTAINING_RECORD(Entry, WRITE_BUFFER, NextWbBuffer);
|
|||
|
dprintf(DPRT_RITEBHND, ("RdrUninitializeWriteBufferHead: buffer %lx still on flush list\n", Buffer));
|
|||
|
|
|||
|
WriteHeader->WriteBuffersActive--;
|
|||
|
FREE_POOL(Buffer);
|
|||
|
|
|||
|
//
|
|||
|
// If this is not a temporary file, write an error log entry
|
|||
|
// indicating that data has been lost.
|
|||
|
//
|
|||
|
|
|||
|
if (!FlagOn(WriteHeader->FileObject->Flags, FO_TEMPORARY_FILE)) {
|
|||
|
RdrWriteErrorLogEntry(NULL,
|
|||
|
IO_ERR_LAYERED_FAILURE,
|
|||
|
EVENT_RDR_WRITE_BEHIND_FLUSH_FAILED,
|
|||
|
STATUS_SUCCESS,
|
|||
|
NULL,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
DELETE_WRITE_BUFFER_HEAD_LOCK(WriteHeader);
|
|||
|
}
|
|||
|
|