851 lines
24 KiB
C
851 lines
24 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1991 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
FspDisp.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the main dispatch procedure/thread for the Ntfs
|
|||
|
Fsp
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Gary Kimura [GaryKi] 21-May-1991
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "NtfsProc.h"
|
|||
|
|
|||
|
#define BugCheckFileId (NTFS_BUG_CHECK_FSPDISP)
|
|||
|
|
|||
|
#pragma alloc_text(PAGE, NtfsSpecialDispatch)
|
|||
|
#pragma alloc_text(PAGE, NtfsPostSpecial)
|
|||
|
|
|||
|
//
|
|||
|
// Define our local debug trace level
|
|||
|
//
|
|||
|
|
|||
|
#define Dbg (DEBUG_TRACE_FSP_DISPATCHER)
|
|||
|
|
|||
|
extern PETHREAD NtfsDesignatedTimeoutThread;
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NtfsFspDispatch (
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the main FSP thread routine that is executed to receive
|
|||
|
and dispatch IRP requests. Each FSP thread begins its execution here.
|
|||
|
There is one thread created at system initialization time and subsequent
|
|||
|
threads created as needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
Context - Supplies the thread id.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None - This routine never exits
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
TOP_LEVEL_CONTEXT TopLevelContext;
|
|||
|
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
|||
|
OPLOCK_CLEANUP OplockCleanup;
|
|||
|
|
|||
|
PIRP Irp;
|
|||
|
PIRP_CONTEXT IrpContext;
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
ULONG LogFileFullCount = 0;
|
|||
|
|
|||
|
PVOLUME_DEVICE_OBJECT VolDo;
|
|||
|
BOOLEAN Retry;
|
|||
|
NTSTATUS Status = STATUS_SUCCESS;
|
|||
|
IrpContext = (PIRP_CONTEXT)Context;
|
|||
|
|
|||
|
Irp = IrpContext->OriginatingIrp;
|
|||
|
|
|||
|
if (Irp != NULL) {
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now because we are the Fsp we will force the IrpContext to
|
|||
|
// indicate true on Wait.
|
|||
|
//
|
|||
|
|
|||
|
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
|||
|
|
|||
|
//
|
|||
|
// If this request has an associated volume device object, remember it.
|
|||
|
//
|
|||
|
|
|||
|
if ((Irp != NULL) &&
|
|||
|
(IrpSp->FileObject != NULL)) {
|
|||
|
|
|||
|
VolDo = CONTAINING_RECORD( IrpSp->DeviceObject,
|
|||
|
VOLUME_DEVICE_OBJECT,
|
|||
|
DeviceObject );
|
|||
|
} else {
|
|||
|
|
|||
|
VolDo = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Now case on the function code. For each major function code,
|
|||
|
// either call the appropriate FSP routine or case on the minor
|
|||
|
// function and then call the FSP routine. The FSP routine that
|
|||
|
// we call is responsible for completing the IRP, and not us.
|
|||
|
// That way the routine can complete the IRP and then continue
|
|||
|
// post processing as required. For example, a read can be
|
|||
|
// satisfied right away and then read can be done.
|
|||
|
//
|
|||
|
// We'll do all of the work within an exception handler that
|
|||
|
// will be invoked if ever some underlying operation gets into
|
|||
|
// trouble (e.g., if NtfsReadSectorsSync has trouble).
|
|||
|
//
|
|||
|
|
|||
|
while (TRUE) {
|
|||
|
|
|||
|
FsRtlEnterFileSystem();
|
|||
|
|
|||
|
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
|||
|
|
|||
|
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
|
|||
|
ASSERT( ThreadTopLevelContext == &TopLevelContext );
|
|||
|
|
|||
|
NtfsPostRequests += 1;
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// If this is the initial try with this Irp Context, update the
|
|||
|
// top level Irp fields.
|
|||
|
//
|
|||
|
|
|||
|
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
|||
|
|
|||
|
Retry = FALSE;
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Always clear the exception code in the IrpContext so we respond
|
|||
|
// correctly to errors encountered in the Fsp.
|
|||
|
//
|
|||
|
|
|||
|
IrpContext->ExceptionStatus = 0;
|
|||
|
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_IN_FSP );
|
|||
|
|
|||
|
//
|
|||
|
// See if we were posted due to a log file full condition, and
|
|||
|
// if so, then do a clean volume checkpoint if we are the
|
|||
|
// first ones to get there. If we see a different Lsn and do
|
|||
|
// not do the checkpoint, the worst that can happen is that we
|
|||
|
// will get posted again if the log file is still full.
|
|||
|
//
|
|||
|
|
|||
|
if (IrpContext->LastRestartArea.QuadPart != 0) {
|
|||
|
|
|||
|
NtfsCheckpointForLogFileFull( IrpContext );
|
|||
|
|
|||
|
if (++LogFileFullCount >= 2) {
|
|||
|
|
|||
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we have an Irp then proceed with our normal processing.
|
|||
|
//
|
|||
|
|
|||
|
if (Irp != NULL) {
|
|||
|
|
|||
|
switch ( IrpContext->MajorFunction ) {
|
|||
|
|
|||
|
//
|
|||
|
// For Create Operation,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_CREATE:
|
|||
|
|
|||
|
ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_EFS_CREATE );
|
|||
|
|
|||
|
if (FlagOn( IrpContext->State, IRP_CONTEXT_STATE_DASD_OPEN )) {
|
|||
|
|
|||
|
Status = NtfsCommonVolumeOpen( IrpContext, Irp );
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
RtlZeroMemory( &OplockCleanup, sizeof( OplockCleanup ) );
|
|||
|
Status = NtfsCommonCreate( IrpContext, Irp, &OplockCleanup, NULL );
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For close operations
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_CLOSE:
|
|||
|
|
|||
|
//
|
|||
|
// We should never post closes to this workqueue.
|
|||
|
//
|
|||
|
|
|||
|
NtfsBugCheck( 0, 0, 0 );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For read operations
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_READ:
|
|||
|
|
|||
|
(VOID) NtfsCommonRead( IrpContext, Irp, TRUE );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For write operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_WRITE:
|
|||
|
|
|||
|
(VOID) NtfsCommonWrite( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Query Information operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_QUERY_INFORMATION:
|
|||
|
|
|||
|
(VOID) NtfsCommonQueryInformation( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Set Information operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_SET_INFORMATION:
|
|||
|
|
|||
|
(VOID) NtfsCommonSetInformation( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Query EA operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_QUERY_EA:
|
|||
|
|
|||
|
(VOID) NtfsCommonQueryEa( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Set EA operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_SET_EA:
|
|||
|
|
|||
|
(VOID) NtfsCommonSetEa( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// For Flush buffers operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_FLUSH_BUFFERS:
|
|||
|
|
|||
|
(VOID) NtfsCommonFlushBuffers( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Query Volume Information operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_QUERY_VOLUME_INFORMATION:
|
|||
|
|
|||
|
(VOID) NtfsCommonQueryVolumeInfo( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Set Volume Information operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_SET_VOLUME_INFORMATION:
|
|||
|
|
|||
|
(VOID) NtfsCommonSetVolumeInfo( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For File Cleanup operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_CLEANUP:
|
|||
|
|
|||
|
(VOID) NtfsCommonCleanup( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Directory Control operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_DIRECTORY_CONTROL:
|
|||
|
|
|||
|
(VOID) NtfsCommonDirectoryControl( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For File System Control operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_FILE_SYSTEM_CONTROL:
|
|||
|
|
|||
|
(VOID) NtfsCommonFileSystemControl( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Lock Control operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_LOCK_CONTROL:
|
|||
|
|
|||
|
(VOID) NtfsCommonLockControl( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Device Control operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_DEVICE_CONTROL:
|
|||
|
|
|||
|
(VOID) NtfsCommonDeviceControl( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Query Security Information operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_QUERY_SECURITY:
|
|||
|
|
|||
|
(VOID) NtfsCommonQuerySecurityInfo( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Set Security Information operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_SET_SECURITY:
|
|||
|
|
|||
|
(VOID) NtfsCommonSetSecurityInfo( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Query Quota operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_QUERY_QUOTA:
|
|||
|
|
|||
|
(VOID) NtfsCommonQueryQuota( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For Set Quota operations,
|
|||
|
//
|
|||
|
|
|||
|
case IRP_MJ_SET_QUOTA:
|
|||
|
|
|||
|
(VOID) NtfsCommonSetQuota( IrpContext, Irp );
|
|||
|
break;
|
|||
|
|
|||
|
//
|
|||
|
// For any other major operations, return an invalid
|
|||
|
// request.
|
|||
|
//
|
|||
|
|
|||
|
default:
|
|||
|
|
|||
|
NtfsCompleteRequest( IrpContext, Irp, STATUS_INVALID_DEVICE_REQUEST );
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Otherwise complete the request to clean up this Irp Context.
|
|||
|
//
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
|
|||
|
IrpContext = NULL;
|
|||
|
}
|
|||
|
|
|||
|
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
|||
|
|
|||
|
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|||
|
|
|||
|
PIO_STACK_LOCATION IrpSp;
|
|||
|
|
|||
|
//
|
|||
|
// 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
|
|||
|
//
|
|||
|
|
|||
|
if (Irp != NULL) {
|
|||
|
|
|||
|
IrpSp = IoGetCurrentIrpStackLocation( Irp );
|
|||
|
|
|||
|
Status = GetExceptionCode();
|
|||
|
|
|||
|
if ((Status == STATUS_FILE_DELETED) &&
|
|||
|
((IrpContext->MajorFunction == IRP_MJ_READ) ||
|
|||
|
(IrpContext->MajorFunction == IRP_MJ_WRITE) ||
|
|||
|
((IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) &&
|
|||
|
(IrpSp->Parameters.SetFile.FileInformationClass == FileEndOfFileInformation)))) {
|
|||
|
|
|||
|
IrpContext->ExceptionStatus = Status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If we failed to upgrade the volume's version during mount, we may
|
|||
|
// not have put the right exception code into the irp context yet.
|
|||
|
//
|
|||
|
|
|||
|
if ((IrpContext != NULL) &&
|
|||
|
(FlagOn( IrpContext->State, IRP_CONTEXT_STATE_VOL_UPGR_FAILED )) &&
|
|||
|
(IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) &&
|
|||
|
(IrpContext->MinorFunction == IRP_MN_MOUNT_VOLUME)) {
|
|||
|
|
|||
|
IrpContext->ExceptionStatus = Status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is the return status code that we want the Irp Completion routine to receive.
|
|||
|
//
|
|||
|
|
|||
|
Status = NtfsProcessException( IrpContext, Irp, Status );
|
|||
|
|
|||
|
if ((Status == STATUS_CANT_WAIT) || (Status == STATUS_LOG_FILE_FULL)) {
|
|||
|
|
|||
|
Retry = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} while (Retry);
|
|||
|
|
|||
|
FsRtlExitFileSystem();
|
|||
|
|
|||
|
//
|
|||
|
// If there are any entries on this volume's overflow queue, service
|
|||
|
// them.
|
|||
|
//
|
|||
|
|
|||
|
if (VolDo != NULL) {
|
|||
|
|
|||
|
KIRQL SavedIrql;
|
|||
|
PLIST_ENTRY Entry = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// We have a volume device object so see if there is any work
|
|||
|
// left to do in its overflow queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql );
|
|||
|
|
|||
|
while (VolDo->OverflowQueueCount > 0) {
|
|||
|
|
|||
|
//
|
|||
|
// There is overflow work to do in this volume so we'll
|
|||
|
// decrement the Overflow count, dequeue the IRP, and release
|
|||
|
// the Event
|
|||
|
//
|
|||
|
|
|||
|
Entry = VolDo->OverflowQueue.Flink;
|
|||
|
IrpContext = CONTAINING_RECORD( Entry,
|
|||
|
IRP_CONTEXT,
|
|||
|
WorkQueueItem.List );
|
|||
|
Irp = IrpContext->OriginatingIrp;
|
|||
|
|
|||
|
//
|
|||
|
// If the cancel routine thinks it owns the irp ignore it
|
|||
|
//
|
|||
|
|
|||
|
if (NtfsSetCancelRoutine( Irp, NULL, 0, FALSE )) {
|
|||
|
|
|||
|
VolDo->OverflowQueueCount -= 1;
|
|||
|
RemoveEntryList( (PLIST_ENTRY)Entry );
|
|||
|
break;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Release the spinlock to let the cancel routine gain it and finish
|
|||
|
// its action
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql );
|
|||
|
KeAcquireSpinLock( &VolDo->OverflowQueueSpinLock, &SavedIrql );
|
|||
|
Entry = NULL;
|
|||
|
}
|
|||
|
} // endwhile
|
|||
|
|
|||
|
KeReleaseSpinLock( &VolDo->OverflowQueueSpinLock, SavedIrql );
|
|||
|
|
|||
|
//
|
|||
|
// There wasn't an entry, break out of the loop and return to
|
|||
|
// the Ex Worker thread.
|
|||
|
//
|
|||
|
|
|||
|
if ( Entry == NULL ) {
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (VolDo->OverflowQueueCount < OVERFLOW_QUEUE_LIMIT) {
|
|||
|
KeSetEvent( &VolDo->OverflowQueueEvent, IO_NO_INCREMENT, FALSE );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// set wait to TRUE, and loop.
|
|||
|
//
|
|||
|
|
|||
|
LogFileFullCount = 0;
|
|||
|
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_WAIT );
|
|||
|
continue;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Decrement the PostedRequestCount.
|
|||
|
//
|
|||
|
|
|||
|
if (VolDo) {
|
|||
|
|
|||
|
ExInterlockedAddUlong( &VolDo->PostedRequestCount,
|
|||
|
0xffffffff,
|
|||
|
&VolDo->OverflowQueueSpinLock );
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
NtfsPostSpecial (
|
|||
|
IN PIRP_CONTEXT IrpContext,
|
|||
|
IN PVCB Vcb,
|
|||
|
IN POST_SPECIAL_CALLOUT PostSpecialCallout,
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine posts a special request to a worker thread. The function
|
|||
|
to be called is passed in. The Vcb is referenced to ensure it is not
|
|||
|
deleted while the posted request is excuting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Vcb - Volume control block for volume to post to.
|
|||
|
|
|||
|
PostSpecialCallout - Function to be called from the worker thread.
|
|||
|
|
|||
|
Context - Context point to pass to the function.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP_CONTEXT NewIrpContext = NULL;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER( IrpContext );
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// Create an IrpContext for use to post the request.
|
|||
|
//
|
|||
|
|
|||
|
NtfsInitializeIrpContext( NULL, TRUE, &NewIrpContext );
|
|||
|
NewIrpContext->Vcb = Vcb;
|
|||
|
|
|||
|
NewIrpContext->Union.PostSpecialCallout = PostSpecialCallout;
|
|||
|
NewIrpContext->OriginatingIrp = Context;
|
|||
|
|
|||
|
//
|
|||
|
// Updating the CloseCount and SystemFileCloseCount allows the volume
|
|||
|
// to be locked or dismounted, but the Vcb will not be deleted. This
|
|||
|
// routine will only be called with non-zero close counts so it is ok
|
|||
|
// to increment theses counts.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( Vcb->CloseCount > 0 );
|
|||
|
InterlockedIncrement( &Vcb->CloseCount );
|
|||
|
InterlockedIncrement( &Vcb->SystemFileCloseCount );
|
|||
|
|
|||
|
ExInitializeWorkItem( &NewIrpContext->WorkQueueItem,
|
|||
|
NtfsSpecialDispatch,
|
|||
|
NewIrpContext );
|
|||
|
|
|||
|
//
|
|||
|
// Determine if the scavenger is already running.
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireFastMutexUnsafe( &NtfsScavengerLock );
|
|||
|
|
|||
|
if (NtfsScavengerRunning) {
|
|||
|
|
|||
|
//
|
|||
|
// Add this item to the scavanger work list.
|
|||
|
//
|
|||
|
|
|||
|
NewIrpContext->WorkQueueItem.List.Flink = NULL;
|
|||
|
|
|||
|
if (NtfsScavengerWorkList == NULL) {
|
|||
|
|
|||
|
NtfsScavengerWorkList = NewIrpContext;
|
|||
|
} else {
|
|||
|
PIRP_CONTEXT WorkIrpContext;
|
|||
|
|
|||
|
WorkIrpContext = NtfsScavengerWorkList;
|
|||
|
|
|||
|
while (WorkIrpContext->WorkQueueItem.List.Flink != NULL) {
|
|||
|
WorkIrpContext = (PIRP_CONTEXT)
|
|||
|
WorkIrpContext->WorkQueueItem.List.Flink;
|
|||
|
}
|
|||
|
|
|||
|
WorkIrpContext->WorkQueueItem.List.Flink = (PLIST_ENTRY)
|
|||
|
NewIrpContext;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Start a worker thread to do scavenger work.
|
|||
|
//
|
|||
|
|
|||
|
ExQueueWorkItem( &NewIrpContext->WorkQueueItem, DelayedWorkQueue );
|
|||
|
NtfsScavengerRunning = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseFastMutexUnsafe( &NtfsScavengerLock);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
NtfsSpecialDispatch (
|
|||
|
PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called when a special operation needs to be posted.
|
|||
|
It is called indirectly by NtfsPostSpecial. It is assumes that the
|
|||
|
Vcb is protected from going away by incrementing the volemue close
|
|||
|
counts for a file. If this routine fails nothing is done except
|
|||
|
to clean up the Vcb. This routine also handles issues log file full
|
|||
|
and can't wait.
|
|||
|
|
|||
|
The function to be called is stored in the PostSpecialCallout field
|
|||
|
of the Irp Context, and the context is stored int he OriginatingIrp.
|
|||
|
Both fields are zeroed before the the callout function is called.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies a pointer to an IrpContext.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PVCB Vcb;
|
|||
|
PIRP_CONTEXT IrpContext = Context;
|
|||
|
TOP_LEVEL_CONTEXT TopLevelContext;
|
|||
|
PTOP_LEVEL_CONTEXT ThreadTopLevelContext;
|
|||
|
POST_SPECIAL_CALLOUT PostSpecialCallout;
|
|||
|
PVOID SpecialContext;
|
|||
|
ULONG LogFileFullCount;
|
|||
|
BOOLEAN Retry;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
FsRtlEnterFileSystem();
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
Vcb = IrpContext->Vcb;
|
|||
|
LogFileFullCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Capture the funciton pointer and context before using the IrpContext.
|
|||
|
//
|
|||
|
|
|||
|
PostSpecialCallout = IrpContext->Union.PostSpecialCallout;
|
|||
|
SpecialContext = IrpContext->OriginatingIrp;
|
|||
|
IrpContext->Union.PostSpecialCallout = NULL;
|
|||
|
IrpContext->OriginatingIrp = NULL;
|
|||
|
|
|||
|
ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, TRUE, TRUE );
|
|||
|
ASSERT( ThreadTopLevelContext == &TopLevelContext );
|
|||
|
ASSERT( !FlagOn( IrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
|
|||
|
ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_ALLOC_FROM_POOL ));
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the thread top level structure, if needed.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( IoGetTopLevelIrp() != (PIRP) &TopLevelContext );
|
|||
|
NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext );
|
|||
|
|
|||
|
//
|
|||
|
// Don't let this IrpContext be deleted.
|
|||
|
//
|
|||
|
|
|||
|
SetFlag( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT );
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
Retry = FALSE;
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// See if we failed due to a log file full condition, and
|
|||
|
// if so, then do a clean volume checkpoint if we are the
|
|||
|
// first ones to get there. If we see a different Lsn and do
|
|||
|
// not do the checkpoint, the worst that can happen is that we
|
|||
|
// will fail again if the log file is still full.
|
|||
|
//
|
|||
|
|
|||
|
if (IrpContext->LastRestartArea.QuadPart != 0) {
|
|||
|
|
|||
|
NtfsCheckpointForLogFileFull( IrpContext );
|
|||
|
|
|||
|
if (++LogFileFullCount >= 2) {
|
|||
|
|
|||
|
SetFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_EXCESS_LOG_FULL );
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Call the requested function.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( FlagOn( IrpContext->TopLevelIrpContext->State, IRP_CONTEXT_STATE_OWNS_TOP_LEVEL ));
|
|||
|
PostSpecialCallout( IrpContext, SpecialContext );
|
|||
|
|
|||
|
NtfsCompleteRequest( IrpContext, NULL, STATUS_SUCCESS );
|
|||
|
|
|||
|
} except(NtfsExceptionFilter( IrpContext, GetExceptionInformation() )) {
|
|||
|
|
|||
|
NTSTATUS ExceptionCode;
|
|||
|
|
|||
|
ExceptionCode = GetExceptionCode();
|
|||
|
ExceptionCode = NtfsProcessException( IrpContext, NULL, ExceptionCode );
|
|||
|
|
|||
|
if ((ExceptionCode == STATUS_CANT_WAIT) ||
|
|||
|
(ExceptionCode == STATUS_LOG_FILE_FULL)) {
|
|||
|
|
|||
|
Retry = TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} while (Retry);
|
|||
|
|
|||
|
//
|
|||
|
// Ok to let this IrpContext be deleted.
|
|||
|
//
|
|||
|
|
|||
|
ClearFlag( IrpContext->State, IRP_CONTEXT_STATE_PERSISTENT );
|
|||
|
|
|||
|
//
|
|||
|
// At this point regardless of the status the volume needs to
|
|||
|
// be cleaned up and the IrpContext freed.
|
|||
|
// Dereference the Vcb and check to see if it needs to be deleted.
|
|||
|
// since this call might raise wrap it with a try/execpt.
|
|||
|
//
|
|||
|
|
|||
|
try {
|
|||
|
|
|||
|
//
|
|||
|
// Acquire the volume exclusive so the counts can be
|
|||
|
// updated.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT( FlagOn( IrpContext->State, IRP_CONTEXT_STATE_WAIT ));
|
|||
|
NtfsAcquireExclusiveVcb( IrpContext, Vcb, TRUE );
|
|||
|
|
|||
|
InterlockedDecrement( &Vcb->SystemFileCloseCount );
|
|||
|
InterlockedDecrement( &Vcb->CloseCount );
|
|||
|
|
|||
|
NtfsReleaseVcb( IrpContext, Vcb );
|
|||
|
|
|||
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|||
|
|
|||
|
ASSERT( FsRtlIsNtstatusExpected( GetExceptionCode() ) );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Free the irp context.
|
|||
|
//
|
|||
|
|
|||
|
NtfsCleanupIrpContext( IrpContext, TRUE );
|
|||
|
|
|||
|
//
|
|||
|
// See if there is more work on the scavenger list.
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireFastMutexUnsafe( &NtfsScavengerLock );
|
|||
|
|
|||
|
ASSERT( NtfsScavengerRunning );
|
|||
|
|
|||
|
IrpContext = NtfsScavengerWorkList;
|
|||
|
|
|||
|
if (IrpContext != NULL) {
|
|||
|
|
|||
|
//
|
|||
|
// Remove the entry from the list.
|
|||
|
//
|
|||
|
|
|||
|
NtfsScavengerWorkList = (PIRP_CONTEXT) IrpContext->WorkQueueItem.List.Flink;
|
|||
|
IrpContext->WorkQueueItem.List.Flink = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
NtfsScavengerRunning = FALSE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ExReleaseFastMutexUnsafe( &NtfsScavengerLock );
|
|||
|
|
|||
|
} while ( IrpContext != NULL );
|
|||
|
|
|||
|
FsRtlExitFileSystem();
|
|||
|
}
|