mirror of https://github.com/tongzx/nt5src
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.
1819 lines
63 KiB
1819 lines
63 KiB
/*++
|
|
|
|
Copyright (c) 1989 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
Write.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the File Write routine for Write called by the
|
|
dispatch driver.
|
|
|
|
Author:
|
|
|
|
Joe Linn [JoeLinn] 2-Nov-94
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// The local debug trace level
|
|
//
|
|
|
|
#define Dbg (DEBUG_TRACE_WRITE)
|
|
|
|
BOOLEAN RxNoAsync = FALSE;
|
|
|
|
NTSTATUS
|
|
RxLowIoWriteShell (
|
|
IN PRX_CONTEXT RxContext
|
|
);
|
|
|
|
NTSTATUS
|
|
RxLowIoWriteShellCompletion (
|
|
IN PRX_CONTEXT RxContext
|
|
);
|
|
|
|
//defined in read.c
|
|
VOID CheckForLoudOperations(
|
|
PRX_CONTEXT RxContext
|
|
);
|
|
|
|
VOID
|
|
__RxWriteReleaseResources(
|
|
PRX_CONTEXT RxContext
|
|
RX_FCBTRACKER_PARAMS
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, RxCommonWrite)
|
|
#pragma alloc_text(PAGE, __RxWriteReleaseResources)
|
|
#pragma alloc_text(PAGE, RxLowIoWriteShellCompletion)
|
|
#pragma alloc_text(PAGE, RxLowIoWriteShell)
|
|
#endif
|
|
|
|
#if DBG
|
|
#define DECLARE_POSTIRP() PCHAR PostIrp = NULL
|
|
#define SET_POSTIRP(__XXX__) (PostIrp = (__XXX__))
|
|
#define RESET_POSTIRP() (PostIrp = NULL)
|
|
#else
|
|
#define DECLARE_POSTIRP() BOOLEAN PostIrp = FALSE
|
|
#define SET_POSTIRP(__XXX__) (PostIrp = TRUE)
|
|
#define RESET_POSTIRP() (PostIrp = FALSE)
|
|
#endif
|
|
|
|
#ifdef RDBSS_TRACKER
|
|
#define RxWriteReleaseResources(__a__) __RxWriteReleaseResources(__a__,__LINE__,__FILE__,0)
|
|
#else
|
|
#define RxWriteReleaseResources(__a__) __RxWriteReleaseResources(__a__)
|
|
#endif
|
|
|
|
VOID
|
|
__RxWriteReleaseResources(
|
|
PRX_CONTEXT RxContext
|
|
RX_FCBTRACKER_PARAMS
|
|
)
|
|
/* this module frees resources and tracks the state */
|
|
{
|
|
RxCaptureFcb;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT((RxContext!=NULL) && (capFcb!=NULL));
|
|
|
|
if (RxContext->FcbResourceAcquired) {
|
|
|
|
RxDbgTrace( 0, Dbg,("RxCommonWrite ReleasingFcb\n"));
|
|
__RxReleaseFcb(RxContext, (PMRX_FCB)(capFcb)
|
|
#ifdef RDBSS_TRACKER
|
|
,LineNumber,FileName,SerialNumber
|
|
#endif
|
|
);
|
|
RxContext->FcbResourceAcquired = FALSE;
|
|
}
|
|
|
|
if (RxContext->FcbPagingIoResourceAcquired) {
|
|
RxDbgTrace( 0, Dbg,("RxCommonWrite ReleasingPaginIo\n"));
|
|
RxReleasePagingIoResource(capFcb,RxContext);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
RxCommonWrite ( RXCOMMON_SIGNATURE )
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the common write routine for NtWriteFile, called from both
|
|
the Fsd, or from the Fsp if a request could not be completed without
|
|
blocking in the Fsd. This routine's actions are
|
|
conditionalized by the Wait input parameter, which determines whether
|
|
it is allowed to block or not. If a blocking condition is encountered
|
|
with Wait == FALSE, however, the request is posted to the Fsp, who
|
|
always calls with WAIT == TRUE.
|
|
|
|
Arguments:
|
|
|
|
Irp - Supplies the Irp to process
|
|
|
|
Return Value:
|
|
|
|
RXSTATUS - The return status for the operation
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
NODE_TYPE_CODE TypeOfOpen = NodeType(capFcb);
|
|
PSRV_OPEN SrvOpen= (capFobx != NULL) ? capFobx->SrvOpen : NULL;
|
|
PRDBSS_DEVICE_OBJECT RxDeviceObject = RxContext->RxDeviceObject;
|
|
|
|
LARGE_INTEGER StartingByte;
|
|
RXVBO StartingVbo;
|
|
ULONG ByteCount;
|
|
LONGLONG FileSize;
|
|
LONGLONG ValidDataLength;
|
|
LONGLONG InitialFileSize;
|
|
LONGLONG InitialValidDataLength;
|
|
|
|
ULONG CapturedRxContextSerialNumber = RxContext->SerialNumber;
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
DECLARE_POSTIRP();
|
|
|
|
BOOLEAN OplockPostIrp = FALSE;
|
|
BOOLEAN ExtendingFile = FALSE;
|
|
BOOLEAN SwitchBackToAsync = FALSE;
|
|
BOOLEAN CalledByLazyWriter = FALSE;
|
|
BOOLEAN ExtendingValidData = FALSE;
|
|
BOOLEAN WriteFileSizeToDirent = FALSE;
|
|
BOOLEAN RecursiveWriteThrough = FALSE;
|
|
BOOLEAN UnwindOutstandingAsync = FALSE;
|
|
|
|
BOOLEAN RefdContextForTracker = FALSE;
|
|
|
|
BOOLEAN SynchronousIo;
|
|
BOOLEAN WriteToEof;
|
|
BOOLEAN PagingIo;
|
|
BOOLEAN NonCachedIo;
|
|
BOOLEAN Wait;
|
|
|
|
PNET_ROOT NetRoot = (PNET_ROOT)(capFcb->pNetRoot);
|
|
BOOLEAN ThisIsADiskWrite = (BOOLEAN)((NetRoot->Type == NET_ROOT_DISK)||(NetRoot->Type == NET_ROOT_WILD));
|
|
BOOLEAN ThisIsAPipeWrite = (BOOLEAN)(NetRoot->Type == NET_ROOT_PIPE);
|
|
BOOLEAN ThisIsABlockingResume = BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_BLOCKED_PIPE_RESUME);
|
|
|
|
PAGED_CODE();
|
|
|
|
// Get rid of invalid write requests right away.
|
|
if (TypeOfOpen != RDBSS_NTC_STORAGE_TYPE_FILE
|
|
&& TypeOfOpen != RDBSS_NTC_VOLUME_FCB
|
|
&& TypeOfOpen != RDBSS_NTC_SPOOLFILE
|
|
&& TypeOfOpen != RDBSS_NTC_MAILSLOT) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("Invalid file object for write\n", 0 ));
|
|
RxDbgTrace( -1, Dbg, ("RxCommonWrite: Exit -> %08lx\n", STATUS_INVALID_DEVICE_REQUEST ));
|
|
|
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|
}
|
|
|
|
#ifdef RX_WJ_DBG_SUPPORT
|
|
RxdUpdateJournalOnWriteInitiation(
|
|
capFcb,
|
|
capPARAMS->Parameters.Write.ByteOffset,
|
|
capPARAMS->Parameters.Write.Length);
|
|
#endif
|
|
|
|
// Initialize the appropriate local variables.
|
|
Wait = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_WAIT);
|
|
PagingIo = BooleanFlagOn(capReqPacket->Flags, IRP_PAGING_IO);
|
|
NonCachedIo = BooleanFlagOn(capReqPacket->Flags,IRP_NOCACHE);
|
|
SynchronousIo = !BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION);
|
|
|
|
//pick up a write-through specified only for this irp
|
|
if (FlagOn(capPARAMS->Flags,SL_WRITE_THROUGH)) {
|
|
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH );
|
|
}
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxCommonWrite...IrpC %08lx, Fobx %08lx, Fcb %08lx\n",
|
|
RxContext, capFobx, capFcb));
|
|
RxDbgTrace( 0, Dbg, (" ->ByteCount = %08lx, ByteOffset = %08lx %lx\n",
|
|
capPARAMS->Parameters.Write.Length,
|
|
capPARAMS->Parameters.Write.ByteOffset.LowPart,
|
|
capPARAMS->Parameters.Write.ByteOffset.HighPart));
|
|
RxDbgTrace( 0, Dbg,(" ->%s%s%s%s\n",
|
|
Wait ?"Wait ":"",
|
|
PagingIo ?"PagingIo ":"",
|
|
NonCachedIo ?"NonCachedIo ":"",
|
|
SynchronousIo ?"SynchronousIo ":""
|
|
));
|
|
|
|
RxLog(("CommonWrite %lx %lx %lx\n", RxContext, capFobx, capFcb));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_1,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFobx)
|
|
LOGPTR(capFcb));
|
|
RxLog((
|
|
" write %lx@%lx %lx %s%s%s%s\n",
|
|
capPARAMS->Parameters.Write.Length,
|
|
capPARAMS->Parameters.Write.ByteOffset.LowPart,
|
|
capPARAMS->Parameters.Write.ByteOffset.HighPart,
|
|
Wait?"Wt":"",
|
|
PagingIo?"Pg":"",
|
|
NonCachedIo?"Nc":"",
|
|
SynchronousIo?"Sy":""));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_2,
|
|
LOGULONG(capPARAMS->Parameters.Write.Length)
|
|
LOGULONG(capPARAMS->Parameters.Write.ByteOffset.LowPart)
|
|
LOGULONG(capPARAMS->Parameters.Write.ByteOffset.HighPart)
|
|
LOGUCHAR(Wait)
|
|
LOGUCHAR(PagingIo)
|
|
LOGUCHAR(NonCachedIo)
|
|
LOGUCHAR(SynchronousIo));
|
|
|
|
RxItsTheSameContext();
|
|
|
|
RxContext->FcbResourceAcquired = FALSE;
|
|
RxContext->FcbPagingIoResourceAcquired = FALSE;
|
|
|
|
// Extract starting Vbo and offset.
|
|
StartingByte = capPARAMS->Parameters.Write.ByteOffset;
|
|
StartingVbo = StartingByte.QuadPart;
|
|
ByteCount = capPARAMS->Parameters.Write.Length;
|
|
WriteToEof = (StartingVbo < 0);
|
|
|
|
DbgDoit(CheckForLoudOperations(RxContext););
|
|
|
|
IF_DEBUG{
|
|
if (FlagOn(LowIoContext->Flags,LOWIO_CONTEXT_FLAG_LOUDOPS)){
|
|
DbgPrint("LoudWrite %lx/%lx on %lx vdl/size/alloc %lx/%lx/%lx\n",
|
|
StartingByte.LowPart,ByteCount,capFcb,
|
|
capFcb->Header.ValidDataLength.LowPart,
|
|
capFcb->Header.FileSize.LowPart,
|
|
capFcb->Header.AllocationSize.LowPart);
|
|
}
|
|
}
|
|
|
|
//Statistics............
|
|
if (!FlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP)
|
|
&& capFcb->CachedNetRootType == NET_ROOT_DISK) {
|
|
InterlockedIncrement(&RxDeviceObject->WriteOperations);
|
|
|
|
if (StartingVbo != capFobx->Specific.DiskFile.PredictedWriteOffset) {
|
|
InterlockedIncrement(&RxDeviceObject->RandomWriteOperations);
|
|
}
|
|
|
|
capFobx->Specific.DiskFile.PredictedWriteOffset = StartingVbo + ByteCount;
|
|
|
|
if (PagingIo) {
|
|
ExInterlockedAddLargeStatistic(&RxDeviceObject->PagingWriteBytesRequested,ByteCount);
|
|
} else if (NonCachedIo) {
|
|
ExInterlockedAddLargeStatistic(&RxDeviceObject->NonPagingWriteBytesRequested,ByteCount);
|
|
} else {
|
|
ExInterlockedAddLargeStatistic(&RxDeviceObject->CacheWriteBytesRequested,ByteCount);
|
|
}
|
|
}
|
|
|
|
// If there is nothing to write, return immediately or if the buffers are invalid
|
|
// return the appropriate status
|
|
|
|
if (ThisIsADiskWrite && (ByteCount == 0)) {
|
|
return STATUS_SUCCESS;
|
|
} else if ((capReqPacket->UserBuffer == NULL) && (capReqPacket->MdlAddress == NULL)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
} else if ((MAXLONGLONG - StartingVbo < ByteCount) && (!WriteToEof)) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
// See if we have to defer the write. note that if writecacheing is disabled then we don't have
|
|
// to check.
|
|
|
|
if (!NonCachedIo &&
|
|
!CcCanIWrite(
|
|
capFileObject,
|
|
ByteCount,
|
|
(BOOLEAN)(Wait && !BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_IN_FSP)),
|
|
BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_DEFERRED_WRITE))) {
|
|
|
|
BOOLEAN Retrying = BooleanFlagOn(RxContext->Flags, RX_CONTEXT_FLAG_DEFERRED_WRITE);
|
|
|
|
RxPrePostIrp( RxContext, capReqPacket );
|
|
|
|
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_DEFERRED_WRITE );
|
|
|
|
CcDeferWrite( capFileObject,
|
|
(PCC_POST_DEFERRED_WRITE)RxAddToWorkque,
|
|
RxContext,
|
|
capReqPacket,
|
|
ByteCount,
|
|
Retrying );
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
// Initialize LowIO_CONTEXT block in the RxContext
|
|
|
|
RxInitializeLowIoContext(LowIoContext,LOWIO_OP_WRITE);
|
|
|
|
// Use a try-finally to free Fcb and buffers on the way out.
|
|
|
|
try {
|
|
|
|
BOOLEAN DoLowIoWrite = TRUE;
|
|
//
|
|
// This case corresponds to a normal user write file.
|
|
//
|
|
|
|
ASSERT ((TypeOfOpen == RDBSS_NTC_STORAGE_TYPE_FILE )
|
|
|| (TypeOfOpen == RDBSS_NTC_SPOOLFILE)
|
|
|| (TypeOfOpen == RDBSS_NTC_MAILSLOT));
|
|
|
|
RxDbgTrace(0, Dbg, ("Type of write is user file open\n", 0));
|
|
|
|
//
|
|
// If this is a noncached transfer and is not a paging I/O, and
|
|
// the file has been opened cached, then we will do a flush here
|
|
// to avoid stale data problems.
|
|
//
|
|
// The Purge following the flush will guarantee cache coherency.
|
|
//
|
|
|
|
if ((NonCachedIo || !RxWriteCacheingAllowed(capFcb,SrvOpen)) &&
|
|
!PagingIo &&
|
|
(capFileObject->SectionObjectPointer->DataSectionObject != NULL)) {
|
|
|
|
LARGE_INTEGER FlushBase;
|
|
|
|
//
|
|
// We need the Fcb exclusive to do the CcPurgeCache
|
|
//
|
|
|
|
Status = RxAcquireExclusiveFcb( RxContext, capFcb );
|
|
if (Status == STATUS_LOCK_NOT_GRANTED) {
|
|
RxDbgTrace( 0, Dbg, ("Cannot acquire Fcb = %08lx shared without waiting\n", capFcb ));
|
|
try_return( SET_POSTIRP("Couldn't acquireex for flush") );
|
|
} else if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("Cannot acquire Fcb = %08lx shared without waiting\n", capFcb ));
|
|
try_return( PostIrp = FALSE );
|
|
}
|
|
|
|
RxContext->FcbResourceAcquired = TRUE;
|
|
//we don't set fcbacquiredexclusive here since we either return or release
|
|
|
|
|
|
if (WriteToEof) {
|
|
RxGetFileSizeWithLock(capFcb,&FlushBase.QuadPart);
|
|
} else {
|
|
FlushBase = StartingByte;
|
|
}
|
|
|
|
RxAcquirePagingIoResource(capFcb,RxContext);
|
|
|
|
CcFlushCache(
|
|
capFileObject->SectionObjectPointer, //ok4flush
|
|
&FlushBase,
|
|
ByteCount,
|
|
&capReqPacket->IoStatus );
|
|
|
|
RxReleasePagingIoResource(capFcb,RxContext);
|
|
|
|
if (!NT_SUCCESS( capReqPacket->IoStatus.Status)) {
|
|
try_return( Status = capReqPacket->IoStatus.Status );
|
|
}
|
|
|
|
RxAcquirePagingIoResource(capFcb,RxContext);
|
|
RxReleasePagingIoResource(capFcb,RxContext);
|
|
|
|
CcPurgeCacheSection(
|
|
capFileObject->SectionObjectPointer,
|
|
&FlushBase,
|
|
ByteCount,
|
|
FALSE);
|
|
}
|
|
|
|
// We assert that Paging Io writes will never WriteToEof.
|
|
|
|
ASSERT( !(WriteToEof && PagingIo) );
|
|
|
|
//
|
|
// First let's acquire the Fcb shared. Shared is enough if we
|
|
// are not writing beyond EOF.
|
|
//
|
|
|
|
RxItsTheSameContext();
|
|
|
|
if ( PagingIo ) {
|
|
BOOLEAN AcquiredFile;
|
|
|
|
ASSERT( !ThisIsAPipeWrite );
|
|
|
|
AcquiredFile = RxAcquirePagingIoResourceShared(capFcb,TRUE,RxContext);
|
|
|
|
LowIoContext->Resource = capFcb->Header.PagingIoResource;
|
|
|
|
} else if (!ThisIsABlockingResume) {
|
|
//
|
|
// If this could be async, noncached IO we need to check that
|
|
// we don't exhaust the number of times a single thread can
|
|
// acquire the resource.
|
|
//
|
|
// The writes which extend the valid data length result in the the
|
|
// capability of collapsing opens being renounced. This is required to
|
|
// ensure that directory control can see the updated state of the file
|
|
// on close. If this is not done the extended file length is not visible
|
|
// on directory control immediately after a close. In such cases the FCB
|
|
// is accquired exclusive, the changes are made to the buffering state
|
|
// and then downgraded to a shared accquisition.
|
|
|
|
if (!RxContext->FcbResourceAcquired) {
|
|
if (!ThisIsAPipeWrite) {
|
|
if (!Wait &&
|
|
(NonCachedIo || !RxWriteCacheingAllowed(capFcb,SrvOpen))) {
|
|
Status = RxAcquireSharedFcbWaitForEx( RxContext, capFcb );
|
|
} else {
|
|
Status = RxAcquireSharedFcb( RxContext, capFcb );
|
|
}
|
|
} else {
|
|
Status = RxAcquireExclusiveFcb( RxContext, capFcb );
|
|
}
|
|
|
|
if (Status == STATUS_LOCK_NOT_GRANTED) {
|
|
RxDbgTrace( 0, Dbg, ("Cannot acquire Fcb = %08lx shared without waiting\n", capFcb ));
|
|
try_return( SET_POSTIRP("couldn't get mainr w/o waiting sh") );
|
|
} else if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("RxCommonWrite : Cannot acquire Fcb(%lx) %lx\n", capFcb, Status ));
|
|
try_return( PostIrp = FALSE );
|
|
}
|
|
|
|
RxContext->FcbResourceAcquired = TRUE;
|
|
} else {
|
|
ASSERT(!ThisIsAPipeWrite);
|
|
}
|
|
|
|
if (!ThisIsAPipeWrite) {
|
|
if (ExIsResourceAcquiredSharedLite(capFcb->Header.Resource) &&
|
|
(StartingVbo + ByteCount > capFcb->Header.ValidDataLength.QuadPart) &&
|
|
BooleanFlagOn(capFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED)) {
|
|
|
|
RxReleaseFcb(RxContext,capFcb);
|
|
RxContext->FcbResourceAcquired = FALSE;
|
|
|
|
Status = RxAcquireExclusiveFcb( RxContext, capFcb );
|
|
|
|
if (Status == STATUS_LOCK_NOT_GRANTED) {
|
|
RxDbgTrace( 0, Dbg, ("Cannot acquire Fcb = %08lx shared without waiting\n", capFcb ));
|
|
try_return( SET_POSTIRP("couldn't get mainr w/o waiting sh") );
|
|
} else if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("RxCommonWrite : Cannot acquire Fcb(%lx) %lx\n", capFcb, Status ));
|
|
try_return( PostIrp = FALSE );
|
|
} else {
|
|
RxContext->FcbResourceAcquired = TRUE;
|
|
}
|
|
}
|
|
|
|
if ((StartingVbo + ByteCount > capFcb->Header.ValidDataLength.QuadPart) &&
|
|
(BooleanFlagOn(capFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED))) {
|
|
ASSERT ( RxIsFcbAcquiredExclusive ( capFcb ) );
|
|
|
|
RxLog(("RxCommonWrite Disable Collapsing %lx\n",capFcb));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_3,
|
|
LOGPTR(capFcb));
|
|
|
|
// If we are extending the file disable collapsing to ensure that
|
|
// once the file is closed directory control will reflect the sizes
|
|
// correctly.
|
|
ClearFlag(capFcb->FcbState,FCB_STATE_COLLAPSING_ENABLED);
|
|
} else {
|
|
// If the resource has been acquired exclusive we downgrade it
|
|
// to shared. This enables a combination of buffered and
|
|
// unbuffered writes to be synchronized correctly.
|
|
|
|
if (ExIsResourceAcquiredExclusiveLite(capFcb->Header.Resource)) {
|
|
ExConvertExclusiveToSharedLite( capFcb->Header.Resource );
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(RxContext->FcbResourceAcquired);
|
|
LowIoContext->Resource = capFcb->Header.Resource;
|
|
}
|
|
|
|
// for pipe writes, bail out now. we avoid a goto by duplicating the calldown
|
|
// indeed, pipe writes should be removed from the main path.
|
|
|
|
if (ThisIsAPipeWrite) {
|
|
//
|
|
// In order to prevent corruption on multi-threaded multi-block
|
|
// message mode pipe reads, we do this little dance with the fcb resource
|
|
//
|
|
|
|
if (!ThisIsABlockingResume) {
|
|
|
|
if ((capFobx != NULL) &&
|
|
((capFobx->Specific.NamedPipe.TypeOfPipe == FILE_PIPE_MESSAGE_TYPE) ||
|
|
((capFobx->Specific.NamedPipe.TypeOfPipe == FILE_PIPE_BYTE_STREAM_TYPE) &&
|
|
!(capFobx->Specific.NamedPipe.CompletionMode & FILE_PIPE_COMPLETE_OPERATION))) ) {
|
|
|
|
//
|
|
// Synchronization is effected here that will prevent other
|
|
// threads from coming in and reading from this file while the
|
|
// message pipe read is continuing.
|
|
//
|
|
// This is necessary because we will release the FCB lock while
|
|
// actually performing the I/O to allow open (and other) requests
|
|
// to continue on this file while the I/O is in progress.
|
|
//
|
|
|
|
RxDbgTrace( 0,Dbg,("Message pipe write: Fobx: %lx, Fcb: %lx, Enqueuing...\n", capFobx, capFcb ));
|
|
|
|
Status = RxSynchronizeBlockingOperationsAndDropFcbLock(
|
|
RxContext,
|
|
&capFobx->Specific.NamedPipe.WriteSerializationQueue);
|
|
|
|
RxContext->FcbResourceAcquired = FALSE; //this happens in the above routine
|
|
RxItsTheSameContext();
|
|
|
|
if (!NT_SUCCESS(Status) ||
|
|
(Status == STATUS_PENDING)) {
|
|
try_return(Status);
|
|
}
|
|
|
|
RxDbgTrace( 0,Dbg,("Succeeded: Fobx: %lx\n", capFobx ));
|
|
}
|
|
}
|
|
|
|
LowIoContext->ParamsFor.ReadWrite.ByteCount = ByteCount;
|
|
LowIoContext->ParamsFor.ReadWrite.ByteOffset = StartingVbo;
|
|
|
|
SetFlag(
|
|
RxContext->FlagsForLowIo,
|
|
RXCONTEXT_FLAG4LOWIO_PIPE_OPERATION);
|
|
|
|
Status = RxLowIoWriteShell(RxContext);
|
|
|
|
RxItsTheSameContext();
|
|
try_return( Status );
|
|
}
|
|
|
|
// We check whether we can proceed based on the state of the file oplocks.
|
|
|
|
Status = FsRtlCheckOplock(
|
|
&capFcb->Specific.Fcb.Oplock,
|
|
capReqPacket,
|
|
RxContext,
|
|
RxOplockComplete,
|
|
RxPrePostIrp );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
OplockPostIrp = TRUE;
|
|
SET_POSTIRP("couldn't get mainr w/o waiting shstarveex");
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// Set the flag indicating if Fast I/O is possible
|
|
//
|
|
|
|
//capFcb->Header.IsFastIoPossible = RxIsFastIoPossible( capFcb );
|
|
|
|
//
|
|
// If this is the normal data stream object we have to check for
|
|
// write access according to the current state of the file locks.
|
|
|
|
if (!PagingIo &&
|
|
!FsRtlCheckLockForWriteAccess( &capFcb->Specific.Fcb.FileLock,
|
|
capReqPacket )) {
|
|
|
|
try_return( Status = STATUS_FILE_LOCK_CONFLICT );
|
|
}
|
|
|
|
// we never write these without protextion...so the following comment is bogus.
|
|
// also, we manipulate the vdl and filesize as if we owned them.....in fact, we don't unless
|
|
// the file is cached for writing! i'm leaving the comment in case i understand it later
|
|
|
|
// HERE IS THE BOGUS COMMENT!!! (the part about not being protected.......)
|
|
// Get a first tentative file size and valid data length.
|
|
// We must get ValidDataLength first since it is always
|
|
// increased second (the case we are unprotected) and
|
|
// we don't want to capture ValidDataLength > FileSize.
|
|
|
|
ValidDataLength = capFcb->Header.ValidDataLength.QuadPart;
|
|
RxGetFileSizeWithLock(capFcb,&FileSize);
|
|
|
|
ASSERT( ValidDataLength <= FileSize );
|
|
|
|
//
|
|
// If this is paging io, then we do not want
|
|
// to write beyond end of file. If the base is beyond Eof, we will just
|
|
// Noop the call. If the transfer starts before Eof, but extends
|
|
// beyond, we will limit write to file size.
|
|
// Otherwise, in case of write through, since Mm rounds up
|
|
// to a page, we might try to acquire the resource exclusive
|
|
// when our top level guy only acquired it shared. Thus, =><=.
|
|
|
|
// finally, if this is for a minirdr (as opposed to a local miniFS) AND
|
|
// if cacheing is not enabled then i have no idea what VDL is! so, i have to just pass
|
|
// it thru. Currently we do not provide for this case and let the RDBSS
|
|
// throw the write on the floor. A better fix would be to let the mini
|
|
// redirectors deal with it.
|
|
|
|
if (PagingIo) {
|
|
if (StartingVbo >= FileSize) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("PagingIo started beyond EOF.\n", 0 ));
|
|
try_return( Status = STATUS_SUCCESS );
|
|
}
|
|
|
|
if (ByteCount > FileSize - StartingVbo) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("PagingIo extending beyond EOF.\n", 0 ));
|
|
ByteCount = (ULONG)(FileSize - StartingVbo);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine if we were called by the lazywriter.
|
|
// see resrcsup.c for where we captured the lazywriter's thread
|
|
//
|
|
|
|
if (capFcb->Specific.Fcb.LazyWriteThread == PsGetCurrentThread()) {
|
|
|
|
RxDbgTrace( 0, Dbg,("RxCommonWrite ThisIsCalledByLazyWriter%c\n",'!'));
|
|
CalledByLazyWriter = TRUE;
|
|
|
|
if (FlagOn( capFcb->Header.Flags, FSRTL_FLAG_USER_MAPPED_FILE )) {
|
|
|
|
//
|
|
// Fail if the start of this request is beyond valid data length.
|
|
// Don't worry if this is an unsafe test. MM and CC won't
|
|
// throw this page away if it is really dirty.
|
|
//
|
|
|
|
if ((StartingVbo + ByteCount > ValidDataLength) &&
|
|
(StartingVbo < FileSize)) {
|
|
|
|
//
|
|
// It's OK if byte range is within the page containing valid data length,
|
|
// since we will use ValidDataToDisk as the start point.
|
|
//
|
|
|
|
if (StartingVbo + ByteCount > ((ValidDataLength + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) {
|
|
|
|
//
|
|
// Don't flush this now.
|
|
//
|
|
|
|
try_return( Status = STATUS_FILE_LOCK_CONFLICT );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// This code detects if we are a recursive synchronous page write
|
|
// on a write through file object.
|
|
//
|
|
|
|
if (FlagOn(capReqPacket->Flags, IRP_SYNCHRONOUS_PAGING_IO) &&
|
|
FlagOn(RxContext->Flags, RX_CONTEXT_FLAG_RECURSIVE_CALL)) {
|
|
|
|
PIRP TopIrp;
|
|
|
|
TopIrp = RxGetTopIrpIfRdbssIrp();
|
|
|
|
//
|
|
// This clause determines if the top level request was
|
|
// in the FastIo path.
|
|
//
|
|
|
|
if ( (TopIrp != NULL) &&
|
|
((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG) ) {
|
|
|
|
PIO_STACK_LOCATION IrpStack;
|
|
|
|
ASSERT( NodeType(TopIrp) == IO_TYPE_IRP );
|
|
|
|
IrpStack = IoGetCurrentIrpStackLocation(TopIrp);
|
|
|
|
//
|
|
// Finally this routine detects if the Top irp was a
|
|
// write to this file and thus we are the writethrough.
|
|
//
|
|
|
|
if ((IrpStack->MajorFunction == IRP_MJ_WRITE) &&
|
|
(IrpStack->FileObject->FsContext == capFileObject->FsContext)) { //ok4->FileObj butmove
|
|
|
|
RecursiveWriteThrough = TRUE;
|
|
RxDbgTrace( 0, Dbg,("RxCommonWrite ThisIsRecursiveWriteThru%c\n",'!'));
|
|
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_WRITE_THROUGH );
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Here is the deal with ValidDataLength and FileSize:
|
|
//
|
|
// Rule 1: PagingIo is never allowed to extend file size.
|
|
//
|
|
// Rule 2: Only the top level requestor may extend Valid
|
|
// Data Length. This may be paging IO, as when a
|
|
// a user maps a file, but will never be as a result
|
|
// of cache lazy writer writes since they are not the
|
|
// top level request.
|
|
//
|
|
// Rule 3: If, using Rules 1 and 2, we decide we must extend
|
|
// file size or valid data, we take the Fcb exclusive.
|
|
//
|
|
|
|
//
|
|
// Now see if we are writing beyond valid data length, and thus
|
|
// maybe beyond the file size. If so, then we must
|
|
// release the Fcb and reacquire it exclusive. Note that it is
|
|
// important that when not writing beyond EOF that we check it
|
|
// while acquired shared and keep the FCB acquired, in case some
|
|
// turkey truncates the file.
|
|
//
|
|
|
|
//
|
|
// Note that the lazy writer must not be allowed to try and
|
|
// acquire the resource exclusive. This is not a problem since
|
|
// the lazy writer is paging IO and thus not allowed to extend
|
|
// file size, and is never the top level guy, thus not able to
|
|
// extend valid data length.
|
|
|
|
//
|
|
// finally, all the discussion of VDL and filesize is conditioned on the fact
|
|
// that cacheing is enabled. if not, we don't know the VDL OR the filesize and
|
|
// we have to just send out the IOs
|
|
|
|
if ( !CalledByLazyWriter &&
|
|
!RecursiveWriteThrough &&
|
|
( WriteToEof || (StartingVbo + ByteCount > ValidDataLength))) {
|
|
|
|
//
|
|
// If this was an asynchronous write, we are going to make
|
|
// the request synchronous at this point, but only temporarily.
|
|
// At the last moment, before sending the write off to the
|
|
// driver, we may shift back to async.
|
|
//
|
|
// The modified page writer already has the resources
|
|
// he requires, so this will complete in small finite
|
|
// time.
|
|
//
|
|
|
|
if (!SynchronousIo) {
|
|
Wait = TRUE;
|
|
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_WAIT );
|
|
ClearFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION );
|
|
SynchronousIo = TRUE;
|
|
|
|
if (NonCachedIo) {
|
|
|
|
SwitchBackToAsync = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We need Exclusive access to the Fcb since we will
|
|
// probably have to extend valid data and/or file. Drop
|
|
// whatever we have and grab the normal resource exclusive.
|
|
//
|
|
|
|
RxWriteReleaseResources(RxContext); //release whatever resources we may have
|
|
|
|
Status = RxAcquireExclusiveFcb(RxContext, capFcb);
|
|
|
|
if (Status == STATUS_LOCK_NOT_GRANTED) {
|
|
RxDbgTrace( 0, Dbg, ("Cannot acquire Fcb = %08lx shared without waiting\n", capFcb ));
|
|
try_return( SET_POSTIRP("could get excl for extend") );
|
|
} else if (Status != STATUS_SUCCESS) {
|
|
RxDbgTrace( 0, Dbg, ("RxCommonWrite : Cannot acquire Fcb(%lx) : %lx\n", capFcb,Status ));
|
|
try_return( RESET_POSTIRP() );
|
|
}
|
|
|
|
RxItsTheSameContext();
|
|
|
|
RxContext->FcbResourceAcquired = TRUE;
|
|
|
|
//
|
|
// Set the flag indicating if Fast I/O is possible
|
|
//
|
|
|
|
//capFcb->Header.IsFastIoPossible = RxIsFastIoPossible( capFcb );
|
|
|
|
//
|
|
// Now that we have the Fcb exclusive, get a new batch of
|
|
// filesize and ValidDataLength.
|
|
//
|
|
|
|
ValidDataLength = capFcb->Header.ValidDataLength.QuadPart;
|
|
RxGetFileSizeWithLock(capFcb,&FileSize);
|
|
|
|
ASSERT( ValidDataLength <= FileSize );
|
|
|
|
//
|
|
// Now that we have the Fcb exclusive, see if this write
|
|
// qualifies for being made async again. The key point
|
|
// here is that we are going to update ValidDataLength in
|
|
// the Fcb before returning. We must make sure this will
|
|
// not cause a problem. So, if we are extending the file OR if we have
|
|
// a section on the file, we can't go async.
|
|
|
|
// Another thing we must do is keep out
|
|
// the FastIo path.....this is done since we have the resource exclusive
|
|
//
|
|
|
|
if (SwitchBackToAsync) {
|
|
|
|
if ((capFcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL)
|
|
|| ((StartingVbo + ByteCount) > FileSize)
|
|
|| RxNoAsync) {
|
|
|
|
SwitchBackToAsync = FALSE;
|
|
|
|
} else {
|
|
|
|
NOTHING;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// We check whether we can proceed
|
|
// based on the state of the file oplocks.
|
|
//
|
|
|
|
Status = FsRtlCheckOplock(
|
|
&capFcb->Specific.Fcb.Oplock,
|
|
capReqPacket,
|
|
RxContext,
|
|
RxOplockComplete,
|
|
RxPrePostIrp );
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
|
|
OplockPostIrp = TRUE;
|
|
SET_POSTIRP("SecondOplockChk");
|
|
try_return( NOTHING );
|
|
}
|
|
|
|
//
|
|
// If this is PagingIo check again if any pruning is
|
|
// required.
|
|
//
|
|
|
|
if ( PagingIo ) {
|
|
|
|
if (StartingVbo >= FileSize) {
|
|
try_return( Status = STATUS_SUCCESS );
|
|
}
|
|
if (ByteCount > FileSize - StartingVbo) {
|
|
ByteCount = (ULONG)(FileSize - StartingVbo);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember the initial file size and valid data length,
|
|
// just in case .....
|
|
|
|
InitialFileSize = FileSize;
|
|
InitialValidDataLength = ValidDataLength;
|
|
|
|
//
|
|
// Check for writing to end of File. If we are, then we have to
|
|
// recalculate a number of fields. These may have changed if we dropped
|
|
// and regained the resource.
|
|
//
|
|
|
|
if ( WriteToEof ) { //&& RxWriteCacheingAllowed(capFcb,SrvOpen)) {
|
|
StartingVbo = FileSize;
|
|
StartingByte.QuadPart = FileSize;
|
|
}
|
|
|
|
//
|
|
// If this is the normal data stream object we have to check for
|
|
// write access according to the current state of the file locks.
|
|
|
|
if (!PagingIo &&
|
|
!FsRtlCheckLockForWriteAccess( &capFcb->Specific.Fcb.FileLock,
|
|
capReqPacket )) {
|
|
|
|
try_return( Status = STATUS_FILE_LOCK_CONFLICT );
|
|
}
|
|
|
|
//
|
|
// Determine if we will deal with extending the file.
|
|
//
|
|
|
|
if (!PagingIo &&
|
|
ThisIsADiskWrite &&
|
|
(StartingVbo >= 0) &&
|
|
(StartingVbo + ByteCount > FileSize)) {
|
|
RxLog(("NeedToExtending %lx", RxContext));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_4,
|
|
LOGPTR(RxContext));
|
|
ExtendingFile = TRUE;
|
|
|
|
SetFlag(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_EXTENDING_FILESIZE);
|
|
}
|
|
|
|
if ( ExtendingFile ) {
|
|
LARGE_INTEGER OriginalFileSize;
|
|
LARGE_INTEGER OriginalAllocationSize;
|
|
LARGE_INTEGER OriginalValidDataLength;
|
|
|
|
//
|
|
// EXTENDING THE FILE
|
|
//
|
|
// Update our local copy of FileSize
|
|
//
|
|
|
|
OriginalFileSize.QuadPart = capFcb->Header.FileSize.QuadPart;
|
|
OriginalAllocationSize.QuadPart = capFcb->Header.AllocationSize.QuadPart;
|
|
OriginalValidDataLength.QuadPart = capFcb->Header.ValidDataLength.QuadPart;
|
|
|
|
FileSize = StartingVbo + ByteCount;
|
|
|
|
if (FileSize > capFcb->Header.AllocationSize.QuadPart) {
|
|
|
|
LARGE_INTEGER AllocationSize;
|
|
|
|
RxLog(("Extending %lx", RxContext));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_5,
|
|
LOGPTR(RxContext));
|
|
if (NonCachedIo) {
|
|
MINIRDR_CALL(
|
|
Status,
|
|
RxContext,
|
|
capFcb->MRxDispatch,
|
|
MRxExtendForNonCache,
|
|
(RxContext,(PLARGE_INTEGER)&FileSize,&AllocationSize));
|
|
} else {
|
|
MINIRDR_CALL(
|
|
Status,
|
|
RxContext,
|
|
capFcb->MRxDispatch,
|
|
MRxExtendForCache,
|
|
(RxContext,(PLARGE_INTEGER)&FileSize,&AllocationSize));
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RxDbgTrace(0, Dbg, ("Couldn't extend for cacheing.\n", 0));
|
|
try_return(Status);
|
|
}
|
|
|
|
if ( FileSize > AllocationSize.QuadPart ) {
|
|
// When the file is sparse this test is not valid. exclude
|
|
// this case by resetting the allocation size to file size.
|
|
// This effectively implies that we will go to the server
|
|
// for sparse I/O.
|
|
//
|
|
// This test is also not valid for compressed files. NTFS
|
|
// keeps track of the compressed file size and the uncompressed
|
|
// file size. It however returns the compressed file size for
|
|
// directory queries and information queries.
|
|
//
|
|
// For now we rely on the servers return code. If it returned
|
|
// success and the allocation size is less we believe that
|
|
// it is one of the two cases above and set allocation size
|
|
// to the desired file size.
|
|
|
|
AllocationSize.QuadPart = FileSize;
|
|
}
|
|
|
|
//
|
|
// Set the new file allocation in the Fcb.
|
|
//
|
|
capFcb->Header.AllocationSize = AllocationSize;
|
|
}
|
|
|
|
//
|
|
// Set the new file size in the Fcb.
|
|
//
|
|
|
|
RxSetFileSizeWithLock(capFcb,&FileSize);
|
|
RxAdjustAllocationSizeforCC(capFcb);
|
|
|
|
//
|
|
// Extend the cache map, letting mm knows the new file size.
|
|
// We only have to do this if the file is cached.
|
|
//
|
|
|
|
if (CcIsFileCached(capFileObject)) {
|
|
NTSTATUS SetFileSizeStatus = STATUS_SUCCESS;
|
|
|
|
try {
|
|
CcSetFileSizes(
|
|
capFileObject,
|
|
(PCC_FILE_SIZES)&capFcb->Header.AllocationSize );
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
SetFileSizeStatus = GetExceptionCode();
|
|
}
|
|
|
|
if (SetFileSizeStatus != STATUS_SUCCESS) {
|
|
// Restore the original file sizes in the FCB and File object
|
|
capFcb->Header.FileSize.QuadPart = OriginalFileSize.QuadPart;
|
|
capFcb->Header.AllocationSize.QuadPart = OriginalAllocationSize.QuadPart;
|
|
capFcb->Header.ValidDataLength.QuadPart = OriginalValidDataLength.QuadPart;
|
|
|
|
if (capFileObject->SectionObjectPointer->SharedCacheMap != NULL) {
|
|
*CcGetFileSizePointer(capFileObject) = capFcb->Header.FileSize;
|
|
}
|
|
|
|
Status = SetFileSizeStatus;
|
|
try_return(Status);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Determine if we will deal with extending valid data.
|
|
//
|
|
|
|
if ( !CalledByLazyWriter &&
|
|
!RecursiveWriteThrough &&
|
|
(WriteToEof ||
|
|
(StartingVbo + ByteCount > ValidDataLength))) {
|
|
ExtendingValidData = TRUE;
|
|
SetFlag(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_EXTENDING_VDL);
|
|
}
|
|
|
|
//
|
|
// HANDLE CACHED CASE
|
|
//
|
|
|
|
if (!PagingIo &&
|
|
!NonCachedIo && //this part is not discretionary
|
|
RxWriteCacheingAllowed(capFcb,SrvOpen) ) {
|
|
|
|
ASSERT( !PagingIo );
|
|
|
|
//
|
|
// We delay setting up the file cache until now, in case the
|
|
// caller never does any I/O to the file, and thus
|
|
// FileObject->PrivateCacheMap == NULL.
|
|
//
|
|
|
|
if ( capFileObject->PrivateCacheMap == NULL ) {
|
|
|
|
RxDbgTrace(0, Dbg, ("Initialize cache mapping.\n", 0));
|
|
|
|
RxAdjustAllocationSizeforCC(capFcb);
|
|
|
|
//
|
|
// Now initialize the cache map.
|
|
//
|
|
|
|
try {
|
|
Status = STATUS_SUCCESS;
|
|
|
|
CcInitializeCacheMap(
|
|
capFileObject,
|
|
(PCC_FILE_SIZES)&capFcb->Header.AllocationSize,
|
|
FALSE,
|
|
&RxData.CacheManagerCallbacks,
|
|
capFcb );
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Status = GetExceptionCode();
|
|
}
|
|
|
|
if (Status != STATUS_SUCCESS) {
|
|
try_return(Status);
|
|
}
|
|
|
|
CcSetReadAheadGranularity(
|
|
capFileObject,
|
|
NetRoot->DiskParameters.ReadAheadGranularity );
|
|
}
|
|
|
|
|
|
// For local file systems, there's a call here to zero data from VDL
|
|
// to starting VBO....for remote FSs, that happens on the other end.
|
|
|
|
//
|
|
// DO A NORMAL CACHED WRITE, if the MDL bit is not set,
|
|
//
|
|
|
|
if (!FlagOn(RxContext->MinorFunction, IRP_MN_MDL)) {
|
|
|
|
PVOID SystemBuffer;
|
|
DEBUG_ONLY_DECL(ULONG SaveExceptionFlag;)
|
|
|
|
RxDbgTrace(0, Dbg, ("Cached write.\n", 0));
|
|
|
|
//
|
|
// Get hold of the user's buffer.
|
|
|
|
SystemBuffer = RxNewMapUserBuffer( RxContext );
|
|
if (SystemBuffer == NULL) {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
try_return(Status);
|
|
}
|
|
|
|
//
|
|
// Make sure that a returned exception clears the breakpoint in the filter
|
|
|
|
RxSaveAndSetExceptionNoBreakpointFlag(RxContext,SaveExceptionFlag);
|
|
|
|
//
|
|
// Do the write, possibly writing through
|
|
|
|
RxItsTheSameContext();
|
|
if (!CcCopyWrite(
|
|
capFileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
Wait,
|
|
SystemBuffer )) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("Cached Write could not wait\n", 0 ));
|
|
RxRestoreExceptionNoBreakpointFlag(RxContext,SaveExceptionFlag);
|
|
|
|
RxItsTheSameContext();
|
|
|
|
RxLog(
|
|
("CcCW2 FO %lx Of %lx Si %lx St %lx\n",
|
|
capFileObject,
|
|
capFcb->Header.FileSize.LowPart,
|
|
ByteCount,
|
|
Status));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_6,
|
|
LOGPTR(capFileObject)
|
|
LOGULONG(capFcb->Header.FileSize.LowPart)
|
|
LOGULONG(ByteCount)
|
|
LOGULONG(Status));
|
|
|
|
try_return( SET_POSTIRP("cccopywritefailed") );
|
|
}
|
|
|
|
capReqPacket->IoStatus.Status = STATUS_SUCCESS;
|
|
capReqPacket->IoStatus.Information = ByteCount;
|
|
RxRestoreExceptionNoBreakpointFlag(RxContext,SaveExceptionFlag);
|
|
RxItsTheSameContext();
|
|
|
|
RxLog(
|
|
("CcCW3 FO %lx Of %lx Si %lx St %lx\n",
|
|
capFileObject,
|
|
capFcb->Header.FileSize.LowPart,
|
|
ByteCount,
|
|
Status));
|
|
RxWmiLog(LOG,
|
|
RxCommonWrite_7,
|
|
LOGPTR(capFileObject)
|
|
LOGULONG(capFcb->Header.FileSize.LowPart)
|
|
LOGULONG(ByteCount)
|
|
LOGULONG(Status));
|
|
|
|
try_return( Status = STATUS_SUCCESS );
|
|
} else {
|
|
|
|
//
|
|
// DO AN MDL WRITE
|
|
//
|
|
|
|
RxDbgTrace(0, Dbg, ("MDL write.\n", 0));
|
|
|
|
ASSERT(FALSE); //NOT YET IMPLEMENTED
|
|
ASSERT( Wait );
|
|
|
|
CcPrepareMdlWrite(
|
|
capFileObject,
|
|
&StartingByte,
|
|
ByteCount,
|
|
&capReqPacket->MdlAddress,
|
|
&capReqPacket->IoStatus );
|
|
|
|
Status = capReqPacket->IoStatus.Status;
|
|
|
|
try_return( Status );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// HANDLE THE NON-CACHED CASE
|
|
|
|
if (SwitchBackToAsync) {
|
|
|
|
Wait = FALSE;
|
|
SynchronousIo = FALSE;
|
|
ClearFlag( RxContext->Flags, RX_CONTEXT_FLAG_WAIT );
|
|
SetFlag( RxContext->Flags, RX_CONTEXT_FLAG_ASYNC_OPERATION );
|
|
}
|
|
|
|
if (!SynchronousIo) {
|
|
//here we have to setup for async writes. there is a special dance in acquiring
|
|
//the fcb that looks at these variables..........
|
|
|
|
//only init on the first usage
|
|
if (!capFcb->NonPaged->OutstandingAsyncEvent) {
|
|
|
|
capFcb->NonPaged->OutstandingAsyncEvent =
|
|
&capFcb->NonPaged->TheActualEvent;
|
|
|
|
KeInitializeEvent(
|
|
capFcb->NonPaged->OutstandingAsyncEvent,
|
|
NotificationEvent,
|
|
FALSE );
|
|
}
|
|
|
|
//
|
|
// If we are transitioning from 0 to 1, reset the event.
|
|
|
|
if (ExInterlockedAddUlong(
|
|
&capFcb->NonPaged->OutstandingAsyncWrites,
|
|
1,
|
|
&RxStrucSupSpinLock ) == 0) {
|
|
|
|
KeResetEvent( capFcb->NonPaged->OutstandingAsyncEvent );
|
|
}
|
|
|
|
UnwindOutstandingAsync = TRUE; // this says that we counted an async write
|
|
LowIoContext->ParamsFor.ReadWrite.NonPagedFcb = capFcb->NonPaged;
|
|
}
|
|
|
|
|
|
LowIoContext->ParamsFor.ReadWrite.ByteCount = ByteCount;
|
|
LowIoContext->ParamsFor.ReadWrite.ByteOffset = StartingVbo;
|
|
|
|
RxDbgTrace( 0, Dbg,("RxCommonWriteJustBeforeCalldown %s%s%s lowiononpaged is \n",
|
|
RxContext->FcbResourceAcquired ?"FcbAcquired ":"",
|
|
RxContext->FcbPagingIoResourceAcquired ?"PagingIoResourceAcquired ":"",
|
|
(LowIoContext->ParamsFor.ReadWrite.NonPagedFcb)?"NonNull":"Null"
|
|
));
|
|
|
|
RxItsTheSameContext();
|
|
ASSERT ( RxContext->FcbResourceAcquired || RxContext->FcbPagingIoResourceAcquired );
|
|
|
|
Status = RxLowIoWriteShell(RxContext);
|
|
|
|
RxItsTheSameContext();
|
|
if (UnwindOutstandingAsync && Status==STATUS_PENDING) {
|
|
UnwindOutstandingAsync = FALSE;
|
|
}
|
|
|
|
try_return( Status );
|
|
|
|
try_exit: NOTHING;
|
|
|
|
ASSERT(capReqPacket);
|
|
|
|
RxItsTheSameContext();
|
|
if ( !PostIrp ) {
|
|
|
|
RxDbgTrace( 0, Dbg, ("CommonWrite InnerFinally-> %08lx,%08lx\n",
|
|
Status, capReqPacket->IoStatus.Information));
|
|
|
|
if (!ThisIsAPipeWrite) {
|
|
|
|
//
|
|
// Record the total number of bytes actually written
|
|
//
|
|
|
|
if (!PagingIo && NT_SUCCESS(Status) &&
|
|
BooleanFlagOn(capFileObject->Flags, FO_SYNCHRONOUS_IO)) {
|
|
capFileObject->CurrentByteOffset.QuadPart =
|
|
StartingVbo + capReqPacket->IoStatus.Information;
|
|
}
|
|
|
|
//
|
|
// The following are things we only do if we were successful
|
|
//
|
|
|
|
if ( NT_SUCCESS( Status ) && (Status!=STATUS_PENDING) ) {
|
|
|
|
//
|
|
// If this was not PagingIo, mark that the modify
|
|
// time on the dirent needs to be updated on close.
|
|
//
|
|
|
|
if ( !PagingIo ) {
|
|
|
|
SetFlag( capFileObject->Flags, FO_FILE_MODIFIED );
|
|
}
|
|
|
|
if ( ExtendingFile ) {
|
|
|
|
SetFlag( capFileObject->Flags, FO_FILE_SIZE_CHANGED );
|
|
}
|
|
|
|
if ( ExtendingValidData ) {
|
|
|
|
LONGLONG EndingVboWritten = StartingVbo + capReqPacket->IoStatus.Information;
|
|
|
|
//
|
|
// Never set a ValidDataLength greater than FileSize.
|
|
|
|
capFcb->Header.ValidDataLength.QuadPart =
|
|
( FileSize < EndingVboWritten ) ? FileSize : EndingVboWritten;
|
|
|
|
//
|
|
// Now, if we are noncached and the file is cached, we must
|
|
// tell the cache manager about the VDL extension so that
|
|
// async cached IO will not be optimized into zero-page faults
|
|
// beyond where it believes VDL is.
|
|
//
|
|
// In the cached case, since Cc did the work, it has updated
|
|
// itself already.
|
|
//
|
|
|
|
if (NonCachedIo && CcIsFileCached(capFileObject)) {
|
|
CcSetFileSizes( capFileObject, (PCC_FILE_SIZES)&capFcb->Header.AllocationSize );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Take action if the Oplock package is not going to post the Irp.
|
|
//
|
|
|
|
if (!OplockPostIrp) {
|
|
|
|
if ( ExtendingFile && !ThisIsAPipeWrite ) {
|
|
|
|
ASSERT ( RxWriteCacheingAllowed(capFcb,SrvOpen) );
|
|
|
|
//
|
|
// We need the PagingIo resource exclusive whenever we
|
|
// pull back either file size or valid data length.
|
|
//
|
|
|
|
ASSERT ( capFcb->Header.PagingIoResource != NULL );
|
|
|
|
RxAcquirePagingIoResource(capFcb,RxContext);
|
|
|
|
RxSetFileSizeWithLock(capFcb,&InitialFileSize);
|
|
RxReleasePagingIoResource(capFcb,RxContext);
|
|
|
|
//
|
|
// Pull back the cache map as well
|
|
//
|
|
|
|
if (capFileObject->SectionObjectPointer->SharedCacheMap != NULL) {
|
|
*CcGetFileSizePointer(capFileObject) = capFcb->Header.FileSize;
|
|
}
|
|
}
|
|
|
|
RxDbgTrace( 0, Dbg, ("Passing request to Fsp\n", 0 ));
|
|
|
|
InterlockedIncrement(&RxContext->ReferenceCount);
|
|
RefdContextForTracker = TRUE;
|
|
|
|
// we only do this here because we're having a problem finding out why resources
|
|
// are not released.
|
|
RxWriteReleaseResources(RxContext); //release whatever resources we may have
|
|
|
|
#ifdef RDBSS_TRACKER
|
|
if (RxContext->AcquireReleaseFcbTrackerX != 0) {
|
|
DbgPrint("TrackerNBadBeforePost %08lx %08lx\n",RxContext,&PostIrp);
|
|
ASSERT(!"BadTrackerBeforePost");
|
|
}
|
|
#endif //ifdef RDBSS_TRACKER
|
|
Status = RxFsdPostRequest(RxContext);
|
|
}
|
|
}
|
|
|
|
} finally {
|
|
|
|
DebugUnwind( RxCommonWrite );
|
|
|
|
if (AbnormalTermination()) {
|
|
//
|
|
// Restore initial file size and valid data length
|
|
//
|
|
|
|
if ((ExtendingFile || ExtendingValidData) && !ThisIsAPipeWrite) {
|
|
|
|
//
|
|
// We got an error, pull back the file size if we extended it.
|
|
//
|
|
// We need the PagingIo resource exclusive whenever we
|
|
// pull back either file size or valid data length.
|
|
//
|
|
|
|
ASSERT (capFcb->Header.PagingIoResource != NULL);
|
|
|
|
(VOID)RxAcquirePagingIoResource(capFcb,RxContext);
|
|
|
|
RxSetFileSizeWithLock(capFcb,&InitialFileSize);
|
|
|
|
capFcb->Header.ValidDataLength.QuadPart = InitialValidDataLength;
|
|
|
|
RxReleasePagingIoResource(capFcb,RxContext);
|
|
|
|
//
|
|
// Pull back the cache map as well
|
|
//
|
|
if (capFileObject->SectionObjectPointer->SharedCacheMap != NULL) {
|
|
*CcGetFileSizePointer(capFileObject) = capFcb->Header.FileSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check if this needs to be backed out.
|
|
//
|
|
|
|
if (UnwindOutstandingAsync) {
|
|
|
|
ASSERT (!ThisIsAPipeWrite);
|
|
ExInterlockedAddUlong( &capFcb->NonPaged->OutstandingAsyncWrites,
|
|
0xffffffff,
|
|
&RxStrucSupSpinLock );
|
|
|
|
KeSetEvent( LowIoContext->ParamsFor.ReadWrite.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE );
|
|
}
|
|
#if 0
|
|
//
|
|
// If we did an MDL write, and we are going to complete the request
|
|
// successfully, keep the resource acquired, reducing to shared
|
|
// if it was acquired exclusive.
|
|
//
|
|
|
|
if (FlagOn(RxContext->MinorFunction, IRP_MN_MDL) &&
|
|
!PostIrp &&
|
|
!AbnormalTermination() &&
|
|
NT_SUCCESS(Status)) {
|
|
|
|
ASSERT( FcbAcquired && !PagingIoResourceAcquired );
|
|
|
|
FcbAcquired = FALSE;
|
|
|
|
if (FcbAcquiredExclusive) {
|
|
|
|
ExConvertExclusiveToSharedLite( Fcb->Header.Resource );
|
|
}
|
|
}
|
|
#endif
|
|
//
|
|
// If resources have been acquired, release them under the right conditions.
|
|
// the right conditions are these:
|
|
// 1) if we have abnormal termination. here we obviously release the since no one else will.
|
|
// 2) if the underlying call did not succeed: Status==Pending.
|
|
// 3) if we posted the request
|
|
|
|
if (AbnormalTermination() || (Status!=STATUS_PENDING) || PostIrp) {
|
|
if (!PostIrp) {
|
|
RxWriteReleaseResources(RxContext); //release whatever resources we may have
|
|
}
|
|
|
|
if (RefdContextForTracker) {
|
|
RxDereferenceAndDeleteRxContext(RxContext);
|
|
}
|
|
|
|
if (!PostIrp) {
|
|
if (FlagOn(RxContext->FlagsForLowIo,RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION)) {
|
|
RxResumeBlockedOperations_Serially(
|
|
RxContext,
|
|
&capFobx->Specific.NamedPipe.WriteSerializationQueue);
|
|
}
|
|
}
|
|
|
|
if (Status == STATUS_SUCCESS) {
|
|
ASSERT ( capReqPacket->IoStatus.Information
|
|
<= capPARAMS->Parameters.Write.Length );
|
|
}
|
|
} else {
|
|
//here the guy below is going to handle the completion....but, we don't know the finish
|
|
//order....in all likelihood the deletecontext call below just reduces the refcount
|
|
// but the guy may already have finished in which case this will really delete the context.
|
|
ASSERT(!SynchronousIo);
|
|
RxDereferenceAndDeleteRxContext(RxContext);
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("CommonWrite -> %08lx\n", Status ));
|
|
|
|
if( (Status!=STATUS_PENDING) && (Status!=STATUS_SUCCESS) )
|
|
{
|
|
if( PagingIo )
|
|
{
|
|
RxLogRetail(( "PgWrtFail %x %x %x\n", capFcb, NetRoot, Status ));
|
|
}
|
|
}
|
|
|
|
} //finally
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Internal support routine
|
|
//
|
|
|
|
NTSTATUS
|
|
RxLowIoWriteShellCompletion (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine postprocesses a write request after it comes back from the
|
|
minirdr. It does callouts to handle compression, buffering and
|
|
shadowing. It is the opposite number of LowIoWriteShell.
|
|
|
|
This will be called from LowIo; for async, originally in the
|
|
completion routine. If RxStatus(MORE_PROCESSING_REQUIRED) is returned,
|
|
LowIo will call again in a thread. If this was syncIo, you'll be back
|
|
in the user's thread; if async, lowIo will requeue to a thread.
|
|
Currrently, we always get to a thread before anything; this is a bit slower
|
|
than completing at DPC time,
|
|
but it's aheckuva lot safer and we may often have stuff to do
|
|
(like decompressing, shadowing, etc) that we don't want to do at DPC
|
|
time.
|
|
|
|
Arguments:
|
|
|
|
RxContext - the usual
|
|
|
|
Return Value:
|
|
|
|
whatever value supplied by the caller or RxStatus(MORE_PROCESSING_REQUIRED).
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxCaptureRequestPacket;
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
RxCaptureParamBlock;
|
|
RxCaptureFileObject;
|
|
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
BOOLEAN SynchronousIo = !BooleanFlagOn(RxContext->Flags,RX_CONTEXT_FLAG_ASYNC_OPERATION);
|
|
BOOLEAN PagingIo = BooleanFlagOn(capReqPacket->Flags, IRP_PAGING_IO);
|
|
BOOLEAN ThisIsAPipeOperation =
|
|
BooleanFlagOn(RxContext->FlagsForLowIo,RXCONTEXT_FLAG4LOWIO_PIPE_OPERATION);
|
|
BOOLEAN ThisIsASynchronizedPipeOperation =
|
|
BooleanFlagOn(RxContext->FlagsForLowIo,RXCONTEXT_FLAG4LOWIO_PIPE_SYNC_OPERATION);
|
|
|
|
PAGED_CODE();
|
|
|
|
Status = RxContext->StoredStatus;
|
|
capReqPacket->IoStatus.Information = RxContext->InformationToReturn;
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxLowIoWriteShellCompletion entry Status = %08lx\n", Status));
|
|
RxLog(("WtShlComp %lx %lx %lx\n",RxContext,Status,capReqPacket->IoStatus.Information));
|
|
RxWmiLog(LOG,
|
|
RxLowIoWriteShellCompletion_1,
|
|
LOGPTR(RxContext)
|
|
LOGULONG(Status)
|
|
LOGPTR(capReqPacket->IoStatus.Information));
|
|
|
|
ASSERT (RxLowIoIsBufferLocked(LowIoContext));
|
|
|
|
switch (Status) {
|
|
case STATUS_SUCCESS:
|
|
{
|
|
if(FlagOn(RxContext->FlagsForLowIo, RXCONTEXT_FLAG4LOWIO_THIS_IO_BUFFERED)){
|
|
if (FlagOn(capFcb->FcbState, FCB_STATE_FILE_IS_DISK_COMPRESSED)){
|
|
ASSERT(FALSE); // NOT YET IMPLEMENTED should decompress and put away
|
|
} else if (FlagOn(capFcb->FcbState, FCB_STATE_FILE_IS_BUF_COMPRESSED)){
|
|
ASSERT(FALSE); // NOT YET IMPLEMENTED should decompress and put away
|
|
}
|
|
}
|
|
if (FlagOn(capFcb->FcbState, FCB_STATE_FILE_IS_SHADOWED)) {
|
|
ASSERT(FALSE); //RxSdwAddData(RxContext);
|
|
}
|
|
|
|
#ifdef RX_WJ_DBG_SUPPORT
|
|
RxdUpdateJournalOnLowIoWriteCompletion(
|
|
capFcb,
|
|
capPARAMS->Parameters.Write.ByteOffset,
|
|
capPARAMS->Parameters.Write.Length);
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case STATUS_FILE_LOCK_CONFLICT:
|
|
break;
|
|
|
|
case STATUS_CONNECTION_INVALID:
|
|
//NOT YET IMPLEMENTED here is where the failover will happen
|
|
//first we give the local guy current minirdr another chance...then we go
|
|
//to fullscale retry
|
|
//return(RxStatus(DISCONNECTED)); //special....let LowIo get us back
|
|
break;
|
|
}
|
|
|
|
if( Status!=STATUS_SUCCESS )
|
|
{
|
|
if( PagingIo )
|
|
{
|
|
RxLogRetail(( "PgWrtFail %x %x %x\n", capFcb, capFcb->pNetRoot, Status ));
|
|
}
|
|
}
|
|
|
|
if (FlagOn(LowIoContext->Flags,LOWIO_CONTEXT_FLAG_SYNCCALL)){
|
|
//if we're being called from lowioubmit then just get out
|
|
RxDbgTrace(-1, Dbg, ("RxLowIoWriteShellCompletion syncexit Status = %08lx\n", Status));
|
|
return(Status);
|
|
}
|
|
|
|
//otherwise we have to do the end of the write from here
|
|
if ( NT_SUCCESS( Status ) && !ThisIsAPipeOperation ) {
|
|
|
|
ASSERT( capReqPacket->IoStatus.Information == LowIoContext->ParamsFor.ReadWrite.ByteCount );
|
|
|
|
// If this was not PagingIo, mark that the modify
|
|
// time on the dirent needs to be updated on close.
|
|
if ( !PagingIo ) {
|
|
|
|
SetFlag( capFileObject->Flags, FO_FILE_MODIFIED );
|
|
}
|
|
|
|
if ( FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_EXTENDING_FILESIZE) ) {
|
|
|
|
SetFlag( capFileObject->Flags, FO_FILE_SIZE_CHANGED );
|
|
}
|
|
|
|
if ( FlagOn(LowIoContext->ParamsFor.ReadWrite.Flags,LOWIO_READWRITEFLAG_EXTENDING_VDL) ) {
|
|
|
|
//this flag will not be set unless we have a valid filesize and therefore the starting
|
|
//vbo will not be write-to-end-of-file
|
|
LONGLONG StartingVbo = LowIoContext->ParamsFor.ReadWrite.ByteOffset;
|
|
LONGLONG EndingVboWritten = StartingVbo + capReqPacket->IoStatus.Information;
|
|
LONGLONG FileSize;// = capFcb->Header.FileSize.QuadPart;
|
|
|
|
//
|
|
// Never set a ValidDataLength greater than FileSize.
|
|
//
|
|
RxGetFileSizeWithLock(capFcb,&FileSize);
|
|
capFcb->Header.ValidDataLength.QuadPart =
|
|
( FileSize < EndingVboWritten ) ? FileSize : EndingVboWritten;
|
|
|
|
}
|
|
}
|
|
|
|
if ((!ThisIsASynchronizedPipeOperation) &&
|
|
(LowIoContext->ParamsFor.ReadWrite.NonPagedFcb != NULL) &&
|
|
(ExInterlockedAddUlong( &LowIoContext->ParamsFor.ReadWrite.NonPagedFcb->OutstandingAsyncWrites,
|
|
0xffffffff,
|
|
&RxStrucSupSpinLock ) == 1) ) {
|
|
|
|
KeSetEvent( LowIoContext->ParamsFor.ReadWrite.NonPagedFcb->OutstandingAsyncEvent, 0, FALSE );
|
|
}
|
|
|
|
if ( RxContext->FcbPagingIoResourceAcquired ) {
|
|
RxReleasePagingIoResourceForThread(capFcb,LowIoContext->ResourceThreadId,RxContext);
|
|
}
|
|
|
|
if ((!ThisIsASynchronizedPipeOperation) &&
|
|
(RxContext->FcbResourceAcquired)) {
|
|
RxReleaseFcbForThread(
|
|
RxContext,
|
|
capFcb,
|
|
LowIoContext->ResourceThreadId );
|
|
}
|
|
|
|
if (ThisIsASynchronizedPipeOperation) {
|
|
RxCaptureFobx;
|
|
|
|
RxResumeBlockedOperations_Serially(
|
|
RxContext,
|
|
&capFobx->Specific.NamedPipe.WriteSerializationQueue);
|
|
}
|
|
|
|
ASSERT(Status != STATUS_RETRY);
|
|
if ( Status != STATUS_RETRY){
|
|
ASSERT( (Status != STATUS_SUCCESS) ||
|
|
(capReqPacket->IoStatus.Information <= capPARAMS->Parameters.Write.Length ));
|
|
ASSERT (RxContext->MajorFunction == IRP_MJ_WRITE);
|
|
}
|
|
|
|
if (ThisIsAPipeOperation) {
|
|
RxCaptureFobx;
|
|
|
|
if (capReqPacket->IoStatus.Information != 0) {
|
|
|
|
// if we have been throttling on this pipe, stop because our writing to the pipe may
|
|
// cause the pipeserver (not smbserver) on the other end to unblock so we should go back
|
|
// and see
|
|
|
|
RxTerminateThrottling(&capFobx->Specific.NamedPipe.ThrottlingState);
|
|
|
|
RxLog((
|
|
"WThrottlNo %lx %lx %lx %ld\n",
|
|
RxContext,capFobx,&capFobx->Specific.NamedPipe.ThrottlingState,
|
|
capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
RxWmiLog(LOG,
|
|
RxLowIoWriteShellCompletion_2,
|
|
LOGPTR(RxContext)
|
|
LOGPTR(capFobx)
|
|
LOGULONG(capFobx->Specific.NamedPipe.ThrottlingState.NumberOfQueries));
|
|
}
|
|
}
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxLowIoWriteShellCompletion exit Status = %08lx\n", Status));
|
|
return(Status);
|
|
}
|
|
|
|
#define RxSdwWrite(RXCONTEXT) STATUS_MORE_PROCESSING_REQUIRED
|
|
NTSTATUS
|
|
RxLowIoWriteShell (
|
|
IN PRX_CONTEXT RxContext
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine preprocesses a write request before it goes down to the minirdr.
|
|
It does callouts to handle compression, buffering and shadowing. It is the
|
|
opposite number of LowIoWriteShellCompletion. By the time we get here, we are
|
|
going to the wire. Write buffering was already tried in the UncachedWrite strategy
|
|
|
|
Arguments:
|
|
|
|
RxContext - the usual
|
|
|
|
Return Value:
|
|
|
|
whatever value is returned by a callout....or by LowIo.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
RxCaptureFcb;
|
|
RxCaptureFobx;
|
|
RxCaptureParamBlock;
|
|
|
|
PLOWIO_CONTEXT LowIoContext = &RxContext->LowIoContext;
|
|
|
|
PAGED_CODE();
|
|
|
|
RxDbgTrace(+1, Dbg, ("RxLowIoWriteShell entry %08lx\n", 0));
|
|
RxLog(("WrtShl in %lx\n",RxContext));
|
|
RxWmiLog(LOG,
|
|
RxLowIoWriteShell_1,
|
|
LOGPTR(RxContext));
|
|
|
|
if (FlagOn(capFcb->FcbState, FCB_STATE_FILE_IS_SHADOWED)) {
|
|
RxSdwWrite(RxContext);
|
|
}
|
|
|
|
if (FlagOn(capFcb->FcbState, FCB_STATE_FILE_IS_DISK_COMPRESSED)){
|
|
// NOT YET IMPLEMENTED should translated to a buffered but not held diskcompressed write
|
|
ASSERT(FALSE);
|
|
} else if (FlagOn(capFcb->FcbState, FCB_STATE_FILE_IS_BUF_COMPRESSED)){
|
|
// NOT YET IMPLEMENTED should translated to a buffered and bufcompressed write
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
if (capFcb->CachedNetRootType == NET_ROOT_DISK) {
|
|
ExInterlockedAddLargeStatistic(
|
|
&RxContext->RxDeviceObject->NetworkReadBytesRequested,
|
|
LowIoContext->ParamsFor.ReadWrite.ByteCount);
|
|
}
|
|
|
|
#ifdef RX_WJ_DBG_SUPPORT
|
|
RxdUpdateJournalOnLowIoWriteInitiation(
|
|
capFcb,
|
|
capPARAMS->Parameters.Write.ByteOffset,
|
|
capPARAMS->Parameters.Write.Length);
|
|
#endif
|
|
|
|
Status = RxLowIoSubmit(
|
|
RxContext,
|
|
RxLowIoWriteShellCompletion);
|
|
|
|
RxDbgTrace(-1, Dbg, ("RxLowIoWriteShell exit Status = %08lx\n", Status));
|
|
RxLog(("WrtShl out %lx %lx\n",RxContext,Status));
|
|
RxWmiLog(LOG,
|
|
RxLowIoWriteShell_2,
|
|
LOGPTR(RxContext)
|
|
LOGULONG(Status));
|
|
|
|
return(Status);
|
|
}
|
|
|
|
|