NTSTATUS CdProcessException ( _In_opt_ PIRP_CONTEXT IrpContext, _Inout_ PIRP Irp, _In_ NTSTATUS ExceptionCode ) /*++ Routine Description: This routine processes an exception. It either completes the request with the exception status in the IrpContext, sends this off to the Fsp workque or causes it to be retried in the current thread if a verification is needed. If the volume needs to be verified (STATUS_VERIFY_REQUIRED) and we can do the work in the current thread we will translate the status code to STATUS_CANT_WAIT to indicate that we need to retry the request. Arguments: Irp - Supplies the Irp being processed ExceptionCode - Supplies the normalized exception status being handled Return Value: NTSTATUS - Returns the results of either posting the Irp or the saved completion status. --*/ { PDEVICE_OBJECT Device = NULL; PVPB Vpb; PETHREAD Thread; ASSERT_OPTIONAL_IRP_CONTEXT( IrpContext ); ASSERT_IRP( Irp ); // // If there is not an irp context, then complete the request with the // current status code. // if (!ARGUMENT_PRESENT( IrpContext )) { CdCompleteRequest( NULL, Irp, ExceptionCode ); return ExceptionCode; } // // Get the real exception status from the IrpContext. // ExceptionCode = IrpContext->ExceptionStatus; // // Check if we are posting this request. One of the following must be true // if we are to post a request. // // - Status code is STATUS_CANT_WAIT and the request is asynchronous // or we are forcing this to be posted. // // - Status code is STATUS_VERIFY_REQUIRED and we are at APC level // or higher, or within a guarded region. Can't wait for IO in // the verify path in this case. // // Set the MORE_PROCESSING flag in the IrpContext to keep if from being // deleted if this is a retryable condition. // // // Note that (children of) CdFsdPostRequest can raise (Mdl allocation). // try { if (ExceptionCode == STATUS_CANT_WAIT) { if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_FORCE_POST )) { ExceptionCode = CdFsdPostRequest( IrpContext, Irp ); } } else if ((ExceptionCode == STATUS_VERIFY_REQUIRED) && FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL ) && KeAreAllApcsDisabled()) { ExceptionCode = CdFsdPostRequest( IrpContext, Irp ); } } except( CdExceptionFilter( IrpContext, GetExceptionInformation() )) { ExceptionCode = GetExceptionCode(); } // // If we posted the request or our caller will retry then just return here. // if ((ExceptionCode == STATUS_PENDING) || (ExceptionCode == STATUS_CANT_WAIT)) { return ExceptionCode; } ClearFlag( IrpContext->Flags, IRP_CONTEXT_FLAG_MORE_PROCESSING ); // // If we are not a top level request then we just complete the request // with the current status code. // if (!FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_TOP_LEVEL )) { CdCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // Store this error into the Irp for posting back to the Io system. // Irp->IoStatus.Status = ExceptionCode; if (IoIsErrorUserInduced( ExceptionCode )) { // // Check for the various error conditions that can be caused by, // and possibly resolved my the user. // if (ExceptionCode == STATUS_VERIFY_REQUIRED) { // // Now we are at the top level file system entry point. // // If we have already posted this request then the device to // verify is in the original thread. Find this via the Irp. // Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); // // If there is no device in that location then check in the // current thread. // if (Device == NULL) { Device = IoGetDeviceToVerify( PsGetCurrentThread() ); IoSetDeviceToVerify( PsGetCurrentThread(), NULL ); NT_ASSERT( Device != NULL ); } // // It turns out some storage drivers really do set invalid non-NULL device // objects to verify. // // To work around this, completely ignore the device to verify in the thread, // and just use our real device object instead. // if (IrpContext->Vcb) { Device = IrpContext->Vcb->Vpb->RealDevice; } // // Let's not BugCheck just because the device to verify is somehow still NULL. // if (Device == NULL) { ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; CdCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // CdPerformVerify() will do the right thing with the Irp. // If we return STATUS_CANT_WAIT then the current thread // can retry the request. // return CdPerformVerify( IrpContext, Irp, Device ); } // // The other user induced conditions generate an error unless // they have been disabled for this request. // if (FlagOn( IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS )) { CdCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // Generate a pop-up. // else { if (IoGetCurrentIrpStackLocation( Irp )->FileObject != NULL) { Vpb = IoGetCurrentIrpStackLocation( Irp )->FileObject->Vpb; } else { Vpb = NULL; } // // The device to verify is either in my thread local storage // or that of the thread that owns the Irp. // Thread = Irp->Tail.Overlay.Thread; Device = IoGetDeviceToVerify( Thread ); if (Device == NULL) { Thread = PsGetCurrentThread(); Device = IoGetDeviceToVerify( Thread ); NT_ASSERT( Device != NULL ); } // // It turns out some storage drivers really do set invalid non-NULL device // objects to verify. // // To work around this, completely ignore the device to verify in the thread, // and just use our real device object instead. // if (IrpContext->Vcb) { Device = IrpContext->Vcb->Vpb->RealDevice; } // // Let's not BugCheck just because the device to verify is somehow still NULL. // if (Device == NULL) { CdCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // This routine actually causes the pop-up. It usually // does this by queuing an APC to the callers thread, // but in some cases it will complete the request immediately, // so it is very important to IoMarkIrpPending() first. // IoMarkIrpPending( Irp ); IoRaiseHardError( Irp, Vpb, Device ); // // We will be handing control back to the caller here, so // reset the saved device object. // IoSetDeviceToVerify( Thread, NULL ); // // The Irp will be completed by Io or resubmitted. In either // case we must clean up the IrpContext here. // CdCompleteRequest( IrpContext, NULL, STATUS_SUCCESS ); return STATUS_PENDING; } } // // This is just a run of the mill error. // CdCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; }
NTSTATUS FatProcessException ( IN PIRP_CONTEXT IrpContext, IN PIRP Irp, IN NTSTATUS ExceptionCode ) /*++ Routine Description: This routine process an exception. It either completes the request with the saved exception status or it sends it off to IoRaiseHardError() Arguments: Irp - Supplies the Irp being processed ExceptionCode - Supplies the normalized exception status being handled Return Value: NTSTATUS - Returns the results of either posting the Irp or the saved completion status. --*/ { PVCB Vcb; PIO_STACK_LOCATION IrpSp; FAT_VOLUME_STATE TransitionState = VolumeDirty; ULONG SavedFlags; DebugTrace(0, Dbg, "FatProcessException\n", 0); // // If there is not an irp context, we must have had insufficient resources. // if ( !ARGUMENT_PRESENT( IrpContext ) ) { FatCompleteRequest( FatNull, Irp, ExceptionCode ); return ExceptionCode; } // // Get the real exception status from IrpContext->ExceptionStatus, and // reset it. // ExceptionCode = IrpContext->ExceptionStatus; FatResetExceptionState( IrpContext ); // // If this is an Mdl write request, then take care of the Mdl // here so that things get cleaned up properly. Cc now leaves // the MDL in place so a filesystem can retry after clearing an // internal condition (FAT does not). // #if __NDAS_FAT_WIN2K_SUPPORT__ if (NdFatCcMdlWriteAbort && (IrpContext->MajorFunction == IRP_MJ_WRITE) && (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE_MDL ) == IRP_MN_COMPLETE_MDL) && (Irp->MdlAddress != NULL)) { PIO_STACK_LOCATION LocalIrpSp = IoGetCurrentIrpStackLocation(Irp); NdFatCcMdlWriteAbort( LocalIrpSp->FileObject, Irp->MdlAddress ); Irp->MdlAddress = NULL; } #else if ((IrpContext->MajorFunction == IRP_MJ_WRITE) && (FlagOn( IrpContext->MinorFunction, IRP_MN_COMPLETE_MDL ) == IRP_MN_COMPLETE_MDL) && (Irp->MdlAddress != NULL)) { PIO_STACK_LOCATION LocalIrpSp = IoGetCurrentIrpStackLocation(Irp); CcMdlWriteAbort( LocalIrpSp->FileObject, Irp->MdlAddress ); Irp->MdlAddress = NULL; } #endif // // If we are going to post the request, we may have to lock down the // user's buffer, so do it here in a try except so that we failed the // request if the LockPages fails. // // Also unpin any repinned Bcbs, protected by the try {} except {} filter. // try { SavedFlags = IrpContext->Flags; // // Make sure we don't try to write through Bcbs // SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_WRITE_THROUGH); FatUnpinRepinnedBcbs( IrpContext ); IrpContext->Flags = SavedFlags; // // If we will have to post the request, do it here. Note // that the last thing FatPrePostIrp() does is mark the Irp pending, // so it is critical that we actually return PENDING. Nothing // from this point to return can fail, so we are OK. // // We cannot do a verify operations at APC level because we // have to wait for Io operations to complete. // #if __NDAS_FAT__ if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) && (((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) || (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) && ExceptionCode == STATUS_CANT_WAIT))) { ExceptionCode = FatFsdPostRequest( IrpContext, Irp ); } #else if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL) && (((ExceptionCode == STATUS_VERIFY_REQUIRED) && (KeGetCurrentIrql() >= APC_LEVEL)) || (ExceptionCode == STATUS_CANT_WAIT))) { ExceptionCode = FatFsdPostRequest( IrpContext, Irp ); } #endif } except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) { ExceptionCode = IrpContext->ExceptionStatus; IrpContext->ExceptionStatus = 0; IrpContext->Flags = SavedFlags; } // // If we posted the request, just return here. // if (ExceptionCode == STATUS_PENDING) { return ExceptionCode; } Irp->IoStatus.Status = ExceptionCode; // // If this request is not a "top-level" irp, just complete it. // if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_RECURSIVE_CALL)) { // // If there is a cache operation above us, commute verify // to a lock conflict. This will cause retries so that // we have a chance of getting through without needing // to return an unaesthetic error for the operation. // if (IoGetTopLevelIrp() == (PIRP)FSRTL_CACHE_TOP_LEVEL_IRP && ExceptionCode == STATUS_VERIFY_REQUIRED) { ExceptionCode = STATUS_FILE_LOCK_CONFLICT; } FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } if (IoIsErrorUserInduced(ExceptionCode)) { // // Check for the various error conditions that can be caused by, // and possibly resolved by the user. // if (ExceptionCode == STATUS_VERIFY_REQUIRED) { PDEVICE_OBJECT Device; DebugTrace(0, Dbg, "Perform Verify Operation\n", 0); // // Now we are at the top level file system entry point. // // Grab the device to verify from the thread local storage // and stick it in the information field for transportation // to the fsp. We also clear the field at this time. // Device = IoGetDeviceToVerify( Irp->Tail.Overlay.Thread ); IoSetDeviceToVerify( Irp->Tail.Overlay.Thread, NULL ); if ( Device == NULL ) { Device = IoGetDeviceToVerify( PsGetCurrentThread() ); IoSetDeviceToVerify( PsGetCurrentThread(), NULL ); ASSERT( Device != NULL ); } // // Let's not BugCheck just because the driver messed up. // if (Device == NULL) { ExceptionCode = STATUS_DRIVER_INTERNAL_ERROR; FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // FatPerformVerify() will do the right thing with the Irp. return FatPerformVerify( IrpContext, Irp, Device ); } // // The other user induced conditions generate an error unless // they have been disabled for this request. // if (FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS)) { FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } else { // // Generate a pop-up // PDEVICE_OBJECT RealDevice; PVPB Vpb; PETHREAD Thread; if (IoGetCurrentIrpStackLocation(Irp)->FileObject != NULL) { Vpb = IoGetCurrentIrpStackLocation(Irp)->FileObject->Vpb; } else { Vpb = NULL; } // // The device to verify is either in my thread local storage // or that of the thread that owns the Irp. // Thread = Irp->Tail.Overlay.Thread; RealDevice = IoGetDeviceToVerify( Thread ); if ( RealDevice == NULL ) { Thread = PsGetCurrentThread(); RealDevice = IoGetDeviceToVerify( Thread ); ASSERT( RealDevice != NULL ); } // // Let's not BugCheck just because the driver messed up. // if (RealDevice == NULL) { FatCompleteRequest( IrpContext, Irp, ExceptionCode ); return ExceptionCode; } // // This routine actually causes the pop-up. It usually // does this by queuing an APC to the callers thread, // but in some cases it will complete the request immediately, // so it is very important to IoMarkIrpPending() first. // IoMarkIrpPending( Irp ); IoRaiseHardError( Irp, Vpb, RealDevice ); // // We will be handing control back to the caller here, so // reset the saved device object. // IoSetDeviceToVerify( Thread, NULL ); // // The Irp will be completed by Io or resubmitted. In either // case we must clean up the IrpContext here. // FatDeleteIrpContext( IrpContext ); return STATUS_PENDING; } } // // This is just a run of the mill error. If is a STATUS that we // raised ourselves, and the information would be use for the // user, raise an informational pop-up. // IrpSp = IoGetCurrentIrpStackLocation( Irp ); Vcb = IrpContext->Vcb; // // Now, if the Vcb is unknown to us this means that the error was raised // in the process of a mount and before we even had a chance to build // a full Vcb - and was really handled there. // if (Vcb != NULL) { if ( !FatDeviceIsFatFsdo( IrpSp->DeviceObject) && !NT_SUCCESS(ExceptionCode) && !FsRtlIsTotalDeviceFailure(ExceptionCode) ) { TransitionState = VolumeDirtyWithSurfaceTest; } // // If this was a STATUS_FILE_CORRUPT or similar error indicating some // nastiness out on the media, then mark the volume permanently dirty. // if (!FlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_DISABLE_POPUPS) && ( TransitionState == VolumeDirtyWithSurfaceTest || (ExceptionCode == STATUS_FILE_CORRUPT_ERROR) || (ExceptionCode == STATUS_DISK_CORRUPT_ERROR) || (ExceptionCode == STATUS_EA_CORRUPT_ERROR) || (ExceptionCode == STATUS_INVALID_EA_NAME) || (ExceptionCode == STATUS_EA_LIST_INCONSISTENT) || (ExceptionCode == STATUS_NO_EAS_ON_FILE) )) { ASSERT( NodeType(Vcb) == FAT_NTC_VCB ); SetFlag( Vcb->VcbState, VCB_STATE_FLAG_MOUNTED_DIRTY ); // // Do the "dirty" work, ignoring any error. // try { FatMarkVolume( IrpContext, Vcb, TransitionState ); } except( FatExceptionFilter( IrpContext, GetExceptionInformation() ) ) { NOTHING; } } }