NTSTATUS NtfsFsdLockControl ( IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of Lock Control. Arguments: VolumeDeviceObject - Supplies the volume device object where the file exists Irp - Supplies the Irp being processed Return Value: NTSTATUS - The FSD status for the IRP --*/ { TOP_LEVEL_CONTEXT TopLevelContext; PTOP_LEVEL_CONTEXT ThreadTopLevelContext; NTSTATUS Status = STATUS_SUCCESS; PIRP_CONTEXT IrpContext = NULL; #if __NDAS_NTFS__ if ((PVOID)NdasNtfsControlDeviceObject == VolumeDeviceObject) { Status = Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; Irp->IoStatus.Information = 0; IoCompleteRequest( Irp, IO_DISK_INCREMENT ); return Status; } #endif ASSERT_IRP( Irp ); UNREFERENCED_PARAMETER( VolumeDeviceObject ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsFsdLockControl\n") ); // // Call the common Lock Control routine // FsRtlEnterFileSystem(); ThreadTopLevelContext = NtfsInitializeTopLevelIrp( &TopLevelContext, FALSE, FALSE ); do { try { // // We are either initiating this request or retrying it. // if (IrpContext == NULL) { // // Allocate and initialize the Irp. // NtfsInitializeIrpContext( Irp, CanFsdWait( Irp ), &IrpContext ); // // Initialize the thread top level structure, if needed. // NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); } else if (Status == STATUS_LOG_FILE_FULL) { NtfsCheckpointForLogFileFull( IrpContext ); } #if __NDAS_NTFS_SECONDARY__ if (IS_SECONDARY_FILEOBJECT(IoGetCurrentIrpStackLocation(Irp)->FileObject)) { BOOLEAN secondaryResourceAcquired = FALSE; BOOLEAN secondaryRecoveryResourceAcquired = FALSE; ASSERT( NtfsIsTopLevelRequest(IrpContext) ); SetFlag( IrpContext->NdasNtfsFlags, NDAS_NTFS_IRP_CONTEXT_FLAG_SECONDARY_CONTEXT ); SetFlag( IrpContext->NdasNtfsFlags, NDAS_NTFS_IRP_CONTEXT_FLAG_SECONDARY_FILE ); Status = STATUS_SUCCESS; for (;;) { NDAS_ASSERT( secondaryRecoveryResourceAcquired == FALSE ); NDAS_ASSERT( secondaryResourceAcquired == FALSE ); if (!FlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT)) { Status = NtfsPostRequest( IrpContext, Irp ); break; } if (FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED)) { secondaryRecoveryResourceAcquired = SecondaryAcquireResourceExclusiveLite( IrpContext, &VolumeDeviceObject->RecoveryResource, BooleanFlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) ); if (!FlagOn(VolumeDeviceObject->Secondary->Thread.Flags, SECONDARY_THREAD_FLAG_REMOTE_DISCONNECTED) ) { SecondaryReleaseResourceLite( IrpContext, &VolumeDeviceObject->RecoveryResource ); secondaryRecoveryResourceAcquired = FALSE; continue; } secondaryResourceAcquired = SecondaryAcquireResourceExclusiveLite( IrpContext, &VolumeDeviceObject->Resource, BooleanFlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) ); try { SecondaryRecoverySessionStart( VolumeDeviceObject->Secondary, IrpContext ); } finally { SecondaryReleaseResourceLite( IrpContext, &VolumeDeviceObject->Resource ); secondaryResourceAcquired = FALSE; SecondaryReleaseResourceLite( IrpContext, &VolumeDeviceObject->RecoveryResource ); secondaryRecoveryResourceAcquired = FALSE; } continue; } secondaryResourceAcquired = SecondaryAcquireResourceSharedLite( IrpContext, &VolumeDeviceObject->Resource, BooleanFlagOn(IrpContext->State, IRP_CONTEXT_STATE_WAIT) ); NDAS_ASSERT( secondaryResourceAcquired == TRUE ); break; } if (Status == STATUS_SUCCESS) { try { Status = NtfsCommonLockControl( IrpContext, Irp ); } finally { ASSERT( ExIsResourceAcquiredSharedLite(&VolumeDeviceObject->Resource) ); SecondaryReleaseResourceLite( NULL, &VolumeDeviceObject->Resource ); } } } else Status = NtfsCommonLockControl( IrpContext, Irp ); #else Status = NtfsCommonLockControl( IrpContext, Irp ); #endif break; } except(NtfsExceptionFilter( 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 = NtfsProcessException( IrpContext, Irp, GetExceptionCode() ); } } while (Status == STATUS_CANT_WAIT ||
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(); }
NTSTATUS NtfsFsdLockControl ( IN PVOLUME_DEVICE_OBJECT VolumeDeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine implements the FSD part of Lock Control. Arguments: VolumeDeviceObject - Supplies the volume device object where the file exists Irp - Supplies the Irp being processed Return Value: NTSTATUS - The FSD status for the IRP --*/ { TOP_LEVEL_CONTEXT TopLevelContext; PTOP_LEVEL_CONTEXT ThreadTopLevelContext; NTSTATUS Status = STATUS_SUCCESS; PIRP_CONTEXT IrpContext = NULL; ASSERT_IRP( Irp ); UNREFERENCED_PARAMETER( VolumeDeviceObject ); PAGED_CODE(); DebugTrace( +1, Dbg, ("NtfsFsdLockControl\n") ); // // Call the common Lock Control routine // FsRtlEnterFileSystem(); ThreadTopLevelContext = NtfsSetTopLevelIrp( &TopLevelContext, FALSE, FALSE ); do { try { // // We are either initiating this request or retrying it. // if (IrpContext == NULL) { IrpContext = NtfsCreateIrpContext( Irp, CanFsdWait( Irp ) ); NtfsUpdateIrpContextWithTopLevel( IrpContext, ThreadTopLevelContext ); } else if (Status == STATUS_LOG_FILE_FULL) { NtfsCheckpointForLogFileFull( IrpContext ); } Status = NtfsCommonLockControl( IrpContext, Irp ); break; } except(NtfsExceptionFilter( 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 = NtfsProcessException( IrpContext, Irp, GetExceptionCode() ); } } while (Status == STATUS_CANT_WAIT || Status == STATUS_LOG_FILE_FULL); if (ThreadTopLevelContext == &TopLevelContext) { NtfsRestoreTopLevelIrp( ThreadTopLevelContext ); } FsRtlExitFileSystem(); // // And return to our caller // DebugTrace( -1, Dbg, ("NtfsFsdLockControl -> %08lx\n", Status) ); return Status; }
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; }