📄 write.c
字号:
ASSERT( WriteToEof ? !PagingIo : TRUE );
//
// First let's acquire the Fcb shared. Shared is enough if we
// are not writing beyond EOF.
//
if ( PagingIo ) {
(VOID)ExAcquireResourceSharedLite( FcbOrDcb->Header.PagingIoResource, TRUE );
PagingIoResourceAcquired = TRUE;
if (!Wait) {
IrpContext->FatIoContext->Wait.Async.Resource =
FcbOrDcb->Header.PagingIoResource;
}
//
// Check to see if we colided with a MoveFile call, and if
// so block until it completes.
//
if (FcbOrDcb->MoveFileEvent) {
(VOID)KeWaitForSingleObject( FcbOrDcb->MoveFileEvent,
Executive,
KernelMode,
FALSE,
NULL );
}
} else {
//
// We may already have the Fcb due to noncached coherency
// work done just above; however, we may still have to extend
// valid data length. We can't demote this to shared, matching
// what occured before, until we figure that out a bit later.
//
// We kept ahold of it since our lockorder is main->paging,
// and paging must now held across the noncached write from
// the purge on.
//
//
// If this is async I/O, we will wait if there is an exclusive
// waiter.
//
if (!Wait && NonCachedIo) {
if (!FcbOrDcbAcquired &&
!FatAcquireSharedFcbWaitForEx( IrpContext, FcbOrDcb )) {
DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
try_return( PostIrp = TRUE );
}
//
// Note we will have to release this resource elsewhere. If we came
// out of the noncached coherency path, we will also have to drop
// the paging io resource.
//
IrpContext->FatIoContext->Wait.Async.Resource = FcbOrDcb->Header.Resource;
if (FcbCanDemoteToShared) {
IrpContext->FatIoContext->Wait.Async.Resource2 = FcbOrDcb->Header.PagingIoResource;
}
} else {
if (!FcbOrDcbAcquired &&
!FatAcquireSharedFcb( IrpContext, FcbOrDcb )) {
DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
try_return( PostIrp = TRUE );
}
}
FcbOrDcbAcquired = TRUE;
}
//
// Get a first tentative file size and valid data length.
// We must get ValidDataLength first since it is always
// increased second (in case we are unprotected) and
// we don't want to capture ValidDataLength > FileSize.
//
ValidDataToDisk = FcbOrDcb->ValidDataToDisk;
ValidDataLength = FcbOrDcb->Header.ValidDataLength.LowPart;
FileSize = FcbOrDcb->Header.FileSize.LowPart;
ASSERT( ValidDataLength <= FileSize );
//
// If are 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 truncate the transfer to the last sector
// boundary.
//
//
// Just in case this is paging io, 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, =><=.
//
if ( PagingIo ) {
if (StartingVbo >= FileSize) {
DebugTrace( 0, Dbg, "PagingIo started beyond EOF.\n", 0 );
Irp->IoStatus.Information = 0;
try_return( Status = STATUS_SUCCESS );
}
if (ByteCount > FileSize - StartingVbo) {
DebugTrace( 0, Dbg, "PagingIo extending beyond EOF.\n", 0 );
ByteCount = FileSize - StartingVbo;
}
}
//
// Determine if we were called by the lazywriter.
// (see resrcsup.c)
//
if (FcbOrDcb->Specific.Fcb.LazyWriteThread == PsGetCurrentThread()) {
CalledByLazyWriter = TRUE;
if (FlagOn( FcbOrDcb->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(Irp->Flags, IRP_SYNCHRONOUS_PAGING_IO) &&
FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) {
PIRP TopIrp;
TopIrp = IoGetTopLevelIrp();
//
// This clause determines if the top level request was
// in the FastIo path. Gack. Since we don't have a
// real sharing protocol for the top level IRP field ...
// yet ... if someone put things other than a pure IRP in
// there we best be careful.
//
if ((ULONG_PTR)TopIrp > FSRTL_MAX_TOP_LEVEL_IRP_FLAG &&
NodeType(TopIrp) == IO_TYPE_IRP) {
PIO_STACK_LOCATION IrpStack;
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 == FileObject->FsContext)) {
RecursiveWriteThrough = TRUE;
SetFlag( IrpContext->Flags, IRP_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.
//
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 kinda.
// 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 (!Wait) {
Wait = TRUE;
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT );
if (NonCachedIo) {
ASSERT( TypeOfOpen == UserFileOpen );
SwitchBackToAsync = TRUE;
}
}
//
// We need Exclusive access to the Fcb/Dcb since we will
// probably have to extend valid data and/or file.
//
//
// Y'know, the PagingIo case is a mapped page writer, and
// MmFlushSection or the mapped page writer itself already
// snatched up the main exclusive for us via the AcquireForCcFlush
// or AcquireForModWrite logic (the default logic parallels FAT's
// requirements since this order/model came first). Should ASSERT
// this since it'll just go 1->2, and a few more unnecesary DPC
// transitions.
//
// The preacquire is done to avoid inversion over the collided flush
// meta-resource in Mm. The one time this is not true is at final
// system shutdown time, when Mm goes off and flushes all the dirty
// pages. Since the callback is defined as Wait == FALSE he can't
// guarantee acquisition (though with clean process shutdown being
// enforced, it really should be now). Permit this to float.
//
// Note that since we're going to fall back on the acquisition aleady
// done for us, don't confuse things by thinking we did the work
// for it.
//
if ( PagingIo ) {
ExReleaseResourceLite( FcbOrDcb->Header.PagingIoResource );
PagingIoResourceAcquired = FALSE;
} else {
//
// The Fcb may already be acquired exclusive due to coherency
// work performed earlier. If so, obviously no work to do.
//
if (!FcbAcquiredExclusive) {
FatReleaseFcb( IrpContext, FcbOrDcb );
FcbOrDcbAcquired = FALSE;
if (!FatAcquireExclusiveFcb( IrpContext, FcbOrDcb )) {
DebugTrace( 0, Dbg, "Cannot acquire FcbOrDcb = %08lx shared without waiting\n", FcbOrDcb );
try_return( PostIrp = TRUE );
}
FcbOrDcbAcquired = TRUE;
FcbAcquiredExclusive = TRUE;
}
}
//
// 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. One thing we must do is keep out
// the FastIo path.
//
if (SwitchBackToAsync) {
if ((FcbOrDcb->NonPaged->SectionObjectPointers.DataSectionObject != NULL) ||
(StartingVbo + ByteCount > FcbOrDcb->Header.ValidDataLength.LowPart) ||
FatNoAsync) {
RtlZeroMemory( IrpContext->FatIoContext, sizeof(FAT_IO_CONTEXT) );
KeInitializeEvent( &IrpContext->FatIoContext->Wait.SyncEvent,
NotificationEvent,
FALSE );
SwitchBackToAsync = FALSE;
} else {
if (!FcbOrDcb->NonPaged->OutstandingAsyncEvent) {
FcbOrDcb->NonPaged->OutstandingAsyncEvent =
FsRtlAllocatePoolWithTag( NonPagedPool,
sizeof(KEVENT),
TAG_EVENT );
KeInitializeEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent,
NotificationEvent,
FALSE );
}
//
// If we are transitioning from 0 to 1, reset the event.
//
if (ExInterlockedAddUlong( &FcbOrDcb->NonPaged->OutstandingAsyncWrites,
1,
&FatData.GeneralSpinLock ) == 0) {
KeClearEvent( FcbOrDcb->NonPaged->OutstandingAsyncEvent );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -