1263 lines
29 KiB
C
1263 lines
29 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989-2000 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
Flush.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the File Flush buffers routine for Fat called by the
|
|||
|
dispatch driver.
|
|||
|
|
|||
|
// @@BEGIN_DDKSPLIT
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Gary Kimura [GaryKi] 28-Dec-1989
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
// @@END_DDKSPLIT
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "FatProcs.h"
|
|||
|
|
|||
|
//
|
|||
|
// The Bug check file id for this module
|
|||
|
//
|
|||
|
|
|||
|
#define BugCheckFileId (FAT_BUG_CHECK_FLUSH)
|
|||
|
|
|||
|
//
|
|||
|
// The local debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_FLUSH)
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE, FatCommonFlushBuffers)
|
|||
|
#pragma alloc_text(PAGE, FatFlushDirectory)
|
|||
|
#pragma alloc_text(PAGE, FatFlushFat)
|
|||
|
#pragma alloc_text(PAGE, FatFlushFile)
|
|||
|
#pragma alloc_text(PAGE, FatFlushVolume)
|
|||
|
#pragma alloc_text(PAGE, FatFsdFlushBuffers)
|
|||
|
#pragma alloc_text(PAGE, FatFlushDirentForFile)
|
|||
|
#pragma alloc_text(PAGE, FatFlushFatEntries)
|
|||
|
#pragma alloc_text(PAGE, FatHijackIrpAndFlushDevice)
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Local procedure prototypes
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFlushCompletionRoutine (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Contxt
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatHijackCompletionRoutine (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Contxt
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFsdFlushBuffers (
|
|||
|
IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the FSD part of Flush buffers.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
VolumeDeviceObject - Supplies the volume device object where the
|
|||
|
file being flushed exists
|
|||
|
|
|||
|
Irp - Supplies the Irp being processed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The FSD status for the IRP
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PIRP_CONTEXT IrpContext = NULL;
|
|||
|
|
|||
|
BOOLEAN TopLevel;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "FatFsdFlushBuffers\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Call the common Cleanup routine, with blocking allowed if synchronous
|
|||
|
//
|
|||
|
|
|||
|
FsRtlEnterFileSystem();
|
|||
|
|
|||
|
TopLevel = FatIsIrpTopLevel( Irp );
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
IrpContext = FatCreateIrpContext( Irp, CanFsdWait( Irp ) );
|
|||
|
|
|||
|
Status = FatCommonFlushBuffers( IrpContext, Irp );
|
|||
|
|
|||
|
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|||
|
|
|||
|
//
|
|||
|
// We had some trouble trying to perform the requested
|
|||
|
// operation, so we'll abort the I/O request with
|
|||
|
// the error status that we get back from the
|
|||
|
// execption code
|
|||
|
//
|
|||
|
|
|||
|
Status = FatProcessException( IrpContext, Irp, GetExceptionCode() );
|
|||
|
}
|
|||
|
|
|||
|
if (TopLevel) { IoSetTopLevelIrp( NULL ); }
|
|||
|
|
|||
|
FsRtlExitFileSystem();
|
|||
|
|
|||
|
//
|
|||
|
// And return to our caller
|
|||
|
//
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatFsdFlushBuffers -> %08lx\n", Status);
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( VolumeDeviceObject );
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatCommonFlushBuffers (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the common routine for flushing a buffer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - Supplies the Irp to process
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The return status for the operation
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
|
|||
|
PFILE_OBJECT FileObject;
|
|||
|
|
|||
|
TYPE_OF_OPEN TypeOfOpen;
|
|||
|
PVCB Vcb;
|
|||
|
PFCB Fcb;
|
|||
|
PCCB Ccb;
|
|||
|
|
|||
|
BOOLEAN VcbAcquired = FALSE;
|
|||
|
BOOLEAN FcbAcquired = FALSE;
|
|||
|
|
|||
|
PDIRENT Dirent;
|
|||
|
PBCB DirentBcb = NULL;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "FatCommonFlushBuffers\n", 0);
|
|||
|
DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp);
|
|||
|
DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject);
|
|||
|
|
|||
|
//
|
|||
|
// Extract and decode the file object
|
|||
|
//
|
|||
|
|
|||
|
FileObject = IrpSp->FileObject;
|
|||
|
TypeOfOpen = FatDecodeFileObject( FileObject, &Vcb, &Fcb, &Ccb );
|
|||
|
|
|||
|
//
|
|||
|
// CcFlushCache is always synchronous, so if we can't wait enqueue
|
|||
|
// the irp to the Fsp.
|
|||
|
//
|
|||
|
|
|||
|
if ( !FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ) {
|
|||
|
|
|||
|
Status = FatFsdPostRequest( IrpContext, Irp );
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status );
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Case on the type of open that we are trying to flush
|
|||
|
//
|
|||
|
|
|||
|
switch (TypeOfOpen) {
|
|||
|
|
|||
|
case VirtualVolumeFile:
|
|||
|
case EaFile:
|
|||
|
case DirectoryFile:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Flush that does nothing\n", 0);
|
|||
|
break;
|
|||
|
|
|||
|
case UserFileOpen:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Flush User File Open\n", 0);
|
|||
|
|
|||
|
(VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
FcbAcquired = TRUE;
|
|||
|
|
|||
|
FatVerifyFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
//
|
|||
|
// If the file is cached then flush its cache
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushFile( IrpContext, Fcb, Flush );
|
|||
|
|
|||
|
//
|
|||
|
// Also update and flush the file's dirent in the parent directory if the
|
|||
|
// file flush worked.
|
|||
|
//
|
|||
|
|
|||
|
if (NT_SUCCESS( Status )) {
|
|||
|
|
|||
|
//
|
|||
|
// Insure that we get the filesize to disk correctly. This is
|
|||
|
// benign if it was already good.
|
|||
|
//
|
|||
|
// (why do we need to do this?)
|
|||
|
//
|
|||
|
|
|||
|
SetFlag(FileObject->Flags, FO_FILE_SIZE_CHANGED);
|
|||
|
|
|||
|
FatUpdateDirentFromFcb( IrpContext, FileObject, Fcb, Ccb );
|
|||
|
|
|||
|
//
|
|||
|
// Flush the volume file to get any allocation information
|
|||
|
// updates to disk.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Fcb->FcbState, FCB_STATE_FLUSH_FAT)) {
|
|||
|
|
|||
|
Status = FatFlushFat( IrpContext, Vcb );
|
|||
|
|
|||
|
ClearFlag(Fcb->FcbState, FCB_STATE_FLUSH_FAT);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Set the write through bit so that these modifications
|
|||
|
// will be completed with the request.
|
|||
|
//
|
|||
|
|
|||
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
case UserDirectoryOpen:
|
|||
|
|
|||
|
//
|
|||
|
// If the user had opened the root directory then we'll
|
|||
|
// oblige by flushing the volume.
|
|||
|
//
|
|||
|
|
|||
|
if (NodeType(Fcb) != FAT_NTC_ROOT_DCB) {
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Flush a directory does nothing\n", 0);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case UserVolumeOpen:
|
|||
|
|
|||
|
DebugTrace(0, Dbg, "Flush User Volume Open, or root dcb\n", 0);
|
|||
|
|
|||
|
//
|
|||
|
// Acquire exclusive access to the Vcb.
|
|||
|
//
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN Finished;
|
|||
|
Finished = FatAcquireExclusiveVcb( IrpContext, Vcb );
|
|||
|
ASSERT( Finished );
|
|||
|
}
|
|||
|
|
|||
|
VcbAcquired = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// Mark the volume clean and then flush the volume file,
|
|||
|
// and then all directories
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushVolume( IrpContext, Vcb, Flush );
|
|||
|
|
|||
|
//
|
|||
|
// If the volume was dirty, do the processing that the delayed
|
|||
|
// callback would have done.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY)) {
|
|||
|
|
|||
|
//
|
|||
|
// Cancel any pending clean volumes.
|
|||
|
//
|
|||
|
|
|||
|
(VOID)KeCancelTimer( &Vcb->CleanVolumeTimer );
|
|||
|
(VOID)KeRemoveQueueDpc( &Vcb->CleanVolumeDpc );
|
|||
|
|
|||
|
//
|
|||
|
// The volume is now clean, note it.
|
|||
|
//
|
|||
|
|
|||
|
if (!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY)) {
|
|||
|
|
|||
|
FatMarkVolume( IrpContext, Vcb, VolumeClean );
|
|||
|
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_VOLUME_DIRTY );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the volume if it is removable.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
|
|||
|
|
|||
|
FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
FatBugCheck( TypeOfOpen, 0, 0 );
|
|||
|
}
|
|||
|
|
|||
|
FatUnpinBcb( IrpContext, DirentBcb );
|
|||
|
|
|||
|
FatUnpinRepinnedBcbs( IrpContext );
|
|||
|
|
|||
|
} finally {
|
|||
|
|
|||
|
DebugUnwind( FatCommonFlushBuffers );
|
|||
|
|
|||
|
FatUnpinBcb( IrpContext, DirentBcb );
|
|||
|
|
|||
|
if (VcbAcquired) { FatReleaseVcb( IrpContext, Vcb ); }
|
|||
|
|
|||
|
if (FcbAcquired) { FatReleaseFcb( IrpContext, Fcb ); }
|
|||
|
|
|||
|
//
|
|||
|
// If this is a normal termination then pass the request on
|
|||
|
// to the target device object.
|
|||
|
//
|
|||
|
|
|||
|
if (!AbnormalTermination()) {
|
|||
|
|
|||
|
NTSTATUS DriverStatus;
|
|||
|
PIO_STACK_LOCATION NextIrpSp;
|
|||
|
|
|||
|
//
|
|||
|
// Get the next stack location, and copy over the stack location
|
|||
|
//
|
|||
|
|
|||
|
NextIrpSp = IoGetNextIrpStackLocation( Irp );
|
|||
|
|
|||
|
*NextIrpSp = *IrpSp;
|
|||
|
|
|||
|
//
|
|||
|
// Set up the completion routine
|
|||
|
//
|
|||
|
|
|||
|
IoSetCompletionRoutine( Irp,
|
|||
|
FatFlushCompletionRoutine,
|
|||
|
ULongToPtr( Status ),
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
DriverStatus = IoCallDriver(Vcb->TargetDeviceObject, Irp);
|
|||
|
|
|||
|
Status = (DriverStatus == STATUS_INVALID_DEVICE_REQUEST) ?
|
|||
|
Status : DriverStatus;
|
|||
|
|
|||
|
//
|
|||
|
// Free the IrpContext and return to the caller.
|
|||
|
//
|
|||
|
|
|||
|
FatCompleteRequest( IrpContext, FatNull, STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatCommonFlushBuffers -> %08lx\n", Status);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFlushDirectory (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PDCB Dcb,
|
|||
|
IN FAT_FLUSH_TYPE FlushType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine non-recursively flushes a dcb tree.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dcb - Supplies the Dcb being flushed
|
|||
|
|
|||
|
FlushType - Specifies the kind of flushing to perform
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PFCB Fcb;
|
|||
|
PVCB Vcb;
|
|||
|
PFCB NextFcb;
|
|||
|
|
|||
|
PDIRENT Dirent;
|
|||
|
PBCB DirentBcb = NULL;
|
|||
|
|
|||
|
NTSTATUS Status;
|
|||
|
NTSTATUS ReturnStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
BOOLEAN ClearWriteThroughOnExit = FALSE;
|
|||
|
BOOLEAN ClearWaitOnExit = FALSE;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ASSERT( FatVcbAcquiredExclusive(IrpContext, Dcb->Vcb) );
|
|||
|
|
|||
|
DebugTrace(+1, Dbg, "FatFlushDirectory, Dcb = %08lx\n", Dcb);
|
|||
|
|
|||
|
//
|
|||
|
// First flush all the files, then the directories, to make sure all the
|
|||
|
// file sizes and times get sets correctly on disk.
|
|||
|
//
|
|||
|
// We also have to check here if the "Ea Data. Sf" fcb really
|
|||
|
// corressponds to an existing file.
|
|||
|
//
|
|||
|
|
|||
|
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH)) {
|
|||
|
|
|||
|
ClearWriteThroughOnExit = TRUE;
|
|||
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
|
|||
|
}
|
|||
|
|
|||
|
if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)) {
|
|||
|
|
|||
|
ClearWaitOnExit = TRUE;
|
|||
|
SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|||
|
}
|
|||
|
|
|||
|
Vcb = Dcb->Vcb;
|
|||
|
Fcb = Dcb;
|
|||
|
|
|||
|
while (Fcb != NULL) {
|
|||
|
|
|||
|
NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb);
|
|||
|
|
|||
|
if ( (NodeType( Fcb ) == FAT_NTC_FCB) &&
|
|||
|
(Vcb->EaFcb != Fcb) &&
|
|||
|
!IsFileDeleted(IrpContext, Fcb)) {
|
|||
|
|
|||
|
(VOID)FatAcquireExclusiveFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
ClearFlag( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB );
|
|||
|
|
|||
|
//
|
|||
|
// Exception handler to catch and commute errors encountered
|
|||
|
// doing the flush dance. We may encounter corruption, and
|
|||
|
// should continue flushing the volume as much as possible.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Standard handler to release resources, etc.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the Fcb is OK.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
FatVerifyFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
FatResetExceptionState( IrpContext );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If this Fcb is not good skip it. Note that a 'continue'
|
|||
|
// here would be very expensive as we inside a try{} body.
|
|||
|
//
|
|||
|
|
|||
|
if (Fcb->FcbCondition != FcbGood) {
|
|||
|
|
|||
|
goto NextFcb;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// In case a handle was never closed and the FS and AS are more
|
|||
|
// than a cluster different, do this truncate.
|
|||
|
//
|
|||
|
|
|||
|
if ( FlagOn(Fcb->FcbState, FCB_STATE_TRUNCATE_ON_CLOSE) ) {
|
|||
|
|
|||
|
FatTruncateFileAllocation( IrpContext,
|
|||
|
Fcb,
|
|||
|
Fcb->Header.FileSize.LowPart );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Also compare the file's dirent in the parent directory
|
|||
|
// with the size information in the Fcb and update
|
|||
|
// it if neccessary. Note that we don't mark the Bcb dirty
|
|||
|
// because we will be flushing the file object presently, and
|
|||
|
// Mm knows what's really dirty.
|
|||
|
//
|
|||
|
|
|||
|
FatGetDirentFromFcbOrDcb( IrpContext,
|
|||
|
Fcb,
|
|||
|
&Dirent,
|
|||
|
&DirentBcb );
|
|||
|
|
|||
|
if (Dirent->FileSize != Fcb->Header.FileSize.LowPart) {
|
|||
|
|
|||
|
Dirent->FileSize = Fcb->Header.FileSize.LowPart;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We must unpin the Bcb before the flush since we recursively tear up
|
|||
|
// the tree if Mm decides that the data section is no longer referenced
|
|||
|
// and the final close comes in for this file. If this parent has no
|
|||
|
// more children as a result, we will try to initiate teardown on it
|
|||
|
// and Cc will deadlock against the active count of this Bcb.
|
|||
|
//
|
|||
|
|
|||
|
FatUnpinBcb( IrpContext, DirentBcb );
|
|||
|
|
|||
|
//
|
|||
|
// Now flush the file. Note that this may make the Fcb
|
|||
|
// go away if Mm dereferences its file object.
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushFile( IrpContext, Fcb, FlushType );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
ReturnStatus = Status;
|
|||
|
}
|
|||
|
|
|||
|
NextFcb: NOTHING;
|
|||
|
} finally {
|
|||
|
|
|||
|
FatUnpinBcb( IrpContext, DirentBcb );
|
|||
|
|
|||
|
//
|
|||
|
// Since we have the Vcb exclusive we know that if any closes
|
|||
|
// come in it is because the CcPurgeCacheSection caused the
|
|||
|
// Fcb to go away.
|
|||
|
//
|
|||
|
|
|||
|
if ( !FlagOn(Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB) ) {
|
|||
|
|
|||
|
FatReleaseFcb( (IRPCONTEXT), Fcb );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} except( (ReturnStatus = FsRtlIsNtstatusExpected(GetExceptionCode())) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
FatResetExceptionState( IrpContext );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Fcb = NextFcb;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// OK, now flush the directories.
|
|||
|
//
|
|||
|
|
|||
|
Fcb = Dcb;
|
|||
|
|
|||
|
while (Fcb != NULL) {
|
|||
|
|
|||
|
NextFcb = FatGetNextFcbTopDown(IrpContext, Fcb, Dcb);
|
|||
|
|
|||
|
if ( (NodeType( Fcb ) != FAT_NTC_FCB) &&
|
|||
|
!IsFileDeleted(IrpContext, Fcb) ) {
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the Fcb is OK.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
FatVerifyFcb( IrpContext, Fcb );
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
FatResetExceptionState( IrpContext );
|
|||
|
}
|
|||
|
|
|||
|
if (Fcb->FcbCondition == FcbGood) {
|
|||
|
|
|||
|
Status = FatFlushFile( IrpContext, Fcb, FlushType );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
ReturnStatus = Status;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Fcb = NextFcb;
|
|||
|
}
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
FatUnpinRepinnedBcbs( IrpContext );
|
|||
|
|
|||
|
} except(FatExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|||
|
|
|||
|
ReturnStatus = IrpContext->ExceptionStatus;
|
|||
|
}
|
|||
|
|
|||
|
if (ClearWriteThroughOnExit) {
|
|||
|
|
|||
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH);
|
|||
|
}
|
|||
|
if (ClearWaitOnExit) {
|
|||
|
|
|||
|
ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT);
|
|||
|
}
|
|||
|
|
|||
|
DebugTrace(-1, Dbg, "FatFlushDirectory -> 0x%08lx\n", ReturnStatus);
|
|||
|
|
|||
|
return ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFlushFat (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The function carefully flushes the entire FAT for a volume. It is
|
|||
|
nessecary to dance around a bit because of complicated synchronization
|
|||
|
reasons.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vcb - Supplies the Vcb whose FAT is being flushed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PBCB Bcb;
|
|||
|
PVOID DontCare;
|
|||
|
IO_STATUS_BLOCK Iosb;
|
|||
|
LARGE_INTEGER Offset;
|
|||
|
|
|||
|
NTSTATUS ReturnStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// If this volume is write protected, no need to flush.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Make sure the Vcb is OK.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
FatVerifyVcb( IrpContext, Vcb );
|
|||
|
|
|||
|
} except( FsRtlIsNtstatusExpected(GetExceptionCode()) ?
|
|||
|
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) {
|
|||
|
|
|||
|
FatResetExceptionState( IrpContext );
|
|||
|
}
|
|||
|
|
|||
|
if (Vcb->VcbCondition != VcbGood) {
|
|||
|
|
|||
|
return STATUS_FILE_INVALID;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The only way we have to correctly synchronize things is to
|
|||
|
// repin stuff, and then unpin repin it.
|
|||
|
//
|
|||
|
// With NT 5.0, we can use some new cache manager support to make
|
|||
|
// this a lot more efficient (important for FAT32). Since we're
|
|||
|
// only worried about ranges that are dirty - and since we're a
|
|||
|
// modified-no-write stream - we can assume that if there is no
|
|||
|
// BCB, there is no work to do in the range. I.e., the lazy writer
|
|||
|
// beat us to it.
|
|||
|
//
|
|||
|
// This is much better than reading the entire FAT in and trying
|
|||
|
// to punch it out (see the test in the write path to blow
|
|||
|
// off writes that don't correspond to dirty ranges of the FAT).
|
|||
|
// For FAT32, this would be a *lot* of reading.
|
|||
|
//
|
|||
|
|
|||
|
if (Vcb->AllocationSupport.FatIndexBitSize != 12) {
|
|||
|
|
|||
|
//
|
|||
|
// Walk through the Fat, one page at a time.
|
|||
|
//
|
|||
|
|
|||
|
ULONG NumberOfPages;
|
|||
|
ULONG Page;
|
|||
|
|
|||
|
NumberOfPages = ( FatReservedBytes(&Vcb->Bpb) +
|
|||
|
FatBytesPerFat(&Vcb->Bpb) +
|
|||
|
(PAGE_SIZE - 1) ) / PAGE_SIZE;
|
|||
|
|
|||
|
|
|||
|
for ( Page = 0, Offset.QuadPart = 0;
|
|||
|
Page < NumberOfPages;
|
|||
|
Page++, Offset.LowPart += PAGE_SIZE ) {
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
if (CcPinRead( Vcb->VirtualVolumeFile,
|
|||
|
&Offset,
|
|||
|
PAGE_SIZE,
|
|||
|
PIN_WAIT | PIN_IF_BCB,
|
|||
|
&Bcb,
|
|||
|
&DontCare )) {
|
|||
|
|
|||
|
CcSetDirtyPinnedData( Bcb, NULL );
|
|||
|
CcRepinBcb( Bcb );
|
|||
|
CcUnpinData( Bcb );
|
|||
|
CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Iosb.Status)) {
|
|||
|
|
|||
|
ReturnStatus = Iosb.Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} except(FatExceptionFilter(IrpContext, GetExceptionInformation())) {
|
|||
|
|
|||
|
ReturnStatus = IrpContext->ExceptionStatus;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We read in the entire fat in the 12 bit case.
|
|||
|
//
|
|||
|
|
|||
|
Offset.QuadPart = FatReservedBytes( &Vcb->Bpb );
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
if (CcPinRead( Vcb->VirtualVolumeFile,
|
|||
|
&Offset,
|
|||
|
FatBytesPerFat( &Vcb->Bpb ),
|
|||
|
PIN_WAIT | PIN_IF_BCB,
|
|||
|
&Bcb,
|
|||
|
&DontCare )) {
|
|||
|
|
|||
|
CcSetDirtyPinnedData( Bcb, NULL );
|
|||
|
CcRepinBcb( Bcb );
|
|||
|
CcUnpinData( Bcb );
|
|||
|
CcUnpinRepinnedBcb( Bcb, TRUE, &Iosb );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Iosb.Status)) {
|
|||
|
|
|||
|
ReturnStatus = Iosb.Status;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} except(FatExceptionFilter(IrpContext, GetExceptionInformation())) {
|
|||
|
|
|||
|
ReturnStatus = IrpContext->ExceptionStatus;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFlushVolume (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb,
|
|||
|
IN FAT_FLUSH_TYPE FlushType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
The following routine is used to flush a volume to disk, including the
|
|||
|
volume file, and ea file.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vcb - Supplies the volume being flushed
|
|||
|
|
|||
|
FlushType - Specifies the kind of flushing to perform
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The Status from the flush.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
NTSTATUS ReturnStatus = STATUS_SUCCESS;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// If this volume is write protected, no need to flush.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_WRITE_PROTECTED)) {
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Flush all the files and directories.
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushDirectory( IrpContext, Vcb->RootDcb, FlushType );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
ReturnStatus = Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now Flush the FAT
|
|||
|
//
|
|||
|
|
|||
|
Status = FatFlushFat( IrpContext, Vcb );
|
|||
|
|
|||
|
if (!NT_SUCCESS(Status)) {
|
|||
|
|
|||
|
ReturnStatus = Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock the volume if it is removable.
|
|||
|
//
|
|||
|
|
|||
|
if (FlagOn(Vcb->VcbState, VCB_STATE_FLAG_REMOVABLE_MEDIA) &&
|
|||
|
!FlagOn(Vcb->VcbState, VCB_STATE_FLAG_BOOT_OR_PAGING_FILE)) {
|
|||
|
|
|||
|
FatToggleMediaEjectDisable( IrpContext, Vcb, FALSE );
|
|||
|
}
|
|||
|
|
|||
|
return ReturnStatus;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFlushFile (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PFCB Fcb,
|
|||
|
IN FAT_FLUSH_TYPE FlushType
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine simply flushes the data section on a file.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Fcb - Supplies the file being flushed
|
|||
|
|
|||
|
FlushType - Specifies the kind of flushing to perform
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The Status from the flush.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
IO_STATUS_BLOCK Iosb;
|
|||
|
PVCB Vcb = Fcb->Vcb;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
CcFlushCache( &Fcb->NonPaged->SectionObjectPointers, NULL, 0, &Iosb );
|
|||
|
|
|||
|
if ( !FlagOn( Vcb->VcbState, VCB_STATE_FLAG_DELETED_FCB )) {
|
|||
|
|
|||
|
//
|
|||
|
// Grab and release PagingIo to serialize ourselves with the lazy writer.
|
|||
|
// This will work to ensure that all IO has completed on the cached
|
|||
|
// data.
|
|||
|
//
|
|||
|
// If we are to invalidate the file, now is the right time to do it. Do
|
|||
|
// it non-recursively so we don't thump children before their time.
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireResourceExclusiveLite( Fcb->Header.PagingIoResource, TRUE);
|
|||
|
|
|||
|
if (FlushType == FlushAndInvalidate) {
|
|||
|
|
|||
|
FatMarkFcbCondition( IrpContext, Fcb, FcbBad, FALSE );
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseResourceLite( Fcb->Header.PagingIoResource );
|
|||
|
}
|
|||
|
|
|||
|
return Iosb.Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatHijackIrpAndFlushDevice (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PDEVICE_OBJECT TargetDeviceObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when we need to send a flush to a device but
|
|||
|
we don't have a flush Irp. What this routine does is make a copy
|
|||
|
of its current Irp stack location, but changes the Irp Major code
|
|||
|
to a IRP_MJ_FLUSH_BUFFERS amd then send it down, but cut it off at
|
|||
|
the knees in the completion routine, fix it up and return to the
|
|||
|
user as if nothing had happened.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Irp - The Irp to hijack
|
|||
|
|
|||
|
TargetDeviceObject - The device to send the request to.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS - The Status from the flush in case anybody cares.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KEVENT Event;
|
|||
|
NTSTATUS Status;
|
|||
|
PIO_STACK_LOCATION NextIrpSp;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Get the next stack location, and copy over the stack location
|
|||
|
//
|
|||
|
|
|||
|
NextIrpSp = IoGetNextIrpStackLocation( Irp );
|
|||
|
|
|||
|
*NextIrpSp = *IoGetCurrentIrpStackLocation( Irp );
|
|||
|
|
|||
|
NextIrpSp->MajorFunction = IRP_MJ_FLUSH_BUFFERS;
|
|||
|
NextIrpSp->MinorFunction = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Set up the completion routine
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent( &Event, NotificationEvent, FALSE );
|
|||
|
|
|||
|
IoSetCompletionRoutine( Irp,
|
|||
|
FatHijackCompletionRoutine,
|
|||
|
&Event,
|
|||
|
TRUE,
|
|||
|
TRUE,
|
|||
|
TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// Send the request.
|
|||
|
//
|
|||
|
|
|||
|
Status = IoCallDriver( TargetDeviceObject, Irp );
|
|||
|
|
|||
|
if (Status == STATUS_PENDING) {
|
|||
|
|
|||
|
KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
|
|||
|
|
|||
|
Status = Irp->IoStatus.Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the driver doesn't support flushes, return SUCCESS.
|
|||
|
//
|
|||
|
|
|||
|
if (Status == STATUS_INVALID_DEVICE_REQUEST) {
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
Irp->IoStatus.Status = 0;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FatFlushFatEntries (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb,
|
|||
|
IN ULONG Cluster,
|
|||
|
IN ULONG Count
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This macro flushes the FAT page(s) containing the passed in run.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vcb - Supplies the volume being flushed
|
|||
|
|
|||
|
Cluster - The starting cluster
|
|||
|
|
|||
|
Count - The number of FAT entries in the run
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG ByteCount;
|
|||
|
LARGE_INTEGER FileOffset;
|
|||
|
|
|||
|
IO_STATUS_BLOCK Iosb;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
FileOffset.HighPart = 0;
|
|||
|
FileOffset.LowPart = FatReservedBytes( &Vcb->Bpb );
|
|||
|
|
|||
|
if (Vcb->AllocationSupport.FatIndexBitSize == 12) {
|
|||
|
|
|||
|
FileOffset.LowPart += Cluster * 3 / 2;
|
|||
|
ByteCount = (Count * 3 / 2) + 1;
|
|||
|
|
|||
|
} else if (Vcb->AllocationSupport.FatIndexBitSize == 32) {
|
|||
|
|
|||
|
FileOffset.LowPart += Cluster * sizeof(ULONG);
|
|||
|
ByteCount = Count * sizeof(ULONG);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
FileOffset.LowPart += Cluster * sizeof( USHORT );
|
|||
|
ByteCount = Count * sizeof( USHORT );
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
CcFlushCache( &Vcb->SectionObjectPointers,
|
|||
|
&FileOffset,
|
|||
|
ByteCount,
|
|||
|
&Iosb );
|
|||
|
|
|||
|
if (NT_SUCCESS(Iosb.Status)) {
|
|||
|
Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext,
|
|||
|
IrpContext->OriginatingIrp,
|
|||
|
Vcb->TargetDeviceObject );
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Iosb.Status)) {
|
|||
|
FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
FatFlushDirentForFile (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PFCB Fcb
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This macro flushes the page containing a file's DIRENT in its parent.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Fcb - Supplies the file whose DIRENT is being flushed
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
VOID
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LARGE_INTEGER FileOffset;
|
|||
|
IO_STATUS_BLOCK Iosb;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
FileOffset.QuadPart = Fcb->DirentOffsetWithinDirectory;
|
|||
|
|
|||
|
CcFlushCache( &Fcb->ParentDcb->NonPaged->SectionObjectPointers,
|
|||
|
&FileOffset,
|
|||
|
sizeof( DIRENT ),
|
|||
|
&Iosb );
|
|||
|
|
|||
|
if (NT_SUCCESS(Iosb.Status)) {
|
|||
|
Iosb.Status = FatHijackIrpAndFlushDevice( IrpContext,
|
|||
|
IrpContext->OriginatingIrp,
|
|||
|
Fcb->Vcb->TargetDeviceObject );
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(Iosb.Status)) {
|
|||
|
FatNormalizeAndRaiseStatus(IrpContext, Iosb.Status);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Local support routine
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatFlushCompletionRoutine (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Contxt
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS Status = (NTSTATUS) (ULONG_PTR) Contxt;
|
|||
|
|
|||
|
//
|
|||
|
// Add the hack-o-ramma to fix formats.
|
|||
|
//
|
|||
|
|
|||
|
if ( Irp->PendingReturned ) {
|
|||
|
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If the Irp got STATUS_INVALID_DEVICE_REQUEST, normalize it
|
|||
|
// to STATUS_SUCCESS.
|
|||
|
//
|
|||
|
|
|||
|
if (Irp->IoStatus.Status == STATUS_INVALID_DEVICE_REQUEST) {
|
|||
|
|
|||
|
Irp->IoStatus.Status = Status;
|
|||
|
}
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|||
|
UNREFERENCED_PARAMETER( Contxt );
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Local support routine
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
FatHijackCompletionRoutine (
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Contxt
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
//
|
|||
|
// Set the event so that our call will wake up.
|
|||
|
//
|
|||
|
|
|||
|
KeSetEvent( (PKEVENT)Contxt, 0, FALSE );
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( DeviceObject );
|
|||
|
UNREFERENCED_PARAMETER( Irp );
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|